aboutsummaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
authorArda Serdar Pektezol <1669855+pektezol@users.noreply.github.com>2025-10-27 22:12:31 +0300
committerArda Serdar Pektezol <1669855+pektezol@users.noreply.github.com>2025-10-27 23:13:45 +0400
commit96f55dba15bcbba4e6d8f14035ecfd20ebcea8a8 (patch)
treed33156305b0e34e1c28e294e021447635dde3531 /backend
parentfix/frontend: hide breakpoint in-between tablet and desktop on maps (#290) (diff)
downloadlphub-96f55dba15bcbba4e6d8f14035ecfd20ebcea8a8.tar.gz
lphub-96f55dba15bcbba4e6d8f14035ecfd20ebcea8a8.tar.bz2
lphub-96f55dba15bcbba4e6d8f14035ecfd20ebcea8a8.zip
feat/backend: timeline stats endpoint
Diffstat (limited to 'backend')
-rw-r--r--backend/api/routes.go6
-rw-r--r--backend/docs/docs.go148
-rw-r--r--backend/docs/swagger.json148
-rw-r--r--backend/docs/swagger.yaml87
-rw-r--r--backend/go.mod41
-rw-r--r--backend/go.sum90
-rw-r--r--backend/handlers/logs.go103
-rw-r--r--backend/handlers/record.go10
-rw-r--r--backend/handlers/stats.go204
-rw-r--r--backend/main.go36
10 files changed, 561 insertions, 312 deletions
diff --git a/backend/api/routes.go b/backend/api/routes.go
index 690f844..ab6e704 100644
--- a/backend/api/routes.go
+++ b/backend/api/routes.go
@@ -54,8 +54,8 @@ func InitRoutes(router *gin.Engine) {
54 v1.GET("/games/:gameid", RateLimit, handlers.FetchChapters) 54 v1.GET("/games/:gameid", RateLimit, handlers.FetchChapters)
55 v1.GET("/chapters/:chapterid", RateLimit, handlers.FetchChapterMaps) 55 v1.GET("/chapters/:chapterid", RateLimit, handlers.FetchChapterMaps)
56 v1.GET("/games/:gameid/maps", RateLimit, handlers.FetchMaps) 56 v1.GET("/games/:gameid/maps", RateLimit, handlers.FetchMaps)
57 // Logs 57 // Stats
58 v1.GET("/logs/score", RateLimit, handlers.ScoreLogs) 58 v1.GET("/stats/timeline", RateLimit, handlers.Timeline)
59 // v1.GET("/logs/mod", IsAuthenticated, handlers.ModLogs) 59 v1.GET("/stats/scores", RateLimit, handlers.Scores)
60 } 60 }
61} 61}
diff --git a/backend/docs/docs.go b/backend/docs/docs.go
index 44d53e1..7ce9a8b 100644
--- a/backend/docs/docs.go
+++ b/backend/docs/docs.go
@@ -250,37 +250,6 @@ const docTemplate = `{
250 } 250 }
251 } 251 }
252 }, 252 },
253 "/logs/score": {
254 "get": {
255 "description": "Get score logs of every player.",
256 "produces": [
257 "application/json"
258 ],
259 "tags": [
260 "logs"
261 ],
262 "responses": {
263 "200": {
264 "description": "OK",
265 "schema": {
266 "allOf": [
267 {
268 "$ref": "#/definitions/models.Response"
269 },
270 {
271 "type": "object",
272 "properties": {
273 "data": {
274 "$ref": "#/definitions/handlers.ScoreLogsResponse"
275 }
276 }
277 }
278 ]
279 }
280 }
281 }
282 }
283 },
284 "/maps/{mapid}/discussions": { 253 "/maps/{mapid}/discussions": {
285 "get": { 254 "get": {
286 "description": "Get map discussions with specified map id.", 255 "description": "Get map discussions with specified map id.",
@@ -1220,6 +1189,68 @@ const docTemplate = `{
1220 } 1189 }
1221 } 1190 }
1222 }, 1191 },
1192 "/stats/scores": {
1193 "get": {
1194 "description": "Get score logs of every player.",
1195 "produces": [
1196 "application/json"
1197 ],
1198 "tags": [
1199 "stats"
1200 ],
1201 "responses": {
1202 "200": {
1203 "description": "OK",
1204 "schema": {
1205 "allOf": [
1206 {
1207 "$ref": "#/definitions/models.Response"
1208 },
1209 {
1210 "type": "object",
1211 "properties": {
1212 "data": {
1213 "$ref": "#/definitions/handlers.ScoresResponse"
1214 }
1215 }
1216 }
1217 ]
1218 }
1219 }
1220 }
1221 }
1222 },
1223 "/stats/timeline": {
1224 "get": {
1225 "description": "Get the history of portal count world records over time.",
1226 "produces": [
1227 "application/json"
1228 ],
1229 "tags": [
1230 "stats"
1231 ],
1232 "responses": {
1233 "200": {
1234 "description": "OK",
1235 "schema": {
1236 "allOf": [
1237 {
1238 "$ref": "#/definitions/models.Response"
1239 },
1240 {
1241 "type": "object",
1242 "properties": {
1243 "data": {
1244 "$ref": "#/definitions/handlers.TimelineResponse"
1245 }
1246 }
1247 }
1248 ]
1249 }
1250 }
1251 }
1252 }
1253 },
1223 "/token": { 1254 "/token": {
1224 "get": { 1255 "get": {
1225 "description": "Gets the token cookie value from the user.", 1256 "description": "Gets the token cookie value from the user.",
@@ -1774,18 +1805,7 @@ const docTemplate = `{
1774 } 1805 }
1775 } 1806 }
1776 }, 1807 },
1777 "handlers.ScoreLogsResponse": { 1808 "handlers.ScoresDetails": {
1778 "type": "object",
1779 "properties": {
1780 "scores": {
1781 "type": "array",
1782 "items": {
1783 "$ref": "#/definitions/handlers.ScoreLogsResponseDetails"
1784 }
1785 }
1786 }
1787 },
1788 "handlers.ScoreLogsResponseDetails": {
1789 "type": "object", 1809 "type": "object",
1790 "properties": { 1810 "properties": {
1791 "date": { 1811 "date": {
@@ -1811,6 +1831,17 @@ const docTemplate = `{
1811 } 1831 }
1812 } 1832 }
1813 }, 1833 },
1834 "handlers.ScoresResponse": {
1835 "type": "object",
1836 "properties": {
1837 "scores": {
1838 "type": "array",
1839 "items": {
1840 "$ref": "#/definitions/handlers.ScoresDetails"
1841 }
1842 }
1843 }
1844 },
1814 "handlers.SearchResponse": { 1845 "handlers.SearchResponse": {
1815 "type": "object", 1846 "type": "object",
1816 "properties": { 1847 "properties": {
@@ -1860,6 +1891,34 @@ const docTemplate = `{
1860 } 1891 }
1861 } 1892 }
1862 }, 1893 },
1894 "handlers.TimelinePoint": {
1895 "type": "object",
1896 "properties": {
1897 "count": {
1898 "type": "integer"
1899 },
1900 "date": {
1901 "type": "string"
1902 }
1903 }
1904 },
1905 "handlers.TimelineResponse": {
1906 "type": "object",
1907 "properties": {
1908 "timeline_multiplayer": {
1909 "type": "array",
1910 "items": {
1911 "$ref": "#/definitions/handlers.TimelinePoint"
1912 }
1913 },
1914 "timeline_singleplayer": {
1915 "type": "array",
1916 "items": {
1917 "$ref": "#/definitions/handlers.TimelinePoint"
1918 }
1919 }
1920 }
1921 },
1863 "models.Category": { 1922 "models.Category": {
1864 "type": "object", 1923 "type": "object",
1865 "properties": { 1924 "properties": {
@@ -1945,6 +2004,9 @@ const docTemplate = `{
1945 "chapter_name": { 2004 "chapter_name": {
1946 "type": "string" 2005 "type": "string"
1947 }, 2006 },
2007 "difficulty": {
2008 "type": "integer"
2009 },
1948 "game_name": { 2010 "game_name": {
1949 "type": "string" 2011 "type": "string"
1950 }, 2012 },
diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json
index 6c10cfc..981d017 100644
--- a/backend/docs/swagger.json
+++ b/backend/docs/swagger.json
@@ -244,37 +244,6 @@
244 } 244 }
245 } 245 }
246 }, 246 },
247 "/logs/score": {
248 "get": {
249 "description": "Get score logs of every player.",
250 "produces": [
251 "application/json"
252 ],
253 "tags": [
254 "logs"
255 ],
256 "responses": {
257 "200": {
258 "description": "OK",
259 "schema": {
260 "allOf": [
261 {
262 "$ref": "#/definitions/models.Response"
263 },
264 {
265 "type": "object",
266 "properties": {
267 "data": {
268 "$ref": "#/definitions/handlers.ScoreLogsResponse"
269 }
270 }
271 }
272 ]
273 }
274 }
275 }
276 }
277 },
278 "/maps/{mapid}/discussions": { 247 "/maps/{mapid}/discussions": {
279 "get": { 248 "get": {
280 "description": "Get map discussions with specified map id.", 249 "description": "Get map discussions with specified map id.",
@@ -1214,6 +1183,68 @@
1214 } 1183 }
1215 } 1184 }
1216 }, 1185 },
1186 "/stats/scores": {
1187 "get": {
1188 "description": "Get score logs of every player.",
1189 "produces": [
1190 "application/json"
1191 ],
1192 "tags": [
1193 "stats"
1194 ],
1195 "responses": {
1196 "200": {
1197 "description": "OK",
1198 "schema": {
1199 "allOf": [
1200 {
1201 "$ref": "#/definitions/models.Response"
1202 },
1203 {
1204 "type": "object",
1205 "properties": {
1206 "data": {
1207 "$ref": "#/definitions/handlers.ScoresResponse"
1208 }
1209 }
1210 }
1211 ]
1212 }
1213 }
1214 }
1215 }
1216 },
1217 "/stats/timeline": {
1218 "get": {
1219 "description": "Get the history of portal count world records over time.",
1220 "produces": [
1221 "application/json"
1222 ],
1223 "tags": [
1224 "stats"
1225 ],
1226 "responses": {
1227 "200": {
1228 "description": "OK",
1229 "schema": {
1230 "allOf": [
1231 {
1232 "$ref": "#/definitions/models.Response"
1233 },
1234 {
1235 "type": "object",
1236 "properties": {
1237 "data": {
1238 "$ref": "#/definitions/handlers.TimelineResponse"
1239 }
1240 }
1241 }
1242 ]
1243 }
1244 }
1245 }
1246 }
1247 },
1217 "/token": { 1248 "/token": {
1218 "get": { 1249 "get": {
1219 "description": "Gets the token cookie value from the user.", 1250 "description": "Gets the token cookie value from the user.",
@@ -1768,18 +1799,7 @@
1768 } 1799 }
1769 } 1800 }
1770 }, 1801 },
1771 "handlers.ScoreLogsResponse": { 1802 "handlers.ScoresDetails": {
1772 "type": "object",
1773 "properties": {
1774 "scores": {
1775 "type": "array",
1776 "items": {
1777 "$ref": "#/definitions/handlers.ScoreLogsResponseDetails"
1778 }
1779 }
1780 }
1781 },
1782 "handlers.ScoreLogsResponseDetails": {
1783 "type": "object", 1803 "type": "object",
1784 "properties": { 1804 "properties": {
1785 "date": { 1805 "date": {
@@ -1805,6 +1825,17 @@
1805 } 1825 }
1806 } 1826 }
1807 }, 1827 },
1828 "handlers.ScoresResponse": {
1829 "type": "object",
1830 "properties": {
1831 "scores": {
1832 "type": "array",
1833 "items": {
1834 "$ref": "#/definitions/handlers.ScoresDetails"
1835 }
1836 }
1837 }
1838 },
1808 "handlers.SearchResponse": { 1839 "handlers.SearchResponse": {
1809 "type": "object", 1840 "type": "object",
1810 "properties": { 1841 "properties": {
@@ -1854,6 +1885,34 @@
1854 } 1885 }
1855 } 1886 }
1856 }, 1887 },
1888 "handlers.TimelinePoint": {
1889 "type": "object",
1890 "properties": {
1891 "count": {
1892 "type": "integer"
1893 },
1894 "date": {
1895 "type": "string"
1896 }
1897 }
1898 },
1899 "handlers.TimelineResponse": {
1900 "type": "object",
1901 "properties": {
1902 "timeline_multiplayer": {
1903 "type": "array",
1904 "items": {
1905 "$ref": "#/definitions/handlers.TimelinePoint"
1906 }
1907 },
1908 "timeline_singleplayer": {
1909 "type": "array",
1910 "items": {
1911 "$ref": "#/definitions/handlers.TimelinePoint"
1912 }
1913 }
1914 }
1915 },
1857 "models.Category": { 1916 "models.Category": {
1858 "type": "object", 1917 "type": "object",
1859 "properties": { 1918 "properties": {
@@ -1939,6 +1998,9 @@
1939 "chapter_name": { 1998 "chapter_name": {
1940 "type": "string" 1999 "type": "string"
1941 }, 2000 },
2001 "difficulty": {
2002 "type": "integer"
2003 },
1942 "game_name": { 2004 "game_name": {
1943 "type": "string" 2005 "type": "string"
1944 }, 2006 },
diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml
index 8f33b94..0b3258c 100644
--- a/backend/docs/swagger.yaml
+++ b/backend/docs/swagger.yaml
@@ -287,14 +287,7 @@ definitions:
287 score_time: 287 score_time:
288 type: integer 288 type: integer
289 type: object 289 type: object
290 handlers.ScoreLogsResponse: 290 handlers.ScoresDetails:
291 properties:
292 scores:
293 items:
294 $ref: '#/definitions/handlers.ScoreLogsResponseDetails'
295 type: array
296 type: object
297 handlers.ScoreLogsResponseDetails:
298 properties: 291 properties:
299 date: 292 date:
300 type: string 293 type: string
@@ -311,6 +304,13 @@ definitions:
311 user: 304 user:
312 $ref: '#/definitions/models.UserShort' 305 $ref: '#/definitions/models.UserShort'
313 type: object 306 type: object
307 handlers.ScoresResponse:
308 properties:
309 scores:
310 items:
311 $ref: '#/definitions/handlers.ScoresDetails'
312 type: array
313 type: object
314 handlers.SearchResponse: 314 handlers.SearchResponse:
315 properties: 315 properties:
316 maps: 316 maps:
@@ -343,6 +343,24 @@ definitions:
343 user_name: 343 user_name:
344 type: string 344 type: string
345 type: object 345 type: object
346 handlers.TimelinePoint:
347 properties:
348 count:
349 type: integer
350 date:
351 type: string
352 type: object
353 handlers.TimelineResponse:
354 properties:
355 timeline_multiplayer:
356 items:
357 $ref: '#/definitions/handlers.TimelinePoint'
358 type: array
359 timeline_singleplayer:
360 items:
361 $ref: '#/definitions/handlers.TimelinePoint'
362 type: array
363 type: object
346 models.Category: 364 models.Category:
347 properties: 365 properties:
348 id: 366 id:
@@ -398,6 +416,8 @@ definitions:
398 properties: 416 properties:
399 chapter_name: 417 chapter_name:
400 type: string 418 type: string
419 difficulty:
420 type: integer
401 game_name: 421 game_name:
402 type: string 422 type: string
403 id: 423 id:
@@ -672,23 +692,6 @@ paths:
672 type: object 692 type: object
673 tags: 693 tags:
674 - login 694 - login
675 /logs/score:
676 get:
677 description: Get score logs of every player.
678 produces:
679 - application/json
680 responses:
681 "200":
682 description: OK
683 schema:
684 allOf:
685 - $ref: '#/definitions/models.Response'
686 - properties:
687 data:
688 $ref: '#/definitions/handlers.ScoreLogsResponse'
689 type: object
690 tags:
691 - logs
692 /maps/{mapid}/discussions: 695 /maps/{mapid}/discussions:
693 get: 696 get:
694 description: Get map discussions with specified map id. 697 description: Get map discussions with specified map id.
@@ -1259,6 +1262,40 @@ paths:
1259 type: object 1262 type: object
1260 tags: 1263 tags:
1261 - search 1264 - search
1265 /stats/scores:
1266 get:
1267 description: Get score logs of every player.
1268 produces:
1269 - application/json
1270 responses:
1271 "200":
1272 description: OK
1273 schema:
1274 allOf:
1275 - $ref: '#/definitions/models.Response'
1276 - properties:
1277 data:
1278 $ref: '#/definitions/handlers.ScoresResponse'
1279 type: object
1280 tags:
1281 - stats
1282 /stats/timeline:
1283 get:
1284 description: Get the history of portal count world records over time.
1285 produces:
1286 - application/json
1287 responses:
1288 "200":
1289 description: OK
1290 schema:
1291 allOf:
1292 - $ref: '#/definitions/models.Response'
1293 - properties:
1294 data:
1295 $ref: '#/definitions/handlers.TimelineResponse'
1296 type: object
1297 tags:
1298 - stats
1262 /token: 1299 /token:
1263 delete: 1300 delete:
1264 description: Deletes the token cookie from the user. 1301 description: Deletes the token cookie from the user.
diff --git a/backend/go.mod b/backend/go.mod
index f9fe0db..df904e8 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -3,7 +3,7 @@ module lphub
3go 1.23.0 3go 1.23.0
4 4
5require ( 5require (
6 github.com/gin-gonic/gin v1.10.0 6 github.com/gin-gonic/gin v1.10.1
7 github.com/joho/godotenv v1.5.1 7 github.com/joho/godotenv v1.5.1
8) 8)
9 9
@@ -11,6 +11,8 @@ require (
11 github.com/Backblaze/blazer v0.7.1 11 github.com/Backblaze/blazer v0.7.1
12 github.com/golang-jwt/jwt/v4 v4.5.0 12 github.com/golang-jwt/jwt/v4 v4.5.0
13 github.com/google/uuid v1.6.0 13 github.com/google/uuid v1.6.0
14 github.com/newrelic/go-agent/v3 v3.40.1
15 github.com/newrelic/go-agent/v3/integrations/nrgin v1.4.1
14 github.com/pektezol/steam_go v1.1.2 16 github.com/pektezol/steam_go v1.1.2
15 github.com/swaggo/files v1.0.1 17 github.com/swaggo/files v1.0.1
16 github.com/swaggo/gin-swagger v1.6.0 18 github.com/swaggo/gin-swagger v1.6.0
@@ -20,34 +22,31 @@ require (
20 22
21require ( 23require (
22 github.com/KyleBanks/depth v1.2.1 // indirect 24 github.com/KyleBanks/depth v1.2.1 // indirect
23 github.com/bytedance/sonic v1.12.2 // indirect 25 github.com/bytedance/sonic v1.13.3 // indirect
24 github.com/bytedance/sonic/loader v0.2.0 // indirect 26 github.com/bytedance/sonic/loader v0.2.4 // indirect
25 github.com/cloudwego/base64x v0.1.4 // indirect 27 github.com/cloudwego/base64x v0.1.5 // indirect
26 github.com/cloudwego/iasm v0.2.0 // indirect 28 github.com/gabriel-vasile/mimetype v1.4.9 // indirect
27 github.com/gabriel-vasile/mimetype v1.4.5 // indirect
28 github.com/go-openapi/jsonpointer v0.21.0 // indirect 29 github.com/go-openapi/jsonpointer v0.21.0 // indirect
29 github.com/go-openapi/jsonreference v0.21.0 // indirect 30 github.com/go-openapi/jsonreference v0.21.0 // indirect
30 github.com/go-openapi/spec v0.21.0 // indirect 31 github.com/go-openapi/spec v0.21.0 // indirect
31 github.com/go-openapi/swag v0.23.0 // indirect 32 github.com/go-openapi/swag v0.23.0 // indirect
32 github.com/josharian/intern v1.0.0 // indirect 33 github.com/josharian/intern v1.0.0 // indirect
33 github.com/klauspost/cpuid/v2 v2.2.8 // indirect 34 github.com/klauspost/cpuid/v2 v2.2.10 // indirect
34 github.com/mailru/easyjson v0.7.7 // indirect 35 github.com/mailru/easyjson v0.7.7 // indirect
35 github.com/newrelic/go-agent/v3 v3.40.1 // indirect
36 github.com/newrelic/go-agent/v3/integrations/nrgin v1.4.1 // indirect
37 github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 36 github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
38 golang.org/x/arch v0.10.0 // indirect 37 golang.org/x/arch v0.18.0 // indirect
39 golang.org/x/tools v0.25.0 // indirect 38 golang.org/x/tools v0.33.0 // indirect
40 google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect 39 google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
41 google.golang.org/grpc v1.65.0 // indirect 40 google.golang.org/grpc v1.65.0 // indirect
42 gopkg.in/yaml.v3 v3.0.1 // indirect 41 gopkg.in/yaml.v3 v3.0.1 // indirect
43) 42)
44 43
45require ( 44require (
46 github.com/gin-contrib/sse v0.1.0 // indirect 45 github.com/gin-contrib/sse v1.1.0 // indirect
47 github.com/go-playground/locales v0.14.1 // indirect 46 github.com/go-playground/locales v0.14.1 // indirect
48 github.com/go-playground/universal-translator v0.18.1 // indirect 47 github.com/go-playground/universal-translator v0.18.1 // indirect
49 github.com/go-playground/validator/v10 v10.22.1 // indirect 48 github.com/go-playground/validator/v10 v10.26.0 // indirect
50 github.com/goccy/go-json v0.10.3 // indirect 49 github.com/goccy/go-json v0.10.5 // indirect
51 github.com/json-iterator/go v1.1.12 // indirect 50 github.com/json-iterator/go v1.1.12 // indirect
52 github.com/leodido/go-urn v1.4.0 // indirect 51 github.com/leodido/go-urn v1.4.0 // indirect
53 github.com/lib/pq v1.10.9 52 github.com/lib/pq v1.10.9
@@ -55,11 +54,11 @@ require (
55 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 54 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
56 github.com/modern-go/reflect2 v1.0.2 // indirect 55 github.com/modern-go/reflect2 v1.0.2 // indirect
57 github.com/pektezol/bitreader v1.4.3 56 github.com/pektezol/bitreader v1.4.3
58 github.com/pelletier/go-toml/v2 v2.2.3 // indirect 57 github.com/pelletier/go-toml/v2 v2.2.4 // indirect
59 github.com/ugorji/go/codec v1.2.12 // indirect 58 github.com/ugorji/go/codec v1.3.0 // indirect
60 golang.org/x/crypto v0.27.0 // indirect 59 golang.org/x/crypto v0.39.0 // indirect
61 golang.org/x/net v0.29.0 // indirect 60 golang.org/x/net v0.41.0 // indirect
62 golang.org/x/sys v0.25.0 // indirect 61 golang.org/x/sys v0.33.0 // indirect
63 golang.org/x/text v0.18.0 // indirect 62 golang.org/x/text v0.26.0 // indirect
64 google.golang.org/protobuf v1.34.2 // indirect 63 google.golang.org/protobuf v1.36.6 // indirect
65) 64)
diff --git a/backend/go.sum b/backend/go.sum
index f655023..b88dd43 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -2,26 +2,25 @@ github.com/Backblaze/blazer v0.7.1 h1:J43PbFj6hXLg1jvCNr+rQoAsxzKK0IP7ftl1ReCwpc
2github.com/Backblaze/blazer v0.7.1/go.mod h1:MhntL1nMpIuoqrPP6TnZu/xTydMgOAe/Xm6KongbjKs= 2github.com/Backblaze/blazer v0.7.1/go.mod h1:MhntL1nMpIuoqrPP6TnZu/xTydMgOAe/Xm6KongbjKs=
3github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= 3github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
4github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= 4github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
5github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg= 5github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
6github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= 6github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
7github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 7github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
8github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= 8github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
9github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 9github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
10github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= 10github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
11github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= 11github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
12github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
13github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= 12github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
14github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 13github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
15github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 14github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
16github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 15github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
17github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= 16github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
18github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= 17github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
19github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= 18github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
20github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= 19github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
21github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 20github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
22github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 21github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
23github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= 22github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
24github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= 23github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
25github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= 24github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
26github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= 25github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
27github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= 26github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
@@ -36,14 +35,14 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
36github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 35github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
37github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 36github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
38github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 37github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
39github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= 38github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
40github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= 39github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
41github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= 40github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
42github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 41github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
43github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= 42github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
44github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 43github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
45github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 44github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
46github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 45github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
47github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 46github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
48github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 47github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
49github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 48github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -54,8 +53,8 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
54github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 53github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
55github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 54github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
56github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 55github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
57github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= 56github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
58github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 57github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
59github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 58github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
60github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 59github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
61github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 60github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -82,8 +81,8 @@ github.com/pektezol/bitreader v1.4.3 h1:+WjsD6qOAaI6Q1jOOlEJcnaEso8vPMKRZnnaDnZh
82github.com/pektezol/bitreader v1.4.3/go.mod h1:xBQEsQpOf8B5yPrnOTkirZGyVUV6Bqp0ups2RIlTskk= 81github.com/pektezol/bitreader v1.4.3/go.mod h1:xBQEsQpOf8B5yPrnOTkirZGyVUV6Bqp0ups2RIlTskk=
83github.com/pektezol/steam_go v1.1.2 h1:fta6SW+La8NfmCtR/Kn73bAmTBvCgUkkLCplsJGzx7g= 82github.com/pektezol/steam_go v1.1.2 h1:fta6SW+La8NfmCtR/Kn73bAmTBvCgUkkLCplsJGzx7g=
84github.com/pektezol/steam_go v1.1.2/go.mod h1:8dk95CLOQKRr0BA8ChnNbTEe0/f2Ibi5O4rmpS9oZCo= 83github.com/pektezol/steam_go v1.1.2/go.mod h1:8dk95CLOQKRr0BA8ChnNbTEe0/f2Ibi5O4rmpS9oZCo=
85github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= 84github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
86github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= 85github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
87github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 86github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
88github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 87github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
89github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= 88github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
@@ -96,8 +95,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
96github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 95github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
97github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 96github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
98github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 97github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
99github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 98github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
100github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 99github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
101github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= 100github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
102github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= 101github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
103github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= 102github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
@@ -106,28 +105,28 @@ github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
106github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= 105github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
107github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 106github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
108github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 107github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
109github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 108github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
110github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 109github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
111github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 110github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
112golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= 111golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
113golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 112golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
114golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 113golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
115golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 114golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
116golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= 115golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
117golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= 116golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
118golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 117golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
119golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= 118golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
120golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 119golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
121golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 120golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
122golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 121golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
123golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 122golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
124golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 123golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
125golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= 124golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
126golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= 125golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
127golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 126golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
128golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 127golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
129golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= 128golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
130golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 129golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
131golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 130golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
132golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 131golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
133golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 132golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -135,8 +134,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
135golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 134golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
136golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 135golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
137golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 136golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
138golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= 137golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
139golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 138golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
140golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 139golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
141golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 140golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
142golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 141golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -144,23 +143,22 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
144golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 143golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
145golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 144golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
146golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 145golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
147golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= 146golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
148golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 147golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
149golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= 148golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
150golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 149golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
151golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 150golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
152golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 151golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
153golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 152golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
154golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= 153golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
155golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= 154golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
156golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 155golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
157google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0=
158google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= 156google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8=
159google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= 157google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
160google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= 158google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
161google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= 159google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
162google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= 160google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
163google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 161google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
164gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 162gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
165gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 163gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
166gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 164gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/backend/handlers/logs.go b/backend/handlers/logs.go
deleted file mode 100644
index 693c448..0000000
--- a/backend/handlers/logs.go
+++ /dev/null
@@ -1,103 +0,0 @@
1package handlers
2
3import (
4 "net/http"
5 "time"
6
7 "lphub/database"
8 "lphub/models"
9
10 "github.com/gin-gonic/gin"
11)
12
13type Log struct {
14 User models.UserShort `json:"user"`
15 Type string `json:"type"`
16 Description string `json:"description"`
17 Message string `json:"message"`
18 Date time.Time `json:"date"`
19}
20
21type LogsResponse struct {
22 Logs []LogsResponseDetails `json:"logs"`
23}
24
25type LogsResponseDetails struct {
26 User models.UserShort `json:"user"`
27 Log string `json:"detail"`
28 Message string `json:"message"`
29 Date time.Time `json:"date"`
30}
31
32type ScoreLogsResponse struct {
33 Logs []ScoreLogsResponseDetails `json:"scores"`
34}
35
36type ScoreLogsResponseDetails struct {
37 Game models.Game `json:"game"`
38 User models.UserShort `json:"user"`
39 Map models.MapShort `json:"map"`
40 ScoreCount int `json:"score_count"`
41 ScoreTime int `json:"score_time"`
42 DemoID string `json:"demo_id"`
43 Date time.Time `json:"date"`
44}
45
46// GET Score Logs
47//
48// @Description Get score logs of every player.
49// @Tags logs
50// @Produce json
51// @Success 200 {object} models.Response{data=ScoreLogsResponse}
52// @Router /logs/score [get]
53func ScoreLogs(c *gin.Context) {
54 response := ScoreLogsResponse{Logs: []ScoreLogsResponseDetails{}}
55 sql := `SELECT g.id,
56 g."name",
57 g.is_coop,
58 rs.map_id,
59 m.name AS map_name,
60 u.steam_id,
61 u.user_name,
62 rs.score_count,
63 rs.score_time,
64 rs.demo_id,
65 rs.record_date
66 FROM (
67 SELECT id, map_id, user_id, score_count, score_time, demo_id, record_date
68 FROM records_sp WHERE is_deleted = false
69
70 UNION ALL
71
72 SELECT id, map_id, host_id AS user_id, score_count, score_time, host_demo_id AS demo_id, record_date
73 FROM records_mp WHERE is_deleted = false
74
75 UNION ALL
76
77 SELECT id, map_id, partner_id AS user_id, score_count, score_time, partner_demo_id AS demo_id, record_date
78 FROM records_mp WHERE is_deleted = false
79 ) AS rs
80 JOIN users u ON rs.user_id = u.steam_id
81 JOIN maps m ON rs.map_id = m.id
82 JOIN games g ON m.game_id = g.id
83 ORDER BY rs.record_date DESC LIMIT 100;`
84 rows, err := database.DB.Query(sql)
85 if err != nil {
86 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
87 return
88 }
89 for rows.Next() {
90 score := ScoreLogsResponseDetails{}
91 err = rows.Scan(&score.Game.ID, &score.Game.Name, &score.Game.IsCoop, &score.Map.ID, &score.Map.Name, &score.User.SteamID, &score.User.UserName, &score.ScoreCount, &score.ScoreTime, &score.DemoID, &score.Date)
92 if err != nil {
93 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
94 return
95 }
96 response.Logs = append(response.Logs, score)
97 }
98 c.JSON(http.StatusOK, models.Response{
99 Success: true,
100 Message: "Successfully retrieved score logs.",
101 Data: response,
102 })
103}
diff --git a/backend/handlers/record.go b/backend/handlers/record.go
index 25a6c6d..07ae5a8 100644
--- a/backend/handlers/record.go
+++ b/backend/handlers/record.go
@@ -35,11 +35,11 @@ type RecordResponse struct {
35// @Tags maps / leaderboards 35// @Tags maps / leaderboards
36// @Accept mpfd 36// @Accept mpfd
37// @Produce json 37// @Produce json
38// @Param mapid path int true "Map ID" 38// @Param mapid path int true "Map ID"
39// @Param Authorization header string true "JWT Token" 39// @Param Authorization header string true "JWT Token"
40// @Param host_demo formData file true "Host Demo" 40// @Param host_demo formData file true "Host Demo"
41// @Param partner_demo formData file false "Partner Demo" 41// @Param partner_demo formData file false "Partner Demo"
42// @Success 200 {object} models.Response{data=RecordResponse} 42// @Success 200 {object} models.Response{data=RecordResponse}
43// @Router /maps/{mapid}/record [post] 43// @Router /maps/{mapid}/record [post]
44func CreateRecordWithDemo(c *gin.Context) { 44func CreateRecordWithDemo(c *gin.Context) {
45 id := c.Param("mapid") 45 id := c.Param("mapid")
diff --git a/backend/handlers/stats.go b/backend/handlers/stats.go
new file mode 100644
index 0000000..8fe98f9
--- /dev/null
+++ b/backend/handlers/stats.go
@@ -0,0 +1,204 @@
1package handlers
2
3import (
4 "net/http"
5 "time"
6
7 "lphub/database"
8 "lphub/models"
9
10 "github.com/gin-gonic/gin"
11)
12
13type ScoresResponse struct {
14 Logs []ScoresDetails `json:"scores"`
15}
16
17type ScoresDetails struct {
18 Game models.Game `json:"game"`
19 User models.UserShort `json:"user"`
20 Map models.MapShort `json:"map"`
21 ScoreCount int `json:"score_count"`
22 ScoreTime int `json:"score_time"`
23 DemoID string `json:"demo_id"`
24 Date time.Time `json:"date"`
25}
26
27type TimelinePoint struct {
28 Date string `json:"date"`
29 Count int `json:"count"`
30}
31
32type TimelineResponse struct {
33 Singleplayer []TimelinePoint `json:"timeline_singleplayer"`
34 Multiplayer []TimelinePoint `json:"timeline_multiplayer"`
35}
36
37// GET Scores
38//
39// @Description Get score logs of every player.
40// @Tags stats
41// @Produce json
42// @Success 200 {object} models.Response{data=ScoresResponse}
43// @Router /stats/scores [get]
44func Scores(c *gin.Context) {
45 response := ScoresResponse{Logs: []ScoresDetails{}}
46 sql := `SELECT g.id,
47 g."name",
48 g.is_coop,
49 rs.map_id,
50 m.name AS map_name,
51 u.steam_id,
52 u.user_name,
53 rs.score_count,
54 rs.score_time,
55 rs.demo_id,
56 rs.record_date
57 FROM (
58 SELECT id, map_id, user_id, score_count, score_time, demo_id, record_date
59 FROM records_sp WHERE is_deleted = false
60
61 UNION ALL
62
63 SELECT id, map_id, host_id AS user_id, score_count, score_time, host_demo_id AS demo_id, record_date
64 FROM records_mp WHERE is_deleted = false
65
66 UNION ALL
67
68 SELECT id, map_id, partner_id AS user_id, score_count, score_time, partner_demo_id AS demo_id, record_date
69 FROM records_mp WHERE is_deleted = false
70 ) AS rs
71 JOIN users u ON rs.user_id = u.steam_id
72 JOIN maps m ON rs.map_id = m.id
73 JOIN games g ON m.game_id = g.id
74 ORDER BY rs.record_date DESC LIMIT 100;`
75 rows, err := database.DB.Query(sql)
76 if err != nil {
77 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
78 return
79 }
80 for rows.Next() {
81 score := ScoresDetails{}
82 err = rows.Scan(&score.Game.ID, &score.Game.Name, &score.Game.IsCoop, &score.Map.ID, &score.Map.Name, &score.User.SteamID, &score.User.UserName, &score.ScoreCount, &score.ScoreTime, &score.DemoID, &score.Date)
83 if err != nil {
84 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
85 return
86 }
87 response.Logs = append(response.Logs, score)
88 }
89 c.JSON(http.StatusOK, models.Response{
90 Success: true,
91 Message: "Successfully retrieved score logs.",
92 Data: response,
93 })
94}
95
96// GET Timeline
97//
98// @Description Get the history of portal count world records over time.
99// @Tags stats
100// @Produce json
101// @Success 200 {object} models.Response{data=TimelineResponse}
102// @Router /stats/timeline [get]
103func Timeline(c *gin.Context) {
104 result := TimelineResponse{
105 Singleplayer: []TimelinePoint{},
106 Multiplayer: []TimelinePoint{},
107 }
108 spQuery := `
109 WITH date_series AS (
110 SELECT DISTINCT record_date as date
111 FROM map_history
112 WHERE category_id = 1 AND map_id <= 60 AND record_date >= '2013-01-31'
113 ORDER BY record_date
114 ),
115 map_best_at_date AS (
116 SELECT
117 ds.date,
118 mh.map_id,
119 MIN(mh.score_count) as best_count
120 FROM date_series ds
121 CROSS JOIN (SELECT DISTINCT map_id FROM map_history WHERE category_id = 1 AND map_id <= 60) maps
122 LEFT JOIN map_history mh ON mh.map_id = maps.map_id
123 AND mh.category_id = 1
124 AND mh.record_date <= ds.date
125 GROUP BY ds.date, mh.map_id
126 )
127 SELECT
128 date,
129 SUM(best_count) as total_count
130 FROM map_best_at_date
131 GROUP BY date
132 ORDER BY date ASC;
133 `
134
135 mpQuery := `
136 WITH date_series AS (
137 SELECT DISTINCT record_date as date
138 FROM map_history
139 WHERE category_id = 1 AND map_id > 60 AND record_date >= '2011-12-21'
140 ORDER BY record_date
141 ),
142 map_best_at_date AS (
143 SELECT
144 ds.date,
145 mh.map_id,
146 MIN(mh.score_count) as best_count
147 FROM date_series ds
148 CROSS JOIN (SELECT DISTINCT map_id FROM map_history WHERE category_id = 1 AND map_id > 60) maps
149 LEFT JOIN map_history mh ON mh.map_id = maps.map_id
150 AND mh.category_id = 1
151 AND mh.record_date <= ds.date
152 GROUP BY ds.date, mh.map_id
153 )
154 SELECT
155 date,
156 SUM(best_count) as total_count
157 FROM map_best_at_date
158 GROUP BY date
159 ORDER BY date ASC;
160 `
161
162 rows, err := database.DB.Query(spQuery)
163 if err != nil {
164 return
165 }
166 defer rows.Close()
167
168 for rows.Next() {
169 var dateTime time.Time
170 var count int
171 if err := rows.Scan(&dateTime, &count); err != nil {
172 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
173 return
174 }
175 result.Singleplayer = append(result.Singleplayer, TimelinePoint{
176 Date: dateTime.Format("2006-01-02"),
177 Count: count,
178 })
179 }
180
181 rows, err = database.DB.Query(mpQuery)
182 if err != nil {
183 return
184 }
185 defer rows.Close()
186
187 for rows.Next() {
188 var dateTime time.Time
189 var count int
190 if err := rows.Scan(&dateTime, &count); err != nil {
191 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
192 return
193 }
194 result.Multiplayer = append(result.Multiplayer, TimelinePoint{
195 Date: dateTime.Format("2006-01-02"),
196 Count: count,
197 })
198 }
199 c.JSON(http.StatusOK, models.Response{
200 Success: true,
201 Message: "Successfully retrieved portal count timeline.",
202 Data: result,
203 })
204}
diff --git a/backend/main.go b/backend/main.go
index e422359..2147be8 100644
--- a/backend/main.go
+++ b/backend/main.go
@@ -29,35 +29,25 @@ func main() {
29 if err != nil { 29 if err != nil {
30 log.Fatal("Error loading .env file") 30 log.Fatal("Error loading .env file")
31 } 31 }
32 var app *newrelic.Application
32 if os.Getenv("ENV") == "PROD" { 33 if os.Getenv("ENV") == "PROD" {
33 gin.SetMode(gin.ReleaseMode) 34 gin.SetMode(gin.ReleaseMode)
34 } 35 app, err = newrelic.NewApplication(
35 app, err := newrelic.NewApplication( 36 newrelic.ConfigAppName("lphub"),
36 newrelic.ConfigAppName("lphub"), 37 newrelic.ConfigLicense(os.Getenv("NEWRELIC_LICENSE_KEY")),
37 newrelic.ConfigLicense(os.Getenv("NEWRELIC_LICENSE_KEY")), 38 newrelic.ConfigAppLogForwardingEnabled(true),
38 newrelic.ConfigAppLogForwardingEnabled(true), 39 )
39 ) 40 if err != nil {
40 if err != nil { 41 log.Fatal("Error instrumenting newrelic")
41 log.Fatal("Error instrumenting newrelic") 42 }
42 } 43 }
43 router := gin.Default() 44 router := gin.Default()
44 router.Use(nrgin.Middleware(app)) 45 if app != nil {
46 router.Use(nrgin.Middleware(app))
47 }
48 // router.Use(cors.Default()) // ONLY FOR DEV
45 database.ConnectDB() 49 database.ConnectDB()
46 api.InitRoutes(router) 50 api.InitRoutes(router)
47 // for debugging
48 // router.Use(cors.New(cors.Config{
49 // AllowOrigins: []string{"*"},
50 // AllowMethods: []string{"GET", "POST", "DELETE", "PUT", "PATCH"},
51 // AllowHeaders: []string{"Origin"},
52 // ExposeHeaders: []string{"Content-Length"},
53 // AllowCredentials: true,
54 // MaxAge: 12 * time.Hour,
55 // }))
56 // router.Static("/static", "../frontend/build/static")
57 // router.StaticFile("/", "../frontend/build/index.html")
58 // router.NoRoute(func(c *gin.Context) {
59 // c.File("../frontend/build/index.html")
60 // })
61 router.MaxMultipartMemory = 250 << 20 // 250 mb limit for demos 51 router.MaxMultipartMemory = 250 << 20 // 250 mb limit for demos
62 router.Run(fmt.Sprintf(":%s", os.Getenv("PORT"))) 52 router.Run(fmt.Sprintf(":%s", os.Getenv("PORT")))
63} 53}