From cff5e845c75e9e2751a2c1f01f8ae3fbf24f0e7d Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:12:07 +0300 Subject: feat/backend: gdrive to backblaze migration, improve create record (#237) --- backend/handlers/record.go | 140 +++++++++++++++------------------------------ 1 file changed, 45 insertions(+), 95 deletions(-) (limited to 'backend/handlers') diff --git a/backend/handlers/record.go b/backend/handlers/record.go index bedde57..91e74b9 100644 --- a/backend/handlers/record.go +++ b/backend/handlers/record.go @@ -2,10 +2,8 @@ package handlers import ( "context" - "encoding/base64" "fmt" "io" - "log" "mime/multipart" "net/http" "os" @@ -16,11 +14,9 @@ import ( "lphub/models" "lphub/parser" + "github.com/Backblaze/blazer/b2" "github.com/gin-gonic/gin" "github.com/google/uuid" - "golang.org/x/oauth2/google" - "golang.org/x/oauth2/jwt" - "google.golang.org/api/drive/v3" ) type RecordRequest struct { @@ -79,19 +75,14 @@ func CreateRecordWithDemo(c *gin.Context) { return } // Demo files - demoFiles := []*multipart.FileHeader{record.HostDemo} + demoFileHeaders := []*multipart.FileHeader{record.HostDemo} if isCoop { - demoFiles = append(demoFiles, record.PartnerDemo) + demoFileHeaders = append(demoFileHeaders, record.PartnerDemo) } - var hostDemoUUID, hostDemoFileID, partnerDemoUUID, partnerDemoFileID string + var hostDemoUUID, partnerDemoUUID string var hostDemoScoreCount, hostDemoScoreTime int var hostSteamID, partnerSteamID string var hostDemoServerNumber, partnerDemoServerNumber int - srv, err := drive.New(serviceAccount()) - if err != nil { - c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) - return - } // Create database transaction for inserts tx, err := database.DB.Begin() if err != nil { @@ -100,22 +91,16 @@ func CreateRecordWithDemo(c *gin.Context) { } // Defer to a rollback in case anything fails defer tx.Rollback() - for i, header := range demoFiles { + for i, header := range demoFileHeaders { uuid := uuid.New().String() // Upload & insert into demos - err = c.SaveUploadedFile(header, "parser/"+uuid+".dem") - if err != nil { - c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) - return - } - defer os.Remove("parser/" + uuid + ".dem") - f, err := os.Open("parser/" + uuid + ".dem") + f, err := header.Open() if err != nil { c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) return } defer f.Close() - parserResult, err := parser.ProcessDemo("parser/" + uuid + ".dem") + parserResult, err := parser.ProcessDemo(f) if err != nil { c.JSON(http.StatusOK, models.ErrorResponse("Error while processing demo: "+err.Error())) return @@ -148,23 +133,15 @@ func CreateRecordWithDemo(c *gin.Context) { return } } - file, err := createFile(srv, uuid+".dem", "application/octet-stream", f, os.Getenv("GOOGLE_FOLDER_ID")) - if err != nil { - c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) - return - } if i == 0 { - hostDemoFileID = file.Id hostDemoUUID = uuid hostDemoServerNumber = parserResult.ServerNumber } else if i == 1 { - partnerDemoFileID = file.Id partnerDemoUUID = uuid partnerDemoServerNumber = parserResult.ServerNumber } - _, err = tx.Exec(`INSERT INTO demos (id,location_id) VALUES ($1,$2)`, uuid, file.Id) + _, err = tx.Exec(`INSERT INTO demos (id) VALUES ($1)`, uuid) if err != nil { - deleteFile(srv, file.Id) c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) return } @@ -172,8 +149,6 @@ func CreateRecordWithDemo(c *gin.Context) { // Insert into records if isCoop { if hostDemoServerNumber != partnerDemoServerNumber { - deleteFile(srv, hostDemoFileID) - deleteFile(srv, partnerDemoFileID) c.JSON(http.StatusOK, models.ErrorResponse(fmt.Sprintf("Host and partner demo server numbers (%d & %d) does not match!", hostDemoServerNumber, partnerDemoServerNumber))) return } @@ -192,8 +167,6 @@ func CreateRecordWithDemo(c *gin.Context) { // return // } if convertedHostSteamID != user.(models.User).SteamID && convertedPartnerSteamID != user.(models.User).SteamID { - deleteFile(srv, hostDemoFileID) - deleteFile(srv, partnerDemoFileID) c.JSON(http.StatusOK, models.ErrorResponse("You are permitted to only upload your own runs!")) return } @@ -205,8 +178,6 @@ func CreateRecordWithDemo(c *gin.Context) { } database.DB.QueryRow("SELECT steam_id FROM users WHERE steam_id = $1", checkPartnerSteamID).Scan(&verifyPartnerSteamID) if verifyPartnerSteamID != checkPartnerSteamID { - deleteFile(srv, hostDemoFileID) - deleteFile(srv, partnerDemoFileID) c.JSON(http.StatusOK, models.ErrorResponse("Partner SteamID does not match an account on LPHUB.")) return } @@ -214,8 +185,6 @@ func CreateRecordWithDemo(c *gin.Context) { VALUES($1, $2, $3, $4, $5, $6, $7)` _, err := tx.Exec(sql, mapID, hostDemoScoreCount, hostDemoScoreTime, convertedHostSteamID, convertedPartnerSteamID, hostDemoUUID, partnerDemoUUID) if err != nil { - deleteFile(srv, hostDemoFileID) - deleteFile(srv, partnerDemoFileID) c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) return } @@ -224,7 +193,39 @@ func CreateRecordWithDemo(c *gin.Context) { VALUES($1, $2, $3, $4, $5)` _, err := tx.Exec(sql, mapID, hostDemoScoreCount, hostDemoScoreTime, user.(models.User).SteamID, hostDemoUUID) if err != nil { - deleteFile(srv, hostDemoFileID) + c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) + return + } + } + // Everything is good, upload the demo files. + client, err := b2.NewClient(context.Background(), os.Getenv("B2_KEY_ID"), os.Getenv("B2_API_KEY")) + if err != nil { + c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) + return + } + bucket, err := client.Bucket(context.Background(), os.Getenv("B2_BUCKET_NAME")) + if err != nil { + c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) + return + } + for i, header := range demoFileHeaders { + f, err := header.Open() + if err != nil { + c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) + return + } + defer f.Close() + var objectName string + if i == 0 { + objectName = hostDemoUUID + ".dem" + } else if i == 1 { + objectName = partnerDemoUUID + ".dem" + } + obj := bucket.Object(objectName) + writer := obj.NewWriter(context.Background()) + defer writer.Close() + _, err = io.Copy(writer, f) + if err != nil { c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) return } @@ -339,29 +340,15 @@ func DownloadDemoWithID(c *gin.Context) { c.JSON(http.StatusOK, models.ErrorResponse("Invalid id given.")) return } - srv, err := drive.New(serviceAccount()) + var checkedUUID string + err := database.DB.QueryRow("SELECT d.id FROM demos d WHERE d.id = $1", uuid).Scan(&checkedUUID) if err != nil { - c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) - return - } - - // Query drive instead of finding location id from db because SOMEONE reuploaded the demos. - // Tbf I had to reupload and will have to do time after time. Fuck you Google. - // I guess there's no need to store location id of demos anymore? - // ALSO ALSO, Google keeps track of old deleted files so sort by createdTime to get the latest demo. - fileList, err := srv.Files.List().Q(fmt.Sprintf("name = '%s.dem'", uuid)). - Fields("files(id, name, createdTime)").OrderBy("createdTime desc").PageSize(1).Do() - if err != nil { - c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) - return - } - if len(fileList.Files) == 0 { - c.JSON(http.StatusOK, models.ErrorResponse("Demo not found.")) + c.JSON(http.StatusOK, models.ErrorResponse("Given id does not match a demo.")) return } - url := "https://drive.google.com/uc?export=download&id=" + fileList.Files[0].Id fileName := uuid + ".dem" + url := os.Getenv("B2_DOWNLOAD_URL") + fileName output, err := os.Create(fileName) if err != nil { c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) @@ -389,43 +376,6 @@ func DownloadDemoWithID(c *gin.Context) { // c.FileAttachment() } -// Use Service account -func serviceAccount() *http.Client { - privateKey, _ := base64.StdEncoding.DecodeString(os.Getenv("GOOGLE_PRIVATE_KEY_BASE64")) - config := &jwt.Config{ - Email: os.Getenv("GOOGLE_CLIENT_EMAIL"), - PrivateKey: []byte(privateKey), - Scopes: []string{ - drive.DriveScope, - }, - TokenURL: google.JWTTokenURL, - } - client := config.Client(context.Background()) - return client -} - -// Create Gdrive file -func createFile(service *drive.Service, name string, mimeType string, content io.Reader, parentId string) (*drive.File, error) { - f := &drive.File{ - MimeType: mimeType, - Name: name, - Parents: []string{parentId}, - } - file, err := service.Files.Create(f).Media(content).Do() - - if err != nil { - log.Println("Could not create file: " + err.Error()) - return nil, err - } - - return file, nil -} - -// Delete Gdrive file -func deleteFile(service *drive.Service, fileId string) { - service.Files.Delete(fileId) -} - // Convert from SteamID64 to Legacy SteamID bits func convertSteamID(steamID64 int64) int64 { return (steamID64 >> 1) & 0x7FFFFFF -- cgit v1.2.3 From 00711cab5bb8f3cdd1b86d062ef067587e3fc18a Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:11:50 +0300 Subject: feat/backend: local demos path for testing (#243) Co-authored-by: NeKz --- backend/.env.example | 27 +++++++------- backend/handlers/record.go | 92 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 86 insertions(+), 33 deletions(-) (limited to 'backend/handlers') diff --git a/backend/.env.example b/backend/.env.example index 90ca8b4..b0a7101 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -1,13 +1,14 @@ -PORT= -SECRET_KEY= -API_KEY= -ENV= -DB_HOST= -DB_PORT= -DB_USER= -DB_PASS= -DB_NAME= -B2_BUCKET_NAME= -B2_KEY_ID= -B2_API_KEY= -B2_DOWNLOAD_URL= +PORT=4000 +SECRET_KEY=123456789ABCDEF +API_KEY=123456789ABCDEF +ENV=DEV +DB_HOST=localhost +DB_PORT=5432 +DB_USER=postgres +DB_PASS=postgres +DB_NAME=postgres +B2_BUCKET_NAME=lphub +B2_KEY_ID=123456789ABCDEF +B2_API_KEY=123456789ABCDEF +B2_DOWNLOAD_URL=https://lphub.s3.eu-central-001.backblazeb2.com/ +LOCAL_DEMOS_PATH=/path/to/demos/ diff --git a/backend/handlers/record.go b/backend/handlers/record.go index 91e74b9..25a6c6d 100644 --- a/backend/handlers/record.go +++ b/backend/handlers/record.go @@ -22,7 +22,6 @@ import ( type RecordRequest struct { HostDemo *multipart.FileHeader `json:"host_demo" form:"host_demo" binding:"required" swaggerignore:"true"` PartnerDemo *multipart.FileHeader `json:"partner_demo" form:"partner_demo" swaggerignore:"true"` - PartnerID string `json:"partner_id" form:"partner_id"` } type RecordResponse struct { @@ -197,6 +196,45 @@ func CreateRecordWithDemo(c *gin.Context) { return } } + if os.Getenv("ENV") == "DEV" { + if localPath := os.Getenv("LOCAL_DEMOS_PATH"); localPath != "" { + for i, header := range demoFileHeaders { + f, err := header.Open() + if err != nil { + c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) + return + } + defer f.Close() + var objectName string + if i == 0 { + objectName = hostDemoUUID + ".dem" + } else if i == 1 { + objectName = partnerDemoUUID + ".dem" + } + demo, err := os.Create(localPath + objectName) + if err != nil { + c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) + return + } + defer demo.Close() + _, err = io.Copy(demo, f) + if err != nil { + c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) + return + } + } + if err = tx.Commit(); err != nil { + c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) + return + } + c.JSON(http.StatusOK, models.Response{ + Success: true, + Message: "Successfully created record.", + Data: RecordResponse{ScoreCount: hostDemoScoreCount, ScoreTime: hostDemoScoreTime}, + }) + return + } + } // Everything is good, upload the demo files. client, err := b2.NewClient(context.Background(), os.Getenv("B2_KEY_ID"), os.Getenv("B2_API_KEY")) if err != nil { @@ -347,32 +385,46 @@ func DownloadDemoWithID(c *gin.Context) { return } - fileName := uuid + ".dem" - url := os.Getenv("B2_DOWNLOAD_URL") + fileName - output, err := os.Create(fileName) - if err != nil { - c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) - return - } - defer os.Remove(fileName) - defer output.Close() - response, err := http.Get(url) - if err != nil { - c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) - return + localPath := "" + if os.Getenv("ENV") == "DEV" { + localPath = os.Getenv("LOCAL_DEMOS_PATH") } - defer response.Body.Close() - _, err = io.Copy(output, response.Body) - if err != nil { - c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) - return + + fileName := uuid + ".dem" + if localPath == "" { + url := os.Getenv("B2_DOWNLOAD_URL") + fileName + output, err := os.Create(fileName) + if err != nil { + c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) + return + } + defer os.Remove(fileName) + defer output.Close() + response, err := http.Get(url) + if err != nil { + c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) + return + } + defer response.Body.Close() + _, err = io.Copy(output, response.Body) + if err != nil { + c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) + return + } } + // Downloaded file c.Header("Content-Description", "File Transfer") c.Header("Content-Transfer-Encoding", "binary") c.Header("Content-Disposition", "attachment; filename="+fileName) c.Header("Content-Type", "application/octet-stream") - c.File(fileName) + + if localPath == "" { + c.File(fileName) + } else { + c.File(localPath + fileName) + } + // c.FileAttachment() } -- cgit v1.2.3 From cde64e47961823613a69e7d0185f5c36eac2e896 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Sat, 21 Dec 2024 16:32:05 +0300 Subject: fix/rankings: sort overall placements for lphub (#250) --- backend/handlers/home.go | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'backend/handlers') diff --git a/backend/handlers/home.go b/backend/handlers/home.go index 714610a..5b4e246 100644 --- a/backend/handlers/home.go +++ b/backend/handlers/home.go @@ -6,6 +6,7 @@ import ( "log" "net/http" "os" + "sort" "strings" "lphub/database" @@ -106,6 +107,15 @@ func RankingsLPHUB(c *gin.Context) { } } } + // Sort the overall rankings + sort.Slice(response.Overall, func(i, j int) bool { + a := response.Overall[i] + b := response.Overall[j] + if a.TotalScore == b.TotalScore { + return a.User.SteamID < b.User.SteamID + } + return a.TotalScore < b.TotalScore + }) placement := 1 ties := 0 -- cgit v1.2.3 From 75c0117ba7c58e6fbcc17648276e8d6951e7ad0d Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Sun, 22 Dec 2024 10:57:40 +0300 Subject: fix/profile: overall completion count wrong (#252) --- backend/handlers/user.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'backend/handlers') diff --git a/backend/handlers/user.go b/backend/handlers/user.go index 53f0d06..ea31065 100644 --- a/backend/handlers/user.go +++ b/backend/handlers/user.go @@ -4,6 +4,7 @@ import ( "net/http" "os" "regexp" + "sort" "time" "lphub/database" @@ -183,6 +184,15 @@ func Profile(c *gin.Context) { } } } + // Sort the overall rankings + sort.Slice(rankingsList.Overall, func(i, j int) bool { + a := rankingsList.Overall[i] + b := rankingsList.Overall[j] + if a.TotalScore == b.TotalScore { + return a.User.SteamID < b.User.SteamID + } + return a.TotalScore < b.TotalScore + }) placement := 1 ties := 0 @@ -507,6 +517,15 @@ func FetchUser(c *gin.Context) { } } } + // Sort the overall rankings + sort.Slice(rankingsList.Overall, func(i, j int) bool { + a := rankingsList.Overall[i] + b := rankingsList.Overall[j] + if a.TotalScore == b.TotalScore { + return a.User.SteamID < b.User.SteamID + } + return a.TotalScore < b.TotalScore + }) placement := 1 ties := 0 -- cgit v1.2.3 From 21980e68693a6941bed352a46212ccb72b8b7d88 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Tue, 31 Dec 2024 15:09:56 +0300 Subject: fix/backend: turret assassin map name typo (#254) --- backend/database/insert/maps.sql | 2 +- backend/handlers/home.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'backend/handlers') diff --git a/backend/database/insert/maps.sql b/backend/database/insert/maps.sql index f0235fa..e896ba9 100644 --- a/backend/database/insert/maps.sql +++ b/backend/database/insert/maps.sql @@ -96,7 +96,7 @@ INSERT INTO maps(game_id, chapter_id, name, is_disabled, image) VALUES (2,13,'Catapult Block',false,''), (2,13,'Bridge Fling',false,''), (2,13,'Turret Walls',false,''), -(2,13,'Turret Assasin',false,''), +(2,13,'Turret Assassin',false,''), (2,13,'Bridge Testing',false,''), -- 4 (2,14,'Cooperative Funnels',false,''), diff --git a/backend/handlers/home.go b/backend/handlers/home.go index 5b4e246..095b666 100644 --- a/backend/handlers/home.go +++ b/backend/handlers/home.go @@ -327,7 +327,7 @@ func SearchWithQuery(c *gin.Context) { {ID: 80, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Catapult Block"}, {ID: 81, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Bridge Fling"}, {ID: 82, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Turret Walls"}, - {ID: 83, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Turret Assasin"}, + {ID: 83, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Turret Assassin"}, {ID: 84, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Bridge Testing"}, {ID: 85, Game: "Portal 2 - Cooperative", Chapter: "Course 4 - Excursion Funnels", Map: "Cooperative Funnels"}, {ID: 86, Game: "Portal 2 - Cooperative", Chapter: "Course 4 - Excursion Funnels", Map: "Funnel Drill"}, -- cgit v1.2.3 From 22547464f756f3620dd1d92bcd249cb7608ca04a Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:31:34 +0000 Subject: feat/backend: basic map difficulty ratings (#266) --- backend/handlers/map.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'backend/handlers') diff --git a/backend/handlers/map.go b/backend/handlers/map.go index b2a0b91..e1e6897 100644 --- a/backend/handlers/map.go +++ b/backend/handlers/map.go @@ -492,6 +492,7 @@ func FetchMaps(c *gin.Context) { m.id, m.name, m.is_disabled, + m.difficulty, m.image, cat.id, cat.name, @@ -529,7 +530,7 @@ func FetchMaps(c *gin.Context) { for rows.Next() { var mapShort models.MapSelect var categoryPortal models.CategoryPortal - if err := rows.Scan(&mapShort.ID, &mapShort.Name, &mapShort.IsDisabled, &mapShort.Image, &categoryPortal.Category.ID, &categoryPortal.Category.Name, &categoryPortal.PortalCount); err != nil { + if err := rows.Scan(&mapShort.ID, &mapShort.Name, &mapShort.IsDisabled, &mapShort.Difficulty, &mapShort.Image, &categoryPortal.Category.ID, &categoryPortal.Category.Name, &categoryPortal.PortalCount); err != nil { c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) return } @@ -571,6 +572,7 @@ func FetchChapterMaps(c *gin.Context) { m.name AS map_name, c.name AS chapter_name, m.is_disabled, + m.difficulty, m.image, cat.id, cat.name, @@ -610,7 +612,7 @@ func FetchChapterMaps(c *gin.Context) { for rows.Next() { var mapShort models.MapSelect var categoryPortal models.CategoryPortal - if err := rows.Scan(&mapShort.ID, &mapShort.Name, &chapterName, &mapShort.IsDisabled, &mapShort.Image, &categoryPortal.Category.ID, &categoryPortal.Category.Name, &categoryPortal.PortalCount); err != nil { + if err := rows.Scan(&mapShort.ID, &mapShort.Name, &chapterName, &mapShort.IsDisabled, &mapShort.Difficulty, &mapShort.Image, &categoryPortal.Category.ID, &categoryPortal.Category.Name, &categoryPortal.PortalCount); err != nil { c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) return } -- cgit v1.2.3 From d221c8a770fa0d01fc191e88b2fda8d7d0faa049 Mon Sep 17 00:00:00 2001 From: Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:53:30 +0000 Subject: feat/backend: send map difficulty in summary (#271) --- backend/handlers/map.go | 4 ++-- backend/models/models.go | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'backend/handlers') diff --git a/backend/handlers/map.go b/backend/handlers/map.go index e1e6897..9cb0bcc 100644 --- a/backend/handlers/map.go +++ b/backend/handlers/map.go @@ -77,12 +77,12 @@ func FetchMapSummary(c *gin.Context) { } // Get map data response.Map.ID = intID - sql := `SELECT m.id, g.name, c.name, m.name, m.image, g.is_coop, m.is_disabled + sql := `SELECT m.id, g.name, c.name, m.name, m.image, g.is_coop, m.is_disabled, m.difficulty FROM maps m INNER JOIN games g ON m.game_id = g.id INNER JOIN chapters c ON m.chapter_id = c.id WHERE m.id = $1` - err = database.DB.QueryRow(sql, id).Scan(&response.Map.ID, &response.Map.GameName, &response.Map.ChapterName, &response.Map.MapName, &response.Map.Image, &response.Map.IsCoop, &response.Map.IsDisabled) + err = database.DB.QueryRow(sql, id).Scan(&response.Map.ID, &response.Map.GameName, &response.Map.ChapterName, &response.Map.MapName, &response.Map.Image, &response.Map.IsCoop, &response.Map.IsDisabled, &response.Map.Difficulty) if err != nil { c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) return diff --git a/backend/models/models.go b/backend/models/models.go index a114f2c..3c38131 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -47,6 +47,7 @@ type Map struct { Image string `json:"image"` IsCoop bool `json:"is_coop"` IsDisabled bool `json:"is_disabled"` + Difficulty int `json:"difficulty"` } type MapShort struct { -- cgit v1.2.3