diff options
| -rw-r--r-- | backend/controllers/homeController.go | 10 | ||||
| -rw-r--r-- | backend/controllers/mapController.go | 126 | ||||
| -rw-r--r-- | backend/controllers/recordController.go | 12 | ||||
| -rw-r--r-- | backend/controllers/userController.go | 14 | ||||
| -rw-r--r-- | backend/middleware/auth.go | 2 | ||||
| -rw-r--r-- | backend/models/models.go | 25 | ||||
| -rw-r--r-- | backend/routes/routes.go | 3 | ||||
| -rw-r--r-- | docs/docs.go | 246 | ||||
| -rw-r--r-- | docs/swagger.json | 246 | ||||
| -rw-r--r-- | docs/swagger.yaml | 148 | ||||
| -rw-r--r-- | main.go | 9 |
11 files changed, 794 insertions, 47 deletions
diff --git a/backend/controllers/homeController.go b/backend/controllers/homeController.go index 206e3ae..edb770f 100644 --- a/backend/controllers/homeController.go +++ b/backend/controllers/homeController.go | |||
| @@ -29,7 +29,7 @@ func Home(c *gin.Context) { | |||
| 29 | // @Failure 400 {object} models.Response | 29 | // @Failure 400 {object} models.Response |
| 30 | // @Router /demo [get] | 30 | // @Router /demo [get] |
| 31 | func Rankings(c *gin.Context) { | 31 | func Rankings(c *gin.Context) { |
| 32 | rows, err := database.DB.Query(`SELECT steam_id, user_name FROM users;`) | 32 | rows, err := database.DB.Query(`SELECT steam_id, user_name FROM users`) |
| 33 | if err != nil { | 33 | if err != nil { |
| 34 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 34 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 35 | return | 35 | return |
| @@ -46,7 +46,7 @@ func Rankings(c *gin.Context) { | |||
| 46 | // Getting all sp records for each user | 46 | // Getting all sp records for each user |
| 47 | var uniqueSingleUserRecords, totalSingleMaps int | 47 | var uniqueSingleUserRecords, totalSingleMaps int |
| 48 | sql := `SELECT COUNT(DISTINCT map_id), (SELECT COUNT(map_name) FROM maps | 48 | sql := `SELECT COUNT(DISTINCT map_id), (SELECT COUNT(map_name) FROM maps |
| 49 | WHERE is_coop = FALSE AND is_disabled = false) FROM records_sp WHERE user_id = $1;` | 49 | WHERE is_coop = FALSE AND is_disabled = false) FROM records_sp WHERE user_id = $1` |
| 50 | err = database.DB.QueryRow(sql, userID).Scan(&uniqueSingleUserRecords, &totalSingleMaps) | 50 | err = database.DB.QueryRow(sql, userID).Scan(&uniqueSingleUserRecords, &totalSingleMaps) |
| 51 | if err != nil { | 51 | if err != nil { |
| 52 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 52 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| @@ -57,7 +57,7 @@ func Rankings(c *gin.Context) { | |||
| 57 | var ranking models.UserRanking | 57 | var ranking models.UserRanking |
| 58 | ranking.UserID = userID | 58 | ranking.UserID = userID |
| 59 | ranking.UserName = username | 59 | ranking.UserName = username |
| 60 | sql := `SELECT DISTINCT map_id, score_count FROM records_sp WHERE user_id = $1 ORDER BY map_id, score_count;` | 60 | sql := `SELECT DISTINCT map_id, score_count FROM records_sp WHERE user_id = $1 ORDER BY map_id, score_count` |
| 61 | rows, err := database.DB.Query(sql, userID) | 61 | rows, err := database.DB.Query(sql, userID) |
| 62 | if err != nil { | 62 | if err != nil { |
| 63 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 63 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| @@ -80,7 +80,7 @@ func Rankings(c *gin.Context) { | |||
| 80 | // Getting all mp records for each user | 80 | // Getting all mp records for each user |
| 81 | var uniqueMultiUserRecords, totalMultiMaps int | 81 | var uniqueMultiUserRecords, totalMultiMaps int |
| 82 | sql = `SELECT COUNT(DISTINCT map_id), (SELECT COUNT(map_name) FROM maps | 82 | sql = `SELECT COUNT(DISTINCT map_id), (SELECT COUNT(map_name) FROM maps |
| 83 | WHERE is_coop = TRUE AND is_disabled = false) FROM records_mp WHERE host_id = $1 OR partner_id = $2;` | 83 | WHERE is_coop = TRUE AND is_disabled = false) FROM records_mp WHERE host_id = $1 OR partner_id = $2` |
| 84 | err = database.DB.QueryRow(sql, userID, userID).Scan(&uniqueMultiUserRecords, &totalMultiMaps) | 84 | err = database.DB.QueryRow(sql, userID, userID).Scan(&uniqueMultiUserRecords, &totalMultiMaps) |
| 85 | if err != nil { | 85 | if err != nil { |
| 86 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 86 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| @@ -91,7 +91,7 @@ func Rankings(c *gin.Context) { | |||
| 91 | var ranking models.UserRanking | 91 | var ranking models.UserRanking |
| 92 | ranking.UserID = userID | 92 | ranking.UserID = userID |
| 93 | ranking.UserName = username | 93 | ranking.UserName = username |
| 94 | sql := `SELECT DISTINCT map_id, score_count FROM records_mp WHERE host_id = $1 OR partner_id = $2 ORDER BY map_id, score_count;` | 94 | sql := `SELECT DISTINCT map_id, score_count FROM records_mp WHERE host_id = $1 OR partner_id = $2 ORDER BY map_id, score_count` |
| 95 | rows, err := database.DB.Query(sql, userID, userID) | 95 | rows, err := database.DB.Query(sql, userID, userID) |
| 96 | if err != nil { | 96 | if err != nil { |
| 97 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 97 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
diff --git a/backend/controllers/mapController.go b/backend/controllers/mapController.go index bd85a97..d8783b7 100644 --- a/backend/controllers/mapController.go +++ b/backend/controllers/mapController.go | |||
| @@ -15,7 +15,6 @@ import ( | |||
| 15 | // | 15 | // |
| 16 | // @Summary Get map summary with specified id. | 16 | // @Summary Get map summary with specified id. |
| 17 | // @Tags maps | 17 | // @Tags maps |
| 18 | // @Accept json | ||
| 19 | // @Produce json | 18 | // @Produce json |
| 20 | // @Param id path int true "Map ID" | 19 | // @Param id path int true "Map ID" |
| 21 | // @Success 200 {object} models.Response{data=models.Map{data=models.MapSummary}} | 20 | // @Success 200 {object} models.Response{data=models.Map{data=models.MapSummary}} |
| @@ -57,7 +56,7 @@ func FetchMapSummary(c *gin.Context) { | |||
| 57 | FROM maps m | 56 | FROM maps m |
| 58 | INNER JOIN games g ON m.game_id = g.id | 57 | INNER JOIN games g ON m.game_id = g.id |
| 59 | INNER JOIN chapters c ON m.chapter_id = c.id | 58 | INNER JOIN chapters c ON m.chapter_id = c.id |
| 60 | WHERE m.id = $1;` | 59 | WHERE m.id = $1` |
| 61 | // TODO: CategoryScores | 60 | // TODO: CategoryScores |
| 62 | err = database.DB.QueryRow(sql, id).Scan(&mapData.GameName, &mapData.ChapterName, &mapData.MapName, &mapSummaryData.Description, &mapSummaryData.Showcase, &routers, &mapSummaryData.Rating) | 61 | err = database.DB.QueryRow(sql, id).Scan(&mapData.GameName, &mapData.ChapterName, &mapData.MapName, &mapSummaryData.Description, &mapSummaryData.Showcase, &routers, &mapSummaryData.Rating) |
| 63 | if err != nil { | 62 | if err != nil { |
| @@ -69,7 +68,7 @@ func FetchMapSummary(c *gin.Context) { | |||
| 69 | var historyDates pq.StringArray | 68 | var historyDates pq.StringArray |
| 70 | sql = `SELECT array_agg(user_name), array_agg(score_count), array_agg(record_date) | 69 | sql = `SELECT array_agg(user_name), array_agg(score_count), array_agg(record_date) |
| 71 | FROM map_history | 70 | FROM map_history |
| 72 | WHERE map_id = $1;` | 71 | WHERE map_id = $1` |
| 73 | err = database.DB.QueryRow(sql, id).Scan(&historyNames, &historyScores, &historyDates) | 72 | err = database.DB.QueryRow(sql, id).Scan(&historyNames, &historyScores, &historyDates) |
| 74 | if err != nil { | 73 | if err != nil { |
| 75 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 74 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| @@ -103,7 +102,6 @@ func FetchMapSummary(c *gin.Context) { | |||
| 103 | // | 102 | // |
| 104 | // @Summary Get map leaderboards with specified id. | 103 | // @Summary Get map leaderboards with specified id. |
| 105 | // @Tags maps | 104 | // @Tags maps |
| 106 | // @Accept json | ||
| 107 | // @Produce json | 105 | // @Produce json |
| 108 | // @Param id path int true "Map ID" | 106 | // @Param id path int true "Map ID" |
| 109 | // @Success 200 {object} models.Response{data=models.Map{data=models.MapRecords}} | 107 | // @Success 200 {object} models.Response{data=models.Map{data=models.MapRecords}} |
| @@ -125,7 +123,7 @@ func FetchMapLeaderboards(c *gin.Context) { | |||
| 125 | FROM maps m | 123 | FROM maps m |
| 126 | INNER JOIN games g ON m.game_id = g.id | 124 | INNER JOIN games g ON m.game_id = g.id |
| 127 | INNER JOIN chapters c ON m.chapter_id = c.id | 125 | INNER JOIN chapters c ON m.chapter_id = c.id |
| 128 | WHERE m.id = $1;` | 126 | WHERE m.id = $1` |
| 129 | err = database.DB.QueryRow(sql, id).Scan(&mapData.GameName, &mapData.ChapterName, &mapData.MapName, &isDisabled) | 127 | err = database.DB.QueryRow(sql, id).Scan(&mapData.GameName, &mapData.ChapterName, &mapData.MapName, &isDisabled) |
| 130 | if err != nil { | 128 | if err != nil { |
| 131 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 129 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| @@ -146,7 +144,7 @@ func FetchMapLeaderboards(c *gin.Context) { | |||
| 146 | FROM records_mp | 144 | FROM records_mp |
| 147 | WHERE map_id = $1 | 145 | WHERE map_id = $1 |
| 148 | ) sub | 146 | ) sub |
| 149 | WHERE rn = 1;` | 147 | WHERE rn = 1` |
| 150 | rows, err := database.DB.Query(sql, id) | 148 | rows, err := database.DB.Query(sql, id) |
| 151 | if err != nil { | 149 | if err != nil { |
| 152 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 150 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| @@ -181,7 +179,7 @@ func FetchMapLeaderboards(c *gin.Context) { | |||
| 181 | WHERE map_id = $1 | 179 | WHERE map_id = $1 |
| 182 | ) sub | 180 | ) sub |
| 183 | INNER JOIN users ON user_id = users.steam_id | 181 | INNER JOIN users ON user_id = users.steam_id |
| 184 | WHERE rn = 1;` | 182 | WHERE rn = 1` |
| 185 | rows, err := database.DB.Query(sql, id) | 183 | rows, err := database.DB.Query(sql, id) |
| 186 | if err != nil { | 184 | if err != nil { |
| 187 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 185 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| @@ -215,3 +213,117 @@ func FetchMapLeaderboards(c *gin.Context) { | |||
| 215 | Data: mapData, | 213 | Data: mapData, |
| 216 | }) | 214 | }) |
| 217 | } | 215 | } |
| 216 | |||
| 217 | // GET Games | ||
| 218 | // | ||
| 219 | // @Summary Get games from the leaderboards. | ||
| 220 | // @Tags games & chapters | ||
| 221 | // @Produce json | ||
| 222 | // @Success 200 {object} models.Response{data=[]models.Game} | ||
| 223 | // @Failure 400 {object} models.Response | ||
| 224 | // @Router /games [get] | ||
| 225 | func FetchGames(c *gin.Context) { | ||
| 226 | rows, err := database.DB.Query(`SELECT id, name FROM games`) | ||
| 227 | if err != nil { | ||
| 228 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 229 | return | ||
| 230 | } | ||
| 231 | var games []models.Game | ||
| 232 | for rows.Next() { | ||
| 233 | var game models.Game | ||
| 234 | if err := rows.Scan(&game.ID, &game.Name); err != nil { | ||
| 235 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 236 | return | ||
| 237 | } | ||
| 238 | games = append(games, game) | ||
| 239 | } | ||
| 240 | c.JSON(http.StatusOK, models.Response{ | ||
| 241 | Success: true, | ||
| 242 | Message: "Successfully retrieved games.", | ||
| 243 | Data: games, | ||
| 244 | }) | ||
| 245 | } | ||
| 246 | |||
| 247 | // GET Chapters of a Game | ||
| 248 | // | ||
| 249 | // @Summary Get chapters from the specified game id. | ||
| 250 | // @Tags games & chapters | ||
| 251 | // @Produce json | ||
| 252 | // @Param id path int true "Game ID" | ||
| 253 | // @Success 200 {object} models.Response{data=models.ChaptersResponse} | ||
| 254 | // @Failure 400 {object} models.Response | ||
| 255 | // @Router /games/{id} [get] | ||
| 256 | func FetchChapters(c *gin.Context) { | ||
| 257 | gameID := c.Param("id") | ||
| 258 | intID, err := strconv.Atoi(gameID) | ||
| 259 | if err != nil { | ||
| 260 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 261 | return | ||
| 262 | } | ||
| 263 | var response models.ChaptersResponse | ||
| 264 | rows, err := database.DB.Query(`SELECT c.id, c.name, g.name FROM chapters c INNER JOIN games g ON c.game_id = g.id WHERE game_id = $1`, gameID) | ||
| 265 | if err != nil { | ||
| 266 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 267 | return | ||
| 268 | } | ||
| 269 | var chapters []models.Chapter | ||
| 270 | var gameName string | ||
| 271 | for rows.Next() { | ||
| 272 | var chapter models.Chapter | ||
| 273 | if err := rows.Scan(&chapter.ID, &chapter.Name, &gameName); err != nil { | ||
| 274 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 275 | return | ||
| 276 | } | ||
| 277 | chapters = append(chapters, chapter) | ||
| 278 | } | ||
| 279 | response.Game.ID = intID | ||
| 280 | response.Game.Name = gameName | ||
| 281 | response.Chapters = chapters | ||
| 282 | c.JSON(http.StatusOK, models.Response{ | ||
| 283 | Success: true, | ||
| 284 | Message: "Successfully retrieved chapters.", | ||
| 285 | Data: response, | ||
| 286 | }) | ||
| 287 | } | ||
| 288 | |||
| 289 | // GET Maps of a Chapter | ||
| 290 | // | ||
| 291 | // @Summary Get maps from the specified chapter id. | ||
| 292 | // @Tags games & chapters | ||
| 293 | // @Produce json | ||
| 294 | // @Param id path int true "Chapter ID" | ||
| 295 | // @Success 200 {object} models.Response{data=models.ChapterMapsResponse} | ||
| 296 | // @Failure 400 {object} models.Response | ||
| 297 | // @Router /chapters/{id} [get] | ||
| 298 | func FetchChapterMaps(c *gin.Context) { | ||
| 299 | chapterID := c.Param("id") | ||
| 300 | intID, err := strconv.Atoi(chapterID) | ||
| 301 | if err != nil { | ||
| 302 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 303 | return | ||
| 304 | } | ||
| 305 | var response models.ChapterMapsResponse | ||
| 306 | rows, err := database.DB.Query(`SELECT m.id, m.name, c.name FROM maps m INNER JOIN chapters c ON m.chapter_id = c.id WHERE chapter_id = $1`, chapterID) | ||
| 307 | if err != nil { | ||
| 308 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 309 | return | ||
| 310 | } | ||
| 311 | var maps []models.MapShort | ||
| 312 | var chapterName string | ||
| 313 | for rows.Next() { | ||
| 314 | var mapShort models.MapShort | ||
| 315 | if err := rows.Scan(&mapShort.ID, &mapShort.Name, &chapterName); err != nil { | ||
| 316 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 317 | return | ||
| 318 | } | ||
| 319 | maps = append(maps, mapShort) | ||
| 320 | } | ||
| 321 | response.Chapter.ID = intID | ||
| 322 | response.Chapter.Name = chapterName | ||
| 323 | response.Maps = maps | ||
| 324 | c.JSON(http.StatusOK, models.Response{ | ||
| 325 | Success: true, | ||
| 326 | Message: "Successfully retrieved maps.", | ||
| 327 | Data: response, | ||
| 328 | }) | ||
| 329 | } | ||
diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index bafa844..627be57 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go | |||
| @@ -46,7 +46,7 @@ func CreateRecordWithDemo(c *gin.Context) { | |||
| 46 | var gameID int | 46 | var gameID int |
| 47 | var isCoop bool | 47 | var isCoop bool |
| 48 | var isDisabled bool | 48 | var isDisabled bool |
| 49 | sql := `SELECT game_id, is_disabled FROM maps WHERE id = $1;` | 49 | sql := `SELECT game_id, is_disabled FROM maps WHERE id = $1` |
| 50 | err := database.DB.QueryRow(sql, mapId).Scan(&gameID, &isDisabled) | 50 | err := database.DB.QueryRow(sql, mapId).Scan(&gameID, &isDisabled) |
| 51 | if err != nil { | 51 | if err != nil { |
| 52 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 52 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| @@ -153,7 +153,7 @@ func CreateRecordWithDemo(c *gin.Context) { | |||
| 153 | // Insert into records | 153 | // Insert into records |
| 154 | if isCoop { | 154 | if isCoop { |
| 155 | sql := `INSERT INTO records_mp(map_id,score_count,score_time,host_id,partner_id,host_demo_id,partner_demo_id) | 155 | sql := `INSERT INTO records_mp(map_id,score_count,score_time,host_id,partner_id,host_demo_id,partner_demo_id) |
| 156 | VALUES($1, $2, $3, $4, $5, $6, $7);` | 156 | VALUES($1, $2, $3, $4, $5, $6, $7)` |
| 157 | var hostID string | 157 | var hostID string |
| 158 | var partnerID string | 158 | var partnerID string |
| 159 | if record.IsPartnerOrange { | 159 | if record.IsPartnerOrange { |
| @@ -171,7 +171,7 @@ func CreateRecordWithDemo(c *gin.Context) { | |||
| 171 | } | 171 | } |
| 172 | // If a new world record based on portal count | 172 | // If a new world record based on portal count |
| 173 | // if record.ScoreCount < wrScore { | 173 | // if record.ScoreCount < wrScore { |
| 174 | // _, err := tx.Exec(`UPDATE maps SET wr_score = $1, wr_time = $2 WHERE id = $3;`, record.ScoreCount, record.ScoreTime, mapId) | 174 | // _, err := tx.Exec(`UPDATE maps SET wr_score = $1, wr_time = $2 WHERE id = $3`, record.ScoreCount, record.ScoreTime, mapId) |
| 175 | // if err != nil { | 175 | // if err != nil { |
| 176 | // c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 176 | // c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 177 | // return | 177 | // return |
| @@ -179,7 +179,7 @@ func CreateRecordWithDemo(c *gin.Context) { | |||
| 179 | // } | 179 | // } |
| 180 | } else { | 180 | } else { |
| 181 | sql := `INSERT INTO records_sp(map_id,score_count,score_time,user_id,demo_id) | 181 | sql := `INSERT INTO records_sp(map_id,score_count,score_time,user_id,demo_id) |
| 182 | VALUES($1, $2, $3, $4, $5);` | 182 | VALUES($1, $2, $3, $4, $5)` |
| 183 | _, err := tx.Exec(sql, mapId, record.ScoreCount, record.ScoreTime, user.(models.User).SteamID, hostDemoUUID) | 183 | _, err := tx.Exec(sql, mapId, record.ScoreCount, record.ScoreTime, user.(models.User).SteamID, hostDemoUUID) |
| 184 | if err != nil { | 184 | if err != nil { |
| 185 | deleteFile(srv, fileID) | 185 | deleteFile(srv, fileID) |
| @@ -188,7 +188,7 @@ func CreateRecordWithDemo(c *gin.Context) { | |||
| 188 | } | 188 | } |
| 189 | // If a new world record based on portal count | 189 | // If a new world record based on portal count |
| 190 | // if record.ScoreCount < wrScore { | 190 | // if record.ScoreCount < wrScore { |
| 191 | // _, err := tx.Exec(`UPDATE maps SET wr_score = $1, wr_time = $2 WHERE id = $3;`, record.ScoreCount, record.ScoreTime, mapId) | 191 | // _, err := tx.Exec(`UPDATE maps SET wr_score = $1, wr_time = $2 WHERE id = $3`, record.ScoreCount, record.ScoreTime, mapId) |
| 192 | // if err != nil { | 192 | // if err != nil { |
| 193 | // c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 193 | // c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 194 | // return | 194 | // return |
| @@ -224,7 +224,7 @@ func DownloadDemoWithID(c *gin.Context) { | |||
| 224 | c.JSON(http.StatusBadRequest, models.ErrorResponse("Invalid id given.")) | 224 | c.JSON(http.StatusBadRequest, models.ErrorResponse("Invalid id given.")) |
| 225 | return | 225 | return |
| 226 | } | 226 | } |
| 227 | err := database.DB.QueryRow(`SELECT location_id FROM demos WHERE id = $1;`, uuid).Scan(&locationID) | 227 | err := database.DB.QueryRow(`SELECT location_id FROM demos WHERE id = $1`, uuid).Scan(&locationID) |
| 228 | if err != nil { | 228 | if err != nil { |
| 229 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 229 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 230 | return | 230 | return |
diff --git a/backend/controllers/userController.go b/backend/controllers/userController.go index 5ad800d..adf936b 100644 --- a/backend/controllers/userController.go +++ b/backend/controllers/userController.go | |||
| @@ -31,7 +31,7 @@ func Profile(c *gin.Context) { | |||
| 31 | } | 31 | } |
| 32 | // Retrieve singleplayer records | 32 | // Retrieve singleplayer records |
| 33 | var scoresSP []models.ScoreResponse | 33 | var scoresSP []models.ScoreResponse |
| 34 | sql := `SELECT id, map_id, score_count, score_time, demo_id, record_date FROM records_sp WHERE user_id = $1 ORDER BY map_id;` | 34 | sql := `SELECT id, map_id, score_count, score_time, demo_id, record_date FROM records_sp WHERE user_id = $1 ORDER BY map_id` |
| 35 | rows, err := database.DB.Query(sql, user.(models.User).SteamID) | 35 | rows, err := database.DB.Query(sql, user.(models.User).SteamID) |
| 36 | if err != nil { | 36 | if err != nil { |
| 37 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 37 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| @@ -58,7 +58,7 @@ func Profile(c *gin.Context) { | |||
| 58 | // Retrieve multiplayer records | 58 | // Retrieve multiplayer records |
| 59 | var scoresMP []models.ScoreResponse | 59 | var scoresMP []models.ScoreResponse |
| 60 | sql = `SELECT id, map_id, host_id, partner_id, score_count, score_time, host_demo_id, partner_demo_id, record_date FROM records_mp | 60 | sql = `SELECT id, map_id, host_id, partner_id, score_count, score_time, host_demo_id, partner_demo_id, record_date FROM records_mp |
| 61 | WHERE host_id = $1 OR partner_id = $2 ORDER BY map_id;` | 61 | WHERE host_id = $1 OR partner_id = $2 ORDER BY map_id` |
| 62 | rows, err = database.DB.Query(sql, user.(models.User).SteamID, user.(models.User).SteamID) | 62 | rows, err = database.DB.Query(sql, user.(models.User).SteamID, user.(models.User).SteamID) |
| 63 | if err != nil { | 63 | if err != nil { |
| 64 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 64 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| @@ -119,7 +119,7 @@ func FetchUser(c *gin.Context) { | |||
| 119 | } | 119 | } |
| 120 | // Check if user exists | 120 | // Check if user exists |
| 121 | var user models.User | 121 | var user models.User |
| 122 | err := database.DB.QueryRow(`SELECT * FROM users WHERE steam_id = $1;`, id).Scan( | 122 | err := database.DB.QueryRow(`SELECT * FROM users WHERE steam_id = $1`, id).Scan( |
| 123 | &user.SteamID, &user.UserName, &user.AvatarLink, &user.CountryCode, | 123 | &user.SteamID, &user.UserName, &user.AvatarLink, &user.CountryCode, |
| 124 | &user.CreatedAt, &user.UpdatedAt) | 124 | &user.CreatedAt, &user.UpdatedAt) |
| 125 | if user.SteamID == "" { | 125 | if user.SteamID == "" { |
| @@ -133,7 +133,7 @@ func FetchUser(c *gin.Context) { | |||
| 133 | } | 133 | } |
| 134 | // Retrieve singleplayer records | 134 | // Retrieve singleplayer records |
| 135 | var scoresSP []models.ScoreResponse | 135 | var scoresSP []models.ScoreResponse |
| 136 | sql := `SELECT id, map_id, score_count, score_time, demo_id, record_date FROM records_sp WHERE user_id = $1 ORDER BY map_id;` | 136 | sql := `SELECT id, map_id, score_count, score_time, demo_id, record_date FROM records_sp WHERE user_id = $1 ORDER BY map_id` |
| 137 | rows, err := database.DB.Query(sql, user.SteamID) | 137 | rows, err := database.DB.Query(sql, user.SteamID) |
| 138 | if err != nil { | 138 | if err != nil { |
| 139 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 139 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| @@ -160,7 +160,7 @@ func FetchUser(c *gin.Context) { | |||
| 160 | // Retrieve multiplayer records | 160 | // Retrieve multiplayer records |
| 161 | var scoresMP []models.ScoreResponse | 161 | var scoresMP []models.ScoreResponse |
| 162 | sql = `SELECT id, map_id, host_id, partner_id, score_count, score_time, host_demo_id, partner_demo_id, record_date FROM records_mp | 162 | sql = `SELECT id, map_id, host_id, partner_id, score_count, score_time, host_demo_id, partner_demo_id, record_date FROM records_mp |
| 163 | WHERE host_id = $1 OR partner_id = $2 ORDER BY map_id;` | 163 | WHERE host_id = $1 OR partner_id = $2 ORDER BY map_id` |
| 164 | rows, err = database.DB.Query(sql, user.SteamID, user.SteamID) | 164 | rows, err = database.DB.Query(sql, user.SteamID, user.SteamID) |
| 165 | if err != nil { | 165 | if err != nil { |
| 166 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 166 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| @@ -225,7 +225,7 @@ func UpdateUser(c *gin.Context) { | |||
| 225 | } | 225 | } |
| 226 | // Update profile | 226 | // Update profile |
| 227 | _, err = database.DB.Exec(`UPDATE users SET username = $1, avatar_link = $2, country_code = $3, updated_at = $4 | 227 | _, err = database.DB.Exec(`UPDATE users SET username = $1, avatar_link = $2, country_code = $3, updated_at = $4 |
| 228 | WHERE steam_id = $5;`, profile.PersonaName, profile.AvatarFull, profile.LocCountryCode, time.Now().UTC(), user.(models.User).SteamID) | 228 | WHERE steam_id = $5`, profile.PersonaName, profile.AvatarFull, profile.LocCountryCode, time.Now().UTC(), user.(models.User).SteamID) |
| 229 | if err != nil { | 229 | if err != nil { |
| 230 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | 230 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 231 | return | 231 | return |
| @@ -268,7 +268,7 @@ func UpdateCountryCode(c *gin.Context) { | |||
| 268 | return | 268 | return |
| 269 | } | 269 | } |
| 270 | var validCode string | 270 | var validCode string |
| 271 | err := database.DB.QueryRow(`SELECT country_code FROM countries WHERE country_code = $1;`, code).Scan(&validCode) | 271 | err := database.DB.QueryRow(`SELECT country_code FROM countries WHERE country_code = $1`, code).Scan(&validCode) |
| 272 | if err != nil { | 272 | if err != nil { |
| 273 | c.JSON(http.StatusNotFound, models.ErrorResponse(err.Error())) | 273 | c.JSON(http.StatusNotFound, models.ErrorResponse(err.Error())) |
| 274 | return | 274 | return |
diff --git a/backend/middleware/auth.go b/backend/middleware/auth.go index cd00a30..14a0b78 100644 --- a/backend/middleware/auth.go +++ b/backend/middleware/auth.go | |||
| @@ -36,7 +36,7 @@ func CheckAuth(c *gin.Context) { | |||
| 36 | } | 36 | } |
| 37 | // Get user from DB | 37 | // Get user from DB |
| 38 | var user models.User | 38 | var user models.User |
| 39 | database.DB.QueryRow(`SELECT * FROM users WHERE steam_id = $1;`, claims["sub"]).Scan( | 39 | database.DB.QueryRow(`SELECT * FROM users WHERE steam_id = $1`, claims["sub"]).Scan( |
| 40 | &user.SteamID, &user.UserName, &user.AvatarLink, | 40 | &user.SteamID, &user.UserName, &user.AvatarLink, |
| 41 | &user.CountryCode, &user.CreatedAt, &user.UpdatedAt) | 41 | &user.CountryCode, &user.CreatedAt, &user.UpdatedAt) |
| 42 | if user.SteamID == "" { | 42 | if user.SteamID == "" { |
diff --git a/backend/models/models.go b/backend/models/models.go index cdcd111..6f5173a 100644 --- a/backend/models/models.go +++ b/backend/models/models.go | |||
| @@ -153,6 +153,31 @@ type PlayerSummaries struct { | |||
| 153 | GameServerIp string `json:"gameserverip"` | 153 | GameServerIp string `json:"gameserverip"` |
| 154 | } | 154 | } |
| 155 | 155 | ||
| 156 | type Game struct { | ||
| 157 | ID int `json:"id"` | ||
| 158 | Name string `json:"name"` | ||
| 159 | } | ||
| 160 | |||
| 161 | type ChaptersResponse struct { | ||
| 162 | Game Game `json:"game"` | ||
| 163 | Chapters []Chapter `json:"chapters"` | ||
| 164 | } | ||
| 165 | |||
| 166 | type Chapter struct { | ||
| 167 | ID int `json:"id"` | ||
| 168 | Name string `json:"name"` | ||
| 169 | } | ||
| 170 | |||
| 171 | type MapShort struct { | ||
| 172 | ID int `json:"id"` | ||
| 173 | Name string `json:"name"` | ||
| 174 | } | ||
| 175 | |||
| 176 | type ChapterMapsResponse struct { | ||
| 177 | Chapter Chapter `json:"chapter"` | ||
| 178 | Maps []MapShort `json:"maps"` | ||
| 179 | } | ||
| 180 | |||
| 156 | func ErrorResponse(message string) Response { | 181 | func ErrorResponse(message string) Response { |
| 157 | return Response{ | 182 | return Response{ |
| 158 | Success: false, | 183 | Success: false, |
diff --git a/backend/routes/routes.go b/backend/routes/routes.go index 53d4e78..2554fa4 100644 --- a/backend/routes/routes.go +++ b/backend/routes/routes.go | |||
| @@ -28,5 +28,8 @@ func InitRoutes(router *gin.Engine) { | |||
| 28 | v1.POST("/maps/:id/record", middleware.CheckAuth, controllers.CreateRecordWithDemo) | 28 | v1.POST("/maps/:id/record", middleware.CheckAuth, controllers.CreateRecordWithDemo) |
| 29 | v1.GET("/rankings", middleware.CheckAuth, controllers.Rankings) | 29 | v1.GET("/rankings", middleware.CheckAuth, controllers.Rankings) |
| 30 | v1.GET("/search", controllers.Search) | 30 | v1.GET("/search", controllers.Search) |
| 31 | v1.GET("/games", controllers.FetchGames) | ||
| 32 | v1.GET("/games/:id", controllers.FetchChapters) | ||
| 33 | v1.GET("/chapters/:id", controllers.FetchChapterMaps) | ||
| 31 | } | 34 | } |
| 32 | } | 35 | } |
diff --git a/docs/docs.go b/docs/docs.go index 22d4362..cd129f0 100644 --- a/docs/docs.go +++ b/docs/docs.go | |||
| @@ -20,6 +20,52 @@ const docTemplate = `{ | |||
| 20 | "host": "{{.Host}}", | 20 | "host": "{{.Host}}", |
| 21 | "basePath": "{{.BasePath}}", | 21 | "basePath": "{{.BasePath}}", |
| 22 | "paths": { | 22 | "paths": { |
| 23 | "/chapters/{id}": { | ||
| 24 | "get": { | ||
| 25 | "produces": [ | ||
| 26 | "application/json" | ||
| 27 | ], | ||
| 28 | "tags": [ | ||
| 29 | "games \u0026 chapters" | ||
| 30 | ], | ||
| 31 | "summary": "Get maps from the specified chapter id.", | ||
| 32 | "parameters": [ | ||
| 33 | { | ||
| 34 | "type": "integer", | ||
| 35 | "description": "Chapter ID", | ||
| 36 | "name": "id", | ||
| 37 | "in": "path", | ||
| 38 | "required": true | ||
| 39 | } | ||
| 40 | ], | ||
| 41 | "responses": { | ||
| 42 | "200": { | ||
| 43 | "description": "OK", | ||
| 44 | "schema": { | ||
| 45 | "allOf": [ | ||
| 46 | { | ||
| 47 | "$ref": "#/definitions/models.Response" | ||
| 48 | }, | ||
| 49 | { | ||
| 50 | "type": "object", | ||
| 51 | "properties": { | ||
| 52 | "data": { | ||
| 53 | "$ref": "#/definitions/models.ChapterMapsResponse" | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | ] | ||
| 58 | } | ||
| 59 | }, | ||
| 60 | "400": { | ||
| 61 | "description": "Bad Request", | ||
| 62 | "schema": { | ||
| 63 | "$ref": "#/definitions/models.Response" | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 68 | }, | ||
| 23 | "/demo": { | 69 | "/demo": { |
| 24 | "get": { | 70 | "get": { |
| 25 | "produces": [ | 71 | "produces": [ |
| @@ -94,6 +140,92 @@ const docTemplate = `{ | |||
| 94 | } | 140 | } |
| 95 | } | 141 | } |
| 96 | }, | 142 | }, |
| 143 | "/games": { | ||
| 144 | "get": { | ||
| 145 | "produces": [ | ||
| 146 | "application/json" | ||
| 147 | ], | ||
| 148 | "tags": [ | ||
| 149 | "games \u0026 chapters" | ||
| 150 | ], | ||
| 151 | "summary": "Get games from the leaderboards.", | ||
| 152 | "responses": { | ||
| 153 | "200": { | ||
| 154 | "description": "OK", | ||
| 155 | "schema": { | ||
| 156 | "allOf": [ | ||
| 157 | { | ||
| 158 | "$ref": "#/definitions/models.Response" | ||
| 159 | }, | ||
| 160 | { | ||
| 161 | "type": "object", | ||
| 162 | "properties": { | ||
| 163 | "data": { | ||
| 164 | "type": "array", | ||
| 165 | "items": { | ||
| 166 | "$ref": "#/definitions/models.Game" | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | ] | ||
| 172 | } | ||
| 173 | }, | ||
| 174 | "400": { | ||
| 175 | "description": "Bad Request", | ||
| 176 | "schema": { | ||
| 177 | "$ref": "#/definitions/models.Response" | ||
| 178 | } | ||
| 179 | } | ||
| 180 | } | ||
| 181 | } | ||
| 182 | }, | ||
| 183 | "/games/{id}": { | ||
| 184 | "get": { | ||
| 185 | "produces": [ | ||
| 186 | "application/json" | ||
| 187 | ], | ||
| 188 | "tags": [ | ||
| 189 | "games \u0026 chapters" | ||
| 190 | ], | ||
| 191 | "summary": "Get chapters from the specified game id.", | ||
| 192 | "parameters": [ | ||
| 193 | { | ||
| 194 | "type": "integer", | ||
| 195 | "description": "Game ID", | ||
| 196 | "name": "id", | ||
| 197 | "in": "path", | ||
| 198 | "required": true | ||
| 199 | } | ||
| 200 | ], | ||
| 201 | "responses": { | ||
| 202 | "200": { | ||
| 203 | "description": "OK", | ||
| 204 | "schema": { | ||
| 205 | "allOf": [ | ||
| 206 | { | ||
| 207 | "$ref": "#/definitions/models.Response" | ||
| 208 | }, | ||
| 209 | { | ||
| 210 | "type": "object", | ||
| 211 | "properties": { | ||
| 212 | "data": { | ||
| 213 | "$ref": "#/definitions/models.ChaptersResponse" | ||
| 214 | } | ||
| 215 | } | ||
| 216 | } | ||
| 217 | ] | ||
| 218 | } | ||
| 219 | }, | ||
| 220 | "400": { | ||
| 221 | "description": "Bad Request", | ||
| 222 | "schema": { | ||
| 223 | "$ref": "#/definitions/models.Response" | ||
| 224 | } | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } | ||
| 228 | }, | ||
| 97 | "/login": { | 229 | "/login": { |
| 98 | "get": { | 230 | "get": { |
| 99 | "consumes": [ | 231 | "consumes": [ |
| @@ -136,9 +268,6 @@ const docTemplate = `{ | |||
| 136 | }, | 268 | }, |
| 137 | "/maps/{id}/leaderboards": { | 269 | "/maps/{id}/leaderboards": { |
| 138 | "get": { | 270 | "get": { |
| 139 | "consumes": [ | ||
| 140 | "application/json" | ||
| 141 | ], | ||
| 142 | "produces": [ | 271 | "produces": [ |
| 143 | "application/json" | 272 | "application/json" |
| 144 | ], | 273 | ], |
| @@ -290,9 +419,6 @@ const docTemplate = `{ | |||
| 290 | }, | 419 | }, |
| 291 | "/maps/{id}/summary": { | 420 | "/maps/{id}/summary": { |
| 292 | "get": { | 421 | "get": { |
| 293 | "consumes": [ | ||
| 294 | "application/json" | ||
| 295 | ], | ||
| 296 | "produces": [ | 422 | "produces": [ |
| 297 | "application/json" | 423 | "application/json" |
| 298 | ], | 424 | ], |
| @@ -530,7 +656,19 @@ const docTemplate = `{ | |||
| 530 | "200": { | 656 | "200": { |
| 531 | "description": "OK", | 657 | "description": "OK", |
| 532 | "schema": { | 658 | "schema": { |
| 533 | "$ref": "#/definitions/models.Response" | 659 | "allOf": [ |
| 660 | { | ||
| 661 | "$ref": "#/definitions/models.Response" | ||
| 662 | }, | ||
| 663 | { | ||
| 664 | "type": "object", | ||
| 665 | "properties": { | ||
| 666 | "data": { | ||
| 667 | "$ref": "#/definitions/models.SearchResponse" | ||
| 668 | } | ||
| 669 | } | ||
| 670 | } | ||
| 671 | ] | ||
| 534 | } | 672 | } |
| 535 | }, | 673 | }, |
| 536 | "400": { | 674 | "400": { |
| @@ -599,6 +737,56 @@ const docTemplate = `{ | |||
| 599 | } | 737 | } |
| 600 | }, | 738 | }, |
| 601 | "definitions": { | 739 | "definitions": { |
| 740 | "models.Chapter": { | ||
| 741 | "type": "object", | ||
| 742 | "properties": { | ||
| 743 | "id": { | ||
| 744 | "type": "integer" | ||
| 745 | }, | ||
| 746 | "name": { | ||
| 747 | "type": "string" | ||
| 748 | } | ||
| 749 | } | ||
| 750 | }, | ||
| 751 | "models.ChapterMapsResponse": { | ||
| 752 | "type": "object", | ||
| 753 | "properties": { | ||
| 754 | "chapter": { | ||
| 755 | "$ref": "#/definitions/models.Chapter" | ||
| 756 | }, | ||
| 757 | "maps": { | ||
| 758 | "type": "array", | ||
| 759 | "items": { | ||
| 760 | "$ref": "#/definitions/models.MapShort" | ||
| 761 | } | ||
| 762 | } | ||
| 763 | } | ||
| 764 | }, | ||
| 765 | "models.ChaptersResponse": { | ||
| 766 | "type": "object", | ||
| 767 | "properties": { | ||
| 768 | "chapters": { | ||
| 769 | "type": "array", | ||
| 770 | "items": { | ||
| 771 | "$ref": "#/definitions/models.Chapter" | ||
| 772 | } | ||
| 773 | }, | ||
| 774 | "game": { | ||
| 775 | "$ref": "#/definitions/models.Game" | ||
| 776 | } | ||
| 777 | } | ||
| 778 | }, | ||
| 779 | "models.Game": { | ||
| 780 | "type": "object", | ||
| 781 | "properties": { | ||
| 782 | "id": { | ||
| 783 | "type": "integer" | ||
| 784 | }, | ||
| 785 | "name": { | ||
| 786 | "type": "string" | ||
| 787 | } | ||
| 788 | } | ||
| 789 | }, | ||
| 602 | "models.LoginResponse": { | 790 | "models.LoginResponse": { |
| 603 | "type": "object", | 791 | "type": "object", |
| 604 | "properties": { | 792 | "properties": { |
| @@ -662,6 +850,17 @@ const docTemplate = `{ | |||
| 662 | "records": {} | 850 | "records": {} |
| 663 | } | 851 | } |
| 664 | }, | 852 | }, |
| 853 | "models.MapShort": { | ||
| 854 | "type": "object", | ||
| 855 | "properties": { | ||
| 856 | "id": { | ||
| 857 | "type": "integer" | ||
| 858 | }, | ||
| 859 | "name": { | ||
| 860 | "type": "string" | ||
| 861 | } | ||
| 862 | } | ||
| 863 | }, | ||
| 665 | "models.MapSummary": { | 864 | "models.MapSummary": { |
| 666 | "type": "object", | 865 | "type": "object", |
| 667 | "properties": { | 866 | "properties": { |
| @@ -784,6 +983,39 @@ const docTemplate = `{ | |||
| 784 | "records": {} | 983 | "records": {} |
| 785 | } | 984 | } |
| 786 | }, | 985 | }, |
| 986 | "models.SearchResponse": { | ||
| 987 | "type": "object", | ||
| 988 | "properties": { | ||
| 989 | "maps": { | ||
| 990 | "type": "array", | ||
| 991 | "items": { | ||
| 992 | "type": "object", | ||
| 993 | "properties": { | ||
| 994 | "id": { | ||
| 995 | "type": "integer" | ||
| 996 | }, | ||
| 997 | "name": { | ||
| 998 | "type": "string" | ||
| 999 | } | ||
| 1000 | } | ||
| 1001 | } | ||
| 1002 | }, | ||
| 1003 | "players": { | ||
| 1004 | "type": "array", | ||
| 1005 | "items": { | ||
| 1006 | "type": "object", | ||
| 1007 | "properties": { | ||
| 1008 | "steam_id": { | ||
| 1009 | "type": "string" | ||
| 1010 | }, | ||
| 1011 | "user_name": { | ||
| 1012 | "type": "string" | ||
| 1013 | } | ||
| 1014 | } | ||
| 1015 | } | ||
| 1016 | } | ||
| 1017 | } | ||
| 1018 | }, | ||
| 787 | "models.UserRanking": { | 1019 | "models.UserRanking": { |
| 788 | "type": "object", | 1020 | "type": "object", |
| 789 | "properties": { | 1021 | "properties": { |
diff --git a/docs/swagger.json b/docs/swagger.json index 0bebe1c..442745f 100644 --- a/docs/swagger.json +++ b/docs/swagger.json | |||
| @@ -13,6 +13,52 @@ | |||
| 13 | "host": "lp.ardapektezol.com/api", | 13 | "host": "lp.ardapektezol.com/api", |
| 14 | "basePath": "/v1", | 14 | "basePath": "/v1", |
| 15 | "paths": { | 15 | "paths": { |
| 16 | "/chapters/{id}": { | ||
| 17 | "get": { | ||
| 18 | "produces": [ | ||
| 19 | "application/json" | ||
| 20 | ], | ||
| 21 | "tags": [ | ||
| 22 | "games \u0026 chapters" | ||
| 23 | ], | ||
| 24 | "summary": "Get maps from the specified chapter id.", | ||
| 25 | "parameters": [ | ||
| 26 | { | ||
| 27 | "type": "integer", | ||
| 28 | "description": "Chapter ID", | ||
| 29 | "name": "id", | ||
| 30 | "in": "path", | ||
| 31 | "required": true | ||
| 32 | } | ||
| 33 | ], | ||
| 34 | "responses": { | ||
| 35 | "200": { | ||
| 36 | "description": "OK", | ||
| 37 | "schema": { | ||
| 38 | "allOf": [ | ||
| 39 | { | ||
| 40 | "$ref": "#/definitions/models.Response" | ||
| 41 | }, | ||
| 42 | { | ||
| 43 | "type": "object", | ||
| 44 | "properties": { | ||
| 45 | "data": { | ||
| 46 | "$ref": "#/definitions/models.ChapterMapsResponse" | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
| 50 | ] | ||
| 51 | } | ||
| 52 | }, | ||
| 53 | "400": { | ||
| 54 | "description": "Bad Request", | ||
| 55 | "schema": { | ||
| 56 | "$ref": "#/definitions/models.Response" | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | }, | ||
| 16 | "/demo": { | 62 | "/demo": { |
| 17 | "get": { | 63 | "get": { |
| 18 | "produces": [ | 64 | "produces": [ |
| @@ -87,6 +133,92 @@ | |||
| 87 | } | 133 | } |
| 88 | } | 134 | } |
| 89 | }, | 135 | }, |
| 136 | "/games": { | ||
| 137 | "get": { | ||
| 138 | "produces": [ | ||
| 139 | "application/json" | ||
| 140 | ], | ||
| 141 | "tags": [ | ||
| 142 | "games \u0026 chapters" | ||
| 143 | ], | ||
| 144 | "summary": "Get games from the leaderboards.", | ||
| 145 | "responses": { | ||
| 146 | "200": { | ||
| 147 | "description": "OK", | ||
| 148 | "schema": { | ||
| 149 | "allOf": [ | ||
| 150 | { | ||
| 151 | "$ref": "#/definitions/models.Response" | ||
| 152 | }, | ||
| 153 | { | ||
| 154 | "type": "object", | ||
| 155 | "properties": { | ||
| 156 | "data": { | ||
| 157 | "type": "array", | ||
| 158 | "items": { | ||
| 159 | "$ref": "#/definitions/models.Game" | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | ||
| 164 | ] | ||
| 165 | } | ||
| 166 | }, | ||
| 167 | "400": { | ||
| 168 | "description": "Bad Request", | ||
| 169 | "schema": { | ||
| 170 | "$ref": "#/definitions/models.Response" | ||
| 171 | } | ||
| 172 | } | ||
| 173 | } | ||
| 174 | } | ||
| 175 | }, | ||
| 176 | "/games/{id}": { | ||
| 177 | "get": { | ||
| 178 | "produces": [ | ||
| 179 | "application/json" | ||
| 180 | ], | ||
| 181 | "tags": [ | ||
| 182 | "games \u0026 chapters" | ||
| 183 | ], | ||
| 184 | "summary": "Get chapters from the specified game id.", | ||
| 185 | "parameters": [ | ||
| 186 | { | ||
| 187 | "type": "integer", | ||
| 188 | "description": "Game ID", | ||
| 189 | "name": "id", | ||
| 190 | "in": "path", | ||
| 191 | "required": true | ||
| 192 | } | ||
| 193 | ], | ||
| 194 | "responses": { | ||
| 195 | "200": { | ||
| 196 | "description": "OK", | ||
| 197 | "schema": { | ||
| 198 | "allOf": [ | ||
| 199 | { | ||
| 200 | "$ref": "#/definitions/models.Response" | ||
| 201 | }, | ||
| 202 | { | ||
| 203 | "type": "object", | ||
| 204 | "properties": { | ||
| 205 | "data": { | ||
| 206 | "$ref": "#/definitions/models.ChaptersResponse" | ||
| 207 | } | ||
| 208 | } | ||
| 209 | } | ||
| 210 | ] | ||
| 211 | } | ||
| 212 | }, | ||
| 213 | "400": { | ||
| 214 | "description": "Bad Request", | ||
| 215 | "schema": { | ||
| 216 | "$ref": "#/definitions/models.Response" | ||
| 217 | } | ||
| 218 | } | ||
| 219 | } | ||
| 220 | } | ||
| 221 | }, | ||
| 90 | "/login": { | 222 | "/login": { |
| 91 | "get": { | 223 | "get": { |
| 92 | "consumes": [ | 224 | "consumes": [ |
| @@ -129,9 +261,6 @@ | |||
| 129 | }, | 261 | }, |
| 130 | "/maps/{id}/leaderboards": { | 262 | "/maps/{id}/leaderboards": { |
| 131 | "get": { | 263 | "get": { |
| 132 | "consumes": [ | ||
| 133 | "application/json" | ||
| 134 | ], | ||
| 135 | "produces": [ | 264 | "produces": [ |
| 136 | "application/json" | 265 | "application/json" |
| 137 | ], | 266 | ], |
| @@ -283,9 +412,6 @@ | |||
| 283 | }, | 412 | }, |
| 284 | "/maps/{id}/summary": { | 413 | "/maps/{id}/summary": { |
| 285 | "get": { | 414 | "get": { |
| 286 | "consumes": [ | ||
| 287 | "application/json" | ||
| 288 | ], | ||
| 289 | "produces": [ | 415 | "produces": [ |
| 290 | "application/json" | 416 | "application/json" |
| 291 | ], | 417 | ], |
| @@ -523,7 +649,19 @@ | |||
| 523 | "200": { | 649 | "200": { |
| 524 | "description": "OK", | 650 | "description": "OK", |
| 525 | "schema": { | 651 | "schema": { |
| 526 | "$ref": "#/definitions/models.Response" | 652 | "allOf": [ |
| 653 | { | ||
| 654 | "$ref": "#/definitions/models.Response" | ||
| 655 | }, | ||
| 656 | { | ||
| 657 | "type": "object", | ||
| 658 | "properties": { | ||
| 659 | "data": { | ||
| 660 | "$ref": "#/definitions/models.SearchResponse" | ||
| 661 | } | ||
| 662 | } | ||
| 663 | } | ||
| 664 | ] | ||
| 527 | } | 665 | } |
| 528 | }, | 666 | }, |
| 529 | "400": { | 667 | "400": { |
| @@ -592,6 +730,56 @@ | |||
| 592 | } | 730 | } |
| 593 | }, | 731 | }, |
| 594 | "definitions": { | 732 | "definitions": { |
| 733 | "models.Chapter": { | ||
| 734 | "type": "object", | ||
| 735 | "properties": { | ||
| 736 | "id": { | ||
| 737 | "type": "integer" | ||
| 738 | }, | ||
| 739 | "name": { | ||
| 740 | "type": "string" | ||
| 741 | } | ||
| 742 | } | ||
| 743 | }, | ||
| 744 | "models.ChapterMapsResponse": { | ||
| 745 | "type": "object", | ||
| 746 | "properties": { | ||
| 747 | "chapter": { | ||
| 748 | "$ref": "#/definitions/models.Chapter" | ||
| 749 | }, | ||
| 750 | "maps": { | ||
| 751 | "type": "array", | ||
| 752 | "items": { | ||
| 753 | "$ref": "#/definitions/models.MapShort" | ||
| 754 | } | ||
| 755 | } | ||
| 756 | } | ||
| 757 | }, | ||
| 758 | "models.ChaptersResponse": { | ||
| 759 | "type": "object", | ||
| 760 | "properties": { | ||
| 761 | "chapters": { | ||
| 762 | "type": "array", | ||
| 763 | "items": { | ||
| 764 | "$ref": "#/definitions/models.Chapter" | ||
| 765 | } | ||
| 766 | }, | ||
| 767 | "game": { | ||
| 768 | "$ref": "#/definitions/models.Game" | ||
| 769 | } | ||
| 770 | } | ||
| 771 | }, | ||
| 772 | "models.Game": { | ||
| 773 | "type": "object", | ||
| 774 | "properties": { | ||
| 775 | "id": { | ||
| 776 | "type": "integer" | ||
| 777 | }, | ||
| 778 | "name": { | ||
| 779 | "type": "string" | ||
| 780 | } | ||
| 781 | } | ||
| 782 | }, | ||
| 595 | "models.LoginResponse": { | 783 | "models.LoginResponse": { |
| 596 | "type": "object", | 784 | "type": "object", |
| 597 | "properties": { | 785 | "properties": { |
| @@ -655,6 +843,17 @@ | |||
| 655 | "records": {} | 843 | "records": {} |
| 656 | } | 844 | } |
| 657 | }, | 845 | }, |
| 846 | "models.MapShort": { | ||
| 847 | "type": "object", | ||
| 848 | "properties": { | ||
| 849 | "id": { | ||
| 850 | "type": "integer" | ||
| 851 | }, | ||
| 852 | "name": { | ||
| 853 | "type": "string" | ||
| 854 | } | ||
| 855 | } | ||
| 856 | }, | ||
| 658 | "models.MapSummary": { | 857 | "models.MapSummary": { |
| 659 | "type": "object", | 858 | "type": "object", |
| 660 | "properties": { | 859 | "properties": { |
| @@ -777,6 +976,39 @@ | |||
| 777 | "records": {} | 976 | "records": {} |
| 778 | } | 977 | } |
| 779 | }, | 978 | }, |
| 979 | "models.SearchResponse": { | ||
| 980 | "type": "object", | ||
| 981 | "properties": { | ||
| 982 | "maps": { | ||
| 983 | "type": "array", | ||
| 984 | "items": { | ||
| 985 | "type": "object", | ||
| 986 | "properties": { | ||
| 987 | "id": { | ||
| 988 | "type": "integer" | ||
| 989 | }, | ||
| 990 | "name": { | ||
| 991 | "type": "string" | ||
| 992 | } | ||
| 993 | } | ||
| 994 | } | ||
| 995 | }, | ||
| 996 | "players": { | ||
| 997 | "type": "array", | ||
| 998 | "items": { | ||
| 999 | "type": "object", | ||
| 1000 | "properties": { | ||
| 1001 | "steam_id": { | ||
| 1002 | "type": "string" | ||
| 1003 | }, | ||
| 1004 | "user_name": { | ||
| 1005 | "type": "string" | ||
| 1006 | } | ||
| 1007 | } | ||
| 1008 | } | ||
| 1009 | } | ||
| 1010 | } | ||
| 1011 | }, | ||
| 780 | "models.UserRanking": { | 1012 | "models.UserRanking": { |
| 781 | "type": "object", | 1013 | "type": "object", |
| 782 | "properties": { | 1014 | "properties": { |
diff --git a/docs/swagger.yaml b/docs/swagger.yaml index f719008..fa1a26d 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml | |||
| @@ -1,5 +1,37 @@ | |||
| 1 | basePath: /v1 | 1 | basePath: /v1 |
| 2 | definitions: | 2 | definitions: |
| 3 | models.Chapter: | ||
| 4 | properties: | ||
| 5 | id: | ||
| 6 | type: integer | ||
| 7 | name: | ||
| 8 | type: string | ||
| 9 | type: object | ||
| 10 | models.ChapterMapsResponse: | ||
| 11 | properties: | ||
| 12 | chapter: | ||
| 13 | $ref: '#/definitions/models.Chapter' | ||
| 14 | maps: | ||
| 15 | items: | ||
| 16 | $ref: '#/definitions/models.MapShort' | ||
| 17 | type: array | ||
| 18 | type: object | ||
| 19 | models.ChaptersResponse: | ||
| 20 | properties: | ||
| 21 | chapters: | ||
| 22 | items: | ||
| 23 | $ref: '#/definitions/models.Chapter' | ||
| 24 | type: array | ||
| 25 | game: | ||
| 26 | $ref: '#/definitions/models.Game' | ||
| 27 | type: object | ||
| 28 | models.Game: | ||
| 29 | properties: | ||
| 30 | id: | ||
| 31 | type: integer | ||
| 32 | name: | ||
| 33 | type: string | ||
| 34 | type: object | ||
| 3 | models.LoginResponse: | 35 | models.LoginResponse: |
| 4 | properties: | 36 | properties: |
| 5 | token: | 37 | token: |
| @@ -41,6 +73,13 @@ definitions: | |||
| 41 | properties: | 73 | properties: |
| 42 | records: {} | 74 | records: {} |
| 43 | type: object | 75 | type: object |
| 76 | models.MapShort: | ||
| 77 | properties: | ||
| 78 | id: | ||
| 79 | type: integer | ||
| 80 | name: | ||
| 81 | type: string | ||
| 82 | type: object | ||
| 44 | models.MapSummary: | 83 | models.MapSummary: |
| 45 | properties: | 84 | properties: |
| 46 | category_scores: | 85 | category_scores: |
| @@ -122,6 +161,27 @@ definitions: | |||
| 122 | type: integer | 161 | type: integer |
| 123 | records: {} | 162 | records: {} |
| 124 | type: object | 163 | type: object |
| 164 | models.SearchResponse: | ||
| 165 | properties: | ||
| 166 | maps: | ||
| 167 | items: | ||
| 168 | properties: | ||
| 169 | id: | ||
| 170 | type: integer | ||
| 171 | name: | ||
| 172 | type: string | ||
| 173 | type: object | ||
| 174 | type: array | ||
| 175 | players: | ||
| 176 | items: | ||
| 177 | properties: | ||
| 178 | steam_id: | ||
| 179 | type: string | ||
| 180 | user_name: | ||
| 181 | type: string | ||
| 182 | type: object | ||
| 183 | type: array | ||
| 184 | type: object | ||
| 125 | models.UserRanking: | 185 | models.UserRanking: |
| 126 | properties: | 186 | properties: |
| 127 | total_score: | 187 | total_score: |
| @@ -141,6 +201,33 @@ info: | |||
| 141 | title: Least Portals Database API | 201 | title: Least Portals Database API |
| 142 | version: "1.0" | 202 | version: "1.0" |
| 143 | paths: | 203 | paths: |
| 204 | /chapters/{id}: | ||
| 205 | get: | ||
| 206 | parameters: | ||
| 207 | - description: Chapter ID | ||
| 208 | in: path | ||
| 209 | name: id | ||
| 210 | required: true | ||
| 211 | type: integer | ||
| 212 | produces: | ||
| 213 | - application/json | ||
| 214 | responses: | ||
| 215 | "200": | ||
| 216 | description: OK | ||
| 217 | schema: | ||
| 218 | allOf: | ||
| 219 | - $ref: '#/definitions/models.Response' | ||
| 220 | - properties: | ||
| 221 | data: | ||
| 222 | $ref: '#/definitions/models.ChapterMapsResponse' | ||
| 223 | type: object | ||
| 224 | "400": | ||
| 225 | description: Bad Request | ||
| 226 | schema: | ||
| 227 | $ref: '#/definitions/models.Response' | ||
| 228 | summary: Get maps from the specified chapter id. | ||
| 229 | tags: | ||
| 230 | - games & chapters | ||
| 144 | /demo: | 231 | /demo: |
| 145 | get: | 232 | get: |
| 146 | produces: | 233 | produces: |
| @@ -186,6 +273,56 @@ paths: | |||
| 186 | summary: Get demo with specified demo uuid. | 273 | summary: Get demo with specified demo uuid. |
| 187 | tags: | 274 | tags: |
| 188 | - demo | 275 | - demo |
| 276 | /games: | ||
| 277 | get: | ||
| 278 | produces: | ||
| 279 | - application/json | ||
| 280 | responses: | ||
| 281 | "200": | ||
| 282 | description: OK | ||
| 283 | schema: | ||
| 284 | allOf: | ||
| 285 | - $ref: '#/definitions/models.Response' | ||
| 286 | - properties: | ||
| 287 | data: | ||
| 288 | items: | ||
| 289 | $ref: '#/definitions/models.Game' | ||
| 290 | type: array | ||
| 291 | type: object | ||
| 292 | "400": | ||
| 293 | description: Bad Request | ||
| 294 | schema: | ||
| 295 | $ref: '#/definitions/models.Response' | ||
| 296 | summary: Get games from the leaderboards. | ||
| 297 | tags: | ||
| 298 | - games & chapters | ||
| 299 | /games/{id}: | ||
| 300 | get: | ||
| 301 | parameters: | ||
| 302 | - description: Game ID | ||
| 303 | in: path | ||
| 304 | name: id | ||
| 305 | required: true | ||
| 306 | type: integer | ||
| 307 | produces: | ||
| 308 | - application/json | ||
| 309 | responses: | ||
| 310 | "200": | ||
| 311 | description: OK | ||
| 312 | schema: | ||
| 313 | allOf: | ||
| 314 | - $ref: '#/definitions/models.Response' | ||
| 315 | - properties: | ||
| 316 | data: | ||
| 317 | $ref: '#/definitions/models.ChaptersResponse' | ||
| 318 | type: object | ||
| 319 | "400": | ||
| 320 | description: Bad Request | ||
| 321 | schema: | ||
| 322 | $ref: '#/definitions/models.Response' | ||
| 323 | summary: Get chapters from the specified game id. | ||
| 324 | tags: | ||
| 325 | - games & chapters | ||
| 189 | /login: | 326 | /login: |
| 190 | get: | 327 | get: |
| 191 | consumes: | 328 | consumes: |
| @@ -211,8 +348,6 @@ paths: | |||
| 211 | - login | 348 | - login |
| 212 | /maps/{id}/leaderboards: | 349 | /maps/{id}/leaderboards: |
| 213 | get: | 350 | get: |
| 214 | consumes: | ||
| 215 | - application/json | ||
| 216 | parameters: | 351 | parameters: |
| 217 | - description: Map ID | 352 | - description: Map ID |
| 218 | in: path | 353 | in: path |
| @@ -305,8 +440,6 @@ paths: | |||
| 305 | - maps | 440 | - maps |
| 306 | /maps/{id}/summary: | 441 | /maps/{id}/summary: |
| 307 | get: | 442 | get: |
| 308 | consumes: | ||
| 309 | - application/json | ||
| 310 | parameters: | 443 | parameters: |
| 311 | - description: Map ID | 444 | - description: Map ID |
| 312 | in: path | 445 | in: path |
| @@ -447,7 +580,12 @@ paths: | |||
| 447 | "200": | 580 | "200": |
| 448 | description: OK | 581 | description: OK |
| 449 | schema: | 582 | schema: |
| 450 | $ref: '#/definitions/models.Response' | 583 | allOf: |
| 584 | - $ref: '#/definitions/models.Response' | ||
| 585 | - properties: | ||
| 586 | data: | ||
| 587 | $ref: '#/definitions/models.SearchResponse' | ||
| 588 | type: object | ||
| 451 | "400": | 589 | "400": |
| 452 | description: Bad Request | 590 | description: Bad Request |
| 453 | schema: | 591 | schema: |
| @@ -5,6 +5,7 @@ import ( | |||
| 5 | "log" | 5 | "log" |
| 6 | "os" | 6 | "os" |
| 7 | 7 | ||
| 8 | "github.com/gin-gonic/contrib/cors" | ||
| 8 | "github.com/gin-gonic/gin" | 9 | "github.com/gin-gonic/gin" |
| 9 | "github.com/joho/godotenv" | 10 | "github.com/joho/godotenv" |
| 10 | "github.com/pektezol/leastportals/backend/database" | 11 | "github.com/pektezol/leastportals/backend/database" |
| @@ -19,8 +20,8 @@ import ( | |||
| 19 | // @license.name GNU General Public License, Version 2 | 20 | // @license.name GNU General Public License, Version 2 |
| 20 | // @license.url https://www.gnu.org/licenses/old-licenses/gpl-2.0.html | 21 | // @license.url https://www.gnu.org/licenses/old-licenses/gpl-2.0.html |
| 21 | 22 | ||
| 22 | // @host lp.ardapektezol.com/api | 23 | // @host lp.ardapektezol.com/api |
| 23 | // @BasePath /v1 | 24 | // @BasePath /v1 |
| 24 | func main() { | 25 | func main() { |
| 25 | if os.Getenv("ENV") == "PROD" { | 26 | if os.Getenv("ENV") == "PROD" { |
| 26 | gin.SetMode(gin.ReleaseMode) | 27 | gin.SetMode(gin.ReleaseMode) |
| @@ -30,6 +31,10 @@ func main() { | |||
| 30 | log.Fatal("Error loading .env file") | 31 | log.Fatal("Error loading .env file") |
| 31 | } | 32 | } |
| 32 | router := gin.Default() | 33 | router := gin.Default() |
| 34 | config := cors.DefaultConfig() | ||
| 35 | config.AllowAllOrigins = true | ||
| 36 | config.AllowCredentials = true | ||
| 37 | router.Use(cors.New(config)) | ||
| 33 | database.ConnectDB() | 38 | database.ConnectDB() |
| 34 | // For frontend static serving - only for local debug | 39 | // For frontend static serving - only for local debug |
| 35 | // router.Use(static.Serve("/", static.LocalFile("./frontend/build", true))) | 40 | // router.Use(static.Serve("/", static.LocalFile("./frontend/build", true))) |