diff options
Diffstat (limited to 'backend')
| -rw-r--r-- | backend/api/routes.go | 5 | ||||
| -rw-r--r-- | backend/database/init.sql | 2 | ||||
| -rw-r--r-- | backend/handlers/home.go | 8 | ||||
| -rw-r--r-- | backend/handlers/logs.go | 6 | ||||
| -rw-r--r-- | backend/handlers/map.go | 8 | ||||
| -rw-r--r-- | backend/handlers/record.go | 79 |
6 files changed, 97 insertions, 11 deletions
diff --git a/backend/api/routes.go b/backend/api/routes.go index 339dc73..9e703f6 100644 --- a/backend/api/routes.go +++ b/backend/api/routes.go | |||
| @@ -21,6 +21,7 @@ const ( | |||
| 21 | mapImagePath string = "/maps/:mapid/image" | 21 | mapImagePath string = "/maps/:mapid/image" |
| 22 | mapLeaderboardsPath string = "/maps/:mapid/leaderboards" | 22 | mapLeaderboardsPath string = "/maps/:mapid/leaderboards" |
| 23 | mapRecordPath string = "/maps/:mapid/record" | 23 | mapRecordPath string = "/maps/:mapid/record" |
| 24 | mapRecordIDPath string = "/maps/:mapid/record/:recordid" | ||
| 24 | mapDiscussionsPath string = "/maps/:mapid/discussions" | 25 | mapDiscussionsPath string = "/maps/:mapid/discussions" |
| 25 | mapDiscussionIDPath string = "/maps/:mapid/discussions/:discussionid" | 26 | mapDiscussionIDPath string = "/maps/:mapid/discussions/:discussionid" |
| 26 | rankingsPath string = "/rankings" | 27 | rankingsPath string = "/rankings" |
| @@ -51,14 +52,18 @@ func InitRoutes(router *gin.Engine) { | |||
| 51 | v1.POST(profilePath, CheckAuth, handlers.UpdateUser) | 52 | v1.POST(profilePath, CheckAuth, handlers.UpdateUser) |
| 52 | v1.GET(usersPath, CheckAuth, handlers.FetchUser) | 53 | v1.GET(usersPath, CheckAuth, handlers.FetchUser) |
| 53 | // Maps | 54 | // Maps |
| 55 | // - Summary | ||
| 54 | v1.GET(mapSummaryPath, handlers.FetchMapSummary) | 56 | v1.GET(mapSummaryPath, handlers.FetchMapSummary) |
| 55 | v1.POST(mapSummaryPath, CheckAuth, handlers.CreateMapSummary) | 57 | v1.POST(mapSummaryPath, CheckAuth, handlers.CreateMapSummary) |
| 56 | v1.PUT(mapSummaryPath, CheckAuth, handlers.EditMapSummary) | 58 | v1.PUT(mapSummaryPath, CheckAuth, handlers.EditMapSummary) |
| 57 | v1.DELETE(mapSummaryPath, CheckAuth, handlers.DeleteMapSummary) | 59 | v1.DELETE(mapSummaryPath, CheckAuth, handlers.DeleteMapSummary) |
| 58 | v1.PUT(mapImagePath, CheckAuth, handlers.EditMapImage) | 60 | v1.PUT(mapImagePath, CheckAuth, handlers.EditMapImage) |
| 61 | // - Leaderboards | ||
| 59 | v1.GET(mapLeaderboardsPath, handlers.FetchMapLeaderboards) | 62 | v1.GET(mapLeaderboardsPath, handlers.FetchMapLeaderboards) |
| 60 | v1.POST(mapRecordPath, CheckAuth, handlers.CreateRecordWithDemo) | 63 | v1.POST(mapRecordPath, CheckAuth, handlers.CreateRecordWithDemo) |
| 64 | v1.DELETE(mapRecordIDPath, CheckAuth, handlers.DeleteRecord) | ||
| 61 | v1.GET(demosPath, handlers.DownloadDemoWithID) | 65 | v1.GET(demosPath, handlers.DownloadDemoWithID) |
| 66 | // - Discussions | ||
| 62 | v1.GET(mapDiscussionsPath, handlers.FetchMapDiscussions) | 67 | v1.GET(mapDiscussionsPath, handlers.FetchMapDiscussions) |
| 63 | v1.GET(mapDiscussionIDPath, handlers.FetchMapDiscussion) | 68 | v1.GET(mapDiscussionIDPath, handlers.FetchMapDiscussion) |
| 64 | v1.POST(mapDiscussionsPath, CheckAuth, handlers.CreateMapDiscussion) | 69 | v1.POST(mapDiscussionsPath, CheckAuth, handlers.CreateMapDiscussion) |
diff --git a/backend/database/init.sql b/backend/database/init.sql index 0f8196b..9d7b68c 100644 --- a/backend/database/init.sql +++ b/backend/database/init.sql | |||
| @@ -130,6 +130,7 @@ CREATE TABLE records_sp ( | |||
| 130 | score_time INTEGER NOT NULL, | 130 | score_time INTEGER NOT NULL, |
| 131 | demo_id UUID NOT NULL, | 131 | demo_id UUID NOT NULL, |
| 132 | record_date TIMESTAMP NOT NULL DEFAULT now(), | 132 | record_date TIMESTAMP NOT NULL DEFAULT now(), |
| 133 | is_deleted BOOLEAN NOT NULL DEFAULT false, | ||
| 133 | PRIMARY KEY (id), | 134 | PRIMARY KEY (id), |
| 134 | FOREIGN KEY (map_id) REFERENCES maps(id), | 135 | FOREIGN KEY (map_id) REFERENCES maps(id), |
| 135 | FOREIGN KEY (user_id) REFERENCES users(steam_id), | 136 | FOREIGN KEY (user_id) REFERENCES users(steam_id), |
| @@ -146,6 +147,7 @@ CREATE TABLE records_mp ( | |||
| 146 | host_demo_id UUID NOT NULL, | 147 | host_demo_id UUID NOT NULL, |
| 147 | partner_demo_id UUID NOT NULL, | 148 | partner_demo_id UUID NOT NULL, |
| 148 | record_date TIMESTAMP NOT NULL DEFAULT now(), | 149 | record_date TIMESTAMP NOT NULL DEFAULT now(), |
| 150 | is_deleted BOOLEAN NOT NULL DEFAULT false, | ||
| 149 | PRIMARY KEY (id), | 151 | PRIMARY KEY (id), |
| 150 | FOREIGN KEY (map_id) REFERENCES maps(id), | 152 | FOREIGN KEY (map_id) REFERENCES maps(id), |
| 151 | FOREIGN KEY (host_id) REFERENCES users(steam_id), | 153 | FOREIGN KEY (host_id) REFERENCES users(steam_id), |
diff --git a/backend/handlers/home.go b/backend/handlers/home.go index c9742f2..5863218 100644 --- a/backend/handlers/home.go +++ b/backend/handlers/home.go | |||
| @@ -49,11 +49,11 @@ func Rankings(c *gin.Context) { | |||
| 49 | SELECT | 49 | SELECT |
| 50 | user_id, | 50 | user_id, |
| 51 | MIN(score_count) AS min_score_count | 51 | MIN(score_count) AS min_score_count |
| 52 | FROM records_sp | 52 | FROM records_sp WHERE is_deleted = false |
| 53 | GROUP BY user_id, map_id | 53 | GROUP BY user_id, map_id |
| 54 | ) AS subquery | 54 | ) AS subquery |
| 55 | WHERE user_id = u.steam_id) | 55 | WHERE user_id = u.steam_id) |
| 56 | FROM records_sp sp JOIN users u ON u.steam_id = sp.user_id GROUP BY u.steam_id, u.user_name` | 56 | FROM records_sp sp JOIN users u ON u.steam_id = sp.user_id WHERE is_deleted = false GROUP BY u.steam_id, u.user_name` |
| 57 | rows, err := database.DB.Query(sql) | 57 | rows, err := database.DB.Query(sql) |
| 58 | if err != nil { | 58 | if err != nil { |
| 59 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 59 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
| @@ -81,11 +81,11 @@ func Rankings(c *gin.Context) { | |||
| 81 | host_id, | 81 | host_id, |
| 82 | partner_id, | 82 | partner_id, |
| 83 | MIN(score_count) AS min_score_count | 83 | MIN(score_count) AS min_score_count |
| 84 | FROM records_mp | 84 | FROM records_mp WHERE is_deleted = false |
| 85 | GROUP BY host_id, partner_id, map_id | 85 | GROUP BY host_id, partner_id, map_id |
| 86 | ) AS subquery | 86 | ) AS subquery |
| 87 | WHERE host_id = u.steam_id OR partner_id = u.steam_id) | 87 | WHERE host_id = u.steam_id OR partner_id = u.steam_id) |
| 88 | FROM records_mp mp JOIN users u ON u.steam_id = mp.host_id OR u.steam_id = mp.partner_id GROUP BY u.steam_id, u.user_name` | 88 | FROM records_mp mp JOIN users u ON u.steam_id = mp.host_id OR u.steam_id = mp.partner_id WHERE is_deleted = false GROUP BY u.steam_id, u.user_name` |
| 89 | rows, err = database.DB.Query(sql) | 89 | rows, err = database.DB.Query(sql) |
| 90 | if err != nil { | 90 | if err != nil { |
| 91 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | 91 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) |
diff --git a/backend/handlers/logs.go b/backend/handlers/logs.go index 6f59238..4c7b415 100644 --- a/backend/handlers/logs.go +++ b/backend/handlers/logs.go | |||
| @@ -140,17 +140,17 @@ func ScoreLogs(c *gin.Context) { | |||
| 140 | rs.record_date | 140 | rs.record_date |
| 141 | FROM ( | 141 | FROM ( |
| 142 | SELECT id, map_id, user_id, score_count, score_time, demo_id, record_date | 142 | SELECT id, map_id, user_id, score_count, score_time, demo_id, record_date |
| 143 | FROM records_sp | 143 | FROM records_sp WHERE is_deleted = false |
| 144 | 144 | ||
| 145 | UNION ALL | 145 | UNION ALL |
| 146 | 146 | ||
| 147 | SELECT id, map_id, host_id AS user_id, score_count, score_time, host_demo_id AS demo_id, record_date | 147 | SELECT id, map_id, host_id AS user_id, score_count, score_time, host_demo_id AS demo_id, record_date |
| 148 | FROM records_mp | 148 | FROM records_mp WHERE is_deleted = false |
| 149 | 149 | ||
| 150 | UNION ALL | 150 | UNION ALL |
| 151 | 151 | ||
| 152 | SELECT id, map_id, partner_id AS user_id, score_count, score_time, partner_demo_id AS demo_id, record_date | 152 | SELECT id, map_id, partner_id AS user_id, score_count, score_time, partner_demo_id AS demo_id, record_date |
| 153 | FROM records_mp | 153 | FROM records_mp WHERE is_deleted = false |
| 154 | ) AS rs | 154 | ) AS rs |
| 155 | JOIN users u ON rs.user_id = u.steam_id | 155 | JOIN users u ON rs.user_id = u.steam_id |
| 156 | JOIN maps m ON rs.map_id = m.id | 156 | JOIN maps m ON rs.map_id = m.id |
diff --git a/backend/handlers/map.go b/backend/handlers/map.go index 3bf14cd..faccee4 100644 --- a/backend/handlers/map.go +++ b/backend/handlers/map.go | |||
| @@ -104,7 +104,7 @@ func FetchMapSummary(c *gin.Context) { | |||
| 104 | if response.Map.IsCoop { | 104 | if response.Map.IsCoop { |
| 105 | sql = `SELECT count(*) FROM ( SELECT host_id, partner_id, score_count, score_time, | 105 | sql = `SELECT count(*) FROM ( SELECT host_id, partner_id, score_count, score_time, |
| 106 | ROW_NUMBER() OVER (PARTITION BY host_id, partner_id ORDER BY score_count, score_time) AS rn | 106 | ROW_NUMBER() OVER (PARTITION BY host_id, partner_id ORDER BY score_count, score_time) AS rn |
| 107 | FROM records_mp WHERE map_id = $1 | 107 | FROM records_mp WHERE map_id = $1 AND is_deleted = false |
| 108 | ) sub WHERE sub.rn = 1 AND score_count = $2` | 108 | ) sub WHERE sub.rn = 1 AND score_count = $2` |
| 109 | err = database.DB.QueryRow(sql, response.Map.ID, route.History.ScoreCount).Scan(&route.CompletionCount) | 109 | err = database.DB.QueryRow(sql, response.Map.ID, route.History.ScoreCount).Scan(&route.CompletionCount) |
| 110 | if err != nil { | 110 | if err != nil { |
| @@ -114,7 +114,7 @@ func FetchMapSummary(c *gin.Context) { | |||
| 114 | } else { | 114 | } else { |
| 115 | sql = `SELECT count(*) FROM ( SELECT user_id, score_count, score_time, | 115 | sql = `SELECT count(*) FROM ( SELECT user_id, score_count, score_time, |
| 116 | ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY score_count, score_time) AS rn | 116 | ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY score_count, score_time) AS rn |
| 117 | FROM records_sp WHERE map_id = $1 | 117 | FROM records_sp WHERE map_id = $1 AND is_deleted = false |
| 118 | ) sub WHERE rn = 1 AND score_count = $2` | 118 | ) sub WHERE rn = 1 AND score_count = $2` |
| 119 | err = database.DB.QueryRow(sql, response.Map.ID, route.History.ScoreCount).Scan(&route.CompletionCount) | 119 | err = database.DB.QueryRow(sql, response.Map.ID, route.History.ScoreCount).Scan(&route.CompletionCount) |
| 120 | if err != nil { | 120 | if err != nil { |
| @@ -204,7 +204,7 @@ func FetchMapLeaderboards(c *gin.Context) { | |||
| 204 | record_date, | 204 | record_date, |
| 205 | ROW_NUMBER() OVER (PARTITION BY host_id, partner_id ORDER BY score_count, score_time) AS rn | 205 | ROW_NUMBER() OVER (PARTITION BY host_id, partner_id ORDER BY score_count, score_time) AS rn |
| 206 | FROM records_mp | 206 | FROM records_mp |
| 207 | WHERE map_id = $1 | 207 | WHERE map_id = $1 AND is_deleted = false |
| 208 | ) sub | 208 | ) sub |
| 209 | JOIN users AS host ON sub.host_id = host.steam_id | 209 | JOIN users AS host ON sub.host_id = host.steam_id |
| 210 | JOIN users AS partner ON sub.partner_id = partner.steam_id | 210 | JOIN users AS partner ON sub.partner_id = partner.steam_id |
| @@ -255,7 +255,7 @@ func FetchMapLeaderboards(c *gin.Context) { | |||
| 255 | SELECT id, user_id, score_count, score_time, demo_id, record_date, | 255 | SELECT id, user_id, score_count, score_time, demo_id, record_date, |
| 256 | ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY score_count, score_time) AS rn | 256 | ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY score_count, score_time) AS rn |
| 257 | FROM records_sp | 257 | FROM records_sp |
| 258 | WHERE map_id = $1 | 258 | WHERE map_id = $1 AND is_deleted = false |
| 259 | ) sub | 259 | ) sub |
| 260 | INNER JOIN users ON user_id = users.steam_id | 260 | INNER JOIN users ON user_id = users.steam_id |
| 261 | WHERE rn = 1 | 261 | WHERE rn = 1 |
diff --git a/backend/handlers/record.go b/backend/handlers/record.go index 4787422..70095bf 100644 --- a/backend/handlers/record.go +++ b/backend/handlers/record.go | |||
| @@ -8,6 +8,7 @@ import ( | |||
| 8 | "mime/multipart" | 8 | "mime/multipart" |
| 9 | "net/http" | 9 | "net/http" |
| 10 | "os" | 10 | "os" |
| 11 | "strconv" | ||
| 11 | 12 | ||
| 12 | "github.com/gin-gonic/gin" | 13 | "github.com/gin-gonic/gin" |
| 13 | "github.com/google/uuid" | 14 | "github.com/google/uuid" |
| @@ -209,6 +210,84 @@ func CreateRecordWithDemo(c *gin.Context) { | |||
| 209 | }) | 210 | }) |
| 210 | } | 211 | } |
| 211 | 212 | ||
| 213 | func DeleteRecord(c *gin.Context) { | ||
| 214 | mapID, err := strconv.Atoi(c.Param("mapid")) | ||
| 215 | if err != nil { | ||
| 216 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | ||
| 217 | return | ||
| 218 | } | ||
| 219 | recordID, err := strconv.Atoi(c.Param("recordid")) | ||
| 220 | if err != nil { | ||
| 221 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | ||
| 222 | return | ||
| 223 | } | ||
| 224 | user, exists := c.Get("user") | ||
| 225 | if !exists { | ||
| 226 | c.JSON(http.StatusOK, models.ErrorResponse("User not logged in.")) | ||
| 227 | return | ||
| 228 | } | ||
| 229 | // Validate map | ||
| 230 | var validateMapID int | ||
| 231 | var isCoop bool | ||
| 232 | sql := `SELECT m.id, g.is_coop FROM maps m INNER JOIN games g ON m.game_id = g.id | ||
| 233 | INNER JOIN chapters c ON m.chapter_id = c.id WHERE m.id = $1` | ||
| 234 | err = database.DB.QueryRow(sql, mapID).Scan(&validateMapID, &isCoop) | ||
| 235 | if err != nil { | ||
| 236 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | ||
| 237 | return | ||
| 238 | } | ||
| 239 | if mapID != validateMapID { | ||
| 240 | c.JSON(http.StatusOK, models.ErrorResponse("Selected map does not exist.")) | ||
| 241 | return | ||
| 242 | } | ||
| 243 | if isCoop { | ||
| 244 | // Validate if cooperative record does exist | ||
| 245 | var validateRecordID int | ||
| 246 | sql = `SELECT mp.id FROM records_mp mp WHERE mp.id = $1 AND mp.map_id = $2 AND (mp.host_id = $3 OR mp.partner_id = $3) AND is_deleted = false` | ||
| 247 | err = database.DB.QueryRow(sql, recordID, mapID, user.(models.User).SteamID).Scan(&validateRecordID) | ||
| 248 | if err != nil { | ||
| 249 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | ||
| 250 | return | ||
| 251 | } | ||
| 252 | if recordID != validateRecordID { | ||
| 253 | c.JSON(http.StatusOK, models.ErrorResponse("Selected record does not exist.")) | ||
| 254 | return | ||
| 255 | } | ||
| 256 | // Remove record | ||
| 257 | sql = `UPDATE records_mp SET is_deleted = true WHERE id = $1` | ||
| 258 | _, err = database.DB.Exec(sql, recordID) | ||
| 259 | if err != nil { | ||
| 260 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | ||
| 261 | return | ||
| 262 | } | ||
| 263 | } else { | ||
| 264 | // Validate if singleplayer record does exist | ||
| 265 | var validateRecordID int | ||
| 266 | sql = `SELECT sp.id FROM records_sp sp WHERE sp.id = $1 AND sp.map_id = $2 AND sp.user_id = $3 AND is_deleted = false` | ||
| 267 | err = database.DB.QueryRow(sql, recordID, mapID, user.(models.User).SteamID).Scan(&validateRecordID) | ||
| 268 | if err != nil { | ||
| 269 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | ||
| 270 | return | ||
| 271 | } | ||
| 272 | if recordID != validateRecordID { | ||
| 273 | c.JSON(http.StatusOK, models.ErrorResponse("Selected record does not exist.")) | ||
| 274 | return | ||
| 275 | } | ||
| 276 | // Remove record | ||
| 277 | sql = `UPDATE records_sp SET is_deleted = true WHERE id = $1` | ||
| 278 | _, err = database.DB.Exec(sql, recordID) | ||
| 279 | if err != nil { | ||
| 280 | c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) | ||
| 281 | return | ||
| 282 | } | ||
| 283 | } | ||
| 284 | c.JSON(http.StatusOK, models.Response{ | ||
| 285 | Success: true, | ||
| 286 | Message: "Successfully deleted record.", | ||
| 287 | Data: nil, | ||
| 288 | }) | ||
| 289 | } | ||
| 290 | |||
| 212 | // GET Demo | 291 | // GET Demo |
| 213 | // | 292 | // |
| 214 | // @Description Get demo with specified demo uuid. | 293 | // @Description Get demo with specified demo uuid. |