aboutsummaryrefslogtreecommitdiff
path: root/backend/controllers/recordController.go
diff options
context:
space:
mode:
authorNidboj132 <lol2s@vp.plm>2023-07-12 17:58:23 +0200
committerNidboj132 <lol2s@vp.plm>2023-07-12 17:58:23 +0200
commit781289455037431d8adbaa0b293b755c88169747 (patch)
tree773824f97c3b21d353b9066afdbde30bee2da4c5 /backend/controllers/recordController.go
parentsummary (diff)
parentfix: 0 score count / showcase not required (#47) (diff)
downloadlphub-781289455037431d8adbaa0b293b755c88169747.tar.gz
lphub-781289455037431d8adbaa0b293b755c88169747.tar.bz2
lphub-781289455037431d8adbaa0b293b755c88169747.zip
Merge branch 'main' of https://github.com/pektezol/LeastPortals
Former-commit-id: af8d8680aafc3d662f8b53a4f50f0ea356b26c26
Diffstat (limited to 'backend/controllers/recordController.go')
-rw-r--r--backend/controllers/recordController.go134
1 files changed, 57 insertions, 77 deletions
diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go
index 627be57..d1404f4 100644
--- a/backend/controllers/recordController.go
+++ b/backend/controllers/recordController.go
@@ -2,17 +2,18 @@ package controllers
2 2
3import ( 3import (
4 "context" 4 "context"
5 b64 "encoding/base64" 5 "encoding/base64"
6 "io" 6 "io"
7 "log" 7 "log"
8 "mime/multipart"
8 "net/http" 9 "net/http"
9 "os" 10 "os"
10 "strconv"
11 11
12 "github.com/gin-gonic/gin" 12 "github.com/gin-gonic/gin"
13 "github.com/google/uuid" 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 "github.com/pektezol/leastportals/backend/parser"
16 "golang.org/x/oauth2/google" 17 "golang.org/x/oauth2/google"
17 "golang.org/x/oauth2/jwt" 18 "golang.org/x/oauth2/jwt"
18 "google.golang.org/api/drive/v3" 19 "google.golang.org/api/drive/v3"
@@ -20,20 +21,20 @@ import (
20 21
21// POST Record 22// POST Record
22// 23//
23// @Summary Post record with demo of a specific map. 24// @Description Post record with demo of a specific map.
24// @Tags maps 25// @Tags maps
25// @Accept mpfd 26// @Accept mpfd
26// @Produce json 27// @Produce json
27// @Param Authorization header string true "JWT Token" 28// @Param id path int true "Map ID"
28// @Param demos formData []file true "Demos" 29// @Param Authorization header string true "JWT Token"
29// @Param score_count formData int true "Score Count" 30// @Param host_demo formData file true "Host Demo"
30// @Param score_time formData int true "Score Time" 31// @Param partner_demo formData file false "Partner Demo"
31// @Param is_partner_orange formData boolean true "Is Partner Orange" 32// @Param is_partner_orange formData boolean false "Is Partner Orange"
32// @Param partner_id formData string true "Partner ID" 33// @Param partner_id formData string false "Partner ID"
33// @Success 200 {object} models.Response{data=models.RecordRequest} 34// @Success 200 {object} models.Response{data=models.RecordResponse}
34// @Failure 400 {object} models.Response 35// @Failure 400 {object} models.Response
35// @Failure 401 {object} models.Response 36// @Failure 401 {object} models.Response
36// @Router /maps/{id}/record [post] 37// @Router /maps/{id}/record [post]
37func CreateRecordWithDemo(c *gin.Context) { 38func CreateRecordWithDemo(c *gin.Context) {
38 mapId := c.Param("id") 39 mapId := c.Param("id")
39 // Check if user exists 40 // Check if user exists
@@ -43,11 +44,11 @@ func CreateRecordWithDemo(c *gin.Context) {
43 return 44 return
44 } 45 }
45 // Check if map is sp or mp 46 // Check if map is sp or mp
46 var gameID int 47 var gameName string
47 var isCoop bool 48 var isCoop bool
48 var isDisabled bool 49 var isDisabled bool
49 sql := `SELECT game_id, is_disabled FROM maps WHERE id = $1` 50 sql := `SELECT g.name, m.is_disabled FROM maps m INNER JOIN games g ON m.game_id=g.id WHERE m.id = $1`
50 err := database.DB.QueryRow(sql, mapId).Scan(&gameID, &isDisabled) 51 err := database.DB.QueryRow(sql, mapId).Scan(&gameName, &isDisabled)
51 if err != nil { 52 if err != nil {
52 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 53 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
53 return 54 return
@@ -56,51 +57,26 @@ func CreateRecordWithDemo(c *gin.Context) {
56 c.JSON(http.StatusBadRequest, models.ErrorResponse("Map is not available for competitive boards.")) 57 c.JSON(http.StatusBadRequest, models.ErrorResponse("Map is not available for competitive boards."))
57 return 58 return
58 } 59 }
59 if gameID == 2 { 60 if gameName == "Portal 2 - Cooperative" {
60 isCoop = true 61 isCoop = true
61 } 62 }
62 // Get record request 63 // Get record request
63 var record models.RecordRequest 64 var record models.RecordRequest
64 score_count, err := strconv.Atoi(c.PostForm("score_count")) 65 if err := c.ShouldBind(&record); err != nil {
65 if err != nil {
66 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
67 return
68 }
69 score_time, err := strconv.Atoi(c.PostForm("score_time"))
70 if err != nil {
71 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
72 return
73 }
74 is_partner_orange, err := strconv.ParseBool(c.PostForm("is_partner_orange"))
75 if err != nil {
76 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
77 return
78 }
79 record.ScoreCount = score_count
80 record.ScoreTime = score_time
81 record.PartnerID = c.PostForm("partner_id")
82 record.IsPartnerOrange = is_partner_orange
83 if record.PartnerID == "" {
84 c.JSON(http.StatusBadRequest, models.ErrorResponse("No partner id given."))
85 return
86 }
87 // Multipart form
88 form, err := c.MultipartForm()
89 if err != nil {
90 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 66 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
91 return 67 return
92 } 68 }
93 files := form.File["demos"] 69 if isCoop && (record.PartnerDemo == nil || record.PartnerID == "") {
94 if len(files) != 2 && isCoop { 70 c.JSON(http.StatusBadRequest, models.ErrorResponse("Invalid entry for coop record submission."))
95 c.JSON(http.StatusBadRequest, models.ErrorResponse("Not enough demos for coop submission."))
96 return 71 return
97 } 72 }
98 if len(files) != 1 && !isCoop { 73 // Demo files
99 c.JSON(http.StatusBadRequest, models.ErrorResponse("Too many demos for singleplayer submission.")) 74 demoFiles := []*multipart.FileHeader{record.HostDemo}
100 return 75 if isCoop {
76 demoFiles = append(demoFiles, record.PartnerDemo)
101 } 77 }
102 var hostDemoUUID string 78 var hostDemoUUID, hostDemoFileID, partnerDemoUUID, partnerDemoFileID string
103 var partnerDemoUUID string 79 var hostDemoScoreCount, hostDemoScoreTime int
104 client := serviceAccount() 80 client := serviceAccount()
105 srv, err := drive.New(client) 81 srv, err := drive.New(client)
106 if err != nil { 82 if err != nil {
@@ -115,16 +91,16 @@ func CreateRecordWithDemo(c *gin.Context) {
115 } 91 }
116 // Defer to a rollback in case anything fails 92 // Defer to a rollback in case anything fails
117 defer tx.Rollback() 93 defer tx.Rollback()
118 fileID := "" 94 for i, header := range demoFiles {
119 for i, header := range files {
120 uuid := uuid.New().String() 95 uuid := uuid.New().String()
121 // Upload & insert into demos 96 // Upload & insert into demos
122 err = c.SaveUploadedFile(header, "docs/"+header.Filename) 97 err = c.SaveUploadedFile(header, "backend/parser/"+uuid+".dem")
123 if err != nil { 98 if err != nil {
124 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 99 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
125 return 100 return
126 } 101 }
127 f, err := os.Open("docs/" + header.Filename) 102 defer os.Remove("backend/parser/" + uuid + ".dem")
103 f, err := os.Open("backend/parser/" + uuid + ".dem")
128 if err != nil { 104 if err != nil {
129 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 105 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
130 return 106 return
@@ -135,11 +111,16 @@ func CreateRecordWithDemo(c *gin.Context) {
135 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 111 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
136 return 112 return
137 } 113 }
138 fileID = file.Id 114 hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("backend/parser/" + uuid + ".dem")
115 if err != nil {
116 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
117 return
118 }
139 if i == 0 { 119 if i == 0 {
120 hostDemoFileID = file.Id
140 hostDemoUUID = uuid 121 hostDemoUUID = uuid
141 } 122 } else if i == 1 {
142 if i == 1 { 123 partnerDemoFileID = file.Id
143 partnerDemoUUID = uuid 124 partnerDemoUUID = uuid
144 } 125 }
145 _, err = tx.Exec(`INSERT INTO demos (id,location_id) VALUES ($1,$2)`, uuid, file.Id) 126 _, err = tx.Exec(`INSERT INTO demos (id,location_id) VALUES ($1,$2)`, uuid, file.Id)
@@ -148,7 +129,6 @@ func CreateRecordWithDemo(c *gin.Context) {
148 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 129 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
149 return 130 return
150 } 131 }
151 os.Remove("docs/" + header.Filename)
152 } 132 }
153 // Insert into records 133 // Insert into records
154 if isCoop { 134 if isCoop {
@@ -163,9 +143,10 @@ func CreateRecordWithDemo(c *gin.Context) {
163 partnerID = user.(models.User).SteamID 143 partnerID = user.(models.User).SteamID
164 hostID = record.PartnerID 144 hostID = record.PartnerID
165 } 145 }
166 _, err := tx.Exec(sql, mapId, record.ScoreCount, record.ScoreTime, hostID, partnerID, hostDemoUUID, partnerDemoUUID) 146 _, err := tx.Exec(sql, mapId, hostDemoScoreCount, hostDemoScoreTime, hostID, partnerID, hostDemoUUID, partnerDemoUUID)
167 if err != nil { 147 if err != nil {
168 deleteFile(srv, fileID) 148 deleteFile(srv, hostDemoFileID)
149 deleteFile(srv, partnerDemoFileID)
169 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 150 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
170 return 151 return
171 } 152 }
@@ -180,9 +161,9 @@ func CreateRecordWithDemo(c *gin.Context) {
180 } else { 161 } else {
181 sql := `INSERT INTO records_sp(map_id,score_count,score_time,user_id,demo_id) 162 sql := `INSERT INTO records_sp(map_id,score_count,score_time,user_id,demo_id)
182 VALUES($1, $2, $3, $4, $5)` 163 VALUES($1, $2, $3, $4, $5)`
183 _, err := tx.Exec(sql, mapId, record.ScoreCount, record.ScoreTime, user.(models.User).SteamID, hostDemoUUID) 164 _, err := tx.Exec(sql, mapId, hostDemoScoreCount, hostDemoScoreTime, user.(models.User).SteamID, hostDemoUUID)
184 if err != nil { 165 if err != nil {
185 deleteFile(srv, fileID) 166 deleteFile(srv, hostDemoFileID)
186 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error())) 167 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
187 return 168 return
188 } 169 }
@@ -202,21 +183,20 @@ func CreateRecordWithDemo(c *gin.Context) {
202 c.JSON(http.StatusOK, models.Response{ 183 c.JSON(http.StatusOK, models.Response{
203 Success: true, 184 Success: true,
204 Message: "Successfully created record.", 185 Message: "Successfully created record.",
205 Data: record, 186 Data: models.RecordResponse{ScoreCount: hostDemoScoreCount, ScoreTime: hostDemoScoreTime},
206 }) 187 })
207 return
208} 188}
209 189
210// GET Demo 190// GET Demo
211// 191//
212// @Summary Get demo with specified demo uuid. 192// @Description Get demo with specified demo uuid.
213// @Tags demo 193// @Tags demo
214// @Accept json 194// @Accept json
215// @Produce octet-stream 195// @Produce octet-stream
216// @Param uuid query int true "Demo UUID" 196// @Param uuid query string true "Demo UUID"
217// @Success 200 {file} binary "Demo File" 197// @Success 200 {file} binary "Demo File"
218// @Failure 400 {object} models.Response 198// @Failure 400 {object} models.Response
219// @Router /demos [get] 199// @Router /demos [get]
220func DownloadDemoWithID(c *gin.Context) { 200func DownloadDemoWithID(c *gin.Context) {
221 uuid := c.Query("uuid") 201 uuid := c.Query("uuid")
222 var locationID string 202 var locationID string
@@ -260,7 +240,7 @@ func DownloadDemoWithID(c *gin.Context) {
260 240
261// Use Service account 241// Use Service account
262func serviceAccount() *http.Client { 242func serviceAccount() *http.Client {
263 privateKey, _ := b64.StdEncoding.DecodeString(os.Getenv("GOOGLE_PRIVATE_KEY_BASE64")) 243 privateKey, _ := base64.StdEncoding.DecodeString(os.Getenv("GOOGLE_PRIVATE_KEY_BASE64"))
264 config := &jwt.Config{ 244 config := &jwt.Config{
265 Email: os.Getenv("GOOGLE_CLIENT_EMAIL"), 245 Email: os.Getenv("GOOGLE_CLIENT_EMAIL"),
266 PrivateKey: []byte(privateKey), 246 PrivateKey: []byte(privateKey),