From 44504dc1bc4ec8458c1f9285bb4d7b4f79bb306f Mon Sep 17 00:00:00 2001 From: goro Date: Sun, 31 May 2026 12:08:43 +0300 Subject: [PATCH] added multiple grading --- Makefile | 9 +- api/image.go | 9 + api/location.go | 134 +- api/news_event.go | 18 + api/region.go | 15 + api/reviews.go | 80 ++ api/server.go | 6 + api/tags.go | 7 + api/user.go | 53 + ...r_tags_column_approvedby_nullable.down.sql | 1 + ...added_multiple_grades_for_reviews.down.sql | 6 + ...d_added_multiple_grades_for_reviews.up.sql | 6 + ...014_create_menu_items_and_reviews.down.sql | 6 + ...00014_create_menu_items_and_reviews.up.sql | 59 + db/mock/store.go | 290 ++-- db/repository/locations.go | 6 +- db/repository/reviews.go | 53 + db/repository/store.go | 1 + db/repository/tx_location.go | 2 + docs/docs.go | 1212 +++++++++++++++++ docs/swagger.json | 1188 ++++++++++++++++ docs/swagger.yaml | 785 +++++++++++ go.mod | 25 +- go.sum | 60 + main.go | 12 +- 25 files changed, 3874 insertions(+), 169 deletions(-) create mode 100644 db/migrations/000013_added_amenities_and_added_multiple_grades_for_reviews.down.sql create mode 100644 db/migrations/000013_added_amenities_and_added_multiple_grades_for_reviews.up.sql create mode 100644 db/migrations/000014_create_menu_items_and_reviews.down.sql create mode 100644 db/migrations/000014_create_menu_items_and_reviews.up.sql create mode 100644 docs/docs.go create mode 100644 docs/swagger.json create mode 100644 docs/swagger.yaml diff --git a/Makefile b/Makefile index 5f0e55a..f78cd54 100755 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ migraterestart: $(MAKE) seed mock-generate: - mockgen -package mockdb -destination db/mock/store.go git.nochill.in/nochill/hiling_go/db/sqlc Store + mockgen -package mockdb -destination db/mock/store.go git.nochill.in/nochill/hiling_go/db/repository Store sqlc: sqlc generate && make mock-generate @@ -26,6 +26,9 @@ test: go test -v -cover ./... server: - go run main.go + go run main.go -.PHONY: server migrateup migratedown sqlc migraterestart \ No newline at end of file +swag: + swag init --generalInfo main.go --output docs + +.PHONY: server migrateup migratedown sqlc migraterestart swag \ No newline at end of file diff --git a/api/image.go b/api/image.go index 731d8b2..b5d3781 100755 --- a/api/image.go +++ b/api/image.go @@ -13,6 +13,15 @@ type getAllImagesReq struct { LocationId int32 `form:"location_id" binding:"required,numeric"` } +// @Summary Get all images for a location +// @Tags images +// @Produce json +// @Param location_id query int true "Location ID" +// @Param page query int true "Page (min 1)" +// @Param page_size query int true "Page size (min 5)" +// @Success 200 {object} map[string]any +// @Failure 400 {object} map[string]any +// @Router /images/location [get] func (server *Server) getAllImagesByLocation(ctx *gin.Context) { var req getAllImagesReq if err := ctx.BindQuery(&req); err != nil { diff --git a/api/location.go b/api/location.go index 110e86e..7a3837b 100755 --- a/api/location.go +++ b/api/location.go @@ -22,9 +22,25 @@ type createLocationReq struct { SubmittedBy int32 `form:"submitted_by" binding:"required,number"` RegencyID int16 `form:"regency_id" binding:"required,number"` LocationType string `form:"location_type" binding:"required"` + Amenities string `form:"amenities"` GoogleMapsLink string `form:"google_maps_link"` } +// @Summary Create a location +// @Tags locations +// @Accept mpfd +// @Produce json +// @Param address formData string true "Address" +// @Param name formData string true "Name" +// @Param submitted_by formData int true "Submitted by user ID" +// @Param regency_id formData int true "Regency ID" +// @Param location_type formData string true "Location type" +// @Param amenities formData string false "Amenities (comma-separated)" +// @Param google_maps_link formData string false "Google Maps link" +// @Param thumbnail formData file false "Thumbnail image" +// @Success 200 +// @Failure 400 {object} map[string]any +// @Router /locations [post] func (server *Server) createLocation(ctx *gin.Context) { var req createLocationReq var imgPath string @@ -65,6 +81,7 @@ func (server *Server) createLocation(ctx *gin.Context) { IsDeleted: false, ApprovedBy: pgtype.Int4{Int32: 0, Valid: false}, GoogleMapsLink: pgtype.Text{Valid: len(req.GoogleMapsLink) > 0, String: req.GoogleMapsLink}, + Amenities: req.Amenities, Thumbnail: tempImg, } @@ -86,6 +103,14 @@ type getListLocationsReq struct { PageSize int32 `form:"page_size" binding:"required,min=5"` } +// @Summary List locations +// @Tags locations +// @Produce json +// @Param page query int true "Page number (min 1)" +// @Param page_size query int true "Page size (min 5)" +// @Success 200 {array} map[string]any +// @Failure 400 {object} map[string]any +// @Router /locations [get] func (server *Server) getListLocations(ctx *gin.Context) { var req getListLocationsReq if err := ctx.ShouldBindQuery(&req); err != nil { @@ -114,6 +139,16 @@ type getTopListLocationsReq struct { RegionType int32 `form:"region_type" binding:"numeric,min=0,max=7"` } +// @Summary Top-rated locations +// @Tags locations +// @Produce json +// @Param page query int true "Page number (min 1)" +// @Param page_size query int true "Page size (min 5)" +// @Param order_by query int false "Sort: 1=overall, 2=critics, 3=users" +// @Param region_type query int false "Region type filter (0–7)" +// @Success 200 {array} map[string]any +// @Failure 400 {object} map[string]any +// @Router /locations/top-ratings [get] func (server *Server) getTopListLocations(ctx *gin.Context) { var req getTopListLocationsReq var orderby string @@ -165,6 +200,15 @@ type getListRecentLocationsWithRatingsReq struct { Region string `form:"regions"` } +// @Summary Recent locations with ratings +// @Tags locations +// @Produce json +// @Param page_size query int true "Number of results (min 1)" +// @Param location_type query string false "Filter by location type" +// @Param regions query string false "Filter by region" +// @Success 200 {array} map[string]any +// @Failure 400 {object} map[string]any +// @Router /locations/recent [get] func (server *Server) getListRecentLocationsWithRatings(ctx *gin.Context) { var req getListRecentLocationsWithRatingsReq @@ -189,15 +233,29 @@ func (server *Server) getListRecentLocationsWithRatings(ctx *gin.Context) { } type getLocationReq struct { - ID int32 `uri:"location_id" binding:"required"` + ID int32 `uri:"location_id" binding:"required"` + Review string `form:"review"` // "user", "critics", or "" (default = both) + Page int8 `form:"page"` // default 1 + PageSize int8 `form:"page_size"` // default 5 } +// @Summary Get location detail +// @Tags locations +// @Produce json +// @Param location_id path int true "Location ID" +// @Param review query string false "Review type: 'user', 'critics', or both (default)" +// @Param page query int false "Page (default 1)" +// @Param page_size query int false "Page size (default 5)" +// @Success 200 {object} map[string]any +// @Failure 404 {object} map[string]any +// @Router /location/{location_id} [get] func (server *Server) getLocation(ctx *gin.Context) { var req getLocationReq if err := ctx.ShouldBindUri(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } + ctx.ShouldBindQuery(&req) location, err := server.Store.GetLocation(ctx, req.ID) if err != nil { @@ -232,26 +290,46 @@ func (server *Server) getLocation(ctx *gin.Context) { return result } - 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 + fetchUsers := req.Review == "" || req.Review == "user" + fetchCritics := req.Review == "" || req.Review == "critics" + + page := req.Page + if page < 1 { + page = 1 + } + pageSize := req.PageSize + if pageSize < 1 { + pageSize = 5 + } + offset := (page - 1) * pageSize + + var users_reviews []db.GetListLocationReviewsRow + var critics_reviews []db.GetListLocationReviewsRow + + if fetchUsers { + users_reviews, err = server.Store.GetListLocationReviews(ctx, db.GetListLocationReviewsParams{ + LocationID: req.ID, + Limit: pageSize, + Offset: offset, + 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 + if fetchCritics { + critics_reviews, err = server.Store.GetListLocationReviews(ctx, db.GetListLocationReviewsParams{ + LocationID: req.ID, + Limit: pageSize, + Offset: offset, + 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{ @@ -276,6 +354,16 @@ type getListLocationReviewsRes struct { UsersReviews []db.GetListLocationReviewsRow `json:"users_reviews"` } +// @Summary List location reviews +// @Tags locations +// @Produce json +// @Param location_id query int true "Location ID" +// @Param page query int true "Page (min 1)" +// @Param page_size query int true "Page size" +// @Param type query int true "Review type: 1=critics, 2=users, 0=all" +// @Success 200 {object} map[string]any +// @Failure 400 {object} map[string]any +// @Router /location/reviews [get] func (server *Server) getListLocationReviews(ctx *gin.Context) { var req getListLocationReviewsReq var res getListLocationReviewsRes @@ -337,6 +425,14 @@ type searchLocationsParams struct { Filter string `form:"filter"` } +// @Summary Search locations +// @Tags locations +// @Produce json +// @Param name query string false "Search query" +// @Param filter query string false "Meilisearch filter expression" +// @Success 200 {array} map[string]any +// @Failure 500 {object} map[string]any +// @Router /locations/search [get] func (server *Server) searchLocations(ctx *gin.Context) { var req searchLocationsParams diff --git a/api/news_event.go b/api/news_event.go index 5825325..f24309f 100755 --- a/api/news_event.go +++ b/api/news_event.go @@ -16,6 +16,15 @@ type CreateNewsEventsReq struct { SubmittedBy int32 `json:"submitted_by" binding:"required,numeric"` } +// @Summary Create a news/event +// @Tags news +// @Accept json +// @Produce json +// @Security CookieAuth +// @Param body body CreateNewsEventsReq true "News/event payload" +// @Success 201 +// @Failure 400 {object} map[string]any +// @Router /news-events [post] func (server *Server) createNews(ctx *gin.Context) { var req CreateNewsEventsReq if err := ctx.ShouldBindJSON(&req); err != nil { @@ -44,6 +53,15 @@ type GetNewsEventsListReq struct { Approved int8 `form:"is_with_approval"` } +// @Summary List news and events +// @Tags news +// @Produce json +// @Param page query int true "Page (min 1)" +// @Param page_size query int true "Page size (min 5)" +// @Param is_with_approval query int false "1 = only approved items" +// @Success 200 {array} map[string]any +// @Failure 400 {object} map[string]any +// @Router /news-events [get] func (server *Server) GetNewsEventsList(ctx *gin.Context) { var req GetNewsEventsListReq isWithApproval := "" diff --git a/api/region.go b/api/region.go index c84ec12..194de1e 100755 --- a/api/region.go +++ b/api/region.go @@ -6,6 +6,11 @@ import ( "github.com/gin-gonic/gin" ) +// @Summary List all regions +// @Tags regions +// @Produce json +// @Success 200 {array} map[string]any +// @Router /regions [get] func (server *Server) getListRegions(ctx *gin.Context) { regions, err := server.Store.GetListRegions(ctx) if err != nil { @@ -16,6 +21,11 @@ func (server *Server) getListRegions(ctx *gin.Context) { ctx.JSON(http.StatusOK, regions) } +// @Summary List provinces +// @Tags regions +// @Produce json +// @Success 200 {array} map[string]any +// @Router /region/provinces [get] func (server *Server) getListProvinces(ctx *gin.Context) { provinces, err := server.Store.GetListProvinces(ctx) if err != nil { @@ -26,6 +36,11 @@ func (server *Server) getListProvinces(ctx *gin.Context) { ctx.JSON(http.StatusOK, provinces) } +// @Summary List regencies +// @Tags regions +// @Produce json +// @Success 200 {array} map[string]any +// @Router /region/regencies [get] func (server *Server) getListRegencies(ctx *gin.Context) { regencies, err := server.Store.GetListRegencies(ctx) if err != nil { diff --git a/api/reviews.go b/api/reviews.go index 47b55f2..592b829 100755 --- a/api/reviews.go +++ b/api/reviews.go @@ -26,6 +26,16 @@ type createReviewReq struct { Title string `json:"title"` } +// @Summary Create a review +// @Tags reviews +// @Accept json +// @Produce json +// @Security CookieAuth +// @Param body body createReviewReq true "Review payload" +// @Success 200 {object} map[string]any +// @Failure 400 {object} map[string]any +// @Failure 409 {object} map[string]any +// @Router /review/location [post] func (server *Server) createReview(ctx *gin.Context) { var req createReviewReq @@ -71,6 +81,16 @@ func (server *Server) createReview(ctx *gin.Context) { ctx.JSON(http.StatusOK, review) } +// @Summary Upload images for a review +// @Tags reviews +// @Accept mpfd +// @Produce json +// @Security CookieAuth +// @Param review_id formData int true "Review ID" +// @Param images formData file true "Image files" +// @Success 200 {object} map[string]any +// @Failure 400 {object} map[string]any +// @Router /review/location/images [post] func (server *Server) uploadReviewImages(ctx *gin.Context) { reviewIdStr := ctx.PostForm("review_id") reviewId, err := strconv.Atoi(reviewIdStr) @@ -133,6 +153,58 @@ func (server *Server) uploadReviewImages(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H{"uploaded": len(params)}) } +type getReviewReq struct { + LocationID int32 `uri:"location_id" binding:"required"` + ReviewID int32 `uri:"review_id" binding:"required"` +} + +type getReviewRes struct { + db.GetReviewByLocationAndId + Images []db.GetImagesByReviewRow `json:"images"` +} + +// @Summary Get a single review +// @Tags reviews +// @Produce json +// @Param location_id path int true "Location ID" +// @Param review_id path int true "Review ID" +// @Success 200 {object} map[string]any +// @Failure 404 {object} map[string]any +// @Router /location/{location_id}/review/{review_id} [get] +func (server *Server) getReview(ctx *gin.Context) { + var req getReviewReq + + if err := ctx.ShouldBindUri(&req); err != nil { + ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) + return + } + + arg := db.GetReviewByLocationAndIdParams{ + ID: req.ReviewID, + LocationID: req.LocationID, + } + + review, err := server.Store.GetReviewByLocationAndId(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 trying to retrieve review")) + return + } + + images, err := server.Store.GetImagesByReview(ctx, review.ID) + if err != nil { + images = []db.GetImagesByReviewRow{} + } + + ctx.JSON(http.StatusOK, getReviewRes{ + GetReviewByLocationAndId: review, + Images: images, + }) +} + type getUserReviewByLocationReq struct { LocationID int32 `uri:"location_id" binding:"required"` } @@ -142,6 +214,14 @@ type userReviewWithImagesResponse struct { Images []db.GetImagesByReviewRow `json:"images"` } +// @Summary Get current user's review for a location +// @Tags reviews +// @Produce json +// @Security CookieAuth +// @Param location_id path int true "Location ID" +// @Success 200 {object} map[string]any +// @Failure 404 {object} map[string]any +// @Router /user/review/location/{location_id} [get] func (server *Server) getUserReviewByLocation(ctx *gin.Context) { var req getUserReviewByLocationReq diff --git a/api/server.go b/api/server.go index 6b759f2..b32da25 100755 --- a/api/server.go +++ b/api/server.go @@ -10,6 +10,8 @@ import ( "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/meilisearch/meilisearch-go" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" ) type Server struct { @@ -80,6 +82,7 @@ func (server *Server) getRoutes() { router.GET("/location/:location_id", server.getLocation) router.GET("/location/tags/:location_id", server.getTagsByLocation) router.GET("/location/reviews", server.getListLocationReviews) + router.GET("/location/:location_id/review/:review_id", server.getReview) router.GET("/locations/search", server.searchLocations) //IMAGES @@ -88,6 +91,9 @@ func (server *Server) getRoutes() { // NEWS / EVENTS router.GET("/news-events", server.GetNewsEventsList) + // SWAGGER + router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) + // REQUIRE AUTH TOKEN authRoutes := router.Use(authMiddleware(server.TokenMaker)) authRoutes.POST("/review/location", server.createReview) diff --git a/api/tags.go b/api/tags.go index d4df584..4f2bf7b 100755 --- a/api/tags.go +++ b/api/tags.go @@ -10,6 +10,13 @@ type getTagsByLocationReq struct { LocationID int32 `uri:"location_id" binding:"required,numeric"` } +// @Summary Get tags for a location +// @Tags locations +// @Produce json +// @Param location_id path int true "Location ID" +// @Success 200 {array} map[string]any +// @Failure 400 {object} map[string]any +// @Router /location/tags/{location_id} [get] func (server *Server) getTagsByLocation(ctx *gin.Context) { var req getTagsByLocationReq diff --git a/api/user.go b/api/user.go index 2718df0..f2957b7 100755 --- a/api/user.go +++ b/api/user.go @@ -36,6 +36,15 @@ type createUserResponse struct { UpdatedAt time.Time `json:"updated_at"` } +// @Summary Register a new user +// @Tags auth +// @Accept json +// @Produce json +// @Param body body createUserRequest true "Signup payload" +// @Success 200 {object} map[string]any +// @Failure 400 {object} map[string]any +// @Failure 409 {object} map[string]any +// @Router /user/signup [post] func (server *Server) createUser(ctx *gin.Context) { var req createUserRequest if err := ctx.ShouldBindJSON(&req); err != nil { @@ -123,6 +132,13 @@ func (server *Server) createUser(ctx *gin.Context) { ctx.JSON(http.StatusOK, res) } +// @Summary Get current user profile and stats +// @Tags user +// @Produce json +// @Security CookieAuth +// @Success 200 {object} map[string]any +// @Failure 401 {object} map[string]any +// @Router /user/profile [get] func (server *Server) getUserStats(ctx *gin.Context) { var scoreDistribution []map[string]int8 authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload) @@ -164,6 +180,15 @@ type UpdateUserRequest struct { SocialMedia interface{} `json:"social_media"` } +// @Summary Update user profile +// @Tags user +// @Accept json +// @Produce json +// @Security CookieAuth +// @Param body body UpdateUserRequest true "Update payload" +// @Success 200 {object} map[string]any +// @Failure 400 {object} map[string]any +// @Router /user [patch] func (server *Server) updateUser(ctx *gin.Context) { var req UpdateUserRequest @@ -192,6 +217,15 @@ func (server *Server) updateUser(ctx *gin.Context) { ctx.JSON(http.StatusOK, user) } +// @Summary Upload user avatar +// @Tags user +// @Accept mpfd +// @Produce json +// @Security CookieAuth +// @Param file formData file true "Avatar image" +// @Success 200 {object} map[string]any +// @Failure 500 {object} map[string]any +// @Router /user/avatar [patch] func (server *Server) updateUserAvatar(ctx *gin.Context) { file, err := ctx.FormFile("file") @@ -230,6 +264,12 @@ func (server *Server) updateUserAvatar(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H{"image_url": url.String}) } +// @Summary Remove user avatar +// @Tags user +// @Security CookieAuth +// @Success 204 +// @Failure 500 {object} map[string]any +// @Router /user/avatar [delete] func (server *Server) removeAvatar(ctx *gin.Context) { authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload) @@ -246,6 +286,15 @@ func (server *Server) removeAvatar(ctx *gin.Context) { ctx.Writer.WriteHeader(http.StatusNoContent) } +// @Summary Login +// @Tags auth +// @Accept json +// @Produce json +// @Param body body createUserRequest true "Login credentials" +// @Success 200 {object} map[string]any +// @Failure 401 {object} map[string]any +// @Failure 404 {object} map[string]any +// @Router /user/login [post] func (server *Server) login(ctx *gin.Context) { var req createUserRequest @@ -309,6 +358,10 @@ func (server *Server) login(ctx *gin.Context) { ctx.JSON(http.StatusOK, user) } +// @Summary Logout +// @Tags auth +// @Success 204 +// @Router /user/logout [post] func (server *Server) logout(ctx *gin.Context) { ctx.SetCookie( "paseto", diff --git a/db/migrations/000005_alter_tags_column_approvedby_nullable.down.sql b/db/migrations/000005_alter_tags_column_approvedby_nullable.down.sql index e69de29..70763cc 100755 --- a/db/migrations/000005_alter_tags_column_approvedby_nullable.down.sql +++ b/db/migrations/000005_alter_tags_column_approvedby_nullable.down.sql @@ -0,0 +1 @@ +ALTER TABLE tags ALTER COLUMN approved_by SET NOT NULL; \ No newline at end of file diff --git a/db/migrations/000013_added_amenities_and_added_multiple_grades_for_reviews.down.sql b/db/migrations/000013_added_amenities_and_added_multiple_grades_for_reviews.down.sql new file mode 100644 index 0000000..54d8af4 --- /dev/null +++ b/db/migrations/000013_added_amenities_and_added_multiple_grades_for_reviews.down.sql @@ -0,0 +1,6 @@ +ALTER TABLE locations DROP COLUMN IF EXISTS amenities; +ALTER TABLE reviews DROP COLUMN IF EXISTS taste_grade; +ALTER TABLE reviews DROP COLUMN IF EXISTS comfort_grade; +ALTER TABLE reviews DROP COLUMN IF EXISTS cleanliness_grade; +ALTER TABLE reviews DROP COLUMN IF EXISTS service_grade; +ALTER TABLE reviews DROP COLUMN IF EXISTS facilities_grade; \ No newline at end of file diff --git a/db/migrations/000013_added_amenities_and_added_multiple_grades_for_reviews.up.sql b/db/migrations/000013_added_amenities_and_added_multiple_grades_for_reviews.up.sql new file mode 100644 index 0000000..ff934e2 --- /dev/null +++ b/db/migrations/000013_added_amenities_and_added_multiple_grades_for_reviews.up.sql @@ -0,0 +1,6 @@ +ALTER TABLE locations ADD COLUMN amenities jsonb; +ALTER TABLE reviews ADD COLUMN taste_grade DOUBLE PRECISION; +ALTER TABLE reviews ADD COLUMN comfort_grade DOUBLE PRECISION; +ALTER TABLE reviews ADD COLUMN cleanliness_grade DOUBLE PRECISION; +ALTER TABLE reviews ADD COLUMN service_grade DOUBLE PRECISION; +ALTER TABLE reviews ADD COLUMN facilities_grade DOUBLE PRECISION; diff --git a/db/migrations/000014_create_menu_items_and_reviews.down.sql b/db/migrations/000014_create_menu_items_and_reviews.down.sql new file mode 100644 index 0000000..7f6a613 --- /dev/null +++ b/db/migrations/000014_create_menu_items_and_reviews.down.sql @@ -0,0 +1,6 @@ +DROP TABLE IF EXISTS menu_item_reviews; +DROP TRIGGER IF EXISTS menu_item_price_change_trigger ON menu_items; +DROP FUNCTION IF EXISTS log_menu_item_price_change(); +DROP TABLE IF EXISTS menu_item_price_history; +DROP TABLE IF EXISTS menu_items; +DROP TYPE IF EXISTS menu_item_category; diff --git a/db/migrations/000014_create_menu_items_and_reviews.up.sql b/db/migrations/000014_create_menu_items_and_reviews.up.sql new file mode 100644 index 0000000..5150d58 --- /dev/null +++ b/db/migrations/000014_create_menu_items_and_reviews.up.sql @@ -0,0 +1,59 @@ +CREATE TYPE menu_item_category AS ENUM ( + 'dessert', + 'main_course', + 'beverages', + 'appetizer', + 'snack' +); + +CREATE TABLE menu_items ( + "id" serial primary key not null, + "location_id" integer references "locations"("id") not null, + "name" varchar not null, + "price" integer, + "category" menu_item_category, + "description" text, + "is_available" boolean default(true), + "is_deleted" boolean default(false), + "submitted_by" integer references "users"("id") not null, + "created_at" timestamp default(now()), + "updated_at" timestamp default(now()) +); + +CREATE TABLE menu_item_price_history ( + "id" serial primary key not null, + "menu_item_id" integer references "menu_items"("id") not null, + "price" integer not null, + "recorded_at" timestamp default(now()) +); + +CREATE OR REPLACE FUNCTION log_menu_item_price_change() +RETURNS TRIGGER AS $$ +BEGIN + IF OLD.price IS DISTINCT FROM NEW.price THEN + INSERT INTO menu_item_price_history (menu_item_id, price, recorded_at) + VALUES (OLD.id, OLD.price, now()); + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER menu_item_price_change_trigger +BEFORE UPDATE ON menu_items +FOR EACH ROW EXECUTE FUNCTION log_menu_item_price_change(); + +-- For menu item images, use the existing polymorphic `images` table: +-- image_type = 'menu_items', image_of = menu_item_id + +CREATE TABLE menu_item_reviews ( + "id" serial primary key not null, + "menu_item_id" integer references "menu_items"("id") not null, + "submitted_by" integer references "users"("id") not null, + "score" smallint not null, + "comments" text, + "is_hidden" boolean default(false), + "created_at" timestamp default(now()), + "updated_at" timestamp default(now()), + UNIQUE("menu_item_id", "submitted_by") +); + diff --git a/db/mock/store.go b/db/mock/store.go index 81288d1..ade02a4 100755 --- a/db/mock/store.go +++ b/db/mock/store.go @@ -1,9 +1,9 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: git.nochill.in/nochill/hiling_go/db/sqlc (interfaces: Store) +// Source: git.nochill.in/nochill/hiling_go/db/repository (interfaces: Store) // // Generated by this command: // -// mockgen -package mockdb -destination db/mock/store.go git.nochill.in/nochill/hiling_go/db/sqlc Store +// mockgen -package mockdb -destination db/mock/store.go git.nochill.in/nochill/hiling_go/db/repository Store // // Package mockdb is a generated GoMock package. @@ -22,6 +22,7 @@ import ( type MockStore struct { ctrl *gomock.Controller recorder *MockStoreMockRecorder + isgomock struct{} } // MockStoreMockRecorder is the mock recorder for MockStore. @@ -42,459 +43,474 @@ func (m *MockStore) EXPECT() *MockStoreMockRecorder { } // AddFollowUser mocks base method. -func (m *MockStore) AddFollowUser(arg0 context.Context, arg1 db.AddFollowUserParams) error { +func (m *MockStore) AddFollowUser(ctx context.Context, arg db.AddFollowUserParams) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AddFollowUser", arg0, arg1) + ret := m.ctrl.Call(m, "AddFollowUser", ctx, arg) ret0, _ := ret[0].(error) return ret0 } // AddFollowUser indicates an expected call of AddFollowUser. -func (mr *MockStoreMockRecorder) AddFollowUser(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) AddFollowUser(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFollowUser", reflect.TypeOf((*MockStore)(nil).AddFollowUser), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFollowUser", reflect.TypeOf((*MockStore)(nil).AddFollowUser), ctx, arg) } // CheckIfReviewExists mocks base method. -func (m *MockStore) CheckIfReviewExists(arg0 context.Context, arg1 db.CheckIfReviewExistsParams) (int64, error) { +func (m *MockStore) CheckIfReviewExists(ctx context.Context, arg db.CheckIfReviewExistsParams) (int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckIfReviewExists", arg0, arg1) + ret := m.ctrl.Call(m, "CheckIfReviewExists", ctx, arg) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // CheckIfReviewExists indicates an expected call of CheckIfReviewExists. -func (mr *MockStoreMockRecorder) CheckIfReviewExists(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) CheckIfReviewExists(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckIfReviewExists", reflect.TypeOf((*MockStore)(nil).CheckIfReviewExists), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckIfReviewExists", reflect.TypeOf((*MockStore)(nil).CheckIfReviewExists), ctx, arg) } // CreateImage mocks base method. -func (m *MockStore) CreateImage(arg0 context.Context, arg1 []db.CreateImageParams) error { +func (m *MockStore) CreateImage(ctx context.Context, arg []db.CreateImageParams) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateImage", arg0, arg1) + ret := m.ctrl.Call(m, "CreateImage", ctx, arg) ret0, _ := ret[0].(error) return ret0 } // CreateImage indicates an expected call of CreateImage. -func (mr *MockStoreMockRecorder) CreateImage(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateImage(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateImage", reflect.TypeOf((*MockStore)(nil).CreateImage), arg0, arg1) -} - -// GetImagesByReview mocks base method. -func (m *MockStore) GetImagesByReview(arg0 context.Context, arg1 int32) ([]db.GetImagesByReviewRow, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetImagesByReview", arg0, arg1) - ret0, _ := ret[0].([]db.GetImagesByReviewRow) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetImagesByReview indicates an expected call of GetImagesByReview. -func (mr *MockStoreMockRecorder) GetImagesByReview(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetImagesByReview", reflect.TypeOf((*MockStore)(nil).GetImagesByReview), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateImage", reflect.TypeOf((*MockStore)(nil).CreateImage), ctx, arg) } // CreateLocation mocks base method. -func (m *MockStore) CreateLocation(arg0 context.Context, arg1 db.CreateLocationParams) (int32, error) { +func (m *MockStore) CreateLocation(ctx context.Context, arg db.CreateLocationParams) (int32, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateLocation", arg0, arg1) + ret := m.ctrl.Call(m, "CreateLocation", ctx, arg) ret0, _ := ret[0].(int32) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateLocation indicates an expected call of CreateLocation. -func (mr *MockStoreMockRecorder) CreateLocation(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateLocation(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLocation", reflect.TypeOf((*MockStore)(nil).CreateLocation), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLocation", reflect.TypeOf((*MockStore)(nil).CreateLocation), ctx, arg) } // CreateLocationTx mocks base method. -func (m *MockStore) CreateLocationTx(arg0 context.Context, arg1 db.CreateLocationTxParams) error { +func (m *MockStore) CreateLocationTx(ctx context.Context, arg db.CreateLocationTxParams) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateLocationTx", arg0, arg1) + ret := m.ctrl.Call(m, "CreateLocationTx", ctx, arg) ret0, _ := ret[0].(error) return ret0 } // CreateLocationTx indicates an expected call of CreateLocationTx. -func (mr *MockStoreMockRecorder) CreateLocationTx(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateLocationTx(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLocationTx", reflect.TypeOf((*MockStore)(nil).CreateLocationTx), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLocationTx", reflect.TypeOf((*MockStore)(nil).CreateLocationTx), ctx, arg) } // CreateNewsEvents mocks base method. -func (m *MockStore) CreateNewsEvents(arg0 context.Context, arg1 db.CreateNewsEventsParams) error { +func (m *MockStore) CreateNewsEvents(ctx context.Context, arg db.CreateNewsEventsParams) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateNewsEvents", arg0, arg1) + ret := m.ctrl.Call(m, "CreateNewsEvents", ctx, arg) ret0, _ := ret[0].(error) return ret0 } // CreateNewsEvents indicates an expected call of CreateNewsEvents. -func (mr *MockStoreMockRecorder) CreateNewsEvents(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateNewsEvents(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewsEvents", reflect.TypeOf((*MockStore)(nil).CreateNewsEvents), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewsEvents", reflect.TypeOf((*MockStore)(nil).CreateNewsEvents), ctx, arg) } // CreateReview mocks base method. -func (m *MockStore) CreateReview(arg0 context.Context, arg1 db.CreateReviewParams) (db.Review, error) { +func (m *MockStore) CreateReview(ctx context.Context, arg db.CreateReviewParams) (db.Review, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateReview", arg0, arg1) + ret := m.ctrl.Call(m, "CreateReview", ctx, arg) ret0, _ := ret[0].(db.Review) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateReview indicates an expected call of CreateReview. -func (mr *MockStoreMockRecorder) CreateReview(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateReview(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateReview", reflect.TypeOf((*MockStore)(nil).CreateReview), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateReview", reflect.TypeOf((*MockStore)(nil).CreateReview), ctx, arg) } // CreateSession mocks base method. -func (m *MockStore) CreateSession(arg0 context.Context, arg1 db.CreateSessionParams) (db.UserSession, error) { +func (m *MockStore) CreateSession(ctx context.Context, arg db.CreateSessionParams) (db.UserSession, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateSession", arg0, arg1) + ret := m.ctrl.Call(m, "CreateSession", ctx, arg) ret0, _ := ret[0].(db.UserSession) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateSession indicates an expected call of CreateSession. -func (mr *MockStoreMockRecorder) CreateSession(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateSession(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSession", reflect.TypeOf((*MockStore)(nil).CreateSession), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSession", reflect.TypeOf((*MockStore)(nil).CreateSession), ctx, arg) } // CreateUser mocks base method. -func (m *MockStore) CreateUser(arg0 context.Context, arg1 db.CreateUserParams) (db.User, error) { +func (m *MockStore) CreateUser(ctx context.Context, arg db.CreateUserParams) (db.User, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateUser", arg0, arg1) + ret := m.ctrl.Call(m, "CreateUser", ctx, arg) ret0, _ := ret[0].(db.User) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateUser indicates an expected call of CreateUser. -func (mr *MockStoreMockRecorder) CreateUser(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) CreateUser(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockStore)(nil).CreateUser), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockStore)(nil).CreateUser), ctx, arg) } // GetCountImageByLocation mocks base method. -func (m *MockStore) GetCountImageByLocation(arg0 context.Context, arg1 int32) (int64, error) { +func (m *MockStore) GetCountImageByLocation(ctx context.Context, imageOf int32) (int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCountImageByLocation", arg0, arg1) + ret := m.ctrl.Call(m, "GetCountImageByLocation", ctx, imageOf) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // GetCountImageByLocation indicates an expected call of GetCountImageByLocation. -func (mr *MockStoreMockRecorder) GetCountImageByLocation(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetCountImageByLocation(ctx, imageOf any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCountImageByLocation", reflect.TypeOf((*MockStore)(nil).GetCountImageByLocation), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCountImageByLocation", reflect.TypeOf((*MockStore)(nil).GetCountImageByLocation), ctx, imageOf) } // GetImagesByLocation mocks base method. -func (m *MockStore) GetImagesByLocation(arg0 context.Context, arg1 db.GetImagesByLocationParams) ([]db.GetImagesByLocationRow, error) { +func (m *MockStore) GetImagesByLocation(ctx context.Context, arg db.GetImagesByLocationParams) ([]db.GetImagesByLocationRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetImagesByLocation", arg0, arg1) + ret := m.ctrl.Call(m, "GetImagesByLocation", ctx, arg) ret0, _ := ret[0].([]db.GetImagesByLocationRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetImagesByLocation indicates an expected call of GetImagesByLocation. -func (mr *MockStoreMockRecorder) GetImagesByLocation(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetImagesByLocation(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetImagesByLocation", reflect.TypeOf((*MockStore)(nil).GetImagesByLocation), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetImagesByLocation", reflect.TypeOf((*MockStore)(nil).GetImagesByLocation), ctx, arg) +} + +// GetImagesByReview mocks base method. +func (m *MockStore) GetImagesByReview(ctx context.Context, reviewID int32) ([]db.GetImagesByReviewRow, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetImagesByReview", ctx, reviewID) + ret0, _ := ret[0].([]db.GetImagesByReviewRow) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetImagesByReview indicates an expected call of GetImagesByReview. +func (mr *MockStoreMockRecorder) GetImagesByReview(ctx, reviewID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetImagesByReview", reflect.TypeOf((*MockStore)(nil).GetImagesByReview), ctx, reviewID) } // GetListLocationReviews mocks base method. -func (m *MockStore) GetListLocationReviews(arg0 context.Context, arg1 db.GetListLocationReviewsParams) ([]db.GetListLocationReviewsRow, error) { +func (m *MockStore) GetListLocationReviews(ctx context.Context, arg db.GetListLocationReviewsParams) ([]db.GetListLocationReviewsRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetListLocationReviews", arg0, arg1) + ret := m.ctrl.Call(m, "GetListLocationReviews", ctx, arg) ret0, _ := ret[0].([]db.GetListLocationReviewsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetListLocationReviews indicates an expected call of GetListLocationReviews. -func (mr *MockStoreMockRecorder) GetListLocationReviews(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetListLocationReviews(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListLocationReviews", reflect.TypeOf((*MockStore)(nil).GetListLocationReviews), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListLocationReviews", reflect.TypeOf((*MockStore)(nil).GetListLocationReviews), ctx, arg) } // GetListLocations mocks base method. -func (m *MockStore) GetListLocations(arg0 context.Context) ([]db.Location, error) { +func (m *MockStore) GetListLocations(ctx context.Context) ([]db.Location, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetListLocations", arg0) + ret := m.ctrl.Call(m, "GetListLocations", ctx) ret0, _ := ret[0].([]db.Location) ret1, _ := ret[1].(error) return ret0, ret1 } // GetListLocations indicates an expected call of GetListLocations. -func (mr *MockStoreMockRecorder) GetListLocations(arg0 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetListLocations(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListLocations", reflect.TypeOf((*MockStore)(nil).GetListLocations), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListLocations", reflect.TypeOf((*MockStore)(nil).GetListLocations), ctx) } // GetListProvinces mocks base method. -func (m *MockStore) GetListProvinces(arg0 context.Context) ([]db.GetListProvincesRow, error) { +func (m *MockStore) GetListProvinces(ctx context.Context) ([]db.GetListProvincesRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetListProvinces", arg0) + ret := m.ctrl.Call(m, "GetListProvinces", ctx) ret0, _ := ret[0].([]db.GetListProvincesRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetListProvinces indicates an expected call of GetListProvinces. -func (mr *MockStoreMockRecorder) GetListProvinces(arg0 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetListProvinces(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListProvinces", reflect.TypeOf((*MockStore)(nil).GetListProvinces), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListProvinces", reflect.TypeOf((*MockStore)(nil).GetListProvinces), ctx) } // GetListRecentLocationsWithRatings mocks base method. -func (m *MockStore) GetListRecentLocationsWithRatings(arg0 context.Context, arg1 db.GetListRecentLocationsParams) ([]db.GetListRecentLocationsWithRatingsRow, error) { +func (m *MockStore) GetListRecentLocationsWithRatings(ctx context.Context, arg db.GetListRecentLocationsParams) ([]db.GetListRecentLocationsWithRatingsRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetListRecentLocationsWithRatings", arg0, arg1) + ret := m.ctrl.Call(m, "GetListRecentLocationsWithRatings", ctx, arg) ret0, _ := ret[0].([]db.GetListRecentLocationsWithRatingsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetListRecentLocationsWithRatings indicates an expected call of GetListRecentLocationsWithRatings. -func (mr *MockStoreMockRecorder) GetListRecentLocationsWithRatings(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetListRecentLocationsWithRatings(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRecentLocationsWithRatings", reflect.TypeOf((*MockStore)(nil).GetListRecentLocationsWithRatings), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRecentLocationsWithRatings", reflect.TypeOf((*MockStore)(nil).GetListRecentLocationsWithRatings), ctx, arg) } // GetListRegencies mocks base method. -func (m *MockStore) GetListRegencies(arg0 context.Context) ([]db.GetListRegenciesRow, error) { +func (m *MockStore) GetListRegencies(ctx context.Context) ([]db.GetListRegenciesRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetListRegencies", arg0) + ret := m.ctrl.Call(m, "GetListRegencies", ctx) ret0, _ := ret[0].([]db.GetListRegenciesRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetListRegencies indicates an expected call of GetListRegencies. -func (mr *MockStoreMockRecorder) GetListRegencies(arg0 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetListRegencies(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRegencies", reflect.TypeOf((*MockStore)(nil).GetListRegencies), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRegencies", reflect.TypeOf((*MockStore)(nil).GetListRegencies), ctx) } // GetListRegions mocks base method. -func (m *MockStore) GetListRegions(arg0 context.Context) ([]db.GetListRegionsRow, error) { +func (m *MockStore) GetListRegions(ctx context.Context) ([]db.GetListRegionsRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetListRegions", arg0) + ret := m.ctrl.Call(m, "GetListRegions", ctx) ret0, _ := ret[0].([]db.GetListRegionsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetListRegions indicates an expected call of GetListRegions. -func (mr *MockStoreMockRecorder) GetListRegions(arg0 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetListRegions(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRegions", reflect.TypeOf((*MockStore)(nil).GetListRegions), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRegions", reflect.TypeOf((*MockStore)(nil).GetListRegions), ctx) } // GetLocation mocks base method. -func (m *MockStore) GetLocation(arg0 context.Context, arg1 int32) (db.GetLocationRow, error) { +func (m *MockStore) GetLocation(ctx context.Context, location_id int32) (db.GetLocationRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLocation", arg0, arg1) + ret := m.ctrl.Call(m, "GetLocation", ctx, location_id) ret0, _ := ret[0].(db.GetLocationRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLocation indicates an expected call of GetLocation. -func (mr *MockStoreMockRecorder) GetLocation(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetLocation(ctx, location_id any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocation", reflect.TypeOf((*MockStore)(nil).GetLocation), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocation", reflect.TypeOf((*MockStore)(nil).GetLocation), ctx, location_id) } // GetLocationTag mocks base method. -func (m *MockStore) GetLocationTag(arg0 context.Context, arg1 int32) ([]string, error) { +func (m *MockStore) GetLocationTag(ctx context.Context, targetID int32) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLocationTag", arg0, arg1) + ret := m.ctrl.Call(m, "GetLocationTag", ctx, targetID) ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } // GetLocationTag indicates an expected call of GetLocationTag. -func (mr *MockStoreMockRecorder) GetLocationTag(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetLocationTag(ctx, targetID any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocationTag", reflect.TypeOf((*MockStore)(nil).GetLocationTag), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocationTag", reflect.TypeOf((*MockStore)(nil).GetLocationTag), ctx, targetID) } // GetNewsEventsList mocks base method. -func (m *MockStore) GetNewsEventsList(arg0 context.Context, arg1 db.GetNewsEventsListParams) ([]db.NewsEventRow, error) { +func (m *MockStore) GetNewsEventsList(ctx context.Context, arg db.GetNewsEventsListParams) ([]db.NewsEventRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetNewsEventsList", arg0, arg1) + ret := m.ctrl.Call(m, "GetNewsEventsList", ctx, arg) ret0, _ := ret[0].([]db.NewsEventRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetNewsEventsList indicates an expected call of GetNewsEventsList. -func (mr *MockStoreMockRecorder) GetNewsEventsList(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetNewsEventsList(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNewsEventsList", reflect.TypeOf((*MockStore)(nil).GetNewsEventsList), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNewsEventsList", reflect.TypeOf((*MockStore)(nil).GetNewsEventsList), ctx, arg) +} + +// GetReviewByLocationAndId mocks base method. +func (m *MockStore) GetReviewByLocationAndId(ctx context.Context, arg db.GetReviewByLocationAndIdParams) (db.GetReviewByLocationAndId, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetReviewByLocationAndId", ctx, arg) + ret0, _ := ret[0].(db.GetReviewByLocationAndId) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetReviewByLocationAndId indicates an expected call of GetReviewByLocationAndId. +func (mr *MockStoreMockRecorder) GetReviewByLocationAndId(ctx, arg any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReviewByLocationAndId", reflect.TypeOf((*MockStore)(nil).GetReviewByLocationAndId), ctx, arg) } // GetSession mocks base method. -func (m *MockStore) GetSession(arg0 context.Context, arg1 int32) (db.UserSession, error) { +func (m *MockStore) GetSession(ctx context.Context, id int32) (db.UserSession, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSession", arg0, arg1) + ret := m.ctrl.Call(m, "GetSession", ctx, id) ret0, _ := ret[0].(db.UserSession) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSession indicates an expected call of GetSession. -func (mr *MockStoreMockRecorder) GetSession(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetSession(ctx, id any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSession", reflect.TypeOf((*MockStore)(nil).GetSession), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSession", reflect.TypeOf((*MockStore)(nil).GetSession), ctx, id) } // GetTopListLocations mocks base method. -func (m *MockStore) GetTopListLocations(arg0 context.Context, arg1 db.GetTopListLocationsParams) ([]db.GetTopListLocationsRow, error) { +func (m *MockStore) GetTopListLocations(ctx context.Context, arg db.GetTopListLocationsParams) ([]db.GetTopListLocationsRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTopListLocations", arg0, arg1) + ret := m.ctrl.Call(m, "GetTopListLocations", ctx, arg) ret0, _ := ret[0].([]db.GetTopListLocationsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetTopListLocations indicates an expected call of GetTopListLocations. -func (mr *MockStoreMockRecorder) GetTopListLocations(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetTopListLocations(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTopListLocations", reflect.TypeOf((*MockStore)(nil).GetTopListLocations), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTopListLocations", reflect.TypeOf((*MockStore)(nil).GetTopListLocations), ctx, arg) } // GetUser mocks base method. -func (m *MockStore) GetUser(arg0 context.Context, arg1 string) (db.GetUserRow, error) { +func (m *MockStore) GetUser(ctx context.Context, username string) (db.GetUserRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUser", arg0, arg1) + ret := m.ctrl.Call(m, "GetUser", ctx, username) ret0, _ := ret[0].(db.GetUserRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetUser indicates an expected call of GetUser. -func (mr *MockStoreMockRecorder) GetUser(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetUser(ctx, username any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUser", reflect.TypeOf((*MockStore)(nil).GetUser), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUser", reflect.TypeOf((*MockStore)(nil).GetUser), ctx, username) } // GetUserReviewByLocation mocks base method. -func (m *MockStore) GetUserReviewByLocation(arg0 context.Context, arg1 db.GetUserReviewByLocationParams) (db.GetUserReviewByLocationRow, error) { +func (m *MockStore) GetUserReviewByLocation(ctx context.Context, arg db.GetUserReviewByLocationParams) (db.GetUserReviewByLocationRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUserReviewByLocation", arg0, arg1) + ret := m.ctrl.Call(m, "GetUserReviewByLocation", ctx, arg) ret0, _ := ret[0].(db.GetUserReviewByLocationRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetUserReviewByLocation indicates an expected call of GetUserReviewByLocation. -func (mr *MockStoreMockRecorder) GetUserReviewByLocation(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetUserReviewByLocation(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserReviewByLocation", reflect.TypeOf((*MockStore)(nil).GetUserReviewByLocation), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserReviewByLocation", reflect.TypeOf((*MockStore)(nil).GetUserReviewByLocation), ctx, arg) } // GetUserStats mocks base method. -func (m *MockStore) GetUserStats(arg0 context.Context, arg1 int32) (db.GetUserStatsRow, error) { +func (m *MockStore) GetUserStats(ctx context.Context, user_id int32) (db.GetUserStatsRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetUserStats", arg0, arg1) + ret := m.ctrl.Call(m, "GetUserStats", ctx, user_id) ret0, _ := ret[0].(db.GetUserStatsRow) ret1, _ := ret[1].(error) return ret0, ret1 } // GetUserStats indicates an expected call of GetUserStats. -func (mr *MockStoreMockRecorder) GetUserStats(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) GetUserStats(ctx, user_id any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserStats", reflect.TypeOf((*MockStore)(nil).GetUserStats), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserStats", reflect.TypeOf((*MockStore)(nil).GetUserStats), ctx, user_id) } // RemoveFollowUser mocks base method. -func (m *MockStore) RemoveFollowUser(arg0 context.Context, arg1 db.RemoveFollowUserParams) error { +func (m *MockStore) RemoveFollowUser(ctx context.Context, arg db.RemoveFollowUserParams) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RemoveFollowUser", arg0, arg1) + ret := m.ctrl.Call(m, "RemoveFollowUser", ctx, arg) ret0, _ := ret[0].(error) return ret0 } // RemoveFollowUser indicates an expected call of RemoveFollowUser. -func (mr *MockStoreMockRecorder) RemoveFollowUser(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) RemoveFollowUser(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveFollowUser", reflect.TypeOf((*MockStore)(nil).RemoveFollowUser), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveFollowUser", reflect.TypeOf((*MockStore)(nil).RemoveFollowUser), ctx, arg) } // UpdateAvatar mocks base method. -func (m *MockStore) UpdateAvatar(arg0 context.Context, arg1 db.UpdateAvatarParams) (pgtype.Text, error) { +func (m *MockStore) UpdateAvatar(ctx context.Context, arg db.UpdateAvatarParams) (pgtype.Text, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateAvatar", arg0, arg1) + ret := m.ctrl.Call(m, "UpdateAvatar", ctx, arg) ret0, _ := ret[0].(pgtype.Text) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateAvatar indicates an expected call of UpdateAvatar. -func (mr *MockStoreMockRecorder) UpdateAvatar(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateAvatar(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAvatar", reflect.TypeOf((*MockStore)(nil).UpdateAvatar), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAvatar", reflect.TypeOf((*MockStore)(nil).UpdateAvatar), ctx, arg) } // UpdateLocationThumbnail mocks base method. -func (m *MockStore) UpdateLocationThumbnail(arg0 context.Context, arg1 db.UpdateLocationThumbnailParams) error { +func (m *MockStore) UpdateLocationThumbnail(ctx context.Context, arg db.UpdateLocationThumbnailParams) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateLocationThumbnail", arg0, arg1) + ret := m.ctrl.Call(m, "UpdateLocationThumbnail", ctx, arg) ret0, _ := ret[0].(error) return ret0 } // UpdateLocationThumbnail indicates an expected call of UpdateLocationThumbnail. -func (mr *MockStoreMockRecorder) UpdateLocationThumbnail(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateLocationThumbnail(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateLocationThumbnail", reflect.TypeOf((*MockStore)(nil).UpdateLocationThumbnail), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateLocationThumbnail", reflect.TypeOf((*MockStore)(nil).UpdateLocationThumbnail), ctx, arg) } // UpdatePassword mocks base method. -func (m *MockStore) UpdatePassword(arg0 context.Context, arg1 db.UpdatePasswordParams) error { +func (m *MockStore) UpdatePassword(ctx context.Context, arg db.UpdatePasswordParams) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdatePassword", arg0, arg1) + ret := m.ctrl.Call(m, "UpdatePassword", ctx, arg) ret0, _ := ret[0].(error) return ret0 } // UpdatePassword indicates an expected call of UpdatePassword. -func (mr *MockStoreMockRecorder) UpdatePassword(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdatePassword(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePassword", reflect.TypeOf((*MockStore)(nil).UpdatePassword), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePassword", reflect.TypeOf((*MockStore)(nil).UpdatePassword), ctx, arg) } // UpdateUser mocks base method. -func (m *MockStore) UpdateUser(arg0 context.Context, arg1 db.UpdateUserParams) (db.UpdateUserRow, error) { +func (m *MockStore) UpdateUser(ctx context.Context, arg db.UpdateUserParams) (db.UpdateUserRow, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateUser", arg0, arg1) + ret := m.ctrl.Call(m, "UpdateUser", ctx, arg) ret0, _ := ret[0].(db.UpdateUserRow) ret1, _ := ret[1].(error) return ret0, ret1 } // UpdateUser indicates an expected call of UpdateUser. -func (mr *MockStoreMockRecorder) UpdateUser(arg0, arg1 any) *gomock.Call { +func (mr *MockStoreMockRecorder) UpdateUser(ctx, arg any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUser", reflect.TypeOf((*MockStore)(nil).UpdateUser), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUser", reflect.TypeOf((*MockStore)(nil).UpdateUser), ctx, arg) } diff --git a/db/repository/locations.go b/db/repository/locations.go index 7a3fed4..d4762b3 100755 --- a/db/repository/locations.go +++ b/db/repository/locations.go @@ -211,6 +211,7 @@ type GetLocationRow struct { CriticCount int32 `json:"critic_count"` UserScore int32 `json:"user_score"` UserCount int32 `json:"user_count"` + Amenities string `json:"amenities"` } var getLocationQ = ` @@ -268,9 +269,10 @@ INSERT INTO locations( regency_id, google_maps_link, approved_by, + amenities, is_deleted ) values ( - $1, $2, $3, $4, $5, $6, $7, $8 + $1, $2, $3, $4, $5, $6, $7, $8, $9 ) RETURNING id ` @@ -284,6 +286,7 @@ type CreateLocationParams struct { GoogleMapsLink pgtype.Text `json:"google_maps_link"` IsDeleted bool `json:"is_deleted"` ApprovedBy pgtype.Int4 `json:"approved_by"` + Amenities string `json:"amenities"` } func (q *Queries) CreateLocation(ctx context.Context, arg CreateLocationParams) (int32, error) { @@ -295,6 +298,7 @@ func (q *Queries) CreateLocation(ctx context.Context, arg CreateLocationParams) arg.RegencyID, arg.GoogleMapsLink, arg.ApprovedBy, + arg.Amenities, arg.IsDeleted, ) diff --git a/db/repository/reviews.go b/db/repository/reviews.go index ee2a4e1..f14286c 100755 --- a/db/repository/reviews.go +++ b/db/repository/reviews.go @@ -93,6 +93,59 @@ LIMIT $3 OFFSET $4; ` +type GetReviewByLocationAndIdParams struct { + ID int32 `json:"id"` + LocationID int32 `json:"location_id"` +} + +type GetReviewByLocationAndId struct { + ID int32 `json:"id"` + Title pgtype.Text `json:"title"` + SubmittedBy int32 `json:"submitted_by"` + LocationID int32 `json:"location_id"` + Score int16 `json:"score"` + Comments string `json:"comments"` + CreatedAt pgtype.Timestamp `json:"created_at"` + UpdatedAt pgtype.Timestamp `json:"updated_at"` + Username string `json:"username"` + UserAvatar pgtype.Text `json:"user_avatar"` +} + +const getReviewByLocationAndId = ` +SELECT + re.id, + re.title, + re.submitted_by, + re.location_id, + re.score, + re.comments, + re.created_at, + re.updated_at, + u.username, + u.avatar_picture +FROM reviews re +JOIN users u on re.submitted_by = u.id +WHERE re.id = $1 AND re.location_id = $2 AND re.is_hided = false; +` + +func (q *Queries) GetReviewByLocationAndId(ctx context.Context, arg GetReviewByLocationAndIdParams) (GetReviewByLocationAndId, error) { + row := q.db.QueryRow(ctx, getReviewByLocationAndId, arg.ID, arg.LocationID) + var i GetReviewByLocationAndId + err := row.Scan( + &i.ID, + &i.Title, + &i.SubmittedBy, + &i.LocationID, + &i.Score, + &i.Comments, + &i.CreatedAt, + &i.UpdatedAt, + &i.Username, + &i.UserAvatar, + ) + return i, err +} + func (q *Queries) GetListLocationReviews(ctx context.Context, arg GetListLocationReviewsParams) ([]GetListLocationReviewsRow, error) { rows, err := q.db.Query(ctx, getListLocationReviews, arg.LocationID, arg.IsCritics, arg.Limit, arg.Offset) if err != nil { diff --git a/db/repository/store.go b/db/repository/store.go index c78c52f..16b5c0f 100755 --- a/db/repository/store.go +++ b/db/repository/store.go @@ -23,6 +23,7 @@ type Store interface { CreateLocationTx(ctx context.Context, arg CreateLocationTxParams) error GetNewsEventsList(ctx context.Context, arg GetNewsEventsListParams) ([]NewsEventRow, error) GetListRecentLocationsWithRatings(ctx context.Context, arg GetListRecentLocationsParams) ([]GetListRecentLocationsWithRatingsRow, error) + GetReviewByLocationAndId(ctx context.Context, arg GetReviewByLocationAndIdParams) (GetReviewByLocationAndId, error) } type SQLStore struct { diff --git a/db/repository/tx_location.go b/db/repository/tx_location.go index 6abf91c..a7b1f30 100755 --- a/db/repository/tx_location.go +++ b/db/repository/tx_location.go @@ -12,6 +12,7 @@ type CreateLocationTxParams struct { SubmittedBy int32 `json:"submitted_by"` LocationType LocationType `json:"location_type"` RegencyID int16 `json:"regency_id"` + Amenities string `json:"amenities"` GoogleMapsLink pgtype.Text `json:"google_maps_link"` IsDeleted bool `json:"is_deleted"` ApprovedBy pgtype.Int4 `json:"approved_by"` @@ -31,6 +32,7 @@ func (store *SQLStore) CreateLocationTx(ctx context.Context, arg CreateLocationT GoogleMapsLink: arg.GoogleMapsLink, IsDeleted: arg.IsDeleted, ApprovedBy: arg.ApprovedBy, + Amenities: arg.Amenities, }) if err != nil { diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 0000000..e0ed6e9 --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,1212 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/images/location": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "images" + ], + "summary": "Get all images for a location", + "parameters": [ + { + "type": "integer", + "description": "Location ID", + "name": "location_id", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page (min 1)", + "name": "page", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page size (min 5)", + "name": "page_size", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/location/reviews": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "List location reviews", + "parameters": [ + { + "type": "integer", + "description": "Location ID", + "name": "location_id", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page (min 1)", + "name": "page", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page size", + "name": "page_size", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Review type: 1=critics, 2=users, 0=all", + "name": "type", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/location/tags/{location_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "Get tags for a location", + "parameters": [ + { + "type": "integer", + "description": "Location ID", + "name": "location_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/location/{location_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "Get location detail", + "parameters": [ + { + "type": "integer", + "description": "Location ID", + "name": "location_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Review type: 'user', 'critics', or both (default)", + "name": "review", + "in": "query" + }, + { + "type": "integer", + "description": "Page (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 5)", + "name": "page_size", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/location/{location_id}/review/{review_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "reviews" + ], + "summary": "Get a single review", + "parameters": [ + { + "type": "integer", + "description": "Location ID", + "name": "location_id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Review ID", + "name": "review_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/locations": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "List locations", + "parameters": [ + { + "type": "integer", + "description": "Page number (min 1)", + "name": "page", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page size (min 5)", + "name": "page_size", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "post": { + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "Create a location", + "parameters": [ + { + "type": "string", + "description": "Address", + "name": "address", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Name", + "name": "name", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Submitted by user ID", + "name": "submitted_by", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Regency ID", + "name": "regency_id", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Location type", + "name": "location_type", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Amenities (comma-separated)", + "name": "amenities", + "in": "formData" + }, + { + "type": "string", + "description": "Google Maps link", + "name": "google_maps_link", + "in": "formData" + }, + { + "type": "file", + "description": "Thumbnail image", + "name": "thumbnail", + "in": "formData" + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/locations/recent": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "Recent locations with ratings", + "parameters": [ + { + "type": "integer", + "description": "Number of results (min 1)", + "name": "page_size", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Filter by location type", + "name": "location_type", + "in": "query" + }, + { + "type": "string", + "description": "Filter by region", + "name": "regions", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/locations/search": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "Search locations", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "name", + "in": "query" + }, + { + "type": "string", + "description": "Meilisearch filter expression", + "name": "filter", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/locations/top-ratings": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "Top-rated locations", + "parameters": [ + { + "type": "integer", + "description": "Page number (min 1)", + "name": "page", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page size (min 5)", + "name": "page_size", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Sort: 1=overall, 2=critics, 3=users", + "name": "order_by", + "in": "query" + }, + { + "type": "integer", + "description": "Region type filter (0–7)", + "name": "region_type", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/news-events": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "news" + ], + "summary": "List news and events", + "parameters": [ + { + "type": "integer", + "description": "Page (min 1)", + "name": "page", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page size (min 5)", + "name": "page_size", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "1 = only approved items", + "name": "is_with_approval", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "post": { + "security": [ + { + "CookieAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "news" + ], + "summary": "Create a news/event", + "parameters": [ + { + "description": "News/event payload", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.CreateNewsEventsReq" + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/region/provinces": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "regions" + ], + "summary": "List provinces", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + } + } + } + }, + "/region/regencies": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "regions" + ], + "summary": "List regencies", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + } + } + } + }, + "/regions": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "regions" + ], + "summary": "List all regions", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + } + } + } + }, + "/review/location": { + "post": { + "security": [ + { + "CookieAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "reviews" + ], + "summary": "Create a review", + "parameters": [ + { + "description": "Review payload", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.createReviewReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/review/location/images": { + "post": { + "security": [ + { + "CookieAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "reviews" + ], + "summary": "Upload images for a review", + "parameters": [ + { + "type": "integer", + "description": "Review ID", + "name": "review_id", + "in": "formData", + "required": true + }, + { + "type": "file", + "description": "Image files", + "name": "images", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/user": { + "patch": { + "security": [ + { + "CookieAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Update user profile", + "parameters": [ + { + "description": "Update payload", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.UpdateUserRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/user/avatar": { + "delete": { + "security": [ + { + "CookieAuth": [] + } + ], + "tags": [ + "user" + ], + "summary": "Remove user avatar", + "responses": { + "204": { + "description": "No Content" + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "patch": { + "security": [ + { + "CookieAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Upload user avatar", + "parameters": [ + { + "type": "file", + "description": "Avatar image", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/user/login": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Login", + "parameters": [ + { + "description": "Login credentials", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.createUserRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/user/logout": { + "post": { + "tags": [ + "auth" + ], + "summary": "Logout", + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/user/profile": { + "get": { + "security": [ + { + "CookieAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Get current user profile and stats", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/user/review/location/{location_id}": { + "get": { + "security": [ + { + "CookieAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "reviews" + ], + "summary": "Get current user's review for a location", + "parameters": [ + { + "type": "integer", + "description": "Location ID", + "name": "location_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/user/signup": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Register a new user", + "parameters": [ + { + "description": "Signup payload", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.createUserRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + } + }, + "definitions": { + "api.CreateNewsEventsReq": { + "type": "object", + "required": [ + "submitted_by", + "title", + "url" + ], + "properties": { + "description": { + "type": "string" + }, + "source": { + "type": "string" + }, + "submitted_by": { + "type": "integer" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "api.UpdateUserRequest": { + "type": "object", + "properties": { + "about": { + "type": "string" + }, + "social_media": {}, + "website": { + "type": "string" + } + } + }, + "api.createReviewReq": { + "type": "object", + "required": [ + "comments", + "location_id", + "submitted_by" + ], + "properties": { + "comments": { + "type": "string" + }, + "is_from_critic": { + "type": "boolean" + }, + "is_hided": { + "type": "boolean" + }, + "location_id": { + "type": "integer" + }, + "score": { + "type": "integer", + "maximum": 100 + }, + "submitted_by": { + "type": "integer" + }, + "title": { + "type": "string" + } + } + }, + "api.createUserRequest": { + "type": "object", + "required": [ + "password", + "username" + ], + "properties": { + "password": { + "type": "string", + "minLength": 7 + }, + "username": { + "type": "string" + } + } + } + }, + "securityDefinitions": { + "CookieAuth": { + "type": "apiKey", + "name": "paseto", + "in": "cookie" + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "localhost:8888", + BasePath: "/", + Schemes: []string{}, + Title: "Hiling API", + Description: "REST API for Hiling — a location review and discovery platform.", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/docs/swagger.json b/docs/swagger.json new file mode 100644 index 0000000..17ce6b9 --- /dev/null +++ b/docs/swagger.json @@ -0,0 +1,1188 @@ +{ + "swagger": "2.0", + "info": { + "description": "REST API for Hiling — a location review and discovery platform.", + "title": "Hiling API", + "contact": {}, + "version": "1.0" + }, + "host": "localhost:8888", + "basePath": "/", + "paths": { + "/images/location": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "images" + ], + "summary": "Get all images for a location", + "parameters": [ + { + "type": "integer", + "description": "Location ID", + "name": "location_id", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page (min 1)", + "name": "page", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page size (min 5)", + "name": "page_size", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/location/reviews": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "List location reviews", + "parameters": [ + { + "type": "integer", + "description": "Location ID", + "name": "location_id", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page (min 1)", + "name": "page", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page size", + "name": "page_size", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Review type: 1=critics, 2=users, 0=all", + "name": "type", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/location/tags/{location_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "Get tags for a location", + "parameters": [ + { + "type": "integer", + "description": "Location ID", + "name": "location_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/location/{location_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "Get location detail", + "parameters": [ + { + "type": "integer", + "description": "Location ID", + "name": "location_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Review type: 'user', 'critics', or both (default)", + "name": "review", + "in": "query" + }, + { + "type": "integer", + "description": "Page (default 1)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size (default 5)", + "name": "page_size", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/location/{location_id}/review/{review_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "reviews" + ], + "summary": "Get a single review", + "parameters": [ + { + "type": "integer", + "description": "Location ID", + "name": "location_id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Review ID", + "name": "review_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/locations": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "List locations", + "parameters": [ + { + "type": "integer", + "description": "Page number (min 1)", + "name": "page", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page size (min 5)", + "name": "page_size", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "post": { + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "Create a location", + "parameters": [ + { + "type": "string", + "description": "Address", + "name": "address", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Name", + "name": "name", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Submitted by user ID", + "name": "submitted_by", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "Regency ID", + "name": "regency_id", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Location type", + "name": "location_type", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Amenities (comma-separated)", + "name": "amenities", + "in": "formData" + }, + { + "type": "string", + "description": "Google Maps link", + "name": "google_maps_link", + "in": "formData" + }, + { + "type": "file", + "description": "Thumbnail image", + "name": "thumbnail", + "in": "formData" + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/locations/recent": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "Recent locations with ratings", + "parameters": [ + { + "type": "integer", + "description": "Number of results (min 1)", + "name": "page_size", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Filter by location type", + "name": "location_type", + "in": "query" + }, + { + "type": "string", + "description": "Filter by region", + "name": "regions", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/locations/search": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "Search locations", + "parameters": [ + { + "type": "string", + "description": "Search query", + "name": "name", + "in": "query" + }, + { + "type": "string", + "description": "Meilisearch filter expression", + "name": "filter", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/locations/top-ratings": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "locations" + ], + "summary": "Top-rated locations", + "parameters": [ + { + "type": "integer", + "description": "Page number (min 1)", + "name": "page", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page size (min 5)", + "name": "page_size", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Sort: 1=overall, 2=critics, 3=users", + "name": "order_by", + "in": "query" + }, + { + "type": "integer", + "description": "Region type filter (0–7)", + "name": "region_type", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/news-events": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "news" + ], + "summary": "List news and events", + "parameters": [ + { + "type": "integer", + "description": "Page (min 1)", + "name": "page", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "Page size (min 5)", + "name": "page_size", + "in": "query", + "required": true + }, + { + "type": "integer", + "description": "1 = only approved items", + "name": "is_with_approval", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "post": { + "security": [ + { + "CookieAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "news" + ], + "summary": "Create a news/event", + "parameters": [ + { + "description": "News/event payload", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.CreateNewsEventsReq" + } + } + ], + "responses": { + "201": { + "description": "Created" + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/region/provinces": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "regions" + ], + "summary": "List provinces", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + } + } + } + }, + "/region/regencies": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "regions" + ], + "summary": "List regencies", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + } + } + } + }, + "/regions": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "regions" + ], + "summary": "List all regions", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + } + } + } + }, + "/review/location": { + "post": { + "security": [ + { + "CookieAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "reviews" + ], + "summary": "Create a review", + "parameters": [ + { + "description": "Review payload", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.createReviewReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/review/location/images": { + "post": { + "security": [ + { + "CookieAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "reviews" + ], + "summary": "Upload images for a review", + "parameters": [ + { + "type": "integer", + "description": "Review ID", + "name": "review_id", + "in": "formData", + "required": true + }, + { + "type": "file", + "description": "Image files", + "name": "images", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/user": { + "patch": { + "security": [ + { + "CookieAuth": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Update user profile", + "parameters": [ + { + "description": "Update payload", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.UpdateUserRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/user/avatar": { + "delete": { + "security": [ + { + "CookieAuth": [] + } + ], + "tags": [ + "user" + ], + "summary": "Remove user avatar", + "responses": { + "204": { + "description": "No Content" + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, + "patch": { + "security": [ + { + "CookieAuth": [] + } + ], + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Upload user avatar", + "parameters": [ + { + "type": "file", + "description": "Avatar image", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/user/login": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Login", + "parameters": [ + { + "description": "Login credentials", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.createUserRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/user/logout": { + "post": { + "tags": [ + "auth" + ], + "summary": "Logout", + "responses": { + "204": { + "description": "No Content" + } + } + } + }, + "/user/profile": { + "get": { + "security": [ + { + "CookieAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Get current user profile and stats", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/user/review/location/{location_id}": { + "get": { + "security": [ + { + "CookieAuth": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "reviews" + ], + "summary": "Get current user's review for a location", + "parameters": [ + { + "type": "integer", + "description": "Location ID", + "name": "location_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + }, + "/user/signup": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "auth" + ], + "summary": "Register a new user", + "parameters": [ + { + "description": "Signup payload", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.createUserRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "409": { + "description": "Conflict", + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + } + } + }, + "definitions": { + "api.CreateNewsEventsReq": { + "type": "object", + "required": [ + "submitted_by", + "title", + "url" + ], + "properties": { + "description": { + "type": "string" + }, + "source": { + "type": "string" + }, + "submitted_by": { + "type": "integer" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "api.UpdateUserRequest": { + "type": "object", + "properties": { + "about": { + "type": "string" + }, + "social_media": {}, + "website": { + "type": "string" + } + } + }, + "api.createReviewReq": { + "type": "object", + "required": [ + "comments", + "location_id", + "submitted_by" + ], + "properties": { + "comments": { + "type": "string" + }, + "is_from_critic": { + "type": "boolean" + }, + "is_hided": { + "type": "boolean" + }, + "location_id": { + "type": "integer" + }, + "score": { + "type": "integer", + "maximum": 100 + }, + "submitted_by": { + "type": "integer" + }, + "title": { + "type": "string" + } + } + }, + "api.createUserRequest": { + "type": "object", + "required": [ + "password", + "username" + ], + "properties": { + "password": { + "type": "string", + "minLength": 7 + }, + "username": { + "type": "string" + } + } + } + }, + "securityDefinitions": { + "CookieAuth": { + "type": "apiKey", + "name": "paseto", + "in": "cookie" + } + } +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 0000000..7c49e70 --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,785 @@ +basePath: / +definitions: + api.CreateNewsEventsReq: + properties: + description: + type: string + source: + type: string + submitted_by: + type: integer + title: + type: string + url: + type: string + required: + - submitted_by + - title + - url + type: object + api.UpdateUserRequest: + properties: + about: + type: string + social_media: {} + website: + type: string + type: object + api.createReviewReq: + properties: + comments: + type: string + is_from_critic: + type: boolean + is_hided: + type: boolean + location_id: + type: integer + score: + maximum: 100 + type: integer + submitted_by: + type: integer + title: + type: string + required: + - comments + - location_id + - submitted_by + type: object + api.createUserRequest: + properties: + password: + minLength: 7 + type: string + username: + type: string + required: + - password + - username + type: object +host: localhost:8888 +info: + contact: {} + description: REST API for Hiling — a location review and discovery platform. + title: Hiling API + version: "1.0" +paths: + /images/location: + get: + parameters: + - description: Location ID + in: query + name: location_id + required: true + type: integer + - description: Page (min 1) + in: query + name: page + required: true + type: integer + - description: Page size (min 5) + in: query + name: page_size + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + summary: Get all images for a location + tags: + - images + /location/{location_id}: + get: + parameters: + - description: Location ID + in: path + name: location_id + required: true + type: integer + - description: 'Review type: ''user'', ''critics'', or both (default)' + in: query + name: review + type: string + - description: Page (default 1) + in: query + name: page + type: integer + - description: Page size (default 5) + in: query + name: page_size + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "404": + description: Not Found + schema: + additionalProperties: true + type: object + summary: Get location detail + tags: + - locations + /location/{location_id}/review/{review_id}: + get: + parameters: + - description: Location ID + in: path + name: location_id + required: true + type: integer + - description: Review ID + in: path + name: review_id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "404": + description: Not Found + schema: + additionalProperties: true + type: object + summary: Get a single review + tags: + - reviews + /location/reviews: + get: + parameters: + - description: Location ID + in: query + name: location_id + required: true + type: integer + - description: Page (min 1) + in: query + name: page + required: true + type: integer + - description: Page size + in: query + name: page_size + required: true + type: integer + - description: 'Review type: 1=critics, 2=users, 0=all' + in: query + name: type + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + summary: List location reviews + tags: + - locations + /location/tags/{location_id}: + get: + parameters: + - description: Location ID + in: path + name: location_id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + additionalProperties: true + type: object + type: array + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + summary: Get tags for a location + tags: + - locations + /locations: + get: + parameters: + - description: Page number (min 1) + in: query + name: page + required: true + type: integer + - description: Page size (min 5) + in: query + name: page_size + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + additionalProperties: true + type: object + type: array + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + summary: List locations + tags: + - locations + post: + consumes: + - multipart/form-data + parameters: + - description: Address + in: formData + name: address + required: true + type: string + - description: Name + in: formData + name: name + required: true + type: string + - description: Submitted by user ID + in: formData + name: submitted_by + required: true + type: integer + - description: Regency ID + in: formData + name: regency_id + required: true + type: integer + - description: Location type + in: formData + name: location_type + required: true + type: string + - description: Amenities (comma-separated) + in: formData + name: amenities + type: string + - description: Google Maps link + in: formData + name: google_maps_link + type: string + - description: Thumbnail image + in: formData + name: thumbnail + type: file + produces: + - application/json + responses: + "200": + description: OK + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + summary: Create a location + tags: + - locations + /locations/recent: + get: + parameters: + - description: Number of results (min 1) + in: query + name: page_size + required: true + type: integer + - description: Filter by location type + in: query + name: location_type + type: string + - description: Filter by region + in: query + name: regions + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + additionalProperties: true + type: object + type: array + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + summary: Recent locations with ratings + tags: + - locations + /locations/search: + get: + parameters: + - description: Search query + in: query + name: name + type: string + - description: Meilisearch filter expression + in: query + name: filter + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + additionalProperties: true + type: object + type: array + "500": + description: Internal Server Error + schema: + additionalProperties: true + type: object + summary: Search locations + tags: + - locations + /locations/top-ratings: + get: + parameters: + - description: Page number (min 1) + in: query + name: page + required: true + type: integer + - description: Page size (min 5) + in: query + name: page_size + required: true + type: integer + - description: 'Sort: 1=overall, 2=critics, 3=users' + in: query + name: order_by + type: integer + - description: Region type filter (0–7) + in: query + name: region_type + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + additionalProperties: true + type: object + type: array + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + summary: Top-rated locations + tags: + - locations + /news-events: + get: + parameters: + - description: Page (min 1) + in: query + name: page + required: true + type: integer + - description: Page size (min 5) + in: query + name: page_size + required: true + type: integer + - description: 1 = only approved items + in: query + name: is_with_approval + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + additionalProperties: true + type: object + type: array + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + summary: List news and events + tags: + - news + post: + consumes: + - application/json + parameters: + - description: News/event payload + in: body + name: body + required: true + schema: + $ref: '#/definitions/api.CreateNewsEventsReq' + produces: + - application/json + responses: + "201": + description: Created + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + security: + - CookieAuth: [] + summary: Create a news/event + tags: + - news + /region/provinces: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + additionalProperties: true + type: object + type: array + summary: List provinces + tags: + - regions + /region/regencies: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + additionalProperties: true + type: object + type: array + summary: List regencies + tags: + - regions + /regions: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + additionalProperties: true + type: object + type: array + summary: List all regions + tags: + - regions + /review/location: + post: + consumes: + - application/json + parameters: + - description: Review payload + in: body + name: body + required: true + schema: + $ref: '#/definitions/api.createReviewReq' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + "409": + description: Conflict + schema: + additionalProperties: true + type: object + security: + - CookieAuth: [] + summary: Create a review + tags: + - reviews + /review/location/images: + post: + consumes: + - multipart/form-data + parameters: + - description: Review ID + in: formData + name: review_id + required: true + type: integer + - description: Image files + in: formData + name: images + required: true + type: file + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + security: + - CookieAuth: [] + summary: Upload images for a review + tags: + - reviews + /user: + patch: + consumes: + - application/json + parameters: + - description: Update payload + in: body + name: body + required: true + schema: + $ref: '#/definitions/api.UpdateUserRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + security: + - CookieAuth: [] + summary: Update user profile + tags: + - user + /user/avatar: + delete: + responses: + "204": + description: No Content + "500": + description: Internal Server Error + schema: + additionalProperties: true + type: object + security: + - CookieAuth: [] + summary: Remove user avatar + tags: + - user + patch: + consumes: + - multipart/form-data + parameters: + - description: Avatar image + in: formData + name: file + required: true + type: file + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "500": + description: Internal Server Error + schema: + additionalProperties: true + type: object + security: + - CookieAuth: [] + summary: Upload user avatar + tags: + - user + /user/login: + post: + consumes: + - application/json + parameters: + - description: Login credentials + in: body + name: body + required: true + schema: + $ref: '#/definitions/api.createUserRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "401": + description: Unauthorized + schema: + additionalProperties: true + type: object + "404": + description: Not Found + schema: + additionalProperties: true + type: object + summary: Login + tags: + - auth + /user/logout: + post: + responses: + "204": + description: No Content + summary: Logout + tags: + - auth + /user/profile: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "401": + description: Unauthorized + schema: + additionalProperties: true + type: object + security: + - CookieAuth: [] + summary: Get current user profile and stats + tags: + - user + /user/review/location/{location_id}: + get: + parameters: + - description: Location ID + in: path + name: location_id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "404": + description: Not Found + schema: + additionalProperties: true + type: object + security: + - CookieAuth: [] + summary: Get current user's review for a location + tags: + - reviews + /user/signup: + post: + consumes: + - application/json + parameters: + - description: Signup payload + in: body + name: body + required: true + schema: + $ref: '#/definitions/api.createUserRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + additionalProperties: true + type: object + "400": + description: Bad Request + schema: + additionalProperties: true + type: object + "409": + description: Conflict + schema: + additionalProperties: true + type: object + summary: Register a new user + tags: + - auth +securityDefinitions: + CookieAuth: + in: cookie + name: paseto + type: apiKey +swagger: "2.0" diff --git a/go.mod b/go.mod index 745dda3..d3a3e3f 100755 --- a/go.mod +++ b/go.mod @@ -15,10 +15,13 @@ require ( github.com/stretchr/testify v1.8.4 github.com/yiplee/sqlc v1.0.2 go.uber.org/mock v0.4.0 - golang.org/x/crypto v0.18.0 + golang.org/x/crypto v0.36.0 ) require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect github.com/andybalholm/brotli v1.0.4 // indirect @@ -48,6 +51,10 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/swag v0.19.15 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/goccy/go-json v0.10.2 // indirect @@ -75,17 +82,23 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect + github.com/swaggo/files v1.0.1 // indirect + github.com/swaggo/gin-swagger v1.6.1 // indirect + github.com/swaggo/swag v1.16.6 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.37.1-0.20220607072126-8a320890c08d // indirect github.com/yiplee/nap v1.0.1 // indirect golang.org/x/arch v0.7.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/protobuf v1.32.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f3a3899..4b5ffc5 100755 --- a/go.sum +++ b/go.sum @@ -38,6 +38,12 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/aead/chacha20poly1305 v0.0.0-20170617001512-233f39982aeb/go.mod h1:UzH9IX1MMqOcwhoNOIjmTQeAxrFgzs50j4golQtXXxU= @@ -130,6 +136,16 @@ github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SU github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -251,6 +267,9 @@ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= @@ -267,6 +286,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/o1egl/paseto v1.0.0 h1:bwpvPu2au176w4IBlhbyUv/S5VPptERIA99Oap5qUd0= github.com/o1egl/paseto v1.0.0/go.mod h1:5HxsZPmw/3RI2pAwGo1HhOOwSdvBpcuVzO7uDkm+CLU= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= @@ -312,6 +332,14 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY= +github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxLnILT/qNiTw= +github.com/swaggo/swag v1.8.12 h1:pctzkNPu0AlQP2royqX3apjKCQonAnf7KGoxeO4y64w= +github.com/swaggo/swag v1.8.12/go.mod h1:lNfm6Gg+oAq3zRJQNEMBE66LIJKM44mxFqhEEgy2its= +github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= +github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= @@ -331,6 +359,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -350,10 +379,13 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -387,6 +419,9 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -418,10 +453,15 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -442,8 +482,11 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -477,6 +520,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -484,13 +528,18 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -499,8 +548,11 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -551,6 +603,9 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -647,16 +702,21 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 4621e49..959c0a4 100755 --- a/main.go +++ b/main.go @@ -6,14 +6,20 @@ import ( "git.nochill.in/nochill/hiling_go/api" db "git.nochill.in/nochill/hiling_go/db/repository" + _ "git.nochill.in/nochill/hiling_go/docs" "git.nochill.in/nochill/hiling_go/util" _ "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" ) -// "database/sql"? - -// "git.nochill.in/nochill/naice_pos/util" +// @title Hiling API +// @version 1.0 +// @description REST API for Hiling — a location review and discovery platform. +// @host localhost:8888 +// @BasePath / +// @securityDefinitions.apikey CookieAuth +// @in cookie +// @name paseto func main() { config, err := util.LoadConfig(".")