package api import ( "fmt" "log" "net/http" "path/filepath" "strconv" "time" db "git.nochill.in/nochill/hiling_go/db/repository" "git.nochill.in/nochill/hiling_go/util" "git.nochill.in/nochill/hiling_go/util/token" "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" ) type createReviewReq struct { SubmittedBy int8 `json:"submitted_by" binding:"required"` Comments string `json:"comments" binding:"required"` Score int8 `json:"score" binding:"numeric,max=100"` IsFromCritic *bool `json:"is_from_critic"` IsHided *bool `json:"is_hided"` LocationId int32 `json:"location_id" binding:"required"` Title string `json:"title"` } func (server *Server) createReview(ctx *gin.Context) { var req createReviewReq if err := ctx.ShouldBindJSON(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } checkArg := db.CheckIfReviewExistsParams{ LocationID: req.LocationId, SubmittedBy: int32(req.SubmittedBy), } reviewExist, err := server.Store.CheckIfReviewExists(ctx, checkArg) if err != nil { ctx.JSON(http.StatusInternalServerError, "Something went wrong while try to check review") return } if reviewExist != 0 { ctx.JSON(http.StatusConflict, "User review already exist") return } arg := db.CreateReviewParams{ SubmittedBy: int32(req.SubmittedBy), Comments: req.Comments, Score: int16(req.Score), IsFromCritic: *req.IsFromCritic, IsHided: *req.IsHided, LocationID: req.LocationId, Title: pgtype.Text{String: req.Title, Valid: req.Title != ""}, } review, err := server.Store.CreateReview(ctx, arg) if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to save review")) return } ctx.JSON(http.StatusOK, review) } func (server *Server) uploadReviewImages(ctx *gin.Context) { reviewIdStr := ctx.PostForm("review_id") reviewId, err := strconv.Atoi(reviewIdStr) if err != nil || reviewId <= 0 { ctx.JSON(http.StatusBadRequest, gin.H{"message": "Invalid review_id"}) return } authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload) form, err := ctx.MultipartForm() if err != nil { ctx.JSON(http.StatusBadRequest, ErrorResponse(err, "Failed to parse multipart form")) return } files := form.File["images"] if len(files) == 0 { ctx.JSON(http.StatusBadRequest, gin.H{"message": "No images provided"}) return } var params []db.CreateImageParams for _, fileHeader := range files { fileExt := filepath.Ext(fileHeader.Filename) key := fmt.Sprintf("reviews/%d/%s%d%s", reviewId, util.RandomString(5), time.Now().UnixNano(), fileExt) f, err := fileHeader.Open() if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Failed to open image")) return } defer f.Close() contentType := fileHeader.Header.Get("Content-Type") if contentType == "" { contentType = "application/octet-stream" } publicURL, err := server.R2.UploadFile(ctx.Request.Context(), key, f, contentType) if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Failed to upload image to storage")) return } params = append(params, db.CreateImageParams{ ImageUrl: publicURL, UploadedBy: int32(authPayload.UserID), ImageType: "reviews", ImageOf: int32(reviewId), }) } if err := server.Store.CreateImage(ctx, params); err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Failed to save image records")) return } ctx.JSON(http.StatusOK, gin.H{"uploaded": len(params)}) } type getUserReviewByLocationReq struct { LocationID int32 `uri:"location_id" binding:"required"` } type userReviewWithImagesResponse struct { db.GetUserReviewByLocationRow Images []db.GetImagesByReviewRow `json:"images"` } func (server *Server) getUserReviewByLocation(ctx *gin.Context) { var req getUserReviewByLocationReq if err := ctx.ShouldBindUri(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload) log.Println(authPayload) arg := db.GetUserReviewByLocationParams{ SubmittedBy: int32(authPayload.UserID), LocationID: req.LocationID, } review, err := server.Store.GetUserReviewByLocation(ctx, arg) if err != nil { if err == pgx.ErrNoRows { ctx.JSON(http.StatusNotFound, ErrorResponse(err, "Review not found")) return } ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to recevie current user review")) return } images, err := server.Store.GetImagesByReview(ctx, review.ID) if err != nil { images = []db.GetImagesByReviewRow{} } ctx.JSON(http.StatusOK, userReviewWithImagesResponse{ GetUserReviewByLocationRow: review, Images: images, }) }