From 5ea893f2be2e5c2b637f115e5d592fcac43d007e Mon Sep 17 00:00:00 2001 From: AiAe Date: Thu, 10 Sep 2026 22:55:19 +0300 Subject: [PATCH 1/2] Map mods pagination --- db/map_mods.go | 25 ++++++++++++++++++++++--- handlers/limits.go | 1 + handlers/map_mods.go | 20 ++++++++++++++++---- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/db/map_mods.go b/db/map_mods.go index e6d9979..48c7356 100644 --- a/db/map_mods.go +++ b/db/map_mods.go @@ -41,15 +41,18 @@ func (mod *MapMod) AfterFind(*gorm.DB) (err error) { return nil } -// GetMapMods Retrieves map mods for a given map -func GetMapMods(id int) ([]*MapMod, error) { +// GetMapMods retrieves a page of map mods and all replies belonging to those mods. +func GetMapMods(id int, page int, limit int) ([]*MapMod, error) { var mods = make([]*MapMod, 0) result := SQL. Joins("Author"). Preload("Replies"). Preload("Replies.Author"). - Where("map_id = ?", id). + Where("map_mods.map_id = ?", id). + Order("map_mods.id ASC"). + Limit(limit). + Offset(page * limit). Find(&mods) if result.Error != nil { @@ -59,6 +62,22 @@ func GetMapMods(id int) ([]*MapMod, error) { return mods, nil } +// GetMapModsCount gets the total number of mods for a map. +func GetMapModsCount(id int) (int64, error) { + var count int64 + + result := SQL. + Model(&MapMod{}). + Where("map_id = ?", id). + Count(&count) + + if result.Error != nil { + return 0, result.Error + } + + return count, nil +} + // GetModById Gets a mod by its id func GetModById(id int) (*MapMod, error) { var mod *MapMod diff --git a/handlers/limits.go b/handlers/limits.go index 1957de2..e97e5a4 100644 --- a/handlers/limits.go +++ b/handlers/limits.go @@ -13,6 +13,7 @@ const ( defaultUserActivityLimit = 50 defaultUserPlaylistLimit = 50 defaultPlaylistMapsetLimit = 25 + defaultMapModLimit = 20 ) // getQueryLimit returns a requested limit bounded by the endpoint's default limit. diff --git a/handlers/map_mods.go b/handlers/map_mods.go index 48f7893..ac6a7a3 100644 --- a/handlers/map_mods.go +++ b/handlers/map_mods.go @@ -11,8 +11,8 @@ import ( "strings" ) -// GetMapMods Gets mods for a given map -// Endpoint: GET /v2/maps/:id/mods +// GetMapMods Gets a page of mods for a given map, including all replies to each mod. +// Endpoint: GET /v2/map/:id/mods?page=0&limit=20 func GetMapMods(c *gin.Context) *APIError { id, err := strconv.Atoi(c.Param("id")) @@ -20,16 +20,28 @@ func GetMapMods(c *gin.Context) *APIError { return APIErrorBadRequest("Invalid id") } - mods, err := db.GetMapMods(id) + page, limit := getMapModsPagination(c) + + mods, err := db.GetMapMods(id, page, limit) if err != nil { return APIErrorServerError("Error retrieving map mods from db", err) } - c.JSON(http.StatusOK, gin.H{"mods": mods}) + total, err := db.GetMapModsCount(id) + + if err != nil { + return APIErrorServerError("Error retrieving map mods count from db", err) + } + + c.JSON(http.StatusOK, gin.H{"mods": mods, "total": total}) return nil } +func getMapModsPagination(c *gin.Context) (int, int) { + return getQueryPage(c), getQueryLimit(c, defaultMapModLimit) +} + // SubmitMapMod Inserts a map mod to the db // Endpoint: POST /v2/maps/:id/mods func SubmitMapMod(c *gin.Context) *APIError { From 33e95525dfc9d32fe522bb17d7a65c0e83be06b2 Mon Sep 17 00:00:00 2001 From: AiAe Date: Thu, 10 Sep 2026 23:05:48 +0300 Subject: [PATCH 2/2] Fetch single mod comment and its replies for permalink --- cmd/api/server.go | 1 + handlers/map_mods.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/cmd/api/server.go b/cmd/api/server.go index 80dad39..c67c74d 100644 --- a/cmd/api/server.go +++ b/cmd/api/server.go @@ -165,6 +165,7 @@ func initializeRoutes(engine *gin.Engine) { // Map Mods engine.GET("/v2/map/:id/mods", handlers.CreateHandler(handlers.GetMapMods)) + engine.GET("/v2/map/:id/mods/:mod_id", handlers.CreateHandler(handlers.GetMapMod)) engine.POST("/v2/map/:id/mods", middleware.RequireAuth, handlers.CreateHandler(handlers.SubmitMapMod)) engine.POST("/v2/map/:id/mods/:mod_id/status", middleware.RequireAuth, handlers.CreateHandler(handlers.UpdateMapModStatus)) engine.POST("/v2/map/:id/mods/:mod_id/comment", middleware.RequireAuth, handlers.CreateHandler(handlers.SubmitMapModComment)) diff --git a/handlers/map_mods.go b/handlers/map_mods.go index ac6a7a3..96f22cf 100644 --- a/handlers/map_mods.go +++ b/handlers/map_mods.go @@ -42,6 +42,35 @@ func getMapModsPagination(c *gin.Context) (int, int) { return getQueryPage(c), getQueryLimit(c, defaultMapModLimit) } +// GetMapMod gets a single mod for a map, including all of its replies. +// Endpoint: GET /v2/map/:id/mods/:mod_id +func GetMapMod(c *gin.Context) *APIError { + mapId, err := strconv.Atoi(c.Param("id")) + + if err != nil { + return APIErrorBadRequest("Invalid map id") + } + + modId, err := strconv.Atoi(c.Param("mod_id")) + + if err != nil { + return APIErrorBadRequest("Invalid mod id") + } + + mod, err := db.GetModById(modId) + + if err != nil && err != gorm.ErrRecordNotFound { + return APIErrorServerError("Error retrieving map mod from db", err) + } + + if mod == nil || mod.MapId != mapId { + return APIErrorNotFound("Mod") + } + + c.JSON(http.StatusOK, gin.H{"mod": mod}) + return nil +} + // SubmitMapMod Inserts a map mod to the db // Endpoint: POST /v2/maps/:id/mods func SubmitMapMod(c *gin.Context) *APIError {