aboutsummaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--backend/.env.example27
-rw-r--r--backend/database/init.sql1
-rw-r--r--backend/database/insert/history.sql1
-rw-r--r--backend/database/insert/maps.sql2
-rw-r--r--backend/go.mod25
-rw-r--r--backend/go.sum124
-rw-r--r--backend/handlers/home.go12
-rw-r--r--backend/handlers/map.go10
-rw-r--r--backend/handlers/record.go222
-rw-r--r--backend/handlers/user.go19
-rw-r--r--backend/main.go11
-rw-r--r--backend/models/models.go1
-rw-r--r--backend/parser/parser.go10
13 files changed, 196 insertions, 269 deletions
diff --git a/backend/.env.example b/backend/.env.example
index 774f2a2..0318aa1 100644
--- a/backend/.env.example
+++ b/backend/.env.example
@@ -1,12 +1,15 @@
1PORT= 1PORT=4000
2SECRET_KEY= 2SECRET_KEY=123456789ABCDEF
3API_KEY= 3API_KEY=123456789ABCDEF
4ENV= 4ENV=DEV
5DB_HOST= 5DB_HOST=localhost
6DB_PORT= 6DB_PORT=5432
7DB_USER= 7DB_USER=postgres
8DB_PASS= 8DB_PASS=postgres
9DB_NAME= 9DB_NAME=postgres
10GOOGLE_CLIENT_EMAIL= 10B2_BUCKET_NAME=lphub
11GOOGLE_PRIVATE_KEY_BASE64= 11B2_KEY_ID=123456789ABCDEF
12GOOGLE_FOLDER_ID= \ No newline at end of file 12B2_API_KEY=123456789ABCDEF
13B2_DOWNLOAD_URL=https://lphub.s3.eu-central-001.backblazeb2.com/
14LOCAL_DEMOS_PATH=/path/to/demos/
15NEWRELIC_LICENSE_KEY=abcdef123456789
diff --git a/backend/database/init.sql b/backend/database/init.sql
index e238eae..d49e519 100644
--- a/backend/database/init.sql
+++ b/backend/database/init.sql
@@ -137,7 +137,6 @@ CREATE TABLE map_discussions_upvotes (
137 137
138CREATE TABLE demos ( 138CREATE TABLE demos (
139 id UUID, 139 id UUID,
140 location_id TEXT NOT NULL,
141 PRIMARY KEY (id) 140 PRIMARY KEY (id)
142); 141);
143 142
diff --git a/backend/database/insert/history.sql b/backend/database/insert/history.sql
index 34fddcb..6b4922a 100644
--- a/backend/database/insert/history.sql
+++ b/backend/database/insert/history.sql
@@ -547,6 +547,7 @@ INSERT INTO map_history(map_id,category_id,user_name,score_count,record_date) VA
547(57,1,'Krank',5,'2012-07-29'), 547(57,1,'Krank',5,'2012-07-29'),
548(57,1,'Krzyhau',0,'2017-10-29'), 548(57,1,'Krzyhau',0,'2017-10-29'),
549(58,1,'Stimich',2,'2011-10-11'), 549(58,1,'Stimich',2,'2011-10-11'),
550(58,1,'Isenstige',0,'2025-01-24'),
550(59,1,'Isimmo',7,'2011-11-04'), 551(59,1,'Isimmo',7,'2011-11-04'),
551(59,1,'sicklebrick',6,'2013-03-20'), 552(59,1,'sicklebrick',6,'2013-03-20'),
552(60,1,'CalmlyFrenetic',7,'2011-10-19'), 553(60,1,'CalmlyFrenetic',7,'2011-10-19'),
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
96(2,13,'Catapult Block',false,''), 96(2,13,'Catapult Block',false,''),
97(2,13,'Bridge Fling',false,''), 97(2,13,'Bridge Fling',false,''),
98(2,13,'Turret Walls',false,''), 98(2,13,'Turret Walls',false,''),
99(2,13,'Turret Assasin',false,''), 99(2,13,'Turret Assassin',false,''),
100(2,13,'Bridge Testing',false,''), 100(2,13,'Bridge Testing',false,''),
101-- 4 101-- 4
102(2,14,'Cooperative Funnels',false,''), 102(2,14,'Cooperative Funnels',false,''),
diff --git a/backend/go.mod b/backend/go.mod
index 17308f3..f9fe0db 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -8,6 +8,7 @@ require (
8) 8)
9 9
10require ( 10require (
11 github.com/Backblaze/blazer v0.7.1
11 github.com/golang-jwt/jwt/v4 v4.5.0 12 github.com/golang-jwt/jwt/v4 v4.5.0
12 github.com/google/uuid v1.6.0 13 github.com/google/uuid v1.6.0
13 github.com/pektezol/steam_go v1.1.2 14 github.com/pektezol/steam_go v1.1.2
@@ -18,40 +19,26 @@ require (
18) 19)
19 20
20require ( 21require (
21 cloud.google.com/go/auth v0.9.3 // indirect
22 cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
23 cloud.google.com/go/compute/metadata v0.5.0 // indirect
24 github.com/KyleBanks/depth v1.2.1 // indirect 22 github.com/KyleBanks/depth v1.2.1 // indirect
25 github.com/bytedance/sonic v1.12.2 // indirect 23 github.com/bytedance/sonic v1.12.2 // indirect
26 github.com/bytedance/sonic/loader v0.2.0 // indirect 24 github.com/bytedance/sonic/loader v0.2.0 // indirect
27 github.com/cloudwego/base64x v0.1.4 // indirect 25 github.com/cloudwego/base64x v0.1.4 // indirect
28 github.com/cloudwego/iasm v0.2.0 // indirect 26 github.com/cloudwego/iasm v0.2.0 // indirect
29 github.com/felixge/httpsnoop v1.0.4 // indirect
30 github.com/gabriel-vasile/mimetype v1.4.5 // indirect 27 github.com/gabriel-vasile/mimetype v1.4.5 // indirect
31 github.com/go-logr/logr v1.4.2 // indirect
32 github.com/go-logr/stdr v1.2.2 // indirect
33 github.com/go-openapi/jsonpointer v0.21.0 // indirect 28 github.com/go-openapi/jsonpointer v0.21.0 // indirect
34 github.com/go-openapi/jsonreference v0.21.0 // indirect 29 github.com/go-openapi/jsonreference v0.21.0 // indirect
35 github.com/go-openapi/spec v0.21.0 // indirect 30 github.com/go-openapi/spec v0.21.0 // indirect
36 github.com/go-openapi/swag v0.23.0 // indirect 31 github.com/go-openapi/swag v0.23.0 // indirect
37 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
38 github.com/google/s2a-go v0.1.8 // indirect
39 github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
40 github.com/googleapis/gax-go/v2 v2.13.0 // indirect
41 github.com/josharian/intern v1.0.0 // indirect 32 github.com/josharian/intern v1.0.0 // indirect
42 github.com/klauspost/cpuid/v2 v2.2.8 // indirect 33 github.com/klauspost/cpuid/v2 v2.2.8 // indirect
43 github.com/mailru/easyjson v0.7.7 // indirect 34 github.com/mailru/easyjson v0.7.7 // indirect
35 github.com/newrelic/go-agent/v3 v3.40.1 // indirect
36 github.com/newrelic/go-agent/v3/integrations/nrgin v1.4.1 // indirect
44 github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 37 github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
45 go.opencensus.io v0.24.0 // indirect
46 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
47 go.opentelemetry.io/otel v1.29.0 // indirect
48 go.opentelemetry.io/otel/metric v1.29.0 // indirect
49 go.opentelemetry.io/otel/trace v1.29.0 // indirect
50 golang.org/x/arch v0.10.0 // indirect 38 golang.org/x/arch v0.10.0 // indirect
51 golang.org/x/tools v0.25.0 // indirect 39 golang.org/x/tools v0.25.0 // indirect
52 google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect 40 google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
53 google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect 41 google.golang.org/grpc v1.65.0 // indirect
54 google.golang.org/grpc v1.66.0 // indirect
55 gopkg.in/yaml.v3 v3.0.1 // indirect 42 gopkg.in/yaml.v3 v3.0.1 // indirect
56) 43)
57 44
@@ -72,9 +59,7 @@ require (
72 github.com/ugorji/go/codec v1.2.12 // indirect 59 github.com/ugorji/go/codec v1.2.12 // indirect
73 golang.org/x/crypto v0.27.0 // indirect 60 golang.org/x/crypto v0.27.0 // indirect
74 golang.org/x/net v0.29.0 // indirect 61 golang.org/x/net v0.29.0 // indirect
75 golang.org/x/oauth2 v0.23.0
76 golang.org/x/sys v0.25.0 // indirect 62 golang.org/x/sys v0.25.0 // indirect
77 golang.org/x/text v0.18.0 // indirect 63 golang.org/x/text v0.18.0 // indirect
78 google.golang.org/api v0.196.0
79 google.golang.org/protobuf v1.34.2 // indirect 64 google.golang.org/protobuf v1.34.2 // indirect
80) 65)
diff --git a/backend/go.sum b/backend/go.sum
index 647f6f9..f655023 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -1,11 +1,5 @@
1cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 1github.com/Backblaze/blazer v0.7.1 h1:J43PbFj6hXLg1jvCNr+rQoAsxzKK0IP7ftl1ReCwpcQ=
2cloud.google.com/go/auth v0.9.3 h1:VOEUIAADkkLtyfr3BLa3R8Ed/j6w1jTBmARx+wb5w5U= 2github.com/Backblaze/blazer v0.7.1/go.mod h1:MhntL1nMpIuoqrPP6TnZu/xTydMgOAe/Xm6KongbjKs=
3cloud.google.com/go/auth v0.9.3/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk=
4cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
5cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
6cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
7cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
8github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
9github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= 3github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
10github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= 4github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
11github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg= 5github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg=
@@ -13,22 +7,13 @@ github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKz
13github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 7github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
14github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= 8github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
15github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 9github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
16github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
17github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
18github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= 10github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
19github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= 11github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
20github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= 12github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
21github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= 13github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
22github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
23github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
24github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 15github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
25github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 16github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
26github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
27github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
28github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
29github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
30github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
31github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
32github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= 17github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
33github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= 18github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
34github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= 19github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
@@ -37,11 +22,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
37github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 22github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
38github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= 23github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
39github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= 24github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
40github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
41github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
42github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
43github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
44github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
45github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= 25github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
46github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= 26github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
47github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= 27github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
@@ -62,38 +42,11 @@ github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
62github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 42github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
63github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= 43github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
64github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 44github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
65github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
66github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
67github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
68github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
69github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
70github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
71github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
72github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
73github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
74github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
75github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
76github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
77github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
78github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
79github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
80github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
81github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
82github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
83github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
84github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
85github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 45github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
86github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 46github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
87github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 47github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
88github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
89github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
90github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
91github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 48github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
92github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 49github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
93github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
94github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
95github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
96github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
97github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 50github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
98github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 51github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
99github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 52github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -121,6 +74,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
121github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 74github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
122github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 75github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
123github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 76github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
77github.com/newrelic/go-agent/v3 v3.40.1 h1:8nb4R252Fpuc3oySvlHpDwqySqaPWL5nf7ZVEhqtUeA=
78github.com/newrelic/go-agent/v3 v3.40.1/go.mod h1:X0TLXDo+ttefTIue1V96Y5seb8H6wqf6uUq4UpPsYj8=
79github.com/newrelic/go-agent/v3/integrations/nrgin v1.4.1 h1:a1waTQToxDTKd31LpwpaFHKWPj8Dav/BrzZayBiiAq8=
80github.com/newrelic/go-agent/v3/integrations/nrgin v1.4.1/go.mod h1:mEbfsZIxBYIPT7FzboYvE+ed2ft4SCFXoCvleI2v5JQ=
124github.com/pektezol/bitreader v1.4.3 h1:+WjsD6qOAaI6Q1jOOlEJcnaEso8vPMKRZnnaDnZhTSg= 81github.com/pektezol/bitreader v1.4.3 h1:+WjsD6qOAaI6Q1jOOlEJcnaEso8vPMKRZnnaDnZhTSg=
125github.com/pektezol/bitreader v1.4.3/go.mod h1:xBQEsQpOf8B5yPrnOTkirZGyVUV6Bqp0ups2RIlTskk= 82github.com/pektezol/bitreader v1.4.3/go.mod h1:xBQEsQpOf8B5yPrnOTkirZGyVUV6Bqp0ups2RIlTskk=
126github.com/pektezol/steam_go v1.1.2 h1:fta6SW+La8NfmCtR/Kn73bAmTBvCgUkkLCplsJGzx7g= 83github.com/pektezol/steam_go v1.1.2 h1:fta6SW+La8NfmCtR/Kn73bAmTBvCgUkkLCplsJGzx7g=
@@ -129,7 +86,6 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH
129github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= 86github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
130github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 87github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
131github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 88github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
132github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
133github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= 89github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
134github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= 90github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
135github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 91github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -153,55 +109,26 @@ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2
153github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 109github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
154github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 110github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
155github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 111github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
156go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
157go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
158go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
159go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
160go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
161go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
162go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
163go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
164go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
165go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
166golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= 112golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8=
167golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 113golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
168golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 114golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
169golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
170golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 115golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
171golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= 116golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
172golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= 117golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
173golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
174golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
175golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
176golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
177golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 118golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
178golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= 119golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
179golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 120golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
180golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
181golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
182golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
183golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
184golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
185golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 121golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
186golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
187golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 122golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
188golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 123golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
189golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 124golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
190golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= 125golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
191golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= 126golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
192golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
193golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
194golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
195golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
196golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
197golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 127golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
198golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 128golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
199golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= 129golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
200golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 130golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
201golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
202golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 131golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
203golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
204golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
205golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 132golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
206golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 133golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
207golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 134golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -222,43 +149,16 @@ golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
222golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= 149golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
223golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 150golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
224golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 151golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
225golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
226golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
227golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
228golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
229golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 152golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
230golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 153golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
231golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= 154golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
232golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= 155golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
233golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 156golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
234golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 157google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0=
235google.golang.org/api v0.196.0 h1:k/RafYqebaIJBO3+SMnfEGtFVlvp5vSgqTUF54UN/zg= 158google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8=
236google.golang.org/api v0.196.0/go.mod h1:g9IL21uGkYgvQ5BZg6BAtoGJQIm8r6EgaAbpNey5wBE= 159google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
237google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 160google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
238google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 161google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
239google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
240google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
241google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
242google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0=
243google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
244google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
245google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
246google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
247google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
248google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
249google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
250google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
251google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
252google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
253google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
254google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
255google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
256google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
257google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
258google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
259google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
260google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
261google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
262google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= 162google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
263google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 163google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
264gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 164gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -267,6 +167,4 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
267gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 167gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
268gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 168gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
269gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 169gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
270honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
271honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
272nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 170nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
diff --git a/backend/handlers/home.go b/backend/handlers/home.go
index 714610a..095b666 100644
--- a/backend/handlers/home.go
+++ b/backend/handlers/home.go
@@ -6,6 +6,7 @@ import (
6 "log" 6 "log"
7 "net/http" 7 "net/http"
8 "os" 8 "os"
9 "sort"
9 "strings" 10 "strings"
10 11
11 "lphub/database" 12 "lphub/database"
@@ -106,6 +107,15 @@ func RankingsLPHUB(c *gin.Context) {
106 } 107 }
107 } 108 }
108 } 109 }
110 // Sort the overall rankings
111 sort.Slice(response.Overall, func(i, j int) bool {
112 a := response.Overall[i]
113 b := response.Overall[j]
114 if a.TotalScore == b.TotalScore {
115 return a.User.SteamID < b.User.SteamID
116 }
117 return a.TotalScore < b.TotalScore
118 })
109 119
110 placement := 1 120 placement := 1
111 ties := 0 121 ties := 0
@@ -317,7 +327,7 @@ func SearchWithQuery(c *gin.Context) {
317 {ID: 80, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Catapult Block"}, 327 {ID: 80, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Catapult Block"},
318 {ID: 81, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Bridge Fling"}, 328 {ID: 81, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Bridge Fling"},
319 {ID: 82, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Turret Walls"}, 329 {ID: 82, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Turret Walls"},
320 {ID: 83, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Turret Assasin"}, 330 {ID: 83, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Turret Assassin"},
321 {ID: 84, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Bridge Testing"}, 331 {ID: 84, Game: "Portal 2 - Cooperative", Chapter: "Course 3 - Hard-Light Surfaces", Map: "Bridge Testing"},
322 {ID: 85, Game: "Portal 2 - Cooperative", Chapter: "Course 4 - Excursion Funnels", Map: "Cooperative Funnels"}, 332 {ID: 85, Game: "Portal 2 - Cooperative", Chapter: "Course 4 - Excursion Funnels", Map: "Cooperative Funnels"},
323 {ID: 86, Game: "Portal 2 - Cooperative", Chapter: "Course 4 - Excursion Funnels", Map: "Funnel Drill"}, 333 {ID: 86, Game: "Portal 2 - Cooperative", Chapter: "Course 4 - Excursion Funnels", Map: "Funnel Drill"},
diff --git a/backend/handlers/map.go b/backend/handlers/map.go
index b2a0b91..9cb0bcc 100644
--- a/backend/handlers/map.go
+++ b/backend/handlers/map.go
@@ -77,12 +77,12 @@ func FetchMapSummary(c *gin.Context) {
77 } 77 }
78 // Get map data 78 // Get map data
79 response.Map.ID = intID 79 response.Map.ID = intID
80 sql := `SELECT m.id, g.name, c.name, m.name, m.image, g.is_coop, m.is_disabled 80 sql := `SELECT m.id, g.name, c.name, m.name, m.image, g.is_coop, m.is_disabled, m.difficulty
81 FROM maps m 81 FROM maps m
82 INNER JOIN games g ON m.game_id = g.id 82 INNER JOIN games g ON m.game_id = g.id
83 INNER JOIN chapters c ON m.chapter_id = c.id 83 INNER JOIN chapters c ON m.chapter_id = c.id
84 WHERE m.id = $1` 84 WHERE m.id = $1`
85 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) 85 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)
86 if err != nil { 86 if err != nil {
87 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 87 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
88 return 88 return
@@ -492,6 +492,7 @@ func FetchMaps(c *gin.Context) {
492 m.id, 492 m.id,
493 m.name, 493 m.name,
494 m.is_disabled, 494 m.is_disabled,
495 m.difficulty,
495 m.image, 496 m.image,
496 cat.id, 497 cat.id,
497 cat.name, 498 cat.name,
@@ -529,7 +530,7 @@ func FetchMaps(c *gin.Context) {
529 for rows.Next() { 530 for rows.Next() {
530 var mapShort models.MapSelect 531 var mapShort models.MapSelect
531 var categoryPortal models.CategoryPortal 532 var categoryPortal models.CategoryPortal
532 if err := rows.Scan(&mapShort.ID, &mapShort.Name, &mapShort.IsDisabled, &mapShort.Image, &categoryPortal.Category.ID, &categoryPortal.Category.Name, &categoryPortal.PortalCount); err != nil { 533 if err := rows.Scan(&mapShort.ID, &mapShort.Name, &mapShort.IsDisabled, &mapShort.Difficulty, &mapShort.Image, &categoryPortal.Category.ID, &categoryPortal.Category.Name, &categoryPortal.PortalCount); err != nil {
533 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 534 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
534 return 535 return
535 } 536 }
@@ -571,6 +572,7 @@ func FetchChapterMaps(c *gin.Context) {
571 m.name AS map_name, 572 m.name AS map_name,
572 c.name AS chapter_name, 573 c.name AS chapter_name,
573 m.is_disabled, 574 m.is_disabled,
575 m.difficulty,
574 m.image, 576 m.image,
575 cat.id, 577 cat.id,
576 cat.name, 578 cat.name,
@@ -610,7 +612,7 @@ func FetchChapterMaps(c *gin.Context) {
610 for rows.Next() { 612 for rows.Next() {
611 var mapShort models.MapSelect 613 var mapShort models.MapSelect
612 var categoryPortal models.CategoryPortal 614 var categoryPortal models.CategoryPortal
613 if err := rows.Scan(&mapShort.ID, &mapShort.Name, &chapterName, &mapShort.IsDisabled, &mapShort.Image, &categoryPortal.Category.ID, &categoryPortal.Category.Name, &categoryPortal.PortalCount); err != nil { 615 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 {
614 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 616 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
615 return 617 return
616 } 618 }
diff --git a/backend/handlers/record.go b/backend/handlers/record.go
index bedde57..25a6c6d 100644
--- a/backend/handlers/record.go
+++ b/backend/handlers/record.go
@@ -2,10 +2,8 @@ package handlers
2 2
3import ( 3import (
4 "context" 4 "context"
5 "encoding/base64"
6 "fmt" 5 "fmt"
7 "io" 6 "io"
8 "log"
9 "mime/multipart" 7 "mime/multipart"
10 "net/http" 8 "net/http"
11 "os" 9 "os"
@@ -16,17 +14,14 @@ import (
16 "lphub/models" 14 "lphub/models"
17 "lphub/parser" 15 "lphub/parser"
18 16
17 "github.com/Backblaze/blazer/b2"
19 "github.com/gin-gonic/gin" 18 "github.com/gin-gonic/gin"
20 "github.com/google/uuid" 19 "github.com/google/uuid"
21 "golang.org/x/oauth2/google"
22 "golang.org/x/oauth2/jwt"
23 "google.golang.org/api/drive/v3"
24) 20)
25 21
26type RecordRequest struct { 22type RecordRequest struct {
27 HostDemo *multipart.FileHeader `json:"host_demo" form:"host_demo" binding:"required" swaggerignore:"true"` 23 HostDemo *multipart.FileHeader `json:"host_demo" form:"host_demo" binding:"required" swaggerignore:"true"`
28 PartnerDemo *multipart.FileHeader `json:"partner_demo" form:"partner_demo" swaggerignore:"true"` 24 PartnerDemo *multipart.FileHeader `json:"partner_demo" form:"partner_demo" swaggerignore:"true"`
29 PartnerID string `json:"partner_id" form:"partner_id"`
30} 25}
31 26
32type RecordResponse struct { 27type RecordResponse struct {
@@ -79,19 +74,14 @@ func CreateRecordWithDemo(c *gin.Context) {
79 return 74 return
80 } 75 }
81 // Demo files 76 // Demo files
82 demoFiles := []*multipart.FileHeader{record.HostDemo} 77 demoFileHeaders := []*multipart.FileHeader{record.HostDemo}
83 if isCoop { 78 if isCoop {
84 demoFiles = append(demoFiles, record.PartnerDemo) 79 demoFileHeaders = append(demoFileHeaders, record.PartnerDemo)
85 } 80 }
86 var hostDemoUUID, hostDemoFileID, partnerDemoUUID, partnerDemoFileID string 81 var hostDemoUUID, partnerDemoUUID string
87 var hostDemoScoreCount, hostDemoScoreTime int 82 var hostDemoScoreCount, hostDemoScoreTime int
88 var hostSteamID, partnerSteamID string 83 var hostSteamID, partnerSteamID string
89 var hostDemoServerNumber, partnerDemoServerNumber int 84 var hostDemoServerNumber, partnerDemoServerNumber int
90 srv, err := drive.New(serviceAccount())
91 if err != nil {
92 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
93 return
94 }
95 // Create database transaction for inserts 85 // Create database transaction for inserts
96 tx, err := database.DB.Begin() 86 tx, err := database.DB.Begin()
97 if err != nil { 87 if err != nil {
@@ -100,22 +90,16 @@ func CreateRecordWithDemo(c *gin.Context) {
100 } 90 }
101 // Defer to a rollback in case anything fails 91 // Defer to a rollback in case anything fails
102 defer tx.Rollback() 92 defer tx.Rollback()
103 for i, header := range demoFiles { 93 for i, header := range demoFileHeaders {
104 uuid := uuid.New().String() 94 uuid := uuid.New().String()
105 // Upload & insert into demos 95 // Upload & insert into demos
106 err = c.SaveUploadedFile(header, "parser/"+uuid+".dem") 96 f, err := header.Open()
107 if err != nil {
108 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
109 return
110 }
111 defer os.Remove("parser/" + uuid + ".dem")
112 f, err := os.Open("parser/" + uuid + ".dem")
113 if err != nil { 97 if err != nil {
114 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 98 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
115 return 99 return
116 } 100 }
117 defer f.Close() 101 defer f.Close()
118 parserResult, err := parser.ProcessDemo("parser/" + uuid + ".dem") 102 parserResult, err := parser.ProcessDemo(f)
119 if err != nil { 103 if err != nil {
120 c.JSON(http.StatusOK, models.ErrorResponse("Error while processing demo: "+err.Error())) 104 c.JSON(http.StatusOK, models.ErrorResponse("Error while processing demo: "+err.Error()))
121 return 105 return
@@ -148,23 +132,15 @@ func CreateRecordWithDemo(c *gin.Context) {
148 return 132 return
149 } 133 }
150 } 134 }
151 file, err := createFile(srv, uuid+".dem", "application/octet-stream", f, os.Getenv("GOOGLE_FOLDER_ID"))
152 if err != nil {
153 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
154 return
155 }
156 if i == 0 { 135 if i == 0 {
157 hostDemoFileID = file.Id
158 hostDemoUUID = uuid 136 hostDemoUUID = uuid
159 hostDemoServerNumber = parserResult.ServerNumber 137 hostDemoServerNumber = parserResult.ServerNumber
160 } else if i == 1 { 138 } else if i == 1 {
161 partnerDemoFileID = file.Id
162 partnerDemoUUID = uuid 139 partnerDemoUUID = uuid
163 partnerDemoServerNumber = parserResult.ServerNumber 140 partnerDemoServerNumber = parserResult.ServerNumber
164 } 141 }
165 _, err = tx.Exec(`INSERT INTO demos (id,location_id) VALUES ($1,$2)`, uuid, file.Id) 142 _, err = tx.Exec(`INSERT INTO demos (id) VALUES ($1)`, uuid)
166 if err != nil { 143 if err != nil {
167 deleteFile(srv, file.Id)
168 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 144 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
169 return 145 return
170 } 146 }
@@ -172,8 +148,6 @@ func CreateRecordWithDemo(c *gin.Context) {
172 // Insert into records 148 // Insert into records
173 if isCoop { 149 if isCoop {
174 if hostDemoServerNumber != partnerDemoServerNumber { 150 if hostDemoServerNumber != partnerDemoServerNumber {
175 deleteFile(srv, hostDemoFileID)
176 deleteFile(srv, partnerDemoFileID)
177 c.JSON(http.StatusOK, models.ErrorResponse(fmt.Sprintf("Host and partner demo server numbers (%d & %d) does not match!", hostDemoServerNumber, partnerDemoServerNumber))) 151 c.JSON(http.StatusOK, models.ErrorResponse(fmt.Sprintf("Host and partner demo server numbers (%d & %d) does not match!", hostDemoServerNumber, partnerDemoServerNumber)))
178 return 152 return
179 } 153 }
@@ -192,8 +166,6 @@ func CreateRecordWithDemo(c *gin.Context) {
192 // return 166 // return
193 // } 167 // }
194 if convertedHostSteamID != user.(models.User).SteamID && convertedPartnerSteamID != user.(models.User).SteamID { 168 if convertedHostSteamID != user.(models.User).SteamID && convertedPartnerSteamID != user.(models.User).SteamID {
195 deleteFile(srv, hostDemoFileID)
196 deleteFile(srv, partnerDemoFileID)
197 c.JSON(http.StatusOK, models.ErrorResponse("You are permitted to only upload your own runs!")) 169 c.JSON(http.StatusOK, models.ErrorResponse("You are permitted to only upload your own runs!"))
198 return 170 return
199 } 171 }
@@ -205,8 +177,6 @@ func CreateRecordWithDemo(c *gin.Context) {
205 } 177 }
206 database.DB.QueryRow("SELECT steam_id FROM users WHERE steam_id = $1", checkPartnerSteamID).Scan(&verifyPartnerSteamID) 178 database.DB.QueryRow("SELECT steam_id FROM users WHERE steam_id = $1", checkPartnerSteamID).Scan(&verifyPartnerSteamID)
207 if verifyPartnerSteamID != checkPartnerSteamID { 179 if verifyPartnerSteamID != checkPartnerSteamID {
208 deleteFile(srv, hostDemoFileID)
209 deleteFile(srv, partnerDemoFileID)
210 c.JSON(http.StatusOK, models.ErrorResponse("Partner SteamID does not match an account on LPHUB.")) 180 c.JSON(http.StatusOK, models.ErrorResponse("Partner SteamID does not match an account on LPHUB."))
211 return 181 return
212 } 182 }
@@ -214,8 +184,6 @@ func CreateRecordWithDemo(c *gin.Context) {
214 VALUES($1, $2, $3, $4, $5, $6, $7)` 184 VALUES($1, $2, $3, $4, $5, $6, $7)`
215 _, err := tx.Exec(sql, mapID, hostDemoScoreCount, hostDemoScoreTime, convertedHostSteamID, convertedPartnerSteamID, hostDemoUUID, partnerDemoUUID) 185 _, err := tx.Exec(sql, mapID, hostDemoScoreCount, hostDemoScoreTime, convertedHostSteamID, convertedPartnerSteamID, hostDemoUUID, partnerDemoUUID)
216 if err != nil { 186 if err != nil {
217 deleteFile(srv, hostDemoFileID)
218 deleteFile(srv, partnerDemoFileID)
219 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 187 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
220 return 188 return
221 } 189 }
@@ -224,7 +192,78 @@ func CreateRecordWithDemo(c *gin.Context) {
224 VALUES($1, $2, $3, $4, $5)` 192 VALUES($1, $2, $3, $4, $5)`
225 _, err := tx.Exec(sql, mapID, hostDemoScoreCount, hostDemoScoreTime, user.(models.User).SteamID, hostDemoUUID) 193 _, err := tx.Exec(sql, mapID, hostDemoScoreCount, hostDemoScoreTime, user.(models.User).SteamID, hostDemoUUID)
226 if err != nil { 194 if err != nil {
227 deleteFile(srv, hostDemoFileID) 195 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
196 return
197 }
198 }
199 if os.Getenv("ENV") == "DEV" {
200 if localPath := os.Getenv("LOCAL_DEMOS_PATH"); localPath != "" {
201 for i, header := range demoFileHeaders {
202 f, err := header.Open()
203 if err != nil {
204 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
205 return
206 }
207 defer f.Close()
208 var objectName string
209 if i == 0 {
210 objectName = hostDemoUUID + ".dem"
211 } else if i == 1 {
212 objectName = partnerDemoUUID + ".dem"
213 }
214 demo, err := os.Create(localPath + objectName)
215 if err != nil {
216 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
217 return
218 }
219 defer demo.Close()
220 _, err = io.Copy(demo, f)
221 if err != nil {
222 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
223 return
224 }
225 }
226 if err = tx.Commit(); err != nil {
227 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
228 return
229 }
230 c.JSON(http.StatusOK, models.Response{
231 Success: true,
232 Message: "Successfully created record.",
233 Data: RecordResponse{ScoreCount: hostDemoScoreCount, ScoreTime: hostDemoScoreTime},
234 })
235 return
236 }
237 }
238 // Everything is good, upload the demo files.
239 client, err := b2.NewClient(context.Background(), os.Getenv("B2_KEY_ID"), os.Getenv("B2_API_KEY"))
240 if err != nil {
241 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
242 return
243 }
244 bucket, err := client.Bucket(context.Background(), os.Getenv("B2_BUCKET_NAME"))
245 if err != nil {
246 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
247 return
248 }
249 for i, header := range demoFileHeaders {
250 f, err := header.Open()
251 if err != nil {
252 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
253 return
254 }
255 defer f.Close()
256 var objectName string
257 if i == 0 {
258 objectName = hostDemoUUID + ".dem"
259 } else if i == 1 {
260 objectName = partnerDemoUUID + ".dem"
261 }
262 obj := bucket.Object(objectName)
263 writer := obj.NewWriter(context.Background())
264 defer writer.Close()
265 _, err = io.Copy(writer, f)
266 if err != nil {
228 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 267 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
229 return 268 return
230 } 269 }
@@ -339,91 +378,54 @@ func DownloadDemoWithID(c *gin.Context) {
339 c.JSON(http.StatusOK, models.ErrorResponse("Invalid id given.")) 378 c.JSON(http.StatusOK, models.ErrorResponse("Invalid id given."))
340 return 379 return
341 } 380 }
342 srv, err := drive.New(serviceAccount()) 381 var checkedUUID string
382 err := database.DB.QueryRow("SELECT d.id FROM demos d WHERE d.id = $1", uuid).Scan(&checkedUUID)
343 if err != nil { 383 if err != nil {
344 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 384 c.JSON(http.StatusOK, models.ErrorResponse("Given id does not match a demo."))
345 return 385 return
346 } 386 }
347 387
348 // Query drive instead of finding location id from db because SOMEONE reuploaded the demos. 388 localPath := ""
349 // Tbf I had to reupload and will have to do time after time. Fuck you Google. 389 if os.Getenv("ENV") == "DEV" {
350 // I guess there's no need to store location id of demos anymore? 390 localPath = os.Getenv("LOCAL_DEMOS_PATH")
351 // ALSO ALSO, Google keeps track of old deleted files so sort by createdTime to get the latest demo.
352 fileList, err := srv.Files.List().Q(fmt.Sprintf("name = '%s.dem'", uuid)).
353 Fields("files(id, name, createdTime)").OrderBy("createdTime desc").PageSize(1).Do()
354 if err != nil {
355 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
356 return
357 }
358 if len(fileList.Files) == 0 {
359 c.JSON(http.StatusOK, models.ErrorResponse("Demo not found."))
360 return
361 } 391 }
362 392
363 url := "https://drive.google.com/uc?export=download&id=" + fileList.Files[0].Id
364 fileName := uuid + ".dem" 393 fileName := uuid + ".dem"
365 output, err := os.Create(fileName) 394 if localPath == "" {
366 if err != nil { 395 url := os.Getenv("B2_DOWNLOAD_URL") + fileName
367 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 396 output, err := os.Create(fileName)
368 return 397 if err != nil {
369 } 398 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
370 defer os.Remove(fileName) 399 return
371 defer output.Close() 400 }
372 response, err := http.Get(url) 401 defer os.Remove(fileName)
373 if err != nil { 402 defer output.Close()
374 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 403 response, err := http.Get(url)
375 return 404 if err != nil {
376 } 405 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
377 defer response.Body.Close() 406 return
378 _, err = io.Copy(output, response.Body) 407 }
379 if err != nil { 408 defer response.Body.Close()
380 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 409 _, err = io.Copy(output, response.Body)
381 return 410 if err != nil {
411 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
412 return
413 }
382 } 414 }
415
383 // Downloaded file 416 // Downloaded file
384 c.Header("Content-Description", "File Transfer") 417 c.Header("Content-Description", "File Transfer")
385 c.Header("Content-Transfer-Encoding", "binary") 418 c.Header("Content-Transfer-Encoding", "binary")
386 c.Header("Content-Disposition", "attachment; filename="+fileName) 419 c.Header("Content-Disposition", "attachment; filename="+fileName)
387 c.Header("Content-Type", "application/octet-stream") 420 c.Header("Content-Type", "application/octet-stream")
388 c.File(fileName)
389 // c.FileAttachment()
390}
391 421
392// Use Service account 422 if localPath == "" {
393func serviceAccount() *http.Client { 423 c.File(fileName)
394 privateKey, _ := base64.StdEncoding.DecodeString(os.Getenv("GOOGLE_PRIVATE_KEY_BASE64")) 424 } else {
395 config := &jwt.Config{ 425 c.File(localPath + fileName)
396 Email: os.Getenv("GOOGLE_CLIENT_EMAIL"),
397 PrivateKey: []byte(privateKey),
398 Scopes: []string{
399 drive.DriveScope,
400 },
401 TokenURL: google.JWTTokenURL,
402 }
403 client := config.Client(context.Background())
404 return client
405}
406
407// Create Gdrive file
408func createFile(service *drive.Service, name string, mimeType string, content io.Reader, parentId string) (*drive.File, error) {
409 f := &drive.File{
410 MimeType: mimeType,
411 Name: name,
412 Parents: []string{parentId},
413 }
414 file, err := service.Files.Create(f).Media(content).Do()
415
416 if err != nil {
417 log.Println("Could not create file: " + err.Error())
418 return nil, err
419 } 426 }
420 427
421 return file, nil 428 // c.FileAttachment()
422}
423
424// Delete Gdrive file
425func deleteFile(service *drive.Service, fileId string) {
426 service.Files.Delete(fileId)
427} 429}
428 430
429// Convert from SteamID64 to Legacy SteamID bits 431// Convert from SteamID64 to Legacy SteamID bits
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 (
4 "net/http" 4 "net/http"
5 "os" 5 "os"
6 "regexp" 6 "regexp"
7 "sort"
7 "time" 8 "time"
8 9
9 "lphub/database" 10 "lphub/database"
@@ -183,6 +184,15 @@ func Profile(c *gin.Context) {
183 } 184 }
184 } 185 }
185 } 186 }
187 // Sort the overall rankings
188 sort.Slice(rankingsList.Overall, func(i, j int) bool {
189 a := rankingsList.Overall[i]
190 b := rankingsList.Overall[j]
191 if a.TotalScore == b.TotalScore {
192 return a.User.SteamID < b.User.SteamID
193 }
194 return a.TotalScore < b.TotalScore
195 })
186 196
187 placement := 1 197 placement := 1
188 ties := 0 198 ties := 0
@@ -507,6 +517,15 @@ func FetchUser(c *gin.Context) {
507 } 517 }
508 } 518 }
509 } 519 }
520 // Sort the overall rankings
521 sort.Slice(rankingsList.Overall, func(i, j int) bool {
522 a := rankingsList.Overall[i]
523 b := rankingsList.Overall[j]
524 if a.TotalScore == b.TotalScore {
525 return a.User.SteamID < b.User.SteamID
526 }
527 return a.TotalScore < b.TotalScore
528 })
510 529
511 placement := 1 530 placement := 1
512 ties := 0 531 ties := 0
diff --git a/backend/main.go b/backend/main.go
index a1a4a20..e422359 100644
--- a/backend/main.go
+++ b/backend/main.go
@@ -11,6 +11,8 @@ import (
11 11
12 "github.com/gin-gonic/gin" 12 "github.com/gin-gonic/gin"
13 "github.com/joho/godotenv" 13 "github.com/joho/godotenv"
14 nrgin "github.com/newrelic/go-agent/v3/integrations/nrgin"
15 "github.com/newrelic/go-agent/v3/newrelic"
14) 16)
15 17
16// @title Least Portals Hub 18// @title Least Portals Hub
@@ -30,7 +32,16 @@ func main() {
30 if os.Getenv("ENV") == "PROD" { 32 if os.Getenv("ENV") == "PROD" {
31 gin.SetMode(gin.ReleaseMode) 33 gin.SetMode(gin.ReleaseMode)
32 } 34 }
35 app, err := newrelic.NewApplication(
36 newrelic.ConfigAppName("lphub"),
37 newrelic.ConfigLicense(os.Getenv("NEWRELIC_LICENSE_KEY")),
38 newrelic.ConfigAppLogForwardingEnabled(true),
39 )
40 if err != nil {
41 log.Fatal("Error instrumenting newrelic")
42 }
33 router := gin.Default() 43 router := gin.Default()
44 router.Use(nrgin.Middleware(app))
34 database.ConnectDB() 45 database.ConnectDB()
35 api.InitRoutes(router) 46 api.InitRoutes(router)
36 // for debugging 47 // for debugging
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 {
47 Image string `json:"image"` 47 Image string `json:"image"`
48 IsCoop bool `json:"is_coop"` 48 IsCoop bool `json:"is_coop"`
49 IsDisabled bool `json:"is_disabled"` 49 IsDisabled bool `json:"is_disabled"`
50 Difficulty int `json:"difficulty"`
50} 51}
51 52
52type MapShort struct { 53type MapShort struct {
diff --git a/backend/parser/parser.go b/backend/parser/parser.go
index 19cd677..e39616c 100644
--- a/backend/parser/parser.go
+++ b/backend/parser/parser.go
@@ -2,8 +2,8 @@ package parser
2 2
3import ( 3import (
4 "errors" 4 "errors"
5 "io"
5 "math" 6 "math"
6 "os"
7 "regexp" 7 "regexp"
8 "strconv" 8 "strconv"
9 "strings" 9 "strings"
@@ -22,13 +22,9 @@ type Result struct {
22} 22}
23 23
24// Don't try to understand it, feel it. 24// Don't try to understand it, feel it.
25func ProcessDemo(filePath string) (Result, error) { 25func ProcessDemo(demoFile io.Reader) (Result, error) {
26 var result Result 26 var result Result
27 file, err := os.Open(filePath) 27 reader := bitreader.NewReader(demoFile, true)
28 if err != nil {
29 return Result{}, err
30 }
31 reader := bitreader.NewReader(file, true)
32 demoFileStamp := reader.TryReadString() 28 demoFileStamp := reader.TryReadString()
33 demoProtocol := reader.TryReadSInt32() 29 demoProtocol := reader.TryReadSInt32()
34 networkProtocol := reader.TryReadSInt32() 30 networkProtocol := reader.TryReadSInt32()