+9
-9
blockstore/blockstore.go
+9
-9
blockstore/blockstore.go
···
5
"fmt"
6
7
"github.com/bluesky-social/indigo/atproto/syntax"
8
"github.com/haileyok/cocoon/models"
9
blocks "github.com/ipfs/go-block-format"
10
"github.com/ipfs/go-cid"
11
-
"gorm.io/gorm"
12
"gorm.io/gorm/clause"
13
)
14
15
type SqliteBlockstore struct {
16
-
db *gorm.DB
17
did string
18
readonly bool
19
inserts map[cid.Cid]blocks.Block
20
}
21
22
-
func New(did string, db *gorm.DB) *SqliteBlockstore {
23
return &SqliteBlockstore{
24
did: did,
25
db: db,
···
28
}
29
}
30
31
-
func NewReadOnly(did string, db *gorm.DB) *SqliteBlockstore {
32
return &SqliteBlockstore{
33
did: did,
34
db: db,
···
45
return maybeBlock, nil
46
}
47
48
-
if err := bs.db.Raw("SELECT * FROM blocks WHERE did = ? AND cid = ?", bs.did, cid.Bytes()).Scan(&block).Error; err != nil {
49
return nil, err
50
}
51
···
71
Value: block.RawData(),
72
}
73
74
-
if err := bs.db.Clauses(clause.OnConflict{
75
Columns: []clause.Column{{Name: "did"}, {Name: "cid"}},
76
UpdateAll: true,
77
-
}).Create(&b).Error; err != nil {
78
return err
79
}
80
···
94
}
95
96
func (bs *SqliteBlockstore) PutMany(ctx context.Context, blocks []blocks.Block) error {
97
-
tx := bs.db.Begin()
98
99
for _, block := range blocks {
100
bs.inserts[block.Cid()] = block
···
137
}
138
139
func (bs *SqliteBlockstore) UpdateRepo(ctx context.Context, root cid.Cid, rev string) error {
140
-
if err := bs.db.Exec("UPDATE repos SET root = ?, rev = ? WHERE did = ?", root.Bytes(), rev, bs.did).Error; err != nil {
141
return err
142
}
143
···
5
"fmt"
6
7
"github.com/bluesky-social/indigo/atproto/syntax"
8
+
"github.com/haileyok/cocoon/internal/db"
9
"github.com/haileyok/cocoon/models"
10
blocks "github.com/ipfs/go-block-format"
11
"github.com/ipfs/go-cid"
12
"gorm.io/gorm/clause"
13
)
14
15
type SqliteBlockstore struct {
16
+
db *db.DB
17
did string
18
readonly bool
19
inserts map[cid.Cid]blocks.Block
20
}
21
22
+
func New(did string, db *db.DB) *SqliteBlockstore {
23
return &SqliteBlockstore{
24
did: did,
25
db: db,
···
28
}
29
}
30
31
+
func NewReadOnly(did string, db *db.DB) *SqliteBlockstore {
32
return &SqliteBlockstore{
33
did: did,
34
db: db,
···
45
return maybeBlock, nil
46
}
47
48
+
if err := bs.db.Raw("SELECT * FROM blocks WHERE did = ? AND cid = ?", nil, bs.did, cid.Bytes()).Scan(&block).Error; err != nil {
49
return nil, err
50
}
51
···
71
Value: block.RawData(),
72
}
73
74
+
if err := bs.db.Create(&b, []clause.Expression{clause.OnConflict{
75
Columns: []clause.Column{{Name: "did"}, {Name: "cid"}},
76
UpdateAll: true,
77
+
}}).Error; err != nil {
78
return err
79
}
80
···
94
}
95
96
func (bs *SqliteBlockstore) PutMany(ctx context.Context, blocks []blocks.Block) error {
97
+
tx := bs.db.BeginDangerously()
98
99
for _, block := range blocks {
100
bs.inserts[block.Cid()] = block
···
137
}
138
139
func (bs *SqliteBlockstore) UpdateRepo(ctx context.Context, root cid.Cid, rev string) error {
140
+
if err := bs.db.Exec("UPDATE repos SET root = ?, rev = ? WHERE did = ?", nil, root.Bytes(), rev, bs.did).Error; err != nil {
141
return err
142
}
143
+32
cmd/cocoon/main.go
+32
cmd/cocoon/main.go
···
91
Required: false,
92
EnvVars: []string{"COCOON_SMTP_NAME"},
93
},
94
},
95
Commands: []*cli.Command{
96
run,
···
126
SmtpPort: cmd.String("smtp-port"),
127
SmtpEmail: cmd.String("smtp-email"),
128
SmtpName: cmd.String("smtp-name"),
129
})
130
if err != nil {
131
fmt.Printf("error creating cocoon: %v", err)
···
91
Required: false,
92
EnvVars: []string{"COCOON_SMTP_NAME"},
93
},
94
+
&cli.BoolFlag{
95
+
Name: "s3-backups-enabled",
96
+
EnvVars: []string{"COCOON_S3_BACKUPS_ENABLED"},
97
+
},
98
+
&cli.StringFlag{
99
+
Name: "s3-region",
100
+
EnvVars: []string{"COCOON_S3_REGION"},
101
+
},
102
+
&cli.StringFlag{
103
+
Name: "s3-bucket",
104
+
EnvVars: []string{"COCOON_S3_BUCKET"},
105
+
},
106
+
&cli.StringFlag{
107
+
Name: "s3-endpoint",
108
+
EnvVars: []string{"COCOON_S3_ENDPOINT"},
109
+
},
110
+
&cli.StringFlag{
111
+
Name: "s3-access-key",
112
+
EnvVars: []string{"COCOON_S3_ACCESS_KEY"},
113
+
},
114
+
&cli.StringFlag{
115
+
Name: "s3-secret-key",
116
+
EnvVars: []string{"COCOON_S3_SECRET_KEY"},
117
+
},
118
},
119
Commands: []*cli.Command{
120
run,
···
150
SmtpPort: cmd.String("smtp-port"),
151
SmtpEmail: cmd.String("smtp-email"),
152
SmtpName: cmd.String("smtp-name"),
153
+
S3Config: &server.S3Config{
154
+
BackupsEnabled: cmd.Bool("s3-backups-enabled"),
155
+
Region: cmd.String("s3-region"),
156
+
Bucket: cmd.String("s3-bucket"),
157
+
Endpoint: cmd.String("s3-endpoint"),
158
+
AccessKey: cmd.String("s3-access-key"),
159
+
SecretKey: cmd.String("s3-secret-key"),
160
+
},
161
})
162
if err != nil {
163
fmt.Printf("error creating cocoon: %v", err)
+8
-15
go.mod
+8
-15
go.mod
···
4
5
require (
6
github.com/Azure/go-autorest/autorest/to v0.4.1
7
github.com/bluesky-social/indigo v0.0.0-20250414202759-826fcdeaa36b
8
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792
9
github.com/go-playground/validator v9.31.0+incompatible
10
github.com/golang-jwt/jwt/v4 v4.5.2
11
github.com/google/uuid v1.4.0
12
github.com/ipfs/go-block-format v0.2.0
13
github.com/ipfs/go-cid v0.4.1
14
github.com/ipfs/go-ipld-cbor v0.1.0
···
16
github.com/joho/godotenv v1.5.1
17
github.com/labstack/echo/v4 v4.13.3
18
github.com/lestrrat-go/jwx/v2 v2.0.12
19
github.com/samber/slog-echo v1.16.1
20
github.com/urfave/cli/v2 v2.27.6
21
golang.org/x/crypto v0.36.0
22
gorm.io/driver/sqlite v1.5.7
23
gorm.io/gorm v1.25.12
···
27
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
28
github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b // indirect
29
github.com/beorn7/perks v1.0.1 // indirect
30
-
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
31
github.com/carlmjohnson/versioninfo v0.22.5 // indirect
32
github.com/cespare/xxhash/v2 v2.2.0 // indirect
33
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
34
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
35
-
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
36
github.com/felixge/httpsnoop v1.0.4 // indirect
37
github.com/go-logr/logr v1.4.2 // indirect
38
github.com/go-logr/stdr v1.2.2 // indirect
···
41
github.com/goccy/go-json v0.10.2 // indirect
42
github.com/gocql/gocql v1.7.0 // indirect
43
github.com/gogo/protobuf v1.3.2 // indirect
44
-
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
45
github.com/golang/snappy v0.0.4 // indirect
46
-
github.com/gorilla/websocket v1.5.1 // indirect
47
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
48
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
49
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
50
github.com/hashicorp/golang-lru v1.0.2 // indirect
51
-
github.com/hashicorp/golang-lru/arc/v2 v2.0.6 // indirect
52
-
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
53
github.com/ipfs/bbloom v0.0.4 // indirect
54
github.com/ipfs/go-blockservice v0.5.2 // indirect
55
github.com/ipfs/go-datastore v0.6.0 // indirect
···
65
github.com/ipfs/go-merkledag v0.11.0 // indirect
66
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
67
github.com/ipfs/go-verifcid v0.0.3 // indirect
68
-
github.com/ipld/go-car/v2 v2.13.1 // indirect
69
github.com/ipld/go-codec-dagpb v1.6.0 // indirect
70
github.com/ipld/go-ipld-prime v0.21.0 // indirect
71
github.com/jackc/pgpassfile v1.0.0 // indirect
···
75
github.com/jbenet/goprocess v0.1.4 // indirect
76
github.com/jinzhu/inflection v1.0.0 // indirect
77
github.com/jinzhu/now v1.1.5 // indirect
78
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
79
github.com/labstack/gommon v0.4.2 // indirect
80
github.com/leodido/go-urn v1.4.0 // indirect
···
92
github.com/multiformats/go-base32 v0.1.0 // indirect
93
github.com/multiformats/go-base36 v0.2.0 // indirect
94
github.com/multiformats/go-multibase v0.2.0 // indirect
95
-
github.com/multiformats/go-multicodec v0.9.0 // indirect
96
-
github.com/multiformats/go-multihash v0.2.3 // indirect
97
github.com/multiformats/go-varint v0.0.7 // indirect
98
github.com/opentracing/opentracing-go v1.2.0 // indirect
99
-
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 // indirect
100
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect
101
github.com/prometheus/client_golang v1.17.0 // indirect
102
github.com/prometheus/client_model v0.5.0 // indirect
···
108
github.com/spaolacci/murmur3 v1.1.0 // indirect
109
github.com/valyala/bytebufferpool v1.0.0 // indirect
110
github.com/valyala/fasttemplate v1.2.2 // indirect
111
-
github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 // indirect
112
-
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e // indirect
113
-
github.com/whyrusleeping/go-did v0.0.0-20230824162731-404d1707d5d6 // indirect
114
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
115
-
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect
116
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
117
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
118
go.opentelemetry.io/otel v1.29.0 // indirect
···
121
go.uber.org/atomic v1.11.0 // indirect
122
go.uber.org/multierr v1.11.0 // indirect
123
go.uber.org/zap v1.26.0 // indirect
124
-
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
125
golang.org/x/net v0.33.0 // indirect
126
golang.org/x/sync v0.12.0 // indirect
127
golang.org/x/sys v0.31.0 // indirect
···
4
5
require (
6
github.com/Azure/go-autorest/autorest/to v0.4.1
7
+
github.com/aws/aws-sdk-go v1.55.7
8
github.com/bluesky-social/indigo v0.0.0-20250414202759-826fcdeaa36b
9
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792
10
+
github.com/domodwyer/mailyak/v3 v3.6.2
11
github.com/go-playground/validator v9.31.0+incompatible
12
github.com/golang-jwt/jwt/v4 v4.5.2
13
github.com/google/uuid v1.4.0
14
+
github.com/gorilla/websocket v1.5.1
15
+
github.com/hashicorp/golang-lru/v2 v2.0.7
16
github.com/ipfs/go-block-format v0.2.0
17
github.com/ipfs/go-cid v0.4.1
18
github.com/ipfs/go-ipld-cbor v0.1.0
···
20
github.com/joho/godotenv v1.5.1
21
github.com/labstack/echo/v4 v4.13.3
22
github.com/lestrrat-go/jwx/v2 v2.0.12
23
+
github.com/multiformats/go-multihash v0.2.3
24
github.com/samber/slog-echo v1.16.1
25
github.com/urfave/cli/v2 v2.27.6
26
+
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e
27
+
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b
28
golang.org/x/crypto v0.36.0
29
gorm.io/driver/sqlite v1.5.7
30
gorm.io/gorm v1.25.12
···
34
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
35
github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b // indirect
36
github.com/beorn7/perks v1.0.1 // indirect
37
github.com/carlmjohnson/versioninfo v0.22.5 // indirect
38
github.com/cespare/xxhash/v2 v2.2.0 // indirect
39
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
40
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
41
github.com/felixge/httpsnoop v1.0.4 // indirect
42
github.com/go-logr/logr v1.4.2 // indirect
43
github.com/go-logr/stdr v1.2.2 // indirect
···
46
github.com/goccy/go-json v0.10.2 // indirect
47
github.com/gocql/gocql v1.7.0 // indirect
48
github.com/gogo/protobuf v1.3.2 // indirect
49
github.com/golang/snappy v0.0.4 // indirect
50
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
51
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
52
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
53
github.com/hashicorp/golang-lru v1.0.2 // indirect
54
github.com/ipfs/bbloom v0.0.4 // indirect
55
github.com/ipfs/go-blockservice v0.5.2 // indirect
56
github.com/ipfs/go-datastore v0.6.0 // indirect
···
66
github.com/ipfs/go-merkledag v0.11.0 // indirect
67
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
68
github.com/ipfs/go-verifcid v0.0.3 // indirect
69
github.com/ipld/go-codec-dagpb v1.6.0 // indirect
70
github.com/ipld/go-ipld-prime v0.21.0 // indirect
71
github.com/jackc/pgpassfile v1.0.0 // indirect
···
75
github.com/jbenet/goprocess v0.1.4 // indirect
76
github.com/jinzhu/inflection v1.0.0 // indirect
77
github.com/jinzhu/now v1.1.5 // indirect
78
+
github.com/jmespath/go-jmespath v0.4.0 // indirect
79
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
80
github.com/labstack/gommon v0.4.2 // indirect
81
github.com/leodido/go-urn v1.4.0 // indirect
···
93
github.com/multiformats/go-base32 v0.1.0 // indirect
94
github.com/multiformats/go-base36 v0.2.0 // indirect
95
github.com/multiformats/go-multibase v0.2.0 // indirect
96
github.com/multiformats/go-varint v0.0.7 // indirect
97
github.com/opentracing/opentracing-go v1.2.0 // indirect
98
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect
99
github.com/prometheus/client_golang v1.17.0 // indirect
100
github.com/prometheus/client_model v0.5.0 // indirect
···
106
github.com/spaolacci/murmur3 v1.1.0 // indirect
107
github.com/valyala/bytebufferpool v1.0.0 // indirect
108
github.com/valyala/fasttemplate v1.2.2 // indirect
109
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
110
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
111
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
112
go.opentelemetry.io/otel v1.29.0 // indirect
···
115
go.uber.org/atomic v1.11.0 // indirect
116
go.uber.org/multierr v1.11.0 // indirect
117
go.uber.org/zap v1.26.0 // indirect
118
golang.org/x/net v0.33.0 // indirect
119
golang.org/x/sync v0.12.0 // indirect
120
golang.org/x/sys v0.31.0 // indirect
+6
-22
go.sum
+6
-22
go.sum
···
7
github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b/go.mod h1:4+EPqMRApwwE/6yo6CxiHoSnBzjRr3jsqer7frxP8y4=
8
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5ljuFxkLGPNem5Ui+KBjFJzKg4Fv2fnxe4dvzpM=
9
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA=
10
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
11
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
12
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
···
14
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
15
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
16
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
17
-
github.com/bluesky-social/indigo v0.0.0-20250322011324-8e3fa7af986a h1:clnSZRgkiifbvfqu9++OHfIh2DWuIoZ8CucxLueQxO0=
18
-
github.com/bluesky-social/indigo v0.0.0-20250322011324-8e3fa7af986a/go.mod h1:NVBwZvbBSa93kfyweAmKwOLYawdVHdwZ9s+GZtBBVLA=
19
github.com/bluesky-social/indigo v0.0.0-20250414202759-826fcdeaa36b h1:elwfbe+W7GkUmPKFX1h7HaeHvC/kC0XJWfiEHC62xPg=
20
github.com/bluesky-social/indigo v0.0.0-20250414202759-826fcdeaa36b/go.mod h1:yjdhLA1LkK8VDS/WPUoYPo25/Hq/8rX38Ftr67EsqKY=
21
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
22
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
23
-
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
24
-
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
25
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
26
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
27
github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc=
···
65
github.com/gocql/gocql v1.7.0/go.mod h1:vnlvXyFZeLBF0Wy+RS8hrOdbn0UWsWtdg07XJnFxZ+4=
66
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
67
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
68
-
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
69
-
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
70
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
71
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
72
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
···
93
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
94
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
95
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
96
-
github.com/hashicorp/golang-lru/arc/v2 v2.0.6 h1:4NU7uP5vSoK6TbaMj3NtY478TTAWLso/vL1gpNrInHg=
97
-
github.com/hashicorp/golang-lru/arc/v2 v2.0.6/go.mod h1:cfdDIX05DWvYV6/shsxDfa/OVcRieOt+q4FnM8x+Xno=
98
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
99
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
100
github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ=
101
github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y=
102
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
103
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
104
-
github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
105
-
github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
106
github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ=
107
github.com/ipfs/go-bitswap v0.11.0/go.mod h1:05aE8H3XOU+LXpTedeAS0OZpcO1WFsj5niYQH9a1Tmk=
108
github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs=
···
123
github.com/ipfs/go-ipfs-blockstore v1.3.1/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE=
124
github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ=
125
github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk=
126
-
github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8=
127
-
github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8=
128
github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ=
129
github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
130
github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw=
···
158
github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY=
159
github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg=
160
github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU=
161
-
github.com/ipfs/go-unixfsnode v1.8.0 h1:yCkakzuE365glu+YkgzZt6p38CSVEBPgngL9ZkfnyQU=
162
-
github.com/ipfs/go-unixfsnode v1.8.0/go.mod h1:HxRu9HYHOjK6HUqFBAi++7DVoWAHn0o4v/nZ/VA+0g8=
163
github.com/ipfs/go-verifcid v0.0.3 h1:gmRKccqhWDocCRkC+a59g5QW7uJw5bpX9HWBevXa0zs=
164
github.com/ipfs/go-verifcid v0.0.3/go.mod h1:gcCtGniVzelKrbk9ooUSX/pM3xlH73fZZJDzQJRvOUw=
165
github.com/ipld/go-car v0.6.1-0.20230509095817-92d28eb23ba4 h1:oFo19cBmcP0Cmg3XXbrr0V/c+xU9U1huEZp8+OgBzdI=
···
170
github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s=
171
github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E=
172
github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ=
173
-
github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd h1:gMlw/MhNr2Wtp5RwGdsW23cs+yCuj9k2ON7i9MiJlRo=
174
-
github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd/go.mod h1:wZ8hH8UxeryOs4kJEJaiui/s00hDSbE37OKsL47g+Sw=
175
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
176
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
177
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
···
189
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
190
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
191
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
192
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
193
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
194
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
···
281
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
282
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
283
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
284
-
github.com/orandin/slog-gorm v1.3.2 h1:C0lKDQPAx/pF+8K2HL7bdShPwOEJpPM0Bn80zTzxU1g=
285
-
github.com/orandin/slog-gorm v1.3.2/go.mod h1:MoZ51+b7xE9lwGNPYEhxcUtRNrYzjdcKvA8QXQQGEPA=
286
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk=
287
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw=
288
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
···
345
github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ=
346
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4=
347
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so=
348
-
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E=
349
-
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8=
350
-
github.com/whyrusleeping/go-did v0.0.0-20230824162731-404d1707d5d6 h1:yJ9/LwIGIk/c0CdoavpC9RNSGSruIspSZtxG3Nnldic=
351
-
github.com/whyrusleeping/go-did v0.0.0-20230824162731-404d1707d5d6/go.mod h1:39U9RRVr4CKbXpXYopWn+FSH5s+vWu6+RmguSPWAq5s=
352
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
353
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
354
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
···
7
github.com/RussellLuo/slidingwindow v0.0.0-20200528002341-535bb99d338b/go.mod h1:4+EPqMRApwwE/6yo6CxiHoSnBzjRr3jsqer7frxP8y4=
8
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5ljuFxkLGPNem5Ui+KBjFJzKg4Fv2fnxe4dvzpM=
9
github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA=
10
+
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
11
+
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
12
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
13
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
14
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
···
16
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
17
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
18
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
19
github.com/bluesky-social/indigo v0.0.0-20250414202759-826fcdeaa36b h1:elwfbe+W7GkUmPKFX1h7HaeHvC/kC0XJWfiEHC62xPg=
20
github.com/bluesky-social/indigo v0.0.0-20250414202759-826fcdeaa36b/go.mod h1:yjdhLA1LkK8VDS/WPUoYPo25/Hq/8rX38Ftr67EsqKY=
21
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
22
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
23
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
24
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
25
github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc=
···
63
github.com/gocql/gocql v1.7.0/go.mod h1:vnlvXyFZeLBF0Wy+RS8hrOdbn0UWsWtdg07XJnFxZ+4=
64
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
65
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
66
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
67
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
68
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
···
89
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
90
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
91
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
92
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
93
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
94
github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ=
95
github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y=
96
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
97
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
98
github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ=
99
github.com/ipfs/go-bitswap v0.11.0/go.mod h1:05aE8H3XOU+LXpTedeAS0OZpcO1WFsj5niYQH9a1Tmk=
100
github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs=
···
115
github.com/ipfs/go-ipfs-blockstore v1.3.1/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE=
116
github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ=
117
github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk=
118
github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ=
119
github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw=
120
github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw=
···
148
github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY=
149
github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg=
150
github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU=
151
github.com/ipfs/go-verifcid v0.0.3 h1:gmRKccqhWDocCRkC+a59g5QW7uJw5bpX9HWBevXa0zs=
152
github.com/ipfs/go-verifcid v0.0.3/go.mod h1:gcCtGniVzelKrbk9ooUSX/pM3xlH73fZZJDzQJRvOUw=
153
github.com/ipld/go-car v0.6.1-0.20230509095817-92d28eb23ba4 h1:oFo19cBmcP0Cmg3XXbrr0V/c+xU9U1huEZp8+OgBzdI=
···
158
github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s=
159
github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E=
160
github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ=
161
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
162
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
163
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
···
175
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
176
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
177
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
178
+
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
179
+
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
180
+
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
181
+
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
182
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
183
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
184
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
···
271
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
272
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
273
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
274
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk=
275
github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9/go.mod h1:x3N5drFsm2uilKKuuYo6LdyD8vZAW55sH/9w+pbo1sw=
276
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
···
333
github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11/go.mod h1:Wlo/SzPmxVp6vXpGt/zaXhHH0fn4IxgqZc82aKg6bpQ=
334
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e h1:28X54ciEwwUxyHn9yrZfl5ojgF4CBNLWX7LR0rvBkf4=
335
github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so=
336
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
337
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
338
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+65
internal/db/db.go
+65
internal/db/db.go
···
···
1
+
package db
2
+
3
+
import (
4
+
"sync"
5
+
6
+
"gorm.io/gorm"
7
+
"gorm.io/gorm/clause"
8
+
)
9
+
10
+
type DB struct {
11
+
cli *gorm.DB
12
+
mu sync.Mutex
13
+
}
14
+
15
+
func NewDB(cli *gorm.DB) *DB {
16
+
return &DB{
17
+
cli: cli,
18
+
mu: sync.Mutex{},
19
+
}
20
+
}
21
+
22
+
func (db *DB) Create(value any, clauses []clause.Expression) *gorm.DB {
23
+
db.mu.Lock()
24
+
defer db.mu.Unlock()
25
+
return db.cli.Clauses(clauses...).Create(value)
26
+
}
27
+
28
+
func (db *DB) Exec(sql string, clauses []clause.Expression, values ...any) *gorm.DB {
29
+
db.mu.Lock()
30
+
defer db.mu.Unlock()
31
+
return db.cli.Clauses(clauses...).Exec(sql, values...)
32
+
}
33
+
34
+
func (db *DB) Raw(sql string, clauses []clause.Expression, values ...any) *gorm.DB {
35
+
return db.cli.Clauses(clauses...).Raw(sql, values...)
36
+
}
37
+
38
+
func (db *DB) AutoMigrate(models ...any) error {
39
+
return db.cli.AutoMigrate(models...)
40
+
}
41
+
42
+
func (db *DB) Delete(value any, clauses []clause.Expression) *gorm.DB {
43
+
db.mu.Lock()
44
+
defer db.mu.Unlock()
45
+
return db.cli.Clauses(clauses...).Delete(value)
46
+
}
47
+
48
+
func (db *DB) First(dest any, conds ...any) *gorm.DB {
49
+
return db.cli.First(dest, conds...)
50
+
}
51
+
52
+
// 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
53
+
// 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.
54
+
// e.g. when we do apply writes we should also be using a transcation but we don't right now
55
+
func (db *DB) BeginDangerously() *gorm.DB {
56
+
return db.cli.Begin()
57
+
}
58
+
59
+
func (db *DB) Lock() {
60
+
db.mu.Lock()
61
+
}
62
+
63
+
func (db *DB) Unlock() {
64
+
db.mu.Unlock()
65
+
}
+2
-2
server/common.go
+2
-2
server/common.go
···
22
23
func (s *Server) getRepoActorByEmail(email string) (*models.RepoActor, error) {
24
var repo models.RepoActor
25
-
if err := s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.email= ?", email).Scan(&repo).Error; err != nil {
26
return nil, err
27
}
28
return &repo, nil
···
30
31
func (s *Server) getRepoActorByDid(did string) (*models.RepoActor, error) {
32
var repo models.RepoActor
33
-
if err := s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.did = ?", did).Scan(&repo).Error; err != nil {
34
return nil, err
35
}
36
return &repo, nil
···
22
23
func (s *Server) getRepoActorByEmail(email string) (*models.RepoActor, error) {
24
var repo models.RepoActor
25
+
if err := s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.email= ?", nil, email).Scan(&repo).Error; err != nil {
26
return nil, err
27
}
28
return &repo, nil
···
30
31
func (s *Server) getRepoActorByDid(did string) (*models.RepoActor, error) {
32
var repo models.RepoActor
33
+
if err := s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.did = ?", nil, did).Scan(&repo).Error; err != nil {
34
return nil, err
35
}
36
return &repo, nil
+1
-1
server/handle_actor_put_preferences.go
+1
-1
server/handle_actor_put_preferences.go
+1
-1
server/handle_identity_update_handle.go
+1
-1
server/handle_identity_update_handle.go
+1
-1
server/handle_import_repo.go
+1
-1
server/handle_import_repo.go
+1
-1
server/handle_repo_describe_repo.go
+1
-1
server/handle_repo_describe_repo.go
+1
-1
server/handle_repo_get_record.go
+1
-1
server/handle_repo_get_record.go
+1
-1
server/handle_repo_list_records.go
+1
-1
server/handle_repo_list_records.go
···
64
params = append(params, limit)
65
66
var records []models.Record
67
-
if err := s.db.Raw("SELECT * FROM records WHERE did = ? AND nsid = ? "+cursorquery+" ORDER BY created_at "+sort+" limit ?", params...).Scan(&records).Error; err != nil {
68
s.logger.Error("error getting records", "error", err)
69
return helpers.ServerError(e, nil)
70
}
···
64
params = append(params, limit)
65
66
var records []models.Record
67
+
if err := s.db.Raw("SELECT * FROM records WHERE did = ? AND nsid = ? "+cursorquery+" ORDER BY created_at "+sort+" limit ?", nil, params...).Scan(&records).Error; err != nil {
68
s.logger.Error("error getting records", "error", err)
69
return helpers.ServerError(e, nil)
70
}
+1
-1
server/handle_repo_list_repos.go
+1
-1
server/handle_repo_list_repos.go
+3
-3
server/handle_repo_upload_blob.go
+3
-3
server/handle_repo_upload_blob.go
···
40
CreatedAt: s.repoman.clock.Next().String(),
41
}
42
43
-
if err := s.db.Create(&blob).Error; err != nil {
44
s.logger.Error("error creating new blob in db", "error", err)
45
return helpers.ServerError(e, nil)
46
}
···
72
Data: data,
73
}
74
75
-
if err := s.db.Create(&blobPart).Error; err != nil {
76
s.logger.Error("error adding blob part to db", "error", err)
77
return helpers.ServerError(e, nil)
78
}
···
89
return helpers.ServerError(e, nil)
90
}
91
92
-
if err := s.db.Exec("UPDATE blobs SET cid = ? WHERE id = ?", c.Bytes(), blob.ID).Error; err != nil {
93
// there should probably be somme handling here if this fails...
94
s.logger.Error("error updating blob", "error", err)
95
return helpers.ServerError(e, nil)
···
40
CreatedAt: s.repoman.clock.Next().String(),
41
}
42
43
+
if err := s.db.Create(&blob, nil).Error; err != nil {
44
s.logger.Error("error creating new blob in db", "error", err)
45
return helpers.ServerError(e, nil)
46
}
···
72
Data: data,
73
}
74
75
+
if err := s.db.Create(&blobPart, nil).Error; err != nil {
76
s.logger.Error("error adding blob part to db", "error", err)
77
return helpers.ServerError(e, nil)
78
}
···
89
return helpers.ServerError(e, nil)
90
}
91
92
+
if err := s.db.Exec("UPDATE blobs SET cid = ? WHERE id = ?", nil, c.Bytes(), blob.ID).Error; err != nil {
93
// there should probably be somme handling here if this fails...
94
s.logger.Error("error updating blob", "error", err)
95
return helpers.ServerError(e, nil)
+1
-1
server/handle_server_confirm_email.go
+1
-1
server/handle_server_confirm_email.go
···
41
42
now := time.Now().UTC()
43
44
-
if err := s.db.Exec("UPDATE repos SET email_verification_code = NULL, email_verification_code_expires_at = NULL, email_confirmed_at = ? WHERE did = ?", now, urepo.Repo.Did).Error; err != nil {
45
s.logger.Error("error updating user", "error", err)
46
return helpers.ServerError(e, nil)
47
}
···
41
42
now := time.Now().UTC()
43
44
+
if err := s.db.Exec("UPDATE repos SET email_verification_code = NULL, email_verification_code_expires_at = NULL, email_confirmed_at = ? WHERE did = ?", nil, now, urepo.Repo.Did).Error; err != nil {
45
s.logger.Error("error updating user", "error", err)
46
return helpers.ServerError(e, nil)
47
}
+4
-4
server/handle_server_create_account.go
+4
-4
server/handle_server_create_account.go
···
102
}
103
104
var ic models.InviteCode
105
-
if err := s.db.Raw("SELECT * FROM invite_codes WHERE code = ?", request.InviteCode).Scan(&ic).Error; err != nil {
106
if err == gorm.ErrRecordNotFound {
107
return helpers.InputError(e, to.StringPtr("InvalidInviteCode"))
108
}
···
166
Handle: request.Handle,
167
}
168
169
-
if err := s.db.Create(&urepo).Error; err != nil {
170
s.logger.Error("error inserting new repo", "error", err)
171
return helpers.ServerError(e, nil)
172
}
173
174
-
if err := s.db.Create(&actor).Error; err != nil {
175
s.logger.Error("error inserting new actor", "error", err)
176
return helpers.ServerError(e, nil)
177
}
···
210
})
211
}
212
213
-
if err := s.db.Raw("UPDATE invite_codes SET remaining_use_count = remaining_use_count - 1 WHERE code = ?", request.InviteCode).Scan(&ic).Error; err != nil {
214
s.logger.Error("error decrementing use count", "error", err)
215
return helpers.ServerError(e, nil)
216
}
···
102
}
103
104
var ic models.InviteCode
105
+
if err := s.db.Raw("SELECT * FROM invite_codes WHERE code = ?", nil, request.InviteCode).Scan(&ic).Error; err != nil {
106
if err == gorm.ErrRecordNotFound {
107
return helpers.InputError(e, to.StringPtr("InvalidInviteCode"))
108
}
···
166
Handle: request.Handle,
167
}
168
169
+
if err := s.db.Create(&urepo, nil).Error; err != nil {
170
s.logger.Error("error inserting new repo", "error", err)
171
return helpers.ServerError(e, nil)
172
}
173
174
+
if err := s.db.Create(&actor, nil).Error; err != nil {
175
s.logger.Error("error inserting new actor", "error", err)
176
return helpers.ServerError(e, nil)
177
}
···
210
})
211
}
212
213
+
if err := s.db.Raw("UPDATE invite_codes SET remaining_use_count = remaining_use_count - 1 WHERE code = ?", nil, request.InviteCode).Scan(&ic).Error; err != nil {
214
s.logger.Error("error decrementing use count", "error", err)
215
return helpers.ServerError(e, nil)
216
}
+1
-1
server/handle_server_create_invite_code.go
+1
-1
server/handle_server_create_invite_code.go
+1
-1
server/handle_server_create_invite_codes.go
+1
-1
server/handle_server_create_invite_codes.go
+3
-3
server/handle_server_create_session.go
+3
-3
server/handle_server_create_session.go
···
65
var err error
66
switch idtype {
67
case "did":
68
-
err = s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.did = ?", req.Identifier).Scan(&repo).Error
69
case "handle":
70
-
err = s.db.Raw("SELECT r.*, a.* FROM actors a LEFT JOIN repos r ON a.did = r.did WHERE a.handle = ?", req.Identifier).Scan(&repo).Error
71
case "email":
72
-
err = s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.email = ?", req.Identifier).Scan(&repo).Error
73
}
74
75
if err != nil {
···
65
var err error
66
switch idtype {
67
case "did":
68
+
err = s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.did = ?", nil, req.Identifier).Scan(&repo).Error
69
case "handle":
70
+
err = s.db.Raw("SELECT r.*, a.* FROM actors a LEFT JOIN repos r ON a.did = r.did WHERE a.handle = ?", nil, req.Identifier).Scan(&repo).Error
71
case "email":
72
+
err = s.db.Raw("SELECT r.*, a.* FROM repos r LEFT JOIN actors a ON r.did = a.did WHERE r.email = ?", nil, req.Identifier).Scan(&repo).Error
73
}
74
75
if err != nil {
+2
-2
server/handle_server_delete_session.go
+2
-2
server/handle_server_delete_session.go
···
10
token := e.Get("token").(string)
11
12
var acctok models.Token
13
-
if err := s.db.Raw("DELETE FROM tokens WHERE token = ? RETURNING *", token).Scan(&acctok).Error; err != nil {
14
s.logger.Error("error deleting access token from db", "error", err)
15
return helpers.ServerError(e, nil)
16
}
17
18
-
if err := s.db.Exec("DELETE FROM refresh_tokens WHERE token = ?", acctok.RefreshToken).Error; err != nil {
19
s.logger.Error("error deleting refresh token from db", "error", err)
20
return helpers.ServerError(e, nil)
21
}
···
10
token := e.Get("token").(string)
11
12
var acctok models.Token
13
+
if err := s.db.Raw("DELETE FROM tokens WHERE token = ? RETURNING *", nil, token).Scan(&acctok).Error; err != nil {
14
s.logger.Error("error deleting access token from db", "error", err)
15
return helpers.ServerError(e, nil)
16
}
17
18
+
if err := s.db.Exec("DELETE FROM refresh_tokens WHERE token = ?", nil, acctok.RefreshToken).Error; err != nil {
19
s.logger.Error("error deleting refresh token from db", "error", err)
20
return helpers.ServerError(e, nil)
21
}
+2
-2
server/handle_server_refresh_session.go
+2
-2
server/handle_server_refresh_session.go
···
19
token := e.Get("token").(string)
20
repo := e.Get("repo").(*models.RepoActor)
21
22
-
if err := s.db.Exec("DELETE FROM refresh_tokens WHERE token = ?", token).Error; err != nil {
23
s.logger.Error("error getting refresh token from db", "error", err)
24
return helpers.ServerError(e, nil)
25
}
26
27
-
if err := s.db.Exec("DELETE FROM tokens WHERE refresh_token = ?", token).Error; err != nil {
28
s.logger.Error("error deleting access token from db", "error", err)
29
return helpers.ServerError(e, nil)
30
}
···
19
token := e.Get("token").(string)
20
repo := e.Get("repo").(*models.RepoActor)
21
22
+
if err := s.db.Exec("DELETE FROM refresh_tokens WHERE token = ?", nil, token).Error; err != nil {
23
s.logger.Error("error getting refresh token from db", "error", err)
24
return helpers.ServerError(e, nil)
25
}
26
27
+
if err := s.db.Exec("DELETE FROM tokens WHERE refresh_token = ?", nil, token).Error; err != nil {
28
s.logger.Error("error deleting access token from db", "error", err)
29
return helpers.ServerError(e, nil)
30
}
+1
-1
server/handle_server_request_email_confirmation.go
+1
-1
server/handle_server_request_email_confirmation.go
···
20
code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
21
eat := time.Now().Add(10 * time.Minute).UTC()
22
23
-
if err := s.db.Exec("UPDATE repos SET email_verification_code = ?, email_verification_code_expires_at = ? WHERE did = ?", code, eat, urepo.Repo.Did).Error; err != nil {
24
s.logger.Error("error updating user", "error", err)
25
return helpers.ServerError(e, nil)
26
}
···
20
code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
21
eat := time.Now().Add(10 * time.Minute).UTC()
22
23
+
if err := s.db.Exec("UPDATE repos SET email_verification_code = ?, email_verification_code_expires_at = ? WHERE did = ?", nil, code, eat, urepo.Repo.Did).Error; err != nil {
24
s.logger.Error("error updating user", "error", err)
25
return helpers.ServerError(e, nil)
26
}
+1
-1
server/handle_server_request_email_update.go
+1
-1
server/handle_server_request_email_update.go
···
20
code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
21
eat := time.Now().Add(10 * time.Minute).UTC()
22
23
-
if err := s.db.Exec("UPDATE repos SET email_update_code = ?, email_update_code_expires_at = ? WHERE did = ?", code, eat, urepo.Repo.Did).Error; err != nil {
24
s.logger.Error("error updating repo", "error", err)
25
return helpers.ServerError(e, nil)
26
}
···
20
code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
21
eat := time.Now().Add(10 * time.Minute).UTC()
22
23
+
if err := s.db.Exec("UPDATE repos SET email_update_code = ?, email_update_code_expires_at = ? WHERE did = ?", nil, code, eat, urepo.Repo.Did).Error; err != nil {
24
s.logger.Error("error updating repo", "error", err)
25
return helpers.ServerError(e, nil)
26
}
+1
-1
server/handle_server_request_password_reset.go
+1
-1
server/handle_server_request_password_reset.go
···
36
code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
37
eat := time.Now().Add(10 * time.Minute).UTC()
38
39
-
if err := s.db.Exec("UPDATE repos SET password_reset_code = ?, password_reset_code_expires_at = ? WHERE did = ?", code, eat, urepo.Repo.Did).Error; err != nil {
40
s.logger.Error("error updating repo", "error", err)
41
return helpers.ServerError(e, nil)
42
}
···
36
code := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
37
eat := time.Now().Add(10 * time.Minute).UTC()
38
39
+
if err := s.db.Exec("UPDATE repos SET password_reset_code = ?, password_reset_code_expires_at = ? WHERE did = ?", nil, code, eat, urepo.Repo.Did).Error; err != nil {
40
s.logger.Error("error updating repo", "error", err)
41
return helpers.ServerError(e, nil)
42
}
+1
-1
server/handle_server_reset_password.go
+1
-1
server/handle_server_reset_password.go
···
46
return helpers.ServerError(e, nil)
47
}
48
49
-
if err := s.db.Exec("UPDATE repos SET password_reset_code = NULL, password_reset_code_expires_at = NULL, password = ? WHERE did = ?", hash, urepo.Repo.Did).Error; err != nil {
50
s.logger.Error("error updating repo", "error", err)
51
return helpers.ServerError(e, nil)
52
}
···
46
return helpers.ServerError(e, nil)
47
}
48
49
+
if err := s.db.Exec("UPDATE repos SET password_reset_code = NULL, password_reset_code_expires_at = NULL, password = ? WHERE did = ?", nil, hash, urepo.Repo.Did).Error; err != nil {
50
s.logger.Error("error updating repo", "error", err)
51
return helpers.ServerError(e, nil)
52
}
+1
-1
server/handle_server_update_email.go
+1
-1
server/handle_server_update_email.go
···
40
return helpers.InputError(e, to.StringPtr("ExpiredToken"))
41
}
42
43
-
if err := s.db.Exec("UPDATE repos SET email_update_code = NULL, email_update_code_expires_at = NULL, email_confirmed_at = NULL, email = ? WHERE did = ?", req.Email, urepo.Repo.Did).Error; err != nil {
44
s.logger.Error("error updating repo", "error", err)
45
return helpers.ServerError(e, nil)
46
}
···
40
return helpers.InputError(e, to.StringPtr("ExpiredToken"))
41
}
42
43
+
if err := s.db.Exec("UPDATE repos SET email_update_code = NULL, email_update_code_expires_at = NULL, email_confirmed_at = NULL, email = ? WHERE did = ?", nil, req.Email, urepo.Repo.Did).Error; err != nil {
44
s.logger.Error("error updating repo", "error", err)
45
return helpers.ServerError(e, nil)
46
}
+3
-3
server/handle_sync_get_blob.go
+3
-3
server/handle_sync_get_blob.go
···
26
}
27
28
var blob models.Blob
29
-
if err := s.db.Raw("SELECT * FROM blobs WHERE did = ? AND cid = ?", did, c.Bytes()).Scan(&blob).Error; err != nil {
30
s.logger.Error("error looking up blob", "error", err)
31
return helpers.ServerError(e, nil)
32
}
···
34
buf := new(bytes.Buffer)
35
36
var parts []models.BlobPart
37
-
if err := s.db.Raw("SELECT * FROM blob_parts WHERE blob_id = ? ORDER BY idx", blob.ID).Scan(&parts).Error; err != nil {
38
s.logger.Error("error getting blob parts", "error", err)
39
return helpers.ServerError(e, nil)
40
}
···
44
buf.Write(p.Data)
45
}
46
47
-
e.Response().Header().Set(echo.HeaderContentDisposition, "attachment; filename=" + c.String())
48
49
return e.Stream(200, "application/octet-stream", buf)
50
}
···
26
}
27
28
var blob models.Blob
29
+
if err := s.db.Raw("SELECT * FROM blobs WHERE did = ? AND cid = ?", nil, did, c.Bytes()).Scan(&blob).Error; err != nil {
30
s.logger.Error("error looking up blob", "error", err)
31
return helpers.ServerError(e, nil)
32
}
···
34
buf := new(bytes.Buffer)
35
36
var parts []models.BlobPart
37
+
if err := s.db.Raw("SELECT * FROM blob_parts WHERE blob_id = ? ORDER BY idx", nil, blob.ID).Scan(&parts).Error; err != nil {
38
s.logger.Error("error getting blob parts", "error", err)
39
return helpers.ServerError(e, nil)
40
}
···
44
buf.Write(p.Data)
45
}
46
47
+
e.Response().Header().Set(echo.HeaderContentDisposition, "attachment; filename="+c.String())
48
49
return e.Stream(200, "application/octet-stream", buf)
50
}
+1
-1
server/handle_sync_get_record.go
+1
-1
server/handle_sync_get_record.go
+1
-1
server/handle_sync_get_repo.go
+1
-1
server/handle_sync_get_repo.go
+1
-1
server/handle_sync_list_blobs.go
+1
-1
server/handle_sync_list_blobs.go
···
35
params = append(params, limit)
36
37
var blobs []models.Blob
38
-
if err := s.db.Raw("SELECT * FROM blobs WHERE did = ? "+cursorquery+" ORDER BY created_at DESC LIMIT ?", params...).Scan(&blobs).Error; err != nil {
39
s.logger.Error("error getting records", "error", err)
40
return helpers.ServerError(e, nil)
41
}
···
35
params = append(params, limit)
36
37
var blobs []models.Blob
38
+
if err := s.db.Raw("SELECT * FROM blobs WHERE did = ? "+cursorquery+" ORDER BY created_at DESC LIMIT ?", nil, params...).Scan(&blobs).Error; err != nil {
39
s.logger.Error("error getting records", "error", err)
40
return helpers.ServerError(e, nil)
41
}
+10
-10
server/repo.go
+10
-10
server/repo.go
···
18
"github.com/bluesky-social/indigo/repo"
19
"github.com/bluesky-social/indigo/util"
20
"github.com/haileyok/cocoon/blockstore"
21
"github.com/haileyok/cocoon/models"
22
blocks "github.com/ipfs/go-block-format"
23
"github.com/ipfs/go-cid"
24
cbor "github.com/ipfs/go-ipld-cbor"
25
"github.com/ipld/go-car"
26
-
"gorm.io/gorm"
27
"gorm.io/gorm/clause"
28
)
29
30
type RepoMan struct {
31
-
db *gorm.DB
32
s *Server
33
clock *syntax.TIDClock
34
}
···
162
})
163
case OpTypeDelete:
164
var old models.Record
165
-
if err := rm.db.Raw("SELECT value FROM records WHERE did = ? AND nsid = ? AND rkey = ?", urepo.Did, op.Collection, op.Rkey).Scan(&old).Error; err != nil {
166
return nil, err
167
}
168
entries = append(entries, models.Record{
···
284
for _, entry := range entries {
285
var cids []cid.Cid
286
if entry.Cid != "" {
287
-
if err := rm.s.db.Clauses(clause.OnConflict{
288
Columns: []clause.Column{{Name: "did"}, {Name: "nsid"}, {Name: "rkey"}},
289
UpdateAll: true,
290
-
}).Create(&entry).Error; err != nil {
291
return nil, err
292
}
293
···
296
return nil, err
297
}
298
} else {
299
-
if err := rm.s.db.Delete(&entry).Error; err != nil {
300
return nil, err
301
}
302
cids, err = rm.decrementBlobRefs(urepo, entry.Value)
···
368
}
369
370
for _, c := range cids {
371
-
if err := rm.db.Exec("UPDATE blobs SET ref_count = ref_count + 1 WHERE did = ? AND cid = ?", urepo.Did, c.Bytes()).Error; err != nil {
372
return nil, err
373
}
374
}
···
387
ID uint
388
Count int
389
}
390
-
if err := rm.db.Raw("UPDATE blobs SET ref_count = ref_count - 1 WHERE did = ? AND cid = ? RETURNING id, ref_count", urepo.Did, c.Bytes()).Scan(&res).Error; err != nil {
391
return nil, err
392
}
393
394
if res.Count == 0 {
395
-
if err := rm.db.Exec("DELETE FROM blobs WHERE id = ?", res.ID).Error; err != nil {
396
return nil, err
397
}
398
-
if err := rm.db.Exec("DELETE FROM blob_parts WHERE blob_id = ?", res.ID).Error; err != nil {
399
return nil, err
400
}
401
}
···
18
"github.com/bluesky-social/indigo/repo"
19
"github.com/bluesky-social/indigo/util"
20
"github.com/haileyok/cocoon/blockstore"
21
+
"github.com/haileyok/cocoon/internal/db"
22
"github.com/haileyok/cocoon/models"
23
blocks "github.com/ipfs/go-block-format"
24
"github.com/ipfs/go-cid"
25
cbor "github.com/ipfs/go-ipld-cbor"
26
"github.com/ipld/go-car"
27
"gorm.io/gorm/clause"
28
)
29
30
type RepoMan struct {
31
+
db *db.DB
32
s *Server
33
clock *syntax.TIDClock
34
}
···
162
})
163
case OpTypeDelete:
164
var old models.Record
165
+
if err := rm.db.Raw("SELECT value FROM records WHERE did = ? AND nsid = ? AND rkey = ?", nil, urepo.Did, op.Collection, op.Rkey).Scan(&old).Error; err != nil {
166
return nil, err
167
}
168
entries = append(entries, models.Record{
···
284
for _, entry := range entries {
285
var cids []cid.Cid
286
if entry.Cid != "" {
287
+
if err := rm.s.db.Create(&entry, []clause.Expression{clause.OnConflict{
288
Columns: []clause.Column{{Name: "did"}, {Name: "nsid"}, {Name: "rkey"}},
289
UpdateAll: true,
290
+
}}).Error; err != nil {
291
return nil, err
292
}
293
···
296
return nil, err
297
}
298
} else {
299
+
if err := rm.s.db.Delete(&entry, nil).Error; err != nil {
300
return nil, err
301
}
302
cids, err = rm.decrementBlobRefs(urepo, entry.Value)
···
368
}
369
370
for _, c := range cids {
371
+
if err := rm.db.Exec("UPDATE blobs SET ref_count = ref_count + 1 WHERE did = ? AND cid = ?", nil, urepo.Did, c.Bytes()).Error; err != nil {
372
return nil, err
373
}
374
}
···
387
ID uint
388
Count int
389
}
390
+
if err := rm.db.Raw("UPDATE blobs SET ref_count = ref_count - 1 WHERE did = ? AND cid = ? RETURNING id, ref_count", nil, urepo.Did, c.Bytes()).Scan(&res).Error; err != nil {
391
return nil, err
392
}
393
394
if res.Count == 0 {
395
+
if err := rm.db.Exec("DELETE FROM blobs WHERE id = ?", nil, res.ID).Error; err != nil {
396
return nil, err
397
}
398
+
if err := rm.db.Exec("DELETE FROM blob_parts WHERE blob_id = ?", nil, res.ID).Error; err != nil {
399
return nil, err
400
}
401
}
+148
-4
server/server.go
+148
-4
server/server.go
···
1
package server
2
3
import (
4
"context"
5
"crypto/ecdsa"
6
"errors"
7
"fmt"
8
"log/slog"
9
"net/http"
10
"net/smtp"
···
14
"time"
15
16
"github.com/Azure/go-autorest/autorest/to"
17
"github.com/bluesky-social/indigo/api/atproto"
18
"github.com/bluesky-social/indigo/atproto/syntax"
19
"github.com/bluesky-social/indigo/events"
···
23
"github.com/go-playground/validator"
24
"github.com/golang-jwt/jwt/v4"
25
"github.com/haileyok/cocoon/identity"
26
"github.com/haileyok/cocoon/internal/helpers"
27
"github.com/haileyok/cocoon/models"
28
"github.com/haileyok/cocoon/plc"
···
34
"gorm.io/gorm"
35
)
36
37
type Server struct {
38
http *http.Client
39
httpd *http.Server
40
mail *mailyak.MailYak
41
mailLk *sync.Mutex
42
echo *echo.Echo
43
-
db *gorm.DB
44
plcClient *plc.Client
45
logger *slog.Logger
46
config *config
···
48
repoman *RepoMan
49
evtman *events.EventManager
50
passport *identity.Passport
51
}
52
53
type Args struct {
···
69
SmtpPort string
70
SmtpEmail string
71
SmtpName string
72
}
73
74
type config struct {
···
176
Found bool
177
}
178
var result Result
179
-
if err := s.db.Raw("SELECT EXISTS(SELECT 1 FROM "+table+" WHERE token = ?) AS found", tokenstr).Scan(&result).Error; err != nil {
180
if err == gorm.ErrRecordNotFound {
181
return helpers.InputError(e, to.StringPtr("InvalidToken"))
182
}
···
299
IdleTimeout: 5 * time.Minute,
300
}
301
302
-
db, err := gorm.Open(sqlite.Open("cocoon.db"), &gorm.Config{})
303
if err != nil {
304
return nil, err
305
}
306
307
rkbytes, err := os.ReadFile(args.RotationKeyPath)
308
if err != nil {
···
341
httpd: httpd,
342
echo: e,
343
logger: args.Logger,
344
-
db: db,
345
plcClient: plcClient,
346
privateKey: &pkey,
347
config: &config{
···
357
},
358
evtman: events.NewEventManager(events.NewMemPersister()),
359
passport: identity.NewPassport(h, identity.NewMemCache(10_000)),
360
}
361
362
s.repoman = NewRepoMan(s) // TODO: this is way too lazy, stop it
···
461
}
462
}()
463
464
for _, relay := range s.config.Relays {
465
cli := xrpc.Client{Host: relay}
466
atproto.SyncRequestCrawl(ctx, &cli, &atproto.SyncRequestCrawl_Input{
···
474
475
return nil
476
}
···
1
package server
2
3
import (
4
+
"bytes"
5
"context"
6
"crypto/ecdsa"
7
"errors"
8
"fmt"
9
+
"io"
10
"log/slog"
11
"net/http"
12
"net/smtp"
···
16
"time"
17
18
"github.com/Azure/go-autorest/autorest/to"
19
+
"github.com/aws/aws-sdk-go/aws"
20
+
"github.com/aws/aws-sdk-go/aws/credentials"
21
+
"github.com/aws/aws-sdk-go/aws/session"
22
+
"github.com/aws/aws-sdk-go/service/s3"
23
"github.com/bluesky-social/indigo/api/atproto"
24
"github.com/bluesky-social/indigo/atproto/syntax"
25
"github.com/bluesky-social/indigo/events"
···
29
"github.com/go-playground/validator"
30
"github.com/golang-jwt/jwt/v4"
31
"github.com/haileyok/cocoon/identity"
32
+
"github.com/haileyok/cocoon/internal/db"
33
"github.com/haileyok/cocoon/internal/helpers"
34
"github.com/haileyok/cocoon/models"
35
"github.com/haileyok/cocoon/plc"
···
41
"gorm.io/gorm"
42
)
43
44
+
type S3Config struct {
45
+
BackupsEnabled bool
46
+
Endpoint string
47
+
Region string
48
+
Bucket string
49
+
AccessKey string
50
+
SecretKey string
51
+
}
52
+
53
type Server struct {
54
http *http.Client
55
httpd *http.Server
56
mail *mailyak.MailYak
57
mailLk *sync.Mutex
58
echo *echo.Echo
59
+
db *db.DB
60
plcClient *plc.Client
61
logger *slog.Logger
62
config *config
···
64
repoman *RepoMan
65
evtman *events.EventManager
66
passport *identity.Passport
67
+
68
+
dbName string
69
+
s3Config *S3Config
70
}
71
72
type Args struct {
···
88
SmtpPort string
89
SmtpEmail string
90
SmtpName string
91
+
92
+
S3Config *S3Config
93
}
94
95
type config struct {
···
197
Found bool
198
}
199
var result Result
200
+
if err := s.db.Raw("SELECT EXISTS(SELECT 1 FROM "+table+" WHERE token = ?) AS found", nil, tokenstr).Scan(&result).Error; err != nil {
201
if err == gorm.ErrRecordNotFound {
202
return helpers.InputError(e, to.StringPtr("InvalidToken"))
203
}
···
320
IdleTimeout: 5 * time.Minute,
321
}
322
323
+
gdb, err := gorm.Open(sqlite.Open("cocoon.db"), &gorm.Config{})
324
if err != nil {
325
return nil, err
326
}
327
+
dbw := db.NewDB(gdb)
328
329
rkbytes, err := os.ReadFile(args.RotationKeyPath)
330
if err != nil {
···
363
httpd: httpd,
364
echo: e,
365
logger: args.Logger,
366
+
db: dbw,
367
plcClient: plcClient,
368
privateKey: &pkey,
369
config: &config{
···
379
},
380
evtman: events.NewEventManager(events.NewMemPersister()),
381
passport: identity.NewPassport(h, identity.NewMemCache(10_000)),
382
+
383
+
dbName: args.DbName,
384
+
s3Config: args.S3Config,
385
}
386
387
s.repoman = NewRepoMan(s) // TODO: this is way too lazy, stop it
···
486
}
487
}()
488
489
+
go s.backupRoutine()
490
+
491
for _, relay := range s.config.Relays {
492
cli := xrpc.Client{Host: relay}
493
atproto.SyncRequestCrawl(ctx, &cli, &atproto.SyncRequestCrawl_Input{
···
501
502
return nil
503
}
504
+
505
+
func (s *Server) doBackup() {
506
+
start := time.Now()
507
+
508
+
s.logger.Info("beginning backup to s3...")
509
+
510
+
var buf bytes.Buffer
511
+
if err := func() error {
512
+
s.logger.Info("reading database bytes...")
513
+
s.db.Lock()
514
+
defer s.db.Unlock()
515
+
516
+
sf, err := os.Open(s.dbName)
517
+
if err != nil {
518
+
return fmt.Errorf("error opening database for backup: %w", err)
519
+
}
520
+
defer sf.Close()
521
+
522
+
if _, err := io.Copy(&buf, sf); err != nil {
523
+
return fmt.Errorf("error reading bytes of backup db: %w", err)
524
+
}
525
+
526
+
return nil
527
+
}(); err != nil {
528
+
s.logger.Error("error backing up database", "error", err)
529
+
return
530
+
}
531
+
532
+
if err := func() error {
533
+
s.logger.Info("sending to s3...")
534
+
535
+
currTime := time.Now().Format("2006-01-02_15-04-05")
536
+
key := "cocoon-backup-" + currTime + ".db"
537
+
538
+
config := &aws.Config{
539
+
Region: aws.String(s.s3Config.Region),
540
+
Credentials: credentials.NewStaticCredentials(s.s3Config.AccessKey, s.s3Config.SecretKey, ""),
541
+
}
542
+
543
+
if s.s3Config.Endpoint != "" {
544
+
config.Endpoint = aws.String(s.s3Config.Endpoint)
545
+
config.S3ForcePathStyle = aws.Bool(true)
546
+
}
547
+
548
+
sess, err := session.NewSession(config)
549
+
if err != nil {
550
+
return err
551
+
}
552
+
553
+
svc := s3.New(sess)
554
+
555
+
if _, err := svc.PutObject(&s3.PutObjectInput{
556
+
Bucket: aws.String(s.s3Config.Bucket),
557
+
Key: aws.String(key),
558
+
Body: bytes.NewReader(buf.Bytes()),
559
+
}); err != nil {
560
+
return fmt.Errorf("error uploading file to s3: %w", err)
561
+
}
562
+
563
+
s.logger.Info("finished uploading backup to s3", "key", key, "duration", time.Now().Sub(start).Seconds())
564
+
565
+
return nil
566
+
}(); err != nil {
567
+
s.logger.Error("error uploading database backup", "error", err)
568
+
return
569
+
}
570
+
571
+
os.WriteFile("last-backup.txt", []byte(time.Now().String()), 0644)
572
+
}
573
+
574
+
func (s *Server) backupRoutine() {
575
+
if s.s3Config == nil || !s.s3Config.BackupsEnabled {
576
+
return
577
+
}
578
+
579
+
if s.s3Config.Region == "" {
580
+
s.logger.Warn("no s3 region configured but backups are enabled. backups will not run.")
581
+
return
582
+
}
583
+
584
+
if s.s3Config.Bucket == "" {
585
+
s.logger.Warn("no s3 bucket configured but backups are enabled. backups will not run.")
586
+
return
587
+
}
588
+
589
+
if s.s3Config.AccessKey == "" {
590
+
s.logger.Warn("no s3 access key configured but backups are enabled. backups will not run.")
591
+
return
592
+
}
593
+
594
+
if s.s3Config.SecretKey == "" {
595
+
s.logger.Warn("no s3 secret key configured but backups are enabled. backups will not run.")
596
+
return
597
+
}
598
+
599
+
shouldBackupNow := false
600
+
lastBackupStr, err := os.ReadFile("last-backup.txt")
601
+
if err != nil {
602
+
shouldBackupNow = true
603
+
} else {
604
+
lastBackup, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", string(lastBackupStr))
605
+
if err != nil {
606
+
shouldBackupNow = true
607
+
} else if time.Now().Sub(lastBackup).Seconds() > 3600 {
608
+
shouldBackupNow = true
609
+
}
610
+
}
611
+
612
+
if shouldBackupNow {
613
+
go s.doBackup()
614
+
}
615
+
616
+
ticker := time.NewTicker(time.Hour)
617
+
for range ticker.C {
618
+
go s.doBackup()
619
+
}
620
+
}
+2
-2
server/session.go
+2
-2
server/session.go