tangled
alpha
login
or
join now
moth11.net
/
88x31
0
fork
atom
tiny 88x31 lexicon for atproto
0
fork
atom
overview
issues
pulls
pipelines
add listener
moth11.net
1 month ago
871f220d
1ec1d887
+217
-32
5 changed files
expand all
collapse all
unified
split
atplistener
jetstream.go
db
lexicon.go
go.mod
go.sum
tmpl
upload.html
+170
atplistener/jetstream.go
···
1
1
+
package atplistener
2
2
+
3
3
+
import (
4
4
+
"context"
5
5
+
"encoding/json"
6
6
+
"errors"
7
7
+
"fmt"
8
8
+
"github.com/bluesky-social/jetstream/pkg/client"
9
9
+
"github.com/bluesky-social/jetstream/pkg/client/schedulers/sequential"
10
10
+
"github.com/bluesky-social/jetstream/pkg/models"
11
11
+
"log/slog"
12
12
+
"tangled.org/moth11.net/88x31/db"
13
13
+
"tangled.org/moth11.net/88x31/lex"
14
14
+
"tangled.org/moth11.net/88x31/types"
15
15
+
"time"
16
16
+
)
17
17
+
18
18
+
type Consumer struct {
19
19
+
cfg *client.ClientConfig
20
20
+
logger *slog.Logger
21
21
+
handler *handler
22
22
+
}
23
23
+
24
24
+
type handler struct {
25
25
+
db *db.Store
26
26
+
l *slog.Logger
27
27
+
}
28
28
+
29
29
+
func NewConsumer(jsAddr string, l *slog.Logger, db *db.Store) *Consumer {
30
30
+
cfg := client.DefaultClientConfig()
31
31
+
if jsAddr != "" {
32
32
+
cfg.WebsocketURL = jsAddr
33
33
+
}
34
34
+
cfg.WantedCollections = []string{
35
35
+
"store.88x31.button",
36
36
+
"store.88x31.like",
37
37
+
}
38
38
+
cfg.WantedDids = []string{}
39
39
+
return &Consumer{
40
40
+
cfg: cfg,
41
41
+
logger: l,
42
42
+
handler: &handler{db: db, l: l},
43
43
+
}
44
44
+
}
45
45
+
46
46
+
func (c *Consumer) Consume(ctx context.Context) error {
47
47
+
scheduler := sequential.NewScheduler("jetstream_localdev", c.logger, c.handler.HandleEvent)
48
48
+
defer scheduler.Shutdown()
49
49
+
client, err := client.NewClient(c.cfg, c.logger, scheduler)
50
50
+
if err != nil {
51
51
+
return errors.New("failed to create client: " + err.Error())
52
52
+
}
53
53
+
cursor := time.Now().Add(1 * -time.Minute).UnixMicro()
54
54
+
err = client.ConnectAndRead(ctx, &cursor)
55
55
+
if err != nil {
56
56
+
return errors.New("error connecting and reading: " + err.Error())
57
57
+
}
58
58
+
return nil
59
59
+
}
60
60
+
61
61
+
func (h *handler) HandleEvent(ctx context.Context, event *models.Event) error {
62
62
+
if event.Commit == nil {
63
63
+
return nil
64
64
+
}
65
65
+
switch event.Commit.Collection {
66
66
+
case "store.88x31.button":
67
67
+
return h.handleButton(ctx, event)
68
68
+
case "store.88x31.like":
69
69
+
return h.handleLike(ctx, event)
70
70
+
}
71
71
+
return nil
72
72
+
}
73
73
+
74
74
+
func (h *handler) handleButton(ctx context.Context, event *models.Event) error {
75
75
+
h.l.Debug("handling button")
76
76
+
switch event.Commit.Operation {
77
77
+
case "create", "update":
78
78
+
return h.handleButtonCreateUpdate(ctx, event)
79
79
+
case "delete":
80
80
+
return h.handleButtonDelete(ctx, event)
81
81
+
}
82
82
+
return errors.New("unsupported commit operation")
83
83
+
}
84
84
+
85
85
+
func (h *handler) handleButtonCreateUpdate(ctx context.Context, event *models.Event) error {
86
86
+
var br lex.ButtonRecord
87
87
+
err := json.Unmarshal(event.Commit.Record, &br)
88
88
+
if err != nil {
89
89
+
h.l.Error("error unmarshaling: " + err.Error())
90
90
+
return nil
91
91
+
}
92
92
+
pa, err := time.Parse(br.PostedAt, time.RFC3339)
93
93
+
if err != nil {
94
94
+
h.l.Debug("bad time: " + err.Error())
95
95
+
pa = time.Time{}
96
96
+
}
97
97
+
tbr := types.Button{URI: uri(event),
98
98
+
DID: event.Did,
99
99
+
BlobCID: br.Blob.Ref.String(),
100
100
+
BlobMIME: br.Blob.MimeType,
101
101
+
Alt: br.Alt,
102
102
+
Title: br.Title,
103
103
+
HREF: br.Href,
104
104
+
CID: event.Commit.CID,
105
105
+
PostedAt: pa,
106
106
+
}
107
107
+
err = h.db.StoreButton(&tbr, ctx)
108
108
+
if err != nil {
109
109
+
return err
110
110
+
}
111
111
+
return nil
112
112
+
}
113
113
+
114
114
+
func (h *handler) handleButtonDelete(ctx context.Context, event *models.Event) error {
115
115
+
116
116
+
err := h.db.DeleteButton(uri(event), ctx)
117
117
+
if err != nil {
118
118
+
h.l.Error(err.Error())
119
119
+
}
120
120
+
return nil
121
121
+
}
122
122
+
123
123
+
func uri(event *models.Event) string {
124
124
+
return fmt.Sprintf("at://%s/%s/%s", event.Did, event.Commit.Collection, event.Commit.RKey)
125
125
+
}
126
126
+
func (h *handler) handleLike(ctx context.Context, event *models.Event) error {
127
127
+
h.l.Debug("handling like")
128
128
+
switch event.Commit.Operation {
129
129
+
case "create", "update":
130
130
+
return h.handleLikeCreateUpdate(ctx, event)
131
131
+
case "delete":
132
132
+
return h.handleLikeDelete(ctx, event)
133
133
+
}
134
134
+
return errors.New("unsupported commit operation")
135
135
+
}
136
136
+
137
137
+
func (h *handler) handleLikeCreateUpdate(ctx context.Context, event *models.Event) error {
138
138
+
var lr lex.LikeRecord
139
139
+
err := json.Unmarshal(event.Commit.Record, &lr)
140
140
+
if err != nil {
141
141
+
h.l.Error("error unmarshaling: " + err.Error())
142
142
+
return nil
143
143
+
}
144
144
+
pa, err := time.Parse(lr.CreatedAt, time.RFC3339)
145
145
+
if err != nil {
146
146
+
h.l.Debug("bad time: " + err.Error())
147
147
+
pa = time.Time{}
148
148
+
}
149
149
+
tlr := types.Like{
150
150
+
URI: uri(event),
151
151
+
SubjectURI: lr.Subject.URI,
152
152
+
SubjectCID: lr.Subject.CID,
153
153
+
DID: event.Did,
154
154
+
CID: event.Commit.CID,
155
155
+
CreatedAt: pa,
156
156
+
}
157
157
+
err = h.db.StoreLike(&tlr, ctx)
158
158
+
if err != nil {
159
159
+
return err
160
160
+
}
161
161
+
return nil
162
162
+
}
163
163
+
164
164
+
func (h *handler) handleLikeDelete(ctx context.Context, event *models.Event) error {
165
165
+
err := h.db.DeleteLike(uri(event), ctx)
166
166
+
if err != nil {
167
167
+
h.l.Error(err.Error())
168
168
+
}
169
169
+
return nil
170
170
+
}
+1
-1
db/lexicon.go
···
260
260
posted_at
261
261
) VALUES (
262
262
$1, $2, $3, $4, $5, $6, $7, $8, $9
263
263
-
)
263
263
+
) ON CONFLICT (uri) DO UPDATE
264
264
`, tbr.URI, tbr.DID, tbr.BlobCID, tbr.BlobMIME, tbr.Alt, tbr.Title, tbr.HREF, tbr.CID, tbr.PostedAt)
265
265
return err
266
266
}
+14
-9
go.mod
···
3
3
go 1.25
4
4
5
5
require (
6
6
-
github.com/bluesky-social/indigo v0.0.0-20251127021457-6f2658724b36
6
6
+
github.com/bluesky-social/indigo v0.0.0-20260120225912-12d69fa4d209
7
7
+
github.com/bluesky-social/jetstream v0.0.0-20260121001058-f4e39a4b5bbc
7
8
github.com/gorilla/sessions v1.4.0
8
9
github.com/ipfs/go-cid v0.4.1
9
10
github.com/jackc/pgx/v5 v5.7.6
···
14
15
15
16
require (
16
17
github.com/beorn7/perks v1.0.1 // indirect
17
17
-
github.com/cespare/xxhash/v2 v2.2.0 // indirect
18
18
+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
18
19
github.com/earthboundkid/versioninfo/v2 v2.24.1 // indirect
20
20
+
github.com/goccy/go-json v0.10.2 // indirect
19
21
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
20
22
github.com/google/go-querystring v1.1.0 // indirect
21
23
github.com/gorilla/securecookie v1.1.2 // indirect
24
24
+
github.com/gorilla/websocket v1.5.1 // indirect
22
25
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
23
26
github.com/jackc/pgpassfile v1.0.0 // indirect
24
27
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
25
28
github.com/jackc/puddle/v2 v2.2.2 // indirect
29
29
+
github.com/klauspost/compress v1.17.9 // indirect
26
30
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
27
27
-
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
28
31
github.com/minio/sha256-simd v1.0.1 // indirect
29
32
github.com/mr-tron/base58 v1.2.0 // indirect
30
33
github.com/multiformats/go-base32 v0.1.0 // indirect
···
32
35
github.com/multiformats/go-multibase v0.2.0 // indirect
33
36
github.com/multiformats/go-multihash v0.2.3 // indirect
34
37
github.com/multiformats/go-varint v0.0.7 // indirect
35
35
-
github.com/prometheus/client_golang v1.17.0 // indirect
36
36
-
github.com/prometheus/client_model v0.5.0 // indirect
37
37
-
github.com/prometheus/common v0.45.0 // indirect
38
38
-
github.com/prometheus/procfs v0.12.0 // indirect
38
38
+
github.com/prometheus/client_golang v1.19.1 // indirect
39
39
+
github.com/prometheus/client_model v0.6.1 // indirect
40
40
+
github.com/prometheus/common v0.54.0 // indirect
41
41
+
github.com/prometheus/procfs v0.15.1 // indirect
39
42
github.com/spaolacci/murmur3 v1.1.0 // indirect
40
43
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect
41
44
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect
45
45
+
go.uber.org/atomic v1.11.0 // indirect
42
46
golang.org/x/crypto v0.37.0 // indirect
47
47
+
golang.org/x/net v0.24.0 // indirect
43
48
golang.org/x/sync v0.13.0 // indirect
44
49
golang.org/x/sys v0.32.0 // indirect
45
50
golang.org/x/text v0.24.0 // indirect
46
46
-
golang.org/x/time v0.3.0 // indirect
47
47
-
google.golang.org/protobuf v1.33.0 // indirect
51
51
+
golang.org/x/time v0.5.0 // indirect
52
52
+
google.golang.org/protobuf v1.34.2 // indirect
48
53
lukechampine.com/blake3 v1.2.1 // indirect
49
54
)
+30
-20
go.sum
···
1
1
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
2
2
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
3
3
-
github.com/bluesky-social/indigo v0.0.0-20251127021457-6f2658724b36 h1:Vc+l4sltxQfBT8qC3dm87PRYInmxlGyF1dmpjaW0WkU=
4
4
-
github.com/bluesky-social/indigo v0.0.0-20251127021457-6f2658724b36/go.mod h1:Pm2I1+iDXn/hLbF7XCg/DsZi6uDCiOo7hZGWprSM7k0=
5
5
-
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
6
6
-
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
3
3
+
github.com/bluesky-social/indigo v0.0.0-20260120225912-12d69fa4d209 h1:W01PGqjCexVBzIZ4FoNe4iO8OhI9XbSE7ieWL0QnMu8=
4
4
+
github.com/bluesky-social/indigo v0.0.0-20260120225912-12d69fa4d209/go.mod h1:KIy0FgNQacp4uv2Z7xhNkV3qZiUSGuRky97s7Pa4v+o=
5
5
+
github.com/bluesky-social/jetstream v0.0.0-20260121001058-f4e39a4b5bbc h1:PhAg2bDYjOGxMTgBe4VgwrTyeEVRX+8R997TSoT15QM=
6
6
+
github.com/bluesky-social/jetstream v0.0.0-20260121001058-f4e39a4b5bbc/go.mod h1:vt8kVRKtvrBspt9G38wDD8+BotjIMO8u8IYoVnyE4zY=
7
7
+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
8
8
+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
7
9
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8
10
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
9
11
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10
12
github.com/earthboundkid/versioninfo/v2 v2.24.1 h1:SJTMHaoUx3GzjjnUO1QzP3ZXK6Ee/nbWyCm58eY3oUg=
11
13
github.com/earthboundkid/versioninfo/v2 v2.24.1/go.mod h1:VcWEooDEuyUJnMfbdTh0uFN4cfEIg+kHMuWB2CDCLjw=
14
14
+
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
15
15
+
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
12
16
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
13
17
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
14
18
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
15
15
-
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
16
16
-
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
19
19
+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
20
20
+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
17
21
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
18
22
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
19
23
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
···
22
26
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
23
27
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
24
28
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
29
29
+
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
30
30
+
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
25
31
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
26
32
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
27
33
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
···
36
42
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
37
43
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
38
44
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
45
45
+
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
46
46
+
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
39
47
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
40
48
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
41
41
-
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
42
42
-
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
43
49
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
44
50
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
45
51
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
···
56
62
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
57
63
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
58
64
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
59
59
-
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
60
60
-
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
61
61
-
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
62
62
-
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
63
63
-
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
64
64
-
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
65
65
-
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
66
66
-
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
65
65
+
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
66
66
+
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
67
67
+
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
68
68
+
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
69
69
+
github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8=
70
70
+
github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ=
71
71
+
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
72
72
+
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
67
73
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
68
74
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
69
75
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
···
77
83
gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8=
78
84
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q=
79
85
gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I=
86
86
+
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
87
87
+
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
80
88
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
81
89
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
90
90
+
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
91
91
+
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
82
92
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
83
93
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
84
94
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
···
86
96
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
87
97
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
88
98
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
89
89
-
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
90
90
-
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
99
99
+
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
100
100
+
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
91
101
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
92
102
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
93
103
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
94
94
-
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
95
95
-
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
104
104
+
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
105
105
+
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
96
106
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
97
107
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
98
108
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+2
-2
tmpl/upload.html
···
1
1
{{define "content"}}
2
2
<form action="/upload" method="POST" enctype="multipart/form-data">
3
3
<div><input type="file" name="button" value="select file" accept="image/png, image/jpg, image/gif"/></div>
4
4
-
<div><input type="text" name="alt" placeholder="alt text"/></div>
5
5
-
<div><input type="text" name="title" placeholder="title text"/></div>
4
4
+
<div><input type="text" name="alt" placeholder="alt text (accessibility)"/></div>
5
5
+
<div><input type="text" name="title" placeholder="title text (hover)"/></div>
6
6
<div><input type="text" name="href" placeholder="link"/></div>
7
7
<div><input type="submit" value="upload"/></div>
8
8
</form>