+7
cmd/cocoon/main.go
+7
cmd/cocoon/main.go
···
9
9
"os"
10
10
"time"
11
11
12
+
"github.com/bluesky-social/go-util/pkg/telemetry"
12
13
"github.com/bluesky-social/indigo/atproto/atcrypto"
13
14
"github.com/bluesky-social/indigo/atproto/syntax"
14
15
"github.com/haileyok/cocoon/internal/helpers"
···
154
155
Name: "fallback-proxy",
155
156
EnvVars: []string{"COCOON_FALLBACK_PROXY"},
156
157
},
158
+
telemetry.CLIFlagDebug,
159
+
telemetry.CLIFlagMetricsListenAddress,
157
160
},
158
161
Commands: []*cli.Command{
159
162
runServe,
···
177
180
Flags: []cli.Flag{},
178
181
Action: func(cmd *cli.Context) error {
179
182
183
+
logger := telemetry.StartLogger(cmd)
184
+
telemetry.StartMetrics(cmd)
185
+
180
186
s, err := server.New(&server.Args{
187
+
Logger: logger,
181
188
Addr: cmd.String("addr"),
182
189
DbName: cmd.String("db-name"),
183
190
DbType: cmd.String("db-type"),
+15
-13
go.mod
+15
-13
go.mod
···
1
1
module github.com/haileyok/cocoon
2
2
3
-
go 1.24.1
3
+
go 1.24.5
4
4
5
5
require (
6
6
github.com/Azure/go-autorest/autorest/to v0.4.1
7
7
github.com/aws/aws-sdk-go v1.55.7
8
+
github.com/bluesky-social/go-util v0.0.0-20251012040650-2ebbf57f5934
8
9
github.com/bluesky-social/indigo v0.0.0-20251009212240-20524de167fe
9
10
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792
10
11
github.com/domodwyer/mailyak/v3 v3.6.2
11
12
github.com/go-pkgz/expirable-cache/v3 v3.0.0
12
13
github.com/go-playground/validator v9.31.0+incompatible
13
14
github.com/golang-jwt/jwt/v4 v4.5.2
14
-
github.com/google/uuid v1.4.0
15
+
github.com/google/uuid v1.6.0
15
16
github.com/gorilla/sessions v1.4.0
16
17
github.com/gorilla/websocket v1.5.1
17
18
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b
···
26
27
github.com/labstack/echo/v4 v4.13.3
27
28
github.com/lestrrat-go/jwx/v2 v2.0.12
28
29
github.com/multiformats/go-multihash v0.2.3
30
+
github.com/prometheus/client_golang v1.23.2
29
31
github.com/samber/slog-echo v1.16.1
30
32
github.com/urfave/cli/v2 v2.27.6
31
33
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e
32
34
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b
33
-
golang.org/x/crypto v0.38.0
35
+
golang.org/x/crypto v0.41.0
36
+
gorm.io/driver/postgres v1.5.7
34
37
gorm.io/driver/sqlite v1.5.7
35
38
gorm.io/gorm v1.25.12
36
39
)
···
56
59
github.com/gorilla/securecookie v1.1.2 // indirect
57
60
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
58
61
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
59
-
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
62
+
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
60
63
github.com/hashicorp/golang-lru v1.0.2 // indirect
61
64
github.com/ipfs/bbloom v0.0.4 // indirect
62
65
github.com/ipfs/go-blockservice v0.5.2 // indirect
···
102
105
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
103
106
github.com/opentracing/opentracing-go v1.2.0 // indirect
104
107
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect
105
-
github.com/prometheus/client_golang v1.22.0 // indirect
106
108
github.com/prometheus/client_model v0.6.2 // indirect
107
-
github.com/prometheus/common v0.63.0 // indirect
109
+
github.com/prometheus/common v0.66.1 // indirect
108
110
github.com/prometheus/procfs v0.16.1 // indirect
109
111
github.com/russross/blackfriday/v2 v2.1.0 // indirect
110
112
github.com/samber/lo v1.49.1 // indirect
···
114
116
github.com/valyala/fasttemplate v1.2.2 // indirect
115
117
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
116
118
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
117
-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
119
+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
118
120
go.opentelemetry.io/otel v1.29.0 // indirect
119
121
go.opentelemetry.io/otel/metric v1.29.0 // indirect
120
122
go.opentelemetry.io/otel/trace v1.29.0 // indirect
121
123
go.uber.org/atomic v1.11.0 // indirect
122
124
go.uber.org/multierr v1.11.0 // indirect
123
125
go.uber.org/zap v1.26.0 // indirect
124
-
golang.org/x/net v0.40.0 // indirect
125
-
golang.org/x/sync v0.14.0 // indirect
126
-
golang.org/x/sys v0.33.0 // indirect
127
-
golang.org/x/text v0.25.0 // indirect
126
+
go.yaml.in/yaml/v2 v2.4.2 // indirect
127
+
golang.org/x/net v0.43.0 // indirect
128
+
golang.org/x/sync v0.16.0 // indirect
129
+
golang.org/x/sys v0.35.0 // indirect
130
+
golang.org/x/text v0.28.0 // indirect
128
131
golang.org/x/time v0.11.0 // indirect
129
132
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
130
-
google.golang.org/protobuf v1.36.6 // indirect
133
+
google.golang.org/protobuf v1.36.9 // indirect
131
134
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
132
135
gopkg.in/inf.v0 v0.9.1 // indirect
133
-
gorm.io/driver/postgres v1.5.7 // indirect
134
136
lukechampine.com/blake3 v1.2.1 // indirect
135
137
)
+42
-33
go.sum
+42
-33
go.sum
···
16
16
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
17
17
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
18
18
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
19
+
github.com/bluesky-social/go-util v0.0.0-20251012040650-2ebbf57f5934 h1:btHMur2kTRgWEnCHn6LaI3BE9YRgsqTpwpJ1UdB7VEk=
20
+
github.com/bluesky-social/go-util v0.0.0-20251012040650-2ebbf57f5934/go.mod h1:LWamyZfbQGW7PaVc5jumFfjgrshJ5mXgDUnR6fK7+BI=
19
21
github.com/bluesky-social/indigo v0.0.0-20251009212240-20524de167fe h1:VBhaqE5ewQgXbY5SfSWFZC/AwHFo7cHxZKFYi2ce9Yo=
20
22
github.com/bluesky-social/indigo v0.0.0-20251009212240-20524de167fe/go.mod h1:RuQVrCGm42QNsgumKaR6se+XkFKfCPNwdCiTvqKRUck=
21
23
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
···
39
41
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
40
42
github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8=
41
43
github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c=
44
+
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
45
+
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
42
46
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
43
47
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
44
48
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
···
77
81
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
78
82
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
79
83
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
80
-
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
81
-
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
84
+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
85
+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
82
86
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
83
87
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
84
88
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
···
95
99
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0=
96
100
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
97
101
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
98
-
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
99
-
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
100
-
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
101
-
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
102
+
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
103
+
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
104
+
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
105
+
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
102
106
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
103
107
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
104
108
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
···
195
199
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
196
200
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
197
201
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
202
+
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
203
+
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
198
204
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
199
205
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
200
206
github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8=
···
206
212
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
207
213
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
208
214
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
215
+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
216
+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
209
217
github.com/labstack/echo-contrib v0.17.4 h1:g5mfsrJfJTKv+F5uNKCyrjLK7js+ZW6HTjg4FnDxxgk=
210
218
github.com/labstack/echo-contrib v0.17.4/go.mod h1:9O7ZPAHUeMGTOAfg80YqQduHzt0CzLak36PZRldYrZ0=
211
219
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
···
289
297
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
290
298
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0=
291
299
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw=
292
-
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
293
-
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
300
+
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
301
+
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
294
302
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
295
303
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
296
-
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
297
-
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
304
+
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
305
+
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
298
306
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
299
307
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
300
308
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
···
319
327
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
320
328
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
321
329
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
322
-
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
323
330
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
324
331
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
325
332
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
···
327
334
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
328
335
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
329
336
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
330
-
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
331
-
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
337
+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
338
+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
332
339
github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
333
340
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
334
341
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
···
354
361
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8=
355
362
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q=
356
363
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I=
357
-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
358
-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
364
+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
365
+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
359
366
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
360
367
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
361
368
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
···
367
374
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
368
375
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
369
376
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
370
-
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
371
-
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
377
+
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
378
+
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
372
379
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
373
380
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
374
381
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
···
378
385
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
379
386
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
380
387
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
388
+
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
389
+
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
381
390
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
382
391
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
383
392
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
384
393
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
385
394
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
386
395
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
387
-
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
388
-
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
396
+
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
397
+
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
389
398
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
390
399
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
391
400
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
···
395
404
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
396
405
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
397
406
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
398
-
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
399
-
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
407
+
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
408
+
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
400
409
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
401
410
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
402
411
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
···
407
416
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
408
417
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
409
418
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
410
-
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
411
-
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
419
+
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
420
+
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
412
421
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
413
422
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
414
423
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
415
424
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
416
425
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
417
426
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
418
-
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
419
-
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
427
+
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
428
+
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
420
429
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
421
430
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
422
431
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
···
432
441
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
433
442
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
434
443
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
435
-
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
436
-
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
444
+
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
445
+
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
437
446
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
438
447
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
439
448
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
···
445
454
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
446
455
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
447
456
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
448
-
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
449
-
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
457
+
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
458
+
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
450
459
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
451
460
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
452
461
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
···
461
470
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
462
471
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
463
472
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
464
-
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
465
-
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
473
+
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
474
+
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
466
475
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
467
476
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
468
477
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
469
478
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
470
479
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
471
480
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
472
-
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
473
-
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
481
+
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
482
+
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
474
483
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
475
484
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
476
485
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+15
-14
internal/db/db.go
+15
-14
internal/db/db.go
···
1
1
package db
2
2
3
3
import (
4
+
"context"
4
5
"sync"
5
6
6
7
"gorm.io/gorm"
···
19
20
}
20
21
}
21
22
22
-
func (db *DB) Create(value any, clauses []clause.Expression) *gorm.DB {
23
+
func (db *DB) Create(ctx context.Context, value any, clauses []clause.Expression) *gorm.DB {
23
24
db.mu.Lock()
24
25
defer db.mu.Unlock()
25
-
return db.cli.Clauses(clauses...).Create(value)
26
+
return db.cli.WithContext(ctx).Clauses(clauses...).Create(value)
26
27
}
27
28
28
-
func (db *DB) Save(value any, clauses []clause.Expression) *gorm.DB {
29
+
func (db *DB) Save(ctx context.Context, value any, clauses []clause.Expression) *gorm.DB {
29
30
db.mu.Lock()
30
31
defer db.mu.Unlock()
31
-
return db.cli.Clauses(clauses...).Save(value)
32
+
return db.cli.WithContext(ctx).Clauses(clauses...).Save(value)
32
33
}
33
34
34
-
func (db *DB) Exec(sql string, clauses []clause.Expression, values ...any) *gorm.DB {
35
+
func (db *DB) Exec(ctx context.Context, sql string, clauses []clause.Expression, values ...any) *gorm.DB {
35
36
db.mu.Lock()
36
37
defer db.mu.Unlock()
37
-
return db.cli.Clauses(clauses...).Exec(sql, values...)
38
+
return db.cli.WithContext(ctx).Clauses(clauses...).Exec(sql, values...)
38
39
}
39
40
40
-
func (db *DB) Raw(sql string, clauses []clause.Expression, values ...any) *gorm.DB {
41
-
return db.cli.Clauses(clauses...).Raw(sql, values...)
41
+
func (db *DB) Raw(ctx context.Context, sql string, clauses []clause.Expression, values ...any) *gorm.DB {
42
+
return db.cli.WithContext(ctx).Clauses(clauses...).Raw(sql, values...)
42
43
}
43
44
44
45
func (db *DB) AutoMigrate(models ...any) error {
45
46
return db.cli.AutoMigrate(models...)
46
47
}
47
48
48
-
func (db *DB) Delete(value any, clauses []clause.Expression) *gorm.DB {
49
+
func (db *DB) Delete(ctx context.Context, value any, clauses []clause.Expression) *gorm.DB {
49
50
db.mu.Lock()
50
51
defer db.mu.Unlock()
51
-
return db.cli.Clauses(clauses...).Delete(value)
52
+
return db.cli.WithContext(ctx).Clauses(clauses...).Delete(value)
52
53
}
53
54
54
-
func (db *DB) First(dest any, conds ...any) *gorm.DB {
55
-
return db.cli.First(dest, conds...)
55
+
func (db *DB) First(ctx context.Context, dest any, conds ...any) *gorm.DB {
56
+
return db.cli.WithContext(ctx).First(dest, conds...)
56
57
}
57
58
58
59
// TODO: this isn't actually good. we can commit even if the db is locked here. this is probably okay for the time being, but need to figure
59
60
// out a better solution. right now we only do this whenever we're importing a repo though so i'm mostly not worried, but it's still bad.
60
61
// e.g. when we do apply writes we should also be using a transcation but we don't right now
61
-
func (db *DB) BeginDangerously() *gorm.DB {
62
-
return db.cli.Begin()
62
+
func (db *DB) BeginDangerously(ctx context.Context) *gorm.DB {
63
+
return db.cli.WithContext(ctx).Begin()
63
64
}
64
65
65
66
func (db *DB) Lock() {
+30
metrics/metrics.go
+30
metrics/metrics.go
···
1
+
package metrics
2
+
3
+
import (
4
+
"github.com/prometheus/client_golang/prometheus"
5
+
"github.com/prometheus/client_golang/prometheus/promauto"
6
+
)
7
+
8
+
const (
9
+
NAMESPACE = "cocoon"
10
+
)
11
+
12
+
var (
13
+
RelaysConnected = promauto.NewGaugeVec(prometheus.GaugeOpts{
14
+
Namespace: NAMESPACE,
15
+
Name: "relays_connected",
16
+
Help: "number of connected relays, by host",
17
+
}, []string{"host"})
18
+
19
+
RelaySends = promauto.NewCounterVec(prometheus.CounterOpts{
20
+
Namespace: NAMESPACE,
21
+
Name: "relay_sends",
22
+
Help: "number of events sent to a relay, by host",
23
+
}, []string{"host", "kind"})
24
+
25
+
RepoOperations = promauto.NewCounterVec(prometheus.CounterOpts{
26
+
Namespace: NAMESPACE,
27
+
Name: "repo_operations",
28
+
Help: "number of operations made against repos",
29
+
}, []string{"kind"})
30
+
)
+12
-2
models/models.go
+12
-2
models/models.go
···
8
8
"github.com/bluesky-social/indigo/atproto/atcrypto"
9
9
)
10
10
11
+
type TwoFactorType string
12
+
13
+
var (
14
+
TwoFactorTypeNone = TwoFactorType("none")
15
+
TwoFactorTypeEmail = TwoFactorType("email")
16
+
)
17
+
11
18
type Repo struct {
12
19
Did string `gorm:"primaryKey"`
13
20
CreatedAt time.Time
···
29
36
Root []byte
30
37
Preferences []byte
31
38
Deactivated bool
39
+
TwoFactorCode *string
40
+
TwoFactorCodeExpiresAt *time.Time
41
+
TwoFactorType TwoFactorType `gorm:"default:none"`
32
42
}
33
43
34
44
func (r *Repo) SignFor(ctx context.Context, did string, msg []byte) ([]byte, error) {
···
121
131
}
122
132
123
133
type ReservedKey struct {
124
-
KeyDid string `gorm:"primaryKey"`
125
-
Did *string `gorm:"index"`
134
+
KeyDid string `gorm:"primaryKey"`
135
+
Did *string `gorm:"index"`
126
136
PrivateKey []byte
127
137
CreatedAt time.Time `gorm:"index"`
128
138
}
-1
oauth/client/manager.go
-1
oauth/client/manager.go
+1
-1
oauth/dpop/jti_cache.go
+1
-1
oauth/dpop/jti_cache.go
···
14
14
}
15
15
16
16
func newJTICache(size int) *jtiCache {
17
-
cache := cache.NewCache[string, bool]().WithTTL(24 * time.Hour).WithLRU().WithTTL(constants.JTITtl)
17
+
cache := cache.NewCache[string, bool]().WithTTL(24 * time.Hour).WithLRU().WithTTL(constants.JTITtl).WithMaxKeys(size)
18
18
return &jtiCache{
19
19
cache: cache,
20
20
mu: sync.Mutex{},
+10
-8
server/common.go
+10
-8
server/common.go
···
1
1
package server
2
2
3
3
import (
4
+
"context"
5
+
4
6
"github.com/haileyok/cocoon/models"
5
7
)
6
8
7
-
func (s *Server) getActorByHandle(handle string) (*models.Actor, error) {
9
+
func (s *Server) getActorByHandle(ctx context.Context, handle string) (*models.Actor, error) {
8
10
var actor models.Actor
9
-
if err := s.db.First(&actor, models.Actor{Handle: handle}).Error; err != nil {
11
+
if err := s.db.First(ctx, &actor, models.Actor{Handle: handle}).Error; err != nil {
10
12
return nil, err
11
13
}
12
14
return &actor, nil
13
15
}
14
16
15
-
func (s *Server) getRepoByEmail(email string) (*models.Repo, error) {
17
+
func (s *Server) getRepoByEmail(ctx context.Context, email string) (*models.Repo, error) {
16
18
var repo models.Repo
17
-
if err := s.db.First(&repo, models.Repo{Email: email}).Error; err != nil {
19
+
if err := s.db.First(ctx, &repo, models.Repo{Email: email}).Error; err != nil {
18
20
return nil, err
19
21
}
20
22
return &repo, nil
21
23
}
22
24
23
-
func (s *Server) getRepoActorByEmail(email string) (*models.RepoActor, error) {
25
+
func (s *Server) getRepoActorByEmail(ctx context.Context, email string) (*models.RepoActor, error) {
24
26
var repo models.RepoActor
25
-
if err := s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.email= ?", nil, email).Scan(&repo).Error; err != nil {
27
+
if err := s.db.Raw(ctx, "SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.email= ?", nil, email).Scan(&repo).Error; err != nil {
26
28
return nil, err
27
29
}
28
30
return &repo, nil
29
31
}
30
32
31
-
func (s *Server) getRepoActorByDid(did string) (*models.RepoActor, error) {
33
+
func (s *Server) getRepoActorByDid(ctx context.Context, did string) (*models.RepoActor, error) {
32
34
var repo models.RepoActor
33
-
if err := s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.did = ?", nil, did).Scan(&repo).Error; err != nil {
35
+
if err := s.db.Raw(ctx, "SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.did = ?", nil, did).Scan(&repo).Error; err != nil {
34
36
return nil, err
35
37
}
36
38
return &repo, nil
+4
-2
server/handle_account.go
+4
-2
server/handle_account.go
···
12
12
13
13
func (s *Server) handleAccount(e echo.Context) error {
14
14
ctx := e.Request().Context()
15
+
logger := s.logger.With("name", "handleAuth")
16
+
15
17
repo, sess, err := s.getSessionRepoOrErr(e)
16
18
if err != nil {
17
19
return e.Redirect(303, "/account/signin")
···
20
22
oldestPossibleSession := time.Now().Add(constants.ConfidentialClientSessionLifetime)
21
23
22
24
var tokens []provider.OauthToken
23
-
if err := s.db.Raw("SELECT * FROM oauth_tokens WHERE sub = ? AND created_at < ? ORDER BY created_at ASC", nil, repo.Repo.Did, oldestPossibleSession).Scan(&tokens).Error; err != nil {
24
-
s.logger.Error("couldnt fetch oauth sessions for account", "did", repo.Repo.Did, "error", err)
25
+
if err := s.db.Raw(ctx, "SELECT * FROM oauth_tokens WHERE sub = ? AND created_at < ? ORDER BY created_at ASC", nil, repo.Repo.Did, oldestPossibleSession).Scan(&tokens).Error; err != nil {
26
+
logger.Error("couldnt fetch oauth sessions for account", "did", repo.Repo.Did, "error", err)
25
27
sess.AddFlash("Unable to fetch sessions. See server logs for more details.", "error")
26
28
sess.Save(e.Request(), e.Response())
27
29
return e.Render(200, "account.html", map[string]any{
+8
-5
server/handle_account_revoke.go
+8
-5
server/handle_account_revoke.go
···
5
5
"github.com/labstack/echo/v4"
6
6
)
7
7
8
-
type AccountRevokeRequest struct {
8
+
type AccountRevokeInput struct {
9
9
Token string `form:"token"`
10
10
}
11
11
12
12
func (s *Server) handleAccountRevoke(e echo.Context) error {
13
-
var req AccountRevokeRequest
13
+
ctx := e.Request().Context()
14
+
logger := s.logger.With("name", "handleAcocuntRevoke")
15
+
16
+
var req AccountRevokeInput
14
17
if err := e.Bind(&req); err != nil {
15
-
s.logger.Error("could not bind account revoke request", "error", err)
18
+
logger.Error("could not bind account revoke request", "error", err)
16
19
return helpers.ServerError(e, nil)
17
20
}
18
21
···
21
24
return e.Redirect(303, "/account/signin")
22
25
}
23
26
24
-
if err := s.db.Exec("DELETE FROM oauth_tokens WHERE sub = ? AND token = ?", nil, repo.Repo.Did, req.Token).Error; err != nil {
25
-
s.logger.Error("couldnt delete oauth session for account", "did", repo.Repo.Did, "token", req.Token, "error", err)
27
+
if err := s.db.Exec(ctx, "DELETE FROM oauth_tokens WHERE sub = ? AND token = ?", nil, repo.Repo.Did, req.Token).Error; err != nil {
28
+
logger.Error("couldnt delete oauth session for account", "did", repo.Repo.Did, "token", req.Token, "error", err)
26
29
sess.AddFlash("Unable to revoke session. See server logs for more details.", "error")
27
30
sess.Save(e.Request(), e.Response())
28
31
return e.Redirect(303, "/account")
+68
-16
server/handle_account_signin.go
+68
-16
server/handle_account_signin.go
···
2
2
3
3
import (
4
4
"errors"
5
+
"fmt"
5
6
"strings"
7
+
"time"
6
8
7
9
"github.com/bluesky-social/indigo/atproto/syntax"
8
10
"github.com/gorilla/sessions"
···
14
16
"gorm.io/gorm"
15
17
)
16
18
17
-
type OauthSigninRequest struct {
18
-
Username string `form:"username"`
19
-
Password string `form:"password"`
20
-
QueryParams string `form:"query_params"`
19
+
type OauthSigninInput struct {
20
+
Username string `form:"username"`
21
+
Password string `form:"password"`
22
+
AuthFactorToken string `form:"token"`
23
+
QueryParams string `form:"query_params"`
21
24
}
22
25
23
26
func (s *Server) getSessionRepoOrErr(e echo.Context) (*models.RepoActor, *sessions.Session, error) {
27
+
ctx := e.Request().Context()
28
+
24
29
sess, err := session.Get("session", e)
25
30
if err != nil {
26
31
return nil, nil, err
···
31
36
return nil, sess, errors.New("did was not set in session")
32
37
}
33
38
34
-
repo, err := s.getRepoActorByDid(did)
39
+
repo, err := s.getRepoActorByDid(ctx, did)
35
40
if err != nil {
36
41
return nil, sess, err
37
42
}
···
42
47
func getFlashesFromSession(e echo.Context, sess *sessions.Session) map[string]any {
43
48
defer sess.Save(e.Request(), e.Response())
44
49
return map[string]any{
45
-
"errors": sess.Flashes("error"),
46
-
"successes": sess.Flashes("success"),
50
+
"errors": sess.Flashes("error"),
51
+
"successes": sess.Flashes("success"),
52
+
"tokenrequired": sess.Flashes("tokenrequired"),
47
53
}
48
54
}
49
55
···
60
66
}
61
67
62
68
func (s *Server) handleAccountSigninPost(e echo.Context) error {
63
-
var req OauthSigninRequest
69
+
ctx := e.Request().Context()
70
+
logger := s.logger.With("name", "handleAccountSigninPost")
71
+
72
+
var req OauthSigninInput
64
73
if err := e.Bind(&req); err != nil {
65
-
s.logger.Error("error binding sign in req", "error", err)
74
+
logger.Error("error binding sign in req", "error", err)
66
75
return helpers.ServerError(e, nil)
67
76
}
68
77
···
76
85
idtype = "handle"
77
86
} else {
78
87
idtype = "email"
88
+
}
89
+
90
+
queryParams := ""
91
+
if req.QueryParams != "" {
92
+
queryParams = fmt.Sprintf("?%s", req.QueryParams)
79
93
}
80
94
81
95
// TODO: we should make this a helper since we do it for the base create_session as well
···
83
97
var err error
84
98
switch idtype {
85
99
case "did":
86
-
err = s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.did = ?", nil, req.Username).Scan(&repo).Error
100
+
err = s.db.Raw(ctx, "SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.did = ?", nil, req.Username).Scan(&repo).Error
87
101
case "handle":
88
-
err = s.db.Raw("SELECT r.*, a.* FROM actors a LEFT JOIN repos r ON a.did = r.did WHERE a.handle = ?", nil, req.Username).Scan(&repo).Error
102
+
err = s.db.Raw(ctx, "SELECT r.*, a.* FROM actors a LEFT JOIN repos r ON a.did = r.did WHERE a.handle = ?", nil, req.Username).Scan(&repo).Error
89
103
case "email":
90
-
err = s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.email = ?", nil, req.Username).Scan(&repo).Error
104
+
err = s.db.Raw(ctx, "SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.email = ?", nil, req.Username).Scan(&repo).Error
91
105
}
92
106
if err != nil {
93
107
if err == gorm.ErrRecordNotFound {
···
96
110
sess.AddFlash("Something went wrong!", "error")
97
111
}
98
112
sess.Save(e.Request(), e.Response())
99
-
return e.Redirect(303, "/account/signin")
113
+
return e.Redirect(303, "/account/signin"+queryParams)
100
114
}
101
115
102
116
if err := bcrypt.CompareHashAndPassword([]byte(repo.Password), []byte(req.Password)); err != nil {
···
106
120
sess.AddFlash("Something went wrong!", "error")
107
121
}
108
122
sess.Save(e.Request(), e.Response())
109
-
return e.Redirect(303, "/account/signin")
123
+
return e.Redirect(303, "/account/signin"+queryParams)
124
+
}
125
+
126
+
// if repo requires 2FA token and one hasn't been provided, return error prompting for one
127
+
if repo.TwoFactorType != models.TwoFactorTypeNone && req.AuthFactorToken == "" {
128
+
err = s.createAndSendTwoFactorCode(ctx, repo)
129
+
if err != nil {
130
+
sess.AddFlash("Something went wrong!", "error")
131
+
sess.Save(e.Request(), e.Response())
132
+
return e.Redirect(303, "/account/signin"+queryParams)
133
+
}
134
+
135
+
sess.AddFlash("requires 2FA token", "tokenrequired")
136
+
sess.Save(e.Request(), e.Response())
137
+
return e.Redirect(303, "/account/signin"+queryParams)
138
+
}
139
+
140
+
// if 2FAis required, now check that the one provided is valid
141
+
if repo.TwoFactorType != models.TwoFactorTypeNone {
142
+
if repo.TwoFactorCode == nil || repo.TwoFactorCodeExpiresAt == nil {
143
+
err = s.createAndSendTwoFactorCode(ctx, repo)
144
+
if err != nil {
145
+
sess.AddFlash("Something went wrong!", "error")
146
+
sess.Save(e.Request(), e.Response())
147
+
return e.Redirect(303, "/account/signin"+queryParams)
148
+
}
149
+
150
+
sess.AddFlash("requires 2FA token", "tokenrequired")
151
+
sess.Save(e.Request(), e.Response())
152
+
return e.Redirect(303, "/account/signin"+queryParams)
153
+
}
154
+
155
+
if *repo.TwoFactorCode != req.AuthFactorToken {
156
+
return helpers.InvalidTokenError(e)
157
+
}
158
+
159
+
if time.Now().UTC().After(*repo.TwoFactorCodeExpiresAt) {
160
+
return helpers.ExpiredTokenError(e)
161
+
}
110
162
}
111
163
112
164
sess.Options = &sessions.Options{
···
122
174
return err
123
175
}
124
176
125
-
if req.QueryParams != "" {
126
-
return e.Redirect(303, "/oauth/authorize?"+req.QueryParams)
177
+
if queryParams != "" {
178
+
return e.Redirect(303, "/oauth/authorize"+queryParams)
127
179
} else {
128
180
return e.Redirect(303, "/account")
129
181
}
+3
-1
server/handle_actor_put_preferences.go
+3
-1
server/handle_actor_put_preferences.go
···
10
10
// This is kinda lame. Not great to implement app.bsky in the pds, but alas
11
11
12
12
func (s *Server) handleActorPutPreferences(e echo.Context) error {
13
+
ctx := e.Request().Context()
14
+
13
15
repo := e.Get("repo").(*models.RepoActor)
14
16
15
17
var prefs map[string]any
···
22
24
return err
23
25
}
24
26
25
-
if err := s.db.Exec("UPDATE repos SET preferences = ? WHERE did = ?", nil, b, repo.Repo.Did).Error; err != nil {
27
+
if err := s.db.Exec(ctx, "UPDATE repos SET preferences = ? WHERE did = ?", nil, b, repo.Repo.Did).Error; err != nil {
26
28
return err
27
29
}
28
30
+4
-2
server/handle_identity_get_recommended_did_credentials.go
+4
-2
server/handle_identity_get_recommended_did_credentials.go
···
8
8
)
9
9
10
10
func (s *Server) handleGetRecommendedDidCredentials(e echo.Context) error {
11
+
logger := s.logger.With("name", "handleIdentityGetRecommendedDidCredentials")
12
+
11
13
repo := e.Get("repo").(*models.RepoActor)
12
14
k, err := atcrypto.ParsePrivateBytesK256(repo.SigningKey)
13
15
if err != nil {
14
-
s.logger.Error("error parsing key", "error", err)
16
+
logger.Error("error parsing key", "error", err)
15
17
return helpers.ServerError(e, nil)
16
18
}
17
19
creds, err := s.plcClient.CreateDidCredentials(k, "", repo.Actor.Handle)
18
20
if err != nil {
19
-
s.logger.Error("error crating did credentials", "error", err)
21
+
logger.Error("error crating did credentials", "error", err)
20
22
return helpers.ServerError(e, nil)
21
23
}
22
24
+6
-3
server/handle_identity_request_plc_operation.go
+6
-3
server/handle_identity_request_plc_operation.go
···
10
10
)
11
11
12
12
func (s *Server) handleIdentityRequestPlcOperationSignature(e echo.Context) error {
13
+
ctx := e.Request().Context()
14
+
logger := s.logger.With("name", "handleIdentityRequestPlcOperationSignature")
15
+
13
16
urepo := e.Get("repo").(*models.RepoActor)
14
17
15
18
code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
16
19
eat := time.Now().Add(10 * time.Minute).UTC()
17
20
18
-
if err := s.db.Exec("UPDATE repos SET plc_operation_code = ?, plc_operation_code_expires_at = ? WHERE did = ?", nil, code, eat, urepo.Repo.Did).Error; err != nil {
19
-
s.logger.Error("error updating user", "error", err)
21
+
if err := s.db.Exec(ctx, "UPDATE repos SET plc_operation_code = ?, plc_operation_code_expires_at = ? WHERE did = ?", nil, code, eat, urepo.Repo.Did).Error; err != nil {
22
+
logger.Error("error updating user", "error", err)
20
23
return helpers.ServerError(e, nil)
21
24
}
22
25
23
26
if err := s.sendPlcTokenReset(urepo.Email, urepo.Handle, code); err != nil {
24
-
s.logger.Error("error sending mail", "error", err)
27
+
logger.Error("error sending mail", "error", err)
25
28
return helpers.ServerError(e, nil)
26
29
}
27
30
+8
-6
server/handle_identity_sign_plc_operation.go
+8
-6
server/handle_identity_sign_plc_operation.go
···
27
27
}
28
28
29
29
func (s *Server) handleSignPlcOperation(e echo.Context) error {
30
+
logger := s.logger.With("name", "handleSignPlcOperation")
31
+
30
32
repo := e.Get("repo").(*models.RepoActor)
31
33
32
34
var req ComAtprotoSignPlcOperationRequest
33
35
if err := e.Bind(&req); err != nil {
34
-
s.logger.Error("error binding", "error", err)
36
+
logger.Error("error binding", "error", err)
35
37
return helpers.ServerError(e, nil)
36
38
}
37
39
···
54
56
ctx := context.WithValue(e.Request().Context(), "skip-cache", true)
55
57
log, err := identity.FetchDidAuditLog(ctx, nil, repo.Repo.Did)
56
58
if err != nil {
57
-
s.logger.Error("error fetching doc", "error", err)
59
+
logger.Error("error fetching doc", "error", err)
58
60
return helpers.ServerError(e, nil)
59
61
}
60
62
···
83
85
84
86
k, err := atcrypto.ParsePrivateBytesK256(repo.SigningKey)
85
87
if err != nil {
86
-
s.logger.Error("error parsing signing key", "error", err)
88
+
logger.Error("error parsing signing key", "error", err)
87
89
return helpers.ServerError(e, nil)
88
90
}
89
91
90
92
if err := s.plcClient.SignOp(k, &op); err != nil {
91
-
s.logger.Error("error signing plc operation", "error", err)
93
+
logger.Error("error signing plc operation", "error", err)
92
94
return helpers.ServerError(e, nil)
93
95
}
94
96
95
-
if err := s.db.Exec("UPDATE repos SET plc_operation_code = NULL, plc_operation_code_expires_at = NULL WHERE did = ?", nil, repo.Repo.Did).Error; err != nil {
96
-
s.logger.Error("error updating repo", "error", err)
97
+
if err := s.db.Exec(ctx, "UPDATE repos SET plc_operation_code = NULL, plc_operation_code_expires_at = NULL WHERE did = ?", nil, repo.Repo.Did).Error; err != nil {
98
+
logger.Error("error updating repo", "error", err)
97
99
return helpers.ServerError(e, nil)
98
100
}
99
101
+6
-4
server/handle_identity_submit_plc_operation.go
+6
-4
server/handle_identity_submit_plc_operation.go
···
21
21
}
22
22
23
23
func (s *Server) handleSubmitPlcOperation(e echo.Context) error {
24
+
logger := s.logger.With("name", "handleIdentitySubmitPlcOperation")
25
+
24
26
repo := e.Get("repo").(*models.RepoActor)
25
27
26
28
var req ComAtprotoSubmitPlcOperationRequest
27
29
if err := e.Bind(&req); err != nil {
28
-
s.logger.Error("error binding", "error", err)
30
+
logger.Error("error binding", "error", err)
29
31
return helpers.ServerError(e, nil)
30
32
}
31
33
···
40
42
41
43
k, err := atcrypto.ParsePrivateBytesK256(repo.SigningKey)
42
44
if err != nil {
43
-
s.logger.Error("error parsing key", "error", err)
45
+
logger.Error("error parsing key", "error", err)
44
46
return helpers.ServerError(e, nil)
45
47
}
46
48
required, err := s.plcClient.CreateDidCredentials(k, "", repo.Actor.Handle)
47
49
if err != nil {
48
-
s.logger.Error("error crating did credentials", "error", err)
50
+
logger.Error("error crating did credentials", "error", err)
49
51
return helpers.ServerError(e, nil)
50
52
}
51
53
···
72
74
}
73
75
74
76
if err := s.passport.BustDoc(context.TODO(), repo.Repo.Did); err != nil {
75
-
s.logger.Warn("error busting did doc", "error", err)
77
+
logger.Warn("error busting did doc", "error", err)
76
78
}
77
79
78
80
s.evtman.AddEvent(context.TODO(), &events.XRPCStreamEvent{
+8
-6
server/handle_identity_update_handle.go
+8
-6
server/handle_identity_update_handle.go
···
22
22
}
23
23
24
24
func (s *Server) handleIdentityUpdateHandle(e echo.Context) error {
25
+
logger := s.logger.With("name", "handleIdentityUpdateHandle")
26
+
25
27
repo := e.Get("repo").(*models.RepoActor)
26
28
27
29
var req ComAtprotoIdentityUpdateHandleRequest
28
30
if err := e.Bind(&req); err != nil {
29
-
s.logger.Error("error binding", "error", err)
31
+
logger.Error("error binding", "error", err)
30
32
return helpers.ServerError(e, nil)
31
33
}
32
34
···
41
43
if strings.HasPrefix(repo.Repo.Did, "did:plc:") {
42
44
log, err := identity.FetchDidAuditLog(ctx, nil, repo.Repo.Did)
43
45
if err != nil {
44
-
s.logger.Error("error fetching doc", "error", err)
46
+
logger.Error("error fetching doc", "error", err)
45
47
return helpers.ServerError(e, nil)
46
48
}
47
49
···
68
70
69
71
k, err := atcrypto.ParsePrivateBytesK256(repo.SigningKey)
70
72
if err != nil {
71
-
s.logger.Error("error parsing signing key", "error", err)
73
+
logger.Error("error parsing signing key", "error", err)
72
74
return helpers.ServerError(e, nil)
73
75
}
74
76
···
82
84
}
83
85
84
86
if err := s.passport.BustDoc(context.TODO(), repo.Repo.Did); err != nil {
85
-
s.logger.Warn("error busting did doc", "error", err)
87
+
logger.Warn("error busting did doc", "error", err)
86
88
}
87
89
88
90
s.evtman.AddEvent(context.TODO(), &events.XRPCStreamEvent{
···
94
96
},
95
97
})
96
98
97
-
if err := s.db.Exec("UPDATE actors SET handle = ? WHERE did = ?", nil, req.Handle, repo.Repo.Did).Error; err != nil {
98
-
s.logger.Error("error updating handle in db", "error", err)
99
+
if err := s.db.Exec(ctx, "UPDATE actors SET handle = ? WHERE did = ?", nil, req.Handle, repo.Repo.Did).Error; err != nil {
100
+
logger.Error("error updating handle in db", "error", err)
99
101
return helpers.ServerError(e, nil)
100
102
}
101
103
+14
-11
server/handle_import_repo.go
+14
-11
server/handle_import_repo.go
···
18
18
)
19
19
20
20
func (s *Server) handleRepoImportRepo(e echo.Context) error {
21
+
ctx := e.Request().Context()
22
+
logger := s.logger.With("name", "handleImportRepo")
23
+
21
24
urepo := e.Get("repo").(*models.RepoActor)
22
25
23
26
b, err := io.ReadAll(e.Request().Body)
24
27
if err != nil {
25
-
s.logger.Error("could not read bytes in import request", "error", err)
28
+
logger.Error("could not read bytes in import request", "error", err)
26
29
return helpers.ServerError(e, nil)
27
30
}
28
31
···
30
33
31
34
cs, err := car.NewCarReader(bytes.NewReader(b))
32
35
if err != nil {
33
-
s.logger.Error("could not read car in import request", "error", err)
36
+
logger.Error("could not read car in import request", "error", err)
34
37
return helpers.ServerError(e, nil)
35
38
}
36
39
37
40
orderedBlocks := []blocks.Block{}
38
41
currBlock, err := cs.Next()
39
42
if err != nil {
40
-
s.logger.Error("could not get first block from car", "error", err)
43
+
logger.Error("could not get first block from car", "error", err)
41
44
return helpers.ServerError(e, nil)
42
45
}
43
46
currBlockCt := 1
44
47
45
48
for currBlock != nil {
46
-
s.logger.Info("someone is importing their repo", "block", currBlockCt)
49
+
logger.Info("someone is importing their repo", "block", currBlockCt)
47
50
orderedBlocks = append(orderedBlocks, currBlock)
48
51
next, _ := cs.Next()
49
52
currBlock = next
···
53
56
slices.Reverse(orderedBlocks)
54
57
55
58
if err := bs.PutMany(context.TODO(), orderedBlocks); err != nil {
56
-
s.logger.Error("could not insert blocks", "error", err)
59
+
logger.Error("could not insert blocks", "error", err)
57
60
return helpers.ServerError(e, nil)
58
61
}
59
62
60
63
r, err := repo.OpenRepo(context.TODO(), bs, cs.Header.Roots[0])
61
64
if err != nil {
62
-
s.logger.Error("could not open repo", "error", err)
65
+
logger.Error("could not open repo", "error", err)
63
66
return helpers.ServerError(e, nil)
64
67
}
65
68
66
-
tx := s.db.BeginDangerously()
69
+
tx := s.db.BeginDangerously(ctx)
67
70
68
71
clock := syntax.NewTIDClock(0)
69
72
···
74
77
cidStr := cid.String()
75
78
b, err := bs.Get(context.TODO(), cid)
76
79
if err != nil {
77
-
s.logger.Error("record bytes don't exist in blockstore", "error", err)
80
+
logger.Error("record bytes don't exist in blockstore", "error", err)
78
81
return helpers.ServerError(e, nil)
79
82
}
80
83
···
94
97
return nil
95
98
}); err != nil {
96
99
tx.Rollback()
97
-
s.logger.Error("record bytes don't exist in blockstore", "error", err)
100
+
logger.Error("record bytes don't exist in blockstore", "error", err)
98
101
return helpers.ServerError(e, nil)
99
102
}
100
103
···
102
105
103
106
root, rev, err := r.Commit(context.TODO(), urepo.SignFor)
104
107
if err != nil {
105
-
s.logger.Error("error committing", "error", err)
108
+
logger.Error("error committing", "error", err)
106
109
return helpers.ServerError(e, nil)
107
110
}
108
111
109
112
if err := s.UpdateRepo(context.TODO(), urepo.Repo.Did, root, rev); err != nil {
110
-
s.logger.Error("error updating repo after commit", "error", err)
113
+
logger.Error("error updating repo after commit", "error", err)
111
114
return helpers.ServerError(e, nil)
112
115
}
113
116
+11
-8
server/handle_oauth_par.go
+11
-8
server/handle_oauth_par.go
···
19
19
}
20
20
21
21
func (s *Server) handleOauthPar(e echo.Context) error {
22
+
ctx := e.Request().Context()
23
+
logger := s.logger.With("name", "handleOauthPar")
24
+
22
25
var parRequest provider.ParRequest
23
26
if err := e.Bind(&parRequest); err != nil {
24
-
s.logger.Error("error binding for par request", "error", err)
27
+
logger.Error("error binding for par request", "error", err)
25
28
return helpers.ServerError(e, nil)
26
29
}
27
30
28
31
if err := e.Validate(parRequest); err != nil {
29
-
s.logger.Error("missing parameters for par request", "error", err)
32
+
logger.Error("missing parameters for par request", "error", err)
30
33
return helpers.InputError(e, nil)
31
34
}
32
35
···
43
46
"error": "use_dpop_nonce",
44
47
})
45
48
}
46
-
s.logger.Error("error getting dpop proof", "error", err)
49
+
logger.Error("error getting dpop proof", "error", err)
47
50
return helpers.InputError(e, nil)
48
51
}
49
52
···
53
56
AllowMissingDpopProof: true,
54
57
})
55
58
if err != nil {
56
-
s.logger.Error("error authenticating client", "client_id", parRequest.ClientID, "error", err)
59
+
logger.Error("error authenticating client", "client_id", parRequest.ClientID, "error", err)
57
60
return helpers.InputError(e, to.StringPtr(err.Error()))
58
61
}
59
62
···
64
67
} else {
65
68
if !client.Metadata.DpopBoundAccessTokens {
66
69
msg := "dpop bound access tokens are not enabled for this client"
67
-
s.logger.Error(msg)
70
+
logger.Error(msg)
68
71
return helpers.InputError(e, &msg)
69
72
}
70
73
71
74
if dpopProof.JKT != *parRequest.DpopJkt {
72
75
msg := "supplied dpop jkt does not match header dpop jkt"
73
-
s.logger.Error(msg)
76
+
logger.Error(msg)
74
77
return helpers.InputError(e, &msg)
75
78
}
76
79
}
···
86
89
ExpiresAt: eat,
87
90
}
88
91
89
-
if err := s.db.Create(authRequest, nil).Error; err != nil {
90
-
s.logger.Error("error creating auth request in db", "error", err)
92
+
if err := s.db.Create(ctx, authRequest, nil).Error; err != nil {
93
+
logger.Error("error creating auth request in db", "error", err)
91
94
return helpers.ServerError(e, nil)
92
95
}
93
96
+16
-13
server/handle_oauth_token.go
+16
-13
server/handle_oauth_token.go
···
38
38
}
39
39
40
40
func (s *Server) handleOauthToken(e echo.Context) error {
41
+
ctx := e.Request().Context()
42
+
logger := s.logger.With("name", "handleOauthToken")
43
+
41
44
var req OauthTokenRequest
42
45
if err := e.Bind(&req); err != nil {
43
-
s.logger.Error("error binding token request", "error", err)
46
+
logger.Error("error binding token request", "error", err)
44
47
return helpers.ServerError(e, nil)
45
48
}
46
49
···
56
59
"error": "use_dpop_nonce",
57
60
})
58
61
}
59
-
s.logger.Error("error getting dpop proof", "error", err)
62
+
logger.Error("error getting dpop proof", "error", err)
60
63
return helpers.InputError(e, nil)
61
64
}
62
65
···
64
67
AllowMissingDpopProof: true,
65
68
})
66
69
if err != nil {
67
-
s.logger.Error("error authenticating client", "client_id", req.ClientID, "error", err)
70
+
logger.Error("error authenticating client", "client_id", req.ClientID, "error", err)
68
71
return helpers.InputError(e, to.StringPtr(err.Error()))
69
72
}
70
73
···
84
87
85
88
var authReq provider.OauthAuthorizationRequest
86
89
// get the lil guy and delete him
87
-
if err := s.db.Raw("DELETE FROM oauth_authorization_requests WHERE code = ? RETURNING *", nil, *req.Code).Scan(&authReq).Error; err != nil {
88
-
s.logger.Error("error finding authorization request", "error", err)
90
+
if err := s.db.Raw(ctx, "DELETE FROM oauth_authorization_requests WHERE code = ? RETURNING *", nil, *req.Code).Scan(&authReq).Error; err != nil {
91
+
logger.Error("error finding authorization request", "error", err)
89
92
return helpers.ServerError(e, nil)
90
93
}
91
94
···
110
113
case "S256":
111
114
inputChal, err := base64.RawURLEncoding.DecodeString(*authReq.Parameters.CodeChallenge)
112
115
if err != nil {
113
-
s.logger.Error("error decoding code challenge", "error", err)
116
+
logger.Error("error decoding code challenge", "error", err)
114
117
return helpers.ServerError(e, nil)
115
118
}
116
119
···
128
131
return helpers.InputError(e, to.StringPtr("code_challenge parameter wasn't provided"))
129
132
}
130
133
131
-
repo, err := s.getRepoActorByDid(*authReq.Sub)
134
+
repo, err := s.getRepoActorByDid(ctx, *authReq.Sub)
132
135
if err != nil {
133
136
helpers.InputError(e, to.StringPtr("unable to find actor"))
134
137
}
···
159
162
return err
160
163
}
161
164
162
-
if err := s.db.Create(&provider.OauthToken{
165
+
if err := s.db.Create(ctx, &provider.OauthToken{
163
166
ClientId: authReq.ClientId,
164
167
ClientAuth: *clientAuth,
165
168
Parameters: authReq.Parameters,
···
171
174
RefreshToken: refreshToken,
172
175
Ip: authReq.Ip,
173
176
}, nil).Error; err != nil {
174
-
s.logger.Error("error creating token in db", "error", err)
177
+
logger.Error("error creating token in db", "error", err)
175
178
return helpers.ServerError(e, nil)
176
179
}
177
180
···
199
202
}
200
203
201
204
var oauthToken provider.OauthToken
202
-
if err := s.db.Raw("SELECT * FROM oauth_tokens WHERE refresh_token = ?", nil, req.RefreshToken).Scan(&oauthToken).Error; err != nil {
203
-
s.logger.Error("error finding oauth token by refresh token", "error", err, "refresh_token", req.RefreshToken)
205
+
if err := s.db.Raw(ctx, "SELECT * FROM oauth_tokens WHERE refresh_token = ?", nil, req.RefreshToken).Scan(&oauthToken).Error; err != nil {
206
+
logger.Error("error finding oauth token by refresh token", "error", err, "refresh_token", req.RefreshToken)
204
207
return helpers.ServerError(e, nil)
205
208
}
206
209
···
257
260
return err
258
261
}
259
262
260
-
if err := s.db.Exec("UPDATE oauth_tokens SET token = ?, refresh_token = ?, expires_at = ?, updated_at = ? WHERE refresh_token = ?", nil, accessString, nextRefreshToken, eat, now, *req.RefreshToken).Error; err != nil {
261
-
s.logger.Error("error updating token", "error", err)
263
+
if err := s.db.Exec(ctx, "UPDATE oauth_tokens SET token = ?, refresh_token = ?, expires_at = ?, updated_at = ? WHERE refresh_token = ?", nil, accessString, nextRefreshToken, eat, now, *req.RefreshToken).Error; err != nil {
264
+
logger.Error("error updating token", "error", err)
262
265
return helpers.ServerError(e, nil)
263
266
}
264
267
+6
-6
server/handle_proxy.go
+6
-6
server/handle_proxy.go
···
47
47
}
48
48
49
49
func (s *Server) handleProxy(e echo.Context) error {
50
-
lgr := s.logger.With("handler", "handleProxy")
50
+
logger := s.logger.With("handler", "handleProxy")
51
51
52
52
repo, isAuthed := e.Get("repo").(*models.RepoActor)
53
53
···
58
58
59
59
endpoint, svcDid, err := s.getAtprotoProxyEndpointFromRequest(e)
60
60
if err != nil {
61
-
lgr.Error("could not get atproto proxy", "error", err)
61
+
logger.Error("could not get atproto proxy", "error", err)
62
62
return helpers.ServerError(e, nil)
63
63
}
64
64
···
90
90
}
91
91
hj, err := json.Marshal(header)
92
92
if err != nil {
93
-
lgr.Error("error marshaling header", "error", err)
93
+
logger.Error("error marshaling header", "error", err)
94
94
return helpers.ServerError(e, nil)
95
95
}
96
96
···
118
118
}
119
119
pj, err := json.Marshal(payload)
120
120
if err != nil {
121
-
lgr.Error("error marashaling payload", "error", err)
121
+
logger.Error("error marashaling payload", "error", err)
122
122
return helpers.ServerError(e, nil)
123
123
}
124
124
···
129
129
130
130
sk, err := secp256k1secec.NewPrivateKey(repo.SigningKey)
131
131
if err != nil {
132
-
lgr.Error("can't load private key", "error", err)
132
+
logger.Error("can't load private key", "error", err)
133
133
return err
134
134
}
135
135
136
136
R, S, _, err := sk.SignRaw(rand.Reader, hash[:])
137
137
if err != nil {
138
-
lgr.Error("error signing", "error", err)
138
+
logger.Error("error signing", "error", err)
139
139
}
140
140
141
141
rBytes := R.Bytes()
+14
-11
server/handle_repo_apply_writes.go
+14
-11
server/handle_repo_apply_writes.go
···
6
6
"github.com/labstack/echo/v4"
7
7
)
8
8
9
-
type ComAtprotoRepoApplyWritesRequest struct {
9
+
type ComAtprotoRepoApplyWritesInput struct {
10
10
Repo string `json:"repo" validate:"required,atproto-did"`
11
11
Validate *bool `json:"bool,omitempty"`
12
12
Writes []ComAtprotoRepoApplyWritesItem `json:"writes"`
···
20
20
Value *MarshalableMap `json:"value,omitempty"`
21
21
}
22
22
23
-
type ComAtprotoRepoApplyWritesResponse struct {
23
+
type ComAtprotoRepoApplyWritesOutput struct {
24
24
Commit RepoCommit `json:"commit"`
25
25
Results []ApplyWriteResult `json:"results"`
26
26
}
27
27
28
28
func (s *Server) handleApplyWrites(e echo.Context) error {
29
-
repo := e.Get("repo").(*models.RepoActor)
29
+
ctx := e.Request().Context()
30
+
logger := s.logger.With("name", "handleRepoApplyWrites")
30
31
31
-
var req ComAtprotoRepoApplyWritesRequest
32
+
var req ComAtprotoRepoApplyWritesInput
32
33
if err := e.Bind(&req); err != nil {
33
-
s.logger.Error("error binding", "error", err)
34
+
logger.Error("error binding", "error", err)
34
35
return helpers.ServerError(e, nil)
35
36
}
36
37
37
38
if err := e.Validate(req); err != nil {
38
-
s.logger.Error("error validating", "error", err)
39
+
logger.Error("error validating", "error", err)
39
40
return helpers.InputError(e, nil)
40
41
}
41
42
43
+
repo := e.Get("repo").(*models.RepoActor)
44
+
42
45
if repo.Repo.Did != req.Repo {
43
-
s.logger.Warn("mismatched repo/auth")
46
+
logger.Warn("mismatched repo/auth")
44
47
return helpers.InputError(e, nil)
45
48
}
46
49
47
-
ops := []Op{}
50
+
ops := make([]Op, 0, len(req.Writes))
48
51
for _, item := range req.Writes {
49
52
ops = append(ops, Op{
50
53
Type: OpType(item.Type),
···
54
57
})
55
58
}
56
59
57
-
results, err := s.repoman.applyWrites(repo.Repo, ops, req.SwapCommit)
60
+
results, err := s.repoman.applyWrites(ctx, repo.Repo, ops, req.SwapCommit)
58
61
if err != nil {
59
-
s.logger.Error("error applying writes", "error", err)
62
+
logger.Error("error applying writes", "error", err)
60
63
return helpers.ServerError(e, nil)
61
64
}
62
65
···
66
69
results[i].Commit = nil
67
70
}
68
71
69
-
return e.JSON(200, ComAtprotoRepoApplyWritesResponse{
72
+
return e.JSON(200, ComAtprotoRepoApplyWritesOutput{
70
73
Commit: commit,
71
74
Results: results,
72
75
})
+10
-7
server/handle_repo_create_record.go
+10
-7
server/handle_repo_create_record.go
···
6
6
"github.com/labstack/echo/v4"
7
7
)
8
8
9
-
type ComAtprotoRepoCreateRecordRequest struct {
9
+
type ComAtprotoRepoCreateRecordInput struct {
10
10
Repo string `json:"repo" validate:"required,atproto-did"`
11
11
Collection string `json:"collection" validate:"required,atproto-nsid"`
12
12
Rkey *string `json:"rkey,omitempty"`
···
17
17
}
18
18
19
19
func (s *Server) handleCreateRecord(e echo.Context) error {
20
+
ctx := e.Request().Context()
21
+
logger := s.logger.With("name", "handleCreateRecord")
22
+
20
23
repo := e.Get("repo").(*models.RepoActor)
21
24
22
-
var req ComAtprotoRepoCreateRecordRequest
25
+
var req ComAtprotoRepoCreateRecordInput
23
26
if err := e.Bind(&req); err != nil {
24
-
s.logger.Error("error binding", "error", err)
27
+
logger.Error("error binding", "error", err)
25
28
return helpers.ServerError(e, nil)
26
29
}
27
30
28
31
if err := e.Validate(req); err != nil {
29
-
s.logger.Error("error validating", "error", err)
32
+
logger.Error("error validating", "error", err)
30
33
return helpers.InputError(e, nil)
31
34
}
32
35
33
36
if repo.Repo.Did != req.Repo {
34
-
s.logger.Warn("mismatched repo/auth")
37
+
logger.Warn("mismatched repo/auth")
35
38
return helpers.InputError(e, nil)
36
39
}
37
40
···
40
43
optype = OpTypeUpdate
41
44
}
42
45
43
-
results, err := s.repoman.applyWrites(repo.Repo, []Op{
46
+
results, err := s.repoman.applyWrites(ctx, repo.Repo, []Op{
44
47
{
45
48
Type: optype,
46
49
Collection: req.Collection,
···
51
54
},
52
55
}, req.SwapCommit)
53
56
if err != nil {
54
-
s.logger.Error("error applying writes", "error", err)
57
+
logger.Error("error applying writes", "error", err)
55
58
return helpers.ServerError(e, nil)
56
59
}
57
60
+10
-7
server/handle_repo_delete_record.go
+10
-7
server/handle_repo_delete_record.go
···
6
6
"github.com/labstack/echo/v4"
7
7
)
8
8
9
-
type ComAtprotoRepoDeleteRecordRequest struct {
9
+
type ComAtprotoRepoDeleteRecordInput struct {
10
10
Repo string `json:"repo" validate:"required,atproto-did"`
11
11
Collection string `json:"collection" validate:"required,atproto-nsid"`
12
12
Rkey string `json:"rkey" validate:"required,atproto-rkey"`
···
15
15
}
16
16
17
17
func (s *Server) handleDeleteRecord(e echo.Context) error {
18
+
ctx := e.Request().Context()
19
+
logger := s.logger.With("name", "handleDeleteRecord")
20
+
18
21
repo := e.Get("repo").(*models.RepoActor)
19
22
20
-
var req ComAtprotoRepoDeleteRecordRequest
23
+
var req ComAtprotoRepoDeleteRecordInput
21
24
if err := e.Bind(&req); err != nil {
22
-
s.logger.Error("error binding", "error", err)
25
+
logger.Error("error binding", "error", err)
23
26
return helpers.ServerError(e, nil)
24
27
}
25
28
26
29
if err := e.Validate(req); err != nil {
27
-
s.logger.Error("error validating", "error", err)
30
+
logger.Error("error validating", "error", err)
28
31
return helpers.InputError(e, nil)
29
32
}
30
33
31
34
if repo.Repo.Did != req.Repo {
32
-
s.logger.Warn("mismatched repo/auth")
35
+
logger.Warn("mismatched repo/auth")
33
36
return helpers.InputError(e, nil)
34
37
}
35
38
36
-
results, err := s.repoman.applyWrites(repo.Repo, []Op{
39
+
results, err := s.repoman.applyWrites(ctx, repo.Repo, []Op{
37
40
{
38
41
Type: OpTypeDelete,
39
42
Collection: req.Collection,
···
42
45
},
43
46
}, req.SwapCommit)
44
47
if err != nil {
45
-
s.logger.Error("error applying writes", "error", err)
48
+
logger.Error("error applying writes", "error", err)
46
49
return helpers.ServerError(e, nil)
47
50
}
48
51
+8
-5
server/handle_repo_describe_repo.go
+8
-5
server/handle_repo_describe_repo.go
···
20
20
}
21
21
22
22
func (s *Server) handleDescribeRepo(e echo.Context) error {
23
+
ctx := e.Request().Context()
24
+
logger := s.logger.With("name", "handleDescribeRepo")
25
+
23
26
did := e.QueryParam("repo")
24
-
repo, err := s.getRepoActorByDid(did)
27
+
repo, err := s.getRepoActorByDid(ctx, did)
25
28
if err != nil {
26
29
if err == gorm.ErrRecordNotFound {
27
30
return helpers.InputError(e, to.StringPtr("RepoNotFound"))
28
31
}
29
32
30
-
s.logger.Error("error looking up repo", "error", err)
33
+
logger.Error("error looking up repo", "error", err)
31
34
return helpers.ServerError(e, nil)
32
35
}
33
36
···
35
38
36
39
diddoc, err := s.passport.FetchDoc(e.Request().Context(), repo.Repo.Did)
37
40
if err != nil {
38
-
s.logger.Error("error fetching diddoc", "error", err)
41
+
logger.Error("error fetching diddoc", "error", err)
39
42
return helpers.ServerError(e, nil)
40
43
}
41
44
···
64
67
}
65
68
66
69
var records []models.Record
67
-
if err := s.db.Raw("SELECT DISTINCT(nsid) FROM records WHERE did = ?", nil, repo.Repo.Did).Scan(&records).Error; err != nil {
68
-
s.logger.Error("error getting collections", "error", err)
70
+
if err := s.db.Raw(ctx, "SELECT DISTINCT(nsid) FROM records WHERE did = ?", nil, repo.Repo.Did).Scan(&records).Error; err != nil {
71
+
logger.Error("error getting collections", "error", err)
69
72
return helpers.ServerError(e, nil)
70
73
}
71
74
+3
-1
server/handle_repo_get_record.go
+3
-1
server/handle_repo_get_record.go
···
14
14
}
15
15
16
16
func (s *Server) handleRepoGetRecord(e echo.Context) error {
17
+
ctx := e.Request().Context()
18
+
17
19
repo := e.QueryParam("repo")
18
20
collection := e.QueryParam("collection")
19
21
rkey := e.QueryParam("rkey")
···
32
34
}
33
35
34
36
var record models.Record
35
-
if err := s.db.Raw("SELECT * FROM records WHERE did = ? AND nsid = ? AND rkey = ?"+cidquery, nil, params...).Scan(&record).Error; err != nil {
37
+
if err := s.db.Raw(ctx, "SELECT * FROM records WHERE did = ? AND nsid = ? AND rkey = ?"+cidquery, nil, params...).Scan(&record).Error; err != nil {
36
38
// TODO: handle error nicely
37
39
return err
38
40
}
+6
-3
server/handle_repo_list_missing_blobs.go
+6
-3
server/handle_repo_list_missing_blobs.go
···
22
22
}
23
23
24
24
func (s *Server) handleListMissingBlobs(e echo.Context) error {
25
+
ctx := e.Request().Context()
26
+
logger := s.logger.With("name", "handleListMissingBlos")
27
+
25
28
urepo := e.Get("repo").(*models.RepoActor)
26
29
27
30
limitStr := e.QueryParam("limit")
···
35
38
}
36
39
37
40
var records []models.Record
38
-
if err := s.db.Raw("SELECT * FROM records WHERE did = ?", nil, urepo.Repo.Did).Scan(&records).Error; err != nil {
39
-
s.logger.Error("failed to get records for listMissingBlobs", "error", err)
41
+
if err := s.db.Raw(ctx, "SELECT * FROM records WHERE did = ?", nil, urepo.Repo.Did).Scan(&records).Error; err != nil {
42
+
logger.Error("failed to get records for listMissingBlobs", "error", err)
40
43
return helpers.ServerError(e, nil)
41
44
}
42
45
···
69
72
}
70
73
71
74
var count int64
72
-
if err := s.db.Raw("SELECT COUNT(*) FROM blobs WHERE did = ? AND cid = ?", nil, urepo.Repo.Did, ref.cid.Bytes()).Scan(&count).Error; err != nil {
75
+
if err := s.db.Raw(ctx, "SELECT COUNT(*) FROM blobs WHERE did = ? AND cid = ?", nil, urepo.Repo.Did, ref.cid.Bytes()).Scan(&count).Error; err != nil {
73
76
continue
74
77
}
75
78
+7
-4
server/handle_repo_list_records.go
+7
-4
server/handle_repo_list_records.go
···
46
46
}
47
47
48
48
func (s *Server) handleListRecords(e echo.Context) error {
49
+
ctx := e.Request().Context()
50
+
logger := s.logger.With("name", "handleListRecords")
51
+
49
52
var req ComAtprotoRepoListRecordsRequest
50
53
if err := e.Bind(&req); err != nil {
51
-
s.logger.Error("could not bind list records request", "error", err)
54
+
logger.Error("could not bind list records request", "error", err)
52
55
return helpers.ServerError(e, nil)
53
56
}
54
57
···
78
81
79
82
did := req.Repo
80
83
if _, err := syntax.ParseDID(did); err != nil {
81
-
actor, err := s.getActorByHandle(req.Repo)
84
+
actor, err := s.getActorByHandle(ctx, req.Repo)
82
85
if err != nil {
83
86
return helpers.InputError(e, to.StringPtr("RepoNotFound"))
84
87
}
···
93
96
params = append(params, limit)
94
97
95
98
var records []models.Record
96
-
if err := s.db.Raw("SELECT * FROM records WHERE did = ? AND nsid = ? "+cursorquery+" ORDER BY created_at "+sort+" limit ?", nil, params...).Scan(&records).Error; err != nil {
97
-
s.logger.Error("error getting records", "error", err)
99
+
if err := s.db.Raw(ctx, "SELECT * FROM records WHERE did = ? AND nsid = ? "+cursorquery+" ORDER BY created_at "+sort+" limit ?", nil, params...).Scan(&records).Error; err != nil {
100
+
logger.Error("error getting records", "error", err)
98
101
return helpers.ServerError(e, nil)
99
102
}
100
103
+3
-1
server/handle_repo_list_repos.go
+3
-1
server/handle_repo_list_repos.go
···
21
21
22
22
// TODO: paginate this bitch
23
23
func (s *Server) handleListRepos(e echo.Context) error {
24
+
ctx := e.Request().Context()
25
+
24
26
var repos []models.Repo
25
-
if err := s.db.Raw("SELECT * FROM repos ORDER BY created_at DESC LIMIT 500", nil).Scan(&repos).Error; err != nil {
27
+
if err := s.db.Raw(ctx, "SELECT * FROM repos ORDER BY created_at DESC LIMIT 500", nil).Scan(&repos).Error; err != nil {
26
28
return err
27
29
}
28
30
+10
-7
server/handle_repo_put_record.go
+10
-7
server/handle_repo_put_record.go
···
6
6
"github.com/labstack/echo/v4"
7
7
)
8
8
9
-
type ComAtprotoRepoPutRecordRequest struct {
9
+
type ComAtprotoRepoPutRecordInput struct {
10
10
Repo string `json:"repo" validate:"required,atproto-did"`
11
11
Collection string `json:"collection" validate:"required,atproto-nsid"`
12
12
Rkey string `json:"rkey" validate:"required,atproto-rkey"`
···
17
17
}
18
18
19
19
func (s *Server) handlePutRecord(e echo.Context) error {
20
+
ctx := e.Request().Context()
21
+
logger := s.logger.With("name", "handlePutRecord")
22
+
20
23
repo := e.Get("repo").(*models.RepoActor)
21
24
22
-
var req ComAtprotoRepoPutRecordRequest
25
+
var req ComAtprotoRepoPutRecordInput
23
26
if err := e.Bind(&req); err != nil {
24
-
s.logger.Error("error binding", "error", err)
27
+
logger.Error("error binding", "error", err)
25
28
return helpers.ServerError(e, nil)
26
29
}
27
30
28
31
if err := e.Validate(req); err != nil {
29
-
s.logger.Error("error validating", "error", err)
32
+
logger.Error("error validating", "error", err)
30
33
return helpers.InputError(e, nil)
31
34
}
32
35
33
36
if repo.Repo.Did != req.Repo {
34
-
s.logger.Warn("mismatched repo/auth")
37
+
logger.Warn("mismatched repo/auth")
35
38
return helpers.InputError(e, nil)
36
39
}
37
40
···
40
43
optype = OpTypeUpdate
41
44
}
42
45
43
-
results, err := s.repoman.applyWrites(repo.Repo, []Op{
46
+
results, err := s.repoman.applyWrites(ctx, repo.Repo, []Op{
44
47
{
45
48
Type: optype,
46
49
Collection: req.Collection,
···
51
54
},
52
55
}, req.SwapCommit)
53
56
if err != nil {
54
-
s.logger.Error("error applying writes", "error", err)
57
+
logger.Error("error applying writes", "error", err)
55
58
return helpers.ServerError(e, nil)
56
59
}
57
60
+13
-10
server/handle_repo_upload_blob.go
+13
-10
server/handle_repo_upload_blob.go
···
32
32
}
33
33
34
34
func (s *Server) handleRepoUploadBlob(e echo.Context) error {
35
+
ctx := e.Request().Context()
36
+
logger := s.logger.With("name", "handleRepoUploadBlob")
37
+
35
38
urepo := e.Get("repo").(*models.RepoActor)
36
39
37
40
mime := e.Request().Header.Get("content-type")
···
51
54
Storage: storage,
52
55
}
53
56
54
-
if err := s.db.Create(&blob, nil).Error; err != nil {
55
-
s.logger.Error("error creating new blob in db", "error", err)
57
+
if err := s.db.Create(ctx, &blob, nil).Error; err != nil {
58
+
logger.Error("error creating new blob in db", "error", err)
56
59
return helpers.ServerError(e, nil)
57
60
}
58
61
···
69
72
break
70
73
}
71
74
} else if err != nil && err != io.ErrUnexpectedEOF {
72
-
s.logger.Error("error reading blob", "error", err)
75
+
logger.Error("error reading blob", "error", err)
73
76
return helpers.ServerError(e, nil)
74
77
}
75
78
···
84
87
Data: data,
85
88
}
86
89
87
-
if err := s.db.Create(&blobPart, nil).Error; err != nil {
88
-
s.logger.Error("error adding blob part to db", "error", err)
90
+
if err := s.db.Create(ctx, &blobPart, nil).Error; err != nil {
91
+
logger.Error("error adding blob part to db", "error", err)
89
92
return helpers.ServerError(e, nil)
90
93
}
91
94
}
···
98
101
99
102
c, err := cid.NewPrefixV1(cid.Raw, multihash.SHA2_256).Sum(fulldata.Bytes())
100
103
if err != nil {
101
-
s.logger.Error("error creating cid prefix", "error", err)
104
+
logger.Error("error creating cid prefix", "error", err)
102
105
return helpers.ServerError(e, nil)
103
106
}
104
107
···
115
118
116
119
sess, err := session.NewSession(config)
117
120
if err != nil {
118
-
s.logger.Error("error creating aws session", "error", err)
121
+
logger.Error("error creating aws session", "error", err)
119
122
return helpers.ServerError(e, nil)
120
123
}
121
124
···
126
129
Key: aws.String(fmt.Sprintf("blobs/%s/%s", urepo.Repo.Did, c.String())),
127
130
Body: bytes.NewReader(fulldata.Bytes()),
128
131
}); err != nil {
129
-
s.logger.Error("error uploading blob to s3", "error", err)
132
+
logger.Error("error uploading blob to s3", "error", err)
130
133
return helpers.ServerError(e, nil)
131
134
}
132
135
}
133
136
134
-
if err := s.db.Exec("UPDATE blobs SET cid = ? WHERE id = ?", nil, c.Bytes(), blob.ID).Error; err != nil {
137
+
if err := s.db.Exec(ctx, "UPDATE blobs SET cid = ? WHERE id = ?", nil, c.Bytes(), blob.ID).Error; err != nil {
135
138
// there should probably be somme handling here if this fails...
136
-
s.logger.Error("error updating blob", "error", err)
139
+
logger.Error("error updating blob", "error", err)
137
140
return helpers.ServerError(e, nil)
138
141
}
139
142
+6
-3
server/handle_server_activate_account.go
+6
-3
server/handle_server_activate_account.go
···
18
18
}
19
19
20
20
func (s *Server) handleServerActivateAccount(e echo.Context) error {
21
+
ctx := e.Request().Context()
22
+
logger := s.logger.With("name", "handleServerActivateAccount")
23
+
21
24
var req ComAtprotoServerDeactivateAccountRequest
22
25
if err := e.Bind(&req); err != nil {
23
-
s.logger.Error("error binding", "error", err)
26
+
logger.Error("error binding", "error", err)
24
27
return helpers.ServerError(e, nil)
25
28
}
26
29
27
30
urepo := e.Get("repo").(*models.RepoActor)
28
31
29
-
if err := s.db.Exec("UPDATE repos SET deactivated = ? WHERE did = ?", nil, false, urepo.Repo.Did).Error; err != nil {
30
-
s.logger.Error("error updating account status to deactivated", "error", err)
32
+
if err := s.db.Exec(ctx, "UPDATE repos SET deactivated = ? WHERE did = ?", nil, false, urepo.Repo.Did).Error; err != nil {
33
+
logger.Error("error updating account status to deactivated", "error", err)
31
34
return helpers.ServerError(e, nil)
32
35
}
33
36
+10
-7
server/handle_server_check_account_status.go
+10
-7
server/handle_server_check_account_status.go
···
20
20
}
21
21
22
22
func (s *Server) handleServerCheckAccountStatus(e echo.Context) error {
23
+
ctx := e.Request().Context()
24
+
logger := s.logger.With("name", "handleServerCheckAccountStatus")
25
+
23
26
urepo := e.Get("repo").(*models.RepoActor)
24
27
25
28
resp := ComAtprotoServerCheckAccountStatusResponse{
···
31
34
32
35
rootcid, err := cid.Cast(urepo.Root)
33
36
if err != nil {
34
-
s.logger.Error("error casting cid", "error", err)
37
+
logger.Error("error casting cid", "error", err)
35
38
return helpers.ServerError(e, nil)
36
39
}
37
40
resp.RepoCommit = rootcid.String()
···
41
44
}
42
45
43
46
var blockCtResp CountResp
44
-
if err := s.db.Raw("SELECT COUNT(*) AS ct FROM blocks WHERE did = ?", nil, urepo.Repo.Did).Scan(&blockCtResp).Error; err != nil {
45
-
s.logger.Error("error getting block count", "error", err)
47
+
if err := s.db.Raw(ctx, "SELECT COUNT(*) AS ct FROM blocks WHERE did = ?", nil, urepo.Repo.Did).Scan(&blockCtResp).Error; err != nil {
48
+
logger.Error("error getting block count", "error", err)
46
49
return helpers.ServerError(e, nil)
47
50
}
48
51
resp.RepoBlocks = blockCtResp.Ct
49
52
50
53
var recCtResp CountResp
51
-
if err := s.db.Raw("SELECT COUNT(*) AS ct FROM records WHERE did = ?", nil, urepo.Repo.Did).Scan(&recCtResp).Error; err != nil {
52
-
s.logger.Error("error getting record count", "error", err)
54
+
if err := s.db.Raw(ctx, "SELECT COUNT(*) AS ct FROM records WHERE did = ?", nil, urepo.Repo.Did).Scan(&recCtResp).Error; err != nil {
55
+
logger.Error("error getting record count", "error", err)
53
56
return helpers.ServerError(e, nil)
54
57
}
55
58
resp.IndexedRecords = recCtResp.Ct
56
59
57
60
var blobCtResp CountResp
58
-
if err := s.db.Raw("SELECT COUNT(*) AS ct FROM blobs WHERE did = ?", nil, urepo.Repo.Did).Scan(&blobCtResp).Error; err != nil {
59
-
s.logger.Error("error getting record count", "error", err)
61
+
if err := s.db.Raw(ctx, "SELECT COUNT(*) AS ct FROM blobs WHERE did = ?", nil, urepo.Repo.Did).Scan(&blobCtResp).Error; err != nil {
62
+
logger.Error("error getting record count", "error", err)
60
63
return helpers.ServerError(e, nil)
61
64
}
62
65
resp.ExpectedBlobs = blobCtResp.Ct
+6
-3
server/handle_server_confirm_email.go
+6
-3
server/handle_server_confirm_email.go
···
15
15
}
16
16
17
17
func (s *Server) handleServerConfirmEmail(e echo.Context) error {
18
+
ctx := e.Request().Context()
19
+
logger := s.logger.With("name", "handleServerConfirmEmail")
20
+
18
21
urepo := e.Get("repo").(*models.RepoActor)
19
22
20
23
var req ComAtprotoServerConfirmEmailRequest
21
24
if err := e.Bind(&req); err != nil {
22
-
s.logger.Error("error binding", "error", err)
25
+
logger.Error("error binding", "error", err)
23
26
return helpers.ServerError(e, nil)
24
27
}
25
28
···
41
44
42
45
now := time.Now().UTC()
43
46
44
-
if err := s.db.Exec("UPDATE repos SET email_verification_code = NULL, email_verification_code_expires_at = NULL, email_confirmed_at = ? WHERE did = ?", nil, now, urepo.Repo.Did).Error; err != nil {
45
-
s.logger.Error("error updating user", "error", err)
47
+
if err := s.db.Exec(ctx, "UPDATE repos SET email_verification_code = NULL, email_verification_code_expires_at = NULL, email_confirmed_at = ? WHERE did = ?", nil, now, urepo.Repo.Did).Error; err != nil {
48
+
logger.Error("error updating user", "error", err)
46
49
return helpers.ServerError(e, nil)
47
50
}
48
51
+39
-36
server/handle_server_create_account.go
+39
-36
server/handle_server_create_account.go
···
36
36
}
37
37
38
38
func (s *Server) handleCreateAccount(e echo.Context) error {
39
+
ctx := e.Request().Context()
40
+
logger := s.logger.With("name", "handleServerCreateAccount")
41
+
39
42
var request ComAtprotoServerCreateAccountRequest
40
43
41
44
if err := e.Bind(&request); err != nil {
42
-
s.logger.Error("error receiving request", "endpoint", "com.atproto.server.createAccount", "error", err)
45
+
logger.Error("error receiving request", "endpoint", "com.atproto.server.createAccount", "error", err)
43
46
return helpers.ServerError(e, nil)
44
47
}
45
48
46
49
request.Handle = strings.ToLower(request.Handle)
47
50
48
51
if err := e.Validate(request); err != nil {
49
-
s.logger.Error("error validating request", "endpoint", "com.atproto.server.createAccount", "error", err)
52
+
logger.Error("error validating request", "endpoint", "com.atproto.server.createAccount", "error", err)
50
53
51
54
var verr ValidationError
52
55
if errors.As(err, &verr) {
···
68
71
}
69
72
}
70
73
}
71
-
74
+
72
75
var signupDid string
73
76
if request.Did != nil {
74
-
signupDid = *request.Did;
75
-
77
+
signupDid = *request.Did
78
+
76
79
token := strings.TrimSpace(strings.Replace(e.Request().Header.Get("authorization"), "Bearer ", "", 1))
77
80
if token == "" {
78
81
return helpers.UnauthorizedError(e, to.StringPtr("must authenticate to use an existing did"))
···
80
83
authDid, err := s.validateServiceAuth(e.Request().Context(), token, "com.atproto.server.createAccount")
81
84
82
85
if err != nil {
83
-
s.logger.Warn("error validating authorization token", "endpoint", "com.atproto.server.createAccount", "error", err)
86
+
logger.Warn("error validating authorization token", "endpoint", "com.atproto.server.createAccount", "error", err)
84
87
return helpers.UnauthorizedError(e, to.StringPtr("invalid authorization token"))
85
88
}
86
89
···
90
93
}
91
94
92
95
// see if the handle is already taken
93
-
actor, err := s.getActorByHandle(request.Handle)
96
+
actor, err := s.getActorByHandle(ctx, request.Handle)
94
97
if err != nil && err != gorm.ErrRecordNotFound {
95
-
s.logger.Error("error looking up handle in db", "endpoint", "com.atproto.server.createAccount", "error", err)
98
+
logger.Error("error looking up handle in db", "endpoint", "com.atproto.server.createAccount", "error", err)
96
99
return helpers.ServerError(e, nil)
97
100
}
98
101
if err == nil && actor.Did != signupDid {
···
109
112
return helpers.InputError(e, to.StringPtr("InvalidInviteCode"))
110
113
}
111
114
112
-
if err := s.db.Raw("SELECT * FROM invite_codes WHERE code = ?", nil, request.InviteCode).Scan(&ic).Error; err != nil {
115
+
if err := s.db.Raw(ctx, "SELECT * FROM invite_codes WHERE code = ?", nil, request.InviteCode).Scan(&ic).Error; err != nil {
113
116
if err == gorm.ErrRecordNotFound {
114
117
return helpers.InputError(e, to.StringPtr("InvalidInviteCode"))
115
118
}
116
-
s.logger.Error("error getting invite code from db", "error", err)
119
+
logger.Error("error getting invite code from db", "error", err)
117
120
return helpers.ServerError(e, nil)
118
121
}
119
122
···
123
126
}
124
127
125
128
// see if the email is already taken
126
-
existingRepo, err := s.getRepoByEmail(request.Email)
129
+
existingRepo, err := s.getRepoByEmail(ctx, request.Email)
127
130
if err != nil && err != gorm.ErrRecordNotFound {
128
-
s.logger.Error("error looking up email in db", "endpoint", "com.atproto.server.createAccount", "error", err)
131
+
logger.Error("error looking up email in db", "endpoint", "com.atproto.server.createAccount", "error", err)
129
132
return helpers.ServerError(e, nil)
130
133
}
131
134
if err == nil && existingRepo.Did != signupDid {
···
137
140
var k *atcrypto.PrivateKeyK256
138
141
139
142
if signupDid != "" {
140
-
reservedKey, err := s.getReservedKey(signupDid)
143
+
reservedKey, err := s.getReservedKey(ctx, signupDid)
141
144
if err != nil {
142
-
s.logger.Error("error looking up reserved key", "error", err)
145
+
logger.Error("error looking up reserved key", "error", err)
143
146
}
144
147
if reservedKey != nil {
145
148
k, err = atcrypto.ParsePrivateBytesK256(reservedKey.PrivateKey)
146
149
if err != nil {
147
-
s.logger.Error("error parsing reserved key", "error", err)
150
+
logger.Error("error parsing reserved key", "error", err)
148
151
k = nil
149
152
} else {
150
153
defer func() {
151
-
if delErr := s.deleteReservedKey(reservedKey.KeyDid, reservedKey.Did); delErr != nil {
152
-
s.logger.Error("error deleting reserved key", "error", delErr)
154
+
if delErr := s.deleteReservedKey(ctx, reservedKey.KeyDid, reservedKey.Did); delErr != nil {
155
+
logger.Error("error deleting reserved key", "error", delErr)
153
156
}
154
157
}()
155
158
}
···
159
162
if k == nil {
160
163
k, err = atcrypto.GeneratePrivateKeyK256()
161
164
if err != nil {
162
-
s.logger.Error("error creating signing key", "endpoint", "com.atproto.server.createAccount", "error", err)
165
+
logger.Error("error creating signing key", "endpoint", "com.atproto.server.createAccount", "error", err)
163
166
return helpers.ServerError(e, nil)
164
167
}
165
168
}
···
167
170
if signupDid == "" {
168
171
did, op, err := s.plcClient.CreateDID(k, "", request.Handle)
169
172
if err != nil {
170
-
s.logger.Error("error creating operation", "endpoint", "com.atproto.server.createAccount", "error", err)
173
+
logger.Error("error creating operation", "endpoint", "com.atproto.server.createAccount", "error", err)
171
174
return helpers.ServerError(e, nil)
172
175
}
173
176
174
177
if err := s.plcClient.SendOperation(e.Request().Context(), did, op); err != nil {
175
-
s.logger.Error("error sending plc op", "endpoint", "com.atproto.server.createAccount", "error", err)
178
+
logger.Error("error sending plc op", "endpoint", "com.atproto.server.createAccount", "error", err)
176
179
return helpers.ServerError(e, nil)
177
180
}
178
181
signupDid = did
···
180
183
181
184
hashed, err := bcrypt.GenerateFromPassword([]byte(request.Password), 10)
182
185
if err != nil {
183
-
s.logger.Error("error hashing password", "error", err)
186
+
logger.Error("error hashing password", "error", err)
184
187
return helpers.ServerError(e, nil)
185
188
}
186
189
···
199
202
Handle: request.Handle,
200
203
}
201
204
202
-
if err := s.db.Create(&urepo, nil).Error; err != nil {
203
-
s.logger.Error("error inserting new repo", "error", err)
205
+
if err := s.db.Create(ctx, &urepo, nil).Error; err != nil {
206
+
logger.Error("error inserting new repo", "error", err)
204
207
return helpers.ServerError(e, nil)
205
208
}
206
-
207
-
if err := s.db.Create(&actor, nil).Error; err != nil {
208
-
s.logger.Error("error inserting new actor", "error", err)
209
+
210
+
if err := s.db.Create(ctx, &actor, nil).Error; err != nil {
211
+
logger.Error("error inserting new actor", "error", err)
209
212
return helpers.ServerError(e, nil)
210
213
}
211
214
} else {
212
-
if err := s.db.Save(&actor, nil).Error; err != nil {
213
-
s.logger.Error("error inserting new actor", "error", err)
215
+
if err := s.db.Save(ctx, &actor, nil).Error; err != nil {
216
+
logger.Error("error inserting new actor", "error", err)
214
217
return helpers.ServerError(e, nil)
215
218
}
216
219
}
···
221
224
222
225
root, rev, err := r.Commit(context.TODO(), urepo.SignFor)
223
226
if err != nil {
224
-
s.logger.Error("error committing", "error", err)
227
+
logger.Error("error committing", "error", err)
225
228
return helpers.ServerError(e, nil)
226
229
}
227
230
228
231
if err := s.UpdateRepo(context.TODO(), urepo.Did, root, rev); err != nil {
229
-
s.logger.Error("error updating repo after commit", "error", err)
232
+
logger.Error("error updating repo after commit", "error", err)
230
233
return helpers.ServerError(e, nil)
231
234
}
232
235
···
241
244
}
242
245
243
246
if s.config.RequireInvite {
244
-
if err := s.db.Raw("UPDATE invite_codes SET remaining_use_count = remaining_use_count - 1 WHERE code = ?", nil, request.InviteCode).Scan(&ic).Error; err != nil {
245
-
s.logger.Error("error decrementing use count", "error", err)
247
+
if err := s.db.Raw(ctx, "UPDATE invite_codes SET remaining_use_count = remaining_use_count - 1 WHERE code = ?", nil, request.InviteCode).Scan(&ic).Error; err != nil {
248
+
logger.Error("error decrementing use count", "error", err)
246
249
return helpers.ServerError(e, nil)
247
250
}
248
251
}
249
252
250
-
sess, err := s.createSession(&urepo)
253
+
sess, err := s.createSession(ctx, &urepo)
251
254
if err != nil {
252
-
s.logger.Error("error creating new session", "error", err)
255
+
logger.Error("error creating new session", "error", err)
253
256
return helpers.ServerError(e, nil)
254
257
}
255
258
256
259
go func() {
257
260
if err := s.sendEmailVerification(urepo.Email, actor.Handle, *urepo.EmailVerificationCode); err != nil {
258
-
s.logger.Error("error sending email verification email", "error", err)
261
+
logger.Error("error sending email verification email", "error", err)
259
262
}
260
263
if err := s.sendWelcomeMail(urepo.Email, actor.Handle); err != nil {
261
-
s.logger.Error("error sending welcome email", "error", err)
264
+
logger.Error("error sending welcome email", "error", err)
262
265
}
263
266
}()
264
267
+7
-4
server/handle_server_create_invite_code.go
+7
-4
server/handle_server_create_invite_code.go
···
17
17
}
18
18
19
19
func (s *Server) handleCreateInviteCode(e echo.Context) error {
20
+
ctx := e.Request().Context()
21
+
logger := s.logger.With("name", "handleServerCreateInviteCode")
22
+
20
23
var req ComAtprotoServerCreateInviteCodeRequest
21
24
if err := e.Bind(&req); err != nil {
22
-
s.logger.Error("error binding", "error", err)
25
+
logger.Error("error binding", "error", err)
23
26
return helpers.ServerError(e, nil)
24
27
}
25
28
26
29
if err := e.Validate(req); err != nil {
27
-
s.logger.Error("error validating", "error", err)
30
+
logger.Error("error validating", "error", err)
28
31
return helpers.InputError(e, nil)
29
32
}
30
33
···
37
40
acc = *req.ForAccount
38
41
}
39
42
40
-
if err := s.db.Create(&models.InviteCode{
43
+
if err := s.db.Create(ctx, &models.InviteCode{
41
44
Code: ic,
42
45
Did: acc,
43
46
RemainingUseCount: req.UseCount,
44
47
}, nil).Error; err != nil {
45
-
s.logger.Error("error creating invite code", "error", err)
48
+
logger.Error("error creating invite code", "error", err)
46
49
return helpers.ServerError(e, nil)
47
50
}
48
51
+7
-4
server/handle_server_create_invite_codes.go
+7
-4
server/handle_server_create_invite_codes.go
···
22
22
}
23
23
24
24
func (s *Server) handleCreateInviteCodes(e echo.Context) error {
25
+
ctx := e.Request().Context()
26
+
logger := s.logger.With("name", "handleServerCreateInviteCodes")
27
+
25
28
var req ComAtprotoServerCreateInviteCodesRequest
26
29
if err := e.Bind(&req); err != nil {
27
-
s.logger.Error("error binding", "error", err)
30
+
logger.Error("error binding", "error", err)
28
31
return helpers.ServerError(e, nil)
29
32
}
30
33
31
34
if err := e.Validate(req); err != nil {
32
-
s.logger.Error("error validating", "error", err)
35
+
logger.Error("error validating", "error", err)
33
36
return helpers.InputError(e, nil)
34
37
}
35
38
···
50
53
ic := uuid.NewString()
51
54
ics = append(ics, ic)
52
55
53
-
if err := s.db.Create(&models.InviteCode{
56
+
if err := s.db.Create(ctx, &models.InviteCode{
54
57
Code: ic,
55
58
Did: did,
56
59
RemainingUseCount: req.UseCount,
57
60
}, nil).Error; err != nil {
58
-
s.logger.Error("error creating invite code", "error", err)
61
+
logger.Error("error creating invite code", "error", err)
59
62
return helpers.ServerError(e, nil)
60
63
}
61
64
}
+65
-9
server/handle_server_create_session.go
+65
-9
server/handle_server_create_session.go
···
1
1
package server
2
2
3
3
import (
4
+
"context"
4
5
"errors"
6
+
"fmt"
5
7
"strings"
8
+
"time"
6
9
7
10
"github.com/Azure/go-autorest/autorest/to"
8
11
"github.com/bluesky-social/indigo/atproto/syntax"
···
32
35
}
33
36
34
37
func (s *Server) handleCreateSession(e echo.Context) error {
38
+
ctx := e.Request().Context()
39
+
logger := s.logger.With("name", "handleServerCreateSession")
40
+
35
41
var req ComAtprotoServerCreateSessionRequest
36
42
if err := e.Bind(&req); err != nil {
37
-
s.logger.Error("error binding request", "endpoint", "com.atproto.server.serverCreateSession", "error", err)
43
+
logger.Error("error binding request", "endpoint", "com.atproto.server.serverCreateSession", "error", err)
38
44
return helpers.ServerError(e, nil)
39
45
}
40
46
···
65
71
var err error
66
72
switch idtype {
67
73
case "did":
68
-
err = s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.did = ?", nil, req.Identifier).Scan(&repo).Error
74
+
err = s.db.Raw(ctx, "SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.did = ?", nil, req.Identifier).Scan(&repo).Error
69
75
case "handle":
70
-
err = s.db.Raw("SELECT r.*, a.* FROM actors a LEFT JOIN repos r ON a.did = r.did WHERE a.handle = ?", nil, req.Identifier).Scan(&repo).Error
76
+
err = s.db.Raw(ctx, "SELECT r.*, a.* FROM actors a LEFT JOIN repos r ON a.did = r.did WHERE a.handle = ?", nil, req.Identifier).Scan(&repo).Error
71
77
case "email":
72
-
err = s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.email = ?", nil, req.Identifier).Scan(&repo).Error
78
+
err = s.db.Raw(ctx, "SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.email = ?", nil, req.Identifier).Scan(&repo).Error
73
79
}
74
80
75
81
if err != nil {
···
77
83
return helpers.InputError(e, to.StringPtr("InvalidRequest"))
78
84
}
79
85
80
-
s.logger.Error("erorr looking up repo", "endpoint", "com.atproto.server.createSession", "error", err)
86
+
logger.Error("erorr looking up repo", "endpoint", "com.atproto.server.createSession", "error", err)
81
87
return helpers.ServerError(e, nil)
82
88
}
83
89
84
90
if err := bcrypt.CompareHashAndPassword([]byte(repo.Password), []byte(req.Password)); err != nil {
85
91
if err != bcrypt.ErrMismatchedHashAndPassword {
86
-
s.logger.Error("erorr comparing hash and password", "error", err)
92
+
logger.Error("erorr comparing hash and password", "error", err)
87
93
}
88
94
return helpers.InputError(e, to.StringPtr("InvalidRequest"))
89
95
}
90
96
91
-
sess, err := s.createSession(&repo.Repo)
97
+
// if repo requires 2FA token and one hasn't been provided, return error prompting for one
98
+
if repo.TwoFactorType != models.TwoFactorTypeNone && (req.AuthFactorToken == nil || *req.AuthFactorToken == "") {
99
+
err = s.createAndSendTwoFactorCode(ctx, repo)
100
+
if err != nil {
101
+
logger.Error("sending 2FA code", "error", err)
102
+
return helpers.ServerError(e, nil)
103
+
}
104
+
105
+
return helpers.InputError(e, to.StringPtr("AuthFactorTokenRequired"))
106
+
}
107
+
108
+
// if 2FA is required, now check that the one provided is valid
109
+
if repo.TwoFactorType != models.TwoFactorTypeNone {
110
+
if repo.TwoFactorCode == nil || repo.TwoFactorCodeExpiresAt == nil {
111
+
err = s.createAndSendTwoFactorCode(ctx, repo)
112
+
if err != nil {
113
+
logger.Error("sending 2FA code", "error", err)
114
+
return helpers.ServerError(e, nil)
115
+
}
116
+
117
+
return helpers.InputError(e, to.StringPtr("AuthFactorTokenRequired"))
118
+
}
119
+
120
+
if *repo.TwoFactorCode != *req.AuthFactorToken {
121
+
return helpers.InvalidTokenError(e)
122
+
}
123
+
124
+
if time.Now().UTC().After(*repo.TwoFactorCodeExpiresAt) {
125
+
return helpers.ExpiredTokenError(e)
126
+
}
127
+
}
128
+
129
+
sess, err := s.createSession(ctx, &repo.Repo)
92
130
if err != nil {
93
-
s.logger.Error("error creating session", "error", err)
131
+
logger.Error("error creating session", "error", err)
94
132
return helpers.ServerError(e, nil)
95
133
}
96
134
···
101
139
Did: repo.Repo.Did,
102
140
Email: repo.Email,
103
141
EmailConfirmed: repo.EmailConfirmedAt != nil,
104
-
EmailAuthFactor: false,
142
+
EmailAuthFactor: repo.TwoFactorType != models.TwoFactorTypeNone,
105
143
Active: repo.Active(),
106
144
Status: repo.Status(),
107
145
})
108
146
}
147
+
148
+
func (s *Server) createAndSendTwoFactorCode(ctx context.Context, repo models.RepoActor) error {
149
+
// TODO: when implementing a new type of 2FA there should be some logic in here to send the
150
+
// right type of code
151
+
152
+
code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
153
+
eat := time.Now().Add(10 * time.Minute).UTC()
154
+
155
+
if err := s.db.Exec(ctx, "UPDATE repos SET two_factor_code = ?, two_factor_code_expires_at = ? WHERE did = ?", nil, code, eat, repo.Repo.Did).Error; err != nil {
156
+
return fmt.Errorf("updating repo: %w", err)
157
+
}
158
+
159
+
if err := s.sendTwoFactorCode(repo.Email, repo.Handle, code); err != nil {
160
+
return fmt.Errorf("sending email: %w", err)
161
+
}
162
+
163
+
return nil
164
+
}
+6
-3
server/handle_server_deactivate_account.go
+6
-3
server/handle_server_deactivate_account.go
···
19
19
}
20
20
21
21
func (s *Server) handleServerDeactivateAccount(e echo.Context) error {
22
+
ctx := e.Request().Context()
23
+
logger := s.logger.With("name", "handleServerDeactivateAccount")
24
+
22
25
var req ComAtprotoServerDeactivateAccountRequest
23
26
if err := e.Bind(&req); err != nil {
24
-
s.logger.Error("error binding", "error", err)
27
+
logger.Error("error binding", "error", err)
25
28
return helpers.ServerError(e, nil)
26
29
}
27
30
28
31
urepo := e.Get("repo").(*models.RepoActor)
29
32
30
-
if err := s.db.Exec("UPDATE repos SET deactivated = ? WHERE did = ?", nil, true, urepo.Repo.Did).Error; err != nil {
31
-
s.logger.Error("error updating account status to deactivated", "error", err)
33
+
if err := s.db.Exec(ctx, "UPDATE repos SET deactivated = ? WHERE did = ?", nil, true, urepo.Repo.Did).Error; err != nil {
34
+
logger.Error("error updating account status to deactivated", "error", err)
32
35
return helpers.ServerError(e, nil)
33
36
}
34
37
+34
-29
server/handle_server_delete_account.go
+34
-29
server/handle_server_delete_account.go
···
20
20
}
21
21
22
22
func (s *Server) handleServerDeleteAccount(e echo.Context) error {
23
+
ctx := e.Request().Context()
24
+
logger := s.logger.With("name", "handleServerDeleteAccount")
25
+
23
26
var req ComAtprotoServerDeleteAccountRequest
24
27
if err := e.Bind(&req); err != nil {
25
-
s.logger.Error("error binding", "error", err)
28
+
logger.Error("error binding", "error", err)
26
29
return helpers.ServerError(e, nil)
27
30
}
28
31
29
32
if err := e.Validate(&req); err != nil {
30
-
s.logger.Error("error validating", "error", err)
33
+
logger.Error("error validating", "error", err)
31
34
return helpers.ServerError(e, nil)
32
35
}
33
36
34
-
urepo, err := s.getRepoActorByDid(req.Did)
37
+
urepo, err := s.getRepoActorByDid(ctx, req.Did)
35
38
if err != nil {
36
-
s.logger.Error("error getting repo", "error", err)
39
+
logger.Error("error getting repo", "error", err)
37
40
return echo.NewHTTPError(400, "account not found")
38
41
}
39
42
40
43
if err := bcrypt.CompareHashAndPassword([]byte(urepo.Repo.Password), []byte(req.Password)); err != nil {
41
-
s.logger.Error("password mismatch", "error", err)
44
+
logger.Error("password mismatch", "error", err)
42
45
return echo.NewHTTPError(401, "Invalid did or password")
43
46
}
44
47
45
48
if urepo.Repo.AccountDeleteCode == nil || urepo.Repo.AccountDeleteCodeExpiresAt == nil {
46
-
s.logger.Error("no deletion token found for account")
49
+
logger.Error("no deletion token found for account")
47
50
return echo.NewHTTPError(400, map[string]interface{}{
48
51
"error": "InvalidToken",
49
52
"message": "Token is invalid",
···
51
54
}
52
55
53
56
if *urepo.Repo.AccountDeleteCode != req.Token {
54
-
s.logger.Error("deletion token mismatch")
57
+
logger.Error("deletion token mismatch")
55
58
return echo.NewHTTPError(400, map[string]interface{}{
56
59
"error": "InvalidToken",
57
60
"message": "Token is invalid",
···
59
62
}
60
63
61
64
if time.Now().UTC().After(*urepo.Repo.AccountDeleteCodeExpiresAt) {
62
-
s.logger.Error("deletion token expired")
65
+
logger.Error("deletion token expired")
63
66
return echo.NewHTTPError(400, map[string]interface{}{
64
67
"error": "ExpiredToken",
65
68
"message": "Token is expired",
66
69
})
67
70
}
68
71
69
-
tx := s.db.BeginDangerously()
72
+
tx := s.db.BeginDangerously(ctx)
70
73
if tx.Error != nil {
71
-
s.logger.Error("error starting transaction", "error", tx.Error)
74
+
logger.Error("error starting transaction", "error", tx.Error)
72
75
return helpers.ServerError(e, nil)
73
76
}
77
+
78
+
status := "error"
79
+
func() {
80
+
if status == "error" {
81
+
if err := tx.Rollback().Error; err != nil {
82
+
logger.Error("error rolling back after delete failure", "err", err)
83
+
}
84
+
}
85
+
}()
74
86
75
87
if err := tx.Exec("DELETE FROM blocks WHERE did = ?", nil, req.Did).Error; err != nil {
76
-
tx.Rollback()
77
-
s.logger.Error("error deleting blocks", "error", err)
88
+
logger.Error("error deleting blocks", "error", err)
78
89
return helpers.ServerError(e, nil)
79
90
}
80
91
81
92
if err := tx.Exec("DELETE FROM records WHERE did = ?", nil, req.Did).Error; err != nil {
82
-
tx.Rollback()
83
-
s.logger.Error("error deleting records", "error", err)
93
+
logger.Error("error deleting records", "error", err)
84
94
return helpers.ServerError(e, nil)
85
95
}
86
96
87
97
if err := tx.Exec("DELETE FROM blobs WHERE did = ?", nil, req.Did).Error; err != nil {
88
-
tx.Rollback()
89
-
s.logger.Error("error deleting blobs", "error", err)
98
+
logger.Error("error deleting blobs", "error", err)
90
99
return helpers.ServerError(e, nil)
91
100
}
92
101
93
102
if err := tx.Exec("DELETE FROM tokens WHERE did = ?", nil, req.Did).Error; err != nil {
94
-
tx.Rollback()
95
-
s.logger.Error("error deleting tokens", "error", err)
103
+
logger.Error("error deleting tokens", "error", err)
96
104
return helpers.ServerError(e, nil)
97
105
}
98
106
99
107
if err := tx.Exec("DELETE FROM refresh_tokens WHERE did = ?", nil, req.Did).Error; err != nil {
100
-
tx.Rollback()
101
-
s.logger.Error("error deleting refresh tokens", "error", err)
108
+
logger.Error("error deleting refresh tokens", "error", err)
102
109
return helpers.ServerError(e, nil)
103
110
}
104
111
105
112
if err := tx.Exec("DELETE FROM reserved_keys WHERE did = ?", nil, req.Did).Error; err != nil {
106
-
tx.Rollback()
107
-
s.logger.Error("error deleting reserved keys", "error", err)
113
+
logger.Error("error deleting reserved keys", "error", err)
108
114
return helpers.ServerError(e, nil)
109
115
}
110
116
111
117
if err := tx.Exec("DELETE FROM invite_codes WHERE did = ?", nil, req.Did).Error; err != nil {
112
-
tx.Rollback()
113
-
s.logger.Error("error deleting invite codes", "error", err)
118
+
logger.Error("error deleting invite codes", "error", err)
114
119
return helpers.ServerError(e, nil)
115
120
}
116
121
117
122
if err := tx.Exec("DELETE FROM actors WHERE did = ?", nil, req.Did).Error; err != nil {
118
-
tx.Rollback()
119
-
s.logger.Error("error deleting actor", "error", err)
123
+
logger.Error("error deleting actor", "error", err)
120
124
return helpers.ServerError(e, nil)
121
125
}
122
126
123
127
if err := tx.Exec("DELETE FROM repos WHERE did = ?", nil, req.Did).Error; err != nil {
124
-
tx.Rollback()
125
-
s.logger.Error("error deleting repo", "error", err)
128
+
logger.Error("error deleting repo", "error", err)
126
129
return helpers.ServerError(e, nil)
127
130
}
128
131
132
+
status = "ok"
133
+
129
134
if err := tx.Commit().Error; err != nil {
130
-
s.logger.Error("error committing transaction", "error", err)
135
+
logger.Error("error committing transaction", "error", err)
131
136
return helpers.ServerError(e, nil)
132
137
}
133
138
+4
-2
server/handle_server_delete_session.go
+4
-2
server/handle_server_delete_session.go
···
7
7
)
8
8
9
9
func (s *Server) handleDeleteSession(e echo.Context) error {
10
+
ctx := e.Request().Context()
11
+
10
12
token := e.Get("token").(string)
11
13
12
14
var acctok models.Token
13
-
if err := s.db.Raw("DELETE FROM tokens WHERE token = ? RETURNING *", nil, token).Scan(&acctok).Error; err != nil {
15
+
if err := s.db.Raw(ctx, "DELETE FROM tokens WHERE token = ? RETURNING *", nil, token).Scan(&acctok).Error; err != nil {
14
16
s.logger.Error("error deleting access token from db", "error", err)
15
17
return helpers.ServerError(e, nil)
16
18
}
17
19
18
-
if err := s.db.Exec("DELETE FROM refresh_tokens WHERE token = ?", nil, acctok.RefreshToken).Error; err != nil {
20
+
if err := s.db.Exec(ctx, "DELETE FROM refresh_tokens WHERE token = ?", nil, acctok.RefreshToken).Error; err != nil {
19
21
s.logger.Error("error deleting refresh token from db", "error", err)
20
22
return helpers.ServerError(e, nil)
21
23
}
+7
-5
server/handle_server_get_service_auth.go
+7
-5
server/handle_server_get_service_auth.go
···
25
25
}
26
26
27
27
func (s *Server) handleServerGetServiceAuth(e echo.Context) error {
28
+
logger := s.logger.With("name", "handleServerGetServiceAuth")
29
+
28
30
var req ServerGetServiceAuthRequest
29
31
if err := e.Bind(&req); err != nil {
30
-
s.logger.Error("could not bind service auth request", "error", err)
32
+
logger.Error("could not bind service auth request", "error", err)
31
33
return helpers.ServerError(e, nil)
32
34
}
33
35
···
64
66
}
65
67
hj, err := json.Marshal(header)
66
68
if err != nil {
67
-
s.logger.Error("error marshaling header", "error", err)
69
+
logger.Error("error marshaling header", "error", err)
68
70
return helpers.ServerError(e, nil)
69
71
}
70
72
···
82
84
}
83
85
pj, err := json.Marshal(payload)
84
86
if err != nil {
85
-
s.logger.Error("error marashaling payload", "error", err)
87
+
logger.Error("error marashaling payload", "error", err)
86
88
return helpers.ServerError(e, nil)
87
89
}
88
90
···
93
95
94
96
sk, err := secp256k1secec.NewPrivateKey(repo.SigningKey)
95
97
if err != nil {
96
-
s.logger.Error("can't load private key", "error", err)
98
+
logger.Error("can't load private key", "error", err)
97
99
return err
98
100
}
99
101
100
102
R, S, _, err := sk.SignRaw(rand.Reader, hash[:])
101
103
if err != nil {
102
-
s.logger.Error("error signing", "error", err)
104
+
logger.Error("error signing", "error", err)
103
105
return helpers.ServerError(e, nil)
104
106
}
105
107
+1
-1
server/handle_server_get_session.go
+1
-1
server/handle_server_get_session.go
+9
-6
server/handle_server_refresh_session.go
+9
-6
server/handle_server_refresh_session.go
···
16
16
}
17
17
18
18
func (s *Server) handleRefreshSession(e echo.Context) error {
19
+
ctx := e.Request().Context()
20
+
logger := s.logger.With("name", "handleServerRefreshSession")
21
+
19
22
token := e.Get("token").(string)
20
23
repo := e.Get("repo").(*models.RepoActor)
21
24
22
-
if err := s.db.Exec("DELETE FROM refresh_tokens WHERE token = ?", nil, token).Error; err != nil {
23
-
s.logger.Error("error getting refresh token from db", "error", err)
25
+
if err := s.db.Exec(ctx, "DELETE FROM refresh_tokens WHERE token = ?", nil, token).Error; err != nil {
26
+
logger.Error("error getting refresh token from db", "error", err)
24
27
return helpers.ServerError(e, nil)
25
28
}
26
29
27
-
if err := s.db.Exec("DELETE FROM tokens WHERE refresh_token = ?", nil, token).Error; err != nil {
28
-
s.logger.Error("error deleting access token from db", "error", err)
30
+
if err := s.db.Exec(ctx, "DELETE FROM tokens WHERE refresh_token = ?", nil, token).Error; err != nil {
31
+
logger.Error("error deleting access token from db", "error", err)
29
32
return helpers.ServerError(e, nil)
30
33
}
31
34
32
-
sess, err := s.createSession(&repo.Repo)
35
+
sess, err := s.createSession(ctx, &repo.Repo)
33
36
if err != nil {
34
-
s.logger.Error("error creating new session for refresh", "error", err)
37
+
logger.Error("error creating new session for refresh", "error", err)
35
38
return helpers.ServerError(e, nil)
36
39
}
37
40
+6
-3
server/handle_server_request_account_delete.go
+6
-3
server/handle_server_request_account_delete.go
···
10
10
)
11
11
12
12
func (s *Server) handleServerRequestAccountDelete(e echo.Context) error {
13
+
ctx := e.Request().Context()
14
+
logger := s.logger.With("name", "handleServerRequestAccountDelete")
15
+
13
16
urepo := e.Get("repo").(*models.RepoActor)
14
17
15
18
token := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
16
19
expiresAt := time.Now().UTC().Add(15 * time.Minute)
17
20
18
-
if err := s.db.Exec("UPDATE repos SET account_delete_code = ?, account_delete_code_expires_at = ? WHERE did = ?", nil, token, expiresAt, urepo.Repo.Did).Error; err != nil {
19
-
s.logger.Error("error setting deletion token", "error", err)
21
+
if err := s.db.Exec(ctx, "UPDATE repos SET account_delete_code = ?, account_delete_code_expires_at = ? WHERE did = ?", nil, token, expiresAt, urepo.Repo.Did).Error; err != nil {
22
+
logger.Error("error setting deletion token", "error", err)
20
23
return helpers.ServerError(e, nil)
21
24
}
22
25
23
26
if urepo.Email != "" {
24
27
if err := s.sendAccountDeleteEmail(urepo.Email, urepo.Actor.Handle, token); err != nil {
25
-
s.logger.Error("error sending account deletion email", "error", err)
28
+
logger.Error("error sending account deletion email", "error", err)
26
29
}
27
30
}
28
31
+6
-3
server/handle_server_request_email_confirmation.go
+6
-3
server/handle_server_request_email_confirmation.go
···
11
11
)
12
12
13
13
func (s *Server) handleServerRequestEmailConfirmation(e echo.Context) error {
14
+
ctx := e.Request().Context()
15
+
logger := s.logger.With("name", "handleServerRequestEmailConfirm")
16
+
14
17
urepo := e.Get("repo").(*models.RepoActor)
15
18
16
19
if urepo.EmailConfirmedAt != nil {
···
20
23
code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
21
24
eat := time.Now().Add(10 * time.Minute).UTC()
22
25
23
-
if err := s.db.Exec("UPDATE repos SET email_verification_code = ?, email_verification_code_expires_at = ? WHERE did = ?", nil, code, eat, urepo.Repo.Did).Error; err != nil {
24
-
s.logger.Error("error updating user", "error", err)
26
+
if err := s.db.Exec(ctx, "UPDATE repos SET email_verification_code = ?, email_verification_code_expires_at = ? WHERE did = ?", nil, code, eat, urepo.Repo.Did).Error; err != nil {
27
+
logger.Error("error updating user", "error", err)
25
28
return helpers.ServerError(e, nil)
26
29
}
27
30
28
31
if err := s.sendEmailVerification(urepo.Email, urepo.Handle, code); err != nil {
29
-
s.logger.Error("error sending mail", "error", err)
32
+
logger.Error("error sending mail", "error", err)
30
33
return helpers.ServerError(e, nil)
31
34
}
32
35
+6
-3
server/handle_server_request_email_update.go
+6
-3
server/handle_server_request_email_update.go
···
14
14
}
15
15
16
16
func (s *Server) handleServerRequestEmailUpdate(e echo.Context) error {
17
+
ctx := e.Request().Context()
18
+
logger := s.logger.With("name", "handleServerRequestEmailUpdate")
19
+
17
20
urepo := e.Get("repo").(*models.RepoActor)
18
21
19
22
if urepo.EmailConfirmedAt != nil {
20
23
code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
21
24
eat := time.Now().Add(10 * time.Minute).UTC()
22
25
23
-
if err := s.db.Exec("UPDATE repos SET email_update_code = ?, email_update_code_expires_at = ? WHERE did = ?", nil, code, eat, urepo.Repo.Did).Error; err != nil {
24
-
s.logger.Error("error updating repo", "error", err)
26
+
if err := s.db.Exec(ctx, "UPDATE repos SET email_update_code = ?, email_update_code_expires_at = ? WHERE did = ?", nil, code, eat, urepo.Repo.Did).Error; err != nil {
27
+
logger.Error("error updating repo", "error", err)
25
28
return helpers.ServerError(e, nil)
26
29
}
27
30
28
31
if err := s.sendEmailUpdate(urepo.Email, urepo.Handle, code); err != nil {
29
-
s.logger.Error("error sending email", "error", err)
32
+
logger.Error("error sending email", "error", err)
30
33
return helpers.ServerError(e, nil)
31
34
}
32
35
}
+7
-4
server/handle_server_request_password_reset.go
+7
-4
server/handle_server_request_password_reset.go
···
14
14
}
15
15
16
16
func (s *Server) handleServerRequestPasswordReset(e echo.Context) error {
17
+
ctx := e.Request().Context()
18
+
logger := s.logger.With("name", "handleServerRequestPasswordReset")
19
+
17
20
urepo, ok := e.Get("repo").(*models.RepoActor)
18
21
if !ok {
19
22
var req ComAtprotoServerRequestPasswordResetRequest
···
25
28
return err
26
29
}
27
30
28
-
murepo, err := s.getRepoActorByEmail(req.Email)
31
+
murepo, err := s.getRepoActorByEmail(ctx, req.Email)
29
32
if err != nil {
30
33
return err
31
34
}
···
36
39
code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
37
40
eat := time.Now().Add(10 * time.Minute).UTC()
38
41
39
-
if err := s.db.Exec("UPDATE repos SET password_reset_code = ?, password_reset_code_expires_at = ? WHERE did = ?", nil, code, eat, urepo.Repo.Did).Error; err != nil {
40
-
s.logger.Error("error updating repo", "error", err)
42
+
if err := s.db.Exec(ctx, "UPDATE repos SET password_reset_code = ?, password_reset_code_expires_at = ? WHERE did = ?", nil, code, eat, urepo.Repo.Did).Error; err != nil {
43
+
logger.Error("error updating repo", "error", err)
41
44
return helpers.ServerError(e, nil)
42
45
}
43
46
44
47
if err := s.sendPasswordReset(urepo.Email, urepo.Handle, code); err != nil {
45
-
s.logger.Error("error sending email", "error", err)
48
+
logger.Error("error sending email", "error", err)
46
49
return helpers.ServerError(e, nil)
47
50
}
48
51
+17
-13
server/handle_server_reserve_signing_key.go
+17
-13
server/handle_server_reserve_signing_key.go
···
1
1
package server
2
2
3
3
import (
4
+
"context"
4
5
"time"
5
6
6
7
"github.com/bluesky-social/indigo/atproto/atcrypto"
···
18
19
}
19
20
20
21
func (s *Server) handleServerReserveSigningKey(e echo.Context) error {
22
+
ctx := e.Request().Context()
23
+
logger := s.logger.With("name", "handleServerReserveSigningKey")
24
+
21
25
var req ServerReserveSigningKeyRequest
22
26
if err := e.Bind(&req); err != nil {
23
-
s.logger.Error("could not bind reserve signing key request", "error", err)
27
+
logger.Error("could not bind reserve signing key request", "error", err)
24
28
return helpers.ServerError(e, nil)
25
29
}
26
30
27
31
if req.Did != nil && *req.Did != "" {
28
32
var existing models.ReservedKey
29
-
if err := s.db.Raw("SELECT * FROM reserved_keys WHERE did = ?", nil, *req.Did).Scan(&existing).Error; err == nil && existing.KeyDid != "" {
33
+
if err := s.db.Raw(ctx, "SELECT * FROM reserved_keys WHERE did = ?", nil, *req.Did).Scan(&existing).Error; err == nil && existing.KeyDid != "" {
30
34
return e.JSON(200, ServerReserveSigningKeyResponse{
31
35
SigningKey: existing.KeyDid,
32
36
})
···
35
39
36
40
k, err := atcrypto.GeneratePrivateKeyK256()
37
41
if err != nil {
38
-
s.logger.Error("error creating signing key", "endpoint", "com.atproto.server.reserveSigningKey", "error", err)
42
+
logger.Error("error creating signing key", "endpoint", "com.atproto.server.reserveSigningKey", "error", err)
39
43
return helpers.ServerError(e, nil)
40
44
}
41
45
42
46
pubKey, err := k.PublicKey()
43
47
if err != nil {
44
-
s.logger.Error("error getting public key", "endpoint", "com.atproto.server.reserveSigningKey", "error", err)
48
+
logger.Error("error getting public key", "endpoint", "com.atproto.server.reserveSigningKey", "error", err)
45
49
return helpers.ServerError(e, nil)
46
50
}
47
51
···
54
58
CreatedAt: time.Now(),
55
59
}
56
60
57
-
if err := s.db.Create(&reservedKey, nil).Error; err != nil {
58
-
s.logger.Error("error storing reserved key", "endpoint", "com.atproto.server.reserveSigningKey", "error", err)
61
+
if err := s.db.Create(ctx, &reservedKey, nil).Error; err != nil {
62
+
logger.Error("error storing reserved key", "endpoint", "com.atproto.server.reserveSigningKey", "error", err)
59
63
return helpers.ServerError(e, nil)
60
64
}
61
65
62
-
s.logger.Info("reserved signing key", "keyDid", keyDid, "forDid", req.Did)
66
+
logger.Info("reserved signing key", "keyDid", keyDid, "forDid", req.Did)
63
67
64
68
return e.JSON(200, ServerReserveSigningKeyResponse{
65
69
SigningKey: keyDid,
66
70
})
67
71
}
68
72
69
-
func (s *Server) getReservedKey(keyDidOrDid string) (*models.ReservedKey, error) {
73
+
func (s *Server) getReservedKey(ctx context.Context, keyDidOrDid string) (*models.ReservedKey, error) {
70
74
var reservedKey models.ReservedKey
71
75
72
-
if err := s.db.Raw("SELECT * FROM reserved_keys WHERE key_did = ?", nil, keyDidOrDid).Scan(&reservedKey).Error; err == nil && reservedKey.KeyDid != "" {
76
+
if err := s.db.Raw(ctx, "SELECT * FROM reserved_keys WHERE key_did = ?", nil, keyDidOrDid).Scan(&reservedKey).Error; err == nil && reservedKey.KeyDid != "" {
73
77
return &reservedKey, nil
74
78
}
75
79
76
-
if err := s.db.Raw("SELECT * FROM reserved_keys WHERE did = ?", nil, keyDidOrDid).Scan(&reservedKey).Error; err == nil && reservedKey.KeyDid != "" {
80
+
if err := s.db.Raw(ctx, "SELECT * FROM reserved_keys WHERE did = ?", nil, keyDidOrDid).Scan(&reservedKey).Error; err == nil && reservedKey.KeyDid != "" {
77
81
return &reservedKey, nil
78
82
}
79
83
80
84
return nil, nil
81
85
}
82
86
83
-
func (s *Server) deleteReservedKey(keyDid string, did *string) error {
84
-
if err := s.db.Exec("DELETE FROM reserved_keys WHERE key_did = ?", nil, keyDid).Error; err != nil {
87
+
func (s *Server) deleteReservedKey(ctx context.Context, keyDid string, did *string) error {
88
+
if err := s.db.Exec(ctx, "DELETE FROM reserved_keys WHERE key_did = ?", nil, keyDid).Error; err != nil {
85
89
return err
86
90
}
87
91
88
92
if did != nil && *did != "" {
89
-
if err := s.db.Exec("DELETE FROM reserved_keys WHERE did = ?", nil, *did).Error; err != nil {
93
+
if err := s.db.Exec(ctx, "DELETE FROM reserved_keys WHERE did = ?", nil, *did).Error; err != nil {
90
94
return err
91
95
}
92
96
}
+7
-4
server/handle_server_reset_password.go
+7
-4
server/handle_server_reset_password.go
···
16
16
}
17
17
18
18
func (s *Server) handleServerResetPassword(e echo.Context) error {
19
+
ctx := e.Request().Context()
20
+
logger := s.logger.With("name", "handleServerResetPassword")
21
+
19
22
urepo := e.Get("repo").(*models.RepoActor)
20
23
21
24
var req ComAtprotoServerResetPasswordRequest
22
25
if err := e.Bind(&req); err != nil {
23
-
s.logger.Error("error binding", "error", err)
26
+
logger.Error("error binding", "error", err)
24
27
return helpers.ServerError(e, nil)
25
28
}
26
29
···
42
45
43
46
hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), 10)
44
47
if err != nil {
45
-
s.logger.Error("error creating hash", "error", err)
48
+
logger.Error("error creating hash", "error", err)
46
49
return helpers.ServerError(e, nil)
47
50
}
48
51
49
-
if err := s.db.Exec("UPDATE repos SET password_reset_code = NULL, password_reset_code_expires_at = NULL, password = ? WHERE did = ?", nil, hash, urepo.Repo.Did).Error; err != nil {
50
-
s.logger.Error("error updating repo", "error", err)
52
+
if err := s.db.Exec(ctx, "UPDATE repos SET password_reset_code = NULL, password_reset_code_expires_at = NULL, password = ? WHERE did = ?", nil, hash, urepo.Repo.Did).Error; err != nil {
53
+
logger.Error("error updating repo", "error", err)
51
54
return helpers.ServerError(e, nil)
52
55
}
53
56
+3
-1
server/handle_server_resolve_handle.go
+3
-1
server/handle_server_resolve_handle.go
···
10
10
)
11
11
12
12
func (s *Server) handleResolveHandle(e echo.Context) error {
13
+
logger := s.logger.With("name", "handleServerResolveHandle")
14
+
13
15
type Resp struct {
14
16
Did string `json:"did"`
15
17
}
···
28
30
ctx := context.WithValue(e.Request().Context(), "skip-cache", true)
29
31
did, err := s.passport.ResolveHandle(ctx, parsed.String())
30
32
if err != nil {
31
-
s.logger.Error("error resolving handle", "error", err)
33
+
logger.Error("error resolving handle", "error", err)
32
34
return helpers.ServerError(e, nil)
33
35
}
34
36
+34
-9
server/handle_server_update_email.go
+34
-9
server/handle_server_update_email.go
···
11
11
type ComAtprotoServerUpdateEmailRequest struct {
12
12
Email string `json:"email" validate:"required"`
13
13
EmailAuthFactor bool `json:"emailAuthFactor"`
14
-
Token string `json:"token" validate:"required"`
14
+
Token string `json:"token"`
15
15
}
16
16
17
17
func (s *Server) handleServerUpdateEmail(e echo.Context) error {
18
+
ctx := e.Request().Context()
19
+
logger := s.logger.With("name", "handleServerUpdateEmail")
20
+
18
21
urepo := e.Get("repo").(*models.RepoActor)
19
22
20
23
var req ComAtprotoServerUpdateEmailRequest
21
24
if err := e.Bind(&req); err != nil {
22
-
s.logger.Error("error binding", "error", err)
25
+
logger.Error("error binding", "error", err)
23
26
return helpers.ServerError(e, nil)
24
27
}
25
28
···
27
30
return helpers.InputError(e, nil)
28
31
}
29
32
30
-
if urepo.EmailUpdateCode == nil || urepo.EmailUpdateCodeExpiresAt == nil {
33
+
// To disable email auth factor a token is required.
34
+
// To enable email auth factor a token is not required.
35
+
// If updating an email address, a token will be sent anyway
36
+
if urepo.TwoFactorType != models.TwoFactorTypeNone && req.EmailAuthFactor == false && req.Token == "" {
31
37
return helpers.InvalidTokenError(e)
32
38
}
33
39
34
-
if *urepo.EmailUpdateCode != req.Token {
35
-
return helpers.InvalidTokenError(e)
40
+
if req.Token != "" {
41
+
if urepo.EmailUpdateCode == nil || urepo.EmailUpdateCodeExpiresAt == nil {
42
+
return helpers.InvalidTokenError(e)
43
+
}
44
+
45
+
if *urepo.EmailUpdateCode != req.Token {
46
+
return helpers.InvalidTokenError(e)
47
+
}
48
+
49
+
if time.Now().UTC().After(*urepo.EmailUpdateCodeExpiresAt) {
50
+
return helpers.ExpiredTokenError(e)
51
+
}
52
+
}
53
+
54
+
twoFactorType := models.TwoFactorTypeNone
55
+
if req.EmailAuthFactor {
56
+
twoFactorType = models.TwoFactorTypeEmail
36
57
}
37
58
38
-
if time.Now().UTC().After(*urepo.EmailUpdateCodeExpiresAt) {
39
-
return helpers.ExpiredTokenError(e)
59
+
query := "UPDATE repos SET email_update_code = NULL, email_update_code_expires_at = NULL, two_factor_type = ?, email = ?"
60
+
61
+
if urepo.Email != req.Email {
62
+
query += ",email_confirmed_at = NULL"
40
63
}
41
64
42
-
if err := s.db.Exec("UPDATE repos SET email_update_code = NULL, email_update_code_expires_at = NULL, email_confirmed_at = NULL, email = ? WHERE did = ?", nil, req.Email, urepo.Repo.Did).Error; err != nil {
43
-
s.logger.Error("error updating repo", "error", err)
65
+
query += " WHERE did = ?"
66
+
67
+
if err := s.db.Exec(ctx, query, nil, twoFactorType, req.Email, urepo.Repo.Did).Error; err != nil {
68
+
logger.Error("error updating repo", "error", err)
44
69
return helpers.ServerError(e, nil)
45
70
}
46
71
+14
-11
server/handle_sync_get_blob.go
+14
-11
server/handle_sync_get_blob.go
···
17
17
)
18
18
19
19
func (s *Server) handleSyncGetBlob(e echo.Context) error {
20
+
ctx := e.Request().Context()
21
+
logger := s.logger.With("name", "handleSyncGetBlob")
22
+
20
23
did := e.QueryParam("did")
21
24
if did == "" {
22
25
return helpers.InputError(e, nil)
···
32
35
return helpers.InputError(e, nil)
33
36
}
34
37
35
-
urepo, err := s.getRepoActorByDid(did)
38
+
urepo, err := s.getRepoActorByDid(ctx, did)
36
39
if err != nil {
37
-
s.logger.Error("could not find user for requested blob", "error", err)
40
+
logger.Error("could not find user for requested blob", "error", err)
38
41
return helpers.InputError(e, nil)
39
42
}
40
43
···
46
49
}
47
50
48
51
var blob models.Blob
49
-
if err := s.db.Raw("SELECT * FROM blobs WHERE did = ? AND cid = ?", nil, did, c.Bytes()).Scan(&blob).Error; err != nil {
50
-
s.logger.Error("error looking up blob", "error", err)
52
+
if err := s.db.Raw(ctx, "SELECT * FROM blobs WHERE did = ? AND cid = ?", nil, did, c.Bytes()).Scan(&blob).Error; err != nil {
53
+
logger.Error("error looking up blob", "error", err)
51
54
return helpers.ServerError(e, nil)
52
55
}
53
56
···
55
58
56
59
if blob.Storage == "sqlite" {
57
60
var parts []models.BlobPart
58
-
if err := s.db.Raw("SELECT * FROM blob_parts WHERE blob_id = ? ORDER BY idx", nil, blob.ID).Scan(&parts).Error; err != nil {
59
-
s.logger.Error("error getting blob parts", "error", err)
61
+
if err := s.db.Raw(ctx, "SELECT * FROM blob_parts WHERE blob_id = ? ORDER BY idx", nil, blob.ID).Scan(&parts).Error; err != nil {
62
+
logger.Error("error getting blob parts", "error", err)
60
63
return helpers.ServerError(e, nil)
61
64
}
62
65
···
66
69
}
67
70
} else if blob.Storage == "s3" {
68
71
if !(s.s3Config != nil && s.s3Config.BlobstoreEnabled) {
69
-
s.logger.Error("s3 storage disabled")
72
+
logger.Error("s3 storage disabled")
70
73
return helpers.ServerError(e, nil)
71
74
}
72
75
···
89
92
90
93
sess, err := session.NewSession(config)
91
94
if err != nil {
92
-
s.logger.Error("error creating aws session", "error", err)
95
+
logger.Error("error creating aws session", "error", err)
93
96
return helpers.ServerError(e, nil)
94
97
}
95
98
···
98
101
Bucket: aws.String(s.s3Config.Bucket),
99
102
Key: aws.String(blobKey),
100
103
}); err != nil {
101
-
s.logger.Error("error getting blob from s3", "error", err)
104
+
logger.Error("error getting blob from s3", "error", err)
102
105
return helpers.ServerError(e, nil)
103
106
} else {
104
107
read := 0
···
112
115
break
113
116
}
114
117
} else if err != nil && err != io.ErrUnexpectedEOF {
115
-
s.logger.Error("error reading blob", "error", err)
118
+
logger.Error("error reading blob", "error", err)
116
119
return helpers.ServerError(e, nil)
117
120
}
118
121
···
123
126
}
124
127
}
125
128
} else {
126
-
s.logger.Error("unknown storage", "storage", blob.Storage)
129
+
logger.Error("unknown storage", "storage", blob.Storage)
127
130
return helpers.ServerError(e, nil)
128
131
}
129
132
+3
-2
server/handle_sync_get_blocks.go
+3
-2
server/handle_sync_get_blocks.go
···
18
18
19
19
func (s *Server) handleGetBlocks(e echo.Context) error {
20
20
ctx := e.Request().Context()
21
+
logger := s.logger.With("name", "handleSyncGetBlocks")
21
22
22
23
var req ComAtprotoSyncGetBlocksRequest
23
24
if err := e.Bind(&req); err != nil {
···
35
36
cids = append(cids, c)
36
37
}
37
38
38
-
urepo, err := s.getRepoActorByDid(req.Did)
39
+
urepo, err := s.getRepoActorByDid(ctx, req.Did)
39
40
if err != nil {
40
41
return helpers.ServerError(e, nil)
41
42
}
···
52
53
})
53
54
54
55
if _, err := carstore.LdWrite(buf, hb); err != nil {
55
-
s.logger.Error("error writing to car", "error", err)
56
+
logger.Error("error writing to car", "error", err)
56
57
return helpers.ServerError(e, nil)
57
58
}
58
59
+3
-1
server/handle_sync_get_latest_commit.go
+3
-1
server/handle_sync_get_latest_commit.go
···
12
12
}
13
13
14
14
func (s *Server) handleSyncGetLatestCommit(e echo.Context) error {
15
+
ctx := e.Request().Context()
16
+
15
17
did := e.QueryParam("did")
16
18
if did == "" {
17
19
return helpers.InputError(e, nil)
18
20
}
19
21
20
-
urepo, err := s.getRepoActorByDid(did)
22
+
urepo, err := s.getRepoActorByDid(ctx, did)
21
23
if err != nil {
22
24
return err
23
25
}
+8
-5
server/handle_sync_get_record.go
+8
-5
server/handle_sync_get_record.go
···
13
13
)
14
14
15
15
func (s *Server) handleSyncGetRecord(e echo.Context) error {
16
+
ctx := e.Request().Context()
17
+
logger := s.logger.With("name", "handleSyncGetRecord")
18
+
16
19
did := e.QueryParam("did")
17
20
collection := e.QueryParam("collection")
18
21
rkey := e.QueryParam("rkey")
19
22
20
23
var urepo models.Repo
21
-
if err := s.db.Raw("SELECT * FROM repos WHERE did = ?", nil, did).Scan(&urepo).Error; err != nil {
22
-
s.logger.Error("error getting repo", "error", err)
24
+
if err := s.db.Raw(ctx, "SELECT * FROM repos WHERE did = ?", nil, did).Scan(&urepo).Error; err != nil {
25
+
logger.Error("error getting repo", "error", err)
23
26
return helpers.ServerError(e, nil)
24
27
}
25
28
26
-
root, blocks, err := s.repoman.getRecordProof(urepo, collection, rkey)
29
+
root, blocks, err := s.repoman.getRecordProof(ctx, urepo, collection, rkey)
27
30
if err != nil {
28
31
return err
29
32
}
···
36
39
})
37
40
38
41
if _, err := carstore.LdWrite(buf, hb); err != nil {
39
-
s.logger.Error("error writing to car", "error", err)
42
+
logger.Error("error writing to car", "error", err)
40
43
return helpers.ServerError(e, nil)
41
44
}
42
45
43
46
for _, blk := range blocks {
44
47
if _, err := carstore.LdWrite(buf, blk.Cid().Bytes(), blk.RawData()); err != nil {
45
-
s.logger.Error("error writing to car", "error", err)
48
+
logger.Error("error writing to car", "error", err)
46
49
return helpers.ServerError(e, nil)
47
50
}
48
51
}
+6
-3
server/handle_sync_get_repo.go
+6
-3
server/handle_sync_get_repo.go
···
13
13
)
14
14
15
15
func (s *Server) handleSyncGetRepo(e echo.Context) error {
16
+
ctx := e.Request().Context()
17
+
logger := s.logger.With("name", "handleSyncGetRepo")
18
+
16
19
did := e.QueryParam("did")
17
20
if did == "" {
18
21
return helpers.InputError(e, nil)
19
22
}
20
23
21
-
urepo, err := s.getRepoActorByDid(did)
24
+
urepo, err := s.getRepoActorByDid(ctx, did)
22
25
if err != nil {
23
26
return err
24
27
}
···
36
39
buf := new(bytes.Buffer)
37
40
38
41
if _, err := carstore.LdWrite(buf, hb); err != nil {
39
-
s.logger.Error("error writing to car", "error", err)
42
+
logger.Error("error writing to car", "error", err)
40
43
return helpers.ServerError(e, nil)
41
44
}
42
45
43
46
var blocks []models.Block
44
-
if err := s.db.Raw("SELECT * FROM blocks WHERE did = ? ORDER BY rev ASC", nil, urepo.Repo.Did).Scan(&blocks).Error; err != nil {
47
+
if err := s.db.Raw(ctx, "SELECT * FROM blocks WHERE did = ? ORDER BY rev ASC", nil, urepo.Repo.Did).Scan(&blocks).Error; err != nil {
45
48
return err
46
49
}
47
50
+3
-1
server/handle_sync_get_repo_status.go
+3
-1
server/handle_sync_get_repo_status.go
···
14
14
15
15
// TODO: make this actually do the right thing
16
16
func (s *Server) handleSyncGetRepoStatus(e echo.Context) error {
17
+
ctx := e.Request().Context()
18
+
17
19
did := e.QueryParam("did")
18
20
if did == "" {
19
21
return helpers.InputError(e, nil)
20
22
}
21
23
22
-
urepo, err := s.getRepoActorByDid(did)
24
+
urepo, err := s.getRepoActorByDid(ctx, did)
23
25
if err != nil {
24
26
return err
25
27
}
+8
-5
server/handle_sync_list_blobs.go
+8
-5
server/handle_sync_list_blobs.go
···
14
14
}
15
15
16
16
func (s *Server) handleSyncListBlobs(e echo.Context) error {
17
+
ctx := e.Request().Context()
18
+
logger := s.logger.With("name", "handleSyncListBlobs")
19
+
17
20
did := e.QueryParam("did")
18
21
if did == "" {
19
22
return helpers.InputError(e, nil)
···
35
38
}
36
39
params = append(params, limit)
37
40
38
-
urepo, err := s.getRepoActorByDid(did)
41
+
urepo, err := s.getRepoActorByDid(ctx, did)
39
42
if err != nil {
40
-
s.logger.Error("could not find user for requested blobs", "error", err)
43
+
logger.Error("could not find user for requested blobs", "error", err)
41
44
return helpers.InputError(e, nil)
42
45
}
43
46
···
49
52
}
50
53
51
54
var blobs []models.Blob
52
-
if err := s.db.Raw("SELECT * FROM blobs WHERE did = ? "+cursorquery+" ORDER BY created_at DESC LIMIT ?", nil, params...).Scan(&blobs).Error; err != nil {
53
-
s.logger.Error("error getting records", "error", err)
55
+
if err := s.db.Raw(ctx, "SELECT * FROM blobs WHERE did = ? "+cursorquery+" ORDER BY created_at DESC LIMIT ?", nil, params...).Scan(&blobs).Error; err != nil {
56
+
logger.Error("error getting records", "error", err)
54
57
return helpers.ServerError(e, nil)
55
58
}
56
59
···
58
61
for _, b := range blobs {
59
62
c, err := cid.Cast(b.Cid)
60
63
if err != nil {
61
-
s.logger.Error("error casting cid", "error", err)
64
+
logger.Error("error casting cid", "error", err)
62
65
return helpers.ServerError(e, nil)
63
66
}
64
67
cstrs = append(cstrs, c.String())
+54
-42
server/handle_sync_subscribe_repos.go
+54
-42
server/handle_sync_subscribe_repos.go
···
7
7
"github.com/bluesky-social/indigo/events"
8
8
"github.com/bluesky-social/indigo/lex/util"
9
9
"github.com/btcsuite/websocket"
10
+
"github.com/haileyok/cocoon/metrics"
10
11
"github.com/labstack/echo/v4"
11
12
)
12
13
···
24
25
logger = logger.With("ident", ident)
25
26
logger.Info("new connection established")
26
27
28
+
metrics.RelaysConnected.WithLabelValues(ident).Inc()
29
+
defer func() {
30
+
metrics.RelaysConnected.WithLabelValues(ident).Dec()
31
+
}()
32
+
27
33
evts, cancel, err := s.evtman.Subscribe(ctx, ident, func(evt *events.XRPCStreamEvent) bool {
28
34
return true
29
35
}, nil)
···
34
40
35
41
header := events.EventHeader{Op: events.EvtKindMessage}
36
42
for evt := range evts {
37
-
wc, err := conn.NextWriter(websocket.BinaryMessage)
38
-
if err != nil {
39
-
logger.Error("error writing message to relay", "err", err)
40
-
break
41
-
}
43
+
func() {
44
+
defer func() {
45
+
metrics.RelaySends.WithLabelValues(ident, header.MsgType).Inc()
46
+
}()
42
47
43
-
if ctx.Err() != nil {
44
-
logger.Error("context error", "err", err)
45
-
break
46
-
}
48
+
wc, err := conn.NextWriter(websocket.BinaryMessage)
49
+
if err != nil {
50
+
logger.Error("error writing message to relay", "err", err)
51
+
return
52
+
}
47
53
48
-
var obj util.CBOR
49
-
switch {
50
-
case evt.Error != nil:
51
-
header.Op = events.EvtKindErrorFrame
52
-
obj = evt.Error
53
-
case evt.RepoCommit != nil:
54
-
header.MsgType = "#commit"
55
-
obj = evt.RepoCommit
56
-
case evt.RepoIdentity != nil:
57
-
header.MsgType = "#identity"
58
-
obj = evt.RepoIdentity
59
-
case evt.RepoAccount != nil:
60
-
header.MsgType = "#account"
61
-
obj = evt.RepoAccount
62
-
case evt.RepoInfo != nil:
63
-
header.MsgType = "#info"
64
-
obj = evt.RepoInfo
65
-
default:
66
-
logger.Warn("unrecognized event kind")
67
-
return nil
68
-
}
54
+
if ctx.Err() != nil {
55
+
logger.Error("context error", "err", err)
56
+
return
57
+
}
69
58
70
-
if err := header.MarshalCBOR(wc); err != nil {
71
-
logger.Error("failed to write header to relay", "err", err)
72
-
break
73
-
}
59
+
var obj util.CBOR
60
+
switch {
61
+
case evt.Error != nil:
62
+
header.Op = events.EvtKindErrorFrame
63
+
obj = evt.Error
64
+
case evt.RepoCommit != nil:
65
+
header.MsgType = "#commit"
66
+
obj = evt.RepoCommit
67
+
case evt.RepoIdentity != nil:
68
+
header.MsgType = "#identity"
69
+
obj = evt.RepoIdentity
70
+
case evt.RepoAccount != nil:
71
+
header.MsgType = "#account"
72
+
obj = evt.RepoAccount
73
+
case evt.RepoInfo != nil:
74
+
header.MsgType = "#info"
75
+
obj = evt.RepoInfo
76
+
default:
77
+
logger.Warn("unrecognized event kind")
78
+
return
79
+
}
74
80
75
-
if err := obj.MarshalCBOR(wc); err != nil {
76
-
logger.Error("failed to write event to relay", "err", err)
77
-
break
78
-
}
81
+
if err := header.MarshalCBOR(wc); err != nil {
82
+
logger.Error("failed to write header to relay", "err", err)
83
+
return
84
+
}
79
85
80
-
if err := wc.Close(); err != nil {
81
-
logger.Error("failed to flush-close our event write", "err", err)
82
-
break
83
-
}
86
+
if err := obj.MarshalCBOR(wc); err != nil {
87
+
logger.Error("failed to write event to relay", "err", err)
88
+
return
89
+
}
90
+
91
+
if err := wc.Close(); err != nil {
92
+
logger.Error("failed to flush-close our event write", "err", err)
93
+
return
94
+
}
95
+
}()
84
96
}
85
97
86
98
// we should tell the relay to request a new crawl at this point if we got disconnected
+5
-2
server/handle_well_known.go
+5
-2
server/handle_well_known.go
···
67
67
}
68
68
69
69
func (s *Server) handleAtprotoDid(e echo.Context) error {
70
+
ctx := e.Request().Context()
71
+
logger := s.logger.With("name", "handleAtprotoDid")
72
+
70
73
host := e.Request().Host
71
74
if host == "" {
72
75
return helpers.InputError(e, to.StringPtr("Invalid handle."))
···
84
87
return e.NoContent(404)
85
88
}
86
89
87
-
actor, err := s.getActorByHandle(host)
90
+
actor, err := s.getActorByHandle(ctx, host)
88
91
if err != nil {
89
92
if err == gorm.ErrRecordNotFound {
90
93
return e.NoContent(404)
91
94
}
92
-
s.logger.Error("error looking up actor by handle", "error", err)
95
+
logger.Error("error looking up actor by handle", "error", err)
93
96
return helpers.ServerError(e, nil)
94
97
}
95
98
+19
server/mail.go
+19
server/mail.go
···
96
96
97
97
return nil
98
98
}
99
+
100
+
func (s *Server) sendTwoFactorCode(email, handle, code string) error {
101
+
if s.mail == nil {
102
+
return nil
103
+
}
104
+
105
+
s.mailLk.Lock()
106
+
defer s.mailLk.Unlock()
107
+
108
+
s.mail.To(email)
109
+
s.mail.Subject("2FA code for " + s.config.Hostname)
110
+
s.mail.Plain().Set(fmt.Sprintf("Hello %s. Your 2FA code is %s. This code will expire in ten minutes.", handle, code))
111
+
112
+
if err := s.mail.Send(); err != nil {
113
+
return err
114
+
}
115
+
116
+
return nil
117
+
}
+42
-21
server/middleware.go
+42
-21
server/middleware.go
···
37
37
38
38
func (s *Server) handleLegacySessionMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
39
39
return func(e echo.Context) error {
40
+
ctx := e.Request().Context()
41
+
logger := s.logger.With("name", "handleLegacySessionMiddleware")
42
+
40
43
authheader := e.Request().Header.Get("authorization")
41
44
if authheader == "" {
42
45
return e.JSON(401, map[string]string{"error": "Unauthorized"})
···
67
70
if hasLxm {
68
71
pts := strings.Split(e.Request().URL.String(), "/")
69
72
if lxm != pts[len(pts)-1] {
70
-
s.logger.Error("service auth lxm incorrect", "lxm", lxm, "expected", pts[len(pts)-1], "error", err)
73
+
logger.Error("service auth lxm incorrect", "lxm", lxm, "expected", pts[len(pts)-1], "error", err)
71
74
return helpers.InputError(e, nil)
72
75
}
73
76
74
77
maybeDid, ok := claims["iss"].(string)
75
78
if !ok {
76
-
s.logger.Error("no iss in service auth token", "error", err)
79
+
logger.Error("no iss in service auth token", "error", err)
77
80
return helpers.InputError(e, nil)
78
81
}
79
82
did = maybeDid
80
83
81
-
maybeRepo, err := s.getRepoActorByDid(did)
84
+
maybeRepo, err := s.getRepoActorByDid(ctx, did)
82
85
if err != nil {
83
-
s.logger.Error("error fetching repo", "error", err)
86
+
logger.Error("error fetching repo", "error", err)
84
87
return helpers.ServerError(e, nil)
85
88
}
86
89
repo = maybeRepo
···
94
97
return s.privateKey.Public(), nil
95
98
})
96
99
if err != nil {
97
-
s.logger.Error("error parsing jwt", "error", err)
100
+
logger.Error("error parsing jwt", "error", err)
98
101
return helpers.ExpiredTokenError(e)
99
102
}
100
103
···
107
110
hash := sha256.Sum256([]byte(signingInput))
108
111
sigBytes, err := base64.RawURLEncoding.DecodeString(kpts[2])
109
112
if err != nil {
110
-
s.logger.Error("error decoding signature bytes", "error", err)
113
+
logger.Error("error decoding signature bytes", "error", err)
111
114
return helpers.ServerError(e, nil)
112
115
}
113
116
114
117
if len(sigBytes) != 64 {
115
-
s.logger.Error("incorrect sigbytes length", "length", len(sigBytes))
118
+
logger.Error("incorrect sigbytes length", "length", len(sigBytes))
116
119
return helpers.ServerError(e, nil)
117
120
}
118
121
···
120
123
sBytes := sigBytes[32:]
121
124
rr, _ := secp256k1.NewScalarFromBytes((*[32]byte)(rBytes))
122
125
ss, _ := secp256k1.NewScalarFromBytes((*[32]byte)(sBytes))
126
+
127
+
if repo == nil {
128
+
sub, ok := claims["sub"].(string)
129
+
if !ok {
130
+
s.logger.Error("no sub claim in ES256K token and repo not set")
131
+
return helpers.InvalidTokenError(e)
132
+
}
133
+
maybeRepo, err := s.getRepoActorByDid(ctx, sub)
134
+
if err != nil {
135
+
s.logger.Error("error fetching repo for ES256K verification", "error", err)
136
+
return helpers.ServerError(e, nil)
137
+
}
138
+
repo = maybeRepo
139
+
did = sub
140
+
}
123
141
124
142
sk, err := secp256k1secec.NewPrivateKey(repo.SigningKey)
125
143
if err != nil {
126
-
s.logger.Error("can't load private key", "error", err)
144
+
logger.Error("can't load private key", "error", err)
127
145
return err
128
146
}
129
147
130
148
pubKey, ok := sk.Public().(*secp256k1secec.PublicKey)
131
149
if !ok {
132
-
s.logger.Error("error getting public key from sk")
150
+
logger.Error("error getting public key from sk")
133
151
return helpers.ServerError(e, nil)
134
152
}
135
153
136
154
verified := pubKey.VerifyRaw(hash[:], rr, ss)
137
155
if !verified {
138
-
s.logger.Error("error verifying", "error", err)
156
+
logger.Error("error verifying", "error", err)
139
157
return helpers.ServerError(e, nil)
140
158
}
141
159
}
···
159
177
Found bool
160
178
}
161
179
var result Result
162
-
if err := s.db.Raw("SELECT EXISTS(SELECT 1 FROM "+table+" WHERE token = ?) AS found", nil, tokenstr).Scan(&result).Error; err != nil {
180
+
if err := s.db.Raw(ctx, "SELECT EXISTS(SELECT 1 FROM "+table+" WHERE token = ?) AS found", nil, tokenstr).Scan(&result).Error; err != nil {
163
181
if err == gorm.ErrRecordNotFound {
164
182
return helpers.InvalidTokenError(e)
165
183
}
166
184
167
-
s.logger.Error("error getting token from db", "error", err)
185
+
logger.Error("error getting token from db", "error", err)
168
186
return helpers.ServerError(e, nil)
169
187
}
170
188
···
175
193
176
194
exp, ok := claims["exp"].(float64)
177
195
if !ok {
178
-
s.logger.Error("error getting iat from token")
196
+
logger.Error("error getting iat from token")
179
197
return helpers.ServerError(e, nil)
180
198
}
181
199
···
184
202
}
185
203
186
204
if repo == nil {
187
-
maybeRepo, err := s.getRepoActorByDid(claims["sub"].(string))
205
+
maybeRepo, err := s.getRepoActorByDid(ctx, claims["sub"].(string))
188
206
if err != nil {
189
-
s.logger.Error("error fetching repo", "error", err)
207
+
logger.Error("error fetching repo", "error", err)
190
208
return helpers.ServerError(e, nil)
191
209
}
192
210
repo = maybeRepo
···
207
225
208
226
func (s *Server) handleOauthSessionMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
209
227
return func(e echo.Context) error {
228
+
ctx := e.Request().Context()
229
+
logger := s.logger.With("name", "handleOauthSessionMiddleware")
230
+
210
231
authheader := e.Request().Header.Get("authorization")
211
232
if authheader == "" {
212
233
return e.JSON(401, map[string]string{"error": "Unauthorized"})
···
238
259
"error": "use_dpop_nonce",
239
260
})
240
261
}
241
-
s.logger.Error("invalid dpop proof", "error", err)
262
+
logger.Error("invalid dpop proof", "error", err)
242
263
return helpers.InputError(e, nil)
243
264
}
244
265
245
266
var oauthToken provider.OauthToken
246
-
if err := s.db.Raw("SELECT * FROM oauth_tokens WHERE token = ?", nil, accessToken).Scan(&oauthToken).Error; err != nil {
247
-
s.logger.Error("error finding access token in db", "error", err)
267
+
if err := s.db.Raw(ctx, "SELECT * FROM oauth_tokens WHERE token = ?", nil, accessToken).Scan(&oauthToken).Error; err != nil {
268
+
logger.Error("error finding access token in db", "error", err)
248
269
return helpers.InputError(e, nil)
249
270
}
250
271
···
253
274
}
254
275
255
276
if *oauthToken.Parameters.DpopJkt != proof.JKT {
256
-
s.logger.Error("jkt mismatch", "token", oauthToken.Parameters.DpopJkt, "proof", proof.JKT)
277
+
logger.Error("jkt mismatch", "token", oauthToken.Parameters.DpopJkt, "proof", proof.JKT)
257
278
return helpers.InputError(e, to.StringPtr("dpop jkt mismatch"))
258
279
}
259
280
···
266
287
})
267
288
}
268
289
269
-
repo, err := s.getRepoActorByDid(oauthToken.Sub)
290
+
repo, err := s.getRepoActorByDid(ctx, oauthToken.Sub)
270
291
if err != nil {
271
-
s.logger.Error("could not find actor in db", "error", err)
292
+
logger.Error("could not find actor in db", "error", err)
272
293
return helpers.ServerError(e, nil)
273
294
}
274
295
+85
-32
server/repo.go
+85
-32
server/repo.go
···
17
17
lexutil "github.com/bluesky-social/indigo/lex/util"
18
18
"github.com/bluesky-social/indigo/repo"
19
19
"github.com/haileyok/cocoon/internal/db"
20
+
"github.com/haileyok/cocoon/metrics"
20
21
"github.com/haileyok/cocoon/models"
21
22
"github.com/haileyok/cocoon/recording_blockstore"
22
23
blocks "github.com/ipfs/go-block-format"
···
96
97
}
97
98
98
99
// TODO make use of swap commit
99
-
func (rm *RepoMan) applyWrites(urepo models.Repo, writes []Op, swapCommit *string) ([]ApplyWriteResult, error) {
100
+
func (rm *RepoMan) applyWrites(ctx context.Context, urepo models.Repo, writes []Op, swapCommit *string) ([]ApplyWriteResult, error) {
100
101
rootcid, err := cid.Cast(urepo.Root)
101
102
if err != nil {
102
103
return nil, err
···
104
105
105
106
dbs := rm.s.getBlockstore(urepo.Did)
106
107
bs := recording_blockstore.New(dbs)
107
-
r, err := repo.OpenRepo(context.TODO(), bs, rootcid)
108
+
r, err := repo.OpenRepo(ctx, bs, rootcid)
108
109
109
-
entries := []models.Record{}
110
110
var results []ApplyWriteResult
111
111
112
+
entries := make([]models.Record, 0, len(writes))
112
113
for i, op := range writes {
114
+
// updates or deletes must supply an rkey
113
115
if op.Type != OpTypeCreate && op.Rkey == nil {
114
116
return nil, fmt.Errorf("invalid rkey")
115
117
} else if op.Type == OpTypeCreate && op.Rkey != nil {
116
-
_, _, err := r.GetRecord(context.TODO(), op.Collection+"/"+*op.Rkey)
118
+
// we should conver this op to an update if the rkey already exists
119
+
_, _, err := r.GetRecord(ctx, fmt.Sprintf("%s/%s", op.Collection, *op.Rkey))
117
120
if err == nil {
118
121
op.Type = OpTypeUpdate
119
122
}
120
123
} else if op.Rkey == nil {
124
+
// creates that don't supply an rkey will have one generated for them
121
125
op.Rkey = to.StringPtr(rm.clock.Next().String())
122
126
writes[i].Rkey = op.Rkey
123
127
}
124
128
129
+
// validate the record key is actually valid
125
130
_, err := syntax.ParseRecordKey(*op.Rkey)
126
131
if err != nil {
127
132
return nil, err
···
129
134
130
135
switch op.Type {
131
136
case OpTypeCreate:
132
-
j, err := json.Marshal(*op.Record)
137
+
// HACK: this fixes some type conversions, mainly around integers
138
+
// first we convert to json bytes
139
+
b, err := json.Marshal(*op.Record)
133
140
if err != nil {
134
141
return nil, err
135
142
}
136
-
out, err := atdata.UnmarshalJSON(j)
143
+
// then we use atdata.UnmarshalJSON to convert it back to a map
144
+
out, err := atdata.UnmarshalJSON(b)
137
145
if err != nil {
138
146
return nil, err
139
147
}
148
+
// finally we can cast to a MarshalableMap
140
149
mm := MarshalableMap(out)
141
150
142
151
// HACK: if a record doesn't contain a $type, we can manually set it here based on the op's collection
152
+
// i forget why this is actually necessary?
143
153
if mm["$type"] == "" {
144
154
mm["$type"] = op.Collection
145
155
}
146
156
147
-
nc, err := r.PutRecord(context.TODO(), op.Collection+"/"+*op.Rkey, &mm)
157
+
nc, err := r.PutRecord(ctx, fmt.Sprintf("%s/%s", op.Collection, *op.Rkey), &mm)
148
158
if err != nil {
149
159
return nil, err
150
160
}
161
+
151
162
d, err := atdata.MarshalCBOR(mm)
152
163
if err != nil {
153
164
return nil, err
154
165
}
166
+
155
167
entries = append(entries, models.Record{
156
168
Did: urepo.Did,
157
169
CreatedAt: rm.clock.Next().String(),
···
160
172
Cid: nc.String(),
161
173
Value: d,
162
174
})
175
+
163
176
results = append(results, ApplyWriteResult{
164
177
Type: to.StringPtr(OpTypeCreate.String()),
165
178
Uri: to.StringPtr("at://" + urepo.Did + "/" + op.Collection + "/" + *op.Rkey),
···
167
180
ValidationStatus: to.StringPtr("valid"), // TODO: obviously this might not be true atm lol
168
181
})
169
182
case OpTypeDelete:
183
+
// try to find the old record in the database
170
184
var old models.Record
171
-
if err := rm.db.Raw("SELECT value FROM records WHERE did = ? AND nsid = ? AND rkey = ?", nil, urepo.Did, op.Collection, op.Rkey).Scan(&old).Error; err != nil {
185
+
if err := rm.db.Raw(ctx, "SELECT value FROM records WHERE did = ? AND nsid = ? AND rkey = ?", nil, urepo.Did, op.Collection, op.Rkey).Scan(&old).Error; err != nil {
172
186
return nil, err
173
187
}
188
+
189
+
// TODO: this is really confusing, and looking at it i have no idea why i did this. below when we are doing deletes, we
190
+
// check if `cid` here is nil to indicate if we should delete. that really doesn't make much sense and its super illogical
191
+
// when reading this code. i dont feel like fixing right now though so
174
192
entries = append(entries, models.Record{
175
193
Did: urepo.Did,
176
194
Nsid: op.Collection,
177
195
Rkey: *op.Rkey,
178
196
Value: old.Value,
179
197
})
180
-
err := r.DeleteRecord(context.TODO(), op.Collection+"/"+*op.Rkey)
198
+
199
+
// delete the record from the repo
200
+
err := r.DeleteRecord(ctx, fmt.Sprintf("%s/%s", op.Collection, *op.Rkey))
181
201
if err != nil {
182
202
return nil, err
183
203
}
204
+
205
+
// add a result for the delete
184
206
results = append(results, ApplyWriteResult{
185
207
Type: to.StringPtr(OpTypeDelete.String()),
186
208
})
187
209
case OpTypeUpdate:
188
-
j, err := json.Marshal(*op.Record)
210
+
// HACK: same hack as above for type fixes
211
+
b, err := json.Marshal(*op.Record)
189
212
if err != nil {
190
213
return nil, err
191
214
}
192
-
out, err := atdata.UnmarshalJSON(j)
215
+
out, err := atdata.UnmarshalJSON(b)
193
216
if err != nil {
194
217
return nil, err
195
218
}
196
219
mm := MarshalableMap(out)
197
-
nc, err := r.UpdateRecord(context.TODO(), op.Collection+"/"+*op.Rkey, &mm)
220
+
221
+
nc, err := r.UpdateRecord(ctx, fmt.Sprintf("%s/%s", op.Collection, *op.Rkey), &mm)
198
222
if err != nil {
199
223
return nil, err
200
224
}
225
+
201
226
d, err := atdata.MarshalCBOR(mm)
202
227
if err != nil {
203
228
return nil, err
204
229
}
230
+
205
231
entries = append(entries, models.Record{
206
232
Did: urepo.Did,
207
233
CreatedAt: rm.clock.Next().String(),
···
210
236
Cid: nc.String(),
211
237
Value: d,
212
238
})
239
+
213
240
results = append(results, ApplyWriteResult{
214
241
Type: to.StringPtr(OpTypeUpdate.String()),
215
242
Uri: to.StringPtr("at://" + urepo.Did + "/" + op.Collection + "/" + *op.Rkey),
···
219
246
}
220
247
}
221
248
222
-
newroot, rev, err := r.Commit(context.TODO(), urepo.SignFor)
249
+
// commit and get the new root
250
+
newroot, rev, err := r.Commit(ctx, urepo.SignFor)
223
251
if err != nil {
224
252
return nil, err
225
253
}
226
254
255
+
for _, result := range results {
256
+
if result.Type != nil {
257
+
metrics.RepoOperations.WithLabelValues(*result.Type).Inc()
258
+
}
259
+
}
260
+
261
+
// create a buffer for dumping our new cbor into
227
262
buf := new(bytes.Buffer)
228
263
264
+
// first write the car header to the buffer
229
265
hb, err := cbor.DumpObject(&car.CarHeader{
230
266
Roots: []cid.Cid{newroot},
231
267
Version: 1,
232
268
})
233
-
234
269
if _, err := carstore.LdWrite(buf, hb); err != nil {
235
270
return nil, err
236
271
}
237
272
238
-
diffops, err := r.DiffSince(context.TODO(), rootcid)
273
+
// get a diff of the changes to the repo
274
+
diffops, err := r.DiffSince(ctx, rootcid)
239
275
if err != nil {
240
276
return nil, err
241
277
}
242
278
279
+
// create the repo ops for the given diff
243
280
ops := make([]*atproto.SyncSubscribeRepos_RepoOp, 0, len(diffops))
244
-
245
281
for _, op := range diffops {
246
282
var c cid.Cid
247
283
switch op.Op {
···
270
306
})
271
307
}
272
308
273
-
blk, err := dbs.Get(context.TODO(), c)
309
+
blk, err := dbs.Get(ctx, c)
274
310
if err != nil {
275
311
return nil, err
276
312
}
277
313
314
+
// write the block to the buffer
278
315
if _, err := carstore.LdWrite(buf, blk.Cid().Bytes(), blk.RawData()); err != nil {
279
316
return nil, err
280
317
}
281
318
}
282
319
320
+
// write the writelog to the buffer
283
321
for _, op := range bs.GetWriteLog() {
284
322
if _, err := carstore.LdWrite(buf, op.Cid().Bytes(), op.RawData()); err != nil {
285
323
return nil, err
286
324
}
287
325
}
288
326
327
+
// blob blob blob blob blob :3
289
328
var blobs []lexutil.LexLink
290
329
for _, entry := range entries {
291
330
var cids []cid.Cid
331
+
// whenever there is cid present, we know it's a create (dumb)
292
332
if entry.Cid != "" {
293
-
if err := rm.s.db.Create(&entry, []clause.Expression{clause.OnConflict{
333
+
if err := rm.s.db.Create(ctx, &entry, []clause.Expression{clause.OnConflict{
294
334
Columns: []clause.Column{{Name: "did"}, {Name: "nsid"}, {Name: "rkey"}},
295
335
UpdateAll: true,
296
336
}}).Error; err != nil {
297
337
return nil, err
298
338
}
299
339
300
-
cids, err = rm.incrementBlobRefs(urepo, entry.Value)
340
+
// increment the given blob refs, yay
341
+
cids, err = rm.incrementBlobRefs(ctx, urepo, entry.Value)
301
342
if err != nil {
302
343
return nil, err
303
344
}
304
345
} else {
305
-
if err := rm.s.db.Delete(&entry, nil).Error; err != nil {
346
+
// as i noted above this is dumb. but we delete whenever the cid is nil. it works solely becaue the pkey
347
+
// is did + collection + rkey. i still really want to separate that out, or use a different type to make
348
+
// this less confusing/easy to read. alas, its 2 am and yea no
349
+
if err := rm.s.db.Delete(ctx, &entry, nil).Error; err != nil {
306
350
return nil, err
307
351
}
308
-
cids, err = rm.decrementBlobRefs(urepo, entry.Value)
352
+
353
+
// TODO:
354
+
cids, err = rm.decrementBlobRefs(ctx, urepo, entry.Value)
309
355
if err != nil {
310
356
return nil, err
311
357
}
312
358
}
313
359
360
+
// add all the relevant blobs to the blobs list of blobs. blob ^.^
314
361
for _, c := range cids {
315
362
blobs = append(blobs, lexutil.LexLink(c))
316
363
}
317
364
}
318
365
319
-
rm.s.evtman.AddEvent(context.TODO(), &events.XRPCStreamEvent{
366
+
// NOTE: using the request ctx seems a bit suss here, so using a background context. i'm not sure if this
367
+
// runs sync or not
368
+
rm.s.evtman.AddEvent(context.Background(), &events.XRPCStreamEvent{
320
369
RepoCommit: &atproto.SyncSubscribeRepos_Commit{
321
370
Repo: urepo.Did,
322
371
Blocks: buf.Bytes(),
···
330
379
},
331
380
})
332
381
333
-
if err := rm.s.UpdateRepo(context.TODO(), urepo.Did, newroot, rev); err != nil {
382
+
if err := rm.s.UpdateRepo(ctx, urepo.Did, newroot, rev); err != nil {
334
383
return nil, err
335
384
}
336
385
···
345
394
return results, nil
346
395
}
347
396
348
-
func (rm *RepoMan) getRecordProof(urepo models.Repo, collection, rkey string) (cid.Cid, []blocks.Block, error) {
397
+
// this is a fun little guy. to get a proof, we need to read the record out of the blockstore and record how we actually
398
+
// got to the guy. we'll wrap a new blockstore in a recording blockstore, then return the log for proof
399
+
func (rm *RepoMan) getRecordProof(ctx context.Context, urepo models.Repo, collection, rkey string) (cid.Cid, []blocks.Block, error) {
349
400
c, err := cid.Cast(urepo.Root)
350
401
if err != nil {
351
402
return cid.Undef, nil, err
···
354
405
dbs := rm.s.getBlockstore(urepo.Did)
355
406
bs := recording_blockstore.New(dbs)
356
407
357
-
r, err := repo.OpenRepo(context.TODO(), bs, c)
408
+
r, err := repo.OpenRepo(ctx, bs, c)
358
409
if err != nil {
359
410
return cid.Undef, nil, err
360
411
}
361
412
362
-
_, _, err = r.GetRecordBytes(context.TODO(), collection+"/"+rkey)
413
+
_, _, err = r.GetRecordBytes(ctx, fmt.Sprintf("%s/%s", collection, rkey))
363
414
if err != nil {
364
415
return cid.Undef, nil, err
365
416
}
···
367
418
return c, bs.GetReadLog(), nil
368
419
}
369
420
370
-
func (rm *RepoMan) incrementBlobRefs(urepo models.Repo, cbor []byte) ([]cid.Cid, error) {
421
+
func (rm *RepoMan) incrementBlobRefs(ctx context.Context, urepo models.Repo, cbor []byte) ([]cid.Cid, error) {
371
422
cids, err := getBlobCidsFromCbor(cbor)
372
423
if err != nil {
373
424
return nil, err
374
425
}
375
426
376
427
for _, c := range cids {
377
-
if err := rm.db.Exec("UPDATE blobs SET ref_count = ref_count + 1 WHERE did = ? AND cid = ?", nil, urepo.Did, c.Bytes()).Error; err != nil {
428
+
if err := rm.db.Exec(ctx, "UPDATE blobs SET ref_count = ref_count + 1 WHERE did = ? AND cid = ?", nil, urepo.Did, c.Bytes()).Error; err != nil {
378
429
return nil, err
379
430
}
380
431
}
···
382
433
return cids, nil
383
434
}
384
435
385
-
func (rm *RepoMan) decrementBlobRefs(urepo models.Repo, cbor []byte) ([]cid.Cid, error) {
436
+
func (rm *RepoMan) decrementBlobRefs(ctx context.Context, urepo models.Repo, cbor []byte) ([]cid.Cid, error) {
386
437
cids, err := getBlobCidsFromCbor(cbor)
387
438
if err != nil {
388
439
return nil, err
···
393
444
ID uint
394
445
Count int
395
446
}
396
-
if err := rm.db.Raw("UPDATE blobs SET ref_count = ref_count - 1 WHERE did = ? AND cid = ? RETURNING id, ref_count", nil, urepo.Did, c.Bytes()).Scan(&res).Error; err != nil {
447
+
if err := rm.db.Raw(ctx, "UPDATE blobs SET ref_count = ref_count - 1 WHERE did = ? AND cid = ? RETURNING id, ref_count", nil, urepo.Did, c.Bytes()).Scan(&res).Error; err != nil {
397
448
return nil, err
398
449
}
399
450
451
+
// TODO: this does _not_ handle deletions of blobs that are on s3 storage!!!! we need to get the blob, see what
452
+
// storage it is in, and clean up s3!!!!
400
453
if res.Count == 0 {
401
-
if err := rm.db.Exec("DELETE FROM blobs WHERE id = ?", nil, res.ID).Error; err != nil {
454
+
if err := rm.db.Exec(ctx, "DELETE FROM blobs WHERE id = ?", nil, res.ID).Error; err != nil {
402
455
return nil, err
403
456
}
404
-
if err := rm.db.Exec("DELETE FROM blob_parts WHERE blob_id = ?", nil, res.ID).Error; err != nil {
457
+
if err := rm.db.Exec(ctx, "DELETE FROM blob_parts WHERE blob_id = ?", nil, res.ID).Error; err != nil {
405
458
return nil, err
406
459
}
407
460
}
+39
-28
server/server.go
+39
-28
server/server.go
···
39
39
"github.com/haileyok/cocoon/oauth/provider"
40
40
"github.com/haileyok/cocoon/plc"
41
41
"github.com/ipfs/go-cid"
42
+
"github.com/labstack/echo-contrib/echoprometheus"
42
43
echo_session "github.com/labstack/echo-contrib/session"
43
44
"github.com/labstack/echo/v4"
44
45
"github.com/labstack/echo/v4/middleware"
···
89
90
}
90
91
91
92
type Args struct {
93
+
Logger *slog.Logger
94
+
92
95
Addr string
93
96
DbName string
94
97
DbType string
95
98
DatabaseURL string
96
-
Logger *slog.Logger
97
99
Version string
98
100
Did string
99
101
Hostname string
···
209
211
}
210
212
211
213
func New(args *Args) (*Server, error) {
214
+
if args.Logger == nil {
215
+
args.Logger = slog.Default()
216
+
}
217
+
218
+
logger := args.Logger.With("name", "New")
219
+
212
220
if args.Addr == "" {
213
221
return nil, fmt.Errorf("addr must be set")
214
222
}
···
237
245
return nil, fmt.Errorf("admin password must be set")
238
246
}
239
247
240
-
if args.Logger == nil {
241
-
args.Logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{}))
242
-
}
243
-
244
248
if args.SessionSecret == "" {
245
249
panic("SESSION SECRET WAS NOT SET. THIS IS REQUIRED. ")
246
250
}
···
248
252
e := echo.New()
249
253
250
254
e.Pre(middleware.RemoveTrailingSlash())
251
-
e.Pre(slogecho.New(args.Logger))
255
+
e.Pre(slogecho.New(args.Logger.With("component", "slogecho")))
252
256
e.Use(echo_session.Middleware(sessions.NewCookieStore([]byte(args.SessionSecret))))
257
+
e.Use(echoprometheus.NewMiddleware("cocoon"))
253
258
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
254
259
AllowOrigins: []string{"*"},
255
260
AllowHeaders: []string{"*"},
···
311
316
if err != nil {
312
317
return nil, fmt.Errorf("failed to connect to postgres: %w", err)
313
318
}
314
-
args.Logger.Info("connected to PostgreSQL database")
319
+
logger.Info("connected to PostgreSQL database")
315
320
default:
316
321
gdb, err = gorm.Open(sqlite.Open(args.DbName), &gorm.Config{})
317
322
if err != nil {
318
323
return nil, fmt.Errorf("failed to open sqlite database: %w", err)
319
324
}
320
-
args.Logger.Info("connected to SQLite database", "path", args.DbName)
325
+
logger.Info("connected to SQLite database", "path", args.DbName)
321
326
}
322
327
dbw := db.NewDB(gdb)
323
328
···
360
365
var nonceSecret []byte
361
366
maybeSecret, err := os.ReadFile("nonce.secret")
362
367
if err != nil && !os.IsNotExist(err) {
363
-
args.Logger.Error("error attempting to read nonce secret", "error", err)
368
+
logger.Error("error attempting to read nonce secret", "error", err)
364
369
} else {
365
370
nonceSecret = maybeSecret
366
371
}
···
398
403
Hostname: args.Hostname,
399
404
ClientManagerArgs: client.ManagerArgs{
400
405
Cli: oauthCli,
401
-
Logger: args.Logger,
406
+
Logger: args.Logger.With("component", "oauth-client-manager"),
402
407
},
403
408
DpopManagerArgs: dpop.ManagerArgs{
404
409
NonceSecret: nonceSecret,
405
410
NonceRotationInterval: constants.NonceMaxRotationInterval / 3,
406
411
OnNonceSecretCreated: func(newNonce []byte) {
407
412
if err := os.WriteFile("nonce.secret", newNonce, 0644); err != nil {
408
-
args.Logger.Error("error writing new nonce secret", "error", err)
413
+
logger.Error("error writing new nonce secret", "error", err)
409
414
}
410
415
},
411
-
Logger: args.Logger,
416
+
Logger: args.Logger.With("component", "dpop-manager"),
412
417
Hostname: args.Hostname,
413
418
},
414
419
}),
···
535
540
}
536
541
537
542
func (s *Server) Serve(ctx context.Context) error {
543
+
logger := s.logger.With("name", "Serve")
544
+
538
545
s.addRoutes()
539
546
540
-
s.logger.Info("migrating...")
547
+
logger.Info("migrating...")
541
548
542
549
s.db.AutoMigrate(
543
550
&models.Actor{},
···
554
561
&provider.OauthAuthorizationRequest{},
555
562
)
556
563
557
-
s.logger.Info("starting cocoon")
564
+
logger.Info("starting cocoon")
558
565
559
566
go func() {
560
567
if err := s.httpd.ListenAndServe(); err != nil {
···
566
573
567
574
go func() {
568
575
if err := s.requestCrawl(ctx); err != nil {
569
-
s.logger.Error("error requesting crawls", "err", err)
576
+
logger.Error("error requesting crawls", "err", err)
570
577
}
571
578
}()
572
579
···
584
591
585
592
logger.Info("requesting crawl with configured relays")
586
593
587
-
if time.Now().Sub(s.lastRequestCrawl) <= 1*time.Minute {
594
+
if time.Since(s.lastRequestCrawl) <= 1*time.Minute {
588
595
return fmt.Errorf("a crawl request has already been made within the last minute")
589
596
}
590
597
···
607
614
}
608
615
609
616
func (s *Server) doBackup() {
617
+
logger := s.logger.With("name", "doBackup")
618
+
610
619
if s.dbType == "postgres" {
611
-
s.logger.Info("skipping S3 backup - PostgreSQL backups should be handled externally (pg_dump, managed database backups, etc.)")
620
+
logger.Info("skipping S3 backup - PostgreSQL backups should be handled externally (pg_dump, managed database backups, etc.)")
612
621
return
613
622
}
614
623
615
624
start := time.Now()
616
625
617
-
s.logger.Info("beginning backup to s3...")
626
+
logger.Info("beginning backup to s3...")
618
627
619
628
var buf bytes.Buffer
620
629
if err := func() error {
621
-
s.logger.Info("reading database bytes...")
630
+
logger.Info("reading database bytes...")
622
631
s.db.Lock()
623
632
defer s.db.Unlock()
624
633
···
634
643
635
644
return nil
636
645
}(); err != nil {
637
-
s.logger.Error("error backing up database", "error", err)
646
+
logger.Error("error backing up database", "error", err)
638
647
return
639
648
}
640
649
641
650
if err := func() error {
642
-
s.logger.Info("sending to s3...")
651
+
logger.Info("sending to s3...")
643
652
644
653
currTime := time.Now().Format("2006-01-02_15-04-05")
645
654
key := "cocoon-backup-" + currTime + ".db"
···
669
678
return fmt.Errorf("error uploading file to s3: %w", err)
670
679
}
671
680
672
-
s.logger.Info("finished uploading backup to s3", "key", key, "duration", time.Now().Sub(start).Seconds())
681
+
logger.Info("finished uploading backup to s3", "key", key, "duration", time.Now().Sub(start).Seconds())
673
682
674
683
return nil
675
684
}(); err != nil {
676
-
s.logger.Error("error uploading database backup", "error", err)
685
+
logger.Error("error uploading database backup", "error", err)
677
686
return
678
687
}
679
688
···
681
690
}
682
691
683
692
func (s *Server) backupRoutine() {
693
+
logger := s.logger.With("name", "backupRoutine")
694
+
684
695
if s.s3Config == nil || !s.s3Config.BackupsEnabled {
685
696
return
686
697
}
687
698
688
699
if s.s3Config.Region == "" {
689
-
s.logger.Warn("no s3 region configured but backups are enabled. backups will not run.")
700
+
logger.Warn("no s3 region configured but backups are enabled. backups will not run.")
690
701
return
691
702
}
692
703
693
704
if s.s3Config.Bucket == "" {
694
-
s.logger.Warn("no s3 bucket configured but backups are enabled. backups will not run.")
705
+
logger.Warn("no s3 bucket configured but backups are enabled. backups will not run.")
695
706
return
696
707
}
697
708
698
709
if s.s3Config.AccessKey == "" {
699
-
s.logger.Warn("no s3 access key configured but backups are enabled. backups will not run.")
710
+
logger.Warn("no s3 access key configured but backups are enabled. backups will not run.")
700
711
return
701
712
}
702
713
703
714
if s.s3Config.SecretKey == "" {
704
-
s.logger.Warn("no s3 secret key configured but backups are enabled. backups will not run.")
715
+
logger.Warn("no s3 secret key configured but backups are enabled. backups will not run.")
705
716
return
706
717
}
707
718
···
729
740
}
730
741
731
742
func (s *Server) UpdateRepo(ctx context.Context, did string, root cid.Cid, rev string) error {
732
-
if err := s.db.Exec("UPDATE repos SET root = ?, rev = ? WHERE did = ?", nil, root.Bytes(), rev, did).Error; err != nil {
743
+
if err := s.db.Exec(ctx, "UPDATE repos SET root = ?, rev = ? WHERE did = ?", nil, root.Bytes(), rev, did).Error; err != nil {
733
744
return err
734
745
}
735
746
+4
-3
server/session.go
+4
-3
server/session.go
···
1
1
package server
2
2
3
3
import (
4
+
"context"
4
5
"time"
5
6
6
7
"github.com/golang-jwt/jwt/v4"
···
13
14
RefreshToken string
14
15
}
15
16
16
-
func (s *Server) createSession(repo *models.Repo) (*Session, error) {
17
+
func (s *Server) createSession(ctx context.Context, repo *models.Repo) (*Session, error) {
17
18
now := time.Now()
18
19
accexp := now.Add(3 * time.Hour)
19
20
refexp := now.Add(7 * 24 * time.Hour)
···
49
50
return nil, err
50
51
}
51
52
52
-
if err := s.db.Create(&models.Token{
53
+
if err := s.db.Create(ctx, &models.Token{
53
54
Token: accessString,
54
55
Did: repo.Did,
55
56
RefreshToken: refreshString,
···
59
60
return nil, err
60
61
}
61
62
62
-
if err := s.db.Create(&models.RefreshToken{
63
+
if err := s.db.Create(ctx, &models.RefreshToken{
63
64
Token: refreshString,
64
65
Did: repo.Did,
65
66
CreatedAt: now,
+4
server/templates/signin.html
+4
server/templates/signin.html
···
26
26
type="password"
27
27
placeholder="Password"
28
28
/>
29
+
{{ if .flashes.tokenrequired }}
30
+
<br />
31
+
<input name="token" id="token" placeholder="Enter your 2FA token" />
32
+
{{ end }}
29
33
<input name="query_params" type="hidden" value="{{ .QueryParams }}" />
30
34
<button class="primary" type="submit" value="Login">Login</button>
31
35
</form>
+3
-3
sqlite_blockstore/sqlite_blockstore.go
+3
-3
sqlite_blockstore/sqlite_blockstore.go
···
45
45
return maybeBlock, nil
46
46
}
47
47
48
-
if err := bs.db.Raw("SELECT * FROM blocks WHERE did = ? AND cid = ?", nil, bs.did, cid.Bytes()).Scan(&block).Error; err != nil {
48
+
if err := bs.db.Raw(ctx, "SELECT * FROM blocks WHERE did = ? AND cid = ?", nil, bs.did, cid.Bytes()).Scan(&block).Error; err != nil {
49
49
return nil, err
50
50
}
51
51
···
71
71
Value: block.RawData(),
72
72
}
73
73
74
-
if err := bs.db.Create(&b, []clause.Expression{clause.OnConflict{
74
+
if err := bs.db.Create(ctx, &b, []clause.Expression{clause.OnConflict{
75
75
Columns: []clause.Column{{Name: "did"}, {Name: "cid"}},
76
76
UpdateAll: true,
77
77
}}).Error; err != nil {
···
94
94
}
95
95
96
96
func (bs *SqliteBlockstore) PutMany(ctx context.Context, blocks []blocks.Block) error {
97
-
tx := bs.db.BeginDangerously()
97
+
tx := bs.db.BeginDangerously(ctx)
98
98
99
99
for _, block := range blocks {
100
100
bs.inserts[block.Cid()] = block
+1
-1
test.go
+1
-1
test.go
···
32
32
33
33
u.Path = "xrpc/com.atproto.sync.subscribeRepos"
34
34
conn, _, err := dialer.Dial(u.String(), http.Header{
35
-
"User-Agent": []string{fmt.Sprintf("hot-topic/0.0.0")},
35
+
"User-Agent": []string{"cocoon-test/0.0.0"},
36
36
})
37
37
if err != nil {
38
38
return fmt.Errorf("subscribing to firehose failed (dialing): %w", err)