aboutsummaryrefslogtreecommitdiff
path: root/backend/handlers/user.go
diff options
context:
space:
mode:
Diffstat (limited to 'backend/handlers/user.go')
-rw-r--r--backend/handlers/user.go719
1 files changed, 719 insertions, 0 deletions
diff --git a/backend/handlers/user.go b/backend/handlers/user.go
new file mode 100644
index 0000000..742a57c
--- /dev/null
+++ b/backend/handlers/user.go
@@ -0,0 +1,719 @@
1package handlers
2
3import (
4 "net/http"
5 "os"
6 "regexp"
7 "time"
8
9 "github.com/gin-gonic/gin"
10 "github.com/pektezol/leastportalshub/backend/database"
11 "github.com/pektezol/leastportalshub/backend/models"
12)
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 Titles []models.Title `json:"titles"`
21 Links models.Links `json:"links"`
22 Rankings ProfileRankings `json:"rankings"`
23 Records []ProfileRecords `json:"records"`
24}
25
26type ProfileRankings struct {
27 Overall ProfileRankingsDetails `json:"overall"`
28 Singleplayer ProfileRankingsDetails `json:"singleplayer"`
29 Cooperative ProfileRankingsDetails `json:"cooperative"`
30}
31
32type ProfileRankingsDetails struct {
33 Rank int `json:"rank"`
34 CompletionCount int `json:"completion_count"`
35 CompletionTotal int `json:"completion_total"`
36}
37type ProfileRecords struct {
38 GameID int `json:"game_id"`
39 CategoryID int `json:"category_id"`
40 MapID int `json:"map_id"`
41 MapName string `json:"map_name"`
42 MapWRCount int `json:"map_wr_count"`
43 Scores []ProfileScores `json:"scores"`
44}
45
46type ProfileScores struct {
47 DemoID string `json:"demo_id"`
48 ScoreCount int `json:"score_count"`
49 ScoreTime int `json:"score_time"`
50 Date time.Time `json:"date"`
51}
52
53type ScoreResponse struct {
54 MapID int `json:"map_id"`
55 Records any `json:"records"`
56}
57
58// GET Profile
59//
60// @Description Get profile page of session user.
61// @Tags users
62// @Accept json
63// @Produce json
64// @Param Authorization header string true "JWT Token"
65// @Success 200 {object} models.Response{data=ProfileResponse}
66// @Failure 400 {object} models.Response
67// @Failure 401 {object} models.Response
68// @Router /profile [get]
69func Profile(c *gin.Context) {
70 // Check if user exists
71 user, exists := c.Get("user")
72 if !exists {
73 c.JSON(http.StatusUnauthorized, models.ErrorResponse("User not logged in."))
74 return
75 }
76 // Get user links
77 links := models.Links{}
78 sql := `SELECT u.p2sr, u.steam, u.youtube, u.twitch FROM users u WHERE u.steam_id = $1`
79 err := database.DB.QueryRow(sql, user.(models.User).SteamID).Scan(&links.P2SR, &links.Steam, &links.YouTube, &links.Twitch)
80 if err != nil {
81 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
82 return
83 }
84 // Get rankings (all maps done in one game)
85 rankings := ProfileRankings{
86 Overall: ProfileRankingsDetails{},
87 Singleplayer: ProfileRankingsDetails{},
88 Cooperative: ProfileRankingsDetails{},
89 }
90 // Get total map count
91 sql = `SELECT count(id), (SELECT count(id) FROM maps m WHERE m.game_id = 2 AND m.is_disabled = false) FROM maps m WHERE m.game_id = 1 AND m.is_disabled = false;`
92 err = database.DB.QueryRow(sql).Scan(&rankings.Singleplayer.CompletionTotal, &rankings.Cooperative.CompletionTotal)
93 if err != nil {
94 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
95 return
96 }
97 rankings.Overall.CompletionTotal = rankings.Singleplayer.CompletionTotal + rankings.Cooperative.CompletionTotal
98 // Get user completion count
99 sql = `SELECT 'records_sp' AS table_name, COUNT(rs.id) AS total_user_scores
100 FROM public.records_sp rs JOIN (
101 SELECT mr.map_id, MIN(mr.score_count) AS min_score_count
102 FROM public.map_routes mr WHERE mr.category_id = 1 GROUP BY mr.map_id
103 ) AS subquery_sp ON rs.map_id = subquery_sp.map_id AND rs.score_count = subquery_sp.min_score_count
104 WHERE rs.user_id = $1
105 UNION ALL
106 SELECT 'records_mp' AS table_name, COUNT(rm.id) AS total_user_scores
107 FROM public.records_mp rm JOIN (
108 SELECT mr.map_id, MIN(mr.score_count) AS min_score_count
109 FROM public.map_routes mr WHERE mr.category_id = 1 GROUP BY mr.map_id
110 ) AS subquery_mp ON rm.map_id = subquery_mp.map_id AND rm.score_count = subquery_mp.min_score_count
111 WHERE rm.host_id = $1 OR rm.partner_id = $1;`
112 rows, err := database.DB.Query(sql, user.(models.User).SteamID)
113 if err != nil {
114 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
115 return
116 }
117 for rows.Next() {
118 var tableName string
119 var completionCount int
120 err = rows.Scan(&tableName, &completionCount)
121 if err != nil {
122 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
123 return
124 }
125 if tableName == "records_sp" {
126 rankings.Singleplayer.CompletionCount = completionCount
127 continue
128 }
129 if tableName == "records_mp" {
130 rankings.Cooperative.CompletionCount = completionCount
131 continue
132 }
133 }
134 rankings.Overall.CompletionCount = rankings.Singleplayer.CompletionCount + rankings.Cooperative.CompletionCount
135 // Get user ranking placement for singleplayer
136 sql = `SELECT u.steam_id, COUNT(DISTINCT map_id),
137 (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = FALSE AND is_disabled = false),
138 (SELECT SUM(min_score_count) AS total_min_score_count FROM (
139 SELECT user_id, MIN(score_count) AS min_score_count FROM records_sp GROUP BY user_id, map_id) AS subquery WHERE user_id = u.steam_id)
140 FROM records_sp sp JOIN users u ON u.steam_id = sp.user_id GROUP BY u.steam_id, u.user_name
141 HAVING COUNT(DISTINCT map_id) = (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = FALSE AND is_disabled = false)
142 ORDER BY total_min_score_count ASC;`
143 rows, err = database.DB.Query(sql)
144 if err != nil {
145 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
146 return
147 }
148 placement := 1
149 for rows.Next() {
150 var steamID string
151 var completionCount int
152 var totalCount int
153 var userPortalCount int
154 err = rows.Scan(&steamID, &completionCount, &totalCount, &userPortalCount)
155 if err != nil {
156 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
157 return
158 }
159 if completionCount != totalCount {
160 placement++
161 continue
162 }
163 if steamID != user.(models.User).SteamID {
164 placement++
165 continue
166 }
167 rankings.Singleplayer.Rank = placement
168 }
169 // Get user ranking placement for multiplayer
170 sql = `SELECT u.steam_id, COUNT(DISTINCT map_id),
171 (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = FALSE AND is_disabled = false),
172 (SELECT SUM(min_score_count) AS total_min_score_count FROM (
173 SELECT host_id, partner_id, MIN(score_count) AS min_score_count FROM records_mp GROUP BY host_id, partner_id, map_id) AS subquery WHERE host_id = u.steam_id OR partner_id = u.steam_id)
174 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
175 HAVING COUNT(DISTINCT map_id) = (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = FALSE AND is_disabled = false)
176 ORDER BY total_min_score_count ASC;`
177 rows, err = database.DB.Query(sql)
178 if err != nil {
179 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
180 return
181 }
182 placement = 1
183 for rows.Next() {
184 var steamID string
185 var completionCount int
186 var totalCount int
187 var userPortalCount int
188 err = rows.Scan(&steamID, &completionCount, &totalCount, &userPortalCount)
189 if err != nil {
190 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
191 return
192 }
193 if completionCount != totalCount {
194 placement++
195 continue
196 }
197 if steamID != user.(models.User).SteamID {
198 placement++
199 continue
200 }
201 rankings.Cooperative.Rank = placement
202 }
203 // TODO: Get user ranking placement for overall if they qualify
204 // if (rankings.Singleplayer.Rank != 0) && (rankings.Cooperative.Rank != 0) {
205 // sql = `SELECT steam_id, SUM(total_min_score_count) AS total_score
206 // FROM (
207 // SELECT u.steam_id,
208 // (SELECT SUM(min_score_count) AS total_min_score_count FROM (
209 // SELECT
210 // user_id,
211 // MIN(score_count) AS min_score_count
212 // FROM records_sp
213 // GROUP BY user_id, map_id
214 // ) AS subquery
215 // WHERE user_id = u.steam_id) AS total_min_score_count
216 // FROM records_sp sp
217 // JOIN users u ON u.steam_id = sp.user_id
218 // UNION ALL
219 // SELECT u.steam_id,
220 // (SELECT SUM(min_score_count) AS total_min_score_count FROM (
221 // SELECT
222 // host_id,
223 // partner_id,
224 // MIN(score_count) AS min_score_count
225 // FROM records_mp
226 // GROUP BY host_id, partner_id, map_id
227 // ) AS subquery
228 // WHERE host_id = u.steam_id OR partner_id = u.steam_id) AS total_min_score_count
229 // FROM records_mp mp
230 // JOIN users u ON u.steam_id = mp.host_id OR u.steam_id = mp.partner_id
231 // ) AS combined_scores
232 // GROUP BY steam_id ORDER BY total_score ASC;`
233 // rows, err = database.DB.Query(sql)
234 // if err != nil {
235 // c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
236 // return
237 // }
238 // placement = 1
239 // for rows.Next() {
240 // var steamID string
241 // var userPortalCount int
242 // err = rows.Scan(&steamID, &userPortalCount)
243 // if err != nil {
244 // c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
245 // return
246 // }
247 // if completionCount != totalCount {
248 // placement++
249 // continue
250 // }
251 // if steamID != user.(models.User).SteamID {
252 // placement++
253 // continue
254 // }
255 // rankings.Cooperative.Rank = placement
256 // }
257 // }
258 records := []ProfileRecords{}
259 // Get singleplayer records
260 sql = `SELECT m.game_id, m.chapter_id, sp.map_id, m."name", (SELECT mr.score_count FROM map_routes mr WHERE mr.map_id = sp.map_id ORDER BY mr.score_count ASC LIMIT 1) AS wr_count, sp.score_count, sp.score_time, sp.demo_id, sp.record_date
261 FROM records_sp sp INNER JOIN maps m ON sp.map_id = m.id WHERE sp.user_id = $1 ORDER BY sp.map_id, sp.score_count, sp.score_time;`
262 rows, err = database.DB.Query(sql, user.(models.User).SteamID)
263 if err != nil {
264 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
265 return
266 }
267 for rows.Next() {
268 var gameID int
269 var categoryID int
270 var mapID int
271 var mapName string
272 var mapWR int
273 score := ProfileScores{}
274 rows.Scan(&gameID, &categoryID, &mapID, &mapName, &mapWR, &score.ScoreCount, &score.ScoreTime, &score.DemoID, &score.Date)
275 // More than one record in one map
276 if len(records) != 0 && mapID == records[len(records)-1].MapID {
277 records[len(records)-1].Scores = append(records[len(records)-1].Scores, score)
278 continue
279 }
280 // New map
281 records = append(records, ProfileRecords{
282 GameID: gameID,
283 CategoryID: categoryID,
284 MapID: mapID,
285 MapName: mapName,
286 MapWRCount: mapWR,
287 Scores: []ProfileScores{},
288 })
289 records[len(records)-1].Scores = append(records[len(records)-1].Scores, score)
290 }
291 // Get multiplayer records
292 sql = `SELECT m.game_id, m.chapter_id, mp.map_id, m."name", (SELECT mr.score_count FROM map_routes mr WHERE mr.map_id = mp.map_id ORDER BY mr.score_count ASC LIMIT 1) AS wr_count, mp.score_count, mp.score_time, CASE WHEN host_id = $1 THEN mp.host_demo_id WHEN partner_id = $1 THEN mp.partner_demo_id END demo_id, mp.record_date
293 FROM records_mp mp INNER JOIN maps m ON mp.map_id = m.id WHERE mp.host_id = $1 OR mp.partner_id = $1 ORDER BY mp.map_id, mp.score_count, mp.score_time;`
294 rows, err = database.DB.Query(sql, user.(models.User).SteamID)
295 if err != nil {
296 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
297 return
298 }
299 for rows.Next() {
300 var gameID int
301 var categoryID int
302 var mapID int
303 var mapName string
304 var mapWR int
305 score := ProfileScores{}
306 rows.Scan(&gameID, &categoryID, &mapID, &mapName, &mapWR, &score.ScoreCount, &score.ScoreTime, &score.DemoID, &score.Date)
307 // More than one record in one map
308 if len(records) != 0 && mapID == records[len(records)-1].MapID {
309 records[len(records)-1].Scores = append(records[len(records)-1].Scores, score)
310 continue
311 }
312 // New map
313 records = append(records, ProfileRecords{
314 GameID: gameID,
315 CategoryID: categoryID,
316 MapID: mapID,
317 MapName: mapName,
318 MapWRCount: mapWR,
319 Scores: []ProfileScores{},
320 })
321 records[len(records)-1].Scores = append(records[len(records)-1].Scores, score)
322 }
323 c.JSON(http.StatusOK, models.Response{
324 Success: true,
325 Message: "Successfully retrieved user scores.",
326 Data: ProfileResponse{
327 Profile: true,
328 SteamID: user.(models.User).SteamID,
329 UserName: user.(models.User).UserName,
330 AvatarLink: user.(models.User).AvatarLink,
331 CountryCode: user.(models.User).CountryCode,
332 Titles: user.(models.User).Titles,
333 Links: links,
334 Rankings: rankings,
335 Records: records,
336 },
337 })
338}
339
340// GET User
341//
342// @Description Get profile page of another user.
343// @Tags users
344// @Accept json
345// @Produce json
346// @Param id path int true "User ID"
347// @Success 200 {object} models.Response{data=ProfileResponse}
348// @Failure 400 {object} models.Response
349// @Failure 404 {object} models.Response
350// @Router /users/{id} [get]
351func FetchUser(c *gin.Context) {
352 id := c.Param("id")
353 // Check if id is all numbers and 17 length
354 match, _ := regexp.MatchString("^[0-9]{17}$", id)
355 if !match {
356 c.JSON(http.StatusNotFound, models.ErrorResponse("User not found."))
357 return
358 }
359 // Check if user exists
360 var user models.User
361 links := models.Links{}
362 sql := `SELECT u.steam_id, u.user_name, u.avatar_link, u.country_code, u.created_at, u.updated_at, u.p2sr, u.steam, u.youtube, u.twitch FROM users u WHERE u.steam_id = $1`
363 err := database.DB.QueryRow(sql, id).Scan(&user.SteamID, &user.UserName, &user.AvatarLink, &user.CountryCode, &user.CreatedAt, &user.UpdatedAt, &links.P2SR, &links.Steam, &links.YouTube, &links.Twitch)
364 if err != nil {
365 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
366 return
367 }
368 if user.SteamID == "" {
369 // User does not exist
370 c.JSON(http.StatusNotFound, models.ErrorResponse("User not found."))
371 return
372 }
373 // Get rankings (all maps done in one game)
374 rankings := ProfileRankings{
375 Overall: ProfileRankingsDetails{},
376 Singleplayer: ProfileRankingsDetails{},
377 Cooperative: ProfileRankingsDetails{},
378 }
379 // Get total map count
380 sql = `SELECT count(id), (SELECT count(id) FROM maps m WHERE m.game_id = 2 AND m.is_disabled = false) FROM maps m WHERE m.game_id = 1 AND m.is_disabled = false;`
381 err = database.DB.QueryRow(sql).Scan(&rankings.Singleplayer.CompletionTotal, &rankings.Cooperative.CompletionTotal)
382 if err != nil {
383 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
384 return
385 }
386 rankings.Overall.CompletionTotal = rankings.Singleplayer.CompletionTotal + rankings.Cooperative.CompletionTotal
387 // Get user completion count
388 sql = `SELECT 'records_sp' AS table_name, COUNT(rs.id) AS total_user_scores
389 FROM public.records_sp rs JOIN (
390 SELECT mr.map_id, MIN(mr.score_count) AS min_score_count
391 FROM public.map_routes mr WHERE mr.category_id = 1 GROUP BY mr.map_id
392 ) AS subquery_sp ON rs.map_id = subquery_sp.map_id AND rs.score_count = subquery_sp.min_score_count
393 WHERE rs.user_id = $1
394 UNION ALL
395 SELECT 'records_mp' AS table_name, COUNT(rm.id) AS total_user_scores
396 FROM public.records_mp rm JOIN (
397 SELECT mr.map_id, MIN(mr.score_count) AS min_score_count
398 FROM public.map_routes mr WHERE mr.category_id = 1 GROUP BY mr.map_id
399 ) AS subquery_mp ON rm.map_id = subquery_mp.map_id AND rm.score_count = subquery_mp.min_score_count
400 WHERE rm.host_id = $1 OR rm.partner_id = $1;`
401 rows, err := database.DB.Query(sql, user.SteamID)
402 if err != nil {
403 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
404 return
405 }
406 for rows.Next() {
407 var tableName string
408 var completionCount int
409 err = rows.Scan(&tableName, &completionCount)
410 if err != nil {
411 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
412 return
413 }
414 if tableName == "records_sp" {
415 rankings.Singleplayer.CompletionCount = completionCount
416 continue
417 }
418 if tableName == "records_mp" {
419 rankings.Cooperative.CompletionCount = completionCount
420 continue
421 }
422 }
423 rankings.Overall.CompletionCount = rankings.Singleplayer.CompletionCount + rankings.Cooperative.CompletionCount
424 // Get user ranking placement for singleplayer
425 sql = `SELECT u.steam_id, COUNT(DISTINCT map_id),
426 (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = FALSE AND is_disabled = false),
427 (SELECT SUM(min_score_count) AS total_min_score_count FROM (
428 SELECT user_id, MIN(score_count) AS min_score_count FROM records_sp GROUP BY user_id, map_id) AS subquery WHERE user_id = u.steam_id)
429 FROM records_sp sp JOIN users u ON u.steam_id = sp.user_id GROUP BY u.steam_id, u.user_name
430 HAVING COUNT(DISTINCT map_id) = (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = FALSE AND is_disabled = false)
431 ORDER BY total_min_score_count ASC;`
432 rows, err = database.DB.Query(sql)
433 if err != nil {
434 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
435 return
436 }
437 placement := 1
438 for rows.Next() {
439 var steamID string
440 var completionCount int
441 var totalCount int
442 var userPortalCount int
443 err = rows.Scan(&steamID, &completionCount, &totalCount, &userPortalCount)
444 if err != nil {
445 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
446 return
447 }
448 if completionCount != totalCount {
449 placement++
450 continue
451 }
452 if steamID != user.SteamID {
453 placement++
454 continue
455 }
456 rankings.Singleplayer.Rank = placement
457 }
458 // Get user ranking placement for multiplayer
459 sql = `SELECT u.steam_id, COUNT(DISTINCT map_id),
460 (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = FALSE AND is_disabled = false),
461 (SELECT SUM(min_score_count) AS total_min_score_count FROM (
462 SELECT host_id, partner_id, MIN(score_count) AS min_score_count FROM records_mp GROUP BY host_id, partner_id, map_id) AS subquery WHERE host_id = u.steam_id OR partner_id = u.steam_id)
463 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
464 HAVING COUNT(DISTINCT map_id) = (SELECT COUNT(maps.name) FROM maps INNER JOIN games g ON maps.game_id = g.id WHERE g.is_coop = FALSE AND is_disabled = false)
465 ORDER BY total_min_score_count ASC;`
466 rows, err = database.DB.Query(sql)
467 if err != nil {
468 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
469 return
470 }
471 placement = 1
472 for rows.Next() {
473 var steamID string
474 var completionCount int
475 var totalCount int
476 var userPortalCount int
477 err = rows.Scan(&steamID, &completionCount, &totalCount, &userPortalCount)
478 if err != nil {
479 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
480 return
481 }
482 if completionCount != totalCount {
483 placement++
484 continue
485 }
486 if steamID != user.SteamID {
487 placement++
488 continue
489 }
490 rankings.Cooperative.Rank = placement
491 }
492 // TODO: Get user ranking placement for overall if they qualify
493 // if (rankings.Singleplayer.Rank != 0) && (rankings.Cooperative.Rank != 0) {
494 // sql = `SELECT steam_id, SUM(total_min_score_count) AS total_score
495 // FROM (
496 // SELECT u.steam_id,
497 // (SELECT SUM(min_score_count) AS total_min_score_count FROM (
498 // SELECT
499 // user_id,
500 // MIN(score_count) AS min_score_count
501 // FROM records_sp
502 // GROUP BY user_id, map_id
503 // ) AS subquery
504 // WHERE user_id = u.steam_id) AS total_min_score_count
505 // FROM records_sp sp
506 // JOIN users u ON u.steam_id = sp.user_id
507 // UNION ALL
508 // SELECT u.steam_id,
509 // (SELECT SUM(min_score_count) AS total_min_score_count FROM (
510 // SELECT
511 // host_id,
512 // partner_id,
513 // MIN(score_count) AS min_score_count
514 // FROM records_mp
515 // GROUP BY host_id, partner_id, map_id
516 // ) AS subquery
517 // WHERE host_id = u.steam_id OR partner_id = u.steam_id) AS total_min_score_count
518 // FROM records_mp mp
519 // JOIN users u ON u.steam_id = mp.host_id OR u.steam_id = mp.partner_id
520 // ) AS combined_scores
521 // GROUP BY steam_id ORDER BY total_score ASC;`
522 // rows, err = database.DB.Query(sql)
523 // if err != nil {
524 // c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
525 // return
526 // }
527 // placement = 1
528 // for rows.Next() {
529 // var steamID string
530 // var userPortalCount int
531 // err = rows.Scan(&steamID, &userPortalCount)
532 // if err != nil {
533 // c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
534 // return
535 // }
536 // if completionCount != totalCount {
537 // placement++
538 // continue
539 // }
540 // if steamID != user.SteamID {
541 // placement++
542 // continue
543 // }
544 // rankings.Cooperative.Rank = placement
545 // }
546 // }
547 records := []ProfileRecords{}
548 // Get singleplayer records
549 sql = `SELECT m.game_id, m.chapter_id, sp.map_id, m."name", (SELECT mr.score_count FROM map_routes mr WHERE mr.map_id = sp.map_id ORDER BY mr.score_count ASC LIMIT 1) AS wr_count, sp.score_count, sp.score_time, sp.demo_id, sp.record_date
550 FROM records_sp sp INNER JOIN maps m ON sp.map_id = m.id WHERE sp.user_id = $1 ORDER BY sp.map_id, sp.score_count, sp.score_time;`
551 rows, err = database.DB.Query(sql, user.SteamID)
552 if err != nil {
553 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
554 return
555 }
556 for rows.Next() {
557 var gameID int
558 var categoryID int
559 var mapID int
560 var mapName string
561 var mapWR int
562 score := ProfileScores{}
563 rows.Scan(&gameID, &categoryID, &mapID, &mapName, &mapWR, &score.ScoreCount, &score.ScoreTime, &score.DemoID, &score.Date)
564 // More than one record in one map
565 if len(records) != 0 && mapID == records[len(records)-1].MapID {
566 records[len(records)-1].Scores = append(records[len(records)-1].Scores, score)
567 continue
568 }
569 // New map
570 records = append(records, ProfileRecords{
571 GameID: gameID,
572 CategoryID: categoryID,
573 MapID: mapID,
574 MapName: mapName,
575 MapWRCount: mapWR,
576 Scores: []ProfileScores{},
577 })
578 records[len(records)-1].Scores = append(records[len(records)-1].Scores, score)
579 }
580 // Get multiplayer records
581 sql = `SELECT m.game_id, m.chapter_id, mp.map_id, m."name", (SELECT mr.score_count FROM map_routes mr WHERE mr.map_id = mp.map_id ORDER BY mr.score_count ASC LIMIT 1) AS wr_count, mp.score_count, mp.score_time, CASE WHEN host_id = $1 THEN mp.host_demo_id WHEN partner_id = $1 THEN mp.partner_demo_id END demo_id, mp.record_date
582 FROM records_mp mp INNER JOIN maps m ON mp.map_id = m.id WHERE mp.host_id = $1 OR mp.partner_id = $1 ORDER BY mp.map_id, mp.score_count, mp.score_time;`
583 rows, err = database.DB.Query(sql, user.SteamID)
584 if err != nil {
585 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
586 return
587 }
588 for rows.Next() {
589 var gameID int
590 var categoryID int
591 var mapID int
592 var mapName string
593 var mapWR int
594 score := ProfileScores{}
595 rows.Scan(&gameID, &categoryID, &mapID, &mapName, &mapWR, &score.ScoreCount, &score.ScoreTime, &score.DemoID, &score.Date)
596 // More than one record in one map
597 if len(records) != 0 && mapID == records[len(records)-1].MapID {
598 records[len(records)-1].Scores = append(records[len(records)-1].Scores, score)
599 continue
600 }
601 // New map
602 records = append(records, ProfileRecords{
603 GameID: gameID,
604 CategoryID: categoryID,
605 MapID: mapID,
606 MapName: mapName,
607 MapWRCount: mapWR,
608 Scores: []ProfileScores{},
609 })
610 records[len(records)-1].Scores = append(records[len(records)-1].Scores, score)
611 }
612 c.JSON(http.StatusOK, models.Response{
613 Success: true,
614 Message: "Successfully retrieved user scores.",
615 Data: ProfileResponse{
616 Profile: true,
617 SteamID: user.SteamID,
618 UserName: user.UserName,
619 AvatarLink: user.AvatarLink,
620 CountryCode: user.CountryCode,
621 Titles: user.Titles,
622 Links: links,
623 Rankings: rankings,
624 Records: records,
625 },
626 })
627}
628
629// PUT Profile
630//
631// @Description Update profile page of session user.
632// @Tags users
633// @Accept json
634// @Produce json
635// @Param Authorization header string true "JWT Token"
636// @Success 200 {object} models.Response{data=ProfileResponse}
637// @Failure 400 {object} models.Response
638// @Failure 401 {object} models.Response
639// @Router /profile [post]
640func UpdateUser(c *gin.Context) {
641 // Check if user exists
642 user, exists := c.Get("user")
643 if !exists {
644 c.JSON(http.StatusUnauthorized, models.ErrorResponse("User not logged in."))
645 return
646 }
647 profile, err := GetPlayerSummaries(user.(models.User).SteamID, os.Getenv("API_KEY"))
648 if err != nil {
649 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateSummaryFail)
650 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
651 return
652 }
653 // Update profile
654 _, err = database.DB.Exec(`UPDATE users SET username = $1, avatar_link = $2, country_code = $3, updated_at = $4
655 WHERE steam_id = $5`, profile.PersonaName, profile.AvatarFull, profile.LocCountryCode, time.Now().UTC(), user.(models.User).SteamID)
656 if err != nil {
657 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateFail)
658 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
659 return
660 }
661 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateSuccess)
662 c.JSON(http.StatusOK, models.Response{
663 Success: true,
664 Message: "Successfully updated user.",
665 Data: ProfileResponse{
666 Profile: true,
667 SteamID: user.(models.User).SteamID,
668 UserName: profile.PersonaName,
669 AvatarLink: profile.AvatarFull,
670 CountryCode: profile.LocCountryCode,
671 },
672 })
673}
674
675// PUT Profile/CountryCode
676//
677// @Description Update country code of session user.
678// @Tags users
679// @Accept json
680// @Produce json
681// @Param Authorization header string true "JWT Token"
682// @Param country_code query string true "Country Code [XX]"
683// @Success 200 {object} models.Response
684// @Failure 400 {object} models.Response
685// @Failure 401 {object} models.Response
686// @Router /profile [put]
687func UpdateCountryCode(c *gin.Context) {
688 // Check if user exists
689 user, exists := c.Get("user")
690 if !exists {
691 c.JSON(http.StatusUnauthorized, models.ErrorResponse("User not logged in."))
692 return
693 }
694 code := c.Query("country_code")
695 if code == "" {
696 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateCountryFail)
697 c.JSON(http.StatusNotFound, models.ErrorResponse("Enter a valid country code."))
698 return
699 }
700 var validCode string
701 err := database.DB.QueryRow(`SELECT country_code FROM countries WHERE country_code = $1`, code).Scan(&validCode)
702 if err != nil {
703 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateCountryFail)
704 c.JSON(http.StatusNotFound, models.ErrorResponse(err.Error()))
705 return
706 }
707 // Valid code, update profile
708 _, err = database.DB.Exec(`UPDATE users SET country_code = $1 WHERE steam_id = $2`, validCode, user.(models.User).SteamID)
709 if err != nil {
710 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateCountryFail)
711 c.JSON(http.StatusNotFound, models.ErrorResponse(err.Error()))
712 return
713 }
714 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateCountrySuccess)
715 c.JSON(http.StatusOK, models.Response{
716 Success: true,
717 Message: "Successfully updated country code.",
718 })
719}