An atproto PDS written in Go

Compare changes

Choose any two refs to compare.

Changed files
+830 -505
cmd
cocoon
internal
db
metrics
models
oauth
server
templates
sqlite_blockstore
+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"),
+18 -16
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 ··· 24 25 github.com/joho/godotenv v1.5.1 25 26 github.com/labstack/echo-contrib v0.17.4 26 27 github.com/labstack/echo/v4 v4.13.3 27 - github.com/lestrrat-go/jwx/v2 v2.0.12 28 + github.com/lestrrat-go/jwx/v2 v2.0.21 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 34 36 gorm.io/driver/postgres v1.5.7 35 37 gorm.io/driver/sqlite v1.5.7 36 38 gorm.io/gorm v1.25.12 ··· 57 59 github.com/gorilla/securecookie v1.1.2 // indirect 58 60 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect 59 61 github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 60 - github.com/hashicorp/go-retryablehttp v0.7.5 // indirect 62 + github.com/hashicorp/go-retryablehttp v0.7.7 // indirect 61 63 github.com/hashicorp/golang-lru v1.0.2 // indirect 62 64 github.com/ipfs/bbloom v0.0.4 // indirect 63 65 github.com/ipfs/go-blockservice v0.5.2 // indirect ··· 77 79 github.com/ipld/go-ipld-prime v0.21.0 // indirect 78 80 github.com/jackc/pgpassfile v1.0.0 // indirect 79 81 github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect 80 - github.com/jackc/pgx/v5 v5.5.0 // indirect 82 + github.com/jackc/pgx/v5 v5.5.4 // indirect 81 83 github.com/jackc/puddle/v2 v2.2.1 // indirect 82 84 github.com/jbenet/goprocess v0.1.4 // indirect 83 85 github.com/jinzhu/inflection v1.0.0 // indirect ··· 86 88 github.com/klauspost/cpuid/v2 v2.2.7 // indirect 87 89 github.com/labstack/gommon v0.4.2 // indirect 88 90 github.com/leodido/go-urn v1.4.0 // indirect 89 - github.com/lestrrat-go/blackmagic v1.0.1 // indirect 91 + github.com/lestrrat-go/blackmagic v1.0.2 // indirect 90 92 github.com/lestrrat-go/httpcc v1.0.1 // indirect 91 - github.com/lestrrat-go/httprc v1.0.4 // indirect 93 + github.com/lestrrat-go/httprc v1.0.5 // indirect 92 94 github.com/lestrrat-go/iter v1.0.2 // indirect 93 95 github.com/lestrrat-go/option v1.0.1 // indirect 94 96 github.com/mattn/go-colorable v0.1.14 // indirect ··· 103 105 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 104 106 github.com/opentracing/opentracing-go v1.2.0 // indirect 105 107 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect 106 - github.com/prometheus/client_golang v1.22.0 // indirect 107 108 github.com/prometheus/client_model v0.6.2 // indirect 108 - github.com/prometheus/common v0.63.0 // indirect 109 + github.com/prometheus/common v0.66.1 // indirect 109 110 github.com/prometheus/procfs v0.16.1 // indirect 110 111 github.com/russross/blackfriday/v2 v2.1.0 // indirect 111 112 github.com/samber/lo v1.49.1 // indirect ··· 115 116 github.com/valyala/fasttemplate v1.2.2 // indirect 116 117 github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect 117 118 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect 118 - 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 119 120 go.opentelemetry.io/otel v1.29.0 // indirect 120 121 go.opentelemetry.io/otel/metric v1.29.0 // indirect 121 122 go.opentelemetry.io/otel/trace v1.29.0 // indirect 122 123 go.uber.org/atomic v1.11.0 // indirect 123 124 go.uber.org/multierr v1.11.0 // indirect 124 125 go.uber.org/zap v1.26.0 // indirect 125 - golang.org/x/net v0.40.0 // indirect 126 - golang.org/x/sync v0.14.0 // indirect 127 - golang.org/x/sys v0.33.0 // indirect 128 - 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 129 131 golang.org/x/time v0.11.0 // indirect 130 132 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect 131 - google.golang.org/protobuf v1.36.6 // indirect 133 + google.golang.org/protobuf v1.36.9 // indirect 132 134 gopkg.in/go-playground/assert.v1 v1.2.1 // indirect 133 135 gopkg.in/inf.v0 v0.9.1 // indirect 134 136 lukechampine.com/blake3 v1.2.1 // indirect
+50 -74
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= ··· 34 36 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 35 37 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 36 38 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 37 - github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= 38 39 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= 39 40 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= 40 41 github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8= 41 42 github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c= 43 + github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= 44 + github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 42 45 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 43 46 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 44 47 github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= ··· 77 80 github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= 78 81 github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= 79 82 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= 83 + github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 84 + github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 82 85 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 83 86 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 84 87 github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= ··· 95 98 github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0= 96 99 github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 97 100 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= 101 + github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= 102 + github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 103 + github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= 104 + github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= 102 105 github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= 103 106 github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 104 107 github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= ··· 172 175 github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 173 176 github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= 174 177 github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 175 - github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw= 176 - github.com/jackc/pgx/v5 v5.5.0/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= 178 + github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8= 179 + github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= 177 180 github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= 178 181 github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= 179 182 github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= ··· 195 198 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 196 199 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 197 200 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 201 + github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= 202 + github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 198 203 github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= 199 204 github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 200 205 github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= ··· 206 211 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 207 212 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 208 213 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 214 + github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 215 + github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 209 216 github.com/labstack/echo-contrib v0.17.4 h1:g5mfsrJfJTKv+F5uNKCyrjLK7js+ZW6HTjg4FnDxxgk= 210 217 github.com/labstack/echo-contrib v0.17.4/go.mod h1:9O7ZPAHUeMGTOAfg80YqQduHzt0CzLak36PZRldYrZ0= 211 218 github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= ··· 214 221 github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= 215 222 github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 216 223 github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 217 - github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80= 218 - github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= 224 + github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= 225 + github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= 219 226 github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= 220 227 github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= 221 - github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= 222 - github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= 228 + github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk= 229 + github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= 223 230 github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= 224 231 github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= 225 - github.com/lestrrat-go/jwx/v2 v2.0.12 h1:3d589+5w/b9b7S3DneICPW16AqTyYXB7VRjgluSDWeA= 226 - github.com/lestrrat-go/jwx/v2 v2.0.12/go.mod h1:Mq4KN1mM7bp+5z/W5HS8aCNs5RKZ911G/0y2qUjAQuQ= 227 - github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 232 + github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0= 233 + github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM= 228 234 github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= 229 235 github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 230 236 github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= ··· 289 295 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 290 296 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0= 291 297 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= 298 + github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= 299 + github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= 294 300 github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= 295 301 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= 302 + github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= 303 + github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= 298 304 github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= 299 305 github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= 300 306 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= ··· 317 323 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 318 324 github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 319 325 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 320 - github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 321 - 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 326 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 324 327 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 325 328 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 326 329 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 327 330 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 328 - github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 329 - 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= 331 + github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= 332 + github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 332 333 github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 333 334 github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= 334 335 github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= ··· 349 350 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 350 351 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 351 352 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 352 - github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 353 353 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRySiX3drau9C6Q5CAbNIApmLdat5jPMqChvDA= 354 354 gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= 355 355 gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= 356 356 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= 357 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= 358 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= 359 359 go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= 360 360 go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= 361 361 go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= ··· 367 367 go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= 368 368 go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 369 369 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= 370 + go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 371 + go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 372 372 go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 373 373 go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 374 374 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= ··· 378 378 go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= 379 379 go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= 380 380 go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= 381 + go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= 382 + go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= 381 383 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 382 384 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 383 385 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 384 386 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 385 - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 386 - 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= 387 + golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= 388 + golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= 389 389 golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= 390 390 golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= 391 391 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= ··· 393 393 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 394 394 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 395 395 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 396 - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 397 - 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= 396 + golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= 397 + golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= 400 398 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 401 399 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 402 400 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 403 401 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 404 402 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 405 - golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 406 403 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 407 - golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 408 - golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 409 - 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= 404 + golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= 405 + golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= 412 406 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 413 407 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 414 408 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 415 409 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 416 - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 417 - 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= 410 + golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= 411 + golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 420 412 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 421 413 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 422 414 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 423 415 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 424 416 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 425 417 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 426 - golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 427 418 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 428 - golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 429 - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 430 - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 431 419 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 432 420 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 433 - golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 434 - 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= 421 + golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= 422 + golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 437 423 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 438 - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 439 - golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 440 - golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 441 - golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= 442 424 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 443 425 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 444 - golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 445 - golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 446 - golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 447 - 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= 426 + golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= 427 + golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= 450 428 golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= 451 429 golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= 452 430 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= ··· 459 437 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 460 438 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 461 439 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 462 - golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 463 - 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= 440 + golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= 441 + golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= 466 442 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 467 443 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 468 444 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 469 445 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 470 446 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= 471 447 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= 448 + google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= 449 + google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= 474 450 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 475 451 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 476 452 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+3 -21
internal/db/db.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "sync" 6 5 7 6 "gorm.io/gorm" 8 7 "gorm.io/gorm/clause" ··· 10 9 11 10 type DB struct { 12 11 cli *gorm.DB 13 - mu sync.Mutex 14 12 } 15 13 16 14 func NewDB(cli *gorm.DB) *DB { 17 15 return &DB{ 18 16 cli: cli, 19 - mu: sync.Mutex{}, 20 17 } 21 18 } 22 19 23 20 func (db *DB) Create(ctx context.Context, value any, clauses []clause.Expression) *gorm.DB { 24 - db.mu.Lock() 25 - defer db.mu.Unlock() 26 21 return db.cli.WithContext(ctx).Clauses(clauses...).Create(value) 27 22 } 28 23 29 24 func (db *DB) Save(ctx context.Context, value any, clauses []clause.Expression) *gorm.DB { 30 - db.mu.Lock() 31 - defer db.mu.Unlock() 32 25 return db.cli.WithContext(ctx).Clauses(clauses...).Save(value) 33 26 } 34 27 35 28 func (db *DB) Exec(ctx context.Context, sql string, clauses []clause.Expression, values ...any) *gorm.DB { 36 - db.mu.Lock() 37 - defer db.mu.Unlock() 38 29 return db.cli.WithContext(ctx).Clauses(clauses...).Exec(sql, values...) 39 30 } 40 31 ··· 47 38 } 48 39 49 40 func (db *DB) Delete(ctx context.Context, value any, clauses []clause.Expression) *gorm.DB { 50 - db.mu.Lock() 51 - defer db.mu.Unlock() 52 41 return db.cli.WithContext(ctx).Clauses(clauses...).Delete(value) 53 42 } 54 43 ··· 56 45 return db.cli.WithContext(ctx).First(dest, conds...) 57 46 } 58 47 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 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. 61 - // e.g. when we do apply writes we should also be using a transcation but we don't right now 62 - func (db *DB) BeginDangerously(ctx context.Context) *gorm.DB { 48 + func (db *DB) Begin(ctx context.Context) *gorm.DB { 63 49 return db.cli.WithContext(ctx).Begin() 64 50 } 65 51 66 - func (db *DB) Lock() { 67 - db.mu.Lock() 68 - } 69 - 70 - func (db *DB) Unlock() { 71 - db.mu.Unlock() 52 + func (db *DB) Client() *gorm.DB { 53 + return db.cli 72 54 }
+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
··· 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 -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{},
+4 -4
oauth/dpop/manager.go
··· 75 75 } 76 76 77 77 proof := extractProof(headers) 78 - 79 78 if proof == "" { 80 79 return nil, nil 81 80 } ··· 197 196 198 197 nonce, _ := claims["nonce"].(string) 199 198 if nonce == "" { 200 - // WARN: this _must_ be `use_dpop_nonce` for clients know they should make another request 199 + // reference impl checks if self.nonce is not null before returning an error, but we always have a 200 + // nonce so we do not bother checking 201 201 return nil, ErrUseDpopNonce 202 202 } 203 203 204 204 if nonce != "" && !dm.nonce.Check(nonce) { 205 - // WARN: this _must_ be `use_dpop_nonce` so that clients will fetch a new nonce 205 + // dpop nonce mismatch 206 206 return nil, ErrUseDpopNonce 207 207 } 208 208 ··· 237 237 } 238 238 239 239 func extractProof(headers http.Header) string { 240 - dpopHeaders := headers["Dpop"] 240 + dpopHeaders := headers.Values("dpop") 241 241 switch len(dpopHeaders) { 242 242 case 0: 243 243 return ""
+3 -3
oauth/provider/client_auth.go
··· 19 19 } 20 20 21 21 type AuthenticateClientRequestBase struct { 22 - ClientID string `form:"client_id" json:"client_id" validate:"required"` 23 - ClientAssertionType *string `form:"client_assertion_type" json:"client_assertion_type,omitempty"` 24 - ClientAssertion *string `form:"client_assertion" json:"client_assertion,omitempty"` 22 + ClientID string `form:"client_id" json:"client_id" query:"client_id" validate:"required"` 23 + ClientAssertionType *string `form:"client_assertion_type" json:"client_assertion_type,omitempty" query:"client_assertion_type"` 24 + ClientAssertion *string `form:"client_assertion" json:"client_assertion,omitempty" query:"client_assertion"` 25 25 } 26 26 27 27 func (p *Provider) AuthenticateClient(ctx context.Context, req AuthenticateClientRequestBase, proof *dpop.Proof, opts *AuthenticateClientOptions) (*client.Client, *ClientAuth, error) {
+9 -8
oauth/provider/models.go
··· 32 32 33 33 type ParRequest struct { 34 34 AuthenticateClientRequestBase 35 - ResponseType string `form:"response_type" json:"response_type" validate:"required"` 36 - CodeChallenge *string `form:"code_challenge" json:"code_challenge" validate:"required"` 37 - CodeChallengeMethod string `form:"code_challenge_method" json:"code_challenge_method" validate:"required"` 38 - State string `form:"state" json:"state" validate:"required"` 39 - RedirectURI string `form:"redirect_uri" json:"redirect_uri" validate:"required"` 40 - Scope string `form:"scope" json:"scope" validate:"required"` 41 - LoginHint *string `form:"login_hint" json:"login_hint,omitempty"` 42 - DpopJkt *string `form:"dpop_jkt" json:"dpop_jkt,omitempty"` 35 + ResponseType string `form:"response_type" json:"response_type" query:"response_type" validate:"required"` 36 + CodeChallenge *string `form:"code_challenge" json:"code_challenge" query:"code_challenge" validate:"required"` 37 + CodeChallengeMethod string `form:"code_challenge_method" json:"code_challenge_method" query:"code_challenge_method" validate:"required"` 38 + State string `form:"state" json:"state" query:"state" validate:"required"` 39 + RedirectURI string `form:"redirect_uri" json:"redirect_uri" query:"redirect_uri" validate:"required"` 40 + Scope string `form:"scope" json:"scope" query:"scope" validate:"required"` 41 + LoginHint *string `form:"login_hint" query:"login_hint" json:"login_hint,omitempty"` 42 + DpopJkt *string `form:"dpop_jkt" query:"dpop_jkt" json:"dpop_jkt,omitempty"` 43 + ResponseMode *string `form:"response_mode" json:"response_mode,omitempty" query:"response_mode"` 43 44 } 44 45 45 46 func (opr *ParRequest) Scan(value any) error {
+2 -1
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") 15 16 16 17 repo, sess, err := s.getSessionRepoOrErr(e) 17 18 if err != nil { ··· 22 23 23 24 var tokens []provider.OauthToken 24 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 { 25 - s.logger.Error("couldnt fetch oauth sessions for account", "did", repo.Repo.Did, "error", err) 26 + logger.Error("couldnt fetch oauth sessions for account", "did", repo.Repo.Did, "error", err) 26 27 sess.AddFlash("Unable to fetch sessions. See server logs for more details.", "error") 27 28 sess.Save(e.Request(), e.Response()) 28 29 return e.Render(200, "account.html", map[string]any{
+3 -2
server/handle_account_revoke.go
··· 11 11 12 12 func (s *Server) handleAccountRevoke(e echo.Context) error { 13 13 ctx := e.Request().Context() 14 + logger := s.logger.With("name", "handleAcocuntRevoke") 14 15 15 16 var req AccountRevokeInput 16 17 if err := e.Bind(&req); err != nil { 17 - s.logger.Error("could not bind account revoke request", "error", err) 18 + logger.Error("could not bind account revoke request", "error", err) 18 19 return helpers.ServerError(e, nil) 19 20 } 20 21 ··· 24 25 } 25 26 26 27 if err := s.db.Exec(ctx, "DELETE FROM oauth_tokens WHERE sub = ? AND token = ?", nil, repo.Repo.Did, req.Token).Error; err != nil { 27 - s.logger.Error("couldnt delete oauth session for account", "did", repo.Repo.Did, "token", req.Token, "error", err) 28 + logger.Error("couldnt delete oauth session for account", "did", repo.Repo.Did, "token", req.Token, "error", err) 28 29 sess.AddFlash("Unable to revoke session. See server logs for more details.", "error") 29 30 sess.Save(e.Request(), e.Response()) 30 31 return e.Redirect(303, "/account")
+58 -10
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" ··· 15 17 ) 16 18 17 19 type OauthSigninInput struct { 18 - Username string `form:"username"` 19 - Password string `form:"password"` 20 - QueryParams string `form:"query_params"` 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) { ··· 44 47 func getFlashesFromSession(e echo.Context, sess *sessions.Session) map[string]any { 45 48 defer sess.Save(e.Request(), e.Response()) 46 49 return map[string]any{ 47 - "errors": sess.Flashes("error"), 48 - "successes": sess.Flashes("success"), 50 + "errors": sess.Flashes("error"), 51 + "successes": sess.Flashes("success"), 52 + "tokenrequired": sess.Flashes("tokenrequired"), 49 53 } 50 54 } 51 55 ··· 63 67 64 68 func (s *Server) handleAccountSigninPost(e echo.Context) error { 65 69 ctx := e.Request().Context() 70 + logger := s.logger.With("name", "handleAccountSigninPost") 66 71 67 72 var req OauthSigninInput 68 73 if err := e.Bind(&req); err != nil { 69 - s.logger.Error("error binding sign in req", "error", err) 74 + logger.Error("error binding sign in req", "error", err) 70 75 return helpers.ServerError(e, nil) 71 76 } 72 77 ··· 82 87 idtype = "email" 83 88 } 84 89 90 + queryParams := "" 91 + if req.QueryParams != "" { 92 + queryParams = fmt.Sprintf("?%s", req.QueryParams) 93 + } 94 + 85 95 // TODO: we should make this a helper since we do it for the base create_session as well 86 96 var repo models.RepoActor 87 97 var err error ··· 100 110 sess.AddFlash("Something went wrong!", "error") 101 111 } 102 112 sess.Save(e.Request(), e.Response()) 103 - return e.Redirect(303, "/account/signin") 113 + return e.Redirect(303, "/account/signin"+queryParams) 104 114 } 105 115 106 116 if err := bcrypt.CompareHashAndPassword([]byte(repo.Password), []byte(req.Password)); err != nil { ··· 110 120 sess.AddFlash("Something went wrong!", "error") 111 121 } 112 122 sess.Save(e.Request(), e.Response()) 113 - 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 + } 114 162 } 115 163 116 164 sess.Options = &sessions.Options{ ··· 126 174 return err 127 175 } 128 176 129 - if req.QueryParams != "" { 130 - return e.Redirect(303, "/oauth/authorize?"+req.QueryParams) 177 + if queryParams != "" { 178 + return e.Redirect(303, "/oauth/authorize"+queryParams) 131 179 } else { 132 180 return e.Redirect(303, "/account") 133 181 }
+3 -2
server/handle_identity_request_plc_operation.go
··· 11 11 12 12 func (s *Server) handleIdentityRequestPlcOperationSignature(e echo.Context) error { 13 13 ctx := e.Request().Context() 14 + logger := s.logger.With("name", "handleIdentityRequestPlcOperationSignature") 14 15 15 16 urepo := e.Get("repo").(*models.RepoActor) 16 17 ··· 18 19 eat := time.Now().Add(10 * time.Minute).UTC() 19 20 20 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 { 21 - s.logger.Error("error updating user", "error", err) 22 + logger.Error("error updating user", "error", err) 22 23 return helpers.ServerError(e, nil) 23 24 } 24 25 25 26 if err := s.sendPlcTokenReset(urepo.Email, urepo.Handle, code); err != nil { 26 - s.logger.Error("error sending mail", "error", err) 27 + logger.Error("error sending mail", "error", err) 27 28 return helpers.ServerError(e, nil) 28 29 } 29 30
+7 -5
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 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 { 96 - s.logger.Error("error updating repo", "error", err) 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
··· 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{
+7 -5
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{ ··· 95 97 }) 96 98 97 99 if err := s.db.Exec(ctx, "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) 100 + logger.Error("error updating handle in db", "error", err) 99 101 return helpers.ServerError(e, nil) 100 102 } 101 103
+12 -11
server/handle_import_repo.go
··· 19 19 20 20 func (s *Server) handleRepoImportRepo(e echo.Context) error { 21 21 ctx := e.Request().Context() 22 + logger := s.logger.With("name", "handleImportRepo") 22 23 23 24 urepo := e.Get("repo").(*models.RepoActor) 24 25 25 26 b, err := io.ReadAll(e.Request().Body) 26 27 if err != nil { 27 - s.logger.Error("could not read bytes in import request", "error", err) 28 + logger.Error("could not read bytes in import request", "error", err) 28 29 return helpers.ServerError(e, nil) 29 30 } 30 31 ··· 32 33 33 34 cs, err := car.NewCarReader(bytes.NewReader(b)) 34 35 if err != nil { 35 - s.logger.Error("could not read car in import request", "error", err) 36 + logger.Error("could not read car in import request", "error", err) 36 37 return helpers.ServerError(e, nil) 37 38 } 38 39 39 40 orderedBlocks := []blocks.Block{} 40 41 currBlock, err := cs.Next() 41 42 if err != nil { 42 - s.logger.Error("could not get first block from car", "error", err) 43 + logger.Error("could not get first block from car", "error", err) 43 44 return helpers.ServerError(e, nil) 44 45 } 45 46 currBlockCt := 1 46 47 47 48 for currBlock != nil { 48 - s.logger.Info("someone is importing their repo", "block", currBlockCt) 49 + logger.Info("someone is importing their repo", "block", currBlockCt) 49 50 orderedBlocks = append(orderedBlocks, currBlock) 50 51 next, _ := cs.Next() 51 52 currBlock = next ··· 55 56 slices.Reverse(orderedBlocks) 56 57 57 58 if err := bs.PutMany(context.TODO(), orderedBlocks); err != nil { 58 - s.logger.Error("could not insert blocks", "error", err) 59 + logger.Error("could not insert blocks", "error", err) 59 60 return helpers.ServerError(e, nil) 60 61 } 61 62 62 63 r, err := repo.OpenRepo(context.TODO(), bs, cs.Header.Roots[0]) 63 64 if err != nil { 64 - s.logger.Error("could not open repo", "error", err) 65 + logger.Error("could not open repo", "error", err) 65 66 return helpers.ServerError(e, nil) 66 67 } 67 68 68 - tx := s.db.BeginDangerously(ctx) 69 + tx := s.db.Begin(ctx) 69 70 70 71 clock := syntax.NewTIDClock(0) 71 72 ··· 76 77 cidStr := cid.String() 77 78 b, err := bs.Get(context.TODO(), cid) 78 79 if err != nil { 79 - s.logger.Error("record bytes don't exist in blockstore", "error", err) 80 + logger.Error("record bytes don't exist in blockstore", "error", err) 80 81 return helpers.ServerError(e, nil) 81 82 } 82 83 ··· 96 97 return nil 97 98 }); err != nil { 98 99 tx.Rollback() 99 - s.logger.Error("record bytes don't exist in blockstore", "error", err) 100 + logger.Error("record bytes don't exist in blockstore", "error", err) 100 101 return helpers.ServerError(e, nil) 101 102 } 102 103 ··· 104 105 105 106 root, rev, err := r.Commit(context.TODO(), urepo.SignFor) 106 107 if err != nil { 107 - s.logger.Error("error committing", "error", err) 108 + logger.Error("error committing", "error", err) 108 109 return helpers.ServerError(e, nil) 109 110 } 110 111 111 112 if err := s.UpdateRepo(context.TODO(), urepo.Repo.Did, root, rev); err != nil { 112 - s.logger.Error("error updating repo after commit", "error", err) 113 + logger.Error("error updating repo after commit", "error", err) 113 114 return helpers.ServerError(e, nil) 114 115 } 115 116
+98 -21
server/handle_oauth_authorize.go
··· 1 1 package server 2 2 3 3 import ( 4 + "fmt" 4 5 "net/url" 5 6 "strings" 6 7 "time" ··· 8 9 "github.com/Azure/go-autorest/autorest/to" 9 10 "github.com/haileyok/cocoon/internal/helpers" 10 11 "github.com/haileyok/cocoon/oauth" 12 + "github.com/haileyok/cocoon/oauth/constants" 11 13 "github.com/haileyok/cocoon/oauth/provider" 12 14 "github.com/labstack/echo/v4" 13 15 ) 14 16 17 + type HandleOauthAuthorizeGetInput struct { 18 + RequestUri string `query:"request_uri"` 19 + } 20 + 15 21 func (s *Server) handleOauthAuthorizeGet(e echo.Context) error { 16 22 ctx := e.Request().Context() 17 23 18 - reqUri := e.QueryParam("request_uri") 19 - if reqUri == "" { 20 - // render page for logged out dev 21 - if s.config.Version == "dev" { 22 - return e.Render(200, "authorize.html", map[string]any{ 23 - "Scopes": []string{"atproto", "transition:generic"}, 24 - "AppName": "DEV MODE AUTHORIZATION PAGE", 25 - "Handle": "paula.cocoon.social", 26 - "RequestUri": "", 27 - }) 24 + logger := s.logger.With("name", "handleOauthAuthorizeGet") 25 + 26 + var input HandleOauthAuthorizeGetInput 27 + if err := e.Bind(&input); err != nil { 28 + logger.Error("error binding request", "err", err) 29 + return fmt.Errorf("error binding request") 30 + } 31 + 32 + var reqId string 33 + if input.RequestUri != "" { 34 + id, err := oauth.DecodeRequestUri(input.RequestUri) 35 + if err != nil { 36 + logger.Error("no request uri found in input", "url", e.Request().URL.String()) 37 + return helpers.InputError(e, to.StringPtr("no request uri")) 38 + } 39 + reqId = id 40 + } else { 41 + var parRequest provider.ParRequest 42 + if err := e.Bind(&parRequest); err != nil { 43 + s.logger.Error("error binding for standard auth request", "error", err) 44 + return helpers.InputError(e, to.StringPtr("InvalidRequest")) 45 + } 46 + 47 + if err := e.Validate(parRequest); err != nil { 48 + // render page for logged out dev 49 + if s.config.Version == "dev" && parRequest.ClientID == "" { 50 + return e.Render(200, "authorize.html", map[string]any{ 51 + "Scopes": []string{"atproto", "transition:generic"}, 52 + "AppName": "DEV MODE AUTHORIZATION PAGE", 53 + "Handle": "paula.cocoon.social", 54 + "RequestUri": "", 55 + }) 56 + } 57 + return helpers.InputError(e, to.StringPtr("no request uri and invalid parameters")) 58 + } 59 + 60 + client, clientAuth, err := s.oauthProvider.AuthenticateClient(ctx, parRequest.AuthenticateClientRequestBase, nil, &provider.AuthenticateClientOptions{ 61 + AllowMissingDpopProof: true, 62 + }) 63 + if err != nil { 64 + s.logger.Error("error authenticating client in standard request", "client_id", parRequest.ClientID, "error", err) 65 + return helpers.ServerError(e, to.StringPtr(err.Error())) 66 + } 67 + 68 + if parRequest.DpopJkt == nil { 69 + if client.Metadata.DpopBoundAccessTokens { 70 + } 71 + } else { 72 + if !client.Metadata.DpopBoundAccessTokens { 73 + msg := "dpop bound access tokens are not enabled for this client" 74 + return helpers.InputError(e, &msg) 75 + } 76 + } 77 + 78 + eat := time.Now().Add(constants.ParExpiresIn) 79 + id := oauth.GenerateRequestId() 80 + 81 + authRequest := &provider.OauthAuthorizationRequest{ 82 + RequestId: id, 83 + ClientId: client.Metadata.ClientID, 84 + ClientAuth: *clientAuth, 85 + Parameters: parRequest, 86 + ExpiresAt: eat, 28 87 } 29 - return helpers.InputError(e, to.StringPtr("no request uri")) 88 + 89 + if err := s.db.Create(ctx, authRequest, nil).Error; err != nil { 90 + s.logger.Error("error creating auth request in db", "error", err) 91 + return helpers.ServerError(e, nil) 92 + } 93 + 94 + input.RequestUri = oauth.EncodeRequestUri(id) 95 + reqId = id 96 + 30 97 } 31 98 32 99 repo, _, err := s.getSessionRepoOrErr(e) ··· 34 101 return e.Redirect(303, "/account/signin?"+e.QueryParams().Encode()) 35 102 } 36 103 37 - reqId, err := oauth.DecodeRequestUri(reqUri) 38 - if err != nil { 39 - return helpers.InputError(e, to.StringPtr(err.Error())) 40 - } 41 - 42 104 var req provider.OauthAuthorizationRequest 43 105 if err := s.db.Raw(ctx, "SELECT * FROM oauth_authorization_requests WHERE request_id = ?", nil, reqId).Scan(&req).Error; err != nil { 44 106 return helpers.ServerError(e, to.StringPtr(err.Error())) ··· 60 122 data := map[string]any{ 61 123 "Scopes": scopes, 62 124 "AppName": appName, 63 - "RequestUri": reqUri, 125 + "RequestUri": input.RequestUri, 64 126 "QueryParams": e.QueryParams().Encode(), 65 127 "Handle": repo.Actor.Handle, 66 128 } ··· 75 137 76 138 func (s *Server) handleOauthAuthorizePost(e echo.Context) error { 77 139 ctx := e.Request().Context() 140 + logger := s.logger.With("name", "handleOauthAuthorizePost") 78 141 79 142 repo, _, err := s.getSessionRepoOrErr(e) 80 143 if err != nil { ··· 83 146 84 147 var req OauthAuthorizePostRequest 85 148 if err := e.Bind(&req); err != nil { 86 - s.logger.Error("error binding authorize post request", "error", err) 149 + logger.Error("error binding authorize post request", "error", err) 87 150 return helpers.InputError(e, nil) 88 151 } 89 152 ··· 118 181 code := oauth.GenerateCode() 119 182 120 183 if err := s.db.Exec(ctx, "UPDATE oauth_authorization_requests SET sub = ?, code = ?, accepted = ?, ip = ? WHERE request_id = ?", nil, repo.Repo.Did, code, true, e.RealIP(), reqId).Error; err != nil { 121 - s.logger.Error("error updating authorization request", "error", err) 184 + logger.Error("error updating authorization request", "error", err) 122 185 return helpers.ServerError(e, nil) 123 186 } 124 187 ··· 128 191 q.Set("code", code) 129 192 130 193 hashOrQuestion := "?" 131 - if authReq.ClientAuth.Method != "private_key_jwt" { 132 - hashOrQuestion = "#" 194 + if authReq.Parameters.ResponseMode != nil { 195 + switch *authReq.Parameters.ResponseMode { 196 + case "fragment": 197 + hashOrQuestion = "#" 198 + case "query": 199 + // do nothing 200 + break 201 + default: 202 + if authReq.Parameters.ResponseType != "code" { 203 + hashOrQuestion = "#" 204 + } 205 + } 206 + } else { 207 + if authReq.Parameters.ResponseType != "code" { 208 + hashOrQuestion = "#" 209 + } 133 210 } 134 211 135 212 return e.Redirect(303, authReq.Parameters.RedirectURI+hashOrQuestion+q.Encode())
+9 -7
server/handle_oauth_par.go
··· 20 20 21 21 func (s *Server) handleOauthPar(e echo.Context) error { 22 22 ctx := e.Request().Context() 23 + logger := s.logger.With("name", "handleOauthPar") 23 24 24 25 var parRequest provider.ParRequest 25 26 if err := e.Bind(&parRequest); err != nil { 26 - s.logger.Error("error binding for par request", "error", err) 27 + logger.Error("error binding for par request", "error", err) 27 28 return helpers.ServerError(e, nil) 28 29 } 29 30 30 31 if err := e.Validate(parRequest); err != nil { 31 - s.logger.Error("missing parameters for par request", "error", err) 32 + logger.Error("missing parameters for par request", "error", err) 32 33 return helpers.InputError(e, nil) 33 34 } 34 35 ··· 41 42 e.Response().Header().Set("DPoP-Nonce", nonce) 42 43 e.Response().Header().Add("access-control-expose-headers", "DPoP-Nonce") 43 44 } 45 + logger.Error("nonce error: use_dpop_nonce", "headers", e.Request().Header) 44 46 return e.JSON(400, map[string]string{ 45 47 "error": "use_dpop_nonce", 46 48 }) 47 49 } 48 - s.logger.Error("error getting dpop proof", "error", err) 50 + logger.Error("error getting dpop proof", "error", err) 49 51 return helpers.InputError(e, nil) 50 52 } 51 53 ··· 55 57 AllowMissingDpopProof: true, 56 58 }) 57 59 if err != nil { 58 - s.logger.Error("error authenticating client", "client_id", parRequest.ClientID, "error", err) 60 + logger.Error("error authenticating client", "client_id", parRequest.ClientID, "error", err) 59 61 return helpers.InputError(e, to.StringPtr(err.Error())) 60 62 } 61 63 ··· 66 68 } else { 67 69 if !client.Metadata.DpopBoundAccessTokens { 68 70 msg := "dpop bound access tokens are not enabled for this client" 69 - s.logger.Error(msg) 71 + logger.Error(msg) 70 72 return helpers.InputError(e, &msg) 71 73 } 72 74 73 75 if dpopProof.JKT != *parRequest.DpopJkt { 74 76 msg := "supplied dpop jkt does not match header dpop jkt" 75 - s.logger.Error(msg) 77 + logger.Error(msg) 76 78 return helpers.InputError(e, &msg) 77 79 } 78 80 } ··· 89 91 } 90 92 91 93 if err := s.db.Create(ctx, authRequest, nil).Error; err != nil { 92 - s.logger.Error("error creating auth request in db", "error", err) 94 + logger.Error("error creating auth request in db", "error", err) 93 95 return helpers.ServerError(e, nil) 94 96 } 95 97
+9 -8
server/handle_oauth_token.go
··· 39 39 40 40 func (s *Server) handleOauthToken(e echo.Context) error { 41 41 ctx := e.Request().Context() 42 + logger := s.logger.With("name", "handleOauthToken") 42 43 43 44 var req OauthTokenRequest 44 45 if err := e.Bind(&req); err != nil { 45 - s.logger.Error("error binding token request", "error", err) 46 + logger.Error("error binding token request", "error", err) 46 47 return helpers.ServerError(e, nil) 47 48 } 48 49 ··· 58 59 "error": "use_dpop_nonce", 59 60 }) 60 61 } 61 - s.logger.Error("error getting dpop proof", "error", err) 62 + logger.Error("error getting dpop proof", "error", err) 62 63 return helpers.InputError(e, nil) 63 64 } 64 65 ··· 66 67 AllowMissingDpopProof: true, 67 68 }) 68 69 if err != nil { 69 - s.logger.Error("error authenticating client", "client_id", req.ClientID, "error", err) 70 + logger.Error("error authenticating client", "client_id", req.ClientID, "error", err) 70 71 return helpers.InputError(e, to.StringPtr(err.Error())) 71 72 } 72 73 ··· 87 88 var authReq provider.OauthAuthorizationRequest 88 89 // get the lil guy and delete him 89 90 if err := s.db.Raw(ctx, "DELETE FROM oauth_authorization_requests WHERE code = ? RETURNING *", nil, *req.Code).Scan(&authReq).Error; err != nil { 90 - s.logger.Error("error finding authorization request", "error", err) 91 + logger.Error("error finding authorization request", "error", err) 91 92 return helpers.ServerError(e, nil) 92 93 } 93 94 ··· 112 113 case "S256": 113 114 inputChal, err := base64.RawURLEncoding.DecodeString(*authReq.Parameters.CodeChallenge) 114 115 if err != nil { 115 - s.logger.Error("error decoding code challenge", "error", err) 116 + logger.Error("error decoding code challenge", "error", err) 116 117 return helpers.ServerError(e, nil) 117 118 } 118 119 ··· 173 174 RefreshToken: refreshToken, 174 175 Ip: authReq.Ip, 175 176 }, nil).Error; err != nil { 176 - s.logger.Error("error creating token in db", "error", err) 177 + logger.Error("error creating token in db", "error", err) 177 178 return helpers.ServerError(e, nil) 178 179 } 179 180 ··· 202 203 203 204 var oauthToken provider.OauthToken 204 205 if err := s.db.Raw(ctx, "SELECT * FROM oauth_tokens WHERE refresh_token = ?", nil, req.RefreshToken).Scan(&oauthToken).Error; err != nil { 205 - s.logger.Error("error finding oauth token by refresh token", "error", err, "refresh_token", req.RefreshToken) 206 + logger.Error("error finding oauth token by refresh token", "error", err, "refresh_token", req.RefreshToken) 206 207 return helpers.ServerError(e, nil) 207 208 } 208 209 ··· 260 261 } 261 262 262 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 { 263 - s.logger.Error("error updating token", "error", err) 264 + logger.Error("error updating token", "error", err) 264 265 return helpers.ServerError(e, nil) 265 266 } 266 267
+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()
+5 -4
server/handle_repo_apply_writes.go
··· 27 27 28 28 func (s *Server) handleApplyWrites(e echo.Context) error { 29 29 ctx := e.Request().Context() 30 + logger := s.logger.With("name", "handleRepoApplyWrites") 30 31 31 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 42 43 repo := e.Get("repo").(*models.RepoActor) 43 44 44 45 if repo.Repo.Did != req.Repo { 45 - s.logger.Warn("mismatched repo/auth") 46 + logger.Warn("mismatched repo/auth") 46 47 return helpers.InputError(e, nil) 47 48 } 48 49 ··· 58 59 59 60 results, err := s.repoman.applyWrites(ctx, repo.Repo, ops, req.SwapCommit) 60 61 if err != nil { 61 - s.logger.Error("error applying writes", "error", err) 62 + logger.Error("error applying writes", "error", err) 62 63 return helpers.ServerError(e, nil) 63 64 } 64 65
+5 -4
server/handle_repo_create_record.go
··· 18 18 19 19 func (s *Server) handleCreateRecord(e echo.Context) error { 20 20 ctx := e.Request().Context() 21 + logger := s.logger.With("name", "handleCreateRecord") 21 22 22 23 repo := e.Get("repo").(*models.RepoActor) 23 24 24 25 var req ComAtprotoRepoCreateRecordInput 25 26 if err := e.Bind(&req); err != nil { 26 - s.logger.Error("error binding", "error", err) 27 + logger.Error("error binding", "error", err) 27 28 return helpers.ServerError(e, nil) 28 29 } 29 30 30 31 if err := e.Validate(req); err != nil { 31 - s.logger.Error("error validating", "error", err) 32 + logger.Error("error validating", "error", err) 32 33 return helpers.InputError(e, nil) 33 34 } 34 35 35 36 if repo.Repo.Did != req.Repo { 36 - s.logger.Warn("mismatched repo/auth") 37 + logger.Warn("mismatched repo/auth") 37 38 return helpers.InputError(e, nil) 38 39 } 39 40 ··· 53 54 }, 54 55 }, req.SwapCommit) 55 56 if err != nil { 56 - s.logger.Error("error applying writes", "error", err) 57 + logger.Error("error applying writes", "error", err) 57 58 return helpers.ServerError(e, nil) 58 59 } 59 60
+5 -4
server/handle_repo_delete_record.go
··· 16 16 17 17 func (s *Server) handleDeleteRecord(e echo.Context) error { 18 18 ctx := e.Request().Context() 19 + logger := s.logger.With("name", "handleDeleteRecord") 19 20 20 21 repo := e.Get("repo").(*models.RepoActor) 21 22 22 23 var req ComAtprotoRepoDeleteRecordInput 23 24 if err := e.Bind(&req); err != nil { 24 - s.logger.Error("error binding", "error", err) 25 + logger.Error("error binding", "error", err) 25 26 return helpers.ServerError(e, nil) 26 27 } 27 28 28 29 if err := e.Validate(req); err != nil { 29 - s.logger.Error("error validating", "error", err) 30 + logger.Error("error validating", "error", err) 30 31 return helpers.InputError(e, nil) 31 32 } 32 33 33 34 if repo.Repo.Did != req.Repo { 34 - s.logger.Warn("mismatched repo/auth") 35 + logger.Warn("mismatched repo/auth") 35 36 return helpers.InputError(e, nil) 36 37 } 37 38 ··· 44 45 }, 45 46 }, req.SwapCommit) 46 47 if err != nil { 47 - s.logger.Error("error applying writes", "error", err) 48 + logger.Error("error applying writes", "error", err) 48 49 return helpers.ServerError(e, nil) 49 50 } 50 51
+4 -3
server/handle_repo_describe_repo.go
··· 21 21 22 22 func (s *Server) handleDescribeRepo(e echo.Context) error { 23 23 ctx := e.Request().Context() 24 + logger := s.logger.With("name", "handleDescribeRepo") 24 25 25 26 did := e.QueryParam("repo") 26 27 repo, err := s.getRepoActorByDid(ctx, did) ··· 29 30 return helpers.InputError(e, to.StringPtr("RepoNotFound")) 30 31 } 31 32 32 - s.logger.Error("error looking up repo", "error", err) 33 + logger.Error("error looking up repo", "error", err) 33 34 return helpers.ServerError(e, nil) 34 35 } 35 36 ··· 37 38 38 39 diddoc, err := s.passport.FetchDoc(e.Request().Context(), repo.Repo.Did) 39 40 if err != nil { 40 - s.logger.Error("error fetching diddoc", "error", err) 41 + logger.Error("error fetching diddoc", "error", err) 41 42 return helpers.ServerError(e, nil) 42 43 } 43 44 ··· 67 68 68 69 var records []models.Record 69 70 if err := s.db.Raw(ctx, "SELECT DISTINCT(nsid) FROM records WHERE did = ?", nil, repo.Repo.Did).Scan(&records).Error; err != nil { 70 - s.logger.Error("error getting collections", "error", err) 71 + logger.Error("error getting collections", "error", err) 71 72 return helpers.ServerError(e, nil) 72 73 } 73 74
+2 -1
server/handle_repo_list_missing_blobs.go
··· 23 23 24 24 func (s *Server) handleListMissingBlobs(e echo.Context) error { 25 25 ctx := e.Request().Context() 26 + logger := s.logger.With("name", "handleListMissingBlos") 26 27 27 28 urepo := e.Get("repo").(*models.RepoActor) 28 29 ··· 38 39 39 40 var records []models.Record 40 41 if err := s.db.Raw(ctx, "SELECT * FROM records WHERE did = ?", nil, urepo.Repo.Did).Scan(&records).Error; err != nil { 41 - s.logger.Error("failed to get records for listMissingBlobs", "error", err) 42 + logger.Error("failed to get records for listMissingBlobs", "error", err) 42 43 return helpers.ServerError(e, nil) 43 44 } 44 45
+3 -2
server/handle_repo_list_records.go
··· 47 47 48 48 func (s *Server) handleListRecords(e echo.Context) error { 49 49 ctx := e.Request().Context() 50 + logger := s.logger.With("name", "handleListRecords") 50 51 51 52 var req ComAtprotoRepoListRecordsRequest 52 53 if err := e.Bind(&req); err != nil { 53 - s.logger.Error("could not bind list records request", "error", err) 54 + logger.Error("could not bind list records request", "error", err) 54 55 return helpers.ServerError(e, nil) 55 56 } 56 57 ··· 96 97 97 98 var records []models.Record 98 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 { 99 - s.logger.Error("error getting records", "error", err) 100 + logger.Error("error getting records", "error", err) 100 101 return helpers.ServerError(e, nil) 101 102 } 102 103
+5 -4
server/handle_repo_put_record.go
··· 18 18 19 19 func (s *Server) handlePutRecord(e echo.Context) error { 20 20 ctx := e.Request().Context() 21 + logger := s.logger.With("name", "handlePutRecord") 21 22 22 23 repo := e.Get("repo").(*models.RepoActor) 23 24 24 25 var req ComAtprotoRepoPutRecordInput 25 26 if err := e.Bind(&req); err != nil { 26 - s.logger.Error("error binding", "error", err) 27 + logger.Error("error binding", "error", err) 27 28 return helpers.ServerError(e, nil) 28 29 } 29 30 30 31 if err := e.Validate(req); err != nil { 31 - s.logger.Error("error validating", "error", err) 32 + logger.Error("error validating", "error", err) 32 33 return helpers.InputError(e, nil) 33 34 } 34 35 35 36 if repo.Repo.Did != req.Repo { 36 - s.logger.Warn("mismatched repo/auth") 37 + logger.Warn("mismatched repo/auth") 37 38 return helpers.InputError(e, nil) 38 39 } 39 40 ··· 53 54 }, 54 55 }, req.SwapCommit) 55 56 if err != nil { 56 - s.logger.Error("error applying writes", "error", err) 57 + logger.Error("error applying writes", "error", err) 57 58 return helpers.ServerError(e, nil) 58 59 } 59 60
+8 -7
server/handle_repo_upload_blob.go
··· 33 33 34 34 func (s *Server) handleRepoUploadBlob(e echo.Context) error { 35 35 ctx := e.Request().Context() 36 + logger := s.logger.With("name", "handleRepoUploadBlob") 36 37 37 38 urepo := e.Get("repo").(*models.RepoActor) 38 39 ··· 54 55 } 55 56 56 57 if err := s.db.Create(ctx, &blob, nil).Error; err != nil { 57 - s.logger.Error("error creating new blob in db", "error", err) 58 + logger.Error("error creating new blob in db", "error", err) 58 59 return helpers.ServerError(e, nil) 59 60 } 60 61 ··· 71 72 break 72 73 } 73 74 } else if err != nil && err != io.ErrUnexpectedEOF { 74 - s.logger.Error("error reading blob", "error", err) 75 + logger.Error("error reading blob", "error", err) 75 76 return helpers.ServerError(e, nil) 76 77 } 77 78 ··· 87 88 } 88 89 89 90 if err := s.db.Create(ctx, &blobPart, nil).Error; err != nil { 90 - s.logger.Error("error adding blob part to db", "error", err) 91 + logger.Error("error adding blob part to db", "error", err) 91 92 return helpers.ServerError(e, nil) 92 93 } 93 94 } ··· 100 101 101 102 c, err := cid.NewPrefixV1(cid.Raw, multihash.SHA2_256).Sum(fulldata.Bytes()) 102 103 if err != nil { 103 - s.logger.Error("error creating cid prefix", "error", err) 104 + logger.Error("error creating cid prefix", "error", err) 104 105 return helpers.ServerError(e, nil) 105 106 } 106 107 ··· 117 118 118 119 sess, err := session.NewSession(config) 119 120 if err != nil { 120 - s.logger.Error("error creating aws session", "error", err) 121 + logger.Error("error creating aws session", "error", err) 121 122 return helpers.ServerError(e, nil) 122 123 } 123 124 ··· 128 129 Key: aws.String(fmt.Sprintf("blobs/%s/%s", urepo.Repo.Did, c.String())), 129 130 Body: bytes.NewReader(fulldata.Bytes()), 130 131 }); err != nil { 131 - s.logger.Error("error uploading blob to s3", "error", err) 132 + logger.Error("error uploading blob to s3", "error", err) 132 133 return helpers.ServerError(e, nil) 133 134 } 134 135 } 135 136 136 137 if err := s.db.Exec(ctx, "UPDATE blobs SET cid = ? WHERE id = ?", nil, c.Bytes(), blob.ID).Error; err != nil { 137 138 // there should probably be somme handling here if this fails... 138 - s.logger.Error("error updating blob", "error", err) 139 + logger.Error("error updating blob", "error", err) 139 140 return helpers.ServerError(e, nil) 140 141 } 141 142
+3 -2
server/handle_server_activate_account.go
··· 19 19 20 20 func (s *Server) handleServerActivateAccount(e echo.Context) error { 21 21 ctx := e.Request().Context() 22 + logger := s.logger.With("name", "handleServerActivateAccount") 22 23 23 24 var req ComAtprotoServerDeactivateAccountRequest 24 25 if err := e.Bind(&req); err != nil { 25 - s.logger.Error("error binding", "error", err) 26 + logger.Error("error binding", "error", err) 26 27 return helpers.ServerError(e, nil) 27 28 } 28 29 29 30 urepo := e.Get("repo").(*models.RepoActor) 30 31 31 32 if err := s.db.Exec(ctx, "UPDATE repos SET deactivated = ? WHERE did = ?", nil, false, urepo.Repo.Did).Error; err != nil { 32 - s.logger.Error("error updating account status to deactivated", "error", err) 33 + logger.Error("error updating account status to deactivated", "error", err) 33 34 return helpers.ServerError(e, nil) 34 35 } 35 36
+5 -4
server/handle_server_check_account_status.go
··· 21 21 22 22 func (s *Server) handleServerCheckAccountStatus(e echo.Context) error { 23 23 ctx := e.Request().Context() 24 + logger := s.logger.With("name", "handleServerCheckAccountStatus") 24 25 25 26 urepo := e.Get("repo").(*models.RepoActor) 26 27 ··· 33 34 34 35 rootcid, err := cid.Cast(urepo.Root) 35 36 if err != nil { 36 - s.logger.Error("error casting cid", "error", err) 37 + logger.Error("error casting cid", "error", err) 37 38 return helpers.ServerError(e, nil) 38 39 } 39 40 resp.RepoCommit = rootcid.String() ··· 44 45 45 46 var blockCtResp CountResp 46 47 if err := s.db.Raw(ctx, "SELECT COUNT(*) AS ct FROM blocks WHERE did = ?", nil, urepo.Repo.Did).Scan(&blockCtResp).Error; err != nil { 47 - s.logger.Error("error getting block count", "error", err) 48 + logger.Error("error getting block count", "error", err) 48 49 return helpers.ServerError(e, nil) 49 50 } 50 51 resp.RepoBlocks = blockCtResp.Ct 51 52 52 53 var recCtResp CountResp 53 54 if err := s.db.Raw(ctx, "SELECT COUNT(*) AS ct FROM records WHERE did = ?", nil, urepo.Repo.Did).Scan(&recCtResp).Error; err != nil { 54 - s.logger.Error("error getting record count", "error", err) 55 + logger.Error("error getting record count", "error", err) 55 56 return helpers.ServerError(e, nil) 56 57 } 57 58 resp.IndexedRecords = recCtResp.Ct 58 59 59 60 var blobCtResp CountResp 60 61 if err := s.db.Raw(ctx, "SELECT COUNT(*) AS ct FROM blobs WHERE did = ?", nil, urepo.Repo.Did).Scan(&blobCtResp).Error; err != nil { 61 - s.logger.Error("error getting record count", "error", err) 62 + logger.Error("error getting record count", "error", err) 62 63 return helpers.ServerError(e, nil) 63 64 } 64 65 resp.ExpectedBlobs = blobCtResp.Ct
+3 -2
server/handle_server_confirm_email.go
··· 16 16 17 17 func (s *Server) handleServerConfirmEmail(e echo.Context) error { 18 18 ctx := e.Request().Context() 19 + logger := s.logger.With("name", "handleServerConfirmEmail") 19 20 20 21 urepo := e.Get("repo").(*models.RepoActor) 21 22 22 23 var req ComAtprotoServerConfirmEmailRequest 23 24 if err := e.Bind(&req); err != nil { 24 - s.logger.Error("error binding", "error", err) 25 + logger.Error("error binding", "error", err) 25 26 return helpers.ServerError(e, nil) 26 27 } 27 28 ··· 44 45 now := time.Now().UTC() 45 46 46 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 { 47 - s.logger.Error("error updating user", "error", err) 48 + logger.Error("error updating user", "error", err) 48 49 return helpers.ServerError(e, nil) 49 50 } 50 51
+23 -22
server/handle_server_create_account.go
··· 37 37 38 38 func (s *Server) handleCreateAccount(e echo.Context) error { 39 39 ctx := e.Request().Context() 40 + logger := s.logger.With("name", "handleServerCreateAccount") 40 41 41 42 var request ComAtprotoServerCreateAccountRequest 42 43 43 44 if err := e.Bind(&request); err != nil { 44 - 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) 45 46 return helpers.ServerError(e, nil) 46 47 } 47 48 48 49 request.Handle = strings.ToLower(request.Handle) 49 50 50 51 if err := e.Validate(request); err != nil { 51 - 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) 52 53 53 54 var verr ValidationError 54 55 if errors.As(err, &verr) { ··· 82 83 authDid, err := s.validateServiceAuth(e.Request().Context(), token, "com.atproto.server.createAccount") 83 84 84 85 if err != nil { 85 - 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) 86 87 return helpers.UnauthorizedError(e, to.StringPtr("invalid authorization token")) 87 88 } 88 89 ··· 94 95 // see if the handle is already taken 95 96 actor, err := s.getActorByHandle(ctx, request.Handle) 96 97 if err != nil && err != gorm.ErrRecordNotFound { 97 - 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) 98 99 return helpers.ServerError(e, nil) 99 100 } 100 101 if err == nil && actor.Did != signupDid { ··· 115 116 if err == gorm.ErrRecordNotFound { 116 117 return helpers.InputError(e, to.StringPtr("InvalidInviteCode")) 117 118 } 118 - s.logger.Error("error getting invite code from db", "error", err) 119 + logger.Error("error getting invite code from db", "error", err) 119 120 return helpers.ServerError(e, nil) 120 121 } 121 122 ··· 127 128 // see if the email is already taken 128 129 existingRepo, err := s.getRepoByEmail(ctx, request.Email) 129 130 if err != nil && err != gorm.ErrRecordNotFound { 130 - 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) 131 132 return helpers.ServerError(e, nil) 132 133 } 133 134 if err == nil && existingRepo.Did != signupDid { ··· 141 142 if signupDid != "" { 142 143 reservedKey, err := s.getReservedKey(ctx, signupDid) 143 144 if err != nil { 144 - s.logger.Error("error looking up reserved key", "error", err) 145 + logger.Error("error looking up reserved key", "error", err) 145 146 } 146 147 if reservedKey != nil { 147 148 k, err = atcrypto.ParsePrivateBytesK256(reservedKey.PrivateKey) 148 149 if err != nil { 149 - s.logger.Error("error parsing reserved key", "error", err) 150 + logger.Error("error parsing reserved key", "error", err) 150 151 k = nil 151 152 } else { 152 153 defer func() { 153 154 if delErr := s.deleteReservedKey(ctx, reservedKey.KeyDid, reservedKey.Did); delErr != nil { 154 - s.logger.Error("error deleting reserved key", "error", delErr) 155 + logger.Error("error deleting reserved key", "error", delErr) 155 156 } 156 157 }() 157 158 } ··· 161 162 if k == nil { 162 163 k, err = atcrypto.GeneratePrivateKeyK256() 163 164 if err != nil { 164 - 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) 165 166 return helpers.ServerError(e, nil) 166 167 } 167 168 } ··· 169 170 if signupDid == "" { 170 171 did, op, err := s.plcClient.CreateDID(k, "", request.Handle) 171 172 if err != nil { 172 - 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) 173 174 return helpers.ServerError(e, nil) 174 175 } 175 176 176 177 if err := s.plcClient.SendOperation(e.Request().Context(), did, op); err != nil { 177 - 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) 178 179 return helpers.ServerError(e, nil) 179 180 } 180 181 signupDid = did ··· 182 183 183 184 hashed, err := bcrypt.GenerateFromPassword([]byte(request.Password), 10) 184 185 if err != nil { 185 - s.logger.Error("error hashing password", "error", err) 186 + logger.Error("error hashing password", "error", err) 186 187 return helpers.ServerError(e, nil) 187 188 } 188 189 ··· 202 203 } 203 204 204 205 if err := s.db.Create(ctx, &urepo, nil).Error; err != nil { 205 - s.logger.Error("error inserting new repo", "error", err) 206 + logger.Error("error inserting new repo", "error", err) 206 207 return helpers.ServerError(e, nil) 207 208 } 208 209 209 210 if err := s.db.Create(ctx, &actor, nil).Error; err != nil { 210 - s.logger.Error("error inserting new actor", "error", err) 211 + logger.Error("error inserting new actor", "error", err) 211 212 return helpers.ServerError(e, nil) 212 213 } 213 214 } else { 214 215 if err := s.db.Save(ctx, &actor, nil).Error; err != nil { 215 - s.logger.Error("error inserting new actor", "error", err) 216 + logger.Error("error inserting new actor", "error", err) 216 217 return helpers.ServerError(e, nil) 217 218 } 218 219 } ··· 223 224 224 225 root, rev, err := r.Commit(context.TODO(), urepo.SignFor) 225 226 if err != nil { 226 - s.logger.Error("error committing", "error", err) 227 + logger.Error("error committing", "error", err) 227 228 return helpers.ServerError(e, nil) 228 229 } 229 230 230 231 if err := s.UpdateRepo(context.TODO(), urepo.Did, root, rev); err != nil { 231 - s.logger.Error("error updating repo after commit", "error", err) 232 + logger.Error("error updating repo after commit", "error", err) 232 233 return helpers.ServerError(e, nil) 233 234 } 234 235 ··· 244 245 245 246 if s.config.RequireInvite { 246 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 { 247 - s.logger.Error("error decrementing use count", "error", err) 248 + logger.Error("error decrementing use count", "error", err) 248 249 return helpers.ServerError(e, nil) 249 250 } 250 251 } 251 252 252 253 sess, err := s.createSession(ctx, &urepo) 253 254 if err != nil { 254 - s.logger.Error("error creating new session", "error", err) 255 + logger.Error("error creating new session", "error", err) 255 256 return helpers.ServerError(e, nil) 256 257 } 257 258 258 259 go func() { 259 260 if err := s.sendEmailVerification(urepo.Email, actor.Handle, *urepo.EmailVerificationCode); err != nil { 260 - s.logger.Error("error sending email verification email", "error", err) 261 + logger.Error("error sending email verification email", "error", err) 261 262 } 262 263 if err := s.sendWelcomeMail(urepo.Email, actor.Handle); err != nil { 263 - s.logger.Error("error sending welcome email", "error", err) 264 + logger.Error("error sending welcome email", "error", err) 264 265 } 265 266 }() 266 267
+4 -3
server/handle_server_create_invite_code.go
··· 18 18 19 19 func (s *Server) handleCreateInviteCode(e echo.Context) error { 20 20 ctx := e.Request().Context() 21 + logger := s.logger.With("name", "handleServerCreateInviteCode") 21 22 22 23 var req ComAtprotoServerCreateInviteCodeRequest 23 24 if err := e.Bind(&req); err != nil { 24 - s.logger.Error("error binding", "error", err) 25 + logger.Error("error binding", "error", err) 25 26 return helpers.ServerError(e, nil) 26 27 } 27 28 28 29 if err := e.Validate(req); err != nil { 29 - s.logger.Error("error validating", "error", err) 30 + logger.Error("error validating", "error", err) 30 31 return helpers.InputError(e, nil) 31 32 } 32 33 ··· 44 45 Did: acc, 45 46 RemainingUseCount: req.UseCount, 46 47 }, nil).Error; err != nil { 47 - s.logger.Error("error creating invite code", "error", err) 48 + logger.Error("error creating invite code", "error", err) 48 49 return helpers.ServerError(e, nil) 49 50 } 50 51
+4 -3
server/handle_server_create_invite_codes.go
··· 23 23 24 24 func (s *Server) handleCreateInviteCodes(e echo.Context) error { 25 25 ctx := e.Request().Context() 26 + logger := s.logger.With("name", "handleServerCreateInviteCodes") 26 27 27 28 var req ComAtprotoServerCreateInviteCodesRequest 28 29 if err := e.Bind(&req); err != nil { 29 - s.logger.Error("error binding", "error", err) 30 + logger.Error("error binding", "error", err) 30 31 return helpers.ServerError(e, nil) 31 32 } 32 33 33 34 if err := e.Validate(req); err != nil { 34 - s.logger.Error("error validating", "error", err) 35 + logger.Error("error validating", "error", err) 35 36 return helpers.InputError(e, nil) 36 37 } 37 38 ··· 57 58 Did: did, 58 59 RemainingUseCount: req.UseCount, 59 60 }, nil).Error; err != nil { 60 - s.logger.Error("error creating invite code", "error", err) 61 + logger.Error("error creating invite code", "error", err) 61 62 return helpers.ServerError(e, nil) 62 63 } 63 64 }
+59 -5
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" ··· 33 36 34 37 func (s *Server) handleCreateSession(e echo.Context) error { 35 38 ctx := e.Request().Context() 39 + logger := s.logger.With("name", "handleServerCreateSession") 36 40 37 41 var req ComAtprotoServerCreateSessionRequest 38 42 if err := e.Bind(&req); err != nil { 39 - 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) 40 44 return helpers.ServerError(e, nil) 41 45 } 42 46 ··· 79 83 return helpers.InputError(e, to.StringPtr("InvalidRequest")) 80 84 } 81 85 82 - 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) 83 87 return helpers.ServerError(e, nil) 84 88 } 85 89 86 90 if err := bcrypt.CompareHashAndPassword([]byte(repo.Password), []byte(req.Password)); err != nil { 87 91 if err != bcrypt.ErrMismatchedHashAndPassword { 88 - s.logger.Error("erorr comparing hash and password", "error", err) 92 + logger.Error("erorr comparing hash and password", "error", err) 89 93 } 90 94 return helpers.InputError(e, to.StringPtr("InvalidRequest")) 91 95 } 92 96 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 + 93 129 sess, err := s.createSession(ctx, &repo.Repo) 94 130 if err != nil { 95 - s.logger.Error("error creating session", "error", err) 131 + logger.Error("error creating session", "error", err) 96 132 return helpers.ServerError(e, nil) 97 133 } 98 134 ··· 103 139 Did: repo.Repo.Did, 104 140 Email: repo.Email, 105 141 EmailConfirmed: repo.EmailConfirmedAt != nil, 106 - EmailAuthFactor: false, 142 + EmailAuthFactor: repo.TwoFactorType != models.TwoFactorTypeNone, 107 143 Active: repo.Active(), 108 144 Status: repo.Status(), 109 145 }) 110 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 + }
+3 -2
server/handle_server_deactivate_account.go
··· 20 20 21 21 func (s *Server) handleServerDeactivateAccount(e echo.Context) error { 22 22 ctx := e.Request().Context() 23 + logger := s.logger.With("name", "handleServerDeactivateAccount") 23 24 24 25 var req ComAtprotoServerDeactivateAccountRequest 25 26 if err := e.Bind(&req); err != nil { 26 - s.logger.Error("error binding", "error", err) 27 + logger.Error("error binding", "error", err) 27 28 return helpers.ServerError(e, nil) 28 29 } 29 30 30 31 urepo := e.Get("repo").(*models.RepoActor) 31 32 32 33 if err := s.db.Exec(ctx, "UPDATE repos SET deactivated = ? WHERE did = ?", nil, true, urepo.Repo.Did).Error; err != nil { 33 - s.logger.Error("error updating account status to deactivated", "error", err) 34 + logger.Error("error updating account status to deactivated", "error", err) 34 35 return helpers.ServerError(e, nil) 35 36 } 36 37
+31 -28
server/handle_server_delete_account.go
··· 21 21 22 22 func (s *Server) handleServerDeleteAccount(e echo.Context) error { 23 23 ctx := e.Request().Context() 24 + logger := s.logger.With("name", "handleServerDeleteAccount") 24 25 25 26 var req ComAtprotoServerDeleteAccountRequest 26 27 if err := e.Bind(&req); err != nil { 27 - s.logger.Error("error binding", "error", err) 28 + logger.Error("error binding", "error", err) 28 29 return helpers.ServerError(e, nil) 29 30 } 30 31 31 32 if err := e.Validate(&req); err != nil { 32 - s.logger.Error("error validating", "error", err) 33 + logger.Error("error validating", "error", err) 33 34 return helpers.ServerError(e, nil) 34 35 } 35 36 36 37 urepo, err := s.getRepoActorByDid(ctx, req.Did) 37 38 if err != nil { 38 - s.logger.Error("error getting repo", "error", err) 39 + logger.Error("error getting repo", "error", err) 39 40 return echo.NewHTTPError(400, "account not found") 40 41 } 41 42 42 43 if err := bcrypt.CompareHashAndPassword([]byte(urepo.Repo.Password), []byte(req.Password)); err != nil { 43 - s.logger.Error("password mismatch", "error", err) 44 + logger.Error("password mismatch", "error", err) 44 45 return echo.NewHTTPError(401, "Invalid did or password") 45 46 } 46 47 47 48 if urepo.Repo.AccountDeleteCode == nil || urepo.Repo.AccountDeleteCodeExpiresAt == nil { 48 - s.logger.Error("no deletion token found for account") 49 + logger.Error("no deletion token found for account") 49 50 return echo.NewHTTPError(400, map[string]interface{}{ 50 51 "error": "InvalidToken", 51 52 "message": "Token is invalid", ··· 53 54 } 54 55 55 56 if *urepo.Repo.AccountDeleteCode != req.Token { 56 - s.logger.Error("deletion token mismatch") 57 + logger.Error("deletion token mismatch") 57 58 return echo.NewHTTPError(400, map[string]interface{}{ 58 59 "error": "InvalidToken", 59 60 "message": "Token is invalid", ··· 61 62 } 62 63 63 64 if time.Now().UTC().After(*urepo.Repo.AccountDeleteCodeExpiresAt) { 64 - s.logger.Error("deletion token expired") 65 + logger.Error("deletion token expired") 65 66 return echo.NewHTTPError(400, map[string]interface{}{ 66 67 "error": "ExpiredToken", 67 68 "message": "Token is expired", 68 69 }) 69 70 } 70 71 71 - tx := s.db.BeginDangerously(ctx) 72 + tx := s.db.Begin(ctx) 72 73 if tx.Error != nil { 73 - s.logger.Error("error starting transaction", "error", tx.Error) 74 + logger.Error("error starting transaction", "error", tx.Error) 74 75 return helpers.ServerError(e, nil) 75 76 } 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 + }() 86 + 77 87 if err := tx.Exec("DELETE FROM blocks WHERE did = ?", nil, req.Did).Error; err != nil { 78 - tx.Rollback() 79 - s.logger.Error("error deleting blocks", "error", err) 88 + logger.Error("error deleting blocks", "error", err) 80 89 return helpers.ServerError(e, nil) 81 90 } 82 91 83 92 if err := tx.Exec("DELETE FROM records WHERE did = ?", nil, req.Did).Error; err != nil { 84 - tx.Rollback() 85 - s.logger.Error("error deleting records", "error", err) 93 + logger.Error("error deleting records", "error", err) 86 94 return helpers.ServerError(e, nil) 87 95 } 88 96 89 97 if err := tx.Exec("DELETE FROM blobs WHERE did = ?", nil, req.Did).Error; err != nil { 90 - tx.Rollback() 91 - s.logger.Error("error deleting blobs", "error", err) 98 + logger.Error("error deleting blobs", "error", err) 92 99 return helpers.ServerError(e, nil) 93 100 } 94 101 95 102 if err := tx.Exec("DELETE FROM tokens WHERE did = ?", nil, req.Did).Error; err != nil { 96 - tx.Rollback() 97 - s.logger.Error("error deleting tokens", "error", err) 103 + logger.Error("error deleting tokens", "error", err) 98 104 return helpers.ServerError(e, nil) 99 105 } 100 106 101 107 if err := tx.Exec("DELETE FROM refresh_tokens WHERE did = ?", nil, req.Did).Error; err != nil { 102 - tx.Rollback() 103 - s.logger.Error("error deleting refresh tokens", "error", err) 108 + logger.Error("error deleting refresh tokens", "error", err) 104 109 return helpers.ServerError(e, nil) 105 110 } 106 111 107 112 if err := tx.Exec("DELETE FROM reserved_keys WHERE did = ?", nil, req.Did).Error; err != nil { 108 - tx.Rollback() 109 - s.logger.Error("error deleting reserved keys", "error", err) 113 + logger.Error("error deleting reserved keys", "error", err) 110 114 return helpers.ServerError(e, nil) 111 115 } 112 116 113 117 if err := tx.Exec("DELETE FROM invite_codes WHERE did = ?", nil, req.Did).Error; err != nil { 114 - tx.Rollback() 115 - s.logger.Error("error deleting invite codes", "error", err) 118 + logger.Error("error deleting invite codes", "error", err) 116 119 return helpers.ServerError(e, nil) 117 120 } 118 121 119 122 if err := tx.Exec("DELETE FROM actors WHERE did = ?", nil, req.Did).Error; err != nil { 120 - tx.Rollback() 121 - s.logger.Error("error deleting actor", "error", err) 123 + logger.Error("error deleting actor", "error", err) 122 124 return helpers.ServerError(e, nil) 123 125 } 124 126 125 127 if err := tx.Exec("DELETE FROM repos WHERE did = ?", nil, req.Did).Error; err != nil { 126 - tx.Rollback() 127 - s.logger.Error("error deleting repo", "error", err) 128 + logger.Error("error deleting repo", "error", err) 128 129 return helpers.ServerError(e, nil) 129 130 } 130 131 132 + status = "ok" 133 + 131 134 if err := tx.Commit().Error; err != nil { 132 - s.logger.Error("error committing transaction", "error", err) 135 + logger.Error("error committing transaction", "error", err) 133 136 return helpers.ServerError(e, nil) 134 137 } 135 138
+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
··· 23 23 Did: repo.Repo.Did, 24 24 Email: repo.Email, 25 25 EmailConfirmed: repo.EmailConfirmedAt != nil, 26 - EmailAuthFactor: false, // TODO: todo todo 26 + EmailAuthFactor: repo.TwoFactorType != models.TwoFactorTypeNone, 27 27 Active: repo.Active(), 28 28 Status: repo.Status(), 29 29 })
+4 -3
server/handle_server_refresh_session.go
··· 17 17 18 18 func (s *Server) handleRefreshSession(e echo.Context) error { 19 19 ctx := e.Request().Context() 20 + logger := s.logger.With("name", "handleServerRefreshSession") 20 21 21 22 token := e.Get("token").(string) 22 23 repo := e.Get("repo").(*models.RepoActor) 23 24 24 25 if err := s.db.Exec(ctx, "DELETE FROM refresh_tokens WHERE token = ?", nil, token).Error; err != nil { 25 - s.logger.Error("error getting refresh token from db", "error", err) 26 + logger.Error("error getting refresh token from db", "error", err) 26 27 return helpers.ServerError(e, nil) 27 28 } 28 29 29 30 if err := s.db.Exec(ctx, "DELETE FROM tokens WHERE refresh_token = ?", nil, token).Error; err != nil { 30 - s.logger.Error("error deleting access token from db", "error", err) 31 + logger.Error("error deleting access token from db", "error", err) 31 32 return helpers.ServerError(e, nil) 32 33 } 33 34 34 35 sess, err := s.createSession(ctx, &repo.Repo) 35 36 if err != nil { 36 - s.logger.Error("error creating new session for refresh", "error", err) 37 + logger.Error("error creating new session for refresh", "error", err) 37 38 return helpers.ServerError(e, nil) 38 39 } 39 40
+3 -2
server/handle_server_request_account_delete.go
··· 11 11 12 12 func (s *Server) handleServerRequestAccountDelete(e echo.Context) error { 13 13 ctx := e.Request().Context() 14 + logger := s.logger.With("name", "handleServerRequestAccountDelete") 14 15 15 16 urepo := e.Get("repo").(*models.RepoActor) 16 17 ··· 18 19 expiresAt := time.Now().UTC().Add(15 * time.Minute) 19 20 20 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 { 21 - s.logger.Error("error setting deletion token", "error", err) 22 + logger.Error("error setting deletion token", "error", err) 22 23 return helpers.ServerError(e, nil) 23 24 } 24 25 25 26 if urepo.Email != "" { 26 27 if err := s.sendAccountDeleteEmail(urepo.Email, urepo.Actor.Handle, token); err != nil { 27 - s.logger.Error("error sending account deletion email", "error", err) 28 + logger.Error("error sending account deletion email", "error", err) 28 29 } 29 30 } 30 31
+3 -2
server/handle_server_request_email_confirmation.go
··· 12 12 13 13 func (s *Server) handleServerRequestEmailConfirmation(e echo.Context) error { 14 14 ctx := e.Request().Context() 15 + logger := s.logger.With("name", "handleServerRequestEmailConfirm") 15 16 16 17 urepo := e.Get("repo").(*models.RepoActor) 17 18 ··· 23 24 eat := time.Now().Add(10 * time.Minute).UTC() 24 25 25 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 { 26 - s.logger.Error("error updating user", "error", err) 27 + logger.Error("error updating user", "error", err) 27 28 return helpers.ServerError(e, nil) 28 29 } 29 30 30 31 if err := s.sendEmailVerification(urepo.Email, urepo.Handle, code); err != nil { 31 - s.logger.Error("error sending mail", "error", err) 32 + logger.Error("error sending mail", "error", err) 32 33 return helpers.ServerError(e, nil) 33 34 } 34 35
+3 -2
server/handle_server_request_email_update.go
··· 15 15 16 16 func (s *Server) handleServerRequestEmailUpdate(e echo.Context) error { 17 17 ctx := e.Request().Context() 18 + logger := s.logger.With("name", "handleServerRequestEmailUpdate") 18 19 19 20 urepo := e.Get("repo").(*models.RepoActor) 20 21 ··· 23 24 eat := time.Now().Add(10 * time.Minute).UTC() 24 25 25 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 { 26 - s.logger.Error("error updating repo", "error", err) 27 + logger.Error("error updating repo", "error", err) 27 28 return helpers.ServerError(e, nil) 28 29 } 29 30 30 31 if err := s.sendEmailUpdate(urepo.Email, urepo.Handle, code); err != nil { 31 - s.logger.Error("error sending email", "error", err) 32 + logger.Error("error sending email", "error", err) 32 33 return helpers.ServerError(e, nil) 33 34 } 34 35 }
+3 -2
server/handle_server_request_password_reset.go
··· 15 15 16 16 func (s *Server) handleServerRequestPasswordReset(e echo.Context) error { 17 17 ctx := e.Request().Context() 18 + logger := s.logger.With("name", "handleServerRequestPasswordReset") 18 19 19 20 urepo, ok := e.Get("repo").(*models.RepoActor) 20 21 if !ok { ··· 39 40 eat := time.Now().Add(10 * time.Minute).UTC() 40 41 41 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 { 42 - s.logger.Error("error updating repo", "error", err) 43 + logger.Error("error updating repo", "error", err) 43 44 return helpers.ServerError(e, nil) 44 45 } 45 46 46 47 if err := s.sendPasswordReset(urepo.Email, urepo.Handle, code); err != nil { 47 - s.logger.Error("error sending email", "error", err) 48 + logger.Error("error sending email", "error", err) 48 49 return helpers.ServerError(e, nil) 49 50 } 50 51
+6 -5
server/handle_server_reserve_signing_key.go
··· 20 20 21 21 func (s *Server) handleServerReserveSigningKey(e echo.Context) error { 22 22 ctx := e.Request().Context() 23 + logger := s.logger.With("name", "handleServerReserveSigningKey") 23 24 24 25 var req ServerReserveSigningKeyRequest 25 26 if err := e.Bind(&req); err != nil { 26 - s.logger.Error("could not bind reserve signing key request", "error", err) 27 + logger.Error("could not bind reserve signing key request", "error", err) 27 28 return helpers.ServerError(e, nil) 28 29 } 29 30 ··· 38 39 39 40 k, err := atcrypto.GeneratePrivateKeyK256() 40 41 if err != nil { 41 - 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) 42 43 return helpers.ServerError(e, nil) 43 44 } 44 45 45 46 pubKey, err := k.PublicKey() 46 47 if err != nil { 47 - 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) 48 49 return helpers.ServerError(e, nil) 49 50 } 50 51 ··· 58 59 } 59 60 60 61 if err := s.db.Create(ctx, &reservedKey, nil).Error; err != nil { 61 - s.logger.Error("error storing reserved key", "endpoint", "com.atproto.server.reserveSigningKey", "error", err) 62 + logger.Error("error storing reserved key", "endpoint", "com.atproto.server.reserveSigningKey", "error", err) 62 63 return helpers.ServerError(e, nil) 63 64 } 64 65 65 - s.logger.Info("reserved signing key", "keyDid", keyDid, "forDid", req.Did) 66 + logger.Info("reserved signing key", "keyDid", keyDid, "forDid", req.Did) 66 67 67 68 return e.JSON(200, ServerReserveSigningKeyResponse{ 68 69 SigningKey: keyDid,
+4 -3
server/handle_server_reset_password.go
··· 17 17 18 18 func (s *Server) handleServerResetPassword(e echo.Context) error { 19 19 ctx := e.Request().Context() 20 + logger := s.logger.With("name", "handleServerResetPassword") 20 21 21 22 urepo := e.Get("repo").(*models.RepoActor) 22 23 23 24 var req ComAtprotoServerResetPasswordRequest 24 25 if err := e.Bind(&req); err != nil { 25 - s.logger.Error("error binding", "error", err) 26 + logger.Error("error binding", "error", err) 26 27 return helpers.ServerError(e, nil) 27 28 } 28 29 ··· 44 45 45 46 hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), 10) 46 47 if err != nil { 47 - s.logger.Error("error creating hash", "error", err) 48 + logger.Error("error creating hash", "error", err) 48 49 return helpers.ServerError(e, nil) 49 50 } 50 51 51 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 { 52 - s.logger.Error("error updating repo", "error", err) 53 + logger.Error("error updating repo", "error", err) 53 54 return helpers.ServerError(e, nil) 54 55 } 55 56
+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
+32 -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 18 ctx := e.Request().Context() 19 + logger := s.logger.With("name", "handleServerUpdateEmail") 19 20 20 21 urepo := e.Get("repo").(*models.RepoActor) 21 22 22 23 var req ComAtprotoServerUpdateEmailRequest 23 24 if err := e.Bind(&req); err != nil { 24 - s.logger.Error("error binding", "error", err) 25 + logger.Error("error binding", "error", err) 25 26 return helpers.ServerError(e, nil) 26 27 } 27 28 ··· 29 30 return helpers.InputError(e, nil) 30 31 } 31 32 32 - 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 == "" { 33 37 return helpers.InvalidTokenError(e) 34 38 } 35 39 36 - if *urepo.EmailUpdateCode != req.Token { 37 - 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 + } 38 52 } 39 53 40 - if time.Now().UTC().After(*urepo.EmailUpdateCodeExpiresAt) { 41 - return helpers.ExpiredTokenError(e) 54 + twoFactorType := models.TwoFactorTypeNone 55 + if req.EmailAuthFactor { 56 + twoFactorType = models.TwoFactorTypeEmail 42 57 } 43 58 44 - if err := s.db.Exec(ctx, "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 { 45 - s.logger.Error("error updating repo", "error", err) 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" 63 + } 64 + 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) 46 69 return helpers.ServerError(e, nil) 47 70 } 48 71
+9 -8
server/handle_sync_get_blob.go
··· 18 18 19 19 func (s *Server) handleSyncGetBlob(e echo.Context) error { 20 20 ctx := e.Request().Context() 21 + logger := s.logger.With("name", "handleSyncGetBlob") 21 22 22 23 did := e.QueryParam("did") 23 24 if did == "" { ··· 36 37 37 38 urepo, err := s.getRepoActorByDid(ctx, did) 38 39 if err != nil { 39 - s.logger.Error("could not find user for requested blob", "error", err) 40 + logger.Error("could not find user for requested blob", "error", err) 40 41 return helpers.InputError(e, nil) 41 42 } 42 43 ··· 49 50 50 51 var blob models.Blob 51 52 if err := s.db.Raw(ctx, "SELECT * FROM blobs WHERE did = ? AND cid = ?", nil, did, c.Bytes()).Scan(&blob).Error; err != nil { 52 - s.logger.Error("error looking up blob", "error", err) 53 + logger.Error("error looking up blob", "error", err) 53 54 return helpers.ServerError(e, nil) 54 55 } 55 56 ··· 58 59 if blob.Storage == "sqlite" { 59 60 var parts []models.BlobPart 60 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 { 61 - s.logger.Error("error getting blob parts", "error", err) 62 + logger.Error("error getting blob parts", "error", err) 62 63 return helpers.ServerError(e, nil) 63 64 } 64 65 ··· 68 69 } 69 70 } else if blob.Storage == "s3" { 70 71 if !(s.s3Config != nil && s.s3Config.BlobstoreEnabled) { 71 - s.logger.Error("s3 storage disabled") 72 + logger.Error("s3 storage disabled") 72 73 return helpers.ServerError(e, nil) 73 74 } 74 75 ··· 91 92 92 93 sess, err := session.NewSession(config) 93 94 if err != nil { 94 - s.logger.Error("error creating aws session", "error", err) 95 + logger.Error("error creating aws session", "error", err) 95 96 return helpers.ServerError(e, nil) 96 97 } 97 98 ··· 100 101 Bucket: aws.String(s.s3Config.Bucket), 101 102 Key: aws.String(blobKey), 102 103 }); err != nil { 103 - s.logger.Error("error getting blob from s3", "error", err) 104 + logger.Error("error getting blob from s3", "error", err) 104 105 return helpers.ServerError(e, nil) 105 106 } else { 106 107 read := 0 ··· 114 115 break 115 116 } 116 117 } else if err != nil && err != io.ErrUnexpectedEOF { 117 - s.logger.Error("error reading blob", "error", err) 118 + logger.Error("error reading blob", "error", err) 118 119 return helpers.ServerError(e, nil) 119 120 } 120 121 ··· 125 126 } 126 127 } 127 128 } else { 128 - s.logger.Error("unknown storage", "storage", blob.Storage) 129 + logger.Error("unknown storage", "storage", blob.Storage) 129 130 return helpers.ServerError(e, nil) 130 131 } 131 132
+2 -1
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 { ··· 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
+4 -3
server/handle_sync_get_record.go
··· 14 14 15 15 func (s *Server) handleSyncGetRecord(e echo.Context) error { 16 16 ctx := e.Request().Context() 17 + logger := s.logger.With("name", "handleSyncGetRecord") 17 18 18 19 did := e.QueryParam("did") 19 20 collection := e.QueryParam("collection") ··· 21 22 22 23 var urepo models.Repo 23 24 if err := s.db.Raw(ctx, "SELECT * FROM repos WHERE did = ?", nil, did).Scan(&urepo).Error; err != nil { 24 - s.logger.Error("error getting repo", "error", err) 25 + logger.Error("error getting repo", "error", err) 25 26 return helpers.ServerError(e, nil) 26 27 } 27 28 ··· 38 39 }) 39 40 40 41 if _, err := carstore.LdWrite(buf, hb); err != nil { 41 - s.logger.Error("error writing to car", "error", err) 42 + logger.Error("error writing to car", "error", err) 42 43 return helpers.ServerError(e, nil) 43 44 } 44 45 45 46 for _, blk := range blocks { 46 47 if _, err := carstore.LdWrite(buf, blk.Cid().Bytes(), blk.RawData()); err != nil { 47 - s.logger.Error("error writing to car", "error", err) 48 + logger.Error("error writing to car", "error", err) 48 49 return helpers.ServerError(e, nil) 49 50 } 50 51 }
+2 -1
server/handle_sync_get_repo.go
··· 14 14 15 15 func (s *Server) handleSyncGetRepo(e echo.Context) error { 16 16 ctx := e.Request().Context() 17 + logger := s.logger.With("name", "handleSyncGetRepo") 17 18 18 19 did := e.QueryParam("did") 19 20 if did == "" { ··· 38 39 buf := new(bytes.Buffer) 39 40 40 41 if _, err := carstore.LdWrite(buf, hb); err != nil { 41 - s.logger.Error("error writing to car", "error", err) 42 + logger.Error("error writing to car", "error", err) 42 43 return helpers.ServerError(e, nil) 43 44 } 44 45
+4 -3
server/handle_sync_list_blobs.go
··· 15 15 16 16 func (s *Server) handleSyncListBlobs(e echo.Context) error { 17 17 ctx := e.Request().Context() 18 + logger := s.logger.With("name", "handleSyncListBlobs") 18 19 19 20 did := e.QueryParam("did") 20 21 if did == "" { ··· 39 40 40 41 urepo, err := s.getRepoActorByDid(ctx, did) 41 42 if err != nil { 42 - s.logger.Error("could not find user for requested blobs", "error", err) 43 + logger.Error("could not find user for requested blobs", "error", err) 43 44 return helpers.InputError(e, nil) 44 45 } 45 46 ··· 52 53 53 54 var blobs []models.Blob 54 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 { 55 - s.logger.Error("error getting records", "error", err) 56 + logger.Error("error getting records", "error", err) 56 57 return helpers.ServerError(e, nil) 57 58 } 58 59 ··· 60 61 for _, b := range blobs { 61 62 c, err := cid.Cast(b.Cid) 62 63 if err != nil { 63 - s.logger.Error("error casting cid", "error", err) 64 + logger.Error("error casting cid", "error", err) 64 65 return helpers.ServerError(e, nil) 65 66 } 66 67 cstrs = append(cstrs, c.String())
+82 -50
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 13 14 func (s *Server) handleSyncSubscribeRepos(e echo.Context) error { 14 - ctx := e.Request().Context() 15 + ctx, cancel := context.WithCancel(e.Request().Context()) 16 + defer cancel() 17 + 15 18 logger := s.logger.With("component", "subscribe-repos-websocket") 16 19 17 20 conn, err := websocket.Upgrade(e.Response().Writer, e.Request(), e.Response().Header(), 1<<10, 1<<10) ··· 24 27 logger = logger.With("ident", ident) 25 28 logger.Info("new connection established") 26 29 27 - evts, cancel, err := s.evtman.Subscribe(ctx, ident, func(evt *events.XRPCStreamEvent) bool { 30 + metrics.RelaysConnected.WithLabelValues(ident).Inc() 31 + defer func() { 32 + metrics.RelaysConnected.WithLabelValues(ident).Dec() 33 + }() 34 + 35 + evts, evtManCancel, err := s.evtman.Subscribe(ctx, ident, func(evt *events.XRPCStreamEvent) bool { 28 36 return true 29 37 }, nil) 30 38 if err != nil { 31 39 return err 32 40 } 33 - defer cancel() 41 + defer evtManCancel() 42 + 43 + // drop the connection whenever a subscriber disconnects from the socket, we should get errors 44 + go func() { 45 + for { 46 + select { 47 + case <-ctx.Done(): 48 + return 49 + default: 50 + if _, _, err := conn.ReadMessage(); err != nil { 51 + logger.Warn("websocket error", "err", err) 52 + cancel() 53 + return 54 + } 55 + } 56 + } 57 + }() 34 58 35 59 header := events.EventHeader{Op: events.EvtKindMessage} 36 60 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 - } 61 + func() { 62 + defer func() { 63 + metrics.RelaySends.WithLabelValues(ident, header.MsgType).Inc() 64 + }() 42 65 43 - if ctx.Err() != nil { 44 - logger.Error("context error", "err", err) 45 - break 46 - } 66 + wc, err := conn.NextWriter(websocket.BinaryMessage) 67 + if err != nil { 68 + logger.Error("error writing message to relay", "err", err) 69 + return 70 + } 47 71 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 - } 72 + if ctx.Err() != nil { 73 + logger.Error("context error", "err", err) 74 + return 75 + } 69 76 70 - if err := header.MarshalCBOR(wc); err != nil { 71 - logger.Error("failed to write header to relay", "err", err) 72 - break 73 - } 77 + var obj util.CBOR 78 + switch { 79 + case evt.Error != nil: 80 + header.Op = events.EvtKindErrorFrame 81 + obj = evt.Error 82 + case evt.RepoCommit != nil: 83 + header.MsgType = "#commit" 84 + obj = evt.RepoCommit 85 + case evt.RepoIdentity != nil: 86 + header.MsgType = "#identity" 87 + obj = evt.RepoIdentity 88 + case evt.RepoAccount != nil: 89 + header.MsgType = "#account" 90 + obj = evt.RepoAccount 91 + case evt.RepoInfo != nil: 92 + header.MsgType = "#info" 93 + obj = evt.RepoInfo 94 + default: 95 + logger.Warn("unrecognized event kind") 96 + return 97 + } 74 98 75 - if err := obj.MarshalCBOR(wc); err != nil { 76 - logger.Error("failed to write event to relay", "err", err) 77 - break 78 - } 99 + if err := header.MarshalCBOR(wc); err != nil { 100 + logger.Error("failed to write header to relay", "err", err) 101 + return 102 + } 103 + 104 + if err := obj.MarshalCBOR(wc); err != nil { 105 + logger.Error("failed to write event to relay", "err", err) 106 + return 107 + } 79 108 80 - if err := wc.Close(); err != nil { 81 - logger.Error("failed to flush-close our event write", "err", err) 82 - break 83 - } 109 + if err := wc.Close(); err != nil { 110 + logger.Error("failed to flush-close our event write", "err", err) 111 + return 112 + } 113 + }() 84 114 } 85 115 86 116 // we should tell the relay to request a new crawl at this point if we got disconnected 87 117 // use a new context since the old one might be cancelled at this point 88 - ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) 89 - defer cancel() 90 - if err := s.requestCrawl(ctx); err != nil { 91 - logger.Error("error requesting crawls", "err", err) 92 - } 118 + go func() { 119 + retryCtx, retryCancel := context.WithTimeout(context.Background(), 10*time.Second) 120 + defer retryCancel() 121 + if err := s.requestCrawl(retryCtx); err != nil { 122 + logger.Error("error requesting crawls", "err", err) 123 + } 124 + }() 93 125 94 126 return nil 95 127 }
+2 -1
server/handle_well_known.go
··· 68 68 69 69 func (s *Server) handleAtprotoDid(e echo.Context) error { 70 70 ctx := e.Request().Context() 71 + logger := s.logger.With("name", "handleAtprotoDid") 71 72 72 73 host := e.Request().Host 73 74 if host == "" { ··· 91 92 if err == gorm.ErrRecordNotFound { 92 93 return e.NoContent(404) 93 94 } 94 - s.logger.Error("error looking up actor by handle", "error", err) 95 + logger.Error("error looking up actor by handle", "error", err) 95 96 return helpers.ServerError(e, nil) 96 97 } 97 98
+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 + }
+18 -16
server/middleware.go
··· 38 38 func (s *Server) handleLegacySessionMiddleware(next echo.HandlerFunc) echo.HandlerFunc { 39 39 return func(e echo.Context) error { 40 40 ctx := e.Request().Context() 41 + logger := s.logger.With("name", "handleLegacySessionMiddleware") 41 42 42 43 authheader := e.Request().Header.Get("authorization") 43 44 if authheader == "" { ··· 69 70 if hasLxm { 70 71 pts := strings.Split(e.Request().URL.String(), "/") 71 72 if lxm != pts[len(pts)-1] { 72 - 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) 73 74 return helpers.InputError(e, nil) 74 75 } 75 76 76 77 maybeDid, ok := claims["iss"].(string) 77 78 if !ok { 78 - s.logger.Error("no iss in service auth token", "error", err) 79 + logger.Error("no iss in service auth token", "error", err) 79 80 return helpers.InputError(e, nil) 80 81 } 81 82 did = maybeDid 82 83 83 84 maybeRepo, err := s.getRepoActorByDid(ctx, did) 84 85 if err != nil { 85 - s.logger.Error("error fetching repo", "error", err) 86 + logger.Error("error fetching repo", "error", err) 86 87 return helpers.ServerError(e, nil) 87 88 } 88 89 repo = maybeRepo ··· 96 97 return s.privateKey.Public(), nil 97 98 }) 98 99 if err != nil { 99 - s.logger.Error("error parsing jwt", "error", err) 100 + logger.Error("error parsing jwt", "error", err) 100 101 return helpers.ExpiredTokenError(e) 101 102 } 102 103 ··· 109 110 hash := sha256.Sum256([]byte(signingInput)) 110 111 sigBytes, err := base64.RawURLEncoding.DecodeString(kpts[2]) 111 112 if err != nil { 112 - s.logger.Error("error decoding signature bytes", "error", err) 113 + logger.Error("error decoding signature bytes", "error", err) 113 114 return helpers.ServerError(e, nil) 114 115 } 115 116 116 117 if len(sigBytes) != 64 { 117 - s.logger.Error("incorrect sigbytes length", "length", len(sigBytes)) 118 + logger.Error("incorrect sigbytes length", "length", len(sigBytes)) 118 119 return helpers.ServerError(e, nil) 119 120 } 120 121 ··· 140 141 141 142 sk, err := secp256k1secec.NewPrivateKey(repo.SigningKey) 142 143 if err != nil { 143 - s.logger.Error("can't load private key", "error", err) 144 + logger.Error("can't load private key", "error", err) 144 145 return err 145 146 } 146 147 147 148 pubKey, ok := sk.Public().(*secp256k1secec.PublicKey) 148 149 if !ok { 149 - s.logger.Error("error getting public key from sk") 150 + logger.Error("error getting public key from sk") 150 151 return helpers.ServerError(e, nil) 151 152 } 152 153 153 154 verified := pubKey.VerifyRaw(hash[:], rr, ss) 154 155 if !verified { 155 - s.logger.Error("error verifying", "error", err) 156 + logger.Error("error verifying", "error", err) 156 157 return helpers.ServerError(e, nil) 157 158 } 158 159 } ··· 181 182 return helpers.InvalidTokenError(e) 182 183 } 183 184 184 - s.logger.Error("error getting token from db", "error", err) 185 + logger.Error("error getting token from db", "error", err) 185 186 return helpers.ServerError(e, nil) 186 187 } 187 188 ··· 192 193 193 194 exp, ok := claims["exp"].(float64) 194 195 if !ok { 195 - s.logger.Error("error getting iat from token") 196 + logger.Error("error getting iat from token") 196 197 return helpers.ServerError(e, nil) 197 198 } 198 199 ··· 203 204 if repo == nil { 204 205 maybeRepo, err := s.getRepoActorByDid(ctx, claims["sub"].(string)) 205 206 if err != nil { 206 - s.logger.Error("error fetching repo", "error", err) 207 + logger.Error("error fetching repo", "error", err) 207 208 return helpers.ServerError(e, nil) 208 209 } 209 210 repo = maybeRepo ··· 225 226 func (s *Server) handleOauthSessionMiddleware(next echo.HandlerFunc) echo.HandlerFunc { 226 227 return func(e echo.Context) error { 227 228 ctx := e.Request().Context() 229 + logger := s.logger.With("name", "handleOauthSessionMiddleware") 228 230 229 231 authheader := e.Request().Header.Get("authorization") 230 232 if authheader == "" { ··· 257 259 "error": "use_dpop_nonce", 258 260 }) 259 261 } 260 - s.logger.Error("invalid dpop proof", "error", err) 262 + logger.Error("invalid dpop proof", "error", err) 261 263 return helpers.InputError(e, nil) 262 264 } 263 265 264 266 var oauthToken provider.OauthToken 265 267 if err := s.db.Raw(ctx, "SELECT * FROM oauth_tokens WHERE token = ?", nil, accessToken).Scan(&oauthToken).Error; err != nil { 266 - s.logger.Error("error finding access token in db", "error", err) 268 + logger.Error("error finding access token in db", "error", err) 267 269 return helpers.InputError(e, nil) 268 270 } 269 271 ··· 272 274 } 273 275 274 276 if *oauthToken.Parameters.DpopJkt != proof.JKT { 275 - s.logger.Error("jkt mismatch", "token", oauthToken.Parameters.DpopJkt, "proof", proof.JKT) 277 + logger.Error("jkt mismatch", "token", oauthToken.Parameters.DpopJkt, "proof", proof.JKT) 276 278 return helpers.InputError(e, to.StringPtr("dpop jkt mismatch")) 277 279 } 278 280 ··· 287 289 288 290 repo, err := s.getRepoActorByDid(ctx, oauthToken.Sub) 289 291 if err != nil { 290 - s.logger.Error("could not find actor in db", "error", err) 292 + logger.Error("could not find actor in db", "error", err) 291 293 return helpers.ServerError(e, nil) 292 294 } 293 295
+7
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" ··· 249 250 newroot, rev, err := r.Commit(ctx, urepo.SignFor) 250 251 if err != nil { 251 252 return nil, err 253 + } 254 + 255 + for _, result := range results { 256 + if result.Type != nil { 257 + metrics.RepoOperations.WithLabelValues(*result.Type).Inc() 258 + } 252 259 } 253 260 254 261 // create a buffer for dumping our new cbor into
+73 -72
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 + gdb.Exec("PRAGMA journal_mode=WAL") 326 + gdb.Exec("PRAGMA synchronous=NORMAL") 327 + 328 + logger.Info("connected to SQLite database", "path", args.DbName) 321 329 } 322 330 dbw := db.NewDB(gdb) 323 331 ··· 360 368 var nonceSecret []byte 361 369 maybeSecret, err := os.ReadFile("nonce.secret") 362 370 if err != nil && !os.IsNotExist(err) { 363 - args.Logger.Error("error attempting to read nonce secret", "error", err) 371 + logger.Error("error attempting to read nonce secret", "error", err) 364 372 } else { 365 373 nonceSecret = maybeSecret 366 374 } ··· 398 406 Hostname: args.Hostname, 399 407 ClientManagerArgs: client.ManagerArgs{ 400 408 Cli: oauthCli, 401 - Logger: args.Logger, 409 + Logger: args.Logger.With("component", "oauth-client-manager"), 402 410 }, 403 411 DpopManagerArgs: dpop.ManagerArgs{ 404 412 NonceSecret: nonceSecret, 405 413 NonceRotationInterval: constants.NonceMaxRotationInterval / 3, 406 414 OnNonceSecretCreated: func(newNonce []byte) { 407 415 if err := os.WriteFile("nonce.secret", newNonce, 0644); err != nil { 408 - args.Logger.Error("error writing new nonce secret", "error", err) 416 + logger.Error("error writing new nonce secret", "error", err) 409 417 } 410 418 }, 411 - Logger: args.Logger, 419 + Logger: args.Logger.With("component", "dpop-manager"), 412 420 Hostname: args.Hostname, 413 421 }, 414 422 }), ··· 535 543 } 536 544 537 545 func (s *Server) Serve(ctx context.Context) error { 546 + logger := s.logger.With("name", "Serve") 547 + 538 548 s.addRoutes() 539 549 540 - s.logger.Info("migrating...") 550 + logger.Info("migrating...") 541 551 542 552 s.db.AutoMigrate( 543 553 &models.Actor{}, ··· 554 564 &provider.OauthAuthorizationRequest{}, 555 565 ) 556 566 557 - s.logger.Info("starting cocoon") 567 + logger.Info("starting cocoon") 558 568 559 569 go func() { 560 570 if err := s.httpd.ListenAndServe(); err != nil { ··· 566 576 567 577 go func() { 568 578 if err := s.requestCrawl(ctx); err != nil { 569 - s.logger.Error("error requesting crawls", "err", err) 579 + logger.Error("error requesting crawls", "err", err) 570 580 } 571 581 }() 572 582 ··· 584 594 585 595 logger.Info("requesting crawl with configured relays") 586 596 587 - if time.Now().Sub(s.lastRequestCrawl) <= 1*time.Minute { 597 + if time.Since(s.lastRequestCrawl) <= 1*time.Minute { 588 598 return fmt.Errorf("a crawl request has already been made within the last minute") 589 599 } 590 600 ··· 607 617 } 608 618 609 619 func (s *Server) doBackup() { 620 + logger := s.logger.With("name", "doBackup") 621 + 610 622 if s.dbType == "postgres" { 611 - s.logger.Info("skipping S3 backup - PostgreSQL backups should be handled externally (pg_dump, managed database backups, etc.)") 623 + logger.Info("skipping S3 backup - PostgreSQL backups should be handled externally (pg_dump, managed database backups, etc.)") 612 624 return 613 625 } 614 626 615 627 start := time.Now() 616 628 617 - s.logger.Info("beginning backup to s3...") 629 + logger.Info("beginning backup to s3...") 618 630 619 - var buf bytes.Buffer 620 - if err := func() error { 621 - s.logger.Info("reading database bytes...") 622 - s.db.Lock() 623 - defer s.db.Unlock() 624 - 625 - sf, err := os.Open(s.dbName) 626 - if err != nil { 627 - return fmt.Errorf("error opening database for backup: %w", err) 628 - } 629 - defer sf.Close() 631 + tmpFile := fmt.Sprintf("/tmp/cocoon-backup-%s.db", time.Now().Format(time.RFC3339Nano)) 632 + defer os.Remove(tmpFile) 630 633 631 - if _, err := io.Copy(&buf, sf); err != nil { 632 - return fmt.Errorf("error reading bytes of backup db: %w", err) 633 - } 634 + if err := s.db.Client().Exec(fmt.Sprintf("VACUUM INTO '%s'", tmpFile)).Error; err != nil { 635 + logger.Error("error creating tmp backup file", "err", err) 636 + return 637 + } 634 638 635 - return nil 636 - }(); err != nil { 637 - s.logger.Error("error backing up database", "error", err) 639 + backupData, err := os.ReadFile(tmpFile) 640 + if err != nil { 641 + logger.Error("error reading tmp backup file", "err", err) 638 642 return 639 643 } 640 644 641 - if err := func() error { 642 - s.logger.Info("sending to s3...") 643 - 644 - currTime := time.Now().Format("2006-01-02_15-04-05") 645 - key := "cocoon-backup-" + currTime + ".db" 646 - 647 - config := &aws.Config{ 648 - Region: aws.String(s.s3Config.Region), 649 - Credentials: credentials.NewStaticCredentials(s.s3Config.AccessKey, s.s3Config.SecretKey, ""), 650 - } 645 + logger.Info("sending to s3...") 651 646 652 - if s.s3Config.Endpoint != "" { 653 - config.Endpoint = aws.String(s.s3Config.Endpoint) 654 - config.S3ForcePathStyle = aws.Bool(true) 655 - } 647 + currTime := time.Now().Format("2006-01-02_15-04-05") 648 + key := "cocoon-backup-" + currTime + ".db" 656 649 657 - sess, err := session.NewSession(config) 658 - if err != nil { 659 - return err 660 - } 650 + config := &aws.Config{ 651 + Region: aws.String(s.s3Config.Region), 652 + Credentials: credentials.NewStaticCredentials(s.s3Config.AccessKey, s.s3Config.SecretKey, ""), 653 + } 661 654 662 - svc := s3.New(sess) 655 + if s.s3Config.Endpoint != "" { 656 + config.Endpoint = aws.String(s.s3Config.Endpoint) 657 + config.S3ForcePathStyle = aws.Bool(true) 658 + } 663 659 664 - if _, err := svc.PutObject(&s3.PutObjectInput{ 665 - Bucket: aws.String(s.s3Config.Bucket), 666 - Key: aws.String(key), 667 - Body: bytes.NewReader(buf.Bytes()), 668 - }); err != nil { 669 - return fmt.Errorf("error uploading file to s3: %w", err) 670 - } 660 + sess, err := session.NewSession(config) 661 + if err != nil { 662 + logger.Error("error creating s3 session", "err", err) 663 + return 664 + } 671 665 672 - s.logger.Info("finished uploading backup to s3", "key", key, "duration", time.Now().Sub(start).Seconds()) 666 + svc := s3.New(sess) 673 667 674 - return nil 675 - }(); err != nil { 676 - s.logger.Error("error uploading database backup", "error", err) 668 + if _, err := svc.PutObject(&s3.PutObjectInput{ 669 + Bucket: aws.String(s.s3Config.Bucket), 670 + Key: aws.String(key), 671 + Body: bytes.NewReader(backupData), 672 + }); err != nil { 673 + logger.Error("error uploading file to s3", "err", err) 677 674 return 678 675 } 679 676 680 - os.WriteFile("last-backup.txt", []byte(time.Now().String()), 0644) 677 + logger.Info("finished uploading backup to s3", "key", key, "duration", time.Since(start).Seconds()) 678 + 679 + os.WriteFile("last-backup.txt", []byte(time.Now().Format(time.RFC3339Nano)), 0644) 681 680 } 682 681 683 682 func (s *Server) backupRoutine() { 683 + logger := s.logger.With("name", "backupRoutine") 684 + 684 685 if s.s3Config == nil || !s.s3Config.BackupsEnabled { 685 686 return 686 687 } 687 688 688 689 if s.s3Config.Region == "" { 689 - s.logger.Warn("no s3 region configured but backups are enabled. backups will not run.") 690 + logger.Warn("no s3 region configured but backups are enabled. backups will not run.") 690 691 return 691 692 } 692 693 693 694 if s.s3Config.Bucket == "" { 694 - s.logger.Warn("no s3 bucket configured but backups are enabled. backups will not run.") 695 + logger.Warn("no s3 bucket configured but backups are enabled. backups will not run.") 695 696 return 696 697 } 697 698 698 699 if s.s3Config.AccessKey == "" { 699 - s.logger.Warn("no s3 access key configured but backups are enabled. backups will not run.") 700 + logger.Warn("no s3 access key configured but backups are enabled. backups will not run.") 700 701 return 701 702 } 702 703 703 704 if s.s3Config.SecretKey == "" { 704 - s.logger.Warn("no s3 secret key configured but backups are enabled. backups will not run.") 705 + logger.Warn("no s3 secret key configured but backups are enabled. backups will not run.") 705 706 return 706 707 } 707 708 ··· 710 711 if err != nil { 711 712 shouldBackupNow = true 712 713 } else { 713 - lastBackup, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", string(lastBackupStr)) 714 + lastBackup, err := time.Parse(time.RFC3339Nano, string(lastBackupStr)) 714 715 if err != nil { 715 716 shouldBackupNow = true 716 - } else if time.Now().Sub(lastBackup).Seconds() > 3600 { 717 + } else if time.Since(lastBackup).Seconds() > 3600 { 717 718 shouldBackupNow = true 718 719 } 719 720 }
+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>
+1 -1
sqlite_blockstore/sqlite_blockstore.go
··· 94 94 } 95 95 96 96 func (bs *SqliteBlockstore) PutMany(ctx context.Context, blocks []blocks.Block) error { 97 - tx := bs.db.BeginDangerously(ctx) 97 + tx := bs.db.Begin(ctx) 98 98 99 99 for _, block := range blocks { 100 100 bs.inserts[block.Cid()] = block
+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)