From f133acf8a1ec5b797b48e3d9224baea631a2a85c Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Sat, 17 Jun 2023 11:11:41 +0300 Subject: feat: update main --- go.mod | 1 - go.sum | 2 -- main.go | 2 -- 3 files changed, 5 deletions(-) diff --git a/go.mod b/go.mod index c0770da..312afcf 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/pektezol/leastportals go 1.19 require ( - github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19 github.com/gin-gonic/gin v1.8.1 github.com/joho/godotenv v1.4.0 github.com/solovev/steam_go v0.0.0-20170222182106-48eb5aae6c50 diff --git a/go.sum b/go.sum index fe4d065..f5b3907 100644 --- a/go.sum +++ b/go.sum @@ -26,8 +26,6 @@ github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19 h1:J2LPEOcQmWaooBnBtUDV9KHFEnP5LYTZY03GiQ0oQBw= -github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19/go.mod h1:iqneQ2Df3omzIVTkIfn7c1acsVnMGiSLn4XF5Blh3Yg= github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= diff --git a/main.go b/main.go index 991f3c0..8aaa80c 100644 --- a/main.go +++ b/main.go @@ -31,8 +31,6 @@ func main() { } router := gin.Default() database.ConnectDB() - // For frontend static serving - only for local debug - // router.Use(static.Serve("/", static.LocalFile("./frontend/build", true))) routes.InitRoutes(router) router.Run(fmt.Sprintf(":%s", os.Getenv("PORT"))) } -- cgit v1.2.3 From 1f47d78a5f82998fe62fefe6d6f076583c101800 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Sat, 17 Jun 2023 12:30:25 +0300 Subject: feat: search with query (#40) --- backend/controllers/homeController.go | 36 ++++++++++++++++++++++++++++++----- backend/routes/routes.go | 2 +- docs/docs.go | 10 +++++++++- docs/swagger.json | 10 +++++++++- docs/swagger.yaml | 7 ++++++- 5 files changed, 56 insertions(+), 9 deletions(-) diff --git a/backend/controllers/homeController.go b/backend/controllers/homeController.go index edb770f..d30c835 100644 --- a/backend/controllers/homeController.go +++ b/backend/controllers/homeController.go @@ -3,6 +3,7 @@ package controllers import ( "log" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/pektezol/leastportals/backend/database" @@ -122,15 +123,19 @@ func Rankings(c *gin.Context) { }) } -// GET Search +// GET Search With Query // -// @Summary Get all user and map data. +// @Summary Get all user and map data matching to the query. // @Tags search // @Produce json +// @Param q query string false "Search user or map name." // @Success 200 {object} models.Response{data=models.SearchResponse} // @Failure 400 {object} models.Response // @Router /search [get] -func Search(c *gin.Context) { +func SearchWithQuery(c *gin.Context) { + query := c.Query("q") + query = strings.ToLower(query) + log.Println(query) var response models.SearchResponse // Cache all maps for faster response var maps = []struct { @@ -248,8 +253,23 @@ func Search(c *gin.Context) { {ID: 109, Name: "Gel Maze"}, {ID: 110, Name: "Crazier Box"}, } - response.Maps = maps - rows, err := database.DB.Query("SELECT steam_id, user_name FROM users") //WHERE player_name LIKE ?", "%"+query+"%") + var filteredMaps []struct { + ID int `json:"id"` + Name string `json:"name"` + } + for _, m := range maps { + if strings.Contains(strings.ToLower(m.Name), strings.ToLower(query)) { + filteredMaps = append(filteredMaps, m) + } + } + response.Maps = filteredMaps + if len(response.Maps) == 0 { + response.Maps = []struct { + ID int "json:\"id\"" + Name string "json:\"name\"" + }{} + } + rows, err := database.DB.Query("SELECT steam_id, user_name FROM users WHERE lower(user_name) LIKE $1", "%"+query+"%") if err != nil { log.Fatal(err) } @@ -265,6 +285,12 @@ func Search(c *gin.Context) { } response.Players = append(response.Players, user) } + if len(response.Players) == 0 { + response.Players = []struct { + SteamID string "json:\"steam_id\"" + UserName string "json:\"user_name\"" + }{} + } c.JSON(http.StatusOK, models.Response{ Success: true, Message: "Search successfully retrieved.", diff --git a/backend/routes/routes.go b/backend/routes/routes.go index 96da1ce..bf8a995 100644 --- a/backend/routes/routes.go +++ b/backend/routes/routes.go @@ -29,7 +29,7 @@ func InitRoutes(router *gin.Engine) { v1.GET("/maps/:id/leaderboards", middleware.CheckAuth, controllers.FetchMapLeaderboards) v1.POST("/maps/:id/record", middleware.CheckAuth, controllers.CreateRecordWithDemo) v1.GET("/rankings", middleware.CheckAuth, controllers.Rankings) - v1.GET("/search", controllers.Search) + v1.GET("/search", controllers.SearchWithQuery) v1.GET("/games", controllers.FetchGames) v1.GET("/games/:id", controllers.FetchChapters) v1.GET("/chapters/:id", controllers.FetchChapterMaps) diff --git a/docs/docs.go b/docs/docs.go index d39fd1c..34aa7f4 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -651,7 +651,15 @@ const docTemplate = `{ "tags": [ "search" ], - "summary": "Get all user and map data.", + "summary": "Get all user and map data matching to the query.", + "parameters": [ + { + "type": "string", + "description": "Search user or map name.", + "name": "q", + "in": "query" + } + ], "responses": { "200": { "description": "OK", diff --git a/docs/swagger.json b/docs/swagger.json index ad2a659..8491d2e 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -644,7 +644,15 @@ "tags": [ "search" ], - "summary": "Get all user and map data.", + "summary": "Get all user and map data matching to the query.", + "parameters": [ + { + "type": "string", + "description": "Search user or map name.", + "name": "q", + "in": "query" + } + ], "responses": { "200": { "description": "OK", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index d62b46b..597df9f 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -574,6 +574,11 @@ paths: - users /search: get: + parameters: + - description: Search user or map name. + in: query + name: q + type: string produces: - application/json responses: @@ -590,7 +595,7 @@ paths: description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Get all user and map data. + summary: Get all user and map data matching to the query. tags: - search /token: -- cgit v1.2.3 From b960100d246a2c54778dc3501599adb1d7e4b224 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Sat, 17 Jun 2023 17:26:46 +0300 Subject: fix: reorganize models (#44) --- backend/controllers/homeController.go | 25 ++------ backend/controllers/userController.go | 2 +- backend/models/models.go | 113 ++++++++-------------------------- backend/models/requests.go | 8 +++ backend/models/responses.go | 54 ++++++++++++++++ 5 files changed, 94 insertions(+), 108 deletions(-) create mode 100644 backend/models/requests.go create mode 100644 backend/models/responses.go diff --git a/backend/controllers/homeController.go b/backend/controllers/homeController.go index d30c835..b5c6b60 100644 --- a/backend/controllers/homeController.go +++ b/backend/controllers/homeController.go @@ -138,10 +138,7 @@ func SearchWithQuery(c *gin.Context) { log.Println(query) var response models.SearchResponse // Cache all maps for faster response - var maps = []struct { - ID int `json:"id"` - Name string `json:"name"` - }{ + var maps = []models.MapShort{ {ID: 1, Name: "Container Ride"}, {ID: 2, Name: "Portal Carousel"}, {ID: 3, Name: "Portal Gun"}, @@ -253,10 +250,7 @@ func SearchWithQuery(c *gin.Context) { {ID: 109, Name: "Gel Maze"}, {ID: 110, Name: "Crazier Box"}, } - var filteredMaps []struct { - ID int `json:"id"` - Name string `json:"name"` - } + var filteredMaps []models.MapShort for _, m := range maps { if strings.Contains(strings.ToLower(m.Name), strings.ToLower(query)) { filteredMaps = append(filteredMaps, m) @@ -264,10 +258,7 @@ func SearchWithQuery(c *gin.Context) { } response.Maps = filteredMaps if len(response.Maps) == 0 { - response.Maps = []struct { - ID int "json:\"id\"" - Name string "json:\"name\"" - }{} + response.Maps = []models.MapShort{} } rows, err := database.DB.Query("SELECT steam_id, user_name FROM users WHERE lower(user_name) LIKE $1", "%"+query+"%") if err != nil { @@ -275,10 +266,7 @@ func SearchWithQuery(c *gin.Context) { } defer rows.Close() for rows.Next() { - var user struct { - SteamID string `json:"steam_id"` - UserName string `json:"user_name"` - } + var user models.UserShort if err := rows.Scan(&user.SteamID, &user.UserName); err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -286,10 +274,7 @@ func SearchWithQuery(c *gin.Context) { response.Players = append(response.Players, user) } if len(response.Players) == 0 { - response.Players = []struct { - SteamID string "json:\"steam_id\"" - UserName string "json:\"user_name\"" - }{} + response.Players = []models.UserShort{} } c.JSON(http.StatusOK, models.Response{ Success: true, diff --git a/backend/controllers/userController.go b/backend/controllers/userController.go index adf936b..cbce0fe 100644 --- a/backend/controllers/userController.go +++ b/backend/controllers/userController.go @@ -251,7 +251,7 @@ func UpdateUser(c *gin.Context) { // @Produce json // @Param Authorization header string true "JWT Token" // @Param country_code query string true "Country Code [XX]" -// @Success 200 {object} models.Response{data=models.ProfileResponse} +// @Success 200 {object} models.Response // @Failure 400 {object} models.Response // @Failure 401 {object} models.Response // @Router /profile [put] diff --git a/backend/models/models.go b/backend/models/models.go index 6f5173a..49a4f82 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -4,16 +4,6 @@ import ( "time" ) -type Response struct { - Success bool `json:"success"` - Message string `json:"message"` - Data any `json:"data"` -} - -type LoginResponse struct { - Token string `json:"token"` -} - type User struct { SteamID string `json:"steam_id"` UserName string `json:"user_name"` @@ -23,6 +13,11 @@ type User struct { UpdatedAt time.Time `json:"updated_at"` } +type UserShort struct { + SteamID string `json:"steam_id"` + UserName string `json:"user_name"` +} + type Map struct { ID int `json:"id"` GameName string `json:"game_name"` @@ -31,6 +26,11 @@ type Map struct { Data any `json:"data"` } +type MapShort struct { + ID int `json:"id"` + Name string `json:"name"` +} + type MapSummary struct { Description string `json:"description"` Showcase string `json:"showcase"` @@ -57,6 +57,22 @@ type MapHistory struct { Date time.Time `json:"date"` } +type UserRanking struct { + UserID string `json:"user_id"` + UserName string `json:"user_name"` + TotalScore int `json:"total_score"` +} + +type Game struct { + ID int `json:"id"` + Name string `json:"name"` +} + +type Chapter struct { + ID int `json:"id"` + Name string `json:"name"` +} + type RecordSP struct { RecordID int `json:"record_id"` Placement int `json:"placement"` @@ -85,50 +101,6 @@ type RecordMP struct { RecordDate time.Time `json:"record_date"` } -type RecordRequest struct { - ScoreCount int `json:"score_count" form:"score_count" binding:"required"` - ScoreTime int `json:"score_time" form:"score_time" binding:"required"` - PartnerID string `json:"partner_id" form:"partner_id" binding:"required"` - IsPartnerOrange bool `json:"is_partner_orange" form:"is_partner_orange" binding:"required"` -} - -type UserRanking struct { - UserID string `json:"user_id"` - UserName string `json:"user_name"` - TotalScore int `json:"total_score"` -} - -type RankingsResponse struct { - RankingsSP []UserRanking `json:"rankings_sp"` - RankingsMP []UserRanking `json:"rankings_mp"` -} - -type ProfileResponse struct { - Profile bool `json:"profile"` - SteamID string `json:"steam_id"` - UserName string `json:"user_name"` - AvatarLink string `json:"avatar_link"` - CountryCode string `json:"country_code"` - ScoresSP []ScoreResponse `json:"scores_sp"` - ScoresMP []ScoreResponse `json:"scores_mp"` -} - -type ScoreResponse struct { - MapID int `json:"map_id"` - Records any `json:"records"` -} - -type SearchResponse struct { - Players []struct { - SteamID string `json:"steam_id"` - UserName string `json:"user_name"` - } `json:"players"` - Maps []struct { - ID int `json:"id"` - Name string `json:"name"` - } `json:"maps"` -} - type PlayerSummaries struct { SteamId string `json:"steamid"` CommunityVisibilityState int `json:"communityvisibilitystate"` @@ -152,36 +124,3 @@ type PlayerSummaries struct { GameExtraInfo string `json:"gameextrainfo"` GameServerIp string `json:"gameserverip"` } - -type Game struct { - ID int `json:"id"` - Name string `json:"name"` -} - -type ChaptersResponse struct { - Game Game `json:"game"` - Chapters []Chapter `json:"chapters"` -} - -type Chapter struct { - ID int `json:"id"` - Name string `json:"name"` -} - -type MapShort struct { - ID int `json:"id"` - Name string `json:"name"` -} - -type ChapterMapsResponse struct { - Chapter Chapter `json:"chapter"` - Maps []MapShort `json:"maps"` -} - -func ErrorResponse(message string) Response { - return Response{ - Success: false, - Message: message, - Data: nil, - } -} diff --git a/backend/models/requests.go b/backend/models/requests.go new file mode 100644 index 0000000..49b2d75 --- /dev/null +++ b/backend/models/requests.go @@ -0,0 +1,8 @@ +package models + +type RecordRequest struct { + ScoreCount int `json:"score_count" form:"score_count" binding:"required"` + ScoreTime int `json:"score_time" form:"score_time" binding:"required"` + PartnerID string `json:"partner_id" form:"partner_id" binding:"required"` + IsPartnerOrange bool `json:"is_partner_orange" form:"is_partner_orange" binding:"required"` +} diff --git a/backend/models/responses.go b/backend/models/responses.go new file mode 100644 index 0000000..5a88353 --- /dev/null +++ b/backend/models/responses.go @@ -0,0 +1,54 @@ +package models + +type Response struct { + Success bool `json:"success"` + Message string `json:"message"` + Data any `json:"data"` +} + +type LoginResponse struct { + Token string `json:"token"` +} + +type RankingsResponse struct { + RankingsSP []UserRanking `json:"rankings_sp"` + RankingsMP []UserRanking `json:"rankings_mp"` +} + +type ProfileResponse struct { + Profile bool `json:"profile"` + SteamID string `json:"steam_id"` + UserName string `json:"user_name"` + AvatarLink string `json:"avatar_link"` + CountryCode string `json:"country_code"` + ScoresSP []ScoreResponse `json:"scores_sp"` + ScoresMP []ScoreResponse `json:"scores_mp"` +} + +type ScoreResponse struct { + MapID int `json:"map_id"` + Records any `json:"records"` +} + +type SearchResponse struct { + Players []UserShort `json:"players"` + Maps []MapShort `json:"maps"` +} + +type ChaptersResponse struct { + Game Game `json:"game"` + Chapters []Chapter `json:"chapters"` +} + +type ChapterMapsResponse struct { + Chapter Chapter `json:"chapter"` + Maps []MapShort `json:"maps"` +} + +func ErrorResponse(message string) Response { + return Response{ + Success: false, + Message: message, + Data: nil, + } +} -- cgit v1.2.3 From 5fec69af648e7c5c8a682132c9996f2883d1a99e Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Sat, 17 Jun 2023 23:58:23 +0300 Subject: feat: map routes table (#1) --- backend/database/init.sql | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/backend/database/init.sql b/backend/database/init.sql index 51a4881..871aba2 100644 --- a/backend/database/init.sql +++ b/backend/database/init.sql @@ -33,14 +33,24 @@ CREATE TABLE maps ( game_id SMALLINT NOT NULL, chapter_id SMALLINT NOT NULL, name TEXT NOT NULL, - description TEXT NOT NULL, - showcase TEXT NOT NULL, is_disabled BOOLEAN NOT NULL DEFAULT false, PRIMARY KEY (id), FOREIGN KEY (game_id) REFERENCES games(id), FOREIGN KEY (chapter_id) REFERENCES chapters(id) ); +CREATE TABLE map_routes ( + id SMALLSERIAL, + map_id SMALLINT NOT NULL, + category_id SMALLINT NOT NULL, + score_count SMALLINT NOT NULL, + description TEXT NOT NULL, + showcase TEXT NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (map_id) REFERENCES maps(id), + FOREIGN KEY (category_id) REFERENCES categories(id) +); + CREATE TABLE map_history ( id SMALLSERIAL, map_id SMALLINT NOT NULL, @@ -61,17 +71,6 @@ CREATE TABLE map_ratings ( FOREIGN KEY (user_id) REFERENCES users(steam_id) ); -CREATE TABLE map_routers ( - id SMALLSERIAL, - map_id SMALLINT NOT NULL, - user_id TEXT, - user_name TEXT NOT NULL, - score_count SMALLINT NOT NULL, - PRIMARY KEY (id), - FOREIGN KEY (map_id) REFERENCES maps(id), - FOREIGN KEY (user_id) REFERENCES users(steam_id) -); - CREATE TABLE demos ( id UUID, location_id TEXT NOT NULL, -- cgit v1.2.3 From 345fa5d06a47837fca48137163f8a7bdae33cebf Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Sun, 18 Jun 2023 17:08:27 +0300 Subject: feat: improved map summary response (#43) --- backend/controllers/mapController.go | 79 ++++++++++------------ backend/models/models.go | 37 +++++----- backend/models/responses.go | 5 ++ docs/docs.go | 127 +++++++++++++++-------------------- docs/swagger.json | 127 +++++++++++++++-------------------- docs/swagger.yaml | 82 +++++++++++----------- 6 files changed, 207 insertions(+), 250 deletions(-) diff --git a/backend/controllers/mapController.go b/backend/controllers/mapController.go index d8783b7..37b8e9b 100644 --- a/backend/controllers/mapController.go +++ b/backend/controllers/mapController.go @@ -3,10 +3,8 @@ package controllers import ( "net/http" "strconv" - "time" "github.com/gin-gonic/gin" - "github.com/lib/pq" "github.com/pektezol/leastportals/backend/database" "github.com/pektezol/leastportals/backend/models" ) @@ -17,37 +15,20 @@ import ( // @Tags maps // @Produce json // @Param id path int true "Map ID" -// @Success 200 {object} models.Response{data=models.Map{data=models.MapSummary}} +// @Success 200 {object} models.Response{data=models.MapSummaryResponse} // @Failure 400 {object} models.Response // @Router /maps/{id}/summary [get] func FetchMapSummary(c *gin.Context) { id := c.Param("id") // Get map data - var mapData models.Map - var mapSummaryData models.MapSummary - var mapHistoryData []models.MapHistory + response := models.MapSummaryResponse{Map: models.Map{}, Summary: models.MapSummary{History: []models.MapHistory{}, Routes: []models.MapRoute{}}} intID, err := strconv.Atoi(id) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - mapData.ID = intID - var routers pq.StringArray - sql := `SELECT g.name, c.name, m.name, m.description, m.showcase, - ( - SELECT array_agg(user_name) - FROM map_routers - WHERE map_id = $1 - AND score_count = ( - SELECT score_count - FROM map_history - WHERE map_id = $1 - ORDER BY score_count - LIMIT 1 - ) - GROUP BY map_routers.user_name - ORDER BY user_name - ), + response.Map.ID = intID + sql := `SELECT m.id, g.name, c.name, m.name, ( SELECT COALESCE(avg(rating), 0.0) FROM map_ratings @@ -57,44 +38,53 @@ func FetchMapSummary(c *gin.Context) { INNER JOIN games g ON m.game_id = g.id INNER JOIN chapters c ON m.chapter_id = c.id WHERE m.id = $1` - // TODO: CategoryScores - err = database.DB.QueryRow(sql, id).Scan(&mapData.GameName, &mapData.ChapterName, &mapData.MapName, &mapSummaryData.Description, &mapSummaryData.Showcase, &routers, &mapSummaryData.Rating) + err = database.DB.QueryRow(sql, id).Scan(&response.Map.ID, &response.Map.GameName, &response.Map.ChapterName, &response.Map.MapName, &response.Summary.Rating) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - var historyNames pq.StringArray - var historyScores pq.Int32Array - var historyDates pq.StringArray - sql = `SELECT array_agg(user_name), array_agg(score_count), array_agg(record_date) + sql = `SELECT user_name, score_count, record_date FROM map_history - WHERE map_id = $1` - err = database.DB.QueryRow(sql, id).Scan(&historyNames, &historyScores, &historyDates) + WHERE map_id = $1 + ORDER BY record_date ASC` + rows, err := database.DB.Query(sql, id) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + for rows.Next() { + history := models.MapHistory{} + err = rows.Scan(&history.RunnerName, &history.ScoreCount, &history.Date) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + response.Summary.History = append(response.Summary.History, history) + } + sql = `SELECT c.id, c.name, mr.score_count, mr.description, mr.showcase + FROM map_routes mr + INNER JOIN categories c ON mr.category_id = c.id + WHERE mr.map_id = $1 + ORDER BY mr.score_count DESC` + rows, err = database.DB.Query(sql, id) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - for i := 0; i < len(historyNames); i++ { - var history models.MapHistory - history.RunnerName = historyNames[i] - history.ScoreCount = int(historyScores[i]) - layout := "2006-01-02 15:04:05" - date, err := time.Parse(layout, historyDates[i]) + for rows.Next() { + route := models.MapRoute{} + err = rows.Scan(&route.Category.ID, &route.Category.Name, &route.ScoreCount, &route.Description, &route.Showcase) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - history.Date = date - mapHistoryData = append(mapHistoryData, history) + response.Summary.Routes = append(response.Summary.Routes, route) } - mapSummaryData.History = mapHistoryData - mapSummaryData.Routers = routers - mapData.Data = mapSummaryData // Return response c.JSON(http.StatusOK, models.Response{ Success: true, Message: "Successfully retrieved map summary.", - Data: mapData, + Data: response, }) } @@ -108,6 +98,7 @@ func FetchMapSummary(c *gin.Context) { // @Failure 400 {object} models.Response // @Router /maps/{id}/leaderboards [get] func FetchMapLeaderboards(c *gin.Context) { + // TODO: make new response type id := c.Param("id") // Get map data var mapData models.Map @@ -205,7 +196,7 @@ func FetchMapLeaderboards(c *gin.Context) { } mapRecordsData.Records = records } - mapData.Data = mapRecordsData + // mapData.Data = mapRecordsData // Return response c.JSON(http.StatusOK, models.Response{ Success: true, diff --git a/backend/models/models.go b/backend/models/models.go index 49a4f82..5c9b8f8 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -23,7 +23,6 @@ type Map struct { GameName string `json:"game_name"` ChapterName string `json:"chapter_name"` MapName string `json:"map_name"` - Data any `json:"data"` } type MapShort struct { @@ -32,23 +31,9 @@ type MapShort struct { } type MapSummary struct { - Description string `json:"description"` - Showcase string `json:"showcase"` - CategoryScores MapCategoryScores `json:"category_scores"` - Rating float32 `json:"rating"` - Routers []string `json:"routers"` - History []MapHistory `json:"history"` -} - -type MapCategoryScores struct { - CM int `json:"cm"` - NoSLA int `json:"no_sla"` - InboundsSLA int `json:"inbounds_sla"` - Any int `json:"any"` -} - -type MapRecords struct { - Records any `json:"records"` + Rating float32 `json:"rating"` + History []MapHistory `json:"history"` + Routes []MapRoute `json:"routes"` } type MapHistory struct { @@ -57,6 +42,17 @@ type MapHistory struct { Date time.Time `json:"date"` } +type MapRoute struct { + Category Category `json:"category"` + ScoreCount int `json:"score_count"` + Description string `json:"description"` + Showcase string `json:"showcase"` +} + +type MapRecords struct { + Records any `json:"records"` +} + type UserRanking struct { UserID string `json:"user_id"` UserName string `json:"user_name"` @@ -73,6 +69,11 @@ type Chapter struct { Name string `json:"name"` } +type Category struct { + ID int `json:"id"` + Name string `json:"name"` +} + type RecordSP struct { RecordID int `json:"record_id"` Placement int `json:"placement"` diff --git a/backend/models/responses.go b/backend/models/responses.go index 5a88353..dc554ff 100644 --- a/backend/models/responses.go +++ b/backend/models/responses.go @@ -30,6 +30,11 @@ type ScoreResponse struct { Records any `json:"records"` } +type MapSummaryResponse struct { + Map Map `json:"map"` + Summary MapSummary `json:"summary"` +} + type SearchResponse struct { Players []UserShort `json:"players"` Maps []MapShort `json:"maps"` diff --git a/docs/docs.go b/docs/docs.go index 34aa7f4..cf6c00d 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -447,19 +447,7 @@ const docTemplate = `{ "type": "object", "properties": { "data": { - "allOf": [ - { - "$ref": "#/definitions/models.Map" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/models.MapSummary" - } - } - } - ] + "$ref": "#/definitions/models.MapSummaryResponse" } } } @@ -560,19 +548,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "allOf": [ - { - "$ref": "#/definitions/models.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/models.ProfileResponse" - } - } - } - ] + "$ref": "#/definitions/models.Response" } }, "400": { @@ -817,6 +793,17 @@ const docTemplate = `{ } }, "definitions": { + "models.Category": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + } + }, "models.Chapter": { "type": "object", "properties": { @@ -881,7 +868,6 @@ const docTemplate = `{ "chapter_name": { "type": "string" }, - "data": {}, "game_name": { "type": "string" }, @@ -893,23 +879,6 @@ const docTemplate = `{ } } }, - "models.MapCategoryScores": { - "type": "object", - "properties": { - "any": { - "type": "integer" - }, - "cm": { - "type": "integer" - }, - "inbounds_sla": { - "type": "integer" - }, - "no_sla": { - "type": "integer" - } - } - }, "models.MapHistory": { "type": "object", "properties": { @@ -930,6 +899,23 @@ const docTemplate = `{ "records": {} } }, + "models.MapRoute": { + "type": "object", + "properties": { + "category": { + "$ref": "#/definitions/models.Category" + }, + "description": { + "type": "string" + }, + "score_count": { + "type": "integer" + }, + "showcase": { + "type": "string" + } + } + }, "models.MapShort": { "type": "object", "properties": { @@ -944,12 +930,6 @@ const docTemplate = `{ "models.MapSummary": { "type": "object", "properties": { - "category_scores": { - "$ref": "#/definitions/models.MapCategoryScores" - }, - "description": { - "type": "string" - }, "history": { "type": "array", "items": { @@ -959,14 +939,22 @@ const docTemplate = `{ "rating": { "type": "number" }, - "routers": { + "routes": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/models.MapRoute" } + } + } + }, + "models.MapSummaryResponse": { + "type": "object", + "properties": { + "map": { + "$ref": "#/definitions/models.Map" }, - "showcase": { - "type": "string" + "summary": { + "$ref": "#/definitions/models.MapSummary" } } }, @@ -1069,29 +1057,13 @@ const docTemplate = `{ "maps": { "type": "array", "items": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - } - } + "$ref": "#/definitions/models.MapShort" } }, "players": { "type": "array", "items": { - "type": "object", - "properties": { - "steam_id": { - "type": "string" - }, - "user_name": { - "type": "string" - } - } + "$ref": "#/definitions/models.UserShort" } } } @@ -1109,6 +1081,17 @@ const docTemplate = `{ "type": "string" } } + }, + "models.UserShort": { + "type": "object", + "properties": { + "steam_id": { + "type": "string" + }, + "user_name": { + "type": "string" + } + } } } }` diff --git a/docs/swagger.json b/docs/swagger.json index 8491d2e..f2ed3f0 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -440,19 +440,7 @@ "type": "object", "properties": { "data": { - "allOf": [ - { - "$ref": "#/definitions/models.Map" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/models.MapSummary" - } - } - } - ] + "$ref": "#/definitions/models.MapSummaryResponse" } } } @@ -553,19 +541,7 @@ "200": { "description": "OK", "schema": { - "allOf": [ - { - "$ref": "#/definitions/models.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/models.ProfileResponse" - } - } - } - ] + "$ref": "#/definitions/models.Response" } }, "400": { @@ -810,6 +786,17 @@ } }, "definitions": { + "models.Category": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + } + }, "models.Chapter": { "type": "object", "properties": { @@ -874,7 +861,6 @@ "chapter_name": { "type": "string" }, - "data": {}, "game_name": { "type": "string" }, @@ -886,23 +872,6 @@ } } }, - "models.MapCategoryScores": { - "type": "object", - "properties": { - "any": { - "type": "integer" - }, - "cm": { - "type": "integer" - }, - "inbounds_sla": { - "type": "integer" - }, - "no_sla": { - "type": "integer" - } - } - }, "models.MapHistory": { "type": "object", "properties": { @@ -923,6 +892,23 @@ "records": {} } }, + "models.MapRoute": { + "type": "object", + "properties": { + "category": { + "$ref": "#/definitions/models.Category" + }, + "description": { + "type": "string" + }, + "score_count": { + "type": "integer" + }, + "showcase": { + "type": "string" + } + } + }, "models.MapShort": { "type": "object", "properties": { @@ -937,12 +923,6 @@ "models.MapSummary": { "type": "object", "properties": { - "category_scores": { - "$ref": "#/definitions/models.MapCategoryScores" - }, - "description": { - "type": "string" - }, "history": { "type": "array", "items": { @@ -952,14 +932,22 @@ "rating": { "type": "number" }, - "routers": { + "routes": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/models.MapRoute" } + } + } + }, + "models.MapSummaryResponse": { + "type": "object", + "properties": { + "map": { + "$ref": "#/definitions/models.Map" }, - "showcase": { - "type": "string" + "summary": { + "$ref": "#/definitions/models.MapSummary" } } }, @@ -1062,29 +1050,13 @@ "maps": { "type": "array", "items": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - } - } + "$ref": "#/definitions/models.MapShort" } }, "players": { "type": "array", "items": { - "type": "object", - "properties": { - "steam_id": { - "type": "string" - }, - "user_name": { - "type": "string" - } - } + "$ref": "#/definitions/models.UserShort" } } } @@ -1102,6 +1074,17 @@ "type": "string" } } + }, + "models.UserShort": { + "type": "object", + "properties": { + "steam_id": { + "type": "string" + }, + "user_name": { + "type": "string" + } + } } } } \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 597df9f..61f2ad7 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,5 +1,12 @@ basePath: /v1 definitions: + models.Category: + properties: + id: + type: integer + name: + type: string + type: object models.Chapter: properties: id: @@ -41,7 +48,6 @@ definitions: properties: chapter_name: type: string - data: {} game_name: type: string id: @@ -49,17 +55,6 @@ definitions: map_name: type: string type: object - models.MapCategoryScores: - properties: - any: - type: integer - cm: - type: integer - inbounds_sla: - type: integer - no_sla: - type: integer - type: object models.MapHistory: properties: date: @@ -73,6 +68,17 @@ definitions: properties: records: {} type: object + models.MapRoute: + properties: + category: + $ref: '#/definitions/models.Category' + description: + type: string + score_count: + type: integer + showcase: + type: string + type: object models.MapShort: properties: id: @@ -82,22 +88,23 @@ definitions: type: object models.MapSummary: properties: - category_scores: - $ref: '#/definitions/models.MapCategoryScores' - description: - type: string history: items: $ref: '#/definitions/models.MapHistory' type: array rating: type: number - routers: + routes: items: - type: string + $ref: '#/definitions/models.MapRoute' type: array - showcase: - type: string + type: object + models.MapSummaryResponse: + properties: + map: + $ref: '#/definitions/models.Map' + summary: + $ref: '#/definitions/models.MapSummary' type: object models.ProfileResponse: properties: @@ -165,21 +172,11 @@ definitions: properties: maps: items: - properties: - id: - type: integer - name: - type: string - type: object + $ref: '#/definitions/models.MapShort' type: array players: items: - properties: - steam_id: - type: string - user_name: - type: string - type: object + $ref: '#/definitions/models.UserShort' type: array type: object models.UserRanking: @@ -191,6 +188,13 @@ definitions: user_name: type: string type: object + models.UserShort: + properties: + steam_id: + type: string + user_name: + type: string + type: object host: lp.ardapektezol.com/api info: contact: {} @@ -456,12 +460,7 @@ paths: - $ref: '#/definitions/models.Response' - properties: data: - allOf: - - $ref: '#/definitions/models.Map' - - properties: - data: - $ref: '#/definitions/models.MapSummary' - type: object + $ref: '#/definitions/models.MapSummaryResponse' type: object "400": description: Bad Request @@ -555,12 +554,7 @@ paths: "200": description: OK schema: - allOf: - - $ref: '#/definitions/models.Response' - - properties: - data: - $ref: '#/definitions/models.ProfileResponse' - type: object + $ref: '#/definitions/models.Response' "400": description: Bad Request schema: -- cgit v1.2.3 From 40537dace937fc96c851c56f0cb629f36dddfd03 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Tue, 20 Jun 2023 19:55:31 +0300 Subject: feat: improved map summary response, all routes init (#43, #46) --- backend/controllers/mapController.go | 52 ++-- backend/database/history.sql | 524 +++++++++++++++++------------------ backend/database/init.sql | 4 + backend/database/route.sql | 279 +++++++++++++++++++ backend/models/models.go | 14 +- 5 files changed, 570 insertions(+), 303 deletions(-) create mode 100644 backend/database/route.sql diff --git a/backend/controllers/mapController.go b/backend/controllers/mapController.go index 37b8e9b..e46b766 100644 --- a/backend/controllers/mapController.go +++ b/backend/controllers/mapController.go @@ -20,60 +20,44 @@ import ( // @Router /maps/{id}/summary [get] func FetchMapSummary(c *gin.Context) { id := c.Param("id") - // Get map data - response := models.MapSummaryResponse{Map: models.Map{}, Summary: models.MapSummary{History: []models.MapHistory{}, Routes: []models.MapRoute{}}} + response := models.MapSummaryResponse{Map: models.Map{}, Summary: models.MapSummary{Routes: []models.MapRoute{}}} intID, err := strconv.Atoi(id) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } + // ( + // SELECT COALESCE(avg(rating), 0.0) + // FROM route_ratings + // WHERE map_id = $1 + // ) + // Get map data response.Map.ID = intID - sql := `SELECT m.id, g.name, c.name, m.name, - ( - SELECT COALESCE(avg(rating), 0.0) - FROM map_ratings - WHERE map_id = $1 - ) + sql := `SELECT m.id, g.name, c.name, m.name FROM maps m INNER JOIN games g ON m.game_id = g.id INNER JOIN chapters c ON m.chapter_id = c.id WHERE m.id = $1` - err = database.DB.QueryRow(sql, id).Scan(&response.Map.ID, &response.Map.GameName, &response.Map.ChapterName, &response.Map.MapName, &response.Summary.Rating) + err = database.DB.QueryRow(sql, id).Scan(&response.Map.ID, &response.Map.GameName, &response.Map.ChapterName, &response.Map.MapName) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - sql = `SELECT user_name, score_count, record_date - FROM map_history - WHERE map_id = $1 - ORDER BY record_date ASC` + // Get map routes and histories + sql = `SELECT c.id, c.name, h.user_name, h.score_count, h.record_date, r.score_count, r.description, r.showcase, COALESCE(avg(rating), 0.0) FROM map_routes r + INNER JOIN categories c ON r.category_id = c.id + INNER JOIN map_history h ON r.map_id = h.map_id AND r.category_id = h.category_id + LEFT JOIN map_ratings rt ON r.map_id = rt.map_id AND r.category_id = rt.category_id + WHERE r.map_id = $1 AND h.score_count = r.score_count GROUP BY c.id, h.user_name, h.score_count, h.record_date, r.score_count, r.description, r.showcase + ORDER BY h.record_date ASC;` rows, err := database.DB.Query(sql, id) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } for rows.Next() { - history := models.MapHistory{} - err = rows.Scan(&history.RunnerName, &history.ScoreCount, &history.Date) - if err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) - return - } - response.Summary.History = append(response.Summary.History, history) - } - sql = `SELECT c.id, c.name, mr.score_count, mr.description, mr.showcase - FROM map_routes mr - INNER JOIN categories c ON mr.category_id = c.id - WHERE mr.map_id = $1 - ORDER BY mr.score_count DESC` - rows, err = database.DB.Query(sql, id) - if err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) - return - } - for rows.Next() { - route := models.MapRoute{} - err = rows.Scan(&route.Category.ID, &route.Category.Name, &route.ScoreCount, &route.Description, &route.Showcase) + route := models.MapRoute{Category: models.Category{}, History: models.MapHistory{}} + err = rows.Scan(&route.Category.ID, &route.Category.Name, &route.History.RunnerName, &route.History.ScoreCount, &route.History.Date, &route.ScoreCount, &route.Description, &route.Showcase, &route.Rating) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return diff --git a/backend/database/history.sql b/backend/database/history.sql index 0840bf3..d7c4999 100644 --- a/backend/database/history.sql +++ b/backend/database/history.sql @@ -1,279 +1,279 @@ -INSERT INTO map_history(map_id,user_name,score_count,record_date) VALUES +INSERT INTO map_history(map_id,category_id,user_name,score_count,record_date) VALUES -- Portal 2 Singleplayer -- 1 -(3,'slmid1995',3,'2011-10-05 00:00:00'), -(3,'LookLikeAKango',1,'2011-10-06 00:00:00'), -(3,'Bananasaurus Rex',0,'2011-10-24 00:00:00'), -(4,'Tyronis',1,'2011-10-05 00:00:00'), -(4,'Krzyhau',0,'2019-05-10 00:00:00'), -(5,'LookLikeAKango',2,'2011-10-05 00:00:00'), -(5,'Jetwash',1,'2013-12-03 00:00:00'), -(6,'Stimich',4,'2011-10-08 00:00:00'), -(6,'aepaePolakrn',3,'2011-10-19 00:00:00'), -(6,'Krzyhau',2,'2020-10-10 00:00:00'), -(9,'slmid1995',4,'2011-10-05 00:00:00'), -(9,'Jokie',3,'2011-10-05 00:00:00'), -(9,'Tyronis',2,'2011-10-05 00:00:00'), -(9,'sicklebrick',0,'2013-03-13 00:00:00'), +(3,1,'slmid1995',3,'2011-10-05 00:00:00'), +(3,1,'LookLikeAKango',1,'2011-10-06 00:00:00'), +(3,1,'Bananasaurus Rex',0,'2011-10-24 00:00:00'), +(4,1,'Tyronis',1,'2011-10-05 00:00:00'), +(4,1,'Krzyhau',0,'2019-05-10 00:00:00'), +(5,1,'LookLikeAKango',2,'2011-10-05 00:00:00'), +(5,1,'Jetwash',1,'2013-12-03 00:00:00'), +(6,1,'Stimich',4,'2011-10-08 00:00:00'), +(6,1,'aepaePolakrn',3,'2011-10-19 00:00:00'), +(6,1,'Krzyhau',2,'2020-10-10 00:00:00'), +(9,1,'slmid1995',4,'2011-10-05 00:00:00'), +(9,1,'Jokie',3,'2011-10-05 00:00:00'), +(9,1,'Tyronis',2,'2011-10-05 00:00:00'), +(9,1,'sicklebrick',0,'2013-03-13 00:00:00'), -- 2 -(10,'Paraxade0',2,'2011-04-21 00:00:00'), -(10,'PerOculos',0,'2011-04-21 00:00:00'), -(11,'Tyronis',2,'2011-10-05 00:00:00'), -(11,'Krzyhau',0,'2018-06-09 00:00:00'), -(12,'slmid1995',2,'2011-10-04 00:00:00'), -(13,'LookLikeAKango',3,'2011-10-05 00:00:00'), -(13,'Imanex',2,'2011-12-08 00:00:00'), -(13,'jyjey',0,'2012-08-22 00:00:00'), -(15,'Tyronis',2,'2011-10-05 00:00:00'), -(16,'LookLikeAKango',2,'2011-10-05 00:00:00'), -(16,'jyjey',0,'2012-08-25 00:00:00'), -(17,'rocoty',0,'2011-10-05 00:00:00'), -(17,'Nidboj132',0,'2023-02-05 00:00:00'), +(10,1,'Paraxade0',2,'2011-04-21 00:00:00'), +(10,1,'PerOculos',0,'2011-04-21 00:00:00'), +(11,1,'Tyronis',2,'2011-10-05 00:00:00'), +(11,1,'Krzyhau',0,'2018-06-09 00:00:00'), +(12,1,'slmid1995',2,'2011-10-04 00:00:00'), +(13,1,'LookLikeAKango',3,'2011-10-05 00:00:00'), +(13,1,'Imanex',2,'2011-12-08 00:00:00'), +(13,1,'jyjey',0,'2012-08-22 00:00:00'), +(15,1,'Tyronis',2,'2011-10-05 00:00:00'), +(16,1,'LookLikeAKango',2,'2011-10-05 00:00:00'), +(16,1,'jyjey',0,'2012-08-25 00:00:00'), +(17,1,'rocoty',0,'2011-10-05 00:00:00'), +(17,1,'Nidboj132',0,'2023-02-05 00:00:00'), -- 3 -(18,'The Last Tofus',5,'2011-05-08 00:00:00'), -(18,'Schlepian',4,'2011-10-08 00:00:00'), -(18,'szeimartin',3,'2013-10-08 00:00:00'), -(18,'Krzyhau',2,'2020-05-15 00:00:00'), -(18,'Krzyhau',0,'2022-07-02 00:00:00'), -(19,'LookLikeAKango',2,'2011-10-06 00:00:00'), -(20,'Djinndrache',5,'2011-10-20 00:00:00'), -(20,'Schlepian',4,'2011-10-30 00:00:00'), -(20,'Jetwash',3,'2014-09-04 00:00:00'), -(20,'Krzyhau',2,'2022-04-24 00:00:00'), -(21,'LookLikeAKango',4,'2011-10-06 00:00:00'), -(21,'ncla',2,'2011-10-30 00:00:00'), -(21,'PerOculos',0,'2019-07-08 00:00:00'), -(22,'Tyronis',0,'2011-10-05 00:00:00'), -(23,'LookLikeAKango',2,'2011-10-06 00:00:00'), -(23,'Krzyhau',0,'2018-08-01 00:00:00'), -(24,'LeviHB',0,'2011-04-30 00:00:00'), -(25,'Tyronis',0,'2011-10-06 00:00:00'), -(26,'Schlepian',3,'2011-10-30 00:00:00'), -(26,'Tyronis',2,'2012-01-08 00:00:00'), -(26,'PerOculos',0,'2016-06-08 00:00:00'), +(18,1,'The Last Tofus',5,'2011-05-08 00:00:00'), +(18,1,'Schlepian',4,'2011-10-08 00:00:00'), +(18,1,'szeimartin',3,'2013-10-08 00:00:00'), +(18,1,'Krzyhau',2,'2020-05-15 00:00:00'), +(18,1,'Krzyhau',0,'2022-07-02 00:00:00'), +(19,1,'LookLikeAKango',2,'2011-10-06 00:00:00'), +(20,1,'Djinndrache',5,'2011-10-20 00:00:00'), +(20,1,'Schlepian',4,'2011-10-30 00:00:00'), +(20,1,'Jetwash',3,'2014-09-04 00:00:00'), +(20,1,'Krzyhau',2,'2022-04-24 00:00:00'), +(21,1,'LookLikeAKango',4,'2011-10-06 00:00:00'), +(21,1,'ncla',2,'2011-10-30 00:00:00'), +(21,1,'PerOculos',0,'2019-07-08 00:00:00'), +(22,1,'Tyronis',0,'2011-10-05 00:00:00'), +(23,1,'LookLikeAKango',2,'2011-10-06 00:00:00'), +(23,1,'Krzyhau',0,'2018-08-01 00:00:00'), +(24,1,'LeviHB',0,'2011-04-30 00:00:00'), +(25,1,'Tyronis',0,'2011-10-06 00:00:00'), +(26,1,'Schlepian',3,'2011-10-30 00:00:00'), +(26,1,'Tyronis',2,'2012-01-08 00:00:00'), +(26,1,'PerOculos',0,'2016-06-08 00:00:00'), -- 4 -(27,'LeviHB',2,'2011-05-01 00:00:00'), -(27,'PerOculos',0,'2020-07-13 00:00:00'), -(28,'LeviHB',7,'2011-05-01 00:00:00'), -(28,'Andy M.J.',2,'2011-10-07 00:00:00'), -(28,'Krzyhau',0,'2018-05-19 00:00:00'), -(29,'LeviHB',0,'2011-05-01 00:00:00'), -(30,'Schlepian',2,'2011-10-30 00:00:00'), -(31,'Tyronis',0,'2011-10-06 00:00:00'), +(27,1,'LeviHB',2,'2011-05-01 00:00:00'), +(27,1,'PerOculos',0,'2020-07-13 00:00:00'), +(28,1,'LeviHB',7,'2011-05-01 00:00:00'), +(28,1,'Andy M.J.',2,'2011-10-07 00:00:00'), +(28,1,'Krzyhau',0,'2018-05-19 00:00:00'), +(29,1,'LeviHB',0,'2011-05-01 00:00:00'), +(30,1,'Schlepian',2,'2011-10-30 00:00:00'), +(31,1,'Tyronis',0,'2011-10-06 00:00:00'), -- 5 -(32,'Tyronis',6,'2011-10-21 00:00:00'), -(32,'Nidboj132',5,'2022-04-24 00:00:00'), -(33,'Tyronis',7,'2011-10-06 00:00:00'), -(33,'ISimmo',5,'2011-11-02 00:00:00'), -(33,'PerOculos',4,'2017-05-30 00:00:00'), -(34,'Schlepian',3,'2011-11-01 00:00:00'), -(34,'Krzyhau',2,'2020-10-14 00:00:00'), -(34,'zach',0,'2022-11-02 00:00:00'), -(35,'Krank',2,'2012-07-28 00:00:00'), +(32,1,'Tyronis',6,'2011-10-21 00:00:00'), +(32,1,'Nidboj132',5,'2022-04-24 00:00:00'), +(33,1,'Tyronis',7,'2011-10-06 00:00:00'), +(33,1,'ISimmo',5,'2011-11-02 00:00:00'), +(33,1,'PerOculos',4,'2017-05-30 00:00:00'), +(34,1,'Schlepian',3,'2011-11-01 00:00:00'), +(34,1,'Krzyhau',2,'2020-10-14 00:00:00'), +(34,1,'zach',0,'2022-11-02 00:00:00'), +(35,1,'Krank',2,'2012-07-28 00:00:00'), -- 6 -(36,'Tyronis',6,'2011-10-06 00:00:00'), -(36,'CalmlyFrenetic',5,'2011-10-09 00:00:00'), -(36,'sicklebrick',4,'2012-09-13 00:00:00'), -(36,'Nidboj132',2,'2023-03-04 00:00:00'), -(37,'LookLikeAKango',7,'2011-10-06 00:00:00'), -(37,'Schlepian',6,'2011-11-01 00:00:00'), -(37,'Tyronis',5,'2012-01-28 00:00:00'), -(37,'Nidboj132',4,'2021-08-22 00:00:00'), -(38,'Andy M.J.',2,'2011-10-06 00:00:00'), -(38,'Sanguine Dagger',0,'2012-03-19 00:00:00'), -(39,'Lambda Core',6,'2011-05-13 00:00:00'), -(39,'The Last Tofus',5,'2011-05-13 00:00:00'), -(39,'LookLikeAKango',4,'2011-10-16 00:00:00'), -(39,'Kittaye',3,'2013-03-25 00:00:00'), -(40,'LookLikeAKango',7,'2011-10-07 00:00:00'), -(40,'Schlepian',6,'2011-11-05 00:00:00'), -(40,'Kittaye',4,'2013-04-01 00:00:00'), -(40,'Kittaye',3,'2014-09-13 00:00:00'), -(40,'szeimartin',2,'2014-09-13 00:00:00'), -(40,'Kittaye',0,'2014-09-15 00:00:00'), -(41,'CalmlyFrenetic',7,'2011-10-09 00:00:00'), -(41,'Jaso',6,'2011-10-11 00:00:00'), -(41,'Krank',5,'2012-07-17 00:00:00'), +(36,1,'Tyronis',6,'2011-10-06 00:00:00'), +(36,1,'CalmlyFrenetic',5,'2011-10-09 00:00:00'), +(36,1,'sicklebrick',4,'2012-09-13 00:00:00'), +(36,1,'Nidboj132',2,'2023-03-04 00:00:00'), +(37,1,'LookLikeAKango',7,'2011-10-06 00:00:00'), +(37,1,'Schlepian',6,'2011-11-01 00:00:00'), +(37,1,'Tyronis',5,'2012-01-28 00:00:00'), +(37,1,'Nidboj132',4,'2021-08-22 00:00:00'), +(38,1,'Andy M.J.',2,'2011-10-06 00:00:00'), +(38,1,'Sanguine Dagger',0,'2012-03-19 00:00:00'), +(39,1,'Lambda Core',6,'2011-05-13 00:00:00'), +(39,1,'The Last Tofus',5,'2011-05-13 00:00:00'), +(39,1,'LookLikeAKango',4,'2011-10-16 00:00:00'), +(39,1,'Kittaye',3,'2013-03-25 00:00:00'), +(40,1,'LookLikeAKango',7,'2011-10-07 00:00:00'), +(40,1,'Schlepian',6,'2011-11-05 00:00:00'), +(40,1,'Kittaye',4,'2013-04-01 00:00:00'), +(40,1,'Kittaye',3,'2014-09-13 00:00:00'), +(40,1,'szeimartin',2,'2014-09-13 00:00:00'), +(40,1,'Kittaye',0,'2014-09-15 00:00:00'), +(41,1,'CalmlyFrenetic',7,'2011-10-09 00:00:00'), +(41,1,'Jaso',6,'2011-10-11 00:00:00'), +(41,1,'Krank',5,'2012-07-17 00:00:00'), -- 7 -(42,'LookLikeAKango',4,'2011-05-17 00:00:00'), -(42,'ISimmo',2,'2011-11-07 00:00:00'), -(43,'lmao4ever',5,'2011-10-30 00:00:00'), -(43,'Jaso',2,'2011-11-09 00:00:00'), -(43,'feliser',0,'2022-06-26 00:00:00'), -(44,'LookLikeAKango',18,'2011-10-07 00:00:00'), -(44,'Tyronis',13,'2011-10-30 00:00:00'), -(44,'Tyronis',12,'2011-11-10 00:00:00'), -(44,'Jetwash',11,'2017-06-12 00:00:00'), -(44,'Krzyhau',9,'2022-01-02 00:00:00'), -(45,'LookLikeAKango',23,'2011-10-08 00:00:00'), -(45,'CalmlyFrenetic',22,'2011-10-09 00:00:00'), -(45,'cgreactor',17,'2011-10-09 00:00:00'), -(45,'CalmlyFrenetic',16,'2011-10-10 00:00:00'), -(45,'LookLikeAKango',15,'2011-10-19 00:00:00'), -(45,'Jaso',12,'2012-07-19 00:00:00'), -(45,'Krank',10,'2013-01-31 00:00:00'), -(45,'Kittaye',7,'2013-04-04 00:00:00'), -(45,'PerOculos',4,'2014-09-13 00:00:00'), +(42,1,'LookLikeAKango',4,'2011-05-17 00:00:00'), +(42,1,'ISimmo',2,'2011-11-07 00:00:00'), +(43,1,'lmao4ever',5,'2011-10-30 00:00:00'), +(43,1,'Jaso',2,'2011-11-09 00:00:00'), +(43,1,'feliser',0,'2022-06-26 00:00:00'), +(44,1,'LookLikeAKango',18,'2011-10-07 00:00:00'), +(44,1,'Tyronis',13,'2011-10-30 00:00:00'), +(44,1,'Tyronis',12,'2011-11-10 00:00:00'), +(44,1,'Jetwash',11,'2017-06-12 00:00:00'), +(44,1,'Krzyhau',9,'2022-01-02 00:00:00'), +(45,1,'LookLikeAKango',23,'2011-10-08 00:00:00'), +(45,1,'CalmlyFrenetic',22,'2011-10-09 00:00:00'), +(45,1,'cgreactor',17,'2011-10-09 00:00:00'), +(45,1,'CalmlyFrenetic',16,'2011-10-10 00:00:00'), +(45,1,'LookLikeAKango',15,'2011-10-19 00:00:00'), +(45,1,'Jaso',12,'2012-07-19 00:00:00'), +(45,1,'Krank',10,'2013-01-31 00:00:00'), +(45,1,'Kittaye',7,'2013-04-04 00:00:00'), +(45,1,'PerOculos',4,'2014-09-13 00:00:00'), -- 8 -(46,'sparkle1princess',6,'2012-03-24 00:00:00'), -(46,'Krzyhau',2,'2019-11-21 00:00:00'), -(47,'holydevel',2,'2011-10-06 00:00:00'), -(47,'JesusCatFace',0,'2015-01-16 00:00:00'), -(48,'LookLikeAKango',5,'2011-10-08 00:00:00'), -(48,'Tyronis',2,'2011-10-08 00:00:00'), -(48,'adzicents',0,'2011-10-09 00:00:00'), -(49,'adzicents',4,'2011-10-07 00:00:00'), -(49,'Schlepian',2,'2011-10-08 00:00:00'), -(49,'Nidboj132',0,'2022-09-26 00:00:00'), -(50,'LookLikeAKango',4,'2011-10-08 00:00:00'), -(50,'Tyronis',2,'2011-10-11 00:00:00'), -(50,'sicklebrick',0,'2013-03-20 00:00:00'), -(51,'Andy M.J.',3,'2011-10-08 00:00:00'), -(51,'LookLikeAKango',2,'2011-10-20 00:00:00'), -(52,'Jaso',0,'2011-10-10 00:00:00'), -(53,'LookLikeAKango',9,'2011-10-08 00:00:00'), -(53,'LookLikeAKango',2,'2011-10-20 00:00:00'), -(53,'Schlepian',0,'2011-11-06 00:00:00'), -(54,'LookLikeAKango',7,'2011-06-01 00:00:00'), -(54,'Jaso',6,'2011-10-09 00:00:00'), -(54,'Schlepian',5,'2011-11-06 00:00:00'), -(54,'Spyrunite',4,'2012-08-30 00:00:00'), -(54,'Krzyhau',3,'2019-04-22 00:00:00'), -(55,'LookLikeAKango',7,'2011-10-08 00:00:00'), -(55,'CalmlyFrenetic',3,'2011-10-09 00:00:00'), -(55,'Jaso',2,'2011-11-26 00:00:00'), -(55,'PerOculos',0,'2021-02-06 00:00:00'), -(56,'CalmlyFrenetic',9,'2011-10-08 00:00:00'), -(56,'LookLikeAKango',5,'2011-10-09 00:00:00'), -(56,'CalmlyFrenetic',4,'2011-10-09 00:00:00'), -(56,'Jetwash',2,'2014-09-05 00:00:00'), +(46,1,'sparkle1princess',6,'2012-03-24 00:00:00'), +(46,1,'Krzyhau',2,'2019-11-21 00:00:00'), +(47,1,'holydevel',2,'2011-10-06 00:00:00'), +(47,1,'JesusCatFace',0,'2015-01-16 00:00:00'), +(48,1,'LookLikeAKango',5,'2011-10-08 00:00:00'), +(48,1,'Tyronis',2,'2011-10-08 00:00:00'), +(48,1,'adzicents',0,'2011-10-09 00:00:00'), +(49,1,'adzicents',4,'2011-10-07 00:00:00'), +(49,1,'Schlepian',2,'2011-10-08 00:00:00'), +(49,1,'Nidboj132',0,'2022-09-26 00:00:00'), +(50,1,'LookLikeAKango',4,'2011-10-08 00:00:00'), +(50,1,'Tyronis',2,'2011-10-11 00:00:00'), +(50,1,'sicklebrick',0,'2013-03-20 00:00:00'), +(51,1,'Andy M.J.',3,'2011-10-08 00:00:00'), +(51,1,'LookLikeAKango',2,'2011-10-20 00:00:00'), +(52,1,'Jaso',0,'2011-10-10 00:00:00'), +(53,1,'LookLikeAKango',9,'2011-10-08 00:00:00'), +(53,1,'LookLikeAKango',2,'2011-10-20 00:00:00'), +(53,1,'Schlepian',0,'2011-11-06 00:00:00'), +(54,1,'LookLikeAKango',7,'2011-06-01 00:00:00'), +(54,1,'Jaso',6,'2011-10-09 00:00:00'), +(54,1,'Schlepian',5,'2011-11-06 00:00:00'), +(54,1,'Spyrunite',4,'2012-08-30 00:00:00'), +(54,1,'Krzyhau',3,'2019-04-22 00:00:00'), +(55,1,'LookLikeAKango',7,'2011-10-08 00:00:00'), +(55,1,'CalmlyFrenetic',3,'2011-10-09 00:00:00'), +(55,1,'Jaso',2,'2011-11-26 00:00:00'), +(55,1,'PerOculos',0,'2021-02-06 00:00:00'), +(56,1,'CalmlyFrenetic',9,'2011-10-08 00:00:00'), +(56,1,'LookLikeAKango',5,'2011-10-09 00:00:00'), +(56,1,'CalmlyFrenetic',4,'2011-10-09 00:00:00'), +(56,1,'Jetwash',2,'2014-09-05 00:00:00'), -- 9 -(57,'JNS',7,'2011-07-21 00:00:00'), -(57,'Krank',5,'2012-07-29 00:00:00'), -(57,'Krzyhau',0,'2017-10-29 00:00:00'), -(58,'Stimich',2,'2011-10-11 00:00:00'), -(59,'Isimmo',7,'2011-11-04 00:00:00'), -(59,'sicklebrick',6,'2013-03-20 00:00:00'), -(60,'CalmlyFrenetic',7,'2011-10-19 00:00:00'), -(60,'Tyronis',6,'2011-11-01 00:00:00'), +(57,1,'JNS',7,'2011-07-21 00:00:00'), +(57,1,'Krank',5,'2012-07-29 00:00:00'), +(57,1,'Krzyhau',0,'2017-10-29 00:00:00'), +(58,1,'Stimich',2,'2011-10-11 00:00:00'), +(59,1,'Isimmo',7,'2011-11-04 00:00:00'), +(59,1,'sicklebrick',6,'2013-03-20 00:00:00'), +(60,1,'CalmlyFrenetic',7,'2011-10-19 00:00:00'), +(60,1,'Tyronis',6,'2011-11-01 00:00:00'), -- Portal 2 Cooperative -- 1 -(63,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), -(64,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), -(64,'Chubfish & Exhale',2,'2011-11-01 00:00:00'), -(65,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), -(65,'Nidboj132 & Oryn',3,'2022-02-03 00:00:00'), -(66,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), -(66,'Schlepian & Chubfish',2,'2011-10-01 00:00:00'), -(67,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), -(68,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), +(63,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), +(64,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), +(64,1,'Chubfish & Exhale',2,'2011-11-01 00:00:00'), +(65,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), +(65,1,'Nidboj132 & Oryn',3,'2022-02-03 00:00:00'), +(66,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), +(66,1,'Schlepian & Chubfish',2,'2011-10-01 00:00:00'), +(67,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), +(68,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), -- 2 -(69,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), -(70,'Mathias123961 & Sir Spawn Alot',6,'2011-08-01 00:00:00'), -(70,'Schlepian & Chubfish',4,'2011-10-01 00:00:00'), -(70,'Gocnak & z1mb0bw4y',2,'2012-08-03 00:00:00'), -(70,'DM_ & VEGA',0,'2017-10-01 00:00:00'), -(71,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), -(71,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), -(72,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), -(72,'Schlepian & LongJohnDickWeed',2,'2011-10-01 00:00:00'), -(73,'Stimich & HiTMaRkS',9,'2011-05-09 00:00:00'), -(73,'Mathias123961 & Sir Spawn Alot',8,'2011-08-01 00:00:00'), -(73,'Schlepian & Lemonsunshine',7,'2011-11-01 00:00:00'), -(73,'DM_ & LsDK_',6,'2018-01-01 00:00:00'), -(73,'Krzyhau & Klooger',4,'2018-11-01 00:00:00'), -(74,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01 00:00:00'), -(74,'Stimich & Pitkakorva',7,'2011-10-11 00:00:00'), -(74,'Schlepian & Isimmo',3,'2011-10-28 00:00:00'), -(74,'Zypeh & szeimartin',2,'2013-11-01 00:00:00'), -(75,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01 00:00:00'), -(75,'Schlepian & Urination',4,'2011-10-01 00:00:00'), -(75,'Schlepian & Lemonsunshine',2,'2012-02-01 00:00:00'), -(75,'DM_ & follon',0,'2015-04-01 00:00:00'), -(76,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), -(76,'Chubfish & Exhale',0,'2011-12-01 00:00:00'), +(69,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), +(70,1,'Mathias123961 & Sir Spawn Alot',6,'2011-08-01 00:00:00'), +(70,1,'Schlepian & Chubfish',4,'2011-10-01 00:00:00'), +(70,1,'Gocnak & z1mb0bw4y',2,'2012-08-03 00:00:00'), +(70,1,'DM_ & VEGA',0,'2017-10-01 00:00:00'), +(71,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), +(71,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), +(72,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), +(72,1,'Schlepian & LongJohnDickWeed',2,'2011-10-01 00:00:00'), +(73,1,'Stimich & HiTMaRkS',9,'2011-05-09 00:00:00'), +(73,1,'Mathias123961 & Sir Spawn Alot',8,'2011-08-01 00:00:00'), +(73,1,'Schlepian & Lemonsunshine',7,'2011-11-01 00:00:00'), +(73,1,'DM_ & LsDK_',6,'2018-01-01 00:00:00'), +(73,1,'Krzyhau & Klooger',4,'2018-11-01 00:00:00'), +(74,1,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01 00:00:00'), +(74,1,'Stimich & Pitkakorva',7,'2011-10-11 00:00:00'), +(74,1,'Schlepian & Isimmo',3,'2011-10-28 00:00:00'), +(74,1,'Zypeh & szeimartin',2,'2013-11-01 00:00:00'), +(75,1,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01 00:00:00'), +(75,1,'Schlepian & Urination',4,'2011-10-01 00:00:00'), +(75,1,'Schlepian & Lemonsunshine',2,'2012-02-01 00:00:00'), +(75,1,'DM_ & follon',0,'2015-04-01 00:00:00'), +(76,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), +(76,1,'Chubfish & Exhale',0,'2011-12-01 00:00:00'), -- 3 -(77,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), -(78,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), -(78,'DM_ & marK',3,'2016-11-01 00:00:00'), -(78,'Nidboj132 & Oryn',2,'2021-09-04 00:00:00'), -(79,'ganonscrub & ?',5,'2011-04-01 00:00:00'), -(79,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), -(79,'Chubfish & Exhale',2,'2012-08-04 00:00:00'), -(80,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01 00:00:00'), -(80,'Chubfish & Exhale',4,'2011-12-01 00:00:00'), -(81,'Mathias123961 & Sir Spawn Alot',7,'2011-08-01 00:00:00'), -(81,'Schlepian & Lemonsunshine',6,'2011-10-01 00:00:00'), -(81,'takz & dawn',5,'2011-11-01 00:00:00'), -(81,'Nidboj132 & Oryn',4,'2021-03-25 00:00:00'), -(82,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), -(83,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01 00:00:00'), -(83,'Schlepian & Lemonsunshine',2,'2011-10-01 00:00:00'), -(83,'Chubfish & Exhale',0,'2011-12-01 00:00:00'), -(84,'Mathias123961 & Sir Spawn Alot',6,'2011-08-01 00:00:00'), -(84,'Schlepian & Chubfish',4,'2011-10-01 00:00:00'), -(84,'Chubfish & Exhale',2,'2012-01-01 00:00:00'), -(84,'DM_ & wS',0,'2015-05-01 00:00:00'), +(77,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), +(78,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), +(78,1,'DM_ & marK',3,'2016-11-01 00:00:00'), +(78,1,'Nidboj132 & Oryn',2,'2021-09-04 00:00:00'), +(79,1,'ganonscrub & ?',5,'2011-04-01 00:00:00'), +(79,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), +(79,1,'Chubfish & Exhale',2,'2012-08-04 00:00:00'), +(80,1,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01 00:00:00'), +(80,1,'Chubfish & Exhale',4,'2011-12-01 00:00:00'), +(81,1,'Mathias123961 & Sir Spawn Alot',7,'2011-08-01 00:00:00'), +(81,1,'Schlepian & Lemonsunshine',6,'2011-10-01 00:00:00'), +(81,1,'takz & dawn',5,'2011-11-01 00:00:00'), +(81,1,'Nidboj132 & Oryn',4,'2021-03-25 00:00:00'), +(82,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), +(83,1,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01 00:00:00'), +(83,1,'Schlepian & Lemonsunshine',2,'2011-10-01 00:00:00'), +(83,1,'Chubfish & Exhale',0,'2011-12-01 00:00:00'), +(84,1,'Mathias123961 & Sir Spawn Alot',6,'2011-08-01 00:00:00'), +(84,1,'Schlepian & Chubfish',4,'2011-10-01 00:00:00'), +(84,1,'Chubfish & Exhale',2,'2012-01-01 00:00:00'), +(84,1,'DM_ & wS',0,'2015-05-01 00:00:00'), -- 4 -(85,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), -(85,'Chubfish & Exhale',0,'2011-10-01 00:00:00'), -(86,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), -(86,'Chubfish & Exhale',0,'2011-12-01 00:00:00'), -(87,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), -(87,'Schlepian & Gopherdude',2,'2011-10-01 00:00:00'), -(87,'DM_ & follon',0,'2015-04-01 00:00:00'), -(88,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), -(88,'Schlepian & Gopherdude',0,'2011-10-01 00:00:00'), -(89,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), -(90,'Mathias123961 & Sir Spawn Alot',4,'2011-09-01 00:00:00'), -(90,'Schlepian & Urination',2,'2011-10-01 00:00:00'), -(90,'Klooger & Jetwash',0,'2016-08-01 00:00:00'), -(91,'Mathias123961 & Sir Spawn Alot',2,'2011-08-01 00:00:00'), -(91,'Undead & Zypeh',0,'2013-05-19 00:00:00'), -(92,'txx478 & ?',5,'2011-05-01 00:00:00'), -(92,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), -(92,'Schlepian & Gopherdude',2,'2011-10-01 00:00:00'), -(92,'ncla & takz',0,'2012-02-01 00:00:00'), -(93,'Mathias123961 & Sir Spawn Alot',2,'2011-08-01 00:00:00'), -(93,'Schlepian & Gopherdude',0,'2011-10-01 00:00:00'), +(85,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), +(85,1,'Chubfish & Exhale',0,'2011-10-01 00:00:00'), +(86,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), +(86,1,'Chubfish & Exhale',0,'2011-12-01 00:00:00'), +(87,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), +(87,1,'Schlepian & Gopherdude',2,'2011-10-01 00:00:00'), +(87,1,'DM_ & follon',0,'2015-04-01 00:00:00'), +(88,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), +(88,1,'Schlepian & Gopherdude',0,'2011-10-01 00:00:00'), +(89,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), +(90,1,'Mathias123961 & Sir Spawn Alot',4,'2011-09-01 00:00:00'), +(90,1,'Schlepian & Urination',2,'2011-10-01 00:00:00'), +(90,1,'Klooger & Jetwash',0,'2016-08-01 00:00:00'), +(91,1,'Mathias123961 & Sir Spawn Alot',2,'2011-08-01 00:00:00'), +(91,1,'Undead & Zypeh',0,'2013-05-19 00:00:00'), +(92,1,'txx478 & ?',5,'2011-05-01 00:00:00'), +(92,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), +(92,1,'Schlepian & Gopherdude',2,'2011-10-01 00:00:00'), +(92,1,'ncla & takz',0,'2012-02-01 00:00:00'), +(93,1,'Mathias123961 & Sir Spawn Alot',2,'2011-08-01 00:00:00'), +(93,1,'Schlepian & Gopherdude',0,'2011-10-01 00:00:00'), -- 5 -(94,'Chubfish & Exhale',2,'2011-10-01 00:00:00'), -(94,'Klooger & Imanex',0,'2013-08-01 00:00:00'), -(95,'Schlepian & Issimoi',2,'2011-10-01 00:00:00'), -(96,'ThePortalPatrol & ?',4,'2011-04-01 00:00:00'), -(96,'sparkle1princess & Zypeh',2,'2014-01-01 00:00:00'), -(97,'Stimich & HiTMaRkS',7,'2011-05-13 00:00:00'), -(97,'Schlepian & Lemonsunshine',4,'2011-10-01 00:00:00'), -(97,'DM_ & wS',2,'2014-05-01 00:00:00'), -(98,'Imanex & 00svo',0,'2011-11-01 00:00:00'), -(99,'Schlepian & Gopherdude',3,'2011-10-01 00:00:00'), -(99,'Imanex & Klooger',2,'2013-08-01 00:00:00'), -(99,'DM_ & wS',0,'2015-05-01 00:00:00'), -(100,'Schlepian & Bananasaurus Rex',0,'2011-10-01 00:00:00'), -(101,'Chubfish & Exhale',2,'2011-12-01 00:00:00'), -(101,'DM_ & follon',0,'2015-04-01 00:00:00'), +(94,1,'Chubfish & Exhale',2,'2011-10-01 00:00:00'), +(94,1,'Klooger & Imanex',0,'2013-08-01 00:00:00'), +(95,1,'Schlepian & Issimoi',2,'2011-10-01 00:00:00'), +(96,1,'ThePortalPatrol & ?',4,'2011-04-01 00:00:00'), +(96,1,'sparkle1princess & Zypeh',2,'2014-01-01 00:00:00'), +(97,1,'Stimich & HiTMaRkS',7,'2011-05-13 00:00:00'), +(97,1,'Schlepian & Lemonsunshine',4,'2011-10-01 00:00:00'), +(97,1,'DM_ & wS',2,'2014-05-01 00:00:00'), +(98,1,'Imanex & 00svo',0,'2011-11-01 00:00:00'), +(99,1,'Schlepian & Gopherdude',3,'2011-10-01 00:00:00'), +(99,1,'Imanex & Klooger',2,'2013-08-01 00:00:00'), +(99,1,'DM_ & wS',0,'2015-05-01 00:00:00'), +(100,1,'Schlepian & Bananasaurus Rex',0,'2011-10-01 00:00:00'), +(101,1,'Chubfish & Exhale',2,'2011-12-01 00:00:00'), +(101,1,'DM_ & follon',0,'2015-04-01 00:00:00'), -- 6 -(102,'dawn & takz',3,'2011-11-18 00:00:00'), -(102,'Chubfish & Exhale',2,'2012-01-01 00:00:00'), -(102,'Imanex & Klooger',0,'2013-08-01 00:00:00'), -(103,'Schlepian & Lemonsunshine',0,'2011-10-01 00:00:00'), -(104,'Schlepian & Lemonsunshine',0,'2011-10-01 00:00:00'), -(105,'Blaizerazer & ?',8,'2011-10-01 00:00:00'), -(105,'Schlepian & Lemonsunshine',5,'2011-11-01 00:00:00'), -(105,'Imanex & Klooger',4,'2013-08-01 00:00:00'), -(105,'DM_ & wS',3,'2014-05-01 00:00:00'), -(105,'DM_ & follon',2,'2015-04-01 00:00:00'), -(106,'Schlepian & Bananasaurus Rex',4,'2011-10-01 00:00:00'), -(106,'Gig & takz',3,'2012-06-01 00:00:00'), -(106,'Imanex & Klooger',0,'2013-06-01 00:00:00'), -(107,'Chubfish & Exhale',2,'2011-10-01 00:00:00'), -(107,'DM_ & follon',0,'2015-04-01 00:00:00'), -(108,'DaFox & P',0,'2011-12-01 00:00:00'), -(109,'Schlepian & Tyronis',5,'2011-10-01 00:00:00'), -(109,'Chubfish & Exhale',0,'2011-12-01 00:00:00'), -(110,'Tyronis & mr.bob806',15,'2011-10-01 00:00:00'), -(110,'Schlepian & Chubfish',6,'2011-11-01 00:00:00'), -(110,'00svo & z1mb0bw4y',5,'2012-08-08 00:00:00'), -(110,'00svo & z1mb0bw4y',4,'2012-08-10 00:00:00'), -(110,'Klooger & z1mb0bw4y',2,'2014-02-01 00:00:00'), -(110,'DM_ & follon',0,'2015-04-01 00:00:00'); \ No newline at end of file +(102,1,'dawn & takz',3,'2011-11-18 00:00:00'), +(102,1,'Chubfish & Exhale',2,'2012-01-01 00:00:00'), +(102,1,'Imanex & Klooger',0,'2013-08-01 00:00:00'), +(103,1,'Schlepian & Lemonsunshine',0,'2011-10-01 00:00:00'), +(104,1,'Schlepian & Lemonsunshine',0,'2011-10-01 00:00:00'), +(105,1,'Blaizerazer & ?',8,'2011-10-01 00:00:00'), +(105,1,'Schlepian & Lemonsunshine',5,'2011-11-01 00:00:00'), +(105,1,'Imanex & Klooger',4,'2013-08-01 00:00:00'), +(105,1,'DM_ & wS',3,'2014-05-01 00:00:00'), +(105,1,'DM_ & follon',2,'2015-04-01 00:00:00'), +(106,1,'Schlepian & Bananasaurus Rex',4,'2011-10-01 00:00:00'), +(106,1,'Gig & takz',3,'2012-06-01 00:00:00'), +(106,1,'Imanex & Klooger',0,'2013-06-01 00:00:00'), +(107,1,'Chubfish & Exhale',2,'2011-10-01 00:00:00'), +(107,1,'DM_ & follon',0,'2015-04-01 00:00:00'), +(108,1,'DaFox & P',0,'2011-12-01 00:00:00'), +(109,1,'Schlepian & Tyronis',5,'2011-10-01 00:00:00'), +(109,1,'Chubfish & Exhale',0,'2011-12-01 00:00:00'), +(110,1,'Tyronis & mr.bob806',15,'2011-10-01 00:00:00'), +(110,1,'Schlepian & Chubfish',6,'2011-11-01 00:00:00'), +(110,1,'00svo & z1mb0bw4y',5,'2012-08-08 00:00:00'), +(110,1,'00svo & z1mb0bw4y',4,'2012-08-10 00:00:00'), +(110,1,'Klooger & z1mb0bw4y',2,'2014-02-01 00:00:00'), +(110,1,'DM_ & follon',0,'2015-04-01 00:00:00'); \ No newline at end of file diff --git a/backend/database/init.sql b/backend/database/init.sql index 871aba2..76c3aa6 100644 --- a/backend/database/init.sql +++ b/backend/database/init.sql @@ -54,20 +54,24 @@ CREATE TABLE map_routes ( CREATE TABLE map_history ( id SMALLSERIAL, map_id SMALLINT NOT NULL, + category_id SMALLINT NOT NULL, user_name TEXT NOT NULL, score_count SMALLINT NOT NULL, record_date TIMESTAMP NOT NULL, PRIMARY KEY (id), + FOREIGN KEY (category_id) REFERENCES categories(id), FOREIGN KEY (map_id) REFERENCES maps(id) ); CREATE TABLE map_ratings ( id SERIAL, map_id SMALLINT NOT NULL, + category_id SMALLINT NOT NULL, user_id TEXT NOT NULL, rating SMALLINT NOT NULL, PRIMARY KEY (id), FOREIGN KEY (map_id) REFERENCES maps(id), + FOREIGN KEY (category_id) REFERENCES categories(id), FOREIGN KEY (user_id) REFERENCES users(steam_id) ); diff --git a/backend/database/route.sql b/backend/database/route.sql new file mode 100644 index 0000000..6f090c7 --- /dev/null +++ b/backend/database/route.sql @@ -0,0 +1,279 @@ +INSERT INTO map_routes(map_id,category_id,score_count,description,showcase) VALUES +-- Portal 2 Singleplayer +-- 1 +(3,1,3,'',''), +(3,1,1,'',''), +(3,1,0,'',''), +(4,1,1,'',''), +(4,1,0,'',''), +(5,1,2,'',''), +(5,1,1,'',''), +(6,1,4,'',''), +(6,1,3,'',''), +(6,1,2,'',''), +(9,1,4,'',''), +(9,1,3,'',''), +(9,1,2,'',''), +(9,1,0,'',''), +-- 2 +(10,1,2,'',''), +(10,1,0,'',''), +(11,1,2,'',''), +(11,1,0,'',''), +(12,1,2,'',''), +(13,1,3,'',''), +(13,1,2,'',''), +(13,1,0,'',''), +(15,1,2,'',''), +(16,1,2,'',''), +(16,1,0,'',''), +(17,1,0,'',''), +(17,1,0,'',''), +-- 3 +(18,1,5,'',''), +(18,1,4,'',''), +(18,1,3,'',''), +(18,1,2,'',''), +(18,1,0,'',''), +(19,1,2,'',''), +(20,1,5,'',''), +(20,1,4,'',''), +(20,1,3,'',''), +(20,1,2,'',''), +(21,1,4,'',''), +(21,1,2,'',''), +(21,1,0,'',''), +(22,1,0,'',''), +(23,1,2,'',''), +(23,1,0,'',''), +(24,1,0,'',''), +(25,1,0,'',''), +(26,1,3,'',''), +(26,1,2,'',''), +(26,1,0,'',''), +-- 4 +(27,1,2,'',''), +(27,1,0,'',''), +(28,1,7,'',''), +(28,1,2,'',''), +(28,1,0,'',''), +(29,1,0,'',''), +(30,1,2,'',''), +(31,1,0,'',''), +-- 5 +(32,1,6,'',''), +(32,1,5,'',''), +(33,1,7,'',''), +(33,1,5,'',''), +(33,1,4,'',''), +(34,1,3,'',''), +(34,1,2,'',''), +(34,1,0,'',''), +(35,1,2,'',''), +-- 6 +(36,1,6,'',''), +(36,1,5,'',''), +(36,1,4,'',''), +(36,1,2,'',''), +(37,1,7,'',''), +(37,1,6,'',''), +(37,1,5,'',''), +(37,1,4,'',''), +(38,1,2,'',''), +(38,1,0,'',''), +(39,1,6,'',''), +(39,1,5,'',''), +(39,1,4,'',''), +(39,1,3,'',''), +(40,1,7,'',''), +(40,1,6,'',''), +(40,1,4,'',''), +(40,1,3,'',''), +(40,1,2,'',''), +(40,1,0,'',''), +(41,1,7,'',''), +(41,1,6,'',''), +(41,1,5,'',''), +-- 7 +(42,1,4,'',''), +(42,1,2,'',''), +(43,1,5,'',''), +(43,1,2,'',''), +(43,1,0,'',''), +(44,1,18,'',''), +(44,1,13,'',''), +(44,1,12,'',''), +(44,1,11,'',''), +(44,1,9,'',''), +(45,1,23,'',''), +(45,1,22,'',''), +(45,1,17,'',''), +(45,1,16,'',''), +(45,1,15,'',''), +(45,1,12,'',''), +(45,1,10,'',''), +(45,1,7,'',''), +(45,1,4,'',''), +-- 8 +(46,1,6,'',''), +(46,1,2,'',''), +(47,1,2,'',''), +(47,1,0,'',''), +(48,1,5,'',''), +(48,1,2,'',''), +(48,1,0,'',''), +(49,1,4,'',''), +(49,1,2,'',''), +(49,1,0,'',''), +(50,1,4,'',''), +(50,1,2,'',''), +(50,1,0,'',''), +(51,1,3,'',''), +(51,1,2,'',''), +(52,1,0,'',''), +(53,1,9,'',''), +(53,1,2,'',''), +(53,1,0,'',''), +(54,1,7,'',''), +(54,1,6,'',''), +(54,1,5,'',''), +(54,1,4,'',''), +(54,1,3,'',''), +(55,1,7,'',''), +(55,1,3,'',''), +(55,1,2,'',''), +(55,1,0,'',''), +(56,1,9,'',''), +(56,1,5,'',''), +(56,1,4,'',''), +(56,1,2,'',''), +-- 9 +(57,1,7,'',''), +(57,1,5,'',''), +(57,1,0,'',''), +(58,1,2,'',''), +(59,1,7,'',''), +(59,1,6,'',''), +(60,1,7,'',''), +(60,1,6,'',''), +-- Portal 2 Cooperative +-- 1 +(63,1,0,'',''), +(64,1,3,'',''), +(64,1,2,'',''), +(65,1,4,'',''), +(65,1,3,'',''), +(66,1,3,'',''), +(66,1,2,'',''), +(67,1,0,'',''), +(68,1,0,'',''), +-- 2 +(69,1,4,'',''), +(70,1,6,'',''), +(70,1,4,'',''), +(70,1,2,'',''), +(70,1,0,'',''), +(71,1,3,'',''), +(71,1,0,'',''), +(72,1,4,'',''), +(72,1,2,'',''), +(73,1,9,'',''), +(73,1,8,'',''), +(73,1,7,'',''), +(73,1,6,'',''), +(73,1,4,'',''), +(74,1,5,'',''), +(74,1,7,'',''), +(74,1,3,'',''), +(74,1,2,'',''), +(75,1,5,'',''), +(75,1,4,'',''), +(75,1,2,'',''), +(75,1,0,'',''), +(76,1,3,'',''), +(76,1,0,'',''), +-- 3 +(77,1,3,'',''), +(78,1,4,'',''), +(78,1,3,'',''), +(78,1,2,'',''), +(79,1,5,'',''), +(79,1,4,'',''), +(79,1,2,'',''), +(80,1,5,'',''), +(80,1,4,'',''), +(81,1,7,'',''), +(81,1,6,'',''), +(81,1,5,'',''), +(81,1,4,'',''), +(82,1,4,'',''), +(83,1,5,'',''), +(83,1,2,'',''), +(83,1,0,'',''), +(84,1,6,'',''), +(84,1,4,'',''), +(84,1,2,'',''), +(84,1,0,'',''), +-- 4 +(85,1,3,'',''), +(85,1,0,'',''), +(86,1,3,'',''), +(86,1,0,'',''), +(87,1,3,'',''), +(87,1,2,'',''), +(87,1,0,'',''), +(88,1,4,'',''), +(88,1,0,'',''), +(89,1,0,'',''), +(90,1,4,'',''), +(90,1,2,'',''), +(90,1,0,'',''), +(91,1,2,'',''), +(91,1,0,'',''), +(92,1,5,'',''), +(92,1,4,'',''), +(92,1,2,'',''), +(92,1,0,'',''), +(93,1,2,'',''), +(93,1,0,'',''), +-- 5 +(94,1,2,'',''), +(94,1,0,'',''), +(95,1,2,'',''), +(96,1,4,'',''), +(96,1,2,'',''), +(97,1,7,'',''), +(97,1,4,'',''), +(97,1,2,'',''), +(98,1,0,'',''), +(99,1,3,'',''), +(99,1,2,'',''), +(99,1,0,'',''), +(100,1,0,'',''), +(101,1,2,'',''), +(101,1,0,'',''), +-- 6 +(102,1,3,'',''), +(102,1,2,'',''), +(102,1,0,'',''), +(103,1,0,'',''), +(104,1,0,'',''), +(105,1,8,'',''), +(105,1,5,'',''), +(105,1,4,'',''), +(105,1,3,'',''), +(105,1,2,'',''), +(106,1,4,'',''), +(106,1,3,'',''), +(106,1,0,'',''), +(107,1,2,'',''), +(107,1,0,'',''), +(108,1,0,'',''), +(109,1,5,'',''), +(109,1,0,'',''), +(110,1,15,'',''), +(110,1,6,'',''), +(110,1,5,'',''), +(110,1,4,'',''), +(110,1,2,'',''), +(110,1,0,'',''); \ No newline at end of file diff --git a/backend/models/models.go b/backend/models/models.go index 5c9b8f8..783c339 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -31,9 +31,7 @@ type MapShort struct { } type MapSummary struct { - Rating float32 `json:"rating"` - History []MapHistory `json:"history"` - Routes []MapRoute `json:"routes"` + Routes []MapRoute `json:"routes"` } type MapHistory struct { @@ -43,10 +41,12 @@ type MapHistory struct { } type MapRoute struct { - Category Category `json:"category"` - ScoreCount int `json:"score_count"` - Description string `json:"description"` - Showcase string `json:"showcase"` + Category Category `json:"category"` + History MapHistory `json:"history"` + Rating float32 `json:"rating"` + ScoreCount int `json:"score_count"` + Description string `json:"description"` + Showcase string `json:"showcase"` } type MapRecords struct { -- cgit v1.2.3 From 4f9b489186d28055f60e3b5ef39bd27c7a229984 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Tue, 20 Jun 2023 20:09:02 +0300 Subject: docs: map summary response (#46) --- docs/docs.go | 15 ++++++--------- docs/swagger.json | 15 ++++++--------- docs/swagger.yaml | 10 ++++------ 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index cf6c00d..b7dc7ab 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -908,6 +908,12 @@ const docTemplate = `{ "description": { "type": "string" }, + "history": { + "$ref": "#/definitions/models.MapHistory" + }, + "rating": { + "type": "number" + }, "score_count": { "type": "integer" }, @@ -930,15 +936,6 @@ const docTemplate = `{ "models.MapSummary": { "type": "object", "properties": { - "history": { - "type": "array", - "items": { - "$ref": "#/definitions/models.MapHistory" - } - }, - "rating": { - "type": "number" - }, "routes": { "type": "array", "items": { diff --git a/docs/swagger.json b/docs/swagger.json index f2ed3f0..ffcf55a 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -901,6 +901,12 @@ "description": { "type": "string" }, + "history": { + "$ref": "#/definitions/models.MapHistory" + }, + "rating": { + "type": "number" + }, "score_count": { "type": "integer" }, @@ -923,15 +929,6 @@ "models.MapSummary": { "type": "object", "properties": { - "history": { - "type": "array", - "items": { - "$ref": "#/definitions/models.MapHistory" - } - }, - "rating": { - "type": "number" - }, "routes": { "type": "array", "items": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 61f2ad7..3dfe421 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -74,6 +74,10 @@ definitions: $ref: '#/definitions/models.Category' description: type: string + history: + $ref: '#/definitions/models.MapHistory' + rating: + type: number score_count: type: integer showcase: @@ -88,12 +92,6 @@ definitions: type: object models.MapSummary: properties: - history: - items: - $ref: '#/definitions/models.MapHistory' - type: array - rating: - type: number routes: items: $ref: '#/definitions/models.MapRoute' -- cgit v1.2.3 From 9af2c7d17f02be98998d388b421b3d055a96f83e Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Tue, 20 Jun 2023 20:57:10 +0300 Subject: fix: remove duplicate score count in routes (#46) --- backend/controllers/mapController.go | 11 +++-------- backend/models/models.go | 1 - docs/docs.go | 3 --- docs/swagger.json | 3 --- docs/swagger.yaml | 2 -- 5 files changed, 3 insertions(+), 17 deletions(-) diff --git a/backend/controllers/mapController.go b/backend/controllers/mapController.go index e46b766..b5984dc 100644 --- a/backend/controllers/mapController.go +++ b/backend/controllers/mapController.go @@ -26,11 +26,6 @@ func FetchMapSummary(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - // ( - // SELECT COALESCE(avg(rating), 0.0) - // FROM route_ratings - // WHERE map_id = $1 - // ) // Get map data response.Map.ID = intID sql := `SELECT m.id, g.name, c.name, m.name @@ -44,11 +39,11 @@ func FetchMapSummary(c *gin.Context) { return } // Get map routes and histories - sql = `SELECT c.id, c.name, h.user_name, h.score_count, h.record_date, r.score_count, r.description, r.showcase, COALESCE(avg(rating), 0.0) FROM map_routes r + sql = `SELECT c.id, c.name, h.user_name, h.score_count, h.record_date, r.description, r.showcase, COALESCE(avg(rating), 0.0) FROM map_routes r INNER JOIN categories c ON r.category_id = c.id INNER JOIN map_history h ON r.map_id = h.map_id AND r.category_id = h.category_id LEFT JOIN map_ratings rt ON r.map_id = rt.map_id AND r.category_id = rt.category_id - WHERE r.map_id = $1 AND h.score_count = r.score_count GROUP BY c.id, h.user_name, h.score_count, h.record_date, r.score_count, r.description, r.showcase + WHERE r.map_id = $1 AND h.score_count = r.score_count GROUP BY c.id, h.user_name, h.score_count, h.record_date, r.description, r.showcase ORDER BY h.record_date ASC;` rows, err := database.DB.Query(sql, id) if err != nil { @@ -57,7 +52,7 @@ func FetchMapSummary(c *gin.Context) { } for rows.Next() { route := models.MapRoute{Category: models.Category{}, History: models.MapHistory{}} - err = rows.Scan(&route.Category.ID, &route.Category.Name, &route.History.RunnerName, &route.History.ScoreCount, &route.History.Date, &route.ScoreCount, &route.Description, &route.Showcase, &route.Rating) + err = rows.Scan(&route.Category.ID, &route.Category.Name, &route.History.RunnerName, &route.History.ScoreCount, &route.History.Date, &route.Description, &route.Showcase, &route.Rating) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return diff --git a/backend/models/models.go b/backend/models/models.go index 783c339..0727468 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -44,7 +44,6 @@ type MapRoute struct { Category Category `json:"category"` History MapHistory `json:"history"` Rating float32 `json:"rating"` - ScoreCount int `json:"score_count"` Description string `json:"description"` Showcase string `json:"showcase"` } diff --git a/docs/docs.go b/docs/docs.go index b7dc7ab..a0aad6f 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -914,9 +914,6 @@ const docTemplate = `{ "rating": { "type": "number" }, - "score_count": { - "type": "integer" - }, "showcase": { "type": "string" } diff --git a/docs/swagger.json b/docs/swagger.json index ffcf55a..2279ff6 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -907,9 +907,6 @@ "rating": { "type": "number" }, - "score_count": { - "type": "integer" - }, "showcase": { "type": "string" } diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 3dfe421..ba4775a 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -78,8 +78,6 @@ definitions: $ref: '#/definitions/models.MapHistory' rating: type: number - score_count: - type: integer showcase: type: string type: object -- cgit v1.2.3 From 35ac5a65ae6006a669d1eaabcd0b0628b3a0334b Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Tue, 20 Jun 2023 22:10:09 +0300 Subject: feat: parser for cm portal & tick counting (#42) Former-commit-id: fceadf1acf59b2afe0a4d18cc4a7ac847fc2fc42 --- backend/parser/parser.REMOVED.git-id | 1 + 1 file changed, 1 insertion(+) create mode 100644 backend/parser/parser.REMOVED.git-id diff --git a/backend/parser/parser.REMOVED.git-id b/backend/parser/parser.REMOVED.git-id new file mode 100644 index 0000000..b09611f --- /dev/null +++ b/backend/parser/parser.REMOVED.git-id @@ -0,0 +1 @@ +72bd63d0667a78d35f643f1f8fcf71d9269d2719 \ No newline at end of file -- cgit v1.2.3 From 1d69debc7a54360b4682af43a5ad93420f5649ba Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 28 Jun 2023 13:24:15 +0300 Subject: feat: image field for maps Former-commit-id: 374fecd1ef73854c96dd4b80c584ddb823e6e1ba --- backend/controllers/mapController.go | 8 ++++---- backend/models/models.go | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/controllers/mapController.go b/backend/controllers/mapController.go index b5984dc..77f8e10 100644 --- a/backend/controllers/mapController.go +++ b/backend/controllers/mapController.go @@ -28,12 +28,12 @@ func FetchMapSummary(c *gin.Context) { } // Get map data response.Map.ID = intID - sql := `SELECT m.id, g.name, c.name, m.name + sql := `SELECT m.id, g.name, c.name, m.name, m.image FROM maps m INNER JOIN games g ON m.game_id = g.id INNER JOIN chapters c ON m.chapter_id = c.id WHERE m.id = $1` - err = database.DB.QueryRow(sql, id).Scan(&response.Map.ID, &response.Map.GameName, &response.Map.ChapterName, &response.Map.MapName) + err = database.DB.QueryRow(sql, id).Scan(&response.Map.ID, &response.Map.GameName, &response.Map.ChapterName, &response.Map.MapName, &response.Map.Image) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -89,12 +89,12 @@ func FetchMapLeaderboards(c *gin.Context) { return } mapData.ID = intID - sql := `SELECT g.name, c.name, m.name, is_disabled + sql := `SELECT g.name, c.name, m.name, is_disabled, m.image FROM maps m INNER JOIN games g ON m.game_id = g.id INNER JOIN chapters c ON m.chapter_id = c.id WHERE m.id = $1` - err = database.DB.QueryRow(sql, id).Scan(&mapData.GameName, &mapData.ChapterName, &mapData.MapName, &isDisabled) + err = database.DB.QueryRow(sql, id).Scan(&mapData.GameName, &mapData.ChapterName, &mapData.MapName, &isDisabled, &mapData.Image) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return diff --git a/backend/models/models.go b/backend/models/models.go index 0727468..7b8cbc4 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -23,6 +23,7 @@ type Map struct { GameName string `json:"game_name"` ChapterName string `json:"chapter_name"` MapName string `json:"map_name"` + Image string `json:"image"` } type MapShort struct { -- cgit v1.2.3 From 8cce8b446e63d03161f0807c76721b40d851b50d Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 28 Jun 2023 23:27:22 +0300 Subject: feat: mod edit for map summary Former-commit-id: dbef520658347a8c23546371ced24f1c0271749d --- backend/controllers/modController.go | 91 ++++++++++++++++++++++++++++++++++++ backend/middleware/auth.go | 10 +++- backend/models/models.go | 1 + backend/models/requests.go | 29 ++++++++++-- backend/routes/routes.go | 1 + 5 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 backend/controllers/modController.go diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go new file mode 100644 index 0000000..ebf1cb7 --- /dev/null +++ b/backend/controllers/modController.go @@ -0,0 +1,91 @@ +package controllers + +import ( + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + "github.com/pektezol/leastportals/backend/database" + "github.com/pektezol/leastportals/backend/models" +) + +// PUT Map Summary +// +// @Summary Edit map summary with specified map id. +// @Tags maps +// @Produce json +// @Param id path int true "Map ID" +// @Success 200 {object} models.Response{data=models.EditMapSummaryRequest} +// @Failure 400 {object} models.Response +// @Router /maps/{id}/summary [put] +func EditMapSummary(c *gin.Context) { + // Check if user exists + user, exists := c.Get("user") + if !exists { + c.JSON(http.StatusUnauthorized, models.ErrorResponse("User not logged in.")) + return + } + var moderator bool + for _, title := range user.(models.User).Titles { + if title == "Moderator" { + moderator = true + } + } + if !moderator { + c.JSON(http.StatusUnauthorized, "Insufficient permissions.") + return + } + // Bind parameter and body + id := c.Param("id") + mapID, err := strconv.Atoi(id) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + var request models.EditMapSummaryRequest + if err := c.BindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + // Fetch route category and score count + var categoryID, scoreCount int + sql := `SELECT mr.category_id, mr.score_count + FROM map_routes mr + INNER JOIN maps m + WHERE m.id = $1 AND mr.id = $2` + err = database.DB.QueryRow(sql, mapID, request.RouteID).Scan(&categoryID, &scoreCount) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + // Start database transaction + tx, err := database.DB.Begin() + if err != nil { + c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) + return + } + defer tx.Rollback() + // Update database with new data + sql = `UPDATE map_routes SET score_count = $2, description = $3, showcase = $4 WHERE id = $1` + _, err = tx.Exec(sql, request.RouteID, request.ScoreCount, request.Description, request.Showcase) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + sql = `UPDATE map_history SET user_name = $3, score_count = $4, record_date = $5 WHERE map_id = $1 AND category_id = $2` + _, err = tx.Exec(sql, mapID, categoryID, request.UserName, request.ScoreCount, request.RecordDate) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + if err = tx.Commit(); err != nil { + c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) + return + } + // Return response + c.JSON(http.StatusOK, models.Response{ + Success: true, + Message: "Successfully updated map summary.", + Data: request, + }) +} diff --git a/backend/middleware/auth.go b/backend/middleware/auth.go index 14a0b78..86e79d9 100644 --- a/backend/middleware/auth.go +++ b/backend/middleware/auth.go @@ -36,13 +36,21 @@ func CheckAuth(c *gin.Context) { } // Get user from DB var user models.User - database.DB.QueryRow(`SELECT * FROM users WHERE steam_id = $1`, claims["sub"]).Scan( + database.DB.QueryRow(`SELECT u.steam_id, u.user_name, u.avatar_link, u.country_code, u.created_at, u.updated_at, array_agg(t.) FROM users u WHERE steam_id = $1`, claims["sub"]).Scan( &user.SteamID, &user.UserName, &user.AvatarLink, &user.CountryCode, &user.CreatedAt, &user.UpdatedAt) if user.SteamID == "" { c.Next() return } + // Get user titles from DB + user.Titles = []string{} + rows, _ := database.DB.Query(`SELECT t.title_name FROM titles t WHERE t.user_id = $1`, user.SteamID) + for rows.Next() { + var title string + rows.Scan(&title) + user.Titles = append(user.Titles, title) + } c.Set("user", user) c.Next() } else { diff --git a/backend/models/models.go b/backend/models/models.go index 7b8cbc4..5355a9f 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -11,6 +11,7 @@ type User struct { CountryCode string `json:"country_code"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` + Titles []string `json:"titles"` } type UserShort struct { diff --git a/backend/models/requests.go b/backend/models/requests.go index 49b2d75..e95eab6 100644 --- a/backend/models/requests.go +++ b/backend/models/requests.go @@ -1,8 +1,29 @@ package models +import ( + "mime/multipart" + "time" +) + +type EditMapSummaryRequest struct { + RouteID int `json:"route_id" binding:"required"` + Description string `json:"description" binding:"required"` + Showcase string `json:"showcase" binding:"required"` + UserName string `json:"user_name" binding:"required"` + ScoreCount int `json:"score_count" binding:"required"` + RecordDate time.Time `json:"record_date" binding:"required"` +} + +type CreateMapHistoryRequest struct { + CategoryID int `json:"category_id" binding:"required"` + UserName string `json:"user_name" binding:"required"` + ScoreCount int `json:"score_count" binding:"required"` + RecordDate time.Time `json:"record_date" binding:"required"` +} + type RecordRequest struct { - ScoreCount int `json:"score_count" form:"score_count" binding:"required"` - ScoreTime int `json:"score_time" form:"score_time" binding:"required"` - PartnerID string `json:"partner_id" form:"partner_id" binding:"required"` - IsPartnerOrange bool `json:"is_partner_orange" form:"is_partner_orange" binding:"required"` + HostDemo *multipart.FileHeader `json:"host_demo" form:"host_demo" binding:"required"` + PartnerDemo *multipart.FileHeader `json:"partner_demo" form:"partner_demo"` + IsPartnerOrange bool `json:"is_partner_orange" form:"is_partner_orange"` + PartnerID string `json:"partner_id" form:"partner_id"` } diff --git a/backend/routes/routes.go b/backend/routes/routes.go index bf8a995..93dc1dd 100644 --- a/backend/routes/routes.go +++ b/backend/routes/routes.go @@ -26,6 +26,7 @@ func InitRoutes(router *gin.Engine) { v1.GET("/users/:id", middleware.CheckAuth, controllers.FetchUser) v1.GET("/demos", controllers.DownloadDemoWithID) v1.GET("/maps/:id/summary", middleware.CheckAuth, controllers.FetchMapSummary) + v1.PUT("/maps/:id/summary", middleware.CheckAuth, controllers.EditMapSummary) v1.GET("/maps/:id/leaderboards", middleware.CheckAuth, controllers.FetchMapLeaderboards) v1.POST("/maps/:id/record", middleware.CheckAuth, controllers.CreateRecordWithDemo) v1.GET("/rankings", middleware.CheckAuth, controllers.Rankings) -- cgit v1.2.3 From 7050773414c550b7693c41a9bdd8cb390d7ef647 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 29 Jun 2023 10:45:12 +0300 Subject: fix: record controller Former-commit-id: bff6b62474e02f644d93f49827145cfd92682c6f --- backend/controllers/recordController.go | 87 +++++++++-------------- backend/models/requests.go | 4 +- backend/parser/parser.go | 7 ++ docs/docs.go | 118 ++++++++++++++++++++++++-------- docs/swagger.json | 112 ++++++++++++++++++++++-------- docs/swagger.yaml | 78 +++++++++++++++------ 6 files changed, 269 insertions(+), 137 deletions(-) create mode 100644 backend/parser/parser.go diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index 627be57..aec31bb 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -5,14 +5,15 @@ import ( b64 "encoding/base64" "io" "log" + "mime/multipart" "net/http" "os" - "strconv" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/pektezol/leastportals/backend/database" "github.com/pektezol/leastportals/backend/models" + "github.com/pektezol/leastportals/backend/parser" "golang.org/x/oauth2/google" "golang.org/x/oauth2/jwt" "google.golang.org/api/drive/v3" @@ -25,9 +26,8 @@ import ( // @Accept mpfd // @Produce json // @Param Authorization header string true "JWT Token" -// @Param demos formData []file true "Demos" -// @Param score_count formData int true "Score Count" -// @Param score_time formData int true "Score Time" +// @Param host_demo formData file true "Host Demo" +// @Param partner_demo formData file true "Partner Demo" // @Param is_partner_orange formData boolean true "Is Partner Orange" // @Param partner_id formData string true "Partner ID" // @Success 200 {object} models.Response{data=models.RecordRequest} @@ -43,11 +43,11 @@ func CreateRecordWithDemo(c *gin.Context) { return } // Check if map is sp or mp - var gameID int + var gameName string var isCoop bool var isDisabled bool - sql := `SELECT game_id, is_disabled FROM maps WHERE id = $1` - err := database.DB.QueryRow(sql, mapId).Scan(&gameID, &isDisabled) + sql := `SELECT g.name, m.is_disabled FROM maps m INNER JOIN games g ON m.game_id=g.id WHERE id = $1` + err := database.DB.QueryRow(sql, mapId).Scan(&gameName, &isDisabled) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -56,51 +56,23 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse("Map is not available for competitive boards.")) return } - if gameID == 2 { + if gameName == "Portal 2 - Cooperative" { isCoop = true } // Get record request var record models.RecordRequest - score_count, err := strconv.Atoi(c.PostForm("score_count")) - if err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) - return - } - score_time, err := strconv.Atoi(c.PostForm("score_time")) - if err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) - return - } - is_partner_orange, err := strconv.ParseBool(c.PostForm("is_partner_orange")) - if err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) - return - } - record.ScoreCount = score_count - record.ScoreTime = score_time - record.PartnerID = c.PostForm("partner_id") - record.IsPartnerOrange = is_partner_orange - if record.PartnerID == "" { - c.JSON(http.StatusBadRequest, models.ErrorResponse("No partner id given.")) - return - } - // Multipart form - form, err := c.MultipartForm() - if err != nil { + if err := c.ShouldBind(&record); err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - files := form.File["demos"] - if len(files) != 2 && isCoop { - c.JSON(http.StatusBadRequest, models.ErrorResponse("Not enough demos for coop submission.")) - return - } - if len(files) != 1 && !isCoop { - c.JSON(http.StatusBadRequest, models.ErrorResponse("Too many demos for singleplayer submission.")) + if isCoop && (record.PartnerDemo == nil || record.PartnerID == "") { + c.JSON(http.StatusBadRequest, models.ErrorResponse("Invalid entry for coop record submission.")) return } - var hostDemoUUID string - var partnerDemoUUID string + // Demo files + demoFiles := []*multipart.FileHeader{record.HostDemo, record.PartnerDemo} + var hostDemoUUID, hostDemoFileID, partnerDemoUUID, partnerDemoFileID string + var hostDemoScoreCount, hostDemoScoreTime int client := serviceAccount() srv, err := drive.New(client) if err != nil { @@ -115,16 +87,15 @@ func CreateRecordWithDemo(c *gin.Context) { } // Defer to a rollback in case anything fails defer tx.Rollback() - fileID := "" - for i, header := range files { + for i, header := range demoFiles { uuid := uuid.New().String() // Upload & insert into demos - err = c.SaveUploadedFile(header, "docs/"+header.Filename) + err = c.SaveUploadedFile(header, "parser/demos/"+header.Filename) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - f, err := os.Open("docs/" + header.Filename) + f, err := os.Open("parser/demos/" + header.Filename) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -135,11 +106,16 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - fileID = file.Id + hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo(record.HostDemo) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } if i == 0 { + hostDemoFileID = file.Id hostDemoUUID = uuid - } - if i == 1 { + } else if i == 1 { + partnerDemoFileID = file.Id partnerDemoUUID = uuid } _, err = tx.Exec(`INSERT INTO demos (id,location_id) VALUES ($1,$2)`, uuid, file.Id) @@ -148,7 +124,7 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - os.Remove("docs/" + header.Filename) + os.Remove("parser/demos/" + header.Filename) } // Insert into records if isCoop { @@ -163,9 +139,10 @@ func CreateRecordWithDemo(c *gin.Context) { partnerID = user.(models.User).SteamID hostID = record.PartnerID } - _, err := tx.Exec(sql, mapId, record.ScoreCount, record.ScoreTime, hostID, partnerID, hostDemoUUID, partnerDemoUUID) + _, err := tx.Exec(sql, mapId, hostDemoScoreCount, hostDemoScoreTime, hostID, partnerID, hostDemoUUID, partnerDemoUUID) if err != nil { - deleteFile(srv, fileID) + deleteFile(srv, hostDemoFileID) + deleteFile(srv, partnerDemoFileID) c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } @@ -180,9 +157,9 @@ func CreateRecordWithDemo(c *gin.Context) { } else { sql := `INSERT INTO records_sp(map_id,score_count,score_time,user_id,demo_id) VALUES($1, $2, $3, $4, $5)` - _, err := tx.Exec(sql, mapId, record.ScoreCount, record.ScoreTime, user.(models.User).SteamID, hostDemoUUID) + _, err := tx.Exec(sql, mapId, hostDemoScoreCount, hostDemoScoreTime, user.(models.User).SteamID, hostDemoUUID) if err != nil { - deleteFile(srv, fileID) + deleteFile(srv, hostDemoFileID) c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } diff --git a/backend/models/requests.go b/backend/models/requests.go index e95eab6..4b4657b 100644 --- a/backend/models/requests.go +++ b/backend/models/requests.go @@ -22,8 +22,8 @@ type CreateMapHistoryRequest struct { } type RecordRequest struct { - HostDemo *multipart.FileHeader `json:"host_demo" form:"host_demo" binding:"required"` - PartnerDemo *multipart.FileHeader `json:"partner_demo" form:"partner_demo"` + HostDemo *multipart.FileHeader `json:"host_demo" form:"host_demo" binding:"required" swaggerignore:"true"` + PartnerDemo *multipart.FileHeader `json:"partner_demo" form:"partner_demo" swaggerignore:"true"` IsPartnerOrange bool `json:"is_partner_orange" form:"is_partner_orange"` PartnerID string `json:"partner_id" form:"partner_id"` } diff --git a/backend/parser/parser.go b/backend/parser/parser.go new file mode 100644 index 0000000..6f9a24f --- /dev/null +++ b/backend/parser/parser.go @@ -0,0 +1,7 @@ +package parser + +import "mime/multipart" + +func ProcessDemo(demo *multipart.FileHeader) (scoreCount int, scoreTime int, err error) { + return 0, 0, nil +} diff --git a/docs/docs.go b/docs/docs.go index a0aad6f..090d3e8 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1,5 +1,5 @@ -// Package docs GENERATED BY SWAG; DO NOT EDIT -// This file was generated by swaggo/swag +// Code generated by swaggo/swag. DO NOT EDIT. + package docs import "github.com/swaggo/swag" @@ -345,26 +345,16 @@ const docTemplate = `{ "required": true }, { - "type": "array", - "items": { - "type": "file" - }, - "description": "Demos", - "name": "demos", + "type": "file", + "description": "Host Demo", + "name": "host_demo", "in": "formData", "required": true }, { - "type": "integer", - "description": "Score Count", - "name": "score_count", - "in": "formData", - "required": true - }, - { - "type": "integer", - "description": "Score Time", - "name": "score_time", + "type": "file", + "description": "Partner Demo", + "name": "partner_demo", "in": "formData", "required": true }, @@ -461,6 +451,50 @@ const docTemplate = `{ } } } + }, + "put": { + "produces": [ + "application/json" + ], + "tags": [ + "maps" + ], + "summary": "Edit map summary with specified map id.", + "parameters": [ + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/models.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.EditMapSummaryRequest" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Response" + } + } + } } }, "/profile": { @@ -843,6 +877,37 @@ const docTemplate = `{ } } }, + "models.EditMapSummaryRequest": { + "type": "object", + "required": [ + "description", + "record_date", + "route_id", + "score_count", + "showcase", + "user_name" + ], + "properties": { + "description": { + "type": "string" + }, + "record_date": { + "type": "string" + }, + "route_id": { + "type": "integer" + }, + "score_count": { + "type": "integer" + }, + "showcase": { + "type": "string" + }, + "user_name": { + "type": "string" + } + } + }, "models.Game": { "type": "object", "properties": { @@ -874,6 +939,9 @@ const docTemplate = `{ "id": { "type": "integer" }, + "image": { + "type": "string" + }, "map_name": { "type": "string" } @@ -1003,24 +1071,12 @@ const docTemplate = `{ }, "models.RecordRequest": { "type": "object", - "required": [ - "is_partner_orange", - "partner_id", - "score_count", - "score_time" - ], "properties": { "is_partner_orange": { "type": "boolean" }, "partner_id": { "type": "string" - }, - "score_count": { - "type": "integer" - }, - "score_time": { - "type": "integer" } } }, @@ -1100,6 +1156,8 @@ var SwaggerInfo = &swag.Spec{ Description: "Backend API endpoints for the Least Portals Database.", InfoInstanceName: "swagger", SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", } func init() { diff --git a/docs/swagger.json b/docs/swagger.json index 2279ff6..62079b1 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -338,26 +338,16 @@ "required": true }, { - "type": "array", - "items": { - "type": "file" - }, - "description": "Demos", - "name": "demos", + "type": "file", + "description": "Host Demo", + "name": "host_demo", "in": "formData", "required": true }, { - "type": "integer", - "description": "Score Count", - "name": "score_count", - "in": "formData", - "required": true - }, - { - "type": "integer", - "description": "Score Time", - "name": "score_time", + "type": "file", + "description": "Partner Demo", + "name": "partner_demo", "in": "formData", "required": true }, @@ -454,6 +444,50 @@ } } } + }, + "put": { + "produces": [ + "application/json" + ], + "tags": [ + "maps" + ], + "summary": "Edit map summary with specified map id.", + "parameters": [ + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/models.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.EditMapSummaryRequest" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Response" + } + } + } } }, "/profile": { @@ -836,6 +870,37 @@ } } }, + "models.EditMapSummaryRequest": { + "type": "object", + "required": [ + "description", + "record_date", + "route_id", + "score_count", + "showcase", + "user_name" + ], + "properties": { + "description": { + "type": "string" + }, + "record_date": { + "type": "string" + }, + "route_id": { + "type": "integer" + }, + "score_count": { + "type": "integer" + }, + "showcase": { + "type": "string" + }, + "user_name": { + "type": "string" + } + } + }, "models.Game": { "type": "object", "properties": { @@ -867,6 +932,9 @@ "id": { "type": "integer" }, + "image": { + "type": "string" + }, "map_name": { "type": "string" } @@ -996,24 +1064,12 @@ }, "models.RecordRequest": { "type": "object", - "required": [ - "is_partner_orange", - "partner_id", - "score_count", - "score_time" - ], "properties": { "is_partner_orange": { "type": "boolean" }, "partner_id": { "type": "string" - }, - "score_count": { - "type": "integer" - }, - "score_time": { - "type": "integer" } } }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index ba4775a..9d58620 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -32,6 +32,28 @@ definitions: game: $ref: '#/definitions/models.Game' type: object + models.EditMapSummaryRequest: + properties: + description: + type: string + record_date: + type: string + route_id: + type: integer + score_count: + type: integer + showcase: + type: string + user_name: + type: string + required: + - description + - record_date + - route_id + - score_count + - showcase + - user_name + type: object models.Game: properties: id: @@ -52,6 +74,8 @@ definitions: type: string id: type: integer + image: + type: string map_name: type: string type: object @@ -140,15 +164,6 @@ definitions: type: boolean partner_id: type: string - score_count: - type: integer - score_time: - type: integer - required: - - is_partner_orange - - partner_id - - score_count - - score_time type: object models.Response: properties: @@ -388,23 +403,16 @@ paths: name: Authorization required: true type: string - - description: Demos - in: formData - items: - type: file - name: demos - required: true - type: array - - description: Score Count + - description: Host Demo in: formData - name: score_count + name: host_demo required: true - type: integer - - description: Score Time + type: file + - description: Partner Demo in: formData - name: score_time + name: partner_demo required: true - type: integer + type: file - description: Is Partner Orange in: formData name: is_partner_orange @@ -465,6 +473,32 @@ paths: summary: Get map summary with specified id. tags: - maps + put: + parameters: + - description: Map ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/models.Response' + - properties: + data: + $ref: '#/definitions/models.EditMapSummaryRequest' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Response' + summary: Edit map summary with specified map id. + tags: + - maps /profile: get: consumes: -- cgit v1.2.3 From de8cda38173909a0bd3c5539b3773c5fc7825878 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 29 Jun 2023 11:45:40 +0300 Subject: fix: auth sql typo Former-commit-id: e55e1e6727e59d15044514940e20c4ca94b8e3e4 --- backend/middleware/auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/middleware/auth.go b/backend/middleware/auth.go index 86e79d9..0698f0a 100644 --- a/backend/middleware/auth.go +++ b/backend/middleware/auth.go @@ -36,7 +36,7 @@ func CheckAuth(c *gin.Context) { } // Get user from DB var user models.User - database.DB.QueryRow(`SELECT u.steam_id, u.user_name, u.avatar_link, u.country_code, u.created_at, u.updated_at, array_agg(t.) FROM users u WHERE steam_id = $1`, claims["sub"]).Scan( + database.DB.QueryRow(`SELECT u.steam_id, u.user_name, u.avatar_link, u.country_code, u.created_at, u.updated_at FROM users u WHERE steam_id = $1`, claims["sub"]).Scan( &user.SteamID, &user.UserName, &user.AvatarLink, &user.CountryCode, &user.CreatedAt, &user.UpdatedAt) if user.SteamID == "" { -- cgit v1.2.3 From 85c6da965ec401dabb162df09160b4ce9dc28413 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 29 Jun 2023 11:57:53 +0300 Subject: feat: route id in map summary, update docs Former-commit-id: f8db004d2c17f09f665e51ec4e730418248bfd07 --- backend/controllers/mapController.go | 4 ++-- backend/controllers/modController.go | 7 ++++--- backend/controllers/recordController.go | 5 ++--- backend/models/models.go | 1 + docs/docs.go | 9 +++++++++ docs/swagger.json | 9 +++++++++ docs/swagger.yaml | 6 ++++++ 7 files changed, 33 insertions(+), 8 deletions(-) diff --git a/backend/controllers/mapController.go b/backend/controllers/mapController.go index 77f8e10..7dfd2be 100644 --- a/backend/controllers/mapController.go +++ b/backend/controllers/mapController.go @@ -39,7 +39,7 @@ func FetchMapSummary(c *gin.Context) { return } // Get map routes and histories - sql = `SELECT c.id, c.name, h.user_name, h.score_count, h.record_date, r.description, r.showcase, COALESCE(avg(rating), 0.0) FROM map_routes r + sql = `SELECT r.id, c.id, c.name, h.user_name, h.score_count, h.record_date, r.description, r.showcase, COALESCE(avg(rating), 0.0) FROM map_routes r INNER JOIN categories c ON r.category_id = c.id INNER JOIN map_history h ON r.map_id = h.map_id AND r.category_id = h.category_id LEFT JOIN map_ratings rt ON r.map_id = rt.map_id AND r.category_id = rt.category_id @@ -52,7 +52,7 @@ func FetchMapSummary(c *gin.Context) { } for rows.Next() { route := models.MapRoute{Category: models.Category{}, History: models.MapHistory{}} - err = rows.Scan(&route.Category.ID, &route.Category.Name, &route.History.RunnerName, &route.History.ScoreCount, &route.History.Date, &route.Description, &route.Showcase, &route.Rating) + err = rows.Scan(&route.RouteID, &route.Category.ID, &route.Category.Name, &route.History.RunnerName, &route.History.ScoreCount, &route.History.Date, &route.Description, &route.Showcase, &route.Rating) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index ebf1cb7..9d14f92 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -14,9 +14,10 @@ import ( // @Summary Edit map summary with specified map id. // @Tags maps // @Produce json -// @Param id path int true "Map ID" -// @Success 200 {object} models.Response{data=models.EditMapSummaryRequest} -// @Failure 400 {object} models.Response +// @Param id path int true "Map ID" +// @Param request body models.EditMapSummaryRequest true "Body" +// @Success 200 {object} models.Response{data=models.EditMapSummaryRequest} +// @Failure 400 {object} models.Response // @Router /maps/{id}/summary [put] func EditMapSummary(c *gin.Context) { // Check if user exists diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index aec31bb..c865bfb 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -30,7 +30,7 @@ import ( // @Param partner_demo formData file true "Partner Demo" // @Param is_partner_orange formData boolean true "Is Partner Orange" // @Param partner_id formData string true "Partner ID" -// @Success 200 {object} models.Response{data=models.RecordRequest} +// @Success 200 {object} models.Response // @Failure 400 {object} models.Response // @Failure 401 {object} models.Response // @Router /maps/{id}/record [post] @@ -179,9 +179,8 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusOK, models.Response{ Success: true, Message: "Successfully created record.", - Data: record, + Data: nil, }) - return } // GET Demo diff --git a/backend/models/models.go b/backend/models/models.go index 5355a9f..2524935 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -43,6 +43,7 @@ type MapHistory struct { } type MapRoute struct { + RouteID int `json:"route_id"` Category Category `json:"category"` History MapHistory `json:"history"` Rating float32 `json:"rating"` diff --git a/docs/docs.go b/docs/docs.go index 090d3e8..91f91ef 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -467,6 +467,15 @@ const docTemplate = `{ "name": "id", "in": "path", "required": true + }, + { + "description": "Body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.EditMapSummaryRequest" + } } ], "responses": { diff --git a/docs/swagger.json b/docs/swagger.json index 62079b1..c6bbfbc 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -460,6 +460,15 @@ "name": "id", "in": "path", "required": true + }, + { + "description": "Body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.EditMapSummaryRequest" + } } ], "responses": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 9d58620..4291cfc 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -480,6 +480,12 @@ paths: name: id required: true type: integer + - description: Body + in: body + name: request + required: true + schema: + $ref: '#/definitions/models.EditMapSummaryRequest' produces: - application/json responses: -- cgit v1.2.3 From 84346e6006d6e88dfef99550da3c2e80071f0197 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 29 Jun 2023 12:32:28 +0300 Subject: feat: support for multiple route edit, image field Former-commit-id: 3820c1363ece1c6616ec0297e44de851bae410af --- backend/controllers/modController.go | 49 ++++++++++++++++++++---------------- backend/models/requests.go | 5 ++++ docs/docs.go | 45 ++++++++++++++++----------------- docs/swagger.json | 45 ++++++++++++++++----------------- docs/swagger.yaml | 27 ++++++++++---------- 5 files changed, 88 insertions(+), 83 deletions(-) diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index 9d14f92..7c258ef 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -48,17 +48,6 @@ func EditMapSummary(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - // Fetch route category and score count - var categoryID, scoreCount int - sql := `SELECT mr.category_id, mr.score_count - FROM map_routes mr - INNER JOIN maps m - WHERE m.id = $1 AND mr.id = $2` - err = database.DB.QueryRow(sql, mapID, request.RouteID).Scan(&categoryID, &scoreCount) - if err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) - return - } // Start database transaction tx, err := database.DB.Begin() if err != nil { @@ -66,18 +55,34 @@ func EditMapSummary(c *gin.Context) { return } defer tx.Rollback() - // Update database with new data - sql = `UPDATE map_routes SET score_count = $2, description = $3, showcase = $4 WHERE id = $1` - _, err = tx.Exec(sql, request.RouteID, request.ScoreCount, request.Description, request.Showcase) - if err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) - return + if request.Image != "" { + tx.Exec(`UPDATE maps m SET image = $2 WHERE m.id = $1`, mapID, request.Image) } - sql = `UPDATE map_history SET user_name = $3, score_count = $4, record_date = $5 WHERE map_id = $1 AND category_id = $2` - _, err = tx.Exec(sql, mapID, categoryID, request.UserName, request.ScoreCount, request.RecordDate) - if err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) - return + for _, route := range request.Routes { + // Fetch route category and score count + var categoryID, scoreCount int + sql := `SELECT mr.category_id, mr.score_count + FROM map_routes mr + INNER JOIN maps m + WHERE m.id = $1 AND mr.id = $2` + err = database.DB.QueryRow(sql, mapID, route.RouteID).Scan(&categoryID, &scoreCount) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + // Update database with new data + sql = `UPDATE map_routes SET score_count = $2, description = $3, showcase = $4 WHERE id = $1` + _, err = tx.Exec(sql, route.RouteID, route.ScoreCount, route.Description, route.Showcase) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + sql = `UPDATE map_history SET user_name = $3, score_count = $4, record_date = $5 WHERE map_id = $1 AND category_id = $2` + _, err = tx.Exec(sql, mapID, categoryID, route.UserName, route.ScoreCount, route.RecordDate) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } } if err = tx.Commit(); err != nil { c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) diff --git a/backend/models/requests.go b/backend/models/requests.go index 4b4657b..7a00567 100644 --- a/backend/models/requests.go +++ b/backend/models/requests.go @@ -6,6 +6,11 @@ import ( ) type EditMapSummaryRequest struct { + Image string `json:"image" binding:"required"` + Routes []EditMapSummaryRequestDetails `json:"routes" binding:"dive"` +} + +type EditMapSummaryRequestDetails struct { RouteID int `json:"route_id" binding:"required"` Description string `json:"description" binding:"required"` Showcase string `json:"showcase" binding:"required"` diff --git a/docs/docs.go b/docs/docs.go index 91f91ef..8318e14 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -377,19 +377,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "allOf": [ - { - "$ref": "#/definitions/models.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/models.RecordRequest" - } - } - } - ] + "$ref": "#/definitions/models.Response" } }, "400": { @@ -887,6 +875,23 @@ const docTemplate = `{ } }, "models.EditMapSummaryRequest": { + "type": "object", + "required": [ + "image" + ], + "properties": { + "image": { + "type": "string" + }, + "routes": { + "type": "array", + "items": { + "$ref": "#/definitions/models.EditMapSummaryRequestDetails" + } + } + } + }, + "models.EditMapSummaryRequestDetails": { "type": "object", "required": [ "description", @@ -991,6 +996,9 @@ const docTemplate = `{ "rating": { "type": "number" }, + "route_id": { + "type": "integer" + }, "showcase": { "type": "string" } @@ -1078,17 +1086,6 @@ const docTemplate = `{ } } }, - "models.RecordRequest": { - "type": "object", - "properties": { - "is_partner_orange": { - "type": "boolean" - }, - "partner_id": { - "type": "string" - } - } - }, "models.Response": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index c6bbfbc..212ebee 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -370,19 +370,7 @@ "200": { "description": "OK", "schema": { - "allOf": [ - { - "$ref": "#/definitions/models.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/models.RecordRequest" - } - } - } - ] + "$ref": "#/definitions/models.Response" } }, "400": { @@ -880,6 +868,23 @@ } }, "models.EditMapSummaryRequest": { + "type": "object", + "required": [ + "image" + ], + "properties": { + "image": { + "type": "string" + }, + "routes": { + "type": "array", + "items": { + "$ref": "#/definitions/models.EditMapSummaryRequestDetails" + } + } + } + }, + "models.EditMapSummaryRequestDetails": { "type": "object", "required": [ "description", @@ -984,6 +989,9 @@ "rating": { "type": "number" }, + "route_id": { + "type": "integer" + }, "showcase": { "type": "string" } @@ -1071,17 +1079,6 @@ } } }, - "models.RecordRequest": { - "type": "object", - "properties": { - "is_partner_orange": { - "type": "boolean" - }, - "partner_id": { - "type": "string" - } - } - }, "models.Response": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 4291cfc..ba20f6d 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -33,6 +33,17 @@ definitions: $ref: '#/definitions/models.Game' type: object models.EditMapSummaryRequest: + properties: + image: + type: string + routes: + items: + $ref: '#/definitions/models.EditMapSummaryRequestDetails' + type: array + required: + - image + type: object + models.EditMapSummaryRequestDetails: properties: description: type: string @@ -102,6 +113,8 @@ definitions: $ref: '#/definitions/models.MapHistory' rating: type: number + route_id: + type: integer showcase: type: string type: object @@ -158,13 +171,6 @@ definitions: $ref: '#/definitions/models.UserRanking' type: array type: object - models.RecordRequest: - properties: - is_partner_orange: - type: boolean - partner_id: - type: string - type: object models.Response: properties: data: {} @@ -429,12 +435,7 @@ paths: "200": description: OK schema: - allOf: - - $ref: '#/definitions/models.Response' - - properties: - data: - $ref: '#/definitions/models.RecordRequest' - type: object + $ref: '#/definitions/models.Response' "400": description: Bad Request schema: -- cgit v1.2.3 From 211f1b48372c0c1885141d1ab214ab7418520008 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 29 Jun 2023 13:41:47 +0300 Subject: fix: map summary sql Former-commit-id: 3edddea55add68a877d2dbdfcaf3e83f774da775 --- backend/controllers/mapController.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/controllers/mapController.go b/backend/controllers/mapController.go index 7dfd2be..7a26554 100644 --- a/backend/controllers/mapController.go +++ b/backend/controllers/mapController.go @@ -43,7 +43,7 @@ func FetchMapSummary(c *gin.Context) { INNER JOIN categories c ON r.category_id = c.id INNER JOIN map_history h ON r.map_id = h.map_id AND r.category_id = h.category_id LEFT JOIN map_ratings rt ON r.map_id = rt.map_id AND r.category_id = rt.category_id - WHERE r.map_id = $1 AND h.score_count = r.score_count GROUP BY c.id, h.user_name, h.score_count, h.record_date, r.description, r.showcase + WHERE r.map_id = $1 AND h.score_count = r.score_count GROUP BY r.id, c.id, h.user_name, h.score_count, h.record_date, r.description, r.showcase ORDER BY h.record_date ASC;` rows, err := database.DB.Query(sql, id) if err != nil { -- cgit v1.2.3 From 41529b1d6938c52fcbc52aa50a186a0d368d70e5 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 29 Jun 2023 18:57:39 +0300 Subject: feat: return back to single map summary edit Former-commit-id: 853dd376267ea63e67b1e4e7041f4c5c5f1be380 --- backend/controllers/modController.go | 45 ++++++++++++++++-------------------- backend/models/requests.go | 19 +++++++-------- 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index 7c258ef..5fe0f68 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -55,34 +55,29 @@ func EditMapSummary(c *gin.Context) { return } defer tx.Rollback() - if request.Image != "" { - tx.Exec(`UPDATE maps m SET image = $2 WHERE m.id = $1`, mapID, request.Image) - } - for _, route := range request.Routes { - // Fetch route category and score count - var categoryID, scoreCount int - sql := `SELECT mr.category_id, mr.score_count + // Fetch route category and score count + var categoryID, scoreCount int + sql := `SELECT mr.category_id, mr.score_count FROM map_routes mr INNER JOIN maps m WHERE m.id = $1 AND mr.id = $2` - err = database.DB.QueryRow(sql, mapID, route.RouteID).Scan(&categoryID, &scoreCount) - if err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) - return - } - // Update database with new data - sql = `UPDATE map_routes SET score_count = $2, description = $3, showcase = $4 WHERE id = $1` - _, err = tx.Exec(sql, route.RouteID, route.ScoreCount, route.Description, route.Showcase) - if err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) - return - } - sql = `UPDATE map_history SET user_name = $3, score_count = $4, record_date = $5 WHERE map_id = $1 AND category_id = $2` - _, err = tx.Exec(sql, mapID, categoryID, route.UserName, route.ScoreCount, route.RecordDate) - if err != nil { - c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) - return - } + err = database.DB.QueryRow(sql, mapID, request.RouteID).Scan(&categoryID, &scoreCount) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + // Update database with new data + sql = `UPDATE map_routes SET score_count = $2, description = $3, showcase = $4 WHERE id = $1` + _, err = tx.Exec(sql, request.RouteID, request.ScoreCount, request.Description, request.Showcase) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + sql = `UPDATE map_history SET user_name = $3, score_count = $4, record_date = $5 WHERE map_id = $1 AND category_id = $2` + _, err = tx.Exec(sql, mapID, categoryID, request.UserName, request.ScoreCount, request.RecordDate) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return } if err = tx.Commit(); err != nil { c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) diff --git a/backend/models/requests.go b/backend/models/requests.go index 7a00567..767b4d8 100644 --- a/backend/models/requests.go +++ b/backend/models/requests.go @@ -5,12 +5,16 @@ import ( "time" ) -type EditMapSummaryRequest struct { - Image string `json:"image" binding:"required"` - Routes []EditMapSummaryRequestDetails `json:"routes" binding:"dive"` +type CreateMapSummaryRequest struct { + CategoryID int `json:"category_id" binding:"required"` + Description string `json:"description" binding:"required"` + Showcase string `json:"showcase" binding:"required"` + UserName string `json:"user_name" binding:"required"` + ScoreCount int `json:"score_count" binding:"required"` + RecordDate time.Time `json:"record_date" binding:"required"` } -type EditMapSummaryRequestDetails struct { +type EditMapSummaryRequest struct { RouteID int `json:"route_id" binding:"required"` Description string `json:"description" binding:"required"` Showcase string `json:"showcase" binding:"required"` @@ -19,13 +23,6 @@ type EditMapSummaryRequestDetails struct { RecordDate time.Time `json:"record_date" binding:"required"` } -type CreateMapHistoryRequest struct { - CategoryID int `json:"category_id" binding:"required"` - UserName string `json:"user_name" binding:"required"` - ScoreCount int `json:"score_count" binding:"required"` - RecordDate time.Time `json:"record_date" binding:"required"` -} - type RecordRequest struct { HostDemo *multipart.FileHeader `json:"host_demo" form:"host_demo" binding:"required" swaggerignore:"true"` PartnerDemo *multipart.FileHeader `json:"partner_demo" form:"partner_demo" swaggerignore:"true"` -- cgit v1.2.3 From 94d51cc7783519f94176e813f195a07f50d4de69 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 29 Jun 2023 19:23:48 +0300 Subject: feat: create map summary Former-commit-id: 5ab31d8543512b9d32e3d25beee347e9668f8323 --- backend/controllers/modController.go | 87 +++++++++++++++++++++++++++++++++++- backend/routes/routes.go | 1 + 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index 5fe0f68..75be112 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -9,6 +9,91 @@ import ( "github.com/pektezol/leastportals/backend/models" ) +// POST Map Summary +// +// @Summary Create map summary with specified map id. +// @Tags maps +// @Produce json +// @Param id path int true "Map ID" +// @Param request body models.CreateMapSummaryRequest true "Body" +// @Success 200 {object} models.Response{data=models.CreateMapSummaryRequest} +// @Failure 400 {object} models.Response +// @Router /maps/{id}/summary [post] +func CreateMapSummary(c *gin.Context) { + // Check if user exists + user, exists := c.Get("user") + if !exists { + c.JSON(http.StatusUnauthorized, models.ErrorResponse("User not logged in.")) + return + } + var moderator bool + for _, title := range user.(models.User).Titles { + if title == "Moderator" { + moderator = true + } + } + if !moderator { + c.JSON(http.StatusUnauthorized, models.ErrorResponse("Insufficient permissions.")) + return + } + // Bind parameter and body + id := c.Param("id") + mapID, err := strconv.Atoi(id) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + var request models.CreateMapSummaryRequest + if err := c.BindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + // Start database transaction + tx, err := database.DB.Begin() + if err != nil { + c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) + return + } + defer tx.Rollback() + // Fetch route category and score count + var checkMapID int + sql := `SELECT m.id FROM maps m WHERE m.id = $1` + err = database.DB.QueryRow(sql, mapID).Scan(&checkMapID) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + if mapID != checkMapID { + c.JSON(http.StatusBadRequest, models.ErrorResponse("Map ID does not exist.")) + return + } + // Update database with new data + sql = `INSERT INTO map_routes (map_id,category_id,score_count,description,showcase) + VALUES ($1,$2,$3,$4,$5)` + _, err = tx.Exec(sql, mapID, request.CategoryID, request.ScoreCount, request.Description, request.Showcase) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + sql = `INSERT INTO map_history (map_id,category_id,user_name,score_count,record_date) + VALUES ($1,$2,$3,$4,$5)` + _, err = tx.Exec(sql, mapID, request.CategoryID, request.UserName, request.ScoreCount, request.RecordDate) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + if err = tx.Commit(); err != nil { + c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) + return + } + // Return response + c.JSON(http.StatusOK, models.Response{ + Success: true, + Message: "Successfully updated map summary.", + Data: request, + }) +} + // PUT Map Summary // // @Summary Edit map summary with specified map id. @@ -33,7 +118,7 @@ func EditMapSummary(c *gin.Context) { } } if !moderator { - c.JSON(http.StatusUnauthorized, "Insufficient permissions.") + c.JSON(http.StatusUnauthorized, models.ErrorResponse("Insufficient permissions.")) return } // Bind parameter and body diff --git a/backend/routes/routes.go b/backend/routes/routes.go index 93dc1dd..2eb91a4 100644 --- a/backend/routes/routes.go +++ b/backend/routes/routes.go @@ -26,6 +26,7 @@ func InitRoutes(router *gin.Engine) { v1.GET("/users/:id", middleware.CheckAuth, controllers.FetchUser) v1.GET("/demos", controllers.DownloadDemoWithID) v1.GET("/maps/:id/summary", middleware.CheckAuth, controllers.FetchMapSummary) + v1.POST("/maps/:id/summary", middleware.CheckAuth, controllers.CreateMapSummary) v1.PUT("/maps/:id/summary", middleware.CheckAuth, controllers.EditMapSummary) v1.GET("/maps/:id/leaderboards", middleware.CheckAuth, controllers.FetchMapLeaderboards) v1.POST("/maps/:id/record", middleware.CheckAuth, controllers.CreateRecordWithDemo) -- cgit v1.2.3 From f9ebd85d20cfa794468f9b07b4b781bcd6b686ab Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 29 Jun 2023 19:34:25 +0300 Subject: feat: delete map summary Former-commit-id: bd5bc84fc142f44bfa3c1c1ddc35e435a3f816bf --- backend/controllers/modController.go | 91 +++++++++++++++++++++++++++++++++++- backend/models/requests.go | 4 ++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index 75be112..9398b2c 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -89,7 +89,7 @@ func CreateMapSummary(c *gin.Context) { // Return response c.JSON(http.StatusOK, models.Response{ Success: true, - Message: "Successfully updated map summary.", + Message: "Successfully created map summary.", Data: request, }) } @@ -175,3 +175,92 @@ func EditMapSummary(c *gin.Context) { Data: request, }) } + +// DELETE Map Summary +// +// @Summary Delete map summary with specified map id. +// @Tags maps +// @Produce json +// @Param id path int true "Map ID" +// @Param request body models.DeleteMapSummaryRequest true "Body" +// @Success 200 {object} models.Response{data=models.DeleteMapSummaryRequest} +// @Failure 400 {object} models.Response +// @Router /maps/{id}/summary [post] +func DeleteMapSummary(c *gin.Context) { + // Check if user exists + user, exists := c.Get("user") + if !exists { + c.JSON(http.StatusUnauthorized, models.ErrorResponse("User not logged in.")) + return + } + var moderator bool + for _, title := range user.(models.User).Titles { + if title == "Moderator" { + moderator = true + } + } + if !moderator { + c.JSON(http.StatusUnauthorized, models.ErrorResponse("Insufficient permissions.")) + return + } + // Bind parameter and body + id := c.Param("id") + mapID, err := strconv.Atoi(id) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + var request models.DeleteMapSummaryRequest + if err := c.BindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + // Start database transaction + tx, err := database.DB.Begin() + if err != nil { + c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) + return + } + defer tx.Rollback() + // Fetch route category and score count + var checkMapID, scoreCount, mapHistoryID int + sql := `SELECT m.id, mr.score_count FROM maps m INNER JOIN map_routes mr ON m.id=mr.map_id WHERE m.id = $1 AND mr.id = $2` + err = database.DB.QueryRow(sql, mapID, request.RouteID).Scan(&checkMapID, &scoreCount) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + if mapID != checkMapID { + c.JSON(http.StatusBadRequest, models.ErrorResponse("Map ID does not exist.")) + return + } + sql = `SELECT mh.id FROM maps m INNER JOIN map_routes mr ON m.id=mr.map_id INNER JOIN map_history mh ON m.id=mh.map_id WHERE m.id = $1 AND mr.id = $2 AND mh.score_count = $3` + err = database.DB.QueryRow(sql, mapID, request.RouteID, scoreCount).Scan(&mapHistoryID) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + // Update database with new data + sql = `DELETE FROM map_routes mr WHERE mr.id = $1 ` + _, err = tx.Exec(sql, request.RouteID) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + sql = `DELETE FROM map_history mh WHERE mh.id = $1` + _, err = tx.Exec(sql, mapHistoryID) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + if err = tx.Commit(); err != nil { + c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) + return + } + // Return response + c.JSON(http.StatusOK, models.Response{ + Success: true, + Message: "Successfully delete map summary.", + Data: request, + }) +} diff --git a/backend/models/requests.go b/backend/models/requests.go index 767b4d8..f275203 100644 --- a/backend/models/requests.go +++ b/backend/models/requests.go @@ -23,6 +23,10 @@ type EditMapSummaryRequest struct { RecordDate time.Time `json:"record_date" binding:"required"` } +type DeleteMapSummaryRequest struct { + RouteID int `json:"route_id" binding:"required"` +} + type RecordRequest struct { HostDemo *multipart.FileHeader `json:"host_demo" form:"host_demo" binding:"required" swaggerignore:"true"` PartnerDemo *multipart.FileHeader `json:"partner_demo" form:"partner_demo" swaggerignore:"true"` -- cgit v1.2.3 From 7d27b7bde11fa117f37ce18e0807ef299b69c700 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 29 Jun 2023 19:35:02 +0300 Subject: docs: updated for add/edit/delete map summary Former-commit-id: 0f2f20f84b5a52d747f18fba771bc88478f8c059 --- docs/docs.go | 96 +++++++++++++++++++++++++++++++++++++++++++++++++------ docs/swagger.json | 96 +++++++++++++++++++++++++++++++++++++++++++++++++------ docs/swagger.yaml | 66 +++++++++++++++++++++++++++++++++----- 3 files changed, 232 insertions(+), 26 deletions(-) diff --git a/docs/docs.go b/docs/docs.go index 8318e14..40632e3 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -492,6 +492,59 @@ const docTemplate = `{ } } } + }, + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "maps" + ], + "summary": "Delete map summary with specified map id.", + "parameters": [ + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.DeleteMapSummaryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/models.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.DeleteMapSummaryRequest" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Response" + } + } + } } }, "/profile": { @@ -874,24 +927,49 @@ const docTemplate = `{ } } }, - "models.EditMapSummaryRequest": { + "models.CreateMapSummaryRequest": { "type": "object", "required": [ - "image" + "category_id", + "description", + "record_date", + "score_count", + "showcase", + "user_name" ], "properties": { - "image": { + "category_id": { + "type": "integer" + }, + "description": { "type": "string" }, - "routes": { - "type": "array", - "items": { - "$ref": "#/definitions/models.EditMapSummaryRequestDetails" - } + "record_date": { + "type": "string" + }, + "score_count": { + "type": "integer" + }, + "showcase": { + "type": "string" + }, + "user_name": { + "type": "string" } } }, - "models.EditMapSummaryRequestDetails": { + "models.DeleteMapSummaryRequest": { + "type": "object", + "required": [ + "route_id" + ], + "properties": { + "route_id": { + "type": "integer" + } + } + }, + "models.EditMapSummaryRequest": { "type": "object", "required": [ "description", diff --git a/docs/swagger.json b/docs/swagger.json index 212ebee..7e251ca 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -485,6 +485,59 @@ } } } + }, + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "maps" + ], + "summary": "Delete map summary with specified map id.", + "parameters": [ + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.DeleteMapSummaryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/models.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.DeleteMapSummaryRequest" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Response" + } + } + } } }, "/profile": { @@ -867,24 +920,49 @@ } } }, - "models.EditMapSummaryRequest": { + "models.CreateMapSummaryRequest": { "type": "object", "required": [ - "image" + "category_id", + "description", + "record_date", + "score_count", + "showcase", + "user_name" ], "properties": { - "image": { + "category_id": { + "type": "integer" + }, + "description": { "type": "string" }, - "routes": { - "type": "array", - "items": { - "$ref": "#/definitions/models.EditMapSummaryRequestDetails" - } + "record_date": { + "type": "string" + }, + "score_count": { + "type": "integer" + }, + "showcase": { + "type": "string" + }, + "user_name": { + "type": "string" } } }, - "models.EditMapSummaryRequestDetails": { + "models.DeleteMapSummaryRequest": { + "type": "object", + "required": [ + "route_id" + ], + "properties": { + "route_id": { + "type": "integer" + } + } + }, + "models.EditMapSummaryRequest": { "type": "object", "required": [ "description", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index ba20f6d..5309f67 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -32,18 +32,36 @@ definitions: game: $ref: '#/definitions/models.Game' type: object - models.EditMapSummaryRequest: + models.CreateMapSummaryRequest: properties: - image: + category_id: + type: integer + description: type: string - routes: - items: - $ref: '#/definitions/models.EditMapSummaryRequestDetails' - type: array + record_date: + type: string + score_count: + type: integer + showcase: + type: string + user_name: + type: string + required: + - category_id + - description + - record_date + - score_count + - showcase + - user_name + type: object + models.DeleteMapSummaryRequest: + properties: + route_id: + type: integer required: - - image + - route_id type: object - models.EditMapSummaryRequestDetails: + models.EditMapSummaryRequest: properties: description: type: string @@ -474,6 +492,38 @@ paths: summary: Get map summary with specified id. tags: - maps + post: + parameters: + - description: Map ID + in: path + name: id + required: true + type: integer + - description: Body + in: body + name: request + required: true + schema: + $ref: '#/definitions/models.DeleteMapSummaryRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/models.Response' + - properties: + data: + $ref: '#/definitions/models.DeleteMapSummaryRequest' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Response' + summary: Delete map summary with specified map id. + tags: + - maps put: parameters: - description: Map ID -- cgit v1.2.3 From f187bc7df9b25acdbbf07f13063c9ea0111a6a3b Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 29 Jun 2023 19:35:54 +0300 Subject: docs: fix delete map summary Former-commit-id: c94adce2fb9a6210f2732a35075bbe49adebc532 --- backend/controllers/modController.go | 2 +- docs/docs.go | 53 ++++++++++++++++++++++++++++++++++++ docs/swagger.json | 53 ++++++++++++++++++++++++++++++++++++ docs/swagger.yaml | 38 ++++++++++++++++++++++++-- 4 files changed, 142 insertions(+), 4 deletions(-) diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index 9398b2c..a8e0786 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -185,7 +185,7 @@ func EditMapSummary(c *gin.Context) { // @Param request body models.DeleteMapSummaryRequest true "Body" // @Success 200 {object} models.Response{data=models.DeleteMapSummaryRequest} // @Failure 400 {object} models.Response -// @Router /maps/{id}/summary [post] +// @Router /maps/{id}/summary [delete] func DeleteMapSummary(c *gin.Context) { // Check if user exists user, exists := c.Get("user") diff --git a/docs/docs.go b/docs/docs.go index 40632e3..8affdab 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -494,6 +494,59 @@ const docTemplate = `{ } }, "post": { + "produces": [ + "application/json" + ], + "tags": [ + "maps" + ], + "summary": "Create map summary with specified map id.", + "parameters": [ + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CreateMapSummaryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/models.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.CreateMapSummaryRequest" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Response" + } + } + } + }, + "delete": { "produces": [ "application/json" ], diff --git a/docs/swagger.json b/docs/swagger.json index 7e251ca..af3fa11 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -487,6 +487,59 @@ } }, "post": { + "produces": [ + "application/json" + ], + "tags": [ + "maps" + ], + "summary": "Create map summary with specified map id.", + "parameters": [ + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.CreateMapSummaryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/models.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.CreateMapSummaryRequest" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Response" + } + } + } + }, + "delete": { "produces": [ "application/json" ], diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 5309f67..0c55964 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -466,6 +466,38 @@ paths: tags: - maps /maps/{id}/summary: + delete: + parameters: + - description: Map ID + in: path + name: id + required: true + type: integer + - description: Body + in: body + name: request + required: true + schema: + $ref: '#/definitions/models.DeleteMapSummaryRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/models.Response' + - properties: + data: + $ref: '#/definitions/models.DeleteMapSummaryRequest' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Response' + summary: Delete map summary with specified map id. + tags: + - maps get: parameters: - description: Map ID @@ -504,7 +536,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/models.DeleteMapSummaryRequest' + $ref: '#/definitions/models.CreateMapSummaryRequest' produces: - application/json responses: @@ -515,13 +547,13 @@ paths: - $ref: '#/definitions/models.Response' - properties: data: - $ref: '#/definitions/models.DeleteMapSummaryRequest' + $ref: '#/definitions/models.CreateMapSummaryRequest' type: object "400": description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Delete map summary with specified map id. + summary: Create map summary with specified map id. tags: - maps put: -- cgit v1.2.3 From f3b4838e255745cdc3dec368ed29e5b321dedc8b Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 29 Jun 2023 19:40:07 +0300 Subject: docs: add jwt token requirements Former-commit-id: e5b56433686e2180a99293f0ddf339e70b304c67 --- backend/controllers/modController.go | 3 +++ docs/docs.go | 21 +++++++++++++++++++++ docs/swagger.json | 21 +++++++++++++++++++++ docs/swagger.yaml | 15 +++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index a8e0786..b26f1ae 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -14,6 +14,7 @@ import ( // @Summary Create map summary with specified map id. // @Tags maps // @Produce json +// @Param Authorization header string true "JWT Token" // @Param id path int true "Map ID" // @Param request body models.CreateMapSummaryRequest true "Body" // @Success 200 {object} models.Response{data=models.CreateMapSummaryRequest} @@ -99,6 +100,7 @@ func CreateMapSummary(c *gin.Context) { // @Summary Edit map summary with specified map id. // @Tags maps // @Produce json +// @Param Authorization header string true "JWT Token" // @Param id path int true "Map ID" // @Param request body models.EditMapSummaryRequest true "Body" // @Success 200 {object} models.Response{data=models.EditMapSummaryRequest} @@ -181,6 +183,7 @@ func EditMapSummary(c *gin.Context) { // @Summary Delete map summary with specified map id. // @Tags maps // @Produce json +// @Param Authorization header string true "JWT Token" // @Param id path int true "Map ID" // @Param request body models.DeleteMapSummaryRequest true "Body" // @Success 200 {object} models.Response{data=models.DeleteMapSummaryRequest} diff --git a/docs/docs.go b/docs/docs.go index 8affdab..9368c2c 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -449,6 +449,13 @@ const docTemplate = `{ ], "summary": "Edit map summary with specified map id.", "parameters": [ + { + "type": "string", + "description": "JWT Token", + "name": "Authorization", + "in": "header", + "required": true + }, { "type": "integer", "description": "Map ID", @@ -502,6 +509,13 @@ const docTemplate = `{ ], "summary": "Create map summary with specified map id.", "parameters": [ + { + "type": "string", + "description": "JWT Token", + "name": "Authorization", + "in": "header", + "required": true + }, { "type": "integer", "description": "Map ID", @@ -555,6 +569,13 @@ const docTemplate = `{ ], "summary": "Delete map summary with specified map id.", "parameters": [ + { + "type": "string", + "description": "JWT Token", + "name": "Authorization", + "in": "header", + "required": true + }, { "type": "integer", "description": "Map ID", diff --git a/docs/swagger.json b/docs/swagger.json index af3fa11..3acc883 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -442,6 +442,13 @@ ], "summary": "Edit map summary with specified map id.", "parameters": [ + { + "type": "string", + "description": "JWT Token", + "name": "Authorization", + "in": "header", + "required": true + }, { "type": "integer", "description": "Map ID", @@ -495,6 +502,13 @@ ], "summary": "Create map summary with specified map id.", "parameters": [ + { + "type": "string", + "description": "JWT Token", + "name": "Authorization", + "in": "header", + "required": true + }, { "type": "integer", "description": "Map ID", @@ -548,6 +562,13 @@ ], "summary": "Delete map summary with specified map id.", "parameters": [ + { + "type": "string", + "description": "JWT Token", + "name": "Authorization", + "in": "header", + "required": true + }, { "type": "integer", "description": "Map ID", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 0c55964..6d06e22 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -468,6 +468,11 @@ paths: /maps/{id}/summary: delete: parameters: + - description: JWT Token + in: header + name: Authorization + required: true + type: string - description: Map ID in: path name: id @@ -526,6 +531,11 @@ paths: - maps post: parameters: + - description: JWT Token + in: header + name: Authorization + required: true + type: string - description: Map ID in: path name: id @@ -558,6 +568,11 @@ paths: - maps put: parameters: + - description: JWT Token + in: header + name: Authorization + required: true + type: string - description: Map ID in: path name: id -- cgit v1.2.3 From c393b3771a51453258cd504bf816e4f12b0cfc2b Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 29 Jun 2023 19:40:20 +0300 Subject: fix: remove middleware for unneeded routes Former-commit-id: 0e357aca92274049c7bea7d70910befef4427efc --- backend/routes/routes.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/routes/routes.go b/backend/routes/routes.go index 2eb91a4..feb49f3 100644 --- a/backend/routes/routes.go +++ b/backend/routes/routes.go @@ -25,12 +25,12 @@ func InitRoutes(router *gin.Engine) { v1.POST("/profile", middleware.CheckAuth, controllers.UpdateUser) v1.GET("/users/:id", middleware.CheckAuth, controllers.FetchUser) v1.GET("/demos", controllers.DownloadDemoWithID) - v1.GET("/maps/:id/summary", middleware.CheckAuth, controllers.FetchMapSummary) + v1.GET("/maps/:id/summary", controllers.FetchMapSummary) v1.POST("/maps/:id/summary", middleware.CheckAuth, controllers.CreateMapSummary) v1.PUT("/maps/:id/summary", middleware.CheckAuth, controllers.EditMapSummary) - v1.GET("/maps/:id/leaderboards", middleware.CheckAuth, controllers.FetchMapLeaderboards) + v1.GET("/maps/:id/leaderboards", controllers.FetchMapLeaderboards) v1.POST("/maps/:id/record", middleware.CheckAuth, controllers.CreateRecordWithDemo) - v1.GET("/rankings", middleware.CheckAuth, controllers.Rankings) + v1.GET("/rankings", controllers.Rankings) v1.GET("/search", controllers.SearchWithQuery) v1.GET("/games", controllers.FetchGames) v1.GET("/games/:id", controllers.FetchChapters) -- cgit v1.2.3 From 82722f374ce6c6e8a44bffa0a25fe4bf2138004d Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Fri, 30 Jun 2023 14:25:40 +0300 Subject: feat: add moderator status into jwt token Former-commit-id: 3034de650c9e5898a1915814ea12c4c7f4f627db --- backend/controllers/loginController.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/backend/controllers/loginController.go b/backend/controllers/loginController.go index cfe086d..b30d26e 100644 --- a/backend/controllers/loginController.go +++ b/backend/controllers/loginController.go @@ -59,10 +59,20 @@ func Login(c *gin.Context) { database.DB.Exec(`INSERT INTO users (steam_id, user_name, avatar_link, country_code) VALUES ($1, $2, $3, $4)`, steamID, user.PersonaName, user.AvatarFull, user.LocCountryCode) } + moderator := false + rows, _ := database.DB.Query("SELECT title_name FROM titles WHERE user_id = $1", steamID) + for rows.Next() { + var title string + rows.Scan(&title) + if title == "Moderator" { + moderator = true + } + } // Generate JWT token token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "sub": steamID, "exp": time.Now().Add(time.Hour * 24 * 30).Unix(), + "mod": moderator, }) // Sign and get the complete encoded token as a string using the secret tokenString, err := token.SignedString([]byte(os.Getenv("SECRET_KEY"))) -- cgit v1.2.3 From 1a14309e212a697bae8acd4ddb17723b0f6670a6 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 08:40:43 +0000 Subject: feat: parser for getting portal and tick count (#42) Former-commit-id: 1619ece868b7009a661dcc3b622746cc09981042 --- backend/controllers/recordController.go | 2 +- backend/parser/parser.go | 41 ++++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index c865bfb..183ab27 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -106,7 +106,7 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo(record.HostDemo) + hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("parser/demos/" + header.Filename) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return diff --git a/backend/parser/parser.go b/backend/parser/parser.go index 6f9a24f..040b94a 100644 --- a/backend/parser/parser.go +++ b/backend/parser/parser.go @@ -1,7 +1,42 @@ package parser -import "mime/multipart" +import ( + "bufio" + "fmt" + "os/exec" + "strconv" + "strings" +) -func ProcessDemo(demo *multipart.FileHeader) (scoreCount int, scoreTime int, err error) { - return 0, 0, nil +func ProcessDemo(demoPath string) (int, int, error) { + cmd := exec.Command("bash", "-c", fmt.Sprintf(`echo "FEXBash" && ./parser %s`, demoPath)) + stdout, err := cmd.StdoutPipe() + if err != nil { + return 0, 0, err + } + cmd.Start() + scanner := bufio.NewScanner(stdout) + var cmTicks, portalCount int + for scanner.Scan() { + line := scanner.Text() + if strings.Contains(line, "CM ticks") { + cmTicksStr := strings.TrimSpace(strings.Split(line, ":")[1]) + cmTicks, err = strconv.Atoi(cmTicksStr) + if err != nil { + return 0, 0, err + } + } + if strings.Contains(line, "Portal count") { + portalCountStr := strings.TrimSpace(strings.Split(line, ":")[1]) + portalCount, err = strconv.Atoi(portalCountStr) + if err != nil { + return 0, 0, err + } + } + } + err = cmd.Wait() + if err != nil { + return 0, 0, err + } + return cmTicks, portalCount, nil } -- cgit v1.2.3 From be4efc7d09d3ebbe63c4b4d7763341189545ad3c Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 18:01:15 +0300 Subject: docs: update readme Former-commit-id: a810590f30535b528e27eb45d836bf7b5af9c11d --- README.md | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2063667..5a44f82 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ -# Portal 2 Least Portals Database +# Portal 2 Least Portals Hub -This project is work in progress. +## About LPHUB + +Least Portals Hub (LPHUB) is a community-driven platform that aims to gather knowledge and competition of the Portal 2 Least Portals (LP) category in one place. LPHUB is dedicated to bringing together users who love the challenge of LP, where the goal is to complete a level using the fewest number of portals possible that is placed by the player. + +Our comprehensive map library contains detailed, wiki-style information for maps from Portal 2 and their mods, including strategies and routes for achieving specific portal counts. These guides are designed to assist players of all levels in improving their skills, understanding the game's mechanics better, and finding innovative ways to lower their portal usage. + +In addition, we host a leaderboard section for each map where users can compare their accomplishments and compete for having the lowest amount of portals possible. Users can upload their records, along with demo proof in order to get on to the leaderboards. ## Project Team @@ -9,6 +15,10 @@ This project is work in progress. * [@Nidboj132](https://github.com/Nidboj132) - Frontend * [@Oryn](https://github.com/Oryn-Goia) - Subject Expert +## Special Thanks + +* [@UncraftedName](https://github.com/UncraftedName) - For providing the parser that retrieves total portal count and elapsed time from the CM counter. + ## Documentation Full documentation can be found at https://lp.ardapektezol.com/api/v1/ @@ -19,4 +29,22 @@ This project is licensed under the GNU General Public License v2.0 - see the [LI ## Contact -If you have any questions or feedback, please feel free to contact us at our [Discord](https://discord.gg/xq6TySyA4c). \ No newline at end of file +If you have any questions or feedback, please feel free to contact us at our [Discord](https://discord.gg/xq6TySyA4c). + +## Support + +If you want to support the creator, you can do so by using GitHub sponsorships by [clicking here](https://github.com/sponsors/pektezol). + +## Disclaimer + +This project, "Portal 2 Least Portals Hub" (hereafter referred to as "LPHUB"), is an unofficial community-driven resource providing strategies, routes, leaderboards, and other information related to the "Least Portals" category of the game "Portal 2". LPHUB is not affiliated with or endorsed by the creators, developers, or publishers of "Portal 2", including but not limited to Valve Corporation. + +The strategies, routes, leaderboard entries, and other information presented on LPHUB are provided by members of the community and are not guaranteed to be correct, accurate, or applicable to all versions or modes of "Portal 2". Users are encouraged to verify information and submit corrections or additions as necessary. + +Leaderboard entries must include demo proof, which is subject to review by LPHUB administrators and moderators. While every effort is made to ensure the integrity of the leaderboard, LPHUB cannot guarantee the authenticity or accuracy of any submitted record or demo. + +The source code for LPHUB is licensed under the GNU General Public License version 2.0 (GPL-2.0). By using LPHUB, you agree to abide by the terms and conditions of this license. The source code is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software. + +Your use of LPHUB is at your own risk. LPHUB and its administrators and moderators disclaim all liability for any damages, whether direct, indirect, incidental, or consequential, that may result from your use of LPHUB or the strategies, routes, or other information provided therein. + +By using LPHUB, you acknowledge that you have read and understood this disclaimer and agree to its terms. \ No newline at end of file -- cgit v1.2.3 From 9f09b97e7ccd02a95fd95542b1a71a42cc93fca3 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 18:01:31 +0300 Subject: docs: add github sponsorship button Former-commit-id: 146de69d05ae27afbbdefb313c44be6ccf4aada9 --- FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 FUNDING.yml diff --git a/FUNDING.yml b/FUNDING.yml new file mode 100644 index 0000000..bd67f90 --- /dev/null +++ b/FUNDING.yml @@ -0,0 +1 @@ +github: pektezol \ No newline at end of file -- cgit v1.2.3 From 57ef0fc54af49052539306c35501a893f24eca18 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 19:40:43 +0300 Subject: docs: update swagger Former-commit-id: d55150a028f67d5dc8668e2b2384d13511339b46 --- backend/controllers/modController.go | 30 +++++++++++++++--------------- backend/controllers/recordController.go | 6 +++--- docs/docs.go | 15 +++++---------- docs/swagger.json | 9 +++------ docs/swagger.yaml | 3 --- 5 files changed, 26 insertions(+), 37 deletions(-) diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index b26f1ae..f1874f7 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -14,11 +14,11 @@ import ( // @Summary Create map summary with specified map id. // @Tags maps // @Produce json -// @Param Authorization header string true "JWT Token" -// @Param id path int true "Map ID" -// @Param request body models.CreateMapSummaryRequest true "Body" -// @Success 200 {object} models.Response{data=models.CreateMapSummaryRequest} -// @Failure 400 {object} models.Response +// @Param Authorization header string true "JWT Token" +// @Param id path int true "Map ID" +// @Param request body models.CreateMapSummaryRequest true "Body" +// @Success 200 {object} models.Response{data=models.CreateMapSummaryRequest} +// @Failure 400 {object} models.Response // @Router /maps/{id}/summary [post] func CreateMapSummary(c *gin.Context) { // Check if user exists @@ -100,11 +100,11 @@ func CreateMapSummary(c *gin.Context) { // @Summary Edit map summary with specified map id. // @Tags maps // @Produce json -// @Param Authorization header string true "JWT Token" -// @Param id path int true "Map ID" -// @Param request body models.EditMapSummaryRequest true "Body" -// @Success 200 {object} models.Response{data=models.EditMapSummaryRequest} -// @Failure 400 {object} models.Response +// @Param Authorization header string true "JWT Token" +// @Param id path int true "Map ID" +// @Param request body models.EditMapSummaryRequest true "Body" +// @Success 200 {object} models.Response{data=models.EditMapSummaryRequest} +// @Failure 400 {object} models.Response // @Router /maps/{id}/summary [put] func EditMapSummary(c *gin.Context) { // Check if user exists @@ -183,11 +183,11 @@ func EditMapSummary(c *gin.Context) { // @Summary Delete map summary with specified map id. // @Tags maps // @Produce json -// @Param Authorization header string true "JWT Token" -// @Param id path int true "Map ID" -// @Param request body models.DeleteMapSummaryRequest true "Body" -// @Success 200 {object} models.Response{data=models.DeleteMapSummaryRequest} -// @Failure 400 {object} models.Response +// @Param Authorization header string true "JWT Token" +// @Param id path int true "Map ID" +// @Param request body models.DeleteMapSummaryRequest true "Body" +// @Success 200 {object} models.Response{data=models.DeleteMapSummaryRequest} +// @Failure 400 {object} models.Response // @Router /maps/{id}/summary [delete] func DeleteMapSummary(c *gin.Context) { // Check if user exists diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index 183ab27..9cc6da6 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -27,9 +27,9 @@ import ( // @Produce json // @Param Authorization header string true "JWT Token" // @Param host_demo formData file true "Host Demo" -// @Param partner_demo formData file true "Partner Demo" -// @Param is_partner_orange formData boolean true "Is Partner Orange" -// @Param partner_id formData string true "Partner ID" +// @Param partner_demo formData file false "Partner Demo" +// @Param is_partner_orange formData boolean false "Is Partner Orange" +// @Param partner_id formData string false "Partner ID" // @Success 200 {object} models.Response // @Failure 400 {object} models.Response // @Failure 401 {object} models.Response diff --git a/docs/docs.go b/docs/docs.go index 9368c2c..41acab3 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1,5 +1,5 @@ -// Code generated by swaggo/swag. DO NOT EDIT. - +// Package docs GENERATED BY SWAG; DO NOT EDIT +// This file was generated by swaggo/swag package docs import "github.com/swaggo/swag" @@ -355,22 +355,19 @@ const docTemplate = `{ "type": "file", "description": "Partner Demo", "name": "partner_demo", - "in": "formData", - "required": true + "in": "formData" }, { "type": "boolean", "description": "Is Partner Orange", "name": "is_partner_orange", - "in": "formData", - "required": true + "in": "formData" }, { "type": "string", "description": "Partner ID", "name": "partner_id", - "in": "formData", - "required": true + "in": "formData" } ], "responses": { @@ -1314,8 +1311,6 @@ var SwaggerInfo = &swag.Spec{ Description: "Backend API endpoints for the Least Portals Database.", InfoInstanceName: "swagger", SwaggerTemplate: docTemplate, - LeftDelim: "{{", - RightDelim: "}}", } func init() { diff --git a/docs/swagger.json b/docs/swagger.json index 3acc883..3ec06b6 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -348,22 +348,19 @@ "type": "file", "description": "Partner Demo", "name": "partner_demo", - "in": "formData", - "required": true + "in": "formData" }, { "type": "boolean", "description": "Is Partner Orange", "name": "is_partner_orange", - "in": "formData", - "required": true + "in": "formData" }, { "type": "string", "description": "Partner ID", "name": "partner_id", - "in": "formData", - "required": true + "in": "formData" } ], "responses": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 6d06e22..3fe5d66 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -435,17 +435,14 @@ paths: - description: Partner Demo in: formData name: partner_demo - required: true type: file - description: Is Partner Orange in: formData name: is_partner_orange - required: true type: boolean - description: Partner ID in: formData name: partner_id - required: true type: string produces: - application/json -- cgit v1.2.3 From 31fdc794d645967542e38d7670a5f19ac745717e Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 19:47:38 +0300 Subject: fix: sql typo record controller Former-commit-id: 8d78b48a9c745d94f4e287b0d2b7871ecdb4873b --- backend/controllers/recordController.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index 9cc6da6..101f0e4 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -46,7 +46,7 @@ func CreateRecordWithDemo(c *gin.Context) { var gameName string var isCoop bool var isDisabled bool - sql := `SELECT g.name, m.is_disabled FROM maps m INNER JOIN games g ON m.game_id=g.id WHERE id = $1` + sql := `SELECT g.name, m.is_disabled FROM maps m INNER JOIN games g ON m.game_id=g.id WHERE m.id = $1` err := database.DB.QueryRow(sql, mapId).Scan(&gameName, &isDisabled) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) -- cgit v1.2.3 From 4a5a3f13a7b644decb804bc1e1507dda14477205 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 19:52:42 +0300 Subject: docs: add missing field to post record Former-commit-id: 73726de788daf38a82d9663ab2d8f0544b76c3e3 --- backend/controllers/recordController.go | 1 + docs/docs.go | 7 +++++++ docs/swagger.json | 7 +++++++ docs/swagger.yaml | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index 101f0e4..6751afc 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -25,6 +25,7 @@ import ( // @Tags maps // @Accept mpfd // @Produce json +// @Param id path int true "Map ID" // @Param Authorization header string true "JWT Token" // @Param host_demo formData file true "Host Demo" // @Param partner_demo formData file false "Partner Demo" diff --git a/docs/docs.go b/docs/docs.go index 41acab3..bb14382 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -337,6 +337,13 @@ const docTemplate = `{ ], "summary": "Post record with demo of a specific map.", "parameters": [ + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + }, { "type": "string", "description": "JWT Token", diff --git a/docs/swagger.json b/docs/swagger.json index 3ec06b6..3530d2c 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -330,6 +330,13 @@ ], "summary": "Post record with demo of a specific map.", "parameters": [ + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + }, { "type": "string", "description": "JWT Token", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 3fe5d66..d4420e2 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -422,6 +422,11 @@ paths: consumes: - multipart/form-data parameters: + - description: Map ID + in: path + name: id + required: true + type: integer - description: JWT Token in: header name: Authorization -- cgit v1.2.3 From db9482a73804e3461389553ba26a40f488c51180 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 19:56:09 +0300 Subject: fix: change demo save location Former-commit-id: 2e7592b91f3787b501c798397838ba858f4103f7 --- backend/controllers/recordController.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index 6751afc..891dfef 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -91,12 +91,12 @@ func CreateRecordWithDemo(c *gin.Context) { for i, header := range demoFiles { uuid := uuid.New().String() // Upload & insert into demos - err = c.SaveUploadedFile(header, "parser/demos/"+header.Filename) + err = c.SaveUploadedFile(header, "parser/"+header.Filename) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - f, err := os.Open("parser/demos/" + header.Filename) + f, err := os.Open("parser/" + header.Filename) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -107,7 +107,7 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("parser/demos/" + header.Filename) + hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("parser/" + header.Filename) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -125,7 +125,7 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - os.Remove("parser/demos/" + header.Filename) + os.Remove("parser/" + header.Filename) } // Insert into records if isCoop { -- cgit v1.2.3 From 5426b3d6dc367b8edb01cbacebf3a884eb3b0004 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 20:01:07 +0300 Subject: fix: change demo save location Former-commit-id: 9f12bccafd5433775c7de315950f02f9464777fa --- backend/controllers/recordController.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index 891dfef..28c55e0 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -91,12 +91,12 @@ func CreateRecordWithDemo(c *gin.Context) { for i, header := range demoFiles { uuid := uuid.New().String() // Upload & insert into demos - err = c.SaveUploadedFile(header, "parser/"+header.Filename) + err = c.SaveUploadedFile(header, "backend/parser/demos/"+header.Filename) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - f, err := os.Open("parser/" + header.Filename) + f, err := os.Open("backend/parser/demos/" + header.Filename) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -107,7 +107,7 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("parser/" + header.Filename) + hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("backend/parser/demos/" + header.Filename) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -125,7 +125,7 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - os.Remove("parser/" + header.Filename) + os.Remove("backend/parser/demos/" + header.Filename) } // Insert into records if isCoop { -- cgit v1.2.3 From 50f5fff2873847b3e5df92d204e5166b641baeb2 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 17:22:14 +0000 Subject: fix: parser actually works now (#42) Former-commit-id: 28378525f79e2879a1306b3bb169668d238cc117 --- backend/controllers/recordController.go | 13 ++++++++----- backend/parser/parser.go | 13 +++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index 28c55e0..1c0f3b2 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -71,7 +71,10 @@ func CreateRecordWithDemo(c *gin.Context) { return } // Demo files - demoFiles := []*multipart.FileHeader{record.HostDemo, record.PartnerDemo} + demoFiles := []*multipart.FileHeader{record.HostDemo} + if isCoop { + demoFiles = append(demoFiles, record.PartnerDemo) + } var hostDemoUUID, hostDemoFileID, partnerDemoUUID, partnerDemoFileID string var hostDemoScoreCount, hostDemoScoreTime int client := serviceAccount() @@ -91,12 +94,12 @@ func CreateRecordWithDemo(c *gin.Context) { for i, header := range demoFiles { uuid := uuid.New().String() // Upload & insert into demos - err = c.SaveUploadedFile(header, "backend/parser/demos/"+header.Filename) + err = c.SaveUploadedFile(header, "backend/parser/"+header.Filename) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - f, err := os.Open("backend/parser/demos/" + header.Filename) + f, err := os.Open("backend/parser/" + header.Filename) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -107,7 +110,7 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("backend/parser/demos/" + header.Filename) + hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("backend/parser/" + header.Filename) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -125,7 +128,7 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - os.Remove("backend/parser/demos/" + header.Filename) + os.Remove("backend/parser/" + header.Filename) } // Insert into records if isCoop { diff --git a/backend/parser/parser.go b/backend/parser/parser.go index 040b94a..562b8c0 100644 --- a/backend/parser/parser.go +++ b/backend/parser/parser.go @@ -3,15 +3,17 @@ package parser import ( "bufio" "fmt" + "log" "os/exec" "strconv" "strings" ) func ProcessDemo(demoPath string) (int, int, error) { - cmd := exec.Command("bash", "-c", fmt.Sprintf(`echo "FEXBash" && ./parser %s`, demoPath)) + cmd := exec.Command("bash", "-c", fmt.Sprintf(`echo "FEXBash" && ./backend/parser/parser %s`, demoPath)) stdout, err := cmd.StdoutPipe() if err != nil { + log.Println(err) return 0, 0, err } cmd.Start() @@ -34,9 +36,8 @@ func ProcessDemo(demoPath string) (int, int, error) { } } } - err = cmd.Wait() - if err != nil { - return 0, 0, err - } - return cmTicks, portalCount, nil + cmd.Wait() + // We don't check for error in wait, since FEXBash always gives segmentation fault + // Wanted output is retrieved, so it's okay (i think) + return portalCount, cmTicks, nil } -- cgit v1.2.3 From e591e24c3299266eeb65b9de3efe89a2d0addbdc Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 20:47:07 +0300 Subject: fix: cleanup post record, uuid file saving, defer file delete (#42) Former-commit-id: b98bc4164983e6f78566f0a7dd296e7f9cfd9e3e --- backend/controllers/recordController.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index 1c0f3b2..4673683 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -94,12 +94,13 @@ func CreateRecordWithDemo(c *gin.Context) { for i, header := range demoFiles { uuid := uuid.New().String() // Upload & insert into demos - err = c.SaveUploadedFile(header, "backend/parser/"+header.Filename) + err = c.SaveUploadedFile(header, "backend/parser/"+uuid) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - f, err := os.Open("backend/parser/" + header.Filename) + defer os.Remove("backend/parser/" + uuid) + f, err := os.Open("backend/parser/" + uuid) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -110,7 +111,7 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("backend/parser/" + header.Filename) + hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("backend/parser/" + uuid) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -128,7 +129,6 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - os.Remove("backend/parser/" + header.Filename) } // Insert into records if isCoop { -- cgit v1.2.3 From ae24a7a5c4c2f2aa5930f74f62f8c636f6d54bf7 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 20:59:40 +0300 Subject: fix: save uploaded demos as .dem in order for parser to work (#42) Former-commit-id: aa6485ebbdeffe4eaf903988a676fa69f37d67b9 --- backend/controllers/recordController.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index 4673683..409a2e7 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -94,13 +94,13 @@ func CreateRecordWithDemo(c *gin.Context) { for i, header := range demoFiles { uuid := uuid.New().String() // Upload & insert into demos - err = c.SaveUploadedFile(header, "backend/parser/"+uuid) + err = c.SaveUploadedFile(header, "backend/parser/"+uuid+".dem") if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - defer os.Remove("backend/parser/" + uuid) - f, err := os.Open("backend/parser/" + uuid) + defer os.Remove("backend/parser/" + uuid + ".dem") + f, err := os.Open("backend/parser/" + uuid + ".dem") if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -111,7 +111,7 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("backend/parser/" + uuid) + hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("backend/parser/" + uuid + ".dem") if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return -- cgit v1.2.3 From c071eeca829a18941fc29f4e4eb8b8f93a65b5c4 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 23:16:42 +0300 Subject: docs: refactor docs Former-commit-id: 3f3833af352d8758ca509a4fe3badedd5250b1ba --- backend/controllers/homeController.go | 26 ++++---- backend/controllers/loginController.go | 38 +++++------ backend/controllers/mapController.go | 68 +++++++++---------- backend/controllers/modController.go | 54 +++++++-------- backend/controllers/recordController.go | 44 ++++++------- backend/controllers/userController.go | 74 ++++++++++----------- docs/docs.go | 112 ++++++++++++++++---------------- docs/swagger.json | 112 ++++++++++++++++---------------- docs/swagger.yaml | 80 +++++++++++------------ 9 files changed, 304 insertions(+), 304 deletions(-) diff --git a/backend/controllers/homeController.go b/backend/controllers/homeController.go index b5c6b60..2780e63 100644 --- a/backend/controllers/homeController.go +++ b/backend/controllers/homeController.go @@ -23,12 +23,12 @@ func Home(c *gin.Context) { // GET Rankings // -// @Summary Get rankings of every player. -// @Tags rankings -// @Produce json -// @Success 200 {object} models.Response{data=models.RankingsResponse} -// @Failure 400 {object} models.Response -// @Router /demo [get] +// @Description Get rankings of every player. +// @Tags rankings +// @Produce json +// @Success 200 {object} models.Response{data=models.RankingsResponse} +// @Failure 400 {object} models.Response +// @Router /rankings [get] func Rankings(c *gin.Context) { rows, err := database.DB.Query(`SELECT steam_id, user_name FROM users`) if err != nil { @@ -125,13 +125,13 @@ func Rankings(c *gin.Context) { // GET Search With Query // -// @Summary Get all user and map data matching to the query. -// @Tags search -// @Produce json -// @Param q query string false "Search user or map name." -// @Success 200 {object} models.Response{data=models.SearchResponse} -// @Failure 400 {object} models.Response -// @Router /search [get] +// @Description Get all user and map data matching to the query. +// @Tags search +// @Produce json +// @Param q query string false "Search user or map name." +// @Success 200 {object} models.Response{data=models.SearchResponse} +// @Failure 400 {object} models.Response +// @Router /search [get] func SearchWithQuery(c *gin.Context) { query := c.Query("q") query = strings.ToLower(query) diff --git a/backend/controllers/loginController.go b/backend/controllers/loginController.go index b30d26e..ae6e957 100644 --- a/backend/controllers/loginController.go +++ b/backend/controllers/loginController.go @@ -17,13 +17,13 @@ import ( // Login // -// @Summary Get (redirect) login page for Steam auth. -// @Tags login -// @Accept json -// @Produce json -// @Success 200 {object} models.Response{data=models.LoginResponse} -// @Failure 400 {object} models.Response -// @Router /login [get] +// @Description Get (redirect) login page for Steam auth. +// @Tags login +// @Accept json +// @Produce json +// @Success 200 {object} models.Response{data=models.LoginResponse} +// @Failure 400 {object} models.Response +// @Router /login [get] func Login(c *gin.Context) { openID := steam_go.NewOpenId(c.Request) switch openID.Mode() { @@ -95,13 +95,13 @@ func Login(c *gin.Context) { // GET Token // -// @Summary Gets the token cookie value from the user. -// @Tags auth -// @Produce json +// @Description Gets the token cookie value from the user. +// @Tags auth +// @Produce json // -// @Success 200 {object} models.Response{data=models.LoginResponse} -// @Failure 404 {object} models.Response -// @Router /token [get] +// @Success 200 {object} models.Response{data=models.LoginResponse} +// @Failure 404 {object} models.Response +// @Router /token [get] func GetCookie(c *gin.Context) { cookie, err := c.Cookie("token") if err != nil { @@ -119,13 +119,13 @@ func GetCookie(c *gin.Context) { // DELETE Token // -// @Summary Deletes the token cookie from the user. -// @Tags auth -// @Produce json +// @Description Deletes the token cookie from the user. +// @Tags auth +// @Produce json // -// @Success 200 {object} models.Response{data=models.LoginResponse} -// @Failure 404 {object} models.Response -// @Router /token [delete] +// @Success 200 {object} models.Response{data=models.LoginResponse} +// @Failure 404 {object} models.Response +// @Router /token [delete] func DeleteCookie(c *gin.Context) { cookie, err := c.Cookie("token") if err != nil { diff --git a/backend/controllers/mapController.go b/backend/controllers/mapController.go index 7a26554..52b6623 100644 --- a/backend/controllers/mapController.go +++ b/backend/controllers/mapController.go @@ -11,13 +11,13 @@ import ( // GET Map Summary // -// @Summary Get map summary with specified id. -// @Tags maps -// @Produce json -// @Param id path int true "Map ID" -// @Success 200 {object} models.Response{data=models.MapSummaryResponse} -// @Failure 400 {object} models.Response -// @Router /maps/{id}/summary [get] +// @Description Get map summary with specified id. +// @Tags maps +// @Produce json +// @Param id path int true "Map ID" +// @Success 200 {object} models.Response{data=models.MapSummaryResponse} +// @Failure 400 {object} models.Response +// @Router /maps/{id}/summary [get] func FetchMapSummary(c *gin.Context) { id := c.Param("id") response := models.MapSummaryResponse{Map: models.Map{}, Summary: models.MapSummary{Routes: []models.MapRoute{}}} @@ -69,13 +69,13 @@ func FetchMapSummary(c *gin.Context) { // GET Map Leaderboards // -// @Summary Get map leaderboards with specified id. -// @Tags maps -// @Produce json -// @Param id path int true "Map ID" -// @Success 200 {object} models.Response{data=models.Map{data=models.MapRecords}} -// @Failure 400 {object} models.Response -// @Router /maps/{id}/leaderboards [get] +// @Description Get map leaderboards with specified id. +// @Tags maps +// @Produce json +// @Param id path int true "Map ID" +// @Success 200 {object} models.Response{data=models.Map{data=models.MapRecords}} +// @Failure 400 {object} models.Response +// @Router /maps/{id}/leaderboards [get] func FetchMapLeaderboards(c *gin.Context) { // TODO: make new response type id := c.Param("id") @@ -186,12 +186,12 @@ func FetchMapLeaderboards(c *gin.Context) { // GET Games // -// @Summary Get games from the leaderboards. -// @Tags games & chapters -// @Produce json -// @Success 200 {object} models.Response{data=[]models.Game} -// @Failure 400 {object} models.Response -// @Router /games [get] +// @Description Get games from the leaderboards. +// @Tags games & chapters +// @Produce json +// @Success 200 {object} models.Response{data=[]models.Game} +// @Failure 400 {object} models.Response +// @Router /games [get] func FetchGames(c *gin.Context) { rows, err := database.DB.Query(`SELECT id, name FROM games`) if err != nil { @@ -216,13 +216,13 @@ func FetchGames(c *gin.Context) { // GET Chapters of a Game // -// @Summary Get chapters from the specified game id. -// @Tags games & chapters -// @Produce json -// @Param id path int true "Game ID" -// @Success 200 {object} models.Response{data=models.ChaptersResponse} -// @Failure 400 {object} models.Response -// @Router /games/{id} [get] +// @Description Get chapters from the specified game id. +// @Tags games & chapters +// @Produce json +// @Param id path int true "Game ID" +// @Success 200 {object} models.Response{data=models.ChaptersResponse} +// @Failure 400 {object} models.Response +// @Router /games/{id} [get] func FetchChapters(c *gin.Context) { gameID := c.Param("id") intID, err := strconv.Atoi(gameID) @@ -258,13 +258,13 @@ func FetchChapters(c *gin.Context) { // GET Maps of a Chapter // -// @Summary Get maps from the specified chapter id. -// @Tags games & chapters -// @Produce json -// @Param id path int true "Chapter ID" -// @Success 200 {object} models.Response{data=models.ChapterMapsResponse} -// @Failure 400 {object} models.Response -// @Router /chapters/{id} [get] +// @Description Get maps from the specified chapter id. +// @Tags games & chapters +// @Produce json +// @Param id path int true "Chapter ID" +// @Success 200 {object} models.Response{data=models.ChapterMapsResponse} +// @Failure 400 {object} models.Response +// @Router /chapters/{id} [get] func FetchChapterMaps(c *gin.Context) { chapterID := c.Param("id") intID, err := strconv.Atoi(chapterID) diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index f1874f7..d6dc4d4 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -11,15 +11,15 @@ import ( // POST Map Summary // -// @Summary Create map summary with specified map id. -// @Tags maps -// @Produce json -// @Param Authorization header string true "JWT Token" -// @Param id path int true "Map ID" -// @Param request body models.CreateMapSummaryRequest true "Body" -// @Success 200 {object} models.Response{data=models.CreateMapSummaryRequest} -// @Failure 400 {object} models.Response -// @Router /maps/{id}/summary [post] +// @Description Create map summary with specified map id. +// @Tags maps +// @Produce json +// @Param Authorization header string true "JWT Token" +// @Param id path int true "Map ID" +// @Param request body models.CreateMapSummaryRequest true "Body" +// @Success 200 {object} models.Response{data=models.CreateMapSummaryRequest} +// @Failure 400 {object} models.Response +// @Router /maps/{id}/summary [post] func CreateMapSummary(c *gin.Context) { // Check if user exists user, exists := c.Get("user") @@ -97,15 +97,15 @@ func CreateMapSummary(c *gin.Context) { // PUT Map Summary // -// @Summary Edit map summary with specified map id. -// @Tags maps -// @Produce json -// @Param Authorization header string true "JWT Token" -// @Param id path int true "Map ID" -// @Param request body models.EditMapSummaryRequest true "Body" -// @Success 200 {object} models.Response{data=models.EditMapSummaryRequest} -// @Failure 400 {object} models.Response -// @Router /maps/{id}/summary [put] +// @Description Edit map summary with specified map id. +// @Tags maps +// @Produce json +// @Param Authorization header string true "JWT Token" +// @Param id path int true "Map ID" +// @Param request body models.EditMapSummaryRequest true "Body" +// @Success 200 {object} models.Response{data=models.EditMapSummaryRequest} +// @Failure 400 {object} models.Response +// @Router /maps/{id}/summary [put] func EditMapSummary(c *gin.Context) { // Check if user exists user, exists := c.Get("user") @@ -180,15 +180,15 @@ func EditMapSummary(c *gin.Context) { // DELETE Map Summary // -// @Summary Delete map summary with specified map id. -// @Tags maps -// @Produce json -// @Param Authorization header string true "JWT Token" -// @Param id path int true "Map ID" -// @Param request body models.DeleteMapSummaryRequest true "Body" -// @Success 200 {object} models.Response{data=models.DeleteMapSummaryRequest} -// @Failure 400 {object} models.Response -// @Router /maps/{id}/summary [delete] +// @Description Delete map summary with specified map id. +// @Tags maps +// @Produce json +// @Param Authorization header string true "JWT Token" +// @Param id path int true "Map ID" +// @Param request body models.DeleteMapSummaryRequest true "Body" +// @Success 200 {object} models.Response{data=models.DeleteMapSummaryRequest} +// @Failure 400 {object} models.Response +// @Router /maps/{id}/summary [delete] func DeleteMapSummary(c *gin.Context) { // Check if user exists user, exists := c.Get("user") diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index 409a2e7..af8eb63 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -21,20 +21,20 @@ import ( // POST Record // -// @Summary Post record with demo of a specific map. -// @Tags maps -// @Accept mpfd -// @Produce json -// @Param id path int true "Map ID" -// @Param Authorization header string true "JWT Token" -// @Param host_demo formData file true "Host Demo" -// @Param partner_demo formData file false "Partner Demo" -// @Param is_partner_orange formData boolean false "Is Partner Orange" -// @Param partner_id formData string false "Partner ID" -// @Success 200 {object} models.Response -// @Failure 400 {object} models.Response -// @Failure 401 {object} models.Response -// @Router /maps/{id}/record [post] +// @Description Post record with demo of a specific map. +// @Tags maps +// @Accept mpfd +// @Produce json +// @Param id path int true "Map ID" +// @Param Authorization header string true "JWT Token" +// @Param host_demo formData file true "Host Demo" +// @Param partner_demo formData file false "Partner Demo" +// @Param is_partner_orange formData boolean false "Is Partner Orange" +// @Param partner_id formData string false "Partner ID" +// @Success 200 {object} models.Response +// @Failure 400 {object} models.Response +// @Failure 401 {object} models.Response +// @Router /maps/{id}/record [post] func CreateRecordWithDemo(c *gin.Context) { mapId := c.Param("id") // Check if user exists @@ -189,14 +189,14 @@ func CreateRecordWithDemo(c *gin.Context) { // GET Demo // -// @Summary Get demo with specified demo uuid. -// @Tags demo -// @Accept json -// @Produce octet-stream -// @Param uuid query int true "Demo UUID" -// @Success 200 {file} binary "Demo File" -// @Failure 400 {object} models.Response -// @Router /demos [get] +// @Description Get demo with specified demo uuid. +// @Tags demo +// @Accept json +// @Produce octet-stream +// @Param uuid query string true "Demo UUID" +// @Success 200 {file} binary "Demo File" +// @Failure 400 {object} models.Response +// @Router /demos [get] func DownloadDemoWithID(c *gin.Context) { uuid := c.Query("uuid") var locationID string diff --git a/backend/controllers/userController.go b/backend/controllers/userController.go index cbce0fe..e73b1fe 100644 --- a/backend/controllers/userController.go +++ b/backend/controllers/userController.go @@ -13,15 +13,15 @@ import ( // GET Profile // -// @Summary Get profile page of session user. -// @Tags users -// @Accept json -// @Produce json -// @Param Authorization header string true "JWT Token" -// @Success 200 {object} models.Response{data=models.ProfileResponse} -// @Failure 400 {object} models.Response -// @Failure 401 {object} models.Response -// @Router /profile [get] +// @Description Get profile page of session user. +// @Tags users +// @Accept json +// @Produce json +// @Param Authorization header string true "JWT Token" +// @Success 200 {object} models.Response{data=models.ProfileResponse} +// @Failure 400 {object} models.Response +// @Failure 401 {object} models.Response +// @Router /profile [get] func Profile(c *gin.Context) { // Check if user exists user, exists := c.Get("user") @@ -100,15 +100,15 @@ func Profile(c *gin.Context) { // GET User // -// @Summary Get profile page of another user. -// @Tags users -// @Accept json -// @Produce json -// @Param id path int true "User ID" -// @Success 200 {object} models.Response{data=models.ProfileResponse} -// @Failure 400 {object} models.Response -// @Failure 404 {object} models.Response -// @Router /users/{id} [get] +// @Description Get profile page of another user. +// @Tags users +// @Accept json +// @Produce json +// @Param id path int true "User ID" +// @Success 200 {object} models.Response{data=models.ProfileResponse} +// @Failure 400 {object} models.Response +// @Failure 404 {object} models.Response +// @Router /users/{id} [get] func FetchUser(c *gin.Context) { id := c.Param("id") // Check if id is all numbers and 17 length @@ -202,15 +202,15 @@ func FetchUser(c *gin.Context) { // PUT Profile // -// @Summary Update profile page of session user. -// @Tags users -// @Accept json -// @Produce json -// @Param Authorization header string true "JWT Token" -// @Success 200 {object} models.Response{data=models.ProfileResponse} -// @Failure 400 {object} models.Response -// @Failure 401 {object} models.Response -// @Router /profile [post] +// @Description Update profile page of session user. +// @Tags users +// @Accept json +// @Produce json +// @Param Authorization header string true "JWT Token" +// @Success 200 {object} models.Response{data=models.ProfileResponse} +// @Failure 400 {object} models.Response +// @Failure 401 {object} models.Response +// @Router /profile [post] func UpdateUser(c *gin.Context) { // Check if user exists user, exists := c.Get("user") @@ -245,16 +245,16 @@ func UpdateUser(c *gin.Context) { // PUT Profile/CountryCode // -// @Summary Update country code of session user. -// @Tags users -// @Accept json -// @Produce json -// @Param Authorization header string true "JWT Token" -// @Param country_code query string true "Country Code [XX]" -// @Success 200 {object} models.Response -// @Failure 400 {object} models.Response -// @Failure 401 {object} models.Response -// @Router /profile [put] +// @Description Update country code of session user. +// @Tags users +// @Accept json +// @Produce json +// @Param Authorization header string true "JWT Token" +// @Param country_code query string true "Country Code [XX]" +// @Success 200 {object} models.Response +// @Failure 400 {object} models.Response +// @Failure 401 {object} models.Response +// @Router /profile [put] func UpdateCountryCode(c *gin.Context) { // Check if user exists user, exists := c.Get("user") diff --git a/docs/docs.go b/docs/docs.go index bb14382..57984f4 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -22,13 +22,13 @@ const docTemplate = `{ "paths": { "/chapters/{id}": { "get": { + "description": "Get maps from the specified chapter id.", "produces": [ "application/json" ], "tags": [ "games \u0026 chapters" ], - "summary": "Get maps from the specified chapter id.", "parameters": [ { "type": "integer", @@ -66,45 +66,9 @@ const docTemplate = `{ } } }, - "/demo": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "rankings" - ], - "summary": "Get rankings of every player.", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/models.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/models.RankingsResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/models.Response" - } - } - } - } - }, "/demos": { "get": { + "description": "Get demo with specified demo uuid.", "consumes": [ "application/json" ], @@ -114,10 +78,9 @@ const docTemplate = `{ "tags": [ "demo" ], - "summary": "Get demo with specified demo uuid.", "parameters": [ { - "type": "integer", + "type": "string", "description": "Demo UUID", "name": "uuid", "in": "query", @@ -142,13 +105,13 @@ const docTemplate = `{ }, "/games": { "get": { + "description": "Get games from the leaderboards.", "produces": [ "application/json" ], "tags": [ "games \u0026 chapters" ], - "summary": "Get games from the leaderboards.", "responses": { "200": { "description": "OK", @@ -182,13 +145,13 @@ const docTemplate = `{ }, "/games/{id}": { "get": { + "description": "Get chapters from the specified game id.", "produces": [ "application/json" ], "tags": [ "games \u0026 chapters" ], - "summary": "Get chapters from the specified game id.", "parameters": [ { "type": "integer", @@ -228,6 +191,7 @@ const docTemplate = `{ }, "/login": { "get": { + "description": "Get (redirect) login page for Steam auth.", "consumes": [ "application/json" ], @@ -237,7 +201,6 @@ const docTemplate = `{ "tags": [ "login" ], - "summary": "Get (redirect) login page for Steam auth.", "responses": { "200": { "description": "OK", @@ -268,13 +231,13 @@ const docTemplate = `{ }, "/maps/{id}/leaderboards": { "get": { + "description": "Get map leaderboards with specified id.", "produces": [ "application/json" ], "tags": [ "maps" ], - "summary": "Get map leaderboards with specified id.", "parameters": [ { "type": "integer", @@ -326,6 +289,7 @@ const docTemplate = `{ }, "/maps/{id}/record": { "post": { + "description": "Post record with demo of a specific map.", "consumes": [ "multipart/form-data" ], @@ -335,7 +299,6 @@ const docTemplate = `{ "tags": [ "maps" ], - "summary": "Post record with demo of a specific map.", "parameters": [ { "type": "integer", @@ -401,13 +364,13 @@ const docTemplate = `{ }, "/maps/{id}/summary": { "get": { + "description": "Get map summary with specified id.", "produces": [ "application/json" ], "tags": [ "maps" ], - "summary": "Get map summary with specified id.", "parameters": [ { "type": "integer", @@ -445,13 +408,13 @@ const docTemplate = `{ } }, "put": { + "description": "Edit map summary with specified map id.", "produces": [ "application/json" ], "tags": [ "maps" ], - "summary": "Edit map summary with specified map id.", "parameters": [ { "type": "string", @@ -505,13 +468,13 @@ const docTemplate = `{ } }, "post": { + "description": "Create map summary with specified map id.", "produces": [ "application/json" ], "tags": [ "maps" ], - "summary": "Create map summary with specified map id.", "parameters": [ { "type": "string", @@ -565,13 +528,13 @@ const docTemplate = `{ } }, "delete": { + "description": "Delete map summary with specified map id.", "produces": [ "application/json" ], "tags": [ "maps" ], - "summary": "Delete map summary with specified map id.", "parameters": [ { "type": "string", @@ -627,6 +590,7 @@ const docTemplate = `{ }, "/profile": { "get": { + "description": "Get profile page of session user.", "consumes": [ "application/json" ], @@ -636,7 +600,6 @@ const docTemplate = `{ "tags": [ "users" ], - "summary": "Get profile page of session user.", "parameters": [ { "type": "string", @@ -680,6 +643,7 @@ const docTemplate = `{ } }, "put": { + "description": "Update country code of session user.", "consumes": [ "application/json" ], @@ -689,7 +653,6 @@ const docTemplate = `{ "tags": [ "users" ], - "summary": "Update country code of session user.", "parameters": [ { "type": "string", @@ -728,6 +691,7 @@ const docTemplate = `{ } }, "post": { + "description": "Update profile page of session user.", "consumes": [ "application/json" ], @@ -737,7 +701,6 @@ const docTemplate = `{ "tags": [ "users" ], - "summary": "Update profile page of session user.", "parameters": [ { "type": "string", @@ -781,15 +744,52 @@ const docTemplate = `{ } } }, + "/rankings": { + "get": { + "description": "Get rankings of every player.", + "produces": [ + "application/json" + ], + "tags": [ + "rankings" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/models.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.RankingsResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Response" + } + } + } + } + }, "/search": { "get": { + "description": "Get all user and map data matching to the query.", "produces": [ "application/json" ], "tags": [ "search" ], - "summary": "Get all user and map data matching to the query.", "parameters": [ { "type": "string", @@ -828,13 +828,13 @@ const docTemplate = `{ }, "/token": { "get": { + "description": "Gets the token cookie value from the user.", "produces": [ "application/json" ], "tags": [ "auth" ], - "summary": "Gets the token cookie value from the user.", "responses": { "200": { "description": "OK", @@ -863,13 +863,13 @@ const docTemplate = `{ } }, "delete": { + "description": "Deletes the token cookie from the user.", "produces": [ "application/json" ], "tags": [ "auth" ], - "summary": "Deletes the token cookie from the user.", "responses": { "200": { "description": "OK", @@ -900,6 +900,7 @@ const docTemplate = `{ }, "/users/{id}": { "get": { + "description": "Get profile page of another user.", "consumes": [ "application/json" ], @@ -909,7 +910,6 @@ const docTemplate = `{ "tags": [ "users" ], - "summary": "Get profile page of another user.", "parameters": [ { "type": "integer", diff --git a/docs/swagger.json b/docs/swagger.json index 3530d2c..ef422ab 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -15,13 +15,13 @@ "paths": { "/chapters/{id}": { "get": { + "description": "Get maps from the specified chapter id.", "produces": [ "application/json" ], "tags": [ "games \u0026 chapters" ], - "summary": "Get maps from the specified chapter id.", "parameters": [ { "type": "integer", @@ -59,45 +59,9 @@ } } }, - "/demo": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "rankings" - ], - "summary": "Get rankings of every player.", - "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/models.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/models.RankingsResponse" - } - } - } - ] - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/models.Response" - } - } - } - } - }, "/demos": { "get": { + "description": "Get demo with specified demo uuid.", "consumes": [ "application/json" ], @@ -107,10 +71,9 @@ "tags": [ "demo" ], - "summary": "Get demo with specified demo uuid.", "parameters": [ { - "type": "integer", + "type": "string", "description": "Demo UUID", "name": "uuid", "in": "query", @@ -135,13 +98,13 @@ }, "/games": { "get": { + "description": "Get games from the leaderboards.", "produces": [ "application/json" ], "tags": [ "games \u0026 chapters" ], - "summary": "Get games from the leaderboards.", "responses": { "200": { "description": "OK", @@ -175,13 +138,13 @@ }, "/games/{id}": { "get": { + "description": "Get chapters from the specified game id.", "produces": [ "application/json" ], "tags": [ "games \u0026 chapters" ], - "summary": "Get chapters from the specified game id.", "parameters": [ { "type": "integer", @@ -221,6 +184,7 @@ }, "/login": { "get": { + "description": "Get (redirect) login page for Steam auth.", "consumes": [ "application/json" ], @@ -230,7 +194,6 @@ "tags": [ "login" ], - "summary": "Get (redirect) login page for Steam auth.", "responses": { "200": { "description": "OK", @@ -261,13 +224,13 @@ }, "/maps/{id}/leaderboards": { "get": { + "description": "Get map leaderboards with specified id.", "produces": [ "application/json" ], "tags": [ "maps" ], - "summary": "Get map leaderboards with specified id.", "parameters": [ { "type": "integer", @@ -319,6 +282,7 @@ }, "/maps/{id}/record": { "post": { + "description": "Post record with demo of a specific map.", "consumes": [ "multipart/form-data" ], @@ -328,7 +292,6 @@ "tags": [ "maps" ], - "summary": "Post record with demo of a specific map.", "parameters": [ { "type": "integer", @@ -394,13 +357,13 @@ }, "/maps/{id}/summary": { "get": { + "description": "Get map summary with specified id.", "produces": [ "application/json" ], "tags": [ "maps" ], - "summary": "Get map summary with specified id.", "parameters": [ { "type": "integer", @@ -438,13 +401,13 @@ } }, "put": { + "description": "Edit map summary with specified map id.", "produces": [ "application/json" ], "tags": [ "maps" ], - "summary": "Edit map summary with specified map id.", "parameters": [ { "type": "string", @@ -498,13 +461,13 @@ } }, "post": { + "description": "Create map summary with specified map id.", "produces": [ "application/json" ], "tags": [ "maps" ], - "summary": "Create map summary with specified map id.", "parameters": [ { "type": "string", @@ -558,13 +521,13 @@ } }, "delete": { + "description": "Delete map summary with specified map id.", "produces": [ "application/json" ], "tags": [ "maps" ], - "summary": "Delete map summary with specified map id.", "parameters": [ { "type": "string", @@ -620,6 +583,7 @@ }, "/profile": { "get": { + "description": "Get profile page of session user.", "consumes": [ "application/json" ], @@ -629,7 +593,6 @@ "tags": [ "users" ], - "summary": "Get profile page of session user.", "parameters": [ { "type": "string", @@ -673,6 +636,7 @@ } }, "put": { + "description": "Update country code of session user.", "consumes": [ "application/json" ], @@ -682,7 +646,6 @@ "tags": [ "users" ], - "summary": "Update country code of session user.", "parameters": [ { "type": "string", @@ -721,6 +684,7 @@ } }, "post": { + "description": "Update profile page of session user.", "consumes": [ "application/json" ], @@ -730,7 +694,6 @@ "tags": [ "users" ], - "summary": "Update profile page of session user.", "parameters": [ { "type": "string", @@ -774,15 +737,52 @@ } } }, + "/rankings": { + "get": { + "description": "Get rankings of every player.", + "produces": [ + "application/json" + ], + "tags": [ + "rankings" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/models.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.RankingsResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Response" + } + } + } + } + }, "/search": { "get": { + "description": "Get all user and map data matching to the query.", "produces": [ "application/json" ], "tags": [ "search" ], - "summary": "Get all user and map data matching to the query.", "parameters": [ { "type": "string", @@ -821,13 +821,13 @@ }, "/token": { "get": { + "description": "Gets the token cookie value from the user.", "produces": [ "application/json" ], "tags": [ "auth" ], - "summary": "Gets the token cookie value from the user.", "responses": { "200": { "description": "OK", @@ -856,13 +856,13 @@ } }, "delete": { + "description": "Deletes the token cookie from the user.", "produces": [ "application/json" ], "tags": [ "auth" ], - "summary": "Deletes the token cookie from the user.", "responses": { "200": { "description": "OK", @@ -893,6 +893,7 @@ }, "/users/{id}": { "get": { + "description": "Get profile page of another user.", "consumes": [ "application/json" ], @@ -902,7 +903,6 @@ "tags": [ "users" ], - "summary": "Get profile page of another user.", "parameters": [ { "type": "integer", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index d4420e2..2fed413 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -242,6 +242,7 @@ info: paths: /chapters/{id}: get: + description: Get maps from the specified chapter id. parameters: - description: Chapter ID in: path @@ -264,40 +265,19 @@ paths: description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Get maps from the specified chapter id. tags: - games & chapters - /demo: - get: - produces: - - application/json - responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/models.Response' - - properties: - data: - $ref: '#/definitions/models.RankingsResponse' - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/models.Response' - summary: Get rankings of every player. - tags: - - rankings /demos: get: consumes: - application/json + description: Get demo with specified demo uuid. parameters: - description: Demo UUID in: query name: uuid required: true - type: integer + type: string produces: - application/octet-stream responses: @@ -309,11 +289,11 @@ paths: description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Get demo with specified demo uuid. tags: - demo /games: get: + description: Get games from the leaderboards. produces: - application/json responses: @@ -332,11 +312,11 @@ paths: description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Get games from the leaderboards. tags: - games & chapters /games/{id}: get: + description: Get chapters from the specified game id. parameters: - description: Game ID in: path @@ -359,13 +339,13 @@ paths: description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Get chapters from the specified game id. tags: - games & chapters /login: get: consumes: - application/json + description: Get (redirect) login page for Steam auth. produces: - application/json responses: @@ -382,11 +362,11 @@ paths: description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Get (redirect) login page for Steam auth. tags: - login /maps/{id}/leaderboards: get: + description: Get map leaderboards with specified id. parameters: - description: Map ID in: path @@ -414,13 +394,13 @@ paths: description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Get map leaderboards with specified id. tags: - maps /maps/{id}/record: post: consumes: - multipart/form-data + description: Post record with demo of a specific map. parameters: - description: Map ID in: path @@ -464,11 +444,11 @@ paths: description: Unauthorized schema: $ref: '#/definitions/models.Response' - summary: Post record with demo of a specific map. tags: - maps /maps/{id}/summary: delete: + description: Delete map summary with specified map id. parameters: - description: JWT Token in: header @@ -502,10 +482,10 @@ paths: description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Delete map summary with specified map id. tags: - maps get: + description: Get map summary with specified id. parameters: - description: Map ID in: path @@ -528,10 +508,10 @@ paths: description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Get map summary with specified id. tags: - maps post: + description: Create map summary with specified map id. parameters: - description: JWT Token in: header @@ -565,10 +545,10 @@ paths: description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Create map summary with specified map id. tags: - maps put: + description: Edit map summary with specified map id. parameters: - description: JWT Token in: header @@ -602,13 +582,13 @@ paths: description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Edit map summary with specified map id. tags: - maps /profile: get: consumes: - application/json + description: Get profile page of session user. parameters: - description: JWT Token in: header @@ -635,12 +615,12 @@ paths: description: Unauthorized schema: $ref: '#/definitions/models.Response' - summary: Get profile page of session user. tags: - users post: consumes: - application/json + description: Update profile page of session user. parameters: - description: JWT Token in: header @@ -667,12 +647,12 @@ paths: description: Unauthorized schema: $ref: '#/definitions/models.Response' - summary: Update profile page of session user. tags: - users put: consumes: - application/json + description: Update country code of session user. parameters: - description: JWT Token in: header @@ -699,11 +679,32 @@ paths: description: Unauthorized schema: $ref: '#/definitions/models.Response' - summary: Update country code of session user. tags: - users + /rankings: + get: + description: Get rankings of every player. + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/models.Response' + - properties: + data: + $ref: '#/definitions/models.RankingsResponse' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Response' + tags: + - rankings /search: get: + description: Get all user and map data matching to the query. parameters: - description: Search user or map name. in: query @@ -725,11 +726,11 @@ paths: description: Bad Request schema: $ref: '#/definitions/models.Response' - summary: Get all user and map data matching to the query. tags: - search /token: delete: + description: Deletes the token cookie from the user. produces: - application/json responses: @@ -746,10 +747,10 @@ paths: description: Not Found schema: $ref: '#/definitions/models.Response' - summary: Deletes the token cookie from the user. tags: - auth get: + description: Gets the token cookie value from the user. produces: - application/json responses: @@ -766,13 +767,13 @@ paths: description: Not Found schema: $ref: '#/definitions/models.Response' - summary: Gets the token cookie value from the user. tags: - auth /users/{id}: get: consumes: - application/json + description: Get profile page of another user. parameters: - description: User ID in: path @@ -799,7 +800,6 @@ paths: description: Not Found schema: $ref: '#/definitions/models.Response' - summary: Get profile page of another user. tags: - users swagger: "2.0" -- cgit v1.2.3 From b1e2389ff27a16f25ffa3b74d2c68442f0dda12b Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 23:20:33 +0300 Subject: feat: display score count and time in record response (#42) Former-commit-id: f123f9383519780a6e6d0f6828332635b93bad82 --- backend/controllers/recordController.go | 8 ++++---- backend/models/responses.go | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index af8eb63..d1404f4 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go @@ -2,7 +2,7 @@ package controllers import ( "context" - b64 "encoding/base64" + "encoding/base64" "io" "log" "mime/multipart" @@ -31,7 +31,7 @@ import ( // @Param partner_demo formData file false "Partner Demo" // @Param is_partner_orange formData boolean false "Is Partner Orange" // @Param partner_id formData string false "Partner ID" -// @Success 200 {object} models.Response +// @Success 200 {object} models.Response{data=models.RecordResponse} // @Failure 400 {object} models.Response // @Failure 401 {object} models.Response // @Router /maps/{id}/record [post] @@ -183,7 +183,7 @@ func CreateRecordWithDemo(c *gin.Context) { c.JSON(http.StatusOK, models.Response{ Success: true, Message: "Successfully created record.", - Data: nil, + Data: models.RecordResponse{ScoreCount: hostDemoScoreCount, ScoreTime: hostDemoScoreTime}, }) } @@ -240,7 +240,7 @@ func DownloadDemoWithID(c *gin.Context) { // Use Service account func serviceAccount() *http.Client { - privateKey, _ := b64.StdEncoding.DecodeString(os.Getenv("GOOGLE_PRIVATE_KEY_BASE64")) + privateKey, _ := base64.StdEncoding.DecodeString(os.Getenv("GOOGLE_PRIVATE_KEY_BASE64")) config := &jwt.Config{ Email: os.Getenv("GOOGLE_CLIENT_EMAIL"), PrivateKey: []byte(privateKey), diff --git a/backend/models/responses.go b/backend/models/responses.go index dc554ff..459911c 100644 --- a/backend/models/responses.go +++ b/backend/models/responses.go @@ -50,6 +50,11 @@ type ChapterMapsResponse struct { Maps []MapShort `json:"maps"` } +type RecordResponse struct { + ScoreCount int `json:"score_count"` + ScoreTime int `json:"score_time"` +} + func ErrorResponse(message string) Response { return Response{ Success: false, -- cgit v1.2.3 From 47c38325c77204cc8c98c347e5f8fd62b6f33596 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 3 Jul 2023 23:23:19 +0300 Subject: feat: add chapter numbers in names Former-commit-id: 237a4cf44c7930145a8829b640d7f2c95e370860 --- backend/database/chapters.sql | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/backend/database/chapters.sql b/backend/database/chapters.sql index f01ae9c..9538bed 100644 --- a/backend/database/chapters.sql +++ b/backend/database/chapters.sql @@ -1,17 +1,17 @@ INSERT INTO chapters(id, game_id, name) VALUES -(1, 1, 'The Coutesy Call'), -(2, 1, 'The Cold Boot'), -(3, 1, 'The Return'), -(4, 1, 'The Surprise'), -(5, 1, 'The Escape'), -(6, 1, 'The Fall'), -(7, 1, 'The Reunion'), -(8, 1, 'The Itch'), -(9, 1, 'The Part Where He Kills You'), -(10, 2, 'Introduction'), -(11, 2, 'Team Building'), -(12, 2, 'Mass And Velocity'), -(13, 2, 'Hard-Light Surfaces'), -(14, 2, 'Excursion Funnels'), -(15, 2, 'Mobility Gels'), -(16, 2, 'Art Therapy'); \ No newline at end of file +(1, 1, 'Chapter 1 - The Coutesy Call'), +(2, 1, 'Chapter 2 - The Cold Boot'), +(3, 1, 'Chapter 3 - The Return'), +(4, 1, 'Chapter 4 - The Surprise'), +(5, 1, 'Chapter 5 - The Escape'), +(6, 1, 'Chapter 6 - The Fall'), +(7, 1, 'Chapter 7 - The Reunion'), +(8, 1, 'Chapter 8 - The Itch'), +(9, 1, 'Chapter 9 - The Part Where He Kills You'), +(10, 2, 'Course 0 - Introduction'), +(11, 2, 'Course 1 - Team Building'), +(12, 2, 'Course 2 - Mass And Velocity'), +(13, 2, 'Course 3 - Hard-Light Surfaces'), +(14, 2, 'Course 4 - Excursion Funnels'), +(15, 2, 'Course 5 - Mobility Gels'), +(16, 2, 'Course 6 - Art Therapy'); \ No newline at end of file -- cgit v1.2.3 From 3ad53aeeffec00f85bf1f3168d0e9d61c65eecb7 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 10 Jul 2023 16:06:29 +0300 Subject: feat: is_coop field on games Former-commit-id: bf3a5a262557ade646ce8e59db152be8c521ac06 --- backend/controllers/mapController.go | 4 ++-- backend/database/games.sql | 6 +++--- backend/database/init.sql | 1 + backend/models/models.go | 5 +++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/backend/controllers/mapController.go b/backend/controllers/mapController.go index 52b6623..11e56f6 100644 --- a/backend/controllers/mapController.go +++ b/backend/controllers/mapController.go @@ -193,7 +193,7 @@ func FetchMapLeaderboards(c *gin.Context) { // @Failure 400 {object} models.Response // @Router /games [get] func FetchGames(c *gin.Context) { - rows, err := database.DB.Query(`SELECT id, name FROM games`) + rows, err := database.DB.Query(`SELECT id, name, is_coop FROM games`) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return @@ -201,7 +201,7 @@ func FetchGames(c *gin.Context) { var games []models.Game for rows.Next() { var game models.Game - if err := rows.Scan(&game.ID, &game.Name); err != nil { + if err := rows.Scan(&game.ID, &game.Name, &game.IsCoop); err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } diff --git a/backend/database/games.sql b/backend/database/games.sql index 5e2f4ee..0c2374c 100644 --- a/backend/database/games.sql +++ b/backend/database/games.sql @@ -1,3 +1,3 @@ -INSERT INTO games(id, name) VALUES -(1, 'Portal 2 - Singleplayer'), -(2, 'Portal 2 - Cooperative'); \ No newline at end of file +INSERT INTO games(id, name, is_coop) VALUES +(1, 'Portal 2 - Singleplayer', false), +(2, 'Portal 2 - Cooperative', true); \ No newline at end of file diff --git a/backend/database/init.sql b/backend/database/init.sql index 76c3aa6..c7d098c 100644 --- a/backend/database/init.sql +++ b/backend/database/init.sql @@ -11,6 +11,7 @@ CREATE TABLE users ( CREATE TABLE games ( id SMALLSERIAL, name TEXT NOT NULL, + is_coop BOOLEAN NOT NULL, PRIMARY KEY (id) ); diff --git a/backend/models/models.go b/backend/models/models.go index 2524935..e63ff91 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -62,8 +62,9 @@ type UserRanking struct { } type Game struct { - ID int `json:"id"` - Name string `json:"name"` + ID int `json:"id"` + Name string `json:"name"` + IsCoop bool `json:"is_coop"` } type Chapter struct { -- cgit v1.2.3 From a8023ad4949649623c6c2bbe694ce6fc2a26e4dc Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Mon, 10 Jul 2023 20:35:47 +0300 Subject: feat: is_coop field on map summary Former-commit-id: f780291d97e9df9f0d0743b06cedae7345308887 --- backend/controllers/mapController.go | 4 ++-- backend/models/models.go | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/controllers/mapController.go b/backend/controllers/mapController.go index 11e56f6..e7c5566 100644 --- a/backend/controllers/mapController.go +++ b/backend/controllers/mapController.go @@ -28,12 +28,12 @@ func FetchMapSummary(c *gin.Context) { } // Get map data response.Map.ID = intID - sql := `SELECT m.id, g.name, c.name, m.name, m.image + sql := `SELECT m.id, g.name, c.name, m.name, m.image, g.is_coop FROM maps m INNER JOIN games g ON m.game_id = g.id INNER JOIN chapters c ON m.chapter_id = c.id WHERE m.id = $1` - err = database.DB.QueryRow(sql, id).Scan(&response.Map.ID, &response.Map.GameName, &response.Map.ChapterName, &response.Map.MapName, &response.Map.Image) + err = database.DB.QueryRow(sql, id).Scan(&response.Map.ID, &response.Map.GameName, &response.Map.ChapterName, &response.Map.MapName, &response.Map.Image, &response.Map.IsCoop) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return diff --git a/backend/models/models.go b/backend/models/models.go index e63ff91..1231cb1 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -25,6 +25,7 @@ type Map struct { ChapterName string `json:"chapter_name"` MapName string `json:"map_name"` Image string `json:"image"` + IsCoop bool `json:"is_coop"` } type MapShort struct { -- cgit v1.2.3 From cae45e130891cf97f93d743ace381eb8209df2a8 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 12 Jul 2023 09:56:11 +0300 Subject: fix: unique relation for map history and routes Former-commit-id: f3ea2ce6784973385d63980b6680294bbcee0188 --- backend/database/history.sql | 2 +- backend/database/init.sql | 6 ++++-- backend/database/route.sql | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/database/history.sql b/backend/database/history.sql index d7c4999..320d72f 100644 --- a/backend/database/history.sql +++ b/backend/database/history.sql @@ -27,7 +27,7 @@ INSERT INTO map_history(map_id,category_id,user_name,score_count,record_date) VA (15,1,'Tyronis',2,'2011-10-05 00:00:00'), (16,1,'LookLikeAKango',2,'2011-10-05 00:00:00'), (16,1,'jyjey',0,'2012-08-25 00:00:00'), -(17,1,'rocoty',0,'2011-10-05 00:00:00'), +(17,1,'rocoty',2,'2011-10-05 00:00:00'), (17,1,'Nidboj132',0,'2023-02-05 00:00:00'), -- 3 (18,1,'The Last Tofus',5,'2011-05-08 00:00:00'), diff --git a/backend/database/init.sql b/backend/database/init.sql index c7d098c..196cb71 100644 --- a/backend/database/init.sql +++ b/backend/database/init.sql @@ -49,7 +49,8 @@ CREATE TABLE map_routes ( showcase TEXT NOT NULL, PRIMARY KEY (id), FOREIGN KEY (map_id) REFERENCES maps(id), - FOREIGN KEY (category_id) REFERENCES categories(id) + FOREIGN KEY (category_id) REFERENCES categories(id), + UNIQUE (map_id, category_id, score_count) ); CREATE TABLE map_history ( @@ -61,7 +62,8 @@ CREATE TABLE map_history ( record_date TIMESTAMP NOT NULL, PRIMARY KEY (id), FOREIGN KEY (category_id) REFERENCES categories(id), - FOREIGN KEY (map_id) REFERENCES maps(id) + FOREIGN KEY (map_id) REFERENCES maps(id), + UNIQUE (map_id, category_id, score_count) ); CREATE TABLE map_ratings ( diff --git a/backend/database/route.sql b/backend/database/route.sql index 6f090c7..f26180e 100644 --- a/backend/database/route.sql +++ b/backend/database/route.sql @@ -27,7 +27,7 @@ INSERT INTO map_routes(map_id,category_id,score_count,description,showcase) VALU (15,1,2,'',''), (16,1,2,'',''), (16,1,0,'',''), -(17,1,0,'',''), +(17,1,2,'',''), (17,1,0,'',''), -- 3 (18,1,5,'',''), -- cgit v1.2.3 From 779c99ea5425f68d62f53b00a9f1e9920573aea1 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 12 Jul 2023 12:08:59 +0300 Subject: feat: delete map summary route Former-commit-id: 2f2f884426ff0be75d4ec1b1220edb05b20ba9d8 --- backend/routes/routes.go | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/routes/routes.go b/backend/routes/routes.go index feb49f3..a39c8c4 100644 --- a/backend/routes/routes.go +++ b/backend/routes/routes.go @@ -28,6 +28,7 @@ func InitRoutes(router *gin.Engine) { v1.GET("/maps/:id/summary", controllers.FetchMapSummary) v1.POST("/maps/:id/summary", middleware.CheckAuth, controllers.CreateMapSummary) v1.PUT("/maps/:id/summary", middleware.CheckAuth, controllers.EditMapSummary) + v1.DELETE("/maps/:id/summary", middleware.CheckAuth, controllers.DeleteMapSummary) v1.GET("/maps/:id/leaderboards", controllers.FetchMapLeaderboards) v1.POST("/maps/:id/record", middleware.CheckAuth, controllers.CreateRecordWithDemo) v1.GET("/rankings", controllers.Rankings) -- cgit v1.2.3 From 56ca08d83dce247073ddc90be1f01e8a57ddea5e Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 12 Jul 2023 12:09:39 +0300 Subject: fix: sql typo Former-commit-id: a86ddf35ba75a5940575df760ee705473542fd38 --- backend/controllers/modController.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index d6dc4d4..ff79e3e 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -144,10 +144,7 @@ func EditMapSummary(c *gin.Context) { defer tx.Rollback() // Fetch route category and score count var categoryID, scoreCount int - sql := `SELECT mr.category_id, mr.score_count - FROM map_routes mr - INNER JOIN maps m - WHERE m.id = $1 AND mr.id = $2` + sql := `SELECT mr.category_id, mr.score_count FROM map_routes mr INNER JOIN maps m ON m.id = mr.map_id WHERE m.id = $1 AND mr.id = $2` err = database.DB.QueryRow(sql, mapID, request.RouteID).Scan(&categoryID, &scoreCount) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) -- cgit v1.2.3 From 7c9ce0f6bc22dce98216a959ec11182e7af9e790 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 12 Jul 2023 12:11:56 +0300 Subject: fix: turn back to serials Former-commit-id: 9c83d4ac7297c7306f4872d76334507fa09cbde9 --- backend/database/init.sql | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/database/init.sql b/backend/database/init.sql index 196cb71..50e7c15 100644 --- a/backend/database/init.sql +++ b/backend/database/init.sql @@ -9,14 +9,14 @@ CREATE TABLE users ( ); CREATE TABLE games ( - id SMALLSERIAL, + id SERIAL, name TEXT NOT NULL, is_coop BOOLEAN NOT NULL, PRIMARY KEY (id) ); CREATE TABLE chapters ( - id SMALLSERIAL, + id SERIAL, game_id SMALLINT NOT NULL, name TEXT NOT NULL, PRIMARY KEY (id), @@ -24,13 +24,13 @@ CREATE TABLE chapters ( ); CREATE TABLE categories ( - id SMALLSERIAL, + id SERIAL, name TEXT NOT NULL, PRIMARY KEY (id) ); CREATE TABLE maps ( - id SMALLSERIAL, + id SERIAL, game_id SMALLINT NOT NULL, chapter_id SMALLINT NOT NULL, name TEXT NOT NULL, @@ -41,7 +41,7 @@ CREATE TABLE maps ( ); CREATE TABLE map_routes ( - id SMALLSERIAL, + id SERIAL, map_id SMALLINT NOT NULL, category_id SMALLINT NOT NULL, score_count SMALLINT NOT NULL, @@ -54,7 +54,7 @@ CREATE TABLE map_routes ( ); CREATE TABLE map_history ( - id SMALLSERIAL, + id SERIAL, map_id SMALLINT NOT NULL, category_id SMALLINT NOT NULL, user_name TEXT NOT NULL, -- cgit v1.2.3 From 311a039edc8f91666c8d05acd94aabab20833ffe Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 12 Jul 2023 14:26:24 +0300 Subject: feat: edit map image Former-commit-id: 288e6772dda69a9543a01db85069c95476addd4e --- backend/controllers/modController.go | 55 ++++++++++++++++++ backend/models/requests.go | 4 ++ backend/routes/routes.go | 1 + docs/docs.go | 104 ++++++++++++++++++++++++++++++++++- docs/swagger.json | 104 ++++++++++++++++++++++++++++++++++- docs/swagger.yaml | 63 ++++++++++++++++++++- 6 files changed, 328 insertions(+), 3 deletions(-) diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index ff79e3e..98fb165 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -264,3 +264,58 @@ func DeleteMapSummary(c *gin.Context) { Data: request, }) } + +// PUT Map Image +// +// @Description Edit map image with specified map id. +// @Tags maps +// @Produce json +// @Param Authorization header string true "JWT Token" +// @Param id path int true "Map ID" +// @Param request body models.EditMapImageRequest true "Body" +// @Success 200 {object} models.Response{data=models.EditMapImageRequest} +// @Failure 400 {object} models.Response +// @Router /maps/{id}/image [put] +func EditMapImage(c *gin.Context) { + // Check if user exists + user, exists := c.Get("user") + if !exists { + c.JSON(http.StatusUnauthorized, models.ErrorResponse("User not logged in.")) + return + } + var moderator bool + for _, title := range user.(models.User).Titles { + if title == "Moderator" { + moderator = true + } + } + if !moderator { + c.JSON(http.StatusUnauthorized, models.ErrorResponse("Insufficient permissions.")) + return + } + // Bind parameter and body + id := c.Param("id") + mapID, err := strconv.Atoi(id) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + var request models.EditMapImageRequest + if err := c.BindJSON(&request); err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + // Update database with new data + sql := `UPDATE maps SET image = $2 WHERE id = $1` + _, err = database.DB.Exec(sql, mapID, request.Image) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } + // Return response + c.JSON(http.StatusOK, models.Response{ + Success: true, + Message: "Successfully updated map image.", + Data: request, + }) +} diff --git a/backend/models/requests.go b/backend/models/requests.go index f275203..fac05b6 100644 --- a/backend/models/requests.go +++ b/backend/models/requests.go @@ -27,6 +27,10 @@ type DeleteMapSummaryRequest struct { RouteID int `json:"route_id" binding:"required"` } +type EditMapImageRequest struct { + Image string `json:"image" binding:"required"` +} + type RecordRequest struct { HostDemo *multipart.FileHeader `json:"host_demo" form:"host_demo" binding:"required" swaggerignore:"true"` PartnerDemo *multipart.FileHeader `json:"partner_demo" form:"partner_demo" swaggerignore:"true"` diff --git a/backend/routes/routes.go b/backend/routes/routes.go index a39c8c4..1377d32 100644 --- a/backend/routes/routes.go +++ b/backend/routes/routes.go @@ -29,6 +29,7 @@ func InitRoutes(router *gin.Engine) { v1.POST("/maps/:id/summary", middleware.CheckAuth, controllers.CreateMapSummary) v1.PUT("/maps/:id/summary", middleware.CheckAuth, controllers.EditMapSummary) v1.DELETE("/maps/:id/summary", middleware.CheckAuth, controllers.DeleteMapSummary) + v1.PUT("/maps/:id/image", middleware.CheckAuth, controllers.EditMapImage) v1.GET("/maps/:id/leaderboards", controllers.FetchMapLeaderboards) v1.POST("/maps/:id/record", middleware.CheckAuth, controllers.CreateRecordWithDemo) v1.GET("/rankings", controllers.Rankings) diff --git a/docs/docs.go b/docs/docs.go index 57984f4..13d4cf6 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -229,6 +229,68 @@ const docTemplate = `{ } } }, + "/maps/{id}/image": { + "put": { + "description": "Edit map image with specified map id.", + "produces": [ + "application/json" + ], + "tags": [ + "maps" + ], + "parameters": [ + { + "type": "string", + "description": "JWT Token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.EditMapImageRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/models.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.EditMapImageRequest" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Response" + } + } + } + } + }, "/maps/{id}/leaderboards": { "get": { "description": "Get map leaderboards with specified id.", @@ -344,7 +406,19 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.Response" + "allOf": [ + { + "$ref": "#/definitions/models.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.RecordResponse" + } + } + } + ] } }, "400": { @@ -1047,6 +1121,17 @@ const docTemplate = `{ } } }, + "models.EditMapImageRequest": { + "type": "object", + "required": [ + "image" + ], + "properties": { + "image": { + "type": "string" + } + } + }, "models.EditMapSummaryRequest": { "type": "object", "required": [ @@ -1084,6 +1169,9 @@ const docTemplate = `{ "id": { "type": "integer" }, + "is_coop": { + "type": "boolean" + }, "name": { "type": "string" } @@ -1112,6 +1200,9 @@ const docTemplate = `{ "image": { "type": "string" }, + "is_coop": { + "type": "boolean" + }, "map_name": { "type": "string" } @@ -1242,6 +1333,17 @@ const docTemplate = `{ } } }, + "models.RecordResponse": { + "type": "object", + "properties": { + "score_count": { + "type": "integer" + }, + "score_time": { + "type": "integer" + } + } + }, "models.Response": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index ef422ab..f644288 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -222,6 +222,68 @@ } } }, + "/maps/{id}/image": { + "put": { + "description": "Edit map image with specified map id.", + "produces": [ + "application/json" + ], + "tags": [ + "maps" + ], + "parameters": [ + { + "type": "string", + "description": "JWT Token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "integer", + "description": "Map ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.EditMapImageRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/models.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.EditMapImageRequest" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.Response" + } + } + } + } + }, "/maps/{id}/leaderboards": { "get": { "description": "Get map leaderboards with specified id.", @@ -337,7 +399,19 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/models.Response" + "allOf": [ + { + "$ref": "#/definitions/models.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/models.RecordResponse" + } + } + } + ] } }, "400": { @@ -1040,6 +1114,17 @@ } } }, + "models.EditMapImageRequest": { + "type": "object", + "required": [ + "image" + ], + "properties": { + "image": { + "type": "string" + } + } + }, "models.EditMapSummaryRequest": { "type": "object", "required": [ @@ -1077,6 +1162,9 @@ "id": { "type": "integer" }, + "is_coop": { + "type": "boolean" + }, "name": { "type": "string" } @@ -1105,6 +1193,9 @@ "image": { "type": "string" }, + "is_coop": { + "type": "boolean" + }, "map_name": { "type": "string" } @@ -1235,6 +1326,17 @@ } } }, + "models.RecordResponse": { + "type": "object", + "properties": { + "score_count": { + "type": "integer" + }, + "score_time": { + "type": "integer" + } + } + }, "models.Response": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 2fed413..3b706ea 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -61,6 +61,13 @@ definitions: required: - route_id type: object + models.EditMapImageRequest: + properties: + image: + type: string + required: + - image + type: object models.EditMapSummaryRequest: properties: description: @@ -87,6 +94,8 @@ definitions: properties: id: type: integer + is_coop: + type: boolean name: type: string type: object @@ -105,6 +114,8 @@ definitions: type: integer image: type: string + is_coop: + type: boolean map_name: type: string type: object @@ -189,6 +200,13 @@ definitions: $ref: '#/definitions/models.UserRanking' type: array type: object + models.RecordResponse: + properties: + score_count: + type: integer + score_time: + type: integer + type: object models.Response: properties: data: {} @@ -364,6 +382,44 @@ paths: $ref: '#/definitions/models.Response' tags: - login + /maps/{id}/image: + put: + description: Edit map image with specified map id. + parameters: + - description: JWT Token + in: header + name: Authorization + required: true + type: string + - description: Map ID + in: path + name: id + required: true + type: integer + - description: Body + in: body + name: request + required: true + schema: + $ref: '#/definitions/models.EditMapImageRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/models.Response' + - properties: + data: + $ref: '#/definitions/models.EditMapImageRequest' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.Response' + tags: + - maps /maps/{id}/leaderboards: get: description: Get map leaderboards with specified id. @@ -435,7 +491,12 @@ paths: "200": description: OK schema: - $ref: '#/definitions/models.Response' + allOf: + - $ref: '#/definitions/models.Response' + - properties: + data: + $ref: '#/definitions/models.RecordResponse' + type: object "400": description: Bad Request schema: -- cgit v1.2.3 From a337af807c13d5923e71eb23b4d83e6505a7f44d Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 12 Jul 2023 18:27:31 +0300 Subject: fix: logic error on editing map summary (#48) Former-commit-id: 406a95993a6210ed30f78ad3ea562b2f23e3310a --- backend/controllers/modController.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index 98fb165..72f1a53 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -143,13 +143,19 @@ func EditMapSummary(c *gin.Context) { } defer tx.Rollback() // Fetch route category and score count - var categoryID, scoreCount int + var categoryID, scoreCount, historyID int sql := `SELECT mr.category_id, mr.score_count FROM map_routes mr INNER JOIN maps m ON m.id = mr.map_id WHERE m.id = $1 AND mr.id = $2` err = database.DB.QueryRow(sql, mapID, request.RouteID).Scan(&categoryID, &scoreCount) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } + sql = `SELECT mh.id FROM map_history mh WHERE mh.score_count = $1 AND mh.category_id = $2 AND mh.map_id = $3` + err = database.DB.QueryRow(sql, mapID, request.RouteID).Scan(&historyID) + if err != nil { + c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) + return + } // Update database with new data sql = `UPDATE map_routes SET score_count = $2, description = $3, showcase = $4 WHERE id = $1` _, err = tx.Exec(sql, request.RouteID, request.ScoreCount, request.Description, request.Showcase) @@ -157,8 +163,8 @@ func EditMapSummary(c *gin.Context) { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return } - sql = `UPDATE map_history SET user_name = $3, score_count = $4, record_date = $5 WHERE map_id = $1 AND category_id = $2` - _, err = tx.Exec(sql, mapID, categoryID, request.UserName, request.ScoreCount, request.RecordDate) + sql = `UPDATE map_history SET user_name = $2, score_count = $3, record_date = $4 WHERE id = $1` + _, err = tx.Exec(sql, historyID, request.UserName, request.ScoreCount, request.RecordDate) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return -- cgit v1.2.3 From 75a6de9ac25fba229e5daa391222edc495c0ecff Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 12 Jul 2023 18:29:04 +0300 Subject: fix: sql typo for edit map summary (#48) Former-commit-id: 79b149b959b8cad999269e16834443c7de295079 --- backend/controllers/modController.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go index 72f1a53..07edff5 100644 --- a/backend/controllers/modController.go +++ b/backend/controllers/modController.go @@ -151,7 +151,7 @@ func EditMapSummary(c *gin.Context) { return } sql = `SELECT mh.id FROM map_history mh WHERE mh.score_count = $1 AND mh.category_id = $2 AND mh.map_id = $3` - err = database.DB.QueryRow(sql, mapID, request.RouteID).Scan(&historyID) + err = database.DB.QueryRow(sql, scoreCount, categoryID, mapID).Scan(&historyID) if err != nil { c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) return -- cgit v1.2.3 From 5e8b4791a06c8fa00e56f4d043d25d00ef003c76 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 12 Jul 2023 18:44:40 +0300 Subject: fix: 0 score count / showcase not required (#47) Former-commit-id: 2a1cea87348e0af8d97512a46093bd38768257ef --- backend/models/requests.go | 8 ++++---- docs/docs.go | 2 -- docs/swagger.json | 2 -- docs/swagger.yaml | 2 -- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/backend/models/requests.go b/backend/models/requests.go index fac05b6..0113597 100644 --- a/backend/models/requests.go +++ b/backend/models/requests.go @@ -8,18 +8,18 @@ import ( type CreateMapSummaryRequest struct { CategoryID int `json:"category_id" binding:"required"` Description string `json:"description" binding:"required"` - Showcase string `json:"showcase" binding:"required"` + Showcase string `json:"showcase"` UserName string `json:"user_name" binding:"required"` - ScoreCount int `json:"score_count" binding:"required"` + ScoreCount *int `json:"score_count" binding:"required"` RecordDate time.Time `json:"record_date" binding:"required"` } type EditMapSummaryRequest struct { RouteID int `json:"route_id" binding:"required"` Description string `json:"description" binding:"required"` - Showcase string `json:"showcase" binding:"required"` + Showcase string `json:"showcase"` UserName string `json:"user_name" binding:"required"` - ScoreCount int `json:"score_count" binding:"required"` + ScoreCount *int `json:"score_count" binding:"required"` RecordDate time.Time `json:"record_date" binding:"required"` } diff --git a/docs/docs.go b/docs/docs.go index 13d4cf6..423afad 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1086,7 +1086,6 @@ const docTemplate = `{ "description", "record_date", "score_count", - "showcase", "user_name" ], "properties": { @@ -1139,7 +1138,6 @@ const docTemplate = `{ "record_date", "route_id", "score_count", - "showcase", "user_name" ], "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index f644288..2e1a789 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1079,7 +1079,6 @@ "description", "record_date", "score_count", - "showcase", "user_name" ], "properties": { @@ -1132,7 +1131,6 @@ "record_date", "route_id", "score_count", - "showcase", "user_name" ], "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 3b706ea..7571073 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -51,7 +51,6 @@ definitions: - description - record_date - score_count - - showcase - user_name type: object models.DeleteMapSummaryRequest: @@ -87,7 +86,6 @@ definitions: - record_date - route_id - score_count - - showcase - user_name type: object models.Game: -- cgit v1.2.3