aboutsummaryrefslogtreecommitdiff
path: root/backend/controllers/recordController.go
diff options
context:
space:
mode:
authorNidboj132 <lol2s@vp.plm>2023-09-05 18:23:11 +0200
committerNidboj132 <lol2s@vp.plm>2023-09-05 18:23:11 +0200
commit3869cb67351ccf3bc45b076f31afdc7133292c39 (patch)
treedc03341e147dde0964bf6be84b14e13424c647b7 /backend/controllers/recordController.go
parentadded graph and fixed some css (diff)
parentfix: create map summary, why the fuck does this have to be a pointer integer?? (diff)
downloadlphub-3869cb67351ccf3bc45b076f31afdc7133292c39.tar.gz
lphub-3869cb67351ccf3bc45b076f31afdc7133292c39.tar.bz2
lphub-3869cb67351ccf3bc45b076f31afdc7133292c39.zip
Merge branch 'main' of https://github.com/pektezol/LeastPortalsHub
Former-commit-id: 221385f463b7f5b0fc43a093b2c7c46e68d46d68
Diffstat (limited to 'backend/controllers/recordController.go')
-rw-r--r--backend/controllers/recordController.go274
1 files changed, 0 insertions, 274 deletions
diff --git a/backend/controllers/recordController.go b/backend/controllers/recordController.go
deleted file mode 100644
index 951be41..0000000
--- a/backend/controllers/recordController.go
+++ /dev/null
@@ -1,274 +0,0 @@
1package controllers
2
3import (
4 "context"
5 "encoding/base64"
6 "io"
7 "log"
8 "mime/multipart"
9 "net/http"
10 "os"
11
12 "github.com/gin-gonic/gin"
13 "github.com/google/uuid"
14 "github.com/pektezol/leastportalshub/backend/database"
15 "github.com/pektezol/leastportalshub/backend/models"
16 "github.com/pektezol/leastportalshub/backend/parser"
17 "golang.org/x/oauth2/google"
18 "golang.org/x/oauth2/jwt"
19 "google.golang.org/api/drive/v3"
20)
21
22// POST Record
23//
24// @Description Post record with demo of a specific map.
25// @Tags maps
26// @Accept mpfd
27// @Produce json
28// @Param id path int true "Map ID"
29// @Param Authorization header string true "JWT Token"
30// @Param host_demo formData file true "Host Demo"
31// @Param partner_demo formData file false "Partner Demo"
32// @Param is_partner_orange formData boolean false "Is Partner Orange"
33// @Param partner_id formData string false "Partner ID"
34// @Success 200 {object} models.Response{data=models.RecordResponse}
35// @Failure 400 {object} models.Response
36// @Failure 401 {object} models.Response
37// @Router /maps/{id}/record [post]
38func CreateRecordWithDemo(c *gin.Context) {
39 mapId := c.Param("id")
40 // Check if user exists
41 user, exists := c.Get("user")
42 if !exists {
43 c.JSON(http.StatusUnauthorized, models.ErrorResponse("User not logged in."))
44 return
45 }
46 // Check if map is sp or mp
47 var gameName string
48 var isCoop bool
49 var isDisabled bool
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`
51 err := database.DB.QueryRow(sql, mapId).Scan(&gameName, &isDisabled)
52 if err != nil {
53 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
54 return
55 }
56 if isDisabled {
57 c.JSON(http.StatusBadRequest, models.ErrorResponse("Map is not available for competitive boards."))
58 return
59 }
60 if gameName == "Portal 2 - Cooperative" {
61 isCoop = true
62 }
63 // Get record request
64 var record models.RecordRequest
65 if err := c.ShouldBind(&record); err != nil {
66 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
67 return
68 }
69 if isCoop && (record.PartnerDemo == nil || record.PartnerID == "") {
70 c.JSON(http.StatusBadRequest, models.ErrorResponse("Invalid entry for coop record submission."))
71 return
72 }
73 // Demo files
74 demoFiles := []*multipart.FileHeader{record.HostDemo}
75 if isCoop {
76 demoFiles = append(demoFiles, record.PartnerDemo)
77 }
78 var hostDemoUUID, hostDemoFileID, partnerDemoUUID, partnerDemoFileID string
79 var hostDemoScoreCount, hostDemoScoreTime int
80 client := serviceAccount()
81 srv, err := drive.New(client)
82 if err != nil {
83 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
84 return
85 }
86 // Create database transaction for inserts
87 tx, err := database.DB.Begin()
88 if err != nil {
89 c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error()))
90 return
91 }
92 // Defer to a rollback in case anything fails
93 defer tx.Rollback()
94 for i, header := range demoFiles {
95 uuid := uuid.New().String()
96 // Upload & insert into demos
97 err = c.SaveUploadedFile(header, "backend/parser/"+uuid+".dem")
98 if err != nil {
99 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
100 return
101 }
102 defer os.Remove("backend/parser/" + uuid + ".dem")
103 f, err := os.Open("backend/parser/" + uuid + ".dem")
104 if err != nil {
105 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
106 return
107 }
108 defer f.Close()
109 file, err := createFile(srv, uuid+".dem", "application/octet-stream", f, os.Getenv("GOOGLE_FOLDER_ID"))
110 if err != nil {
111 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
112 return
113 }
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 }
119 if i == 0 {
120 hostDemoFileID = file.Id
121 hostDemoUUID = uuid
122 } else if i == 1 {
123 partnerDemoFileID = file.Id
124 partnerDemoUUID = uuid
125 }
126 _, err = tx.Exec(`INSERT INTO demos (id,location_id) VALUES ($1,$2)`, uuid, file.Id)
127 if err != nil {
128 deleteFile(srv, file.Id)
129 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
130 return
131 }
132 }
133 // Insert into records
134 if isCoop {
135 sql := `INSERT INTO records_mp(map_id,score_count,score_time,host_id,partner_id,host_demo_id,partner_demo_id)
136 VALUES($1, $2, $3, $4, $5, $6, $7)`
137 var hostID string
138 var partnerID string
139 if record.IsPartnerOrange {
140 hostID = user.(models.User).SteamID
141 partnerID = record.PartnerID
142 } else {
143 partnerID = user.(models.User).SteamID
144 hostID = record.PartnerID
145 }
146 _, err := tx.Exec(sql, mapId, hostDemoScoreCount, hostDemoScoreTime, hostID, partnerID, hostDemoUUID, partnerDemoUUID)
147 if err != nil {
148 deleteFile(srv, hostDemoFileID)
149 deleteFile(srv, partnerDemoFileID)
150 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
151 return
152 }
153 // If a new world record based on portal count
154 // if record.ScoreCount < wrScore {
155 // _, err := tx.Exec(`UPDATE maps SET wr_score = $1, wr_time = $2 WHERE id = $3`, record.ScoreCount, record.ScoreTime, mapId)
156 // if err != nil {
157 // c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
158 // return
159 // }
160 // }
161 } else {
162 sql := `INSERT INTO records_sp(map_id,score_count,score_time,user_id,demo_id)
163 VALUES($1, $2, $3, $4, $5)`
164 _, err := tx.Exec(sql, mapId, hostDemoScoreCount, hostDemoScoreTime, user.(models.User).SteamID, hostDemoUUID)
165 if err != nil {
166 deleteFile(srv, hostDemoFileID)
167 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
168 return
169 }
170 // If a new world record based on portal count
171 // if record.ScoreCount < wrScore {
172 // _, err := tx.Exec(`UPDATE maps SET wr_score = $1, wr_time = $2 WHERE id = $3`, record.ScoreCount, record.ScoreTime, mapId)
173 // if err != nil {
174 // c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
175 // return
176 // }
177 // }
178 }
179 if err = tx.Commit(); err != nil {
180 c.JSON(http.StatusInternalServerError, models.ErrorResponse(err.Error()))
181 return
182 }
183 c.JSON(http.StatusOK, models.Response{
184 Success: true,
185 Message: "Successfully created record.",
186 Data: models.RecordResponse{ScoreCount: hostDemoScoreCount, ScoreTime: hostDemoScoreTime},
187 })
188}
189
190// GET Demo
191//
192// @Description Get demo with specified demo uuid.
193// @Tags demo
194// @Accept json
195// @Produce octet-stream
196// @Param uuid query string true "Demo UUID"
197// @Success 200 {file} binary "Demo File"
198// @Failure 400 {object} models.Response
199// @Router /demos [get]
200func DownloadDemoWithID(c *gin.Context) {
201 uuid := c.Query("uuid")
202 var locationID string
203 if uuid == "" {
204 c.JSON(http.StatusBadRequest, models.ErrorResponse("Invalid id given."))
205 return
206 }
207 err := database.DB.QueryRow(`SELECT location_id FROM demos WHERE id = $1`, uuid).Scan(&locationID)
208 if err != nil {
209 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
210 return
211 }
212 if locationID == "" {
213 c.JSON(http.StatusBadRequest, models.ErrorResponse("Invalid id given."))
214 return
215 }
216 url := "https://drive.google.com/uc?export=download&id=" + locationID
217 fileName := uuid + ".dem"
218 output, err := os.Create(fileName)
219 defer os.Remove(fileName)
220 defer output.Close()
221 response, err := http.Get(url)
222 if err != nil {
223 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
224 return
225 }
226 defer response.Body.Close()
227 _, err = io.Copy(output, response.Body)
228 if err != nil {
229 c.JSON(http.StatusBadRequest, models.ErrorResponse(err.Error()))
230 return
231 }
232 // Downloaded file
233 c.Header("Content-Description", "File Transfer")
234 c.Header("Content-Transfer-Encoding", "binary")
235 c.Header("Content-Disposition", "attachment; filename="+fileName)
236 c.Header("Content-Type", "application/octet-stream")
237 c.File(fileName)
238 // c.FileAttachment()
239}
240
241// Use Service account
242func serviceAccount() *http.Client {
243 privateKey, _ := base64.StdEncoding.DecodeString(os.Getenv("GOOGLE_PRIVATE_KEY_BASE64"))
244 config := &jwt.Config{
245 Email: os.Getenv("GOOGLE_CLIENT_EMAIL"),
246 PrivateKey: []byte(privateKey),
247 Scopes: []string{
248 drive.DriveScope,
249 },
250 TokenURL: google.JWTTokenURL,
251 }
252 client := config.Client(context.Background())
253 return client
254}
255
256func createFile(service *drive.Service, name string, mimeType string, content io.Reader, parentId string) (*drive.File, error) {
257 f := &drive.File{
258 MimeType: mimeType,
259 Name: name,
260 Parents: []string{parentId},
261 }
262 file, err := service.Files.Create(f).Media(content).Do()
263
264 if err != nil {
265 log.Println("Could not create file: " + err.Error())
266 return nil, err
267 }
268
269 return file, nil
270}
271
272func deleteFile(service *drive.Service, fileId string) {
273 service.Files.Delete(fileId)
274}