aboutsummaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
Diffstat (limited to 'backend')
-rw-r--r--backend/controllers/homeController.go18
-rw-r--r--backend/controllers/loginController.go16
-rw-r--r--backend/controllers/mapController.go27
-rw-r--r--backend/controllers/modController.go67
-rw-r--r--backend/controllers/recordController.go20
-rw-r--r--backend/controllers/userController.go43
-rw-r--r--backend/database/history.sql522
-rw-r--r--backend/database/init.sql2
-rw-r--r--backend/models/models.go14
-rw-r--r--backend/models/requests.go39
-rw-r--r--backend/models/responses.go64
11 files changed, 414 insertions, 418 deletions
diff --git a/backend/controllers/homeController.go b/backend/controllers/homeController.go
index c94590a..d1b99cb 100644
--- a/backend/controllers/homeController.go
+++ b/backend/controllers/homeController.go
@@ -10,6 +10,16 @@ import (
10 "github.com/pektezol/leastportalshub/backend/models" 10 "github.com/pektezol/leastportalshub/backend/models"
11) 11)
12 12
13type SearchResponse struct {
14 Players []models.UserShort `json:"players"`
15 Maps []models.MapShort `json:"maps"`
16}
17
18type RankingsResponse struct {
19 RankingsSP []models.UserRanking `json:"rankings_sp"`
20 RankingsMP []models.UserRanking `json:"rankings_mp"`
21}
22
13func Home(c *gin.Context) { 23func Home(c *gin.Context) {
14 user, exists := c.Get("user") 24 user, exists := c.Get("user")
15 if !exists { 25 if !exists {
@@ -26,7 +36,7 @@ func Home(c *gin.Context) {
26// @Description Get rankings of every player. 36// @Description Get rankings of every player.
27// @Tags rankings 37// @Tags rankings
28// @Produce json 38// @Produce json
29// @Success 200 {object} models.Response{data=models.RankingsResponse} 39// @Success 200 {object} models.Response{data=RankingsResponse}
30// @Failure 400 {object} models.Response 40// @Failure 400 {object} models.Response
31// @Router /rankings [get] 41// @Router /rankings [get]
32func Rankings(c *gin.Context) { 42func Rankings(c *gin.Context) {
@@ -116,7 +126,7 @@ func Rankings(c *gin.Context) {
116 c.JSON(http.StatusOK, models.Response{ 126 c.JSON(http.StatusOK, models.Response{
117 Success: true, 127 Success: true,
118 Message: "Successfully retrieved rankings.", 128 Message: "Successfully retrieved rankings.",
119 Data: models.RankingsResponse{ 129 Data: RankingsResponse{
120 RankingsSP: spRankings, 130 RankingsSP: spRankings,
121 RankingsMP: mpRankings, 131 RankingsMP: mpRankings,
122 }, 132 },
@@ -129,14 +139,14 @@ func Rankings(c *gin.Context) {
129// @Tags search 139// @Tags search
130// @Produce json 140// @Produce json
131// @Param q query string false "Search user or map name." 141// @Param q query string false "Search user or map name."
132// @Success 200 {object} models.Response{data=models.SearchResponse} 142// @Success 200 {object} models.Response{data=SearchResponse}
133// @Failure 400 {object} models.Response 143// @Failure 400 {object} models.Response
134// @Router /search [get] 144// @Router /search [get]
135func SearchWithQuery(c *gin.Context) { 145func SearchWithQuery(c *gin.Context) {
136 query := c.Query("q") 146 query := c.Query("q")
137 query = strings.ToLower(query) 147 query = strings.ToLower(query)
138 log.Println(query) 148 log.Println(query)
139 var response models.SearchResponse 149 var response SearchResponse
140 // Cache all maps for faster response 150 // Cache all maps for faster response
141 var maps = []models.MapShort{ 151 var maps = []models.MapShort{
142 {ID: 1, Name: "Container Ride"}, 152 {ID: 1, Name: "Container Ride"},
diff --git a/backend/controllers/loginController.go b/backend/controllers/loginController.go
index cc7a803..9d772a5 100644
--- a/backend/controllers/loginController.go
+++ b/backend/controllers/loginController.go
@@ -15,13 +15,17 @@ import (
15 "github.com/solovev/steam_go" 15 "github.com/solovev/steam_go"
16) 16)
17 17
18type LoginResponse struct {
19 Token string `json:"token"`
20}
21
18// Login 22// Login
19// 23//
20// @Description Get (redirect) login page for Steam auth. 24// @Description Get (redirect) login page for Steam auth.
21// @Tags login 25// @Tags login
22// @Accept json 26// @Accept json
23// @Produce json 27// @Produce json
24// @Success 200 {object} models.Response{data=models.LoginResponse} 28// @Success 200 {object} models.Response{data=LoginResponse}
25// @Failure 400 {object} models.Response 29// @Failure 400 {object} models.Response
26// @Router /login [get] 30// @Router /login [get]
27func Login(c *gin.Context) { 31func Login(c *gin.Context) {
@@ -85,7 +89,7 @@ func Login(c *gin.Context) {
85 // c.JSON(http.StatusOK, models.Response{ 89 // c.JSON(http.StatusOK, models.Response{
86 // Success: true, 90 // Success: true,
87 // Message: "Successfully generated token.", 91 // Message: "Successfully generated token.",
88 // Data: models.LoginResponse{ 92 // Data: LoginResponse{
89 // Token: tokenString, 93 // Token: tokenString,
90 // }, 94 // },
91 // }) 95 // })
@@ -99,7 +103,7 @@ func Login(c *gin.Context) {
99// @Tags auth 103// @Tags auth
100// @Produce json 104// @Produce json
101// 105//
102// @Success 200 {object} models.Response{data=models.LoginResponse} 106// @Success 200 {object} models.Response{data=LoginResponse}
103// @Failure 404 {object} models.Response 107// @Failure 404 {object} models.Response
104// @Router /token [get] 108// @Router /token [get]
105func GetCookie(c *gin.Context) { 109func GetCookie(c *gin.Context) {
@@ -111,7 +115,7 @@ func GetCookie(c *gin.Context) {
111 c.JSON(http.StatusOK, models.Response{ 115 c.JSON(http.StatusOK, models.Response{
112 Success: true, 116 Success: true,
113 Message: "Token cookie successfully retrieved.", 117 Message: "Token cookie successfully retrieved.",
114 Data: models.LoginResponse{ 118 Data: LoginResponse{
115 Token: cookie, 119 Token: cookie,
116 }, 120 },
117 }) 121 })
@@ -123,7 +127,7 @@ func GetCookie(c *gin.Context) {
123// @Tags auth 127// @Tags auth
124// @Produce json 128// @Produce json
125// 129//
126// @Success 200 {object} models.Response{data=models.LoginResponse} 130// @Success 200 {object} models.Response{data=LoginResponse}
127// @Failure 404 {object} models.Response 131// @Failure 404 {object} models.Response
128// @Router /token [delete] 132// @Router /token [delete]
129func DeleteCookie(c *gin.Context) { 133func DeleteCookie(c *gin.Context) {
@@ -136,7 +140,7 @@ func DeleteCookie(c *gin.Context) {
136 c.JSON(http.StatusOK, models.Response{ 140 c.JSON(http.StatusOK, models.Response{
137 Success: true, 141 Success: true,
138 Message: "Token cookie successfully deleted.", 142 Message: "Token cookie successfully deleted.",
139 Data: models.LoginResponse{ 143 Data: LoginResponse{
140 Token: cookie, 144 Token: cookie,
141 }, 145 },
142 }) 146 })
diff --git a/backend/controllers/mapController.go b/backend/controllers/mapController.go
index ebd65dd..0a324d6 100644
--- a/backend/controllers/mapController.go
+++ b/backend/controllers/mapController.go
@@ -9,18 +9,33 @@ import (
9 "github.com/pektezol/leastportalshub/backend/models" 9 "github.com/pektezol/leastportalshub/backend/models"
10) 10)
11 11
12type MapSummaryResponse struct {
13 Map models.Map `json:"map"`
14 Summary models.MapSummary `json:"summary"`
15}
16
17type ChaptersResponse struct {
18 Game models.Game `json:"game"`
19 Chapters []models.Chapter `json:"chapters"`
20}
21
22type ChapterMapsResponse struct {
23 Chapter models.Chapter `json:"chapter"`
24 Maps []models.MapShort `json:"maps"`
25}
26
12// GET Map Summary 27// GET Map Summary
13// 28//
14// @Description Get map summary with specified id. 29// @Description Get map summary with specified id.
15// @Tags maps 30// @Tags maps
16// @Produce json 31// @Produce json
17// @Param id path int true "Map ID" 32// @Param id path int true "Map ID"
18// @Success 200 {object} models.Response{data=models.MapSummaryResponse} 33// @Success 200 {object} models.Response{data=MapSummaryResponse}
19// @Failure 400 {object} models.Response 34// @Failure 400 {object} models.Response
20// @Router /maps/{id}/summary [get] 35// @Router /maps/{id}/summary [get]
21func FetchMapSummary(c *gin.Context) { 36func FetchMapSummary(c *gin.Context) {
22 id := c.Param("id") 37 id := c.Param("id")
23 response := models.MapSummaryResponse{Map: models.Map{}, Summary: models.MapSummary{Routes: []models.MapRoute{}}} 38 response := MapSummaryResponse{Map: models.Map{}, Summary: models.MapSummary{Routes: []models.MapRoute{}}}
24 intID, err := strconv.Atoi(id) 39 intID, err := strconv.Atoi(id)
25 if err != nil { 40 if err != nil {
26 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 41 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
@@ -220,7 +235,7 @@ func FetchGames(c *gin.Context) {
220// @Tags games & chapters 235// @Tags games & chapters
221// @Produce json 236// @Produce json
222// @Param id path int true "Game ID" 237// @Param id path int true "Game ID"
223// @Success 200 {object} models.Response{data=models.ChaptersResponse} 238// @Success 200 {object} models.Response{data=ChaptersResponse}
224// @Failure 400 {object} models.Response 239// @Failure 400 {object} models.Response
225// @Router /games/{id} [get] 240// @Router /games/{id} [get]
226func FetchChapters(c *gin.Context) { 241func FetchChapters(c *gin.Context) {
@@ -230,7 +245,7 @@ func FetchChapters(c *gin.Context) {
230 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 245 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
231 return 246 return
232 } 247 }
233 var response models.ChaptersResponse 248 var response ChaptersResponse
234 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) 249 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)
235 if err != nil { 250 if err != nil {
236 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 251 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
@@ -262,7 +277,7 @@ func FetchChapters(c *gin.Context) {
262// @Tags games & chapters 277// @Tags games & chapters
263// @Produce json 278// @Produce json
264// @Param id path int true "Chapter ID" 279// @Param id path int true "Chapter ID"
265// @Success 200 {object} models.Response{data=models.ChapterMapsResponse} 280// @Success 200 {object} models.Response{data=ChapterMapsResponse}
266// @Failure 400 {object} models.Response 281// @Failure 400 {object} models.Response
267// @Router /chapters/{id} [get] 282// @Router /chapters/{id} [get]
268func FetchChapterMaps(c *gin.Context) { 283func FetchChapterMaps(c *gin.Context) {
@@ -272,7 +287,7 @@ func FetchChapterMaps(c *gin.Context) {
272 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 287 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
273 return 288 return
274 } 289 }
275 var response models.ChapterMapsResponse 290 var response ChapterMapsResponse
276 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) 291 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)
277 if err != nil { 292 if err != nil {
278 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 293 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
diff --git a/backend/controllers/modController.go b/backend/controllers/modController.go
index e2add1f..7ce5cb4 100644
--- a/backend/controllers/modController.go
+++ b/backend/controllers/modController.go
@@ -3,21 +3,48 @@ package controllers
3import ( 3import (
4 "net/http" 4 "net/http"
5 "strconv" 5 "strconv"
6 "time"
6 7
7 "github.com/gin-gonic/gin" 8 "github.com/gin-gonic/gin"
8 "github.com/pektezol/leastportalshub/backend/database" 9 "github.com/pektezol/leastportalshub/backend/database"
9 "github.com/pektezol/leastportalshub/backend/models" 10 "github.com/pektezol/leastportalshub/backend/models"
10) 11)
11 12
13type CreateMapSummaryRequest struct {
14 CategoryID int `json:"category_id" binding:"required"`
15 Description string `json:"description" binding:"required"`
16 Showcase string `json:"showcase"`
17 UserName string `json:"user_name" binding:"required"`
18 ScoreCount *int `json:"score_count" binding:"required"`
19 RecordDate time.Time `json:"record_date" binding:"required"`
20}
21
22type EditMapSummaryRequest struct {
23 RouteID int `json:"route_id" binding:"required"`
24 Description string `json:"description" binding:"required"`
25 Showcase string `json:"showcase"`
26 UserName string `json:"user_name" binding:"required"`
27 ScoreCount *int `json:"score_count" binding:"required"`
28 RecordDate time.Time `json:"record_date" binding:"required"`
29}
30
31type DeleteMapSummaryRequest struct {
32 RouteID int `json:"route_id" binding:"required"`
33}
34
35type EditMapImageRequest struct {
36 Image string `json:"image" binding:"required"`
37}
38
12// POST Map Summary 39// POST Map Summary
13// 40//
14// @Description Create map summary with specified map id. 41// @Description Create map summary with specified map id.
15// @Tags maps 42// @Tags maps
16// @Produce json 43// @Produce json
17// @Param Authorization header string true "JWT Token" 44// @Param Authorization header string true "JWT Token"
18// @Param id path int true "Map ID" 45// @Param id path int true "Map ID"
19// @Param request body models.CreateMapSummaryRequest true "Body" 46// @Param request body CreateMapSummaryRequest true "Body"
20// @Success 200 {object} models.Response{data=models.CreateMapSummaryRequest} 47// @Success 200 {object} models.Response{data=CreateMapSummaryRequest}
21// @Failure 400 {object} models.Response 48// @Failure 400 {object} models.Response
22// @Router /maps/{id}/summary [post] 49// @Router /maps/{id}/summary [post]
23func CreateMapSummary(c *gin.Context) { 50func CreateMapSummary(c *gin.Context) {
@@ -44,7 +71,7 @@ func CreateMapSummary(c *gin.Context) {
44 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 71 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
45 return 72 return
46 } 73 }
47 var request models.CreateMapSummaryRequest 74 var request CreateMapSummaryRequest
48 if err := c.BindJSON(&request); err != nil { 75 if err := c.BindJSON(&request); err != nil {
49 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 76 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
50 return 77 return
@@ -100,10 +127,10 @@ func CreateMapSummary(c *gin.Context) {
100// @Description Edit map summary with specified map id. 127// @Description Edit map summary with specified map id.
101// @Tags maps 128// @Tags maps
102// @Produce json 129// @Produce json
103// @Param Authorization header string true "JWT Token" 130// @Param Authorization header string true "JWT Token"
104// @Param id path int true "Map ID" 131// @Param id path int true "Map ID"
105// @Param request body models.EditMapSummaryRequest true "Body" 132// @Param request body EditMapSummaryRequest true "Body"
106// @Success 200 {object} models.Response{data=models.EditMapSummaryRequest} 133// @Success 200 {object} models.Response{data=EditMapSummaryRequest}
107// @Failure 400 {object} models.Response 134// @Failure 400 {object} models.Response
108// @Router /maps/{id}/summary [put] 135// @Router /maps/{id}/summary [put]
109func EditMapSummary(c *gin.Context) { 136func EditMapSummary(c *gin.Context) {
@@ -130,7 +157,7 @@ func EditMapSummary(c *gin.Context) {
130 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 157 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
131 return 158 return
132 } 159 }
133 var request models.EditMapSummaryRequest 160 var request EditMapSummaryRequest
134 if err := c.BindJSON(&request); err != nil { 161 if err := c.BindJSON(&request); err != nil {
135 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 162 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
136 return 163 return
@@ -186,10 +213,10 @@ func EditMapSummary(c *gin.Context) {
186// @Description Delete map summary with specified map id. 213// @Description Delete map summary with specified map id.
187// @Tags maps 214// @Tags maps
188// @Produce json 215// @Produce json
189// @Param Authorization header string true "JWT Token" 216// @Param Authorization header string true "JWT Token"
190// @Param id path int true "Map ID" 217// @Param id path int true "Map ID"
191// @Param request body models.DeleteMapSummaryRequest true "Body" 218// @Param request body DeleteMapSummaryRequest true "Body"
192// @Success 200 {object} models.Response{data=models.DeleteMapSummaryRequest} 219// @Success 200 {object} models.Response{data=DeleteMapSummaryRequest}
193// @Failure 400 {object} models.Response 220// @Failure 400 {object} models.Response
194// @Router /maps/{id}/summary [delete] 221// @Router /maps/{id}/summary [delete]
195func DeleteMapSummary(c *gin.Context) { 222func DeleteMapSummary(c *gin.Context) {
@@ -216,7 +243,7 @@ func DeleteMapSummary(c *gin.Context) {
216 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 243 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
217 return 244 return
218 } 245 }
219 var request models.DeleteMapSummaryRequest 246 var request DeleteMapSummaryRequest
220 if err := c.BindJSON(&request); err != nil { 247 if err := c.BindJSON(&request); err != nil {
221 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 248 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
222 return 249 return
@@ -276,10 +303,10 @@ func DeleteMapSummary(c *gin.Context) {
276// @Description Edit map image with specified map id. 303// @Description Edit map image with specified map id.
277// @Tags maps 304// @Tags maps
278// @Produce json 305// @Produce json
279// @Param Authorization header string true "JWT Token" 306// @Param Authorization header string true "JWT Token"
280// @Param id path int true "Map ID" 307// @Param id path int true "Map ID"
281// @Param request body models.EditMapImageRequest true "Body" 308// @Param request body EditMapImageRequest true "Body"
282// @Success 200 {object} models.Response{data=models.EditMapImageRequest} 309// @Success 200 {object} models.Response{data=EditMapImageRequest}
283// @Failure 400 {object} models.Response 310// @Failure 400 {object} models.Response
284// @Router /maps/{id}/image [put] 311// @Router /maps/{id}/image [put]
285func EditMapImage(c *gin.Context) { 312func EditMapImage(c *gin.Context) {
@@ -306,7 +333,7 @@ func EditMapImage(c *gin.Context) {
306 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 333 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
307 return 334 return
308 } 335 }
309 var request models.EditMapImageRequest 336 var request EditMapImageRequest
310 if err := c.BindJSON(&request); err != nil { 337 if err := c.BindJSON(&request); err != nil {
311 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 338 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
312 return 339 return
diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go
index 951be41..d141fc3 100644
--- a/backend/controllers/recordController.go
+++ b/backend/controllers/recordController.go
@@ -19,6 +19,18 @@ import (
19 "google.golang.org/api/drive/v3" 19 "google.golang.org/api/drive/v3"
20) 20)
21 21
22type RecordRequest struct {
23 HostDemo *multipart.FileHeader `json:"host_demo" form:"host_demo" binding:"required" swaggerignore:"true"`
24 PartnerDemo *multipart.FileHeader `json:"partner_demo" form:"partner_demo" swaggerignore:"true"`
25 IsPartnerOrange bool `json:"is_partner_orange" form:"is_partner_orange"`
26 PartnerID string `json:"partner_id" form:"partner_id"`
27}
28
29type RecordResponse struct {
30 ScoreCount int `json:"score_count"`
31 ScoreTime int `json:"score_time"`
32}
33
22// POST Record 34// POST Record
23// 35//
24// @Description Post record with demo of a specific map. 36// @Description Post record with demo of a specific map.
@@ -31,7 +43,7 @@ import (
31// @Param partner_demo formData file false "Partner Demo" 43// @Param partner_demo formData file false "Partner Demo"
32// @Param is_partner_orange formData boolean false "Is Partner Orange" 44// @Param is_partner_orange formData boolean false "Is Partner Orange"
33// @Param partner_id formData string false "Partner ID" 45// @Param partner_id formData string false "Partner ID"
34// @Success 200 {object} models.Response{data=models.RecordResponse} 46// @Success 200 {object} models.Response{data=RecordResponse}
35// @Failure 400 {object} models.Response 47// @Failure 400 {object} models.Response
36// @Failure 401 {object} models.Response 48// @Failure 401 {object} models.Response
37// @Router /maps/{id}/record [post] 49// @Router /maps/{id}/record [post]
@@ -61,7 +73,7 @@ func CreateRecordWithDemo(c *gin.Context) {
61 isCoop = true 73 isCoop = true
62 } 74 }
63 // Get record request 75 // Get record request
64 var record models.RecordRequest 76 var record RecordRequest
65 if err := c.ShouldBind(&record); err != nil { 77 if err := c.ShouldBind(&record); err != nil {
66 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 78 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
67 return 79 return
@@ -183,7 +195,7 @@ func CreateRecordWithDemo(c *gin.Context) {
183 c.JSON(http.StatusOK, models.Response{ 195 c.JSON(http.StatusOK, models.Response{
184 Success: true, 196 Success: true,
185 Message: "Successfully created record.", 197 Message: "Successfully created record.",
186 Data: models.RecordResponse{ScoreCount: hostDemoScoreCount, ScoreTime: hostDemoScoreTime}, 198 Data: RecordResponse{ScoreCount: hostDemoScoreCount, ScoreTime: hostDemoScoreTime},
187 }) 199 })
188} 200}
189 201
@@ -253,6 +265,7 @@ func serviceAccount() *http.Client {
253 return client 265 return client
254} 266}
255 267
268// Create Gdrive file
256func createFile(service *drive.Service, name string, mimeType string, content io.Reader, parentId string) (*drive.File, error) { 269func createFile(service *drive.Service, name string, mimeType string, content io.Reader, parentId string) (*drive.File, error) {
257 f := &drive.File{ 270 f := &drive.File{
258 MimeType: mimeType, 271 MimeType: mimeType,
@@ -269,6 +282,7 @@ func createFile(service *drive.Service, name string, mimeType string, content io
269 return file, nil 282 return file, nil
270} 283}
271 284
285// Delete Gdrive file
272func deleteFile(service *drive.Service, fileId string) { 286func deleteFile(service *drive.Service, fileId string) {
273 service.Files.Delete(fileId) 287 service.Files.Delete(fileId)
274} 288}
diff --git a/backend/controllers/userController.go b/backend/controllers/userController.go
index 6aa77fc..0dae155 100644
--- a/backend/controllers/userController.go
+++ b/backend/controllers/userController.go
@@ -11,6 +11,21 @@ import (
11 "github.com/pektezol/leastportalshub/backend/models" 11 "github.com/pektezol/leastportalshub/backend/models"
12) 12)
13 13
14type ProfileResponse struct {
15 Profile bool `json:"profile"`
16 SteamID string `json:"steam_id"`
17 UserName string `json:"user_name"`
18 AvatarLink string `json:"avatar_link"`
19 CountryCode string `json:"country_code"`
20 ScoresSP []ScoreResponse `json:"scores_sp"`
21 ScoresMP []ScoreResponse `json:"scores_mp"`
22}
23
24type ScoreResponse struct {
25 MapID int `json:"map_id"`
26 Records any `json:"records"`
27}
28
14// GET Profile 29// GET Profile
15// 30//
16// @Description Get profile page of session user. 31// @Description Get profile page of session user.
@@ -18,7 +33,7 @@ import (
18// @Accept json 33// @Accept json
19// @Produce json 34// @Produce json
20// @Param Authorization header string true "JWT Token" 35// @Param Authorization header string true "JWT Token"
21// @Success 200 {object} models.Response{data=models.ProfileResponse} 36// @Success 200 {object} models.Response{data=ProfileResponse}
22// @Failure 400 {object} models.Response 37// @Failure 400 {object} models.Response
23// @Failure 401 {object} models.Response 38// @Failure 401 {object} models.Response
24// @Router /profile [get] 39// @Router /profile [get]
@@ -30,7 +45,7 @@ func Profile(c *gin.Context) {
30 return 45 return
31 } 46 }
32 // Retrieve singleplayer records 47 // Retrieve singleplayer records
33 var scoresSP []models.ScoreResponse 48 var scoresSP []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` 49 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) 50 rows, err := database.DB.Query(sql, user.(models.User).SteamID)
36 if err != nil { 51 if err != nil {
@@ -50,13 +65,13 @@ func Profile(c *gin.Context) {
50 // New map 65 // New map
51 recordsSP = []models.RecordSP{} 66 recordsSP = []models.RecordSP{}
52 recordsSP = append(recordsSP, record) 67 recordsSP = append(recordsSP, record)
53 scoresSP = append(scoresSP, models.ScoreResponse{ 68 scoresSP = append(scoresSP, ScoreResponse{
54 MapID: mapID, 69 MapID: mapID,
55 Records: recordsSP, 70 Records: recordsSP,
56 }) 71 })
57 } 72 }
58 // Retrieve multiplayer records 73 // Retrieve multiplayer records
59 var scoresMP []models.ScoreResponse 74 var scoresMP []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 75 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` 76 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) 77 rows, err = database.DB.Query(sql, user.(models.User).SteamID, user.(models.User).SteamID)
@@ -77,7 +92,7 @@ func Profile(c *gin.Context) {
77 // New map 92 // New map
78 recordsMP = []models.RecordMP{} 93 recordsMP = []models.RecordMP{}
79 recordsMP = append(recordsMP, record) 94 recordsMP = append(recordsMP, record)
80 scoresMP = append(scoresMP, models.ScoreResponse{ 95 scoresMP = append(scoresMP, ScoreResponse{
81 MapID: mapID, 96 MapID: mapID,
82 Records: recordsMP, 97 Records: recordsMP,
83 }) 98 })
@@ -85,7 +100,7 @@ func Profile(c *gin.Context) {
85 c.JSON(http.StatusOK, models.Response{ 100 c.JSON(http.StatusOK, models.Response{
86 Success: true, 101 Success: true,
87 Message: "Successfully retrieved user scores.", 102 Message: "Successfully retrieved user scores.",
88 Data: models.ProfileResponse{ 103 Data: ProfileResponse{
89 Profile: true, 104 Profile: true,
90 SteamID: user.(models.User).SteamID, 105 SteamID: user.(models.User).SteamID,
91 UserName: user.(models.User).UserName, 106 UserName: user.(models.User).UserName,
@@ -105,7 +120,7 @@ func Profile(c *gin.Context) {
105// @Accept json 120// @Accept json
106// @Produce json 121// @Produce json
107// @Param id path int true "User ID" 122// @Param id path int true "User ID"
108// @Success 200 {object} models.Response{data=models.ProfileResponse} 123// @Success 200 {object} models.Response{data=ProfileResponse}
109// @Failure 400 {object} models.Response 124// @Failure 400 {object} models.Response
110// @Failure 404 {object} models.Response 125// @Failure 404 {object} models.Response
111// @Router /users/{id} [get] 126// @Router /users/{id} [get]
@@ -132,7 +147,7 @@ func FetchUser(c *gin.Context) {
132 return 147 return
133 } 148 }
134 // Retrieve singleplayer records 149 // Retrieve singleplayer records
135 var scoresSP []models.ScoreResponse 150 var scoresSP []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` 151 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) 152 rows, err := database.DB.Query(sql, user.SteamID)
138 if err != nil { 153 if err != nil {
@@ -152,13 +167,13 @@ func FetchUser(c *gin.Context) {
152 // New map 167 // New map
153 recordsSP = []models.RecordSP{} 168 recordsSP = []models.RecordSP{}
154 recordsSP = append(recordsSP, record) 169 recordsSP = append(recordsSP, record)
155 scoresSP = append(scoresSP, models.ScoreResponse{ 170 scoresSP = append(scoresSP, ScoreResponse{
156 MapID: mapID, 171 MapID: mapID,
157 Records: recordsSP, 172 Records: recordsSP,
158 }) 173 })
159 } 174 }
160 // Retrieve multiplayer records 175 // Retrieve multiplayer records
161 var scoresMP []models.ScoreResponse 176 var scoresMP []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 177 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` 178 WHERE host_id = $1 OR partner_id = $2 ORDER BY map_id`
164 rows, err = database.DB.Query(sql, user.SteamID, user.SteamID) 179 rows, err = database.DB.Query(sql, user.SteamID, user.SteamID)
@@ -179,7 +194,7 @@ func FetchUser(c *gin.Context) {
179 // New map 194 // New map
180 recordsMP = []models.RecordMP{} 195 recordsMP = []models.RecordMP{}
181 recordsMP = append(recordsMP, record) 196 recordsMP = append(recordsMP, record)
182 scoresMP = append(scoresMP, models.ScoreResponse{ 197 scoresMP = append(scoresMP, ScoreResponse{
183 MapID: mapID, 198 MapID: mapID,
184 Records: recordsMP, 199 Records: recordsMP,
185 }) 200 })
@@ -187,7 +202,7 @@ func FetchUser(c *gin.Context) {
187 c.JSON(http.StatusOK, models.Response{ 202 c.JSON(http.StatusOK, models.Response{
188 Success: true, 203 Success: true,
189 Message: "Successfully retrieved user scores.", 204 Message: "Successfully retrieved user scores.",
190 Data: models.ProfileResponse{ 205 Data: ProfileResponse{
191 Profile: true, 206 Profile: true,
192 SteamID: user.SteamID, 207 SteamID: user.SteamID,
193 UserName: user.UserName, 208 UserName: user.UserName,
@@ -207,7 +222,7 @@ func FetchUser(c *gin.Context) {
207// @Accept json 222// @Accept json
208// @Produce json 223// @Produce json
209// @Param Authorization header string true "JWT Token" 224// @Param Authorization header string true "JWT Token"
210// @Success 200 {object} models.Response{data=models.ProfileResponse} 225// @Success 200 {object} models.Response{data=ProfileResponse}
211// @Failure 400 {object} models.Response 226// @Failure 400 {object} models.Response
212// @Failure 401 {object} models.Response 227// @Failure 401 {object} models.Response
213// @Router /profile [post] 228// @Router /profile [post]
@@ -233,7 +248,7 @@ func UpdateUser(c *gin.Context) {
233 c.JSON(http.StatusOK, models.Response{ 248 c.JSON(http.StatusOK, models.Response{
234 Success: true, 249 Success: true,
235 Message: "Successfully updated user.", 250 Message: "Successfully updated user.",
236 Data: models.ProfileResponse{ 251 Data: ProfileResponse{
237 Profile: true, 252 Profile: true,
238 SteamID: user.(models.User).SteamID, 253 SteamID: user.(models.User).SteamID,
239 UserName: profile.PersonaName, 254 UserName: profile.PersonaName,
diff --git a/backend/database/history.sql b/backend/database/history.sql
index 320d72f..b30d10d 100644
--- a/backend/database/history.sql
+++ b/backend/database/history.sql
@@ -1,279 +1,279 @@
1INSERT INTO map_history(map_id,category_id,user_name,score_count,record_date) VALUES 1INSERT INTO map_history(map_id,category_id,user_name,score_count,record_date) VALUES
2-- Portal 2 Singleplayer 2-- Portal 2 Singleplayer
3-- 1 3-- 1
4(3,1,'slmid1995',3,'2011-10-05 00:00:00'), 4(3,1,'slmid1995',3,'2011-10-05'),
5(3,1,'LookLikeAKango',1,'2011-10-06 00:00:00'), 5(3,1,'LookLikeAKango',1,'2011-10-06'),
6(3,1,'Bananasaurus Rex',0,'2011-10-24 00:00:00'), 6(3,1,'Bananasaurus Rex',0,'2011-10-24'),
7(4,1,'Tyronis',1,'2011-10-05 00:00:00'), 7(4,1,'Tyronis',1,'2011-10-05'),
8(4,1,'Krzyhau',0,'2019-05-10 00:00:00'), 8(4,1,'Krzyhau',0,'2019-05-10'),
9(5,1,'LookLikeAKango',2,'2011-10-05 00:00:00'), 9(5,1,'LookLikeAKango',2,'2011-10-05'),
10(5,1,'Jetwash',1,'2013-12-03 00:00:00'), 10(5,1,'Jetwash',1,'2013-12-03'),
11(6,1,'Stimich',4,'2011-10-08 00:00:00'), 11(6,1,'Stimich',4,'2011-10-08'),
12(6,1,'aepaePolakrn',3,'2011-10-19 00:00:00'), 12(6,1,'aepaePolakrn',3,'2011-10-19'),
13(6,1,'Krzyhau',2,'2020-10-10 00:00:00'), 13(6,1,'Krzyhau',2,'2020-10-10'),
14(9,1,'slmid1995',4,'2011-10-05 00:00:00'), 14(9,1,'slmid1995',4,'2011-10-05'),
15(9,1,'Jokie',3,'2011-10-05 00:00:00'), 15(9,1,'Jokie',3,'2011-10-05'),
16(9,1,'Tyronis',2,'2011-10-05 00:00:00'), 16(9,1,'Tyronis',2,'2011-10-05'),
17(9,1,'sicklebrick',0,'2013-03-13 00:00:00'), 17(9,1,'sicklebrick',0,'2013-03-13'),
18-- 2 18-- 2
19(10,1,'Paraxade0',2,'2011-04-21 00:00:00'), 19(10,1,'Paraxade0',2,'2011-04-21'),
20(10,1,'PerOculos',0,'2011-04-21 00:00:00'), 20(10,1,'PerOculos',0,'2011-04-21'),
21(11,1,'Tyronis',2,'2011-10-05 00:00:00'), 21(11,1,'Tyronis',2,'2011-10-05'),
22(11,1,'Krzyhau',0,'2018-06-09 00:00:00'), 22(11,1,'Krzyhau',0,'2018-06-09'),
23(12,1,'slmid1995',2,'2011-10-04 00:00:00'), 23(12,1,'slmid1995',2,'2011-10-04'),
24(13,1,'LookLikeAKango',3,'2011-10-05 00:00:00'), 24(13,1,'LookLikeAKango',3,'2011-10-05'),
25(13,1,'Imanex',2,'2011-12-08 00:00:00'), 25(13,1,'Imanex',2,'2011-12-08'),
26(13,1,'jyjey',0,'2012-08-22 00:00:00'), 26(13,1,'jyjey',0,'2012-08-22'),
27(15,1,'Tyronis',2,'2011-10-05 00:00:00'), 27(15,1,'Tyronis',2,'2011-10-05'),
28(16,1,'LookLikeAKango',2,'2011-10-05 00:00:00'), 28(16,1,'LookLikeAKango',2,'2011-10-05'),
29(16,1,'jyjey',0,'2012-08-25 00:00:00'), 29(16,1,'jyjey',0,'2012-08-25'),
30(17,1,'rocoty',2,'2011-10-05 00:00:00'), 30(17,1,'rocoty',2,'2011-10-05'),
31(17,1,'Nidboj132',0,'2023-02-05 00:00:00'), 31(17,1,'Nidboj132',0,'2023-02-05'),
32-- 3 32-- 3
33(18,1,'The Last Tofus',5,'2011-05-08 00:00:00'), 33(18,1,'The Last Tofus',5,'2011-05-08'),
34(18,1,'Schlepian',4,'2011-10-08 00:00:00'), 34(18,1,'Schlepian',4,'2011-10-08'),
35(18,1,'szeimartin',3,'2013-10-08 00:00:00'), 35(18,1,'szeimartin',3,'2013-10-08'),
36(18,1,'Krzyhau',2,'2020-05-15 00:00:00'), 36(18,1,'Krzyhau',2,'2020-05-15'),
37(18,1,'Krzyhau',0,'2022-07-02 00:00:00'), 37(18,1,'Krzyhau',0,'2022-07-02'),
38(19,1,'LookLikeAKango',2,'2011-10-06 00:00:00'), 38(19,1,'LookLikeAKango',2,'2011-10-06'),
39(20,1,'Djinndrache',5,'2011-10-20 00:00:00'), 39(20,1,'Djinndrache',5,'2011-10-20'),
40(20,1,'Schlepian',4,'2011-10-30 00:00:00'), 40(20,1,'Schlepian',4,'2011-10-30'),
41(20,1,'Jetwash',3,'2014-09-04 00:00:00'), 41(20,1,'Jetwash',3,'2014-09-04'),
42(20,1,'Krzyhau',2,'2022-04-24 00:00:00'), 42(20,1,'Krzyhau',2,'2022-04-24'),
43(21,1,'LookLikeAKango',4,'2011-10-06 00:00:00'), 43(21,1,'LookLikeAKango',4,'2011-10-06'),
44(21,1,'ncla',2,'2011-10-30 00:00:00'), 44(21,1,'ncla',2,'2011-10-30'),
45(21,1,'PerOculos',0,'2019-07-08 00:00:00'), 45(21,1,'PerOculos',0,'2019-07-08'),
46(22,1,'Tyronis',0,'2011-10-05 00:00:00'), 46(22,1,'Tyronis',0,'2011-10-05'),
47(23,1,'LookLikeAKango',2,'2011-10-06 00:00:00'), 47(23,1,'LookLikeAKango',2,'2011-10-06'),
48(23,1,'Krzyhau',0,'2018-08-01 00:00:00'), 48(23,1,'Krzyhau',0,'2018-08-01'),
49(24,1,'LeviHB',0,'2011-04-30 00:00:00'), 49(24,1,'LeviHB',0,'2011-04-30'),
50(25,1,'Tyronis',0,'2011-10-06 00:00:00'), 50(25,1,'Tyronis',0,'2011-10-06'),
51(26,1,'Schlepian',3,'2011-10-30 00:00:00'), 51(26,1,'Schlepian',3,'2011-10-30'),
52(26,1,'Tyronis',2,'2012-01-08 00:00:00'), 52(26,1,'Tyronis',2,'2012-01-08'),
53(26,1,'PerOculos',0,'2016-06-08 00:00:00'), 53(26,1,'PerOculos',0,'2016-06-08'),
54-- 4 54-- 4
55(27,1,'LeviHB',2,'2011-05-01 00:00:00'), 55(27,1,'LeviHB',2,'2011-05-01'),
56(27,1,'PerOculos',0,'2020-07-13 00:00:00'), 56(27,1,'PerOculos',0,'2020-07-13'),
57(28,1,'LeviHB',7,'2011-05-01 00:00:00'), 57(28,1,'LeviHB',7,'2011-05-01'),
58(28,1,'Andy M.J.',2,'2011-10-07 00:00:00'), 58(28,1,'Andy M.J.',2,'2011-10-07'),
59(28,1,'Krzyhau',0,'2018-05-19 00:00:00'), 59(28,1,'Krzyhau',0,'2018-05-19'),
60(29,1,'LeviHB',0,'2011-05-01 00:00:00'), 60(29,1,'LeviHB',0,'2011-05-01'),
61(30,1,'Schlepian',2,'2011-10-30 00:00:00'), 61(30,1,'Schlepian',2,'2011-10-30'),
62(31,1,'Tyronis',0,'2011-10-06 00:00:00'), 62(31,1,'Tyronis',0,'2011-10-06'),
63-- 5 63-- 5
64(32,1,'Tyronis',6,'2011-10-21 00:00:00'), 64(32,1,'Tyronis',6,'2011-10-21'),
65(32,1,'Nidboj132',5,'2022-04-24 00:00:00'), 65(32,1,'Nidboj132',5,'2022-04-24'),
66(33,1,'Tyronis',7,'2011-10-06 00:00:00'), 66(33,1,'Tyronis',7,'2011-10-06'),
67(33,1,'ISimmo',5,'2011-11-02 00:00:00'), 67(33,1,'ISimmo',5,'2011-11-02'),
68(33,1,'PerOculos',4,'2017-05-30 00:00:00'), 68(33,1,'PerOculos',4,'2017-05-30'),
69(34,1,'Schlepian',3,'2011-11-01 00:00:00'), 69(34,1,'Schlepian',3,'2011-11-01'),
70(34,1,'Krzyhau',2,'2020-10-14 00:00:00'), 70(34,1,'Krzyhau',2,'2020-10-14'),
71(34,1,'zach',0,'2022-11-02 00:00:00'), 71(34,1,'zach',0,'2022-11-02'),
72(35,1,'Krank',2,'2012-07-28 00:00:00'), 72(35,1,'Krank',2,'2012-07-28'),
73-- 6 73-- 6
74(36,1,'Tyronis',6,'2011-10-06 00:00:00'), 74(36,1,'Tyronis',6,'2011-10-06'),
75(36,1,'CalmlyFrenetic',5,'2011-10-09 00:00:00'), 75(36,1,'CalmlyFrenetic',5,'2011-10-09'),
76(36,1,'sicklebrick',4,'2012-09-13 00:00:00'), 76(36,1,'sicklebrick',4,'2012-09-13'),
77(36,1,'Nidboj132',2,'2023-03-04 00:00:00'), 77(36,1,'Nidboj132',2,'2023-03-04'),
78(37,1,'LookLikeAKango',7,'2011-10-06 00:00:00'), 78(37,1,'LookLikeAKango',7,'2011-10-06'),
79(37,1,'Schlepian',6,'2011-11-01 00:00:00'), 79(37,1,'Schlepian',6,'2011-11-01'),
80(37,1,'Tyronis',5,'2012-01-28 00:00:00'), 80(37,1,'Tyronis',5,'2012-01-28'),
81(37,1,'Nidboj132',4,'2021-08-22 00:00:00'), 81(37,1,'Nidboj132',4,'2021-08-22'),
82(38,1,'Andy M.J.',2,'2011-10-06 00:00:00'), 82(38,1,'Andy M.J.',2,'2011-10-06'),
83(38,1,'Sanguine Dagger',0,'2012-03-19 00:00:00'), 83(38,1,'Sanguine Dagger',0,'2012-03-19'),
84(39,1,'Lambda Core',6,'2011-05-13 00:00:00'), 84(39,1,'Lambda Core',6,'2011-05-13'),
85(39,1,'The Last Tofus',5,'2011-05-13 00:00:00'), 85(39,1,'The Last Tofus',5,'2011-05-13'),
86(39,1,'LookLikeAKango',4,'2011-10-16 00:00:00'), 86(39,1,'LookLikeAKango',4,'2011-10-16'),
87(39,1,'Kittaye',3,'2013-03-25 00:00:00'), 87(39,1,'Kittaye',3,'2013-03-25'),
88(40,1,'LookLikeAKango',7,'2011-10-07 00:00:00'), 88(40,1,'LookLikeAKango',7,'2011-10-07'),
89(40,1,'Schlepian',6,'2011-11-05 00:00:00'), 89(40,1,'Schlepian',6,'2011-11-05'),
90(40,1,'Kittaye',4,'2013-04-01 00:00:00'), 90(40,1,'Kittaye',4,'2013-04-01'),
91(40,1,'Kittaye',3,'2014-09-13 00:00:00'), 91(40,1,'Kittaye',3,'2014-09-13'),
92(40,1,'szeimartin',2,'2014-09-13 00:00:00'), 92(40,1,'szeimartin',2,'2014-09-13'),
93(40,1,'Kittaye',0,'2014-09-15 00:00:00'), 93(40,1,'Kittaye',0,'2014-09-15'),
94(41,1,'CalmlyFrenetic',7,'2011-10-09 00:00:00'), 94(41,1,'CalmlyFrenetic',7,'2011-10-09'),
95(41,1,'Jaso',6,'2011-10-11 00:00:00'), 95(41,1,'Jaso',6,'2011-10-11'),
96(41,1,'Krank',5,'2012-07-17 00:00:00'), 96(41,1,'Krank',5,'2012-07-17'),
97-- 7 97-- 7
98(42,1,'LookLikeAKango',4,'2011-05-17 00:00:00'), 98(42,1,'LookLikeAKango',4,'2011-05-17'),
99(42,1,'ISimmo',2,'2011-11-07 00:00:00'), 99(42,1,'ISimmo',2,'2011-11-07'),
100(43,1,'lmao4ever',5,'2011-10-30 00:00:00'), 100(43,1,'lmao4ever',5,'2011-10-30'),
101(43,1,'Jaso',2,'2011-11-09 00:00:00'), 101(43,1,'Jaso',2,'2011-11-09'),
102(43,1,'feliser',0,'2022-06-26 00:00:00'), 102(43,1,'feliser',0,'2022-06-26'),
103(44,1,'LookLikeAKango',18,'2011-10-07 00:00:00'), 103(44,1,'LookLikeAKango',18,'2011-10-07'),
104(44,1,'Tyronis',13,'2011-10-30 00:00:00'), 104(44,1,'Tyronis',13,'2011-10-30'),
105(44,1,'Tyronis',12,'2011-11-10 00:00:00'), 105(44,1,'Tyronis',12,'2011-11-10'),
106(44,1,'Jetwash',11,'2017-06-12 00:00:00'), 106(44,1,'Jetwash',11,'2017-06-12'),
107(44,1,'Krzyhau',9,'2022-01-02 00:00:00'), 107(44,1,'Krzyhau',9,'2022-01-02'),
108(45,1,'LookLikeAKango',23,'2011-10-08 00:00:00'), 108(45,1,'LookLikeAKango',23,'2011-10-08'),
109(45,1,'CalmlyFrenetic',22,'2011-10-09 00:00:00'), 109(45,1,'CalmlyFrenetic',22,'2011-10-09'),
110(45,1,'cgreactor',17,'2011-10-09 00:00:00'), 110(45,1,'cgreactor',17,'2011-10-09'),
111(45,1,'CalmlyFrenetic',16,'2011-10-10 00:00:00'), 111(45,1,'CalmlyFrenetic',16,'2011-10-10'),
112(45,1,'LookLikeAKango',15,'2011-10-19 00:00:00'), 112(45,1,'LookLikeAKango',15,'2011-10-19'),
113(45,1,'Jaso',12,'2012-07-19 00:00:00'), 113(45,1,'Jaso',12,'2012-07-19'),
114(45,1,'Krank',10,'2013-01-31 00:00:00'), 114(45,1,'Krank',10,'2013-01-31'),
115(45,1,'Kittaye',7,'2013-04-04 00:00:00'), 115(45,1,'Kittaye',7,'2013-04-04'),
116(45,1,'PerOculos',4,'2014-09-13 00:00:00'), 116(45,1,'PerOculos',4,'2014-09-13'),
117-- 8 117-- 8
118(46,1,'sparkle1princess',6,'2012-03-24 00:00:00'), 118(46,1,'sparkle1princess',6,'2012-03-24'),
119(46,1,'Krzyhau',2,'2019-11-21 00:00:00'), 119(46,1,'Krzyhau',2,'2019-11-21'),
120(47,1,'holydevel',2,'2011-10-06 00:00:00'), 120(47,1,'holydevel',2,'2011-10-06'),
121(47,1,'JesusCatFace',0,'2015-01-16 00:00:00'), 121(47,1,'JesusCatFace',0,'2015-01-16'),
122(48,1,'LookLikeAKango',5,'2011-10-08 00:00:00'), 122(48,1,'LookLikeAKango',5,'2011-10-08'),
123(48,1,'Tyronis',2,'2011-10-08 00:00:00'), 123(48,1,'Tyronis',2,'2011-10-08'),
124(48,1,'adzicents',0,'2011-10-09 00:00:00'), 124(48,1,'adzicents',0,'2011-10-09'),
125(49,1,'adzicents',4,'2011-10-07 00:00:00'), 125(49,1,'adzicents',4,'2011-10-07'),
126(49,1,'Schlepian',2,'2011-10-08 00:00:00'), 126(49,1,'Schlepian',2,'2011-10-08'),
127(49,1,'Nidboj132',0,'2022-09-26 00:00:00'), 127(49,1,'Nidboj132',0,'2022-09-26'),
128(50,1,'LookLikeAKango',4,'2011-10-08 00:00:00'), 128(50,1,'LookLikeAKango',4,'2011-10-08'),
129(50,1,'Tyronis',2,'2011-10-11 00:00:00'), 129(50,1,'Tyronis',2,'2011-10-11'),
130(50,1,'sicklebrick',0,'2013-03-20 00:00:00'), 130(50,1,'sicklebrick',0,'2013-03-20'),
131(51,1,'Andy M.J.',3,'2011-10-08 00:00:00'), 131(51,1,'Andy M.J.',3,'2011-10-08'),
132(51,1,'LookLikeAKango',2,'2011-10-20 00:00:00'), 132(51,1,'LookLikeAKango',2,'2011-10-20'),
133(52,1,'Jaso',0,'2011-10-10 00:00:00'), 133(52,1,'Jaso',0,'2011-10-10'),
134(53,1,'LookLikeAKango',9,'2011-10-08 00:00:00'), 134(53,1,'LookLikeAKango',9,'2011-10-08'),
135(53,1,'LookLikeAKango',2,'2011-10-20 00:00:00'), 135(53,1,'LookLikeAKango',2,'2011-10-20'),
136(53,1,'Schlepian',0,'2011-11-06 00:00:00'), 136(53,1,'Schlepian',0,'2011-11-06'),
137(54,1,'LookLikeAKango',7,'2011-06-01 00:00:00'), 137(54,1,'LookLikeAKango',7,'2011-06-01'),
138(54,1,'Jaso',6,'2011-10-09 00:00:00'), 138(54,1,'Jaso',6,'2011-10-09'),
139(54,1,'Schlepian',5,'2011-11-06 00:00:00'), 139(54,1,'Schlepian',5,'2011-11-06'),
140(54,1,'Spyrunite',4,'2012-08-30 00:00:00'), 140(54,1,'Spyrunite',4,'2012-08-30'),
141(54,1,'Krzyhau',3,'2019-04-22 00:00:00'), 141(54,1,'Krzyhau',3,'2019-04-22'),
142(55,1,'LookLikeAKango',7,'2011-10-08 00:00:00'), 142(55,1,'LookLikeAKango',7,'2011-10-08'),
143(55,1,'CalmlyFrenetic',3,'2011-10-09 00:00:00'), 143(55,1,'CalmlyFrenetic',3,'2011-10-09'),
144(55,1,'Jaso',2,'2011-11-26 00:00:00'), 144(55,1,'Jaso',2,'2011-11-26'),
145(55,1,'PerOculos',0,'2021-02-06 00:00:00'), 145(55,1,'PerOculos',0,'2021-02-06'),
146(56,1,'CalmlyFrenetic',9,'2011-10-08 00:00:00'), 146(56,1,'CalmlyFrenetic',9,'2011-10-08'),
147(56,1,'LookLikeAKango',5,'2011-10-09 00:00:00'), 147(56,1,'LookLikeAKango',5,'2011-10-09'),
148(56,1,'CalmlyFrenetic',4,'2011-10-09 00:00:00'), 148(56,1,'CalmlyFrenetic',4,'2011-10-09'),
149(56,1,'Jetwash',2,'2014-09-05 00:00:00'), 149(56,1,'Jetwash',2,'2014-09-05'),
150-- 9 150-- 9
151(57,1,'JNS',7,'2011-07-21 00:00:00'), 151(57,1,'JNS',7,'2011-07-21'),
152(57,1,'Krank',5,'2012-07-29 00:00:00'), 152(57,1,'Krank',5,'2012-07-29'),
153(57,1,'Krzyhau',0,'2017-10-29 00:00:00'), 153(57,1,'Krzyhau',0,'2017-10-29'),
154(58,1,'Stimich',2,'2011-10-11 00:00:00'), 154(58,1,'Stimich',2,'2011-10-11'),
155(59,1,'Isimmo',7,'2011-11-04 00:00:00'), 155(59,1,'Isimmo',7,'2011-11-04'),
156(59,1,'sicklebrick',6,'2013-03-20 00:00:00'), 156(59,1,'sicklebrick',6,'2013-03-20'),
157(60,1,'CalmlyFrenetic',7,'2011-10-19 00:00:00'), 157(60,1,'CalmlyFrenetic',7,'2011-10-19'),
158(60,1,'Tyronis',6,'2011-11-01 00:00:00'), 158(60,1,'Tyronis',6,'2011-11-01'),
159-- Portal 2 Cooperative 159-- Portal 2 Cooperative
160-- 1 160-- 1
161(63,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), 161(63,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01'),
162(64,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), 162(64,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01'),
163(64,1,'Chubfish & Exhale',2,'2011-11-01 00:00:00'), 163(64,1,'Chubfish & Exhale',2,'2011-11-01'),
164(65,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), 164(65,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01'),
165(65,1,'Nidboj132 & Oryn',3,'2022-02-03 00:00:00'), 165(65,1,'Nidboj132 & Oryn',3,'2022-02-03'),
166(66,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), 166(66,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01'),
167(66,1,'Schlepian & Chubfish',2,'2011-10-01 00:00:00'), 167(66,1,'Schlepian & Chubfish',2,'2011-10-01'),
168(67,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), 168(67,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01'),
169(68,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), 169(68,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01'),
170-- 2 170-- 2
171(69,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), 171(69,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01'),
172(70,1,'Mathias123961 & Sir Spawn Alot',6,'2011-08-01 00:00:00'), 172(70,1,'Mathias123961 & Sir Spawn Alot',6,'2011-08-01'),
173(70,1,'Schlepian & Chubfish',4,'2011-10-01 00:00:00'), 173(70,1,'Schlepian & Chubfish',4,'2011-10-01'),
174(70,1,'Gocnak & z1mb0bw4y',2,'2012-08-03 00:00:00'), 174(70,1,'Gocnak & z1mb0bw4y',2,'2012-08-03'),
175(70,1,'DM_ & VEGA',0,'2017-10-01 00:00:00'), 175(70,1,'DM_ & VEGA',0,'2017-10-01'),
176(71,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), 176(71,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01'),
177(71,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), 177(71,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01'),
178(72,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), 178(72,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01'),
179(72,1,'Schlepian & LongJohnDickWeed',2,'2011-10-01 00:00:00'), 179(72,1,'Schlepian & LongJohnDickWeed',2,'2011-10-01'),
180(73,1,'Stimich & HiTMaRkS',9,'2011-05-09 00:00:00'), 180(73,1,'Stimich & HiTMaRkS',9,'2011-05-09'),
181(73,1,'Mathias123961 & Sir Spawn Alot',8,'2011-08-01 00:00:00'), 181(73,1,'Mathias123961 & Sir Spawn Alot',8,'2011-08-01'),
182(73,1,'Schlepian & Lemonsunshine',7,'2011-11-01 00:00:00'), 182(73,1,'Schlepian & Lemonsunshine',7,'2011-11-01'),
183(73,1,'DM_ & LsDK_',6,'2018-01-01 00:00:00'), 183(73,1,'DM_ & LsDK_',6,'2018-01-01'),
184(73,1,'Krzyhau & Klooger',4,'2018-11-01 00:00:00'), 184(73,1,'Krzyhau & Klooger',4,'2018-11-01'),
185(74,1,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01 00:00:00'), 185(74,1,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01'),
186(74,1,'Stimich & Pitkakorva',7,'2011-10-11 00:00:00'), 186(74,1,'Stimich & Pitkakorva',7,'2011-10-11'),
187(74,1,'Schlepian & Isimmo',3,'2011-10-28 00:00:00'), 187(74,1,'Schlepian & Isimmo',3,'2011-10-28'),
188(74,1,'Zypeh & szeimartin',2,'2013-11-01 00:00:00'), 188(74,1,'Zypeh & szeimartin',2,'2013-11-01'),
189(75,1,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01 00:00:00'), 189(75,1,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01'),
190(75,1,'Schlepian & Urination',4,'2011-10-01 00:00:00'), 190(75,1,'Schlepian & Urination',4,'2011-10-01'),
191(75,1,'Schlepian & Lemonsunshine',2,'2012-02-01 00:00:00'), 191(75,1,'Schlepian & Lemonsunshine',2,'2012-02-01'),
192(75,1,'DM_ & follon',0,'2015-04-01 00:00:00'), 192(75,1,'DM_ & follon',0,'2015-04-01'),
193(76,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), 193(76,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01'),
194(76,1,'Chubfish & Exhale',0,'2011-12-01 00:00:00'), 194(76,1,'Chubfish & Exhale',0,'2011-12-01'),
195-- 3 195-- 3
196(77,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), 196(77,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01'),
197(78,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), 197(78,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01'),
198(78,1,'DM_ & marK',3,'2016-11-01 00:00:00'), 198(78,1,'DM_ & marK',3,'2016-11-01'),
199(78,1,'Nidboj132 & Oryn',2,'2021-09-04 00:00:00'), 199(78,1,'Nidboj132 & Oryn',2,'2021-09-04'),
200(79,1,'ganonscrub & ?',5,'2011-04-01 00:00:00'), 200(79,1,'ganonscrub & ?',5,'2011-04-01'),
201(79,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), 201(79,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01'),
202(79,1,'Chubfish & Exhale',2,'2012-08-04 00:00:00'), 202(79,1,'Chubfish & Exhale',2,'2012-08-04'),
203(80,1,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01 00:00:00'), 203(80,1,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01'),
204(80,1,'Chubfish & Exhale',4,'2011-12-01 00:00:00'), 204(80,1,'Chubfish & Exhale',4,'2011-12-01'),
205(81,1,'Mathias123961 & Sir Spawn Alot',7,'2011-08-01 00:00:00'), 205(81,1,'Mathias123961 & Sir Spawn Alot',7,'2011-08-01'),
206(81,1,'Schlepian & Lemonsunshine',6,'2011-10-01 00:00:00'), 206(81,1,'Schlepian & Lemonsunshine',6,'2011-10-01'),
207(81,1,'takz & dawn',5,'2011-11-01 00:00:00'), 207(81,1,'takz & dawn',5,'2011-11-01'),
208(81,1,'Nidboj132 & Oryn',4,'2021-03-25 00:00:00'), 208(81,1,'Nidboj132 & Oryn',4,'2021-03-25'),
209(82,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), 209(82,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01'),
210(83,1,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01 00:00:00'), 210(83,1,'Mathias123961 & Sir Spawn Alot',5,'2011-08-01'),
211(83,1,'Schlepian & Lemonsunshine',2,'2011-10-01 00:00:00'), 211(83,1,'Schlepian & Lemonsunshine',2,'2011-10-01'),
212(83,1,'Chubfish & Exhale',0,'2011-12-01 00:00:00'), 212(83,1,'Chubfish & Exhale',0,'2011-12-01'),
213(84,1,'Mathias123961 & Sir Spawn Alot',6,'2011-08-01 00:00:00'), 213(84,1,'Mathias123961 & Sir Spawn Alot',6,'2011-08-01'),
214(84,1,'Schlepian & Chubfish',4,'2011-10-01 00:00:00'), 214(84,1,'Schlepian & Chubfish',4,'2011-10-01'),
215(84,1,'Chubfish & Exhale',2,'2012-01-01 00:00:00'), 215(84,1,'Chubfish & Exhale',2,'2012-01-01'),
216(84,1,'DM_ & wS',0,'2015-05-01 00:00:00'), 216(84,1,'DM_ & wS',0,'2015-05-01'),
217-- 4 217-- 4
218(85,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), 218(85,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01'),
219(85,1,'Chubfish & Exhale',0,'2011-10-01 00:00:00'), 219(85,1,'Chubfish & Exhale',0,'2011-10-01'),
220(86,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), 220(86,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01'),
221(86,1,'Chubfish & Exhale',0,'2011-12-01 00:00:00'), 221(86,1,'Chubfish & Exhale',0,'2011-12-01'),
222(87,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01 00:00:00'), 222(87,1,'Mathias123961 & Sir Spawn Alot',3,'2011-08-01'),
223(87,1,'Schlepian & Gopherdude',2,'2011-10-01 00:00:00'), 223(87,1,'Schlepian & Gopherdude',2,'2011-10-01'),
224(87,1,'DM_ & follon',0,'2015-04-01 00:00:00'), 224(87,1,'DM_ & follon',0,'2015-04-01'),
225(88,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), 225(88,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01'),
226(88,1,'Schlepian & Gopherdude',0,'2011-10-01 00:00:00'), 226(88,1,'Schlepian & Gopherdude',0,'2011-10-01'),
227(89,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01 00:00:00'), 227(89,1,'Mathias123961 & Sir Spawn Alot',0,'2011-08-01'),
228(90,1,'Mathias123961 & Sir Spawn Alot',4,'2011-09-01 00:00:00'), 228(90,1,'Mathias123961 & Sir Spawn Alot',4,'2011-09-01'),
229(90,1,'Schlepian & Urination',2,'2011-10-01 00:00:00'), 229(90,1,'Schlepian & Urination',2,'2011-10-01'),
230(90,1,'Klooger & Jetwash',0,'2016-08-01 00:00:00'), 230(90,1,'Klooger & Jetwash',0,'2016-08-01'),
231(91,1,'Mathias123961 & Sir Spawn Alot',2,'2011-08-01 00:00:00'), 231(91,1,'Mathias123961 & Sir Spawn Alot',2,'2011-08-01'),
232(91,1,'Undead & Zypeh',0,'2013-05-19 00:00:00'), 232(91,1,'Undead & Zypeh',0,'2013-05-19'),
233(92,1,'txx478 & ?',5,'2011-05-01 00:00:00'), 233(92,1,'txx478 & ?',5,'2011-05-01'),
234(92,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01 00:00:00'), 234(92,1,'Mathias123961 & Sir Spawn Alot',4,'2011-08-01'),
235(92,1,'Schlepian & Gopherdude',2,'2011-10-01 00:00:00'), 235(92,1,'Schlepian & Gopherdude',2,'2011-10-01'),
236(92,1,'ncla & takz',0,'2012-02-01 00:00:00'), 236(92,1,'ncla & takz',0,'2012-02-01'),
237(93,1,'Mathias123961 & Sir Spawn Alot',2,'2011-08-01 00:00:00'), 237(93,1,'Mathias123961 & Sir Spawn Alot',2,'2011-08-01'),
238(93,1,'Schlepian & Gopherdude',0,'2011-10-01 00:00:00'), 238(93,1,'Schlepian & Gopherdude',0,'2011-10-01'),
239-- 5 239-- 5
240(94,1,'Chubfish & Exhale',2,'2011-10-01 00:00:00'), 240(94,1,'Chubfish & Exhale',2,'2011-10-01'),
241(94,1,'Klooger & Imanex',0,'2013-08-01 00:00:00'), 241(94,1,'Klooger & Imanex',0,'2013-08-01'),
242(95,1,'Schlepian & Issimoi',2,'2011-10-01 00:00:00'), 242(95,1,'Schlepian & Issimoi',2,'2011-10-01'),
243(96,1,'ThePortalPatrol & ?',4,'2011-04-01 00:00:00'), 243(96,1,'ThePortalPatrol & ?',4,'2011-04-01'),
244(96,1,'sparkle1princess & Zypeh',2,'2014-01-01 00:00:00'), 244(96,1,'sparkle1princess & Zypeh',2,'2014-01-01'),
245(97,1,'Stimich & HiTMaRkS',7,'2011-05-13 00:00:00'), 245(97,1,'Stimich & HiTMaRkS',7,'2011-05-13'),
246(97,1,'Schlepian & Lemonsunshine',4,'2011-10-01 00:00:00'), 246(97,1,'Schlepian & Lemonsunshine',4,'2011-10-01'),
247(97,1,'DM_ & wS',2,'2014-05-01 00:00:00'), 247(97,1,'DM_ & wS',2,'2014-05-01'),
248(98,1,'Imanex & 00svo',0,'2011-11-01 00:00:00'), 248(98,1,'Imanex & 00svo',0,'2011-11-01'),
249(99,1,'Schlepian & Gopherdude',3,'2011-10-01 00:00:00'), 249(99,1,'Schlepian & Gopherdude',3,'2011-10-01'),
250(99,1,'Imanex & Klooger',2,'2013-08-01 00:00:00'), 250(99,1,'Imanex & Klooger',2,'2013-08-01'),
251(99,1,'DM_ & wS',0,'2015-05-01 00:00:00'), 251(99,1,'DM_ & wS',0,'2015-05-01'),
252(100,1,'Schlepian & Bananasaurus Rex',0,'2011-10-01 00:00:00'), 252(100,1,'Schlepian & Bananasaurus Rex',0,'2011-10-01'),
253(101,1,'Chubfish & Exhale',2,'2011-12-01 00:00:00'), 253(101,1,'Chubfish & Exhale',2,'2011-12-01'),
254(101,1,'DM_ & follon',0,'2015-04-01 00:00:00'), 254(101,1,'DM_ & follon',0,'2015-04-01'),
255-- 6 255-- 6
256(102,1,'dawn & takz',3,'2011-11-18 00:00:00'), 256(102,1,'dawn & takz',3,'2011-11-18'),
257(102,1,'Chubfish & Exhale',2,'2012-01-01 00:00:00'), 257(102,1,'Chubfish & Exhale',2,'2012-01-01'),
258(102,1,'Imanex & Klooger',0,'2013-08-01 00:00:00'), 258(102,1,'Imanex & Klooger',0,'2013-08-01'),
259(103,1,'Schlepian & Lemonsunshine',0,'2011-10-01 00:00:00'), 259(103,1,'Schlepian & Lemonsunshine',0,'2011-10-01'),
260(104,1,'Schlepian & Lemonsunshine',0,'2011-10-01 00:00:00'), 260(104,1,'Schlepian & Lemonsunshine',0,'2011-10-01'),
261(105,1,'Blaizerazer & ?',8,'2011-10-01 00:00:00'), 261(105,1,'Blaizerazer & ?',8,'2011-10-01'),
262(105,1,'Schlepian & Lemonsunshine',5,'2011-11-01 00:00:00'), 262(105,1,'Schlepian & Lemonsunshine',5,'2011-11-01'),
263(105,1,'Imanex & Klooger',4,'2013-08-01 00:00:00'), 263(105,1,'Imanex & Klooger',4,'2013-08-01'),
264(105,1,'DM_ & wS',3,'2014-05-01 00:00:00'), 264(105,1,'DM_ & wS',3,'2014-05-01'),
265(105,1,'DM_ & follon',2,'2015-04-01 00:00:00'), 265(105,1,'DM_ & follon',2,'2015-04-01'),
266(106,1,'Schlepian & Bananasaurus Rex',4,'2011-10-01 00:00:00'), 266(106,1,'Schlepian & Bananasaurus Rex',4,'2011-10-01'),
267(106,1,'Gig & takz',3,'2012-06-01 00:00:00'), 267(106,1,'Gig & takz',3,'2012-06-01'),
268(106,1,'Imanex & Klooger',0,'2013-06-01 00:00:00'), 268(106,1,'Imanex & Klooger',0,'2013-06-01'),
269(107,1,'Chubfish & Exhale',2,'2011-10-01 00:00:00'), 269(107,1,'Chubfish & Exhale',2,'2011-10-01'),
270(107,1,'DM_ & follon',0,'2015-04-01 00:00:00'), 270(107,1,'DM_ & follon',0,'2015-04-01'),
271(108,1,'DaFox & P',0,'2011-12-01 00:00:00'), 271(108,1,'DaFox & P',0,'2011-12-01'),
272(109,1,'Schlepian & Tyronis',5,'2011-10-01 00:00:00'), 272(109,1,'Schlepian & Tyronis',5,'2011-10-01'),
273(109,1,'Chubfish & Exhale',0,'2011-12-01 00:00:00'), 273(109,1,'Chubfish & Exhale',0,'2011-12-01'),
274(110,1,'Tyronis & mr.bob806',15,'2011-10-01 00:00:00'), 274(110,1,'Tyronis & mr.bob806',15,'2011-10-01'),
275(110,1,'Schlepian & Chubfish',6,'2011-11-01 00:00:00'), 275(110,1,'Schlepian & Chubfish',6,'2011-11-01'),
276(110,1,'00svo & z1mb0bw4y',5,'2012-08-08 00:00:00'), 276(110,1,'00svo & z1mb0bw4y',5,'2012-08-08'),
277(110,1,'00svo & z1mb0bw4y',4,'2012-08-10 00:00:00'), 277(110,1,'00svo & z1mb0bw4y',4,'2012-08-10'),
278(110,1,'Klooger & z1mb0bw4y',2,'2014-02-01 00:00:00'), 278(110,1,'Klooger & z1mb0bw4y',2,'2014-02-01'),
279(110,1,'DM_ & follon',0,'2015-04-01 00:00:00'); \ No newline at end of file 279(110,1,'DM_ & follon',0,'2015-04-01'); \ No newline at end of file
diff --git a/backend/database/init.sql b/backend/database/init.sql
index 50e7c15..11d4944 100644
--- a/backend/database/init.sql
+++ b/backend/database/init.sql
@@ -59,7 +59,7 @@ CREATE TABLE map_history (
59 category_id SMALLINT NOT NULL, 59 category_id SMALLINT NOT NULL,
60 user_name TEXT NOT NULL, 60 user_name TEXT NOT NULL,
61 score_count SMALLINT NOT NULL, 61 score_count SMALLINT NOT NULL,
62 record_date TIMESTAMP NOT NULL, 62 record_date DATE NOT NULL,
63 PRIMARY KEY (id), 63 PRIMARY KEY (id),
64 FOREIGN KEY (category_id) REFERENCES categories(id), 64 FOREIGN KEY (category_id) REFERENCES categories(id),
65 FOREIGN KEY (map_id) REFERENCES maps(id), 65 FOREIGN KEY (map_id) REFERENCES maps(id),
diff --git a/backend/models/models.go b/backend/models/models.go
index 1231cb1..e21ba6a 100644
--- a/backend/models/models.go
+++ b/backend/models/models.go
@@ -4,6 +4,20 @@ import (
4 "time" 4 "time"
5) 5)
6 6
7type Response struct {
8 Success bool `json:"success"`
9 Message string `json:"message"`
10 Data any `json:"data"`
11}
12
13func ErrorResponse(message string) Response {
14 return Response{
15 Success: false,
16 Message: message,
17 Data: nil,
18 }
19}
20
7type User struct { 21type User struct {
8 SteamID string `json:"steam_id"` 22 SteamID string `json:"steam_id"`
9 UserName string `json:"user_name"` 23 UserName string `json:"user_name"`
diff --git a/backend/models/requests.go b/backend/models/requests.go
deleted file mode 100644
index 0113597..0000000
--- a/backend/models/requests.go
+++ /dev/null
@@ -1,39 +0,0 @@
1package models
2
3import (
4 "mime/multipart"
5 "time"
6)
7
8type CreateMapSummaryRequest struct {
9 CategoryID int `json:"category_id" binding:"required"`
10 Description string `json:"description" binding:"required"`
11 Showcase string `json:"showcase"`
12 UserName string `json:"user_name" binding:"required"`
13 ScoreCount *int `json:"score_count" binding:"required"`
14 RecordDate time.Time `json:"record_date" binding:"required"`
15}
16
17type EditMapSummaryRequest struct {
18 RouteID int `json:"route_id" binding:"required"`
19 Description string `json:"description" binding:"required"`
20 Showcase string `json:"showcase"`
21 UserName string `json:"user_name" binding:"required"`
22 ScoreCount *int `json:"score_count" binding:"required"`
23 RecordDate time.Time `json:"record_date" binding:"required"`
24}
25
26type DeleteMapSummaryRequest struct {
27 RouteID int `json:"route_id" binding:"required"`
28}
29
30type EditMapImageRequest struct {
31 Image string `json:"image" binding:"required"`
32}
33
34type RecordRequest struct {
35 HostDemo *multipart.FileHeader `json:"host_demo" form:"host_demo" binding:"required" swaggerignore:"true"`
36 PartnerDemo *multipart.FileHeader `json:"partner_demo" form:"partner_demo" swaggerignore:"true"`
37 IsPartnerOrange bool `json:"is_partner_orange" form:"is_partner_orange"`
38 PartnerID string `json:"partner_id" form:"partner_id"`
39}
diff --git a/backend/models/responses.go b/backend/models/responses.go
deleted file mode 100644
index 459911c..0000000
--- a/backend/models/responses.go
+++ /dev/null
@@ -1,64 +0,0 @@
1package models
2
3type Response struct {
4 Success bool `json:"success"`
5 Message string `json:"message"`
6 Data any `json:"data"`
7}
8
9type LoginResponse struct {
10 Token string `json:"token"`
11}
12
13type RankingsResponse struct {
14 RankingsSP []UserRanking `json:"rankings_sp"`
15 RankingsMP []UserRanking `json:"rankings_mp"`
16}
17
18type ProfileResponse struct {
19 Profile bool `json:"profile"`
20 SteamID string `json:"steam_id"`
21 UserName string `json:"user_name"`
22 AvatarLink string `json:"avatar_link"`
23 CountryCode string `json:"country_code"`
24 ScoresSP []ScoreResponse `json:"scores_sp"`
25 ScoresMP []ScoreResponse `json:"scores_mp"`
26}
27
28type ScoreResponse struct {
29 MapID int `json:"map_id"`
30 Records any `json:"records"`
31}
32
33type MapSummaryResponse struct {
34 Map Map `json:"map"`
35 Summary MapSummary `json:"summary"`
36}
37
38type SearchResponse struct {
39 Players []UserShort `json:"players"`
40 Maps []MapShort `json:"maps"`
41}
42
43type ChaptersResponse struct {
44 Game Game `json:"game"`
45 Chapters []Chapter `json:"chapters"`
46}
47
48type ChapterMapsResponse struct {
49 Chapter Chapter `json:"chapter"`
50 Maps []MapShort `json:"maps"`
51}
52
53type RecordResponse struct {
54 ScoreCount int `json:"score_count"`
55 ScoreTime int `json:"score_time"`
56}
57
58func ErrorResponse(message string) Response {
59 return Response{
60 Success: false,
61 Message: message,
62 Data: nil,
63 }
64}