aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/api/auth.go19
-rw-r--r--backend/api/routes.go98
-rw-r--r--backend/database/functions.sql14
-rw-r--r--backend/database/init.sql45
-rw-r--r--backend/docs/docs.go68
-rw-r--r--backend/docs/swagger.json68
-rw-r--r--backend/docs/swagger.yaml41
-rw-r--r--backend/handlers/discussions.go24
-rw-r--r--backend/handlers/login.go3
-rw-r--r--backend/handlers/logs.go98
-rw-r--r--backend/handlers/mod.go36
-rw-r--r--backend/handlers/record.go31
-rw-r--r--backend/handlers/user.go28
13 files changed, 106 insertions, 467 deletions
diff --git a/backend/api/auth.go b/backend/api/auth.go
index 621a68b..a1f859c 100644
--- a/backend/api/auth.go
+++ b/backend/api/auth.go
@@ -2,6 +2,7 @@ package api
2 2
3import ( 3import (
4 "fmt" 4 "fmt"
5 "net/http"
5 "os" 6 "os"
6 "time" 7 "time"
7 8
@@ -12,7 +13,7 @@ import (
12 "github.com/golang-jwt/jwt/v4" 13 "github.com/golang-jwt/jwt/v4"
13) 14)
14 15
15func CheckAuth(c *gin.Context) { 16func IsAuthenticated(c *gin.Context) {
16 tokenString := c.GetHeader("Authorization") 17 tokenString := c.GetHeader("Authorization")
17 // Validate token 18 // Validate token
18 token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { 19 token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
@@ -22,17 +23,17 @@ func CheckAuth(c *gin.Context) {
22 return []byte(os.Getenv("SECRET_KEY")), nil 23 return []byte(os.Getenv("SECRET_KEY")), nil
23 }) 24 })
24 if token == nil { 25 if token == nil {
25 c.Next() 26 c.AbortWithStatusJSON(http.StatusOK, models.ErrorResponse("Token is nil."))
26 return 27 return
27 } 28 }
28 if err != nil { 29 if err != nil {
29 c.Next() 30 c.AbortWithStatusJSON(http.StatusOK, models.ErrorResponse("Token is invalid."))
30 return 31 return
31 } 32 }
32 if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { 33 if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
33 // Check exp 34 // Check exp
34 if float64(time.Now().Unix()) > claims["exp"].(float64) { 35 if float64(time.Now().Unix()) > claims["exp"].(float64) {
35 c.Next() 36 c.AbortWithStatusJSON(http.StatusOK, models.ErrorResponse("Token expired."))
36 return 37 return
37 } 38 }
38 // Get user from DB 39 // Get user from DB
@@ -41,7 +42,7 @@ func CheckAuth(c *gin.Context) {
41 &user.SteamID, &user.UserName, &user.AvatarLink, 42 &user.SteamID, &user.UserName, &user.AvatarLink,
42 &user.CountryCode, &user.CreatedAt, &user.UpdatedAt) 43 &user.CountryCode, &user.CreatedAt, &user.UpdatedAt)
43 if user.SteamID == "" { 44 if user.SteamID == "" {
44 c.Next() 45 c.AbortWithStatusJSON(http.StatusOK, models.ErrorResponse("Token does not match a user."))
45 return 46 return
46 } 47 }
47 // Get user titles from DB 48 // Get user titles from DB
@@ -56,11 +57,17 @@ func CheckAuth(c *gin.Context) {
56 } 57 }
57 user.Titles = append(user.Titles, title) 58 user.Titles = append(user.Titles, title)
58 } 59 }
60 // Set user id variable in db session for audit logging
61 _, err = database.DB.Exec(fmt.Sprintf("SET app.user_id = '%s';", user.SteamID))
62 if err != nil {
63 c.AbortWithStatusJSON(http.StatusOK, models.ErrorResponse("Session failed to start."))
64 return
65 }
59 c.Set("user", user) 66 c.Set("user", user)
60 c.Set("mod", moderator) 67 c.Set("mod", moderator)
61 c.Next() 68 c.Next()
62 } else { 69 } else {
63 c.Next() 70 c.AbortWithStatusJSON(http.StatusOK, models.ErrorResponse("Token is invalid."))
64 return 71 return
65 } 72 }
66} 73}
diff --git a/backend/api/routes.go b/backend/api/routes.go
index 81f1ec6..ecfb54b 100644
--- a/backend/api/routes.go
+++ b/backend/api/routes.go
@@ -8,82 +8,54 @@ import (
8 ginSwagger "github.com/swaggo/gin-swagger" 8 ginSwagger "github.com/swaggo/gin-swagger"
9) 9)
10 10
11const (
12 apiPath string = "/api"
13 v1Path string = "/v1"
14 swaggerPath string = "/swagger/*any"
15 indexPath string = "/"
16 tokenPath string = "/token"
17 loginPath string = "/login"
18 profilePath string = "/profile"
19 usersPath string = "/users/:userid"
20 demosPath string = "/demos"
21 mapSummaryPath string = "/maps/:mapid/summary"
22 mapImagePath string = "/maps/:mapid/image"
23 mapLeaderboardsPath string = "/maps/:mapid/leaderboards"
24 mapRecordPath string = "/maps/:mapid/record"
25 mapRecordIDPath string = "/maps/:mapid/record/:recordid"
26 mapDiscussionsPath string = "/maps/:mapid/discussions"
27 mapDiscussionIDPath string = "/maps/:mapid/discussions/:discussionid"
28 rankingsLPHUBPath string = "/rankings/lphub"
29 rankingsSteamPath string = "/rankings/steam"
30 searchPath string = "/search"
31 gamesPath string = "/games"
32 chaptersPath string = "/games/:gameid"
33 gameMapsPath string = "/games/:gameid/maps"
34 chapterMapsPath string = "/chapters/:chapterid"
35 scoreLogsPath string = "/logs/score"
36 modLogsPath string = "/logs/mod"
37)
38
39func InitRoutes(router *gin.Engine) { 11func InitRoutes(router *gin.Engine) {
40 api := router.Group(apiPath) 12 api := router.Group("/api")
41 { 13 {
42 v1 := api.Group(v1Path) 14 v1 := api.Group("/v1")
43 // Swagger 15 // Swagger
44 v1.GET(swaggerPath, ginSwagger.WrapHandler(swaggerfiles.Handler)) 16 v1.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
45 v1.GET(indexPath, func(c *gin.Context) { 17 v1.GET("/", func(c *gin.Context) {
46 c.File("docs/index.html") 18 c.File("docs/index.html")
47 }) 19 })
48 // Tokens, login 20 // Tokens, login
49 v1.GET(tokenPath, handlers.GetCookie) 21 v1.GET("/token", handlers.GetCookie)
50 v1.DELETE(tokenPath, handlers.DeleteCookie) 22 v1.DELETE("/token", handlers.DeleteCookie)
51 v1.GET(loginPath, handlers.Login) 23 v1.GET("/login", handlers.Login)
52 // Users, profiles 24 // Users, profiles
53 v1.GET(profilePath, CheckAuth, handlers.Profile) 25 v1.GET("/profile", IsAuthenticated, handlers.Profile)
54 v1.PUT(profilePath, CheckAuth, handlers.UpdateCountryCode) 26 v1.PUT("/profile", IsAuthenticated, handlers.UpdateCountryCode)
55 v1.POST(profilePath, CheckAuth, handlers.UpdateUser) 27 v1.POST("/profile", IsAuthenticated, handlers.UpdateUser)
56 v1.GET(usersPath, CheckAuth, handlers.FetchUser) 28 v1.GET("/users/:userid", IsAuthenticated, handlers.FetchUser)
57 // Maps 29 // Maps
58 // - Summary 30 // - Summary
59 v1.GET(mapSummaryPath, handlers.FetchMapSummary) 31 v1.GET("/maps/:mapid/summary", handlers.FetchMapSummary)
60 v1.POST(mapSummaryPath, CheckAuth, handlers.CreateMapSummary) 32 v1.POST("/maps/:mapid/summary", IsAuthenticated, handlers.CreateMapSummary)
61 v1.PUT(mapSummaryPath, CheckAuth, handlers.EditMapSummary) 33 v1.PUT("/maps/:mapid/summary", IsAuthenticated, handlers.EditMapSummary)
62 v1.DELETE(mapSummaryPath, CheckAuth, handlers.DeleteMapSummary) 34 v1.DELETE("/maps/:mapid/summary", IsAuthenticated, handlers.DeleteMapSummary)
63 v1.PUT(mapImagePath, CheckAuth, handlers.EditMapImage) 35 v1.PUT("/maps/:mapid/image", IsAuthenticated, handlers.EditMapImage)
64 // - Leaderboards 36 // - Leaderboards
65 v1.GET(mapLeaderboardsPath, handlers.FetchMapLeaderboards) 37 v1.GET("/maps/:mapid/leaderboards", handlers.FetchMapLeaderboards)
66 v1.POST(mapRecordPath, CheckAuth, handlers.CreateRecordWithDemo) 38 v1.POST("/maps/:mapid/record", IsAuthenticated, handlers.CreateRecordWithDemo)
67 v1.DELETE(mapRecordIDPath, CheckAuth, handlers.DeleteRecord) 39 v1.DELETE("/maps/:mapid/record/:recordid", IsAuthenticated, handlers.DeleteRecord)
68 v1.GET(demosPath, handlers.DownloadDemoWithID) 40 v1.GET("/demos", handlers.DownloadDemoWithID)
69 // - Discussions 41 // - Discussions
70 v1.GET(mapDiscussionsPath, handlers.FetchMapDiscussions) 42 v1.GET("/maps/:mapid/discussions", handlers.FetchMapDiscussions)
71 v1.GET(mapDiscussionIDPath, handlers.FetchMapDiscussion) 43 v1.GET("/maps/:mapid/discussions/:discussionid", handlers.FetchMapDiscussion)
72 v1.POST(mapDiscussionsPath, CheckAuth, handlers.CreateMapDiscussion) 44 v1.POST("/maps/:mapid/discussions", IsAuthenticated, handlers.CreateMapDiscussion)
73 v1.POST(mapDiscussionIDPath, CheckAuth, handlers.CreateMapDiscussionComment) 45 v1.POST("/maps/:mapid/discussions/:discussionid", IsAuthenticated, handlers.CreateMapDiscussionComment)
74 v1.PUT(mapDiscussionIDPath, CheckAuth, handlers.EditMapDiscussion) 46 v1.PUT("/maps/:mapid/discussions/:discussionid", IsAuthenticated, handlers.EditMapDiscussion)
75 v1.DELETE(mapDiscussionIDPath, CheckAuth, handlers.DeleteMapDiscussion) 47 v1.DELETE("/maps/:mapid/discussions/:discussionid", IsAuthenticated, handlers.DeleteMapDiscussion)
76 // Rankings, search 48 // Rankings, search
77 v1.GET(rankingsLPHUBPath, handlers.RankingsLPHUB) 49 v1.GET("/rankings/lphub", handlers.RankingsLPHUB)
78 v1.GET(rankingsSteamPath, handlers.RankingsSteam) 50 v1.GET("/rankings/steam", handlers.RankingsSteam)
79 v1.GET(searchPath, handlers.SearchWithQuery) 51 v1.GET("/search", handlers.SearchWithQuery)
80 // Games, chapters, maps 52 // Games, chapters, maps
81 v1.GET(gamesPath, handlers.FetchGames) 53 v1.GET("/games", handlers.FetchGames)
82 v1.GET(chaptersPath, handlers.FetchChapters) 54 v1.GET("/games/:gameid", handlers.FetchChapters)
83 v1.GET(chapterMapsPath, handlers.FetchChapterMaps) 55 v1.GET("/chapters/:chapterid", handlers.FetchChapterMaps)
84 v1.GET(gameMapsPath, handlers.FetchMaps) 56 v1.GET("/games/:gameid/maps", handlers.FetchMaps)
85 // Logs 57 // Logs
86 v1.GET(scoreLogsPath, handlers.ScoreLogs) 58 v1.GET("/logs/score", handlers.ScoreLogs)
87 v1.GET(modLogsPath, CheckAuth, handlers.ModLogs) 59 // v1.GET("/logs/mod", IsAuthenticated, handlers.ModLogs)
88 } 60 }
89} 61}
diff --git a/backend/database/functions.sql b/backend/database/functions.sql
index ca33a60..6a6f6d2 100644
--- a/backend/database/functions.sql
+++ b/backend/database/functions.sql
@@ -1,3 +1,17 @@
1CREATE OR REPLACE FUNCTION log_audit() RETURNS TRIGGER AS $$
2BEGIN
3 INSERT INTO audit (table_name, operation_type, old_data, new_data, changed_by)
4 VALUES (
5 TG_TABLE_NAME,
6 TG_OP,
7 CASE WHEN TG_OP = 'DELETE' OR TG_OP = 'UPDATE' THEN row_to_json(OLD) ELSE NULL END,
8 CASE WHEN TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN row_to_json(NEW) ELSE NULL END,
9 current_setting('app.user_id')::TEXT
10 );
11 RETURN NULL;
12END;
13$$ LANGUAGE plpgsql;
14
1CREATE OR REPLACE FUNCTION get_rankings_singleplayer() 15CREATE OR REPLACE FUNCTION get_rankings_singleplayer()
2RETURNS TABLE ( 16RETURNS TABLE (
3 steam_id TEXT, 17 steam_id TEXT,
diff --git a/backend/database/init.sql b/backend/database/init.sql
index 77a88f5..51bf2af 100644
--- a/backend/database/init.sql
+++ b/backend/database/init.sql
@@ -12,6 +12,10 @@ CREATE TABLE users (
12 PRIMARY KEY (steam_id) 12 PRIMARY KEY (steam_id)
13); 13);
14 14
15CREATE TRIGGER "users"
16AFTER INSERT OR UPDATE OR DELETE ON "users"
17FOR EACH ROW EXECUTE FUNCTION log_audit();
18
15CREATE TABLE games ( 19CREATE TABLE games (
16 id SERIAL, 20 id SERIAL,
17 name TEXT NOT NULL, 21 name TEXT NOT NULL,
@@ -72,6 +76,10 @@ CREATE TABLE map_history (
72 UNIQUE (map_id, category_id, score_count) 76 UNIQUE (map_id, category_id, score_count)
73); 77);
74 78
79CREATE TRIGGER "map_history"
80AFTER INSERT OR UPDATE OR DELETE ON "map_history"
81FOR EACH ROW EXECUTE FUNCTION log_audit();
82
75CREATE TABLE map_ratings ( 83CREATE TABLE map_ratings (
76 id SERIAL, 84 id SERIAL,
77 map_id SMALLINT NOT NULL, 85 map_id SMALLINT NOT NULL,
@@ -98,6 +106,10 @@ CREATE TABLE map_discussions (
98 FOREIGN KEY (user_id) REFERENCES users(steam_id) 106 FOREIGN KEY (user_id) REFERENCES users(steam_id)
99); 107);
100 108
109CREATE TRIGGER "map_discussions"
110AFTER INSERT OR UPDATE OR DELETE ON "map_discussions"
111FOR EACH ROW EXECUTE FUNCTION log_audit();
112
101CREATE TABLE map_discussions_comments ( 113CREATE TABLE map_discussions_comments (
102 id SERIAL, 114 id SERIAL,
103 discussion_id INT NOT NULL, 115 discussion_id INT NOT NULL,
@@ -109,6 +121,10 @@ CREATE TABLE map_discussions_comments (
109 FOREIGN KEY (user_id) REFERENCES users(steam_id) 121 FOREIGN KEY (user_id) REFERENCES users(steam_id)
110); 122);
111 123
124CREATE TRIGGER "map_discussions_comments"
125AFTER INSERT OR UPDATE OR DELETE ON "map_discussions_comments"
126FOR EACH ROW EXECUTE FUNCTION log_audit();
127
112CREATE TABLE map_discussions_upvotes ( 128CREATE TABLE map_discussions_upvotes (
113 id SERIAL, 129 id SERIAL,
114 discussion_id INT NOT NULL, 130 discussion_id INT NOT NULL,
@@ -140,6 +156,10 @@ CREATE TABLE records_sp (
140 FOREIGN KEY (demo_id) REFERENCES demos(id) 156 FOREIGN KEY (demo_id) REFERENCES demos(id)
141); 157);
142 158
159CREATE TRIGGER "records_sp"
160AFTER INSERT OR UPDATE OR DELETE ON "records_sp"
161FOR EACH ROW EXECUTE FUNCTION log_audit();
162
143CREATE TABLE records_mp ( 163CREATE TABLE records_mp (
144 id SERIAL, 164 id SERIAL,
145 map_id SMALLINT NOT NULL, 165 map_id SMALLINT NOT NULL,
@@ -159,6 +179,10 @@ CREATE TABLE records_mp (
159 FOREIGN KEY (partner_demo_id) REFERENCES demos(id) 179 FOREIGN KEY (partner_demo_id) REFERENCES demos(id)
160); 180);
161 181
182CREATE TRIGGER "records_mp"
183AFTER INSERT OR UPDATE OR DELETE ON "records_mp"
184FOR EACH ROW EXECUTE FUNCTION log_audit();
185
162CREATE TABLE titles ( 186CREATE TABLE titles (
163 id SERIAL, 187 id SERIAL,
164 title_name TEXT NOT NULL, 188 title_name TEXT NOT NULL,
@@ -179,13 +203,14 @@ CREATE TABLE countries (
179 PRIMARY KEY (country_code) 203 PRIMARY KEY (country_code)
180); 204);
181 205
182CREATE TABLE logs ( 206CREATE TABLE audit (
183 id SERIAL, 207 id SERIAL,
184 user_id TEXT NOT NULL, 208 table_name TEXT NOT NULL,
185 type TEXT NOT NULL, 209 operation_type TEXT NOT NULL, -- 'INSERT', 'UPDATE', or 'DELETE'
186 description TEXT NOT NULL, 210 old_data JSONB,
187 message TEXT NOT NULL DEFAULT, 211 new_data JSONB,
188 date TIMESTAMP NOT NULL DEFAULT now(), 212 changed_by TEXT NOT NULL,
189 PRIMARY KEY (id), 213 changed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
190 FOREIGN KEY (user_id) REFERENCES users(steam_id) 214 PRIMARY KEY (id),
191); \ No newline at end of file 215 FOREIGN KEY (changed_by) REFERENCES users(steam_id)
216);
diff --git a/backend/docs/docs.go b/backend/docs/docs.go
index 71bd68e..44d53e1 100644
--- a/backend/docs/docs.go
+++ b/backend/docs/docs.go
@@ -250,46 +250,6 @@ const docTemplate = `{
250 } 250 }
251 } 251 }
252 }, 252 },
253 "/logs/mod": {
254 "get": {
255 "description": "Get mod logs.",
256 "produces": [
257 "application/json"
258 ],
259 "tags": [
260 "logs"
261 ],
262 "parameters": [
263 {
264 "type": "string",
265 "description": "JWT Token",
266 "name": "Authorization",
267 "in": "header",
268 "required": true
269 }
270 ],
271 "responses": {
272 "200": {
273 "description": "OK",
274 "schema": {
275 "allOf": [
276 {
277 "$ref": "#/definitions/models.Response"
278 },
279 {
280 "type": "object",
281 "properties": {
282 "data": {
283 "$ref": "#/definitions/handlers.LogsResponse"
284 }
285 }
286 }
287 ]
288 }
289 }
290 }
291 }
292 },
293 "/logs/score": { 253 "/logs/score": {
294 "get": { 254 "get": {
295 "description": "Get score logs of every player.", 255 "description": "Get score logs of every player.",
@@ -1536,34 +1496,6 @@ const docTemplate = `{
1536 } 1496 }
1537 } 1497 }
1538 }, 1498 },
1539 "handlers.LogsResponse": {
1540 "type": "object",
1541 "properties": {
1542 "logs": {
1543 "type": "array",
1544 "items": {
1545 "$ref": "#/definitions/handlers.LogsResponseDetails"
1546 }
1547 }
1548 }
1549 },
1550 "handlers.LogsResponseDetails": {
1551 "type": "object",
1552 "properties": {
1553 "date": {
1554 "type": "string"
1555 },
1556 "detail": {
1557 "type": "string"
1558 },
1559 "message": {
1560 "type": "string"
1561 },
1562 "user": {
1563 "$ref": "#/definitions/models.UserShort"
1564 }
1565 }
1566 },
1567 "handlers.MapDiscussion": { 1499 "handlers.MapDiscussion": {
1568 "type": "object", 1500 "type": "object",
1569 "properties": { 1501 "properties": {
diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json
index 879f35f..6c10cfc 100644
--- a/backend/docs/swagger.json
+++ b/backend/docs/swagger.json
@@ -244,46 +244,6 @@
244 } 244 }
245 } 245 }
246 }, 246 },
247 "/logs/mod": {
248 "get": {
249 "description": "Get mod logs.",
250 "produces": [
251 "application/json"
252 ],
253 "tags": [
254 "logs"
255 ],
256 "parameters": [
257 {
258 "type": "string",
259 "description": "JWT Token",
260 "name": "Authorization",
261 "in": "header",
262 "required": true
263 }
264 ],
265 "responses": {
266 "200": {
267 "description": "OK",
268 "schema": {
269 "allOf": [
270 {
271 "$ref": "#/definitions/models.Response"
272 },
273 {
274 "type": "object",
275 "properties": {
276 "data": {
277 "$ref": "#/definitions/handlers.LogsResponse"
278 }
279 }
280 }
281 ]
282 }
283 }
284 }
285 }
286 },
287 "/logs/score": { 247 "/logs/score": {
288 "get": { 248 "get": {
289 "description": "Get score logs of every player.", 249 "description": "Get score logs of every player.",
@@ -1530,34 +1490,6 @@
1530 } 1490 }
1531 } 1491 }
1532 }, 1492 },
1533 "handlers.LogsResponse": {
1534 "type": "object",
1535 "properties": {
1536 "logs": {
1537 "type": "array",
1538 "items": {
1539 "$ref": "#/definitions/handlers.LogsResponseDetails"
1540 }
1541 }
1542 }
1543 },
1544 "handlers.LogsResponseDetails": {
1545 "type": "object",
1546 "properties": {
1547 "date": {
1548 "type": "string"
1549 },
1550 "detail": {
1551 "type": "string"
1552 },
1553 "message": {
1554 "type": "string"
1555 },
1556 "user": {
1557 "$ref": "#/definitions/models.UserShort"
1558 }
1559 }
1560 },
1561 "handlers.MapDiscussion": { 1493 "handlers.MapDiscussion": {
1562 "type": "object", 1494 "type": "object",
1563 "properties": { 1495 "properties": {
diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml
index 2dee421..8f33b94 100644
--- a/backend/docs/swagger.yaml
+++ b/backend/docs/swagger.yaml
@@ -106,24 +106,6 @@ definitions:
106 token: 106 token:
107 type: string 107 type: string
108 type: object 108 type: object
109 handlers.LogsResponse:
110 properties:
111 logs:
112 items:
113 $ref: '#/definitions/handlers.LogsResponseDetails'
114 type: array
115 type: object
116 handlers.LogsResponseDetails:
117 properties:
118 date:
119 type: string
120 detail:
121 type: string
122 message:
123 type: string
124 user:
125 $ref: '#/definitions/models.UserShort'
126 type: object
127 handlers.MapDiscussion: 109 handlers.MapDiscussion:
128 properties: 110 properties:
129 comments: 111 comments:
@@ -690,29 +672,6 @@ paths:
690 type: object 672 type: object
691 tags: 673 tags:
692 - login 674 - login
693 /logs/mod:
694 get:
695 description: Get mod logs.
696 parameters:
697 - description: JWT Token
698 in: header
699 name: Authorization
700 required: true
701 type: string
702 produces:
703 - application/json
704 responses:
705 "200":
706 description: OK
707 schema:
708 allOf:
709 - $ref: '#/definitions/models.Response'
710 - properties:
711 data:
712 $ref: '#/definitions/handlers.LogsResponse'
713 type: object
714 tags:
715 - logs
716 /logs/score: 675 /logs/score:
717 get: 676 get:
718 description: Get score logs of every player. 677 description: Get score logs of every player.
diff --git a/backend/handlers/discussions.go b/backend/handlers/discussions.go
index 604eb39..6267695 100644
--- a/backend/handlers/discussions.go
+++ b/backend/handlers/discussions.go
@@ -160,11 +160,7 @@ func CreateMapDiscussion(c *gin.Context) {
160 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 160 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
161 return 161 return
162 } 162 }
163 user, exists := c.Get("user") 163 user, _ := c.Get("user")
164 if !exists {
165 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
166 return
167 }
168 var request CreateMapDiscussionRequest 164 var request CreateMapDiscussionRequest
169 if err := c.BindJSON(&request); err != nil { 165 if err := c.BindJSON(&request); err != nil {
170 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 166 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
@@ -206,11 +202,7 @@ func CreateMapDiscussionComment(c *gin.Context) {
206 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 202 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
207 return 203 return
208 } 204 }
209 user, exists := c.Get("user") 205 user, _ := c.Get("user")
210 if !exists {
211 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
212 return
213 }
214 var request CreateMapDiscussionCommentRequest 206 var request CreateMapDiscussionCommentRequest
215 if err := c.BindJSON(&request); err != nil { 207 if err := c.BindJSON(&request); err != nil {
216 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 208 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
@@ -258,11 +250,7 @@ func EditMapDiscussion(c *gin.Context) {
258 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 250 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
259 return 251 return
260 } 252 }
261 user, exists := c.Get("user") 253 user, _ := c.Get("user")
262 if !exists {
263 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
264 return
265 }
266 var request EditMapDiscussionRequest 254 var request EditMapDiscussionRequest
267 if err := c.BindJSON(&request); err != nil { 255 if err := c.BindJSON(&request); err != nil {
268 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 256 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
@@ -311,11 +299,7 @@ func DeleteMapDiscussion(c *gin.Context) {
311 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 299 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
312 return 300 return
313 } 301 }
314 user, exists := c.Get("user") 302 user, _ := c.Get("user")
315 if !exists {
316 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
317 return
318 }
319 sql := `UPDATE map_discussions SET is_deleted = true WHERE id = $1 AND map_id = $2 AND user_id = $3` 303 sql := `UPDATE map_discussions SET is_deleted = true WHERE id = $1 AND map_id = $2 AND user_id = $3`
320 result, err := database.DB.Exec(sql, discussionID, mapID, user.(models.User).SteamID) 304 result, err := database.DB.Exec(sql, discussionID, mapID, user.(models.User).SteamID)
321 if err != nil { 305 if err != nil {
diff --git a/backend/handlers/login.go b/backend/handlers/login.go
index 408d950..51c90d0 100644
--- a/backend/handlers/login.go
+++ b/backend/handlers/login.go
@@ -40,7 +40,6 @@ func Login(c *gin.Context) {
40 default: 40 default:
41 steamID, err := openID.ValidateAndGetID() 41 steamID, err := openID.ValidateAndGetID()
42 if err != nil { 42 if err != nil {
43 CreateLog(steamID, LogTypeUser, LogDescriptionUserLoginFailValidate, err.Error())
44 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 43 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
45 return 44 return
46 } 45 }
@@ -51,7 +50,6 @@ func Login(c *gin.Context) {
51 if checkSteamID == 0 { 50 if checkSteamID == 0 {
52 user, err := GetPlayerSummaries(steamID, os.Getenv("API_KEY")) 51 user, err := GetPlayerSummaries(steamID, os.Getenv("API_KEY"))
53 if err != nil { 52 if err != nil {
54 CreateLog(steamID, LogTypeUser, LogDescriptionUserLoginFailSummary, err.Error())
55 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 53 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
56 return 54 return
57 } 55 }
@@ -81,7 +79,6 @@ func Login(c *gin.Context) {
81 // Sign and get the complete encoded token as a string using the secret 79 // Sign and get the complete encoded token as a string using the secret
82 tokenString, err := token.SignedString([]byte(os.Getenv("SECRET_KEY"))) 80 tokenString, err := token.SignedString([]byte(os.Getenv("SECRET_KEY")))
83 if err != nil { 81 if err != nil {
84 CreateLog(steamID, LogTypeUser, LogDescriptionUserLoginFailToken, err.Error())
85 c.JSON(http.StatusOK, models.ErrorResponse("Failed to generate token.")) 82 c.JSON(http.StatusOK, models.ErrorResponse("Failed to generate token."))
86 return 83 return
87 } 84 }
diff --git a/backend/handlers/logs.go b/backend/handlers/logs.go
index 76ddac4..693c448 100644
--- a/backend/handlers/logs.go
+++ b/backend/handlers/logs.go
@@ -1,7 +1,6 @@
1package handlers 1package handlers
2 2
3import ( 3import (
4 "fmt"
5 "net/http" 4 "net/http"
6 "time" 5 "time"
7 6
@@ -11,42 +10,6 @@ import (
11 "github.com/gin-gonic/gin" 10 "github.com/gin-gonic/gin"
12) 11)
13 12
14const (
15 LogTypeMod string = "Mod"
16 LogTypeUser string = "User"
17 LogTypeRecord string = "Record"
18
19 LogDescriptionUserLoginSuccess string = "LoginSuccess"
20 LogDescriptionUserLoginFailToken string = "LoginTokenFail"
21 LogDescriptionUserLoginFailValidate string = "LoginValidateFail"
22 LogDescriptionUserLoginFailSummary string = "LoginSummaryFail"
23 LogDescriptionUserUpdateSuccess string = "UpdateSuccess"
24 LogDescriptionUserUpdateFail string = "UpdateFail"
25 LogDescriptionUserUpdateSummaryFail string = "UpdateSummaryFail"
26 LogDescriptionUserUpdateCountrySuccess string = "UpdateCountrySuccess"
27 LogDescriptionUserUpdateCountryFail string = "UpdateCountryFail"
28
29 LogDescriptionMapSummaryCreateSuccess string = "MapSummaryCreateSuccess"
30 LogDescriptionMapSummaryCreateFail string = "MapSummaryCreateFail"
31 LogDescriptionMapSummaryEditSuccess string = "MapSummaryEditSuccess"
32 LogDescriptionMapSummaryEditFail string = "MapSummaryEditFail"
33 LogDescriptionMapSummaryEditImageSuccess string = "MapSummaryEditImageSuccess"
34 LogDescriptionMapSummaryEditImageFail string = "MapSummaryEditImageFail"
35 LogDescriptionMapSummaryDeleteSuccess string = "MapSummaryDeleteSuccess"
36 LogDescriptionMapSummaryDeleteFail string = "MapSummaryDeleteFail"
37
38 LogDescriptionCreateRecordSuccess string = "CreateRecordSuccess"
39 LogDescriptionCreateRecordInsertRecordFail string = "InsertRecordFail"
40 LogDescriptionCreateRecordInsertDemoFail string = "InsertDemoFail"
41 LogDescriptionCreateRecordProcessDemoFail string = "ProcessDemoFail"
42 LogDescriptionCreateRecordCreateDemoFail string = "CreateDemoFail"
43 LogDescriptionCreateRecordOpenDemoFail string = "OpenDemoFail"
44 LogDescriptionCreateRecordSaveDemoFail string = "SaveDemoFail"
45 LogDescriptionCreateRecordInvalidRequestFail string = "InvalidRequestFail"
46 LogDescriptionDeleteRecordSuccess string = "DeleteRecordSuccess"
47 LogDescriptionDeleteRecordFail string = "DeleteRecordFail"
48)
49
50type Log struct { 13type Log struct {
51 User models.UserShort `json:"user"` 14 User models.UserShort `json:"user"`
52 Type string `json:"type"` 15 Type string `json:"type"`
@@ -80,54 +43,6 @@ type ScoreLogsResponseDetails struct {
80 Date time.Time `json:"date"` 43 Date time.Time `json:"date"`
81} 44}
82 45
83// GET Mod Logs
84//
85// @Description Get mod logs.
86// @Tags logs
87// @Produce json
88// @Param Authorization header string true "JWT Token"
89// @Success 200 {object} models.Response{data=LogsResponse}
90// @Router /logs/mod [get]
91func ModLogs(c *gin.Context) {
92 mod, exists := c.Get("mod")
93 if !exists || !mod.(bool) {
94 c.JSON(http.StatusOK, models.ErrorResponse("Insufficient permissions."))
95 return
96 }
97 response := LogsResponse{Logs: []LogsResponseDetails{}}
98 sql := `SELECT u.user_name, l.user_id, l.type, l.description, l.message, l.date
99 FROM logs l INNER JOIN users u ON l.user_id = u.steam_id WHERE type != 'Score'
100 ORDER BY l.date DESC LIMIT 100;`
101 rows, err := database.DB.Query(sql)
102 if err != nil {
103 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
104 return
105 }
106 for rows.Next() {
107 log := Log{}
108 err = rows.Scan(&log.User.UserName, &log.User.SteamID, &log.Type, &log.Description, &log.Message, &log.Date)
109 if err != nil {
110 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
111 return
112 }
113 detail := fmt.Sprintf("%s.%s", log.Type, log.Description)
114 response.Logs = append(response.Logs, LogsResponseDetails{
115 User: models.UserShort{
116 SteamID: log.User.SteamID,
117 UserName: log.User.UserName,
118 },
119 Log: detail,
120 Message: log.Message,
121 Date: log.Date,
122 })
123 }
124 c.JSON(http.StatusOK, models.Response{
125 Success: true,
126 Message: "Successfully retrieved logs.",
127 Data: response,
128 })
129}
130
131// GET Score Logs 46// GET Score Logs
132// 47//
133// @Description Get score logs of every player. 48// @Description Get score logs of every player.
@@ -186,16 +101,3 @@ func ScoreLogs(c *gin.Context) {
186 Data: response, 101 Data: response,
187 }) 102 })
188} 103}
189
190func CreateLog(userID string, logType string, logDescription string, logMessage ...string) (err error) {
191 message := "-"
192 if len(logMessage) == 1 {
193 message = logMessage[0]
194 }
195 sql := `INSERT INTO logs (user_id, "type", description, message) VALUES($1, $2, $3, $4)`
196 _, err = database.DB.Exec(sql, userID, logType, logDescription, message)
197 if err != nil {
198 return err
199 }
200 return nil
201}
diff --git a/backend/handlers/mod.go b/backend/handlers/mod.go
index 4fdc78a..66e1437 100644
--- a/backend/handlers/mod.go
+++ b/backend/handlers/mod.go
@@ -1,7 +1,6 @@
1package handlers 1package handlers
2 2
3import ( 3import (
4 "fmt"
5 "net/http" 4 "net/http"
6 "strconv" 5 "strconv"
7 "time" 6 "time"
@@ -49,12 +48,6 @@ type EditMapImageRequest struct {
49// @Success 200 {object} models.Response{data=CreateMapSummaryRequest} 48// @Success 200 {object} models.Response{data=CreateMapSummaryRequest}
50// @Router /maps/{mapid}/summary [post] 49// @Router /maps/{mapid}/summary [post]
51func CreateMapSummary(c *gin.Context) { 50func CreateMapSummary(c *gin.Context) {
52 // Check if user exists
53 user, exists := c.Get("user")
54 if !exists {
55 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
56 return
57 }
58 mod, exists := c.Get("mod") 51 mod, exists := c.Get("mod")
59 if !exists || !mod.(bool) { 52 if !exists || !mod.(bool) {
60 c.JSON(http.StatusOK, models.ErrorResponse("Insufficient permissions.")) 53 c.JSON(http.StatusOK, models.ErrorResponse("Insufficient permissions."))
@@ -69,7 +62,6 @@ func CreateMapSummary(c *gin.Context) {
69 } 62 }
70 var request CreateMapSummaryRequest 63 var request CreateMapSummaryRequest
71 if err := c.BindJSON(&request); err != nil { 64 if err := c.BindJSON(&request); err != nil {
72 CreateLog(user.(models.User).SteamID, LogTypeMod, LogDescriptionMapSummaryCreateFail, fmt.Sprintf("BIND: %s", err.Error()))
73 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 65 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
74 return 66 return
75 } 67 }
@@ -85,7 +77,6 @@ func CreateMapSummary(c *gin.Context) {
85 sql := `SELECT m.id FROM maps m WHERE m.id = $1` 77 sql := `SELECT m.id FROM maps m WHERE m.id = $1`
86 err = database.DB.QueryRow(sql, mapID).Scan(&checkMapID) 78 err = database.DB.QueryRow(sql, mapID).Scan(&checkMapID)
87 if err != nil { 79 if err != nil {
88 CreateLog(user.(models.User).SteamID, LogTypeMod, LogDescriptionMapSummaryCreateFail, fmt.Sprintf("SELECT#maps: %s", err.Error()))
89 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 80 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
90 return 81 return
91 } 82 }
@@ -98,7 +89,6 @@ func CreateMapSummary(c *gin.Context) {
98 VALUES ($1,$2,$3,$4,$5,$6,$7)` 89 VALUES ($1,$2,$3,$4,$5,$6,$7)`
99 _, err = tx.Exec(sql, mapID, request.CategoryID, request.UserName, *request.ScoreCount, request.Description, request.Showcase, request.RecordDate) 90 _, err = tx.Exec(sql, mapID, request.CategoryID, request.UserName, *request.ScoreCount, request.Description, request.Showcase, request.RecordDate)
100 if err != nil { 91 if err != nil {
101 CreateLog(user.(models.User).SteamID, LogTypeMod, LogDescriptionMapSummaryCreateFail, fmt.Sprintf("INSERT#map_history: %s", err.Error()))
102 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 92 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
103 return 93 return
104 } 94 }
@@ -106,7 +96,6 @@ func CreateMapSummary(c *gin.Context) {
106 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 96 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
107 return 97 return
108 } 98 }
109 CreateLog(user.(models.User).SteamID, LogTypeMod, LogDescriptionMapSummaryCreateSuccess, fmt.Sprintf("MapID: %d | CategoryID: %d | ScoreCount: %d", mapID, request.CategoryID, *request.ScoreCount))
110 c.JSON(http.StatusOK, models.Response{ 99 c.JSON(http.StatusOK, models.Response{
111 Success: true, 100 Success: true,
112 Message: "Successfully created map summary.", 101 Message: "Successfully created map summary.",
@@ -125,12 +114,6 @@ func CreateMapSummary(c *gin.Context) {
125// @Success 200 {object} models.Response{data=EditMapSummaryRequest} 114// @Success 200 {object} models.Response{data=EditMapSummaryRequest}
126// @Router /maps/{mapid}/summary [put] 115// @Router /maps/{mapid}/summary [put]
127func EditMapSummary(c *gin.Context) { 116func EditMapSummary(c *gin.Context) {
128 // Check if user exists
129 user, exists := c.Get("user")
130 if !exists {
131 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
132 return
133 }
134 mod, exists := c.Get("mod") 117 mod, exists := c.Get("mod")
135 if !exists || !mod.(bool) { 118 if !exists || !mod.(bool) {
136 c.JSON(http.StatusOK, models.ErrorResponse("Insufficient permissions.")) 119 c.JSON(http.StatusOK, models.ErrorResponse("Insufficient permissions."))
@@ -146,7 +129,6 @@ func EditMapSummary(c *gin.Context) {
146 } 129 }
147 var request EditMapSummaryRequest 130 var request EditMapSummaryRequest
148 if err := c.BindJSON(&request); err != nil { 131 if err := c.BindJSON(&request); err != nil {
149 CreateLog(user.(models.User).SteamID, LogTypeMod, LogDescriptionMapSummaryEditFail, fmt.Sprintf("BIND: %s", err.Error()))
150 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 132 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
151 return 133 return
152 } 134 }
@@ -161,7 +143,6 @@ func EditMapSummary(c *gin.Context) {
161 sql := `UPDATE map_history SET user_name = $2, score_count = $3, record_date = $4, description = $5, showcase = $6 WHERE id = $1` 143 sql := `UPDATE map_history SET user_name = $2, score_count = $3, record_date = $4, description = $5, showcase = $6 WHERE id = $1`
162 _, err = tx.Exec(sql, request.RouteID, request.UserName, *request.ScoreCount, request.RecordDate, request.Description, request.Showcase) 144 _, err = tx.Exec(sql, request.RouteID, request.UserName, *request.ScoreCount, request.RecordDate, request.Description, request.Showcase)
163 if err != nil { 145 if err != nil {
164 CreateLog(user.(models.User).SteamID, LogTypeMod, LogDescriptionMapSummaryEditFail, fmt.Sprintf("(HistoryID: %d) UPDATE#map_history: %s", request.RouteID, err.Error()))
165 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 146 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
166 return 147 return
167 } 148 }
@@ -187,12 +168,6 @@ func EditMapSummary(c *gin.Context) {
187// @Success 200 {object} models.Response{data=DeleteMapSummaryRequest} 168// @Success 200 {object} models.Response{data=DeleteMapSummaryRequest}
188// @Router /maps/{mapid}/summary [delete] 169// @Router /maps/{mapid}/summary [delete]
189func DeleteMapSummary(c *gin.Context) { 170func DeleteMapSummary(c *gin.Context) {
190 // Check if user exists
191 user, exists := c.Get("user")
192 if !exists {
193 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
194 return
195 }
196 mod, exists := c.Get("mod") 171 mod, exists := c.Get("mod")
197 if !exists || !mod.(bool) { 172 if !exists || !mod.(bool) {
198 c.JSON(http.StatusOK, models.ErrorResponse("Insufficient permissions.")) 173 c.JSON(http.StatusOK, models.ErrorResponse("Insufficient permissions."))
@@ -208,7 +183,6 @@ func DeleteMapSummary(c *gin.Context) {
208 } 183 }
209 var request DeleteMapSummaryRequest 184 var request DeleteMapSummaryRequest
210 if err := c.BindJSON(&request); err != nil { 185 if err := c.BindJSON(&request); err != nil {
211 CreateLog(user.(models.User).SteamID, LogTypeMod, LogDescriptionMapSummaryEditFail, fmt.Sprintf("(RouteID: %d) BIND: %s", request.RouteID, err.Error()))
212 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 186 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
213 return 187 return
214 } 188 }
@@ -223,7 +197,6 @@ func DeleteMapSummary(c *gin.Context) {
223 sql := `DELETE FROM map_history mh WHERE mh.id = $1` 197 sql := `DELETE FROM map_history mh WHERE mh.id = $1`
224 _, err = tx.Exec(sql, request.RouteID) 198 _, err = tx.Exec(sql, request.RouteID)
225 if err != nil { 199 if err != nil {
226 CreateLog(user.(models.User).SteamID, LogTypeMod, LogDescriptionMapSummaryDeleteFail, fmt.Sprintf("(HistoryID: %d) DELETE#map_history: %s", request.RouteID, err.Error()))
227 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 200 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
228 return 201 return
229 } 202 }
@@ -249,12 +222,6 @@ func DeleteMapSummary(c *gin.Context) {
249// @Success 200 {object} models.Response{data=EditMapImageRequest} 222// @Success 200 {object} models.Response{data=EditMapImageRequest}
250// @Router /maps/{mapid}/image [put] 223// @Router /maps/{mapid}/image [put]
251func EditMapImage(c *gin.Context) { 224func EditMapImage(c *gin.Context) {
252 // Check if user exists
253 user, exists := c.Get("user")
254 if !exists {
255 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
256 return
257 }
258 mod, exists := c.Get("mod") 225 mod, exists := c.Get("mod")
259 if !exists || !mod.(bool) { 226 if !exists || !mod.(bool) {
260 c.JSON(http.StatusOK, models.ErrorResponse("Insufficient permissions.")) 227 c.JSON(http.StatusOK, models.ErrorResponse("Insufficient permissions."))
@@ -269,7 +236,6 @@ func EditMapImage(c *gin.Context) {
269 } 236 }
270 var request EditMapImageRequest 237 var request EditMapImageRequest
271 if err := c.BindJSON(&request); err != nil { 238 if err := c.BindJSON(&request); err != nil {
272 CreateLog(user.(models.User).SteamID, LogTypeMod, LogDescriptionMapSummaryEditImageFail, fmt.Sprintf("BIND: %s", err.Error()))
273 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 239 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
274 return 240 return
275 } 241 }
@@ -277,11 +243,9 @@ func EditMapImage(c *gin.Context) {
277 sql := `UPDATE maps SET image = $2 WHERE id = $1` 243 sql := `UPDATE maps SET image = $2 WHERE id = $1`
278 _, err = database.DB.Exec(sql, mapID, request.Image) 244 _, err = database.DB.Exec(sql, mapID, request.Image)
279 if err != nil { 245 if err != nil {
280 CreateLog(user.(models.User).SteamID, LogTypeMod, LogDescriptionMapSummaryEditImageFail, fmt.Sprintf("UPDATE#maps: %s", err.Error()))
281 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 246 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
282 return 247 return
283 } 248 }
284 CreateLog(user.(models.User).SteamID, LogTypeMod, LogDescriptionMapSummaryEditImageSuccess)
285 c.JSON(http.StatusOK, models.Response{ 249 c.JSON(http.StatusOK, models.Response{
286 Success: true, 250 Success: true,
287 Message: "Successfully updated map image.", 251 Message: "Successfully updated map image.",
diff --git a/backend/handlers/record.go b/backend/handlers/record.go
index e43cc61..bedde57 100644
--- a/backend/handlers/record.go
+++ b/backend/handlers/record.go
@@ -53,12 +53,7 @@ func CreateRecordWithDemo(c *gin.Context) {
53 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 53 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
54 return 54 return
55 } 55 }
56 // Check if user exists 56 user, _ := c.Get("user")
57 user, exists := c.Get("user")
58 if !exists {
59 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
60 return
61 }
62 // Check if map is sp or mp 57 // Check if map is sp or mp
63 var gameName string 58 var gameName string
64 var isCoop bool 59 var isCoop bool
@@ -76,12 +71,10 @@ func CreateRecordWithDemo(c *gin.Context) {
76 // Get record request 71 // Get record request
77 var record RecordRequest 72 var record RecordRequest
78 if err := c.ShouldBind(&record); err != nil { 73 if err := c.ShouldBind(&record); err != nil {
79 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordInvalidRequestFail, "BIND: "+err.Error())
80 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 74 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
81 return 75 return
82 } 76 }
83 if isCoop && record.PartnerDemo == nil { 77 if isCoop && record.PartnerDemo == nil {
84 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordInvalidRequestFail)
85 c.JSON(http.StatusOK, models.ErrorResponse("Missing partner demo for coop submission.")) 78 c.JSON(http.StatusOK, models.ErrorResponse("Missing partner demo for coop submission."))
86 return 79 return
87 } 80 }
@@ -112,21 +105,18 @@ func CreateRecordWithDemo(c *gin.Context) {
112 // Upload & insert into demos 105 // Upload & insert into demos
113 err = c.SaveUploadedFile(header, "parser/"+uuid+".dem") 106 err = c.SaveUploadedFile(header, "parser/"+uuid+".dem")
114 if err != nil { 107 if err != nil {
115 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordSaveDemoFail, err.Error())
116 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 108 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
117 return 109 return
118 } 110 }
119 defer os.Remove("parser/" + uuid + ".dem") 111 defer os.Remove("parser/" + uuid + ".dem")
120 f, err := os.Open("parser/" + uuid + ".dem") 112 f, err := os.Open("parser/" + uuid + ".dem")
121 if err != nil { 113 if err != nil {
122 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordOpenDemoFail, err.Error())
123 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 114 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
124 return 115 return
125 } 116 }
126 defer f.Close() 117 defer f.Close()
127 parserResult, err := parser.ProcessDemo("parser/" + uuid + ".dem") 118 parserResult, err := parser.ProcessDemo("parser/" + uuid + ".dem")
128 if err != nil { 119 if err != nil {
129 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordProcessDemoFail, err.Error())
130 c.JSON(http.StatusOK, models.ErrorResponse("Error while processing demo: "+err.Error())) 120 c.JSON(http.StatusOK, models.ErrorResponse("Error while processing demo: "+err.Error()))
131 return 121 return
132 } 122 }
@@ -139,7 +129,6 @@ func CreateRecordWithDemo(c *gin.Context) {
139 hostSteamID = parserResult.HostSteamID 129 hostSteamID = parserResult.HostSteamID
140 partnerSteamID = parserResult.PartnerSteamID 130 partnerSteamID = parserResult.PartnerSteamID
141 if hostDemoScoreCount == 0 && hostDemoScoreTime == 0 { 131 if hostDemoScoreCount == 0 && hostDemoScoreTime == 0 {
142 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordProcessDemoFail, err.Error())
143 c.JSON(http.StatusOK, models.ErrorResponse("Processing demo went wrong. Please contact a web admin and provide the demo in question.")) 132 c.JSON(http.StatusOK, models.ErrorResponse("Processing demo went wrong. Please contact a web admin and provide the demo in question."))
144 return 133 return
145 } 134 }
@@ -161,7 +150,6 @@ func CreateRecordWithDemo(c *gin.Context) {
161 } 150 }
162 file, err := createFile(srv, uuid+".dem", "application/octet-stream", f, os.Getenv("GOOGLE_FOLDER_ID")) 151 file, err := createFile(srv, uuid+".dem", "application/octet-stream", f, os.Getenv("GOOGLE_FOLDER_ID"))
163 if err != nil { 152 if err != nil {
164 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordCreateDemoFail, err.Error())
165 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 153 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
166 return 154 return
167 } 155 }
@@ -177,7 +165,6 @@ func CreateRecordWithDemo(c *gin.Context) {
177 _, err = tx.Exec(`INSERT INTO demos (id,location_id) VALUES ($1,$2)`, uuid, file.Id) 165 _, err = tx.Exec(`INSERT INTO demos (id,location_id) VALUES ($1,$2)`, uuid, file.Id)
178 if err != nil { 166 if err != nil {
179 deleteFile(srv, file.Id) 167 deleteFile(srv, file.Id)
180 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordInsertDemoFail, err.Error())
181 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 168 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
182 return 169 return
183 } 170 }
@@ -229,7 +216,6 @@ func CreateRecordWithDemo(c *gin.Context) {
229 if err != nil { 216 if err != nil {
230 deleteFile(srv, hostDemoFileID) 217 deleteFile(srv, hostDemoFileID)
231 deleteFile(srv, partnerDemoFileID) 218 deleteFile(srv, partnerDemoFileID)
232 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordInsertRecordFail, err.Error())
233 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 219 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
234 return 220 return
235 } 221 }
@@ -239,7 +225,6 @@ func CreateRecordWithDemo(c *gin.Context) {
239 _, err := tx.Exec(sql, mapID, hostDemoScoreCount, hostDemoScoreTime, user.(models.User).SteamID, hostDemoUUID) 225 _, err := tx.Exec(sql, mapID, hostDemoScoreCount, hostDemoScoreTime, user.(models.User).SteamID, hostDemoUUID)
240 if err != nil { 226 if err != nil {
241 deleteFile(srv, hostDemoFileID) 227 deleteFile(srv, hostDemoFileID)
242 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordInsertRecordFail, err.Error())
243 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 228 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
244 return 229 return
245 } 230 }
@@ -248,7 +233,6 @@ func CreateRecordWithDemo(c *gin.Context) {
248 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 233 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
249 return 234 return
250 } 235 }
251 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordSuccess)
252 c.JSON(http.StatusOK, models.Response{ 236 c.JSON(http.StatusOK, models.Response{
253 Success: true, 237 Success: true,
254 Message: "Successfully created record.", 238 Message: "Successfully created record.",
@@ -277,11 +261,7 @@ func DeleteRecord(c *gin.Context) {
277 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 261 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
278 return 262 return
279 } 263 }
280 user, exists := c.Get("user") 264 user, _ := c.Get("user")
281 if !exists {
282 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
283 return
284 }
285 // Validate map 265 // Validate map
286 var validateMapID int 266 var validateMapID int
287 var isCoop bool 267 var isCoop bool
@@ -302,12 +282,10 @@ func DeleteRecord(c *gin.Context) {
302 sql = `SELECT mp.id FROM records_mp mp WHERE mp.id = $1 AND mp.map_id = $2 AND (mp.host_id = $3 OR mp.partner_id = $3) AND is_deleted = false` 282 sql = `SELECT mp.id FROM records_mp mp WHERE mp.id = $1 AND mp.map_id = $2 AND (mp.host_id = $3 OR mp.partner_id = $3) AND is_deleted = false`
303 err = database.DB.QueryRow(sql, recordID, mapID, user.(models.User).SteamID).Scan(&validateRecordID) 283 err = database.DB.QueryRow(sql, recordID, mapID, user.(models.User).SteamID).Scan(&validateRecordID)
304 if err != nil { 284 if err != nil {
305 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionDeleteRecordFail, "SELECT#records_mp: "+err.Error())
306 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 285 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
307 return 286 return
308 } 287 }
309 if recordID != validateRecordID { 288 if recordID != validateRecordID {
310 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionDeleteRecordFail, "recordID != validateRecordID")
311 c.JSON(http.StatusOK, models.ErrorResponse("Selected record does not exist.")) 289 c.JSON(http.StatusOK, models.ErrorResponse("Selected record does not exist."))
312 return 290 return
313 } 291 }
@@ -315,7 +293,6 @@ func DeleteRecord(c *gin.Context) {
315 sql = `UPDATE records_mp SET is_deleted = true WHERE id = $1` 293 sql = `UPDATE records_mp SET is_deleted = true WHERE id = $1`
316 _, err = database.DB.Exec(sql, recordID) 294 _, err = database.DB.Exec(sql, recordID)
317 if err != nil { 295 if err != nil {
318 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionDeleteRecordFail, "UPDATE#records_mp: "+err.Error())
319 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 296 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
320 return 297 return
321 } 298 }
@@ -325,12 +302,10 @@ func DeleteRecord(c *gin.Context) {
325 sql = `SELECT sp.id FROM records_sp sp WHERE sp.id = $1 AND sp.map_id = $2 AND sp.user_id = $3 AND is_deleted = false` 302 sql = `SELECT sp.id FROM records_sp sp WHERE sp.id = $1 AND sp.map_id = $2 AND sp.user_id = $3 AND is_deleted = false`
326 err = database.DB.QueryRow(sql, recordID, mapID, user.(models.User).SteamID).Scan(&validateRecordID) 303 err = database.DB.QueryRow(sql, recordID, mapID, user.(models.User).SteamID).Scan(&validateRecordID)
327 if err != nil { 304 if err != nil {
328 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionDeleteRecordFail, "SELECT#records_sp: "+err.Error())
329 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 305 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
330 return 306 return
331 } 307 }
332 if recordID != validateRecordID { 308 if recordID != validateRecordID {
333 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionDeleteRecordFail, "recordID != validateRecordID")
334 c.JSON(http.StatusOK, models.ErrorResponse("Selected record does not exist.")) 309 c.JSON(http.StatusOK, models.ErrorResponse("Selected record does not exist."))
335 return 310 return
336 } 311 }
@@ -338,12 +313,10 @@ func DeleteRecord(c *gin.Context) {
338 sql = `UPDATE records_sp SET is_deleted = true WHERE id = $1` 313 sql = `UPDATE records_sp SET is_deleted = true WHERE id = $1`
339 _, err = database.DB.Exec(sql, recordID) 314 _, err = database.DB.Exec(sql, recordID)
340 if err != nil { 315 if err != nil {
341 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionDeleteRecordFail, "UPDATE#records_sp: "+err.Error())
342 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 316 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
343 return 317 return
344 } 318 }
345 } 319 }
346 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionDeleteRecordSuccess)
347 c.JSON(http.StatusOK, models.Response{ 320 c.JSON(http.StatusOK, models.Response{
348 Success: true, 321 Success: true,
349 Message: "Successfully deleted record.", 322 Message: "Successfully deleted record.",
diff --git a/backend/handlers/user.go b/backend/handlers/user.go
index 021a47f..53f0d06 100644
--- a/backend/handlers/user.go
+++ b/backend/handlers/user.go
@@ -69,12 +69,7 @@ type ScoreResponse struct {
69// @Success 200 {object} models.Response{data=ProfileResponse} 69// @Success 200 {object} models.Response{data=ProfileResponse}
70// @Router /profile [get] 70// @Router /profile [get]
71func Profile(c *gin.Context) { 71func Profile(c *gin.Context) {
72 // Check if user exists 72 user, _ := c.Get("user")
73 user, exists := c.Get("user")
74 if !exists {
75 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
76 return
77 }
78 // Get user links 73 // Get user links
79 links := models.Links{} 74 links := models.Links{}
80 sql := `SELECT u.p2sr, u.steam, u.youtube, u.twitch FROM users u WHERE u.steam_id = $1` 75 sql := `SELECT u.p2sr, u.steam, u.youtube, u.twitch FROM users u WHERE u.steam_id = $1`
@@ -699,15 +694,9 @@ func FetchUser(c *gin.Context) {
699// @Success 200 {object} models.Response{data=ProfileResponse} 694// @Success 200 {object} models.Response{data=ProfileResponse}
700// @Router /profile [post] 695// @Router /profile [post]
701func UpdateUser(c *gin.Context) { 696func UpdateUser(c *gin.Context) {
702 // Check if user exists 697 user, _ := c.Get("user")
703 user, exists := c.Get("user")
704 if !exists {
705 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
706 return
707 }
708 profile, err := GetPlayerSummaries(user.(models.User).SteamID, os.Getenv("API_KEY")) 698 profile, err := GetPlayerSummaries(user.(models.User).SteamID, os.Getenv("API_KEY"))
709 if err != nil { 699 if err != nil {
710 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateSummaryFail, err.Error())
711 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 700 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
712 return 701 return
713 } 702 }
@@ -715,11 +704,9 @@ func UpdateUser(c *gin.Context) {
715 sql := `UPDATE users SET user_name = $1, avatar_link = $2, country_code = $3, updated_at = $4 WHERE steam_id = $5` 704 sql := `UPDATE users SET user_name = $1, avatar_link = $2, country_code = $3, updated_at = $4 WHERE steam_id = $5`
716 _, err = database.DB.Exec(sql, profile.PersonaName, profile.AvatarFull, profile.LocCountryCode, time.Now().UTC(), user.(models.User).SteamID) 705 _, err = database.DB.Exec(sql, profile.PersonaName, profile.AvatarFull, profile.LocCountryCode, time.Now().UTC(), user.(models.User).SteamID)
717 if err != nil { 706 if err != nil {
718 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateFail, "UPDATE#users: "+err.Error())
719 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 707 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
720 return 708 return
721 } 709 }
722 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateSuccess)
723 c.JSON(http.StatusOK, models.Response{ 710 c.JSON(http.StatusOK, models.Response{
724 Success: true, 711 Success: true,
725 Message: "Successfully updated user.", 712 Message: "Successfully updated user.",
@@ -744,33 +731,24 @@ func UpdateUser(c *gin.Context) {
744// @Success 200 {object} models.Response 731// @Success 200 {object} models.Response
745// @Router /profile [put] 732// @Router /profile [put]
746func UpdateCountryCode(c *gin.Context) { 733func UpdateCountryCode(c *gin.Context) {
747 // Check if user exists 734 user, _ := c.Get("user")
748 user, exists := c.Get("user")
749 if !exists {
750 c.JSON(http.StatusOK, models.ErrorResponse("User not logged in."))
751 return
752 }
753 code := c.Query("country_code") 735 code := c.Query("country_code")
754 if code == "" { 736 if code == "" {
755 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateCountryFail)
756 c.JSON(http.StatusOK, models.ErrorResponse("Enter a valid country code.")) 737 c.JSON(http.StatusOK, models.ErrorResponse("Enter a valid country code."))
757 return 738 return
758 } 739 }
759 var validCode string 740 var validCode string
760 err := database.DB.QueryRow(`SELECT country_code FROM countries WHERE country_code = $1`, code).Scan(&validCode) 741 err := database.DB.QueryRow(`SELECT country_code FROM countries WHERE country_code = $1`, code).Scan(&validCode)
761 if err != nil { 742 if err != nil {
762 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateCountryFail)
763 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 743 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
764 return 744 return
765 } 745 }
766 // Valid code, update profile 746 // Valid code, update profile
767 _, err = database.DB.Exec(`UPDATE users SET country_code = $1 WHERE steam_id = $2`, validCode, user.(models.User).SteamID) 747 _, err = database.DB.Exec(`UPDATE users SET country_code = $1 WHERE steam_id = $2`, validCode, user.(models.User).SteamID)
768 if err != nil { 748 if err != nil {
769 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateCountryFail)
770 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 749 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
771 return 750 return
772 } 751 }
773 CreateLog(user.(models.User).SteamID, LogTypeUser, LogDescriptionUserUpdateCountrySuccess)
774 c.JSON(http.StatusOK, models.Response{ 752 c.JSON(http.StatusOK, models.Response{
775 Success: true, 753 Success: true,
776 Message: "Successfully updated country code.", 754 Message: "Successfully updated country code.",