package api import ( "fmt" "net/http" "os" "path/filepath" "time" db "git.nochill.in/nochill/hiling_go/db/sqlc" "git.nochill.in/nochill/hiling_go/util" "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" "github.com/meilisearch/meilisearch-go" ysqlc "github.com/yiplee/sqlc" ) type createLocationReq struct { Address string `form:"address" binding:"required"` Name string `form:"name" binding:"required"` SubmittedBy int32 `form:"submitted_by" binding:"required,number"` RegencyID int16 `form:"regency_id" binding:"required,number"` LocationType string `form:"location_type" binding:"required"` GoogleMapsLink string `form:"google_maps_link"` } func (server *Server) createLocation(ctx *gin.Context) { var req createLocationReq var imgPath string var tempImg []db.CreateImageParams if err := ctx.Bind(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } form, _ := ctx.MultipartForm() thumbnails := form.File["thumbnail"] if len(thumbnails) > 0 { for _, img := range thumbnails { fileExt := filepath.Ext(img.Filename) now := time.Now() dir := fmt.Sprintf("public/upload/images/locations/%s/thumbnail", req.Name) osFilename := fmt.Sprintf("%s%s%s", util.RandomString(5), fmt.Sprintf("%v", now.Unix()), fileExt) imgPath = fmt.Sprintf("%s/%s", dir, osFilename) if _, err := os.Stat(dir); os.IsNotExist(err) { os.Mkdir(dir, 0775) } if err := ctx.SaveUploadedFile(img, imgPath); err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Error while try to save thumbnail image")) return } } arg := db.CreateLocationTxParams{ Address: req.Address, Name: req.Name, LocationType: db.LocationType(req.LocationType), SubmittedBy: req.SubmittedBy, RegencyID: req.RegencyID, IsDeleted: false, ApprovedBy: pgtype.Int4{Int32: 0, Valid: false}, GoogleMapsLink: pgtype.Text{Valid: len(req.GoogleMapsLink) > 0, String: req.GoogleMapsLink}, Thumbnail: tempImg, } err := server.Store.CreateLocationTx(ctx, arg) if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to save Location")) return } } ctx.Writer.WriteHeader(http.StatusOK) } type getListLocationsReq struct { Page int32 `form:"page" binding:"required,min=1"` PageSize int32 `form:"page_size" binding:"required,min=5"` } func (server *Server) getListLocations(ctx *gin.Context) { var req getListLocationsReq if err := ctx.ShouldBindQuery(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } locations, err := server.Store.GetListLocations(ysqlc.Build(ctx, func(builder *ysqlc.Builder) { builder.Limit(int(req.PageSize)) builder.Offset(int(req.Page-1) * int(req.PageSize)) builder.Order("created_at ASC") })) if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong")) return } ctx.JSON(http.StatusOK, locations) } type getTopListLocationsReq struct { Page int32 `form:"page" binding:"required,min=1"` PageSize int32 `form:"page_size" binding:"required,min=5"` OrderBy int16 `form:"order_by" binding:"numeric,min=1,max=3"` RegionType int32 `form:"region_type" binding:"numeric,min=0,max=7"` } func (server *Server) getTopListLocations(ctx *gin.Context) { var req getTopListLocationsReq var orderby string if err := ctx.ShouldBindQuery(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } switch req.OrderBy { case 1: orderby = "iq2.avg_bayes" case 2: orderby = "iq2.critic_bayes" case 3: orderby = "iq2.user_bayes" } arg := db.GetTopListLocationsParams{ Limit: req.PageSize, Offset: (req.Page - 1) * req.PageSize, OrderBy: orderby, RegionType: pgtype.Int4{Valid: req.RegionType > 0, Int32: req.RegionType}, } locations, err := server.Store.GetTopListLocations(ctx, arg) if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong")) return } ctx.JSON(http.StatusOK, locations) // str, err := ctx.Cookie("kek123429") // if err != nil { // ctx.JSON(http.StatusUnauthorized, ErrorResponse(err, "")) // } // ctx.JSON(http.StatusOK, gin.H{ // "str": str, // "res": locations, // }) } type getListRecentLocationsWithRatingsReq struct { Page int32 `form:"page_size" binding:"required,min=1"` LocationType string `form:"location_type"` Region string `form:"regions"` } func (server *Server) getListRecentLocationsWithRatings(ctx *gin.Context) { var req getListRecentLocationsWithRatingsReq if err := ctx.ShouldBindQuery(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } locations, err := server.Store.GetListRecentLocationsWithRatings(ctx, db.GetListRecentLocationsParams{ Limit: req.Page, Regions: req.Region, LocationTypes: req.LocationType, }) if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong")) return } ctx.JSON(http.StatusOK, locations) } type getLocationReq struct { ID int32 `uri:"location_id" binding:"required"` } func (server *Server) getLocation(ctx *gin.Context) { var req getLocationReq if err := ctx.ShouldBindUri(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } location, err := server.Store.GetLocation(ctx, req.ID) if err != nil { if err == pgx.ErrNoRows { ctx.JSON(http.StatusNotFound, ErrorResponse(err, "")) return } ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while retrieving location detail")) return } tags, err := server.Store.GetLocationTag(ctx, req.ID) if err != nil { ctx.JSON(http.StatusBadRequest, ErrorResponse(err, "Something went wrong while retrieving location tags")) return } users_reviews, err := server.Store.GetListLocationReviews(ctx, db.GetListLocationReviewsParams{ LocationID: req.ID, Limit: 5, Offset: 0, IsCritics: false, }) if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to receive user reviews for this location")) return } critics_reviews, err := server.Store.GetListLocationReviews(ctx, db.GetListLocationReviewsParams{ LocationID: req.ID, Limit: 20, Offset: 0, IsCritics: true, }) if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to receive user reviews for this location")) return } res := gin.H{ "tags": tags, "detail": location, "users_review": users_reviews, "critics_review": critics_reviews, } ctx.JSON(http.StatusOK, res) } type getListLocationReviewsReq struct { PageSize int8 `form:"page_size" binding:"required"` Page int8 `form:"page" binding:"required,min=1"` Type int8 `form:"type" binding:"required"` // 0 = all, 1 = critics,2 = users LocationID int32 `form:"location_id" binding:"required"` } type getListLocationReviewsRes struct { CriticsReviews []db.GetListLocationReviewsRow `json:"critics_reviews"` UsersReviews []db.GetListLocationReviewsRow `json:"users_reviews"` } func (server *Server) getListLocationReviews(ctx *gin.Context) { var req getListLocationReviewsReq var res getListLocationReviewsRes if err := ctx.ShouldBindQuery(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } if req.Type-1 == 1 || req.Type-1 == 0 { arg := db.GetListLocationReviewsParams{ LocationID: req.LocationID, Offset: (req.Page - 1) * req.PageSize, Limit: req.PageSize, IsCritics: true, } reviews, err := server.Store.GetListLocationReviews(ctx, arg) if err != nil { if err == pgx.ErrNoRows { ctx.JSON(http.StatusNotFound, ErrorResponse(err, "There's no critics reviews for this location yet")) return } ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get critics reviews for this location")) return } res.CriticsReviews = reviews } if req.Type-1 == 2 || req.Type-1 == 0 { arg := db.GetListLocationReviewsParams{ LocationID: req.LocationID, Limit: req.PageSize, Offset: (req.Page - 1) * req.PageSize, IsCritics: false, } reviews, err := server.Store.GetListLocationReviews(ctx, arg) if err != nil { if err == pgx.ErrNoRows { ctx.JSON(http.StatusNotFound, ErrorResponse(err, "There's no critics reviews for this location yet")) return } ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get critics reviews for this location")) return } res.UsersReviews = reviews } ctx.JSON(http.StatusOK, res) } type searchLocationsParams struct { Name string `form:"name"` Filter string `form:"filter"` } func (server *Server) searchLocations(ctx *gin.Context) { var req searchLocationsParams if err := ctx.ShouldBindQuery(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } searchRequest := meilisearch.SearchRequest{ Limit: 7, Offset: 0, Filter: req.Filter, } searchRes, err := server.MeilisearchClient.Index("locations").Search(req.Name, &searchRequest) if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, fmt.Sprintf("Something went wrong while try to search locations %s", req.Name))) return } ctx.JSON(http.StatusOK, searchRes.Hits) }