diff options
Diffstat (limited to 'backend')
| -rw-r--r-- | backend/controllers/homeController.go | 6 | ||||
| -rw-r--r-- | backend/controllers/recordController.go | 140 | ||||
| -rw-r--r-- | backend/controllers/userController.go | 2 | ||||
| -rw-r--r-- | backend/database/init.sql | 37 | ||||
| -rw-r--r-- | backend/models/models.go | 15 |
5 files changed, 136 insertions, 64 deletions
diff --git a/backend/controllers/homeController.go b/backend/controllers/homeController.go index 8d81eef..635038b 100644 --- a/backend/controllers/homeController.go +++ b/backend/controllers/homeController.go | |||
| @@ -33,21 +33,21 @@ func Login(c *gin.Context) { | |||
| 33 | default: | 33 | default: |
| 34 | steamID, err := openID.ValidateAndGetId() | 34 | steamID, err := openID.ValidateAndGetId() |
| 35 | if err != nil { | 35 | if err != nil { |
| 36 | c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | 36 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 37 | return | 37 | return |
| 38 | } | 38 | } |
| 39 | // Create user if new | 39 | // Create user if new |
| 40 | var checkSteamID int64 | 40 | var checkSteamID int64 |
| 41 | err = database.DB.QueryRow("SELECT steam_id FROM users WHERE steam_id = $1", steamID).Scan(&checkSteamID) | 41 | err = database.DB.QueryRow("SELECT steam_id FROM users WHERE steam_id = $1", steamID).Scan(&checkSteamID) |
| 42 | if err != nil { | 42 | if err != nil { |
| 43 | c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | 43 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 44 | return | 44 | return |
| 45 | } | 45 | } |
| 46 | // User does not exist | 46 | // User does not exist |
| 47 | if checkSteamID == 0 { | 47 | if checkSteamID == 0 { |
| 48 | user, err := steam_go.GetPlayerSummaries(steamID, os.Getenv("API_KEY")) | 48 | user, err := steam_go.GetPlayerSummaries(steamID, os.Getenv("API_KEY")) |
| 49 | if err != nil { | 49 | if err != nil { |
| 50 | c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | 50 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 51 | return | 51 | return |
| 52 | } | 52 | } |
| 53 | // Insert new user to database | 53 | // Insert new user to database |
diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go index a44d6f6..81eb465 100644 --- a/backend/controllers/recordController.go +++ b/backend/controllers/recordController.go | |||
| @@ -3,14 +3,14 @@ package controllers | |||
| 3 | import ( | 3 | import ( |
| 4 | "context" | 4 | "context" |
| 5 | b64 "encoding/base64" | 5 | b64 "encoding/base64" |
| 6 | "fmt" | ||
| 7 | "io" | 6 | "io" |
| 8 | "log" | 7 | "log" |
| 9 | "net/http" | 8 | "net/http" |
| 10 | "os" | 9 | "os" |
| 11 | "time" | 10 | "strconv" |
| 12 | 11 | ||
| 13 | "github.com/gin-gonic/gin" | 12 | "github.com/gin-gonic/gin" |
| 13 | "github.com/google/uuid" | ||
| 14 | "github.com/pektezol/leastportals/backend/database" | 14 | "github.com/pektezol/leastportals/backend/database" |
| 15 | "github.com/pektezol/leastportals/backend/models" | 15 | "github.com/pektezol/leastportals/backend/models" |
| 16 | "golang.org/x/oauth2/google" | 16 | "golang.org/x/oauth2/google" |
| @@ -23,73 +23,125 @@ func CreateRecordWithDemo(c *gin.Context) { | |||
| 23 | // Check if user exists | 23 | // Check if user exists |
| 24 | user, exists := c.Get("user") | 24 | user, exists := c.Get("user") |
| 25 | if !exists { | 25 | if !exists { |
| 26 | c.JSON(http.StatusUnauthorized, gin.H{ | 26 | c.JSON(http.StatusUnauthorized, models.ErrorResponse("User not logged in.")) |
| 27 | "code": http.StatusUnauthorized, | ||
| 28 | "output": gin.H{ | ||
| 29 | "error": "User not logged in. Could be invalid token.", | ||
| 30 | }, | ||
| 31 | }) | ||
| 32 | return | 27 | return |
| 33 | } | 28 | } |
| 34 | var record models.Record | 29 | // Check if map is sp or mp |
| 35 | err := c.Bind(&record) | 30 | var isCoop bool |
| 31 | err := database.DB.QueryRow(`SELECT is_coop FROM maps WHERE id = $1;`, mapId).Scan(&isCoop) | ||
| 36 | if err != nil { | 32 | if err != nil { |
| 37 | c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | 33 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 38 | return | 34 | return |
| 39 | } | 35 | } |
| 40 | var recordCount int | 36 | // Get record request |
| 41 | err = database.DB.QueryRow(`SELECT COUNT(id) FROM records;`).Scan(&recordCount) | 37 | var record models.Record |
| 38 | score_count, err := strconv.Atoi(c.PostForm("score_count")) | ||
| 42 | if err != nil { | 39 | if err != nil { |
| 43 | c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | 40 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 44 | return | 41 | return |
| 45 | } | 42 | } |
| 46 | // var mapName string | 43 | score_time, err := strconv.Atoi(c.PostForm("score_time")) |
| 47 | // err = database.DB.QueryRow(`SELECT map_name FROM maps WHERE id = $1;`, mapId).Scan(&mapName) | ||
| 48 | // if err != nil { | ||
| 49 | // c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | ||
| 50 | // return | ||
| 51 | // } | ||
| 52 | outputDemoName := fmt.Sprintf("%s_%s_%d", time.Now().UTC().Format("2006-01-02"), user.(models.User).SteamID, recordCount) | ||
| 53 | header, err := c.FormFile("file") | ||
| 54 | if err != nil { | 44 | if err != nil { |
| 55 | c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | 45 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 56 | return | 46 | return |
| 57 | } | 47 | } |
| 58 | err = c.SaveUploadedFile(header, "docs/"+header.Filename) | 48 | is_partner_orange, err := strconv.ParseBool(c.PostForm("is_partner_orange")) |
| 59 | if err != nil { | 49 | if err != nil { |
| 60 | c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | 50 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 61 | return | 51 | return |
| 62 | } | 52 | } |
| 63 | f, err := os.Open("docs/" + header.Filename) | 53 | record.ScoreCount = score_count |
| 54 | record.ScoreTime = score_time | ||
| 55 | record.PartnerID = c.PostForm("partner_id") | ||
| 56 | record.IsPartnerOrange = is_partner_orange | ||
| 57 | // Multipart form | ||
| 58 | form, err := c.MultipartForm() | ||
| 64 | if err != nil { | 59 | if err != nil { |
| 65 | c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | 60 | c.String(http.StatusBadRequest, "get form err: %s", err.Error()) |
| 66 | return | 61 | return |
| 67 | } | 62 | } |
| 68 | defer f.Close() | 63 | files := form.File["demos"] |
| 69 | client := serviceAccount() | 64 | if len(files) != 2 && isCoop { |
| 70 | srv, err := drive.New(client) | 65 | c.JSON(http.StatusBadRequest, models.ErrorResponse("Not enough demos for coop submission.")) |
| 71 | if err != nil { | ||
| 72 | c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | ||
| 73 | return | 66 | return |
| 74 | } | 67 | } |
| 75 | file, err := createFile(srv, outputDemoName, "application/octet-stream", f, os.Getenv("GOOGLE_FOLDER_ID")) | 68 | if len(files) != 1 && !isCoop { |
| 76 | if err != nil { | 69 | c.JSON(http.StatusBadRequest, models.ErrorResponse("Too many demos for singleplayer submission.")) |
| 77 | c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | ||
| 78 | return | 70 | return |
| 79 | } | 71 | } |
| 80 | os.Remove("docs/" + header.Filename) | 72 | var hostDemoUUID string |
| 81 | // Demo upload success | 73 | var partnerDemoUUID string |
| 82 | // Insert record into database | 74 | for i, header := range files { |
| 83 | sql := `INSERT INTO records(map_id,host_id,score_count,score_time,is_coop,partner_id,demo_id) | 75 | uuid := uuid.New().String() |
| 84 | VALUES ($1, $2, $3, $4, $5, $6, $7);` | 76 | // Upload & insert into demos |
| 85 | _, err = database.DB.Exec(sql, mapId, user.(models.User).SteamID, record.ScoreCount, record.ScoreTime, record.IsCoop, record.PartnerID, file.Id) | 77 | err = c.SaveUploadedFile(header, "docs/"+header.Filename) |
| 86 | if err != nil { | 78 | if err != nil { |
| 87 | c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | 79 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 88 | return | 80 | return |
| 81 | } | ||
| 82 | f, err := os.Open("docs/" + header.Filename) | ||
| 83 | if err != nil { | ||
| 84 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 85 | return | ||
| 86 | } | ||
| 87 | defer f.Close() | ||
| 88 | client := serviceAccount() | ||
| 89 | srv, err := drive.New(client) | ||
| 90 | if err != nil { | ||
| 91 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 92 | return | ||
| 93 | } | ||
| 94 | file, err := createFile(srv, uuid, "application/octet-stream", f, os.Getenv("GOOGLE_FOLDER_ID")) | ||
| 95 | if err != nil { | ||
| 96 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 97 | return | ||
| 98 | } | ||
| 99 | if i == 0 { | ||
| 100 | hostDemoUUID = uuid | ||
| 101 | } | ||
| 102 | if i == 1 { | ||
| 103 | partnerDemoUUID = uuid | ||
| 104 | } | ||
| 105 | _, err = database.DB.Exec(`INSERT INTO demos (id,location_id) VALUES ($1,$2)`, uuid, file.Id) | ||
| 106 | if err != nil { | ||
| 107 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 108 | return | ||
| 109 | } | ||
| 110 | os.Remove("docs/" + header.Filename) | ||
| 111 | } | ||
| 112 | // Insert into records | ||
| 113 | if isCoop { | ||
| 114 | sql := `INSERT INTO records_mp(map_id,score_count,score_time,host_id,partner_id,host_demo_id,partner_demo_id) | ||
| 115 | VALUES($1, $2, $3, $4, $5, $6, $7);` | ||
| 116 | var hostID string | ||
| 117 | var partnerID string | ||
| 118 | if record.IsPartnerOrange { | ||
| 119 | hostID = user.(models.User).SteamID | ||
| 120 | partnerID = record.PartnerID | ||
| 121 | } else { | ||
| 122 | partnerID = user.(models.User).SteamID | ||
| 123 | hostID = record.PartnerID | ||
| 124 | } | ||
| 125 | _, err := database.DB.Exec(sql, mapId, record.ScoreCount, record.ScoreTime, hostID, partnerID, hostDemoUUID, partnerDemoUUID) | ||
| 126 | if err != nil { | ||
| 127 | _, err = database.DB.Exec(`DELETE FROM demos WHERE id = $1 OR id = $2;`, hostDemoUUID, partnerDemoUUID) | ||
| 128 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 129 | return | ||
| 130 | } | ||
| 131 | } else { | ||
| 132 | sql := `INSERT INTO records_sp(map_id,score_count,score_time,user_id,demo_id) | ||
| 133 | VALUES($1, $2, $3, $4, $5);` | ||
| 134 | _, err := database.DB.Exec(sql, mapId, record.ScoreCount, record.ScoreTime, user.(models.User).SteamID, hostDemoUUID) | ||
| 135 | if err != nil { | ||
| 136 | _, err = database.DB.Exec(`DELETE FROM demos WHERE id = $1;`, hostDemoUUID) | ||
| 137 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) | ||
| 138 | return | ||
| 139 | } | ||
| 89 | } | 140 | } |
| 90 | c.JSON(http.StatusOK, models.Response{ | 141 | c.JSON(http.StatusOK, models.Response{ |
| 91 | Success: true, | 142 | Success: true, |
| 92 | Message: "Successfully created record.", | 143 | Message: "Successfully created record.", |
| 144 | Data: record, | ||
| 93 | }) | 145 | }) |
| 94 | return | 146 | return |
| 95 | } | 147 | } |
diff --git a/backend/controllers/userController.go b/backend/controllers/userController.go index 70a2a34..b23a303 100644 --- a/backend/controllers/userController.go +++ b/backend/controllers/userController.go | |||
| @@ -49,7 +49,7 @@ func FetchUser(c *gin.Context) { | |||
| 49 | return | 49 | return |
| 50 | } | 50 | } |
| 51 | if err != nil { | 51 | if err != nil { |
| 52 | c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error())) | 52 | c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) |
| 53 | return | 53 | return |
| 54 | } | 54 | } |
| 55 | // Target user exists | 55 | // Target user exists |
diff --git a/backend/database/init.sql b/backend/database/init.sql index f69bf10..12a2487 100644 --- a/backend/database/init.sql +++ b/backend/database/init.sql | |||
| @@ -1,9 +1,12 @@ | |||
| 1 | DROP TABLE IF EXISTS showcases; | 1 | DROP TABLE IF EXISTS showcases; |
| 2 | DROP TABLE IF EXISTS titles; | 2 | DROP TABLE IF EXISTS titles; |
| 3 | DROP TABLE IF EXISTS records; | 3 | DROP TABLE IF EXISTS records_sp; |
| 4 | DROP TABLE IF EXISTS records_mp; | ||
| 4 | DROP TABLE IF EXISTS maps; | 5 | DROP TABLE IF EXISTS maps; |
| 5 | DROP TABLE IF EXISTS users; | 6 | DROP TABLE IF EXISTS users; |
| 6 | 7 | ||
| 8 | DROP TABLE IF EXISTS demos; | ||
| 9 | |||
| 7 | CREATE TABLE users ( | 10 | CREATE TABLE users ( |
| 8 | steam_id TEXT, | 11 | steam_id TEXT, |
| 9 | username TEXT NOT NULL, | 12 | username TEXT NOT NULL, |
| @@ -23,20 +26,42 @@ CREATE TABLE maps ( | |||
| 23 | PRIMARY KEY (id) | 26 | PRIMARY KEY (id) |
| 24 | ); | 27 | ); |
| 25 | 28 | ||
| 26 | CREATE TABLE records ( | 29 | CREATE TABLE demos ( |
| 30 | id UUID, | ||
| 31 | location_id TEXT NOT NULL, | ||
| 32 | PRIMARY KEY (id) | ||
| 33 | ); | ||
| 34 | |||
| 35 | CREATE TABLE records_sp ( | ||
| 36 | id SERIAL, | ||
| 37 | map_id SMALLINT NOT NULL, | ||
| 38 | user_id TEXT NOT NULL, | ||
| 39 | score_count SMALLINT NOT NULL, | ||
| 40 | score_time INTEGER NOT NULL, | ||
| 41 | demo_id UUID NOT NULL, | ||
| 42 | record_date TIMESTAMP NOT NULL DEFAULT now(), | ||
| 43 | PRIMARY KEY (id), | ||
| 44 | FOREIGN KEY (map_id) REFERENCES maps(id), | ||
| 45 | FOREIGN KEY (user_id) REFERENCES users(steam_id), | ||
| 46 | FOREIGN KEY (demo_id) REFERENCES demos(id) | ||
| 47 | ); | ||
| 48 | |||
| 49 | CREATE TABLE records_mp ( | ||
| 27 | id SERIAL, | 50 | id SERIAL, |
| 28 | map_id SMALLINT NOT NULL, | 51 | map_id SMALLINT NOT NULL, |
| 29 | host_id TEXT NOT NULL, | 52 | host_id TEXT NOT NULL, |
| 53 | partner_id TEXT NOT NULL, | ||
| 30 | score_count SMALLINT NOT NULL, | 54 | score_count SMALLINT NOT NULL, |
| 31 | score_time INTEGER NOT NULL, | 55 | score_time INTEGER NOT NULL, |
| 32 | is_coop BOOLEAN NOT NULL DEFAULT false, | 56 | host_demo_id UUID NOT NULL, |
| 33 | partner_id TEXT NOT NULL DEFAULT '', | 57 | partner_demo_id UUID NOT NULL, |
| 34 | demo_id TEXT NOT NULL, | ||
| 35 | record_date TIMESTAMP NOT NULL DEFAULT now(), | 58 | record_date TIMESTAMP NOT NULL DEFAULT now(), |
| 36 | PRIMARY KEY (id), | 59 | PRIMARY KEY (id), |
| 37 | FOREIGN KEY (map_id) REFERENCES maps(id), | 60 | FOREIGN KEY (map_id) REFERENCES maps(id), |
| 38 | FOREIGN KEY (host_id) REFERENCES users(steam_id), | 61 | FOREIGN KEY (host_id) REFERENCES users(steam_id), |
| 39 | FOREIGN KEY (partner_id) REFERENCES users(steam_id) | 62 | FOREIGN KEY (partner_id) REFERENCES users(steam_id), |
| 63 | FOREIGN KEY (host_demo_id) REFERENCES demos(id), | ||
| 64 | FOREIGN KEY (partner_demo_id) REFERENCES demos(id) | ||
| 40 | ); | 65 | ); |
| 41 | 66 | ||
| 42 | CREATE TABLE titles ( | 67 | CREATE TABLE titles ( |
diff --git a/backend/models/models.go b/backend/models/models.go index 994a1e7..8f4a5e5 100644 --- a/backend/models/models.go +++ b/backend/models/models.go | |||
| @@ -1,7 +1,6 @@ | |||
| 1 | package models | 1 | package models |
| 2 | 2 | ||
| 3 | import ( | 3 | import ( |
| 4 | "mime/multipart" | ||
| 5 | "time" | 4 | "time" |
| 6 | ) | 5 | ) |
| 7 | 6 | ||
| @@ -15,13 +14,9 @@ type User struct { | |||
| 15 | } | 14 | } |
| 16 | 15 | ||
| 17 | type Record struct { | 16 | type Record struct { |
| 18 | MapID int `json:"map_id" binding:"required"` | 17 | ScoreCount int `json:"score_count" form:"score_count" binding:"required"` |
| 19 | ScoreCount int `json:"score_count" binding:"required"` | 18 | ScoreTime int `json:"score_time" form:"score_time" binding:"required"` |
| 20 | ScoreTime int `json:"score_time" binding:"required"` | 19 | PartnerID string `json:"partner_id" form:"partner_id" binding:"required"` |
| 21 | IsCoop bool `json:"is_coop" binding:"required"` | 20 | IsPartnerOrange bool `json:"is_partner_orange" form:"is_partner_orange" binding:"required"` |
| 22 | PartnerID string `json:"partner_id"` | 21 | //Demos []*multipart.FileHeader `form:"demos[]" binding:"required"` |
| 23 | } | ||
| 24 | |||
| 25 | type ds struct { | ||
| 26 | File *multipart.FileHeader `form:"file" binding:"required"` | ||
| 27 | } | 22 | } |