backend for xcvr appview

i wanna see what breaks

+483 -65
+4 -3
lexicons/org/xcvr/notes.lex
··· 54 title: string, bytes<=640, chars<=64 55 topic?: string, bytes<=2560, chars<=256 56 createdAt: string 57 - host: handle 58 59 sub: record 60 channelUri: uri 61 62 channelView?: def 63 uri: uri 64 - host: handle 65 creator: org.xcvr.actor.profileView 66 title: string, bytes<=640, chars<=64 67 topic?: string, bytes<=2560, chars<=256 ··· 88 signet: record 89 channelURI: uri 90 lrcID: int, [0 4294967295] 91 - author: string 92 startedAt?: date 93 94 media: record
··· 54 title: string, bytes<=640, chars<=64 55 topic?: string, bytes<=2560, chars<=256 56 createdAt: string 57 + host: did 58 59 sub: record 60 channelUri: uri 61 62 channelView?: def 63 uri: uri 64 + host: did 65 creator: org.xcvr.actor.profileView 66 title: string, bytes<=640, chars<=64 67 topic?: string, bytes<=2560, chars<=256 ··· 88 signet: record 89 channelURI: uri 90 lrcID: int, [0 4294967295] 91 + author: did 92 + authorHandle?: string 93 startedAt?: date 94 95 media: record
+3
migrations/007_addauthor.down.sql
···
··· 1 + UPDATE signets SET author_handle = '' WHERE author_handle IS NULL; 2 + ALTER TABLE signets ALTER COLUMN author_handle SET NOT NULL; 3 + ALTER TABLE signets DROP COLUMN author;
+2
migrations/007_addauthor.up.sql
···
··· 1 + ALTER TABLE signets ADD COLUMN author TEXT NOT NULL; 2 + ALTER TABLE signets ALTER COLUMN author_handle DROP NOT NULL;
+1
migrations/008_updatexcvrdid.down.sql
···
··· 1 + UPDATE did_handles SET did = 'did:plc:mqt7hthieqzpa3mwqwggbqei' WHERE handle = 'xcvr.org';
+1
migrations/008_updatexcvrdid.up.sql
···
··· 1 + UPDATE did_handles SET did = 'did:web:xcvr.org' WHERE handle = 'xcvr.org';
+2 -2
server/go.mod
··· 10 github.com/ipfs/go-cid v0.4.1 11 github.com/jackc/pgx/v5 v5.7.4 12 github.com/joho/godotenv v1.5.1 13 - github.com/rachel-mp4/lrcd v0.1.1 14 - github.com/rachel-mp4/lrcproto v1.2.0 15 github.com/rivo/uniseg v0.4.7 16 github.com/whyrusleeping/cbor-gen v0.3.1 17 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028
··· 10 github.com/ipfs/go-cid v0.4.1 11 github.com/jackc/pgx/v5 v5.7.4 12 github.com/joho/godotenv v1.5.1 13 + github.com/rachel-mp4/lrcd v0.2.3 14 + github.com/rachel-mp4/lrcproto v1.2.1 15 github.com/rivo/uniseg v0.4.7 16 github.com/whyrusleeping/cbor-gen v0.3.1 17 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028
+6 -14
server/go.sum
··· 24 github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= 25 github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 26 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 27 - github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 28 - github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 29 github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 30 github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 31 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= ··· 117 github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= 118 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= 119 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= 120 - github.com/rachel-mp4/lrcd v0.0.1 h1:9d5if0HJ/+TLKdupd7Zu9dISA5fF3QRtI/yr+gYVqXM= 121 - github.com/rachel-mp4/lrcd v0.0.1/go.mod h1:aWUVglSyrLf2RGpQdqTucX498b434yWBFJkD6Yzr0OE= 122 - github.com/rachel-mp4/lrcd v0.1.0 h1:iW3ux8otqo+9tYpP2Bsxu5VgbhP324Mjy0vOBf3M39Q= 123 - github.com/rachel-mp4/lrcd v0.1.0/go.mod h1:CyBPWmy94oQ0sTOj24VJjxWoZ9zSnqCKQ/qlR+vhtVE= 124 - github.com/rachel-mp4/lrcd v0.1.1 h1:2AXK61+pnL71ZqINLnxNfKqX/F0hUw+REFbBFZOjJcY= 125 - github.com/rachel-mp4/lrcd v0.1.1/go.mod h1:CyBPWmy94oQ0sTOj24VJjxWoZ9zSnqCKQ/qlR+vhtVE= 126 - github.com/rachel-mp4/lrcproto v0.0.0-20250905154858-2ddb78e31d0c h1:t33xVlfSwvB80nj1jroRXUaq/RTgjWwD4l7p/ISatUQ= 127 - github.com/rachel-mp4/lrcproto v0.0.0-20250905154858-2ddb78e31d0c/go.mod h1:hQzO36tQELGbkmRnUtKeM6NMU34t79ZcTlhM+MO7pHw= 128 - github.com/rachel-mp4/lrcproto v1.2.0 h1:nZI80WQKO6yKgX0O5H6OO1cM/tiJqugs0p52KCoIDOw= 129 - github.com/rachel-mp4/lrcproto v1.2.0/go.mod h1:hQzO36tQELGbkmRnUtKeM6NMU34t79ZcTlhM+MO7pHw= 130 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 131 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 132 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= ··· 170 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 171 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= 172 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 173 - google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= 174 - google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= 175 google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= 176 google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 177 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
··· 24 github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= 25 github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 26 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 27 + github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 28 + github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 29 github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 30 github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 31 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= ··· 117 github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ= 118 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= 119 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= 120 + github.com/rachel-mp4/lrcd v0.2.3 h1:HT3UjFn2ZK2zpFkS8U7KU1+I9kMA/oBF2FVWd7x0TkM= 121 + github.com/rachel-mp4/lrcd v0.2.3/go.mod h1:CyBPWmy94oQ0sTOj24VJjxWoZ9zSnqCKQ/qlR+vhtVE= 122 + github.com/rachel-mp4/lrcproto v1.2.1 h1:oKP/4u8JaRzV9vY/BUn24VTrsN7pgty0S4AP8hWc/JI= 123 + github.com/rachel-mp4/lrcproto v1.2.1/go.mod h1:hQzO36tQELGbkmRnUtKeM6NMU34t79ZcTlhM+MO7pHw= 124 github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 125 github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 126 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= ··· 164 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 165 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= 166 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 167 google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= 168 google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 169 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+196 -1
server/internal/db/db.go
··· 6 "fmt" 7 "os" 8 "rvcx/internal/atputils" 9 "rvcx/internal/types" 10 "time" 11 ··· 55 return did, nil 56 } 57 58 func (s *Store) ResolveDid(did string, ctx context.Context) (string, error) { 59 row := s.pool.QueryRow(ctx, `SELECT h.handle FROM did_handles h WHERE h.did = $1`, did) 60 var handle string ··· 101 return 102 } 103 104 func (s *Store) GetMessages(channelURI string, limit int, cursor *int, ctx context.Context) ([]types.SignedMessageView, error) { 105 queryFmt := ` 106 SELECT ··· 168 &msg.Color, 169 170 &msg.Signet.URI, 171 - &msg.Signet.IssuerHandle, 172 &msg.Signet.ChannelURI, 173 &msg.Signet.LrcId, 174 &msg.Signet.AuthorHandle,
··· 6 "fmt" 7 "os" 8 "rvcx/internal/atputils" 9 + "rvcx/internal/lex" 10 "rvcx/internal/types" 11 "time" 12 ··· 56 return did, nil 57 } 58 59 + func (s *Store) FullResolveHandle(hdl string, ctx context.Context) (string, error) { 60 + did, err := s.ResolveHandle(hdl, ctx) 61 + if err == nil { 62 + return did, nil 63 + } 64 + did, err = atputils.TryLookupHandle(ctx, hdl) 65 + if err != nil { 66 + return "", errors.New("couldn't resolve: " + err.Error()) 67 + } 68 + s.StoreDidHandle(did, hdl, ctx) 69 + return did, nil 70 + } 71 + 72 func (s *Store) ResolveDid(did string, ctx context.Context) (string, error) { 73 row := s.pool.QueryRow(ctx, `SELECT h.handle FROM did_handles h WHERE h.did = $1`, did) 74 var handle string ··· 115 return 116 } 117 118 + func (s *Store) GetHistory(channelURI string, limit int, cursor *int, ctx context.Context) ([]types.SignedItemView, error) { 119 + queryFmt := ` 120 + SELECT 121 + 'message' AS content_type, 122 + m.uri, 123 + m.did, 124 + dh.handle, 125 + p.display_name, 126 + p.status, 127 + p.color, 128 + p.avatar_cid, 129 + p.default_nick, 130 + m.body, 131 + NULL AS blob_cid, 132 + NULL AS blob_mime, 133 + NULL AS alt, 134 + NULL AS height, 135 + NULL AS width, 136 + m.nick, 137 + m.color, 138 + s.uri, 139 + s.issuer_did, 140 + s.channel_uri, 141 + s.message_id, 142 + s.author, 143 + s.author_handle, 144 + s.started_at, 145 + m.posted_at 146 + FROM signets s 147 + JOIN messages m ON s.uri = m.signet_uri 148 + JOIN did_handles dh ON m.did = dh.did 149 + JOIN profiles p ON m.did = p.did 150 + WHERE s.channel_uri = $2 AND m.did = s.author %s 151 + 152 + UNION ALL 153 + 154 + SELECT 155 + 'image' AS content_type, 156 + i.uri, 157 + i.did, 158 + dh.handle, 159 + p.display_name, 160 + p.status, 161 + p.color, 162 + p.avatar_cid, 163 + p.default_nick, 164 + NULL AS body, 165 + i.blob_cid, 166 + i.blob_mime, 167 + i.alt, 168 + i.height, 169 + i.width, 170 + i.nick, 171 + i.color, 172 + s.uri, 173 + s.issuer_did, 174 + s.channel_uri, 175 + s.message_id, 176 + s.author, 177 + s.author_handle, 178 + s.started_at, 179 + i.posted_at 180 + FROM signets s 181 + JOIN images i ON s.uri = i.signet_uri 182 + JOIN did_handles dh ON i.did = dh.did 183 + JOIN profiles p ON i.did = p.did 184 + WHERE s.channel_uri = $2 AND i.did = s.author %s 185 + 186 + ORDER BY message_id DESC 187 + LIMIT $1 188 + ` 189 + var query string 190 + if cursor != nil { 191 + query = fmt.Sprintf(queryFmt, "AND s.message_id < $3", "AND s.message_id < $3") 192 + return s.evalGetItems(query, ctx, limit, *cursor) 193 + } else { 194 + query = fmt.Sprintf(queryFmt, "", "") 195 + return s.evalGetItems(query, ctx, limit) 196 + } 197 + } 198 + func (s *Store) evalGetItems(query string, ctx context.Context, limit int, params ...any) ([]types.SignedItemView, error) { 199 + args := []any{limit} 200 + args = append(args, params...) 201 + rows, err := s.pool.Query(ctx, query, args...) 202 + if err != nil { 203 + return nil, err 204 + } 205 + defer rows.Close() 206 + var items = make([]types.SignedItemView, 0) 207 + for rows.Next() { 208 + var t string 209 + var p types.ProfileView 210 + var uri string 211 + var body string 212 + var image types.Image 213 + var nick string 214 + var color uint32 215 + var s types.SignetView 216 + var time time.Time 217 + 218 + err := rows.Scan( 219 + &t, 220 + &uri, 221 + &p.DID, 222 + &p.Handle, 223 + &p.DisplayName, 224 + &p.Status, 225 + &p.Color, 226 + &p.Avatar, 227 + &p.DefaultNick, 228 + &body, 229 + &image.BlobCID, 230 + &image.BlobMIME, 231 + &image.Alt, 232 + &image.Height, 233 + &image.Width, 234 + 235 + &nick, 236 + &color, 237 + 238 + &s.URI, 239 + &s.Issuer, 240 + &s.ChannelURI, 241 + &s.LrcId, 242 + &s.Author, 243 + &s.AuthorHandle, 244 + &s.StartedAt, 245 + &time, 246 + ) 247 + if err != nil { 248 + return nil, err 249 + } 250 + if t == "message" { 251 + var msg types.SignedMessageView 252 + msg.Body = body 253 + if nick != "" { 254 + msg.Nick = &nick 255 + } 256 + if color != 0 { 257 + msg.Color = &color 258 + } 259 + msg.Author = p 260 + msg.Signet = s 261 + msg.PostedAt = time 262 + msg.URI = uri 263 + 264 + items = append(items, msg) 265 + } else if t == "image" { 266 + var img types.SignedMediaView 267 + var imgview types.ImageView 268 + if image.Height != nil && image.Width != nil { 269 + var aspect lex.AspectRatio 270 + aspect.Width = *image.Width 271 + aspect.Height = *image.Height 272 + imgview.AspectRatio = &aspect 273 + } 274 + imgview.Alt = image.Alt 275 + base := os.Getenv("MY_IDENTITY") 276 + src := fmt.Sprintf("https://%s/xrpc/org.xcvr.lrc.getImage?uri=%s", base, uri) 277 + imgview.Src = &src 278 + img.Image = &imgview 279 + if nick != "" { 280 + img.Nick = &nick 281 + } 282 + if color != 0 { 283 + img.Color = &color 284 + } 285 + img.Author = p 286 + img.Signet = s 287 + img.PostedAt = time 288 + img.URI = uri 289 + 290 + items = append(items, img) 291 + } else { 292 + return nil, errors.New("recieved strange type t: " + t) 293 + } 294 + } 295 + return items, nil 296 + 297 + } 298 + 299 func (s *Store) GetMessages(channelURI string, limit int, cursor *int, ctx context.Context) ([]types.SignedMessageView, error) { 300 queryFmt := ` 301 SELECT ··· 363 &msg.Color, 364 365 &msg.Signet.URI, 366 + &msg.Signet.Issuer, 367 &msg.Signet.ChannelURI, 368 &msg.Signet.LrcId, 369 &msg.Signet.AuthorHandle,
+18 -5
server/internal/db/lexicon.go
··· 220 return 221 } 222 223 func (s *Store) QuerySignetHandle(uri string, ctx context.Context) (string, error) { 224 row := s.pool.QueryRow(ctx, `SELECT s.author_handle FROM signets s WHERE s.uri = $1`, uri) 225 var handle string ··· 228 return "", errors.New("BOBOBOBOBOBOL " + err.Error()) 229 } 230 return handle, nil 231 } 232 233 func (s *Store) QuerySignetChannelIdNum(uri string, ctx context.Context) (channelUri string, messageID uint32, err error) { ··· 254 INSERT INTO signets ( 255 uri, 256 issuer_did, 257 author_handle, 258 channel_uri, 259 message_id, 260 cid, 261 started_at 262 ) VALUES ( 263 - $1, $2, $3, $4, $5, $6, $7 264 ) ON CONFLICT (uri) DO NOTHING 265 - `, signet.URI, signet.IssuerDID, signet.AuthorHandle, signet.ChannelURI, signet.MessageID, signet.CID, signet.StartedAt) 266 if err != nil { 267 err = errors.New("SOMETHING BAD HAPPENED: " + err.Error()) 268 return ··· 276 INSERT INTO signets ( 277 uri, 278 issuer_did, 279 - AuthorHandle, 280 channel_uri, 281 message_id, 282 cid, 283 started_at 284 ) VALUES ( 285 - $1, $2, $3, $4, $5, $6, $7 286 ) 287 - `, signet.URI, signet.IssuerDID, signet.AuthorHandle, signet.ChannelURI, signet.MessageID, signet.CID, signet.StartedAt) 288 if err != nil { 289 err = errors.New("SOMETHING BAD HAPPENED: " + err.Error()) 290 }
··· 220 return 221 } 222 223 + // QuerySignetHandle takes a uri an queries the appropriate signet's handle. 224 + // Deprecated: i should QuerySignetDid instead 225 func (s *Store) QuerySignetHandle(uri string, ctx context.Context) (string, error) { 226 row := s.pool.QueryRow(ctx, `SELECT s.author_handle FROM signets s WHERE s.uri = $1`, uri) 227 var handle string ··· 230 return "", errors.New("BOBOBOBOBOBOL " + err.Error()) 231 } 232 return handle, nil 233 + } 234 + func (s *Store) QuerySignetDid(uri string, ctx context.Context) (string, error) { 235 + row := s.pool.QueryRow(ctx, `SELECT s.author FROM signets s WHERE s.uri = $1`, uri) 236 + var did string 237 + err := row.Scan(&did) 238 + if err != nil { 239 + return "", errors.New("BOBOBOBOBOBOL " + err.Error()) 240 + } 241 + return did, nil 242 } 243 244 func (s *Store) QuerySignetChannelIdNum(uri string, ctx context.Context) (channelUri string, messageID uint32, err error) { ··· 265 INSERT INTO signets ( 266 uri, 267 issuer_did, 268 + author, 269 author_handle, 270 channel_uri, 271 message_id, 272 cid, 273 started_at 274 ) VALUES ( 275 + $1, $2, $3, $4, $5, $6, $7, $8 276 ) ON CONFLICT (uri) DO NOTHING 277 + `, signet.URI, signet.IssuerDID, signet.Author, signet.AuthorHandle, signet.ChannelURI, signet.MessageID, signet.CID, signet.StartedAt) 278 if err != nil { 279 err = errors.New("SOMETHING BAD HAPPENED: " + err.Error()) 280 return ··· 288 INSERT INTO signets ( 289 uri, 290 issuer_did, 291 + author, 292 + author_handle, 293 channel_uri, 294 message_id, 295 cid, 296 started_at 297 ) VALUES ( 298 + $1, $2, $3, $4, $5, $6, $7, $8 299 ) 300 + `, signet.URI, signet.IssuerDID, signet.Author, signet.AuthorHandle, signet.ChannelURI, signet.MessageID, signet.CID, signet.StartedAt) 301 if err != nil { 302 err = errors.New("SOMETHING BAD HAPPENED: " + err.Error()) 303 }
+1
server/internal/handler/handler.go
··· 42 mux.HandleFunc("GET /xrpc/org.xcvr.feed.getChannels", h.WithCORS(h.getChannels)) 43 mux.HandleFunc("GET /xrpc/org.xcvr.feed.getChannel", h.WithCORS(h.getChannel)) 44 mux.HandleFunc("GET /xrpc/org.xcvr.lrc.getMessages", h.WithCORS(h.getMessages)) 45 mux.HandleFunc("GET /xrpc/org.xcvr.lrc.getImage", h.WithCORS(h.getImage)) 46 mux.HandleFunc("GET /xrpc/org.xcvr.actor.resolveChannel", h.WithCORS(h.resolveChannel)) 47 mux.HandleFunc("GET /xrpc/org.xcvr.actor.getProfileView", h.WithCORS(h.getProfileView))
··· 42 mux.HandleFunc("GET /xrpc/org.xcvr.feed.getChannels", h.WithCORS(h.getChannels)) 43 mux.HandleFunc("GET /xrpc/org.xcvr.feed.getChannel", h.WithCORS(h.getChannel)) 44 mux.HandleFunc("GET /xrpc/org.xcvr.lrc.getMessages", h.WithCORS(h.getMessages)) 45 + mux.HandleFunc("GET /xrpc/org.xcvr.lrc.getHistory", h.WithCORS(h.getHistory)) 46 mux.HandleFunc("GET /xrpc/org.xcvr.lrc.getImage", h.WithCORS(h.getImage)) 47 mux.HandleFunc("GET /xrpc/org.xcvr.actor.resolveChannel", h.WithCORS(h.resolveChannel)) 48 mux.HandleFunc("GET /xrpc/org.xcvr.actor.getProfileView", h.WithCORS(h.getProfileView))
+55
server/internal/handler/lexiconHandlers.go
··· 88 encoder.Encode(gmo) 89 } 90 91 func (h *Handler) resolveChannel(w http.ResponseWriter, r *http.Request) { 92 handle := r.URL.Query().Get("handle") 93 did := r.URL.Query().Get("did")
··· 88 encoder.Encode(gmo) 89 } 90 91 + func (h *Handler) getHistory(w http.ResponseWriter, r *http.Request) { 92 + limitstr := r.URL.Query().Get("limit") 93 + limit := 50 94 + if limitstr != "" { 95 + l, err := strconv.Atoi(limitstr) 96 + if err == nil { 97 + limit = max(min(l, 100), 1) 98 + } 99 + } 100 + cursorstr := r.URL.Query().Get("cursor") 101 + var cursor *int 102 + if cursorstr != "" { 103 + c, err := strconv.Atoi(cursorstr) 104 + if err == nil { 105 + cursor = &c 106 + } 107 + } 108 + channelURI := r.URL.Query().Get("channelURI") 109 + items, err := h.db.GetHistory(channelURI, limit, cursor, r.Context()) 110 + if err != nil { 111 + h.serverError(w, errors.New("something went south: "+err.Error())) 112 + return 113 + } 114 + if len(items) != 0 { 115 + var signet types.SignetView 116 + smv := items[len(items)-1] 117 + if smv.IsMedia() { 118 + media, _ := smv.ToSignedMediaView() 119 + signet = media.Signet 120 + } else if smv.IsMessage() { 121 + message, _ := smv.ToSignedMessageView() 122 + signet = message.Signet 123 + } else { 124 + h.serverError(w, errors.New("last item is invalid Signed Item")) 125 + return 126 + } 127 + if int(signet.LrcId) > 2 { 128 + cursor := strconv.Itoa(int(signet.LrcId)) 129 + w.Header().Set("Content-Type", "application/json") 130 + jsitems, err := types.MarshalItems(items) 131 + if err != nil { 132 + h.serverError(w, err) 133 + } 134 + fmt.Fprintf(w, "{\"items\": %s, \"cursor\": %s}", jsitems, cursor) 135 + return 136 + } 137 + } 138 + w.Header().Set("Content-Type", "application/json") 139 + jsitems, err := types.MarshalItems(items) 140 + if err != nil { 141 + h.serverError(w, err) 142 + } 143 + fmt.Fprintf(w, "{\"items\": %s}", jsitems) 144 + } 145 + 146 func (h *Handler) resolveChannel(w http.ResponseWriter, r *http.Request) { 147 handle := r.URL.Query().Get("handle") 148 did := r.URL.Query().Get("did")
+77 -20
server/internal/lex/lexicons_cbor.go
··· 959 } 960 961 cw := cbg.NewCborWriter(w) 962 - fieldCount := 5 963 964 if t.StartedAt == nil { 965 fieldCount-- ··· 1004 return err 1005 } 1006 1007 // t.StartedAt (string) (string) 1008 if t.StartedAt != nil { 1009 ··· 1060 } 1061 1062 // t.AuthorHandle (string) (string) 1063 - if len("authorHandle") > 8192 { 1064 - return xerrors.Errorf("Value in field \"authorHandle\" was too long") 1065 - } 1066 1067 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("authorHandle"))); err != nil { 1068 - return err 1069 - } 1070 - if _, err := cw.WriteString(string("authorHandle")); err != nil { 1071 - return err 1072 - } 1073 1074 - if len(t.AuthorHandle) > 8192 { 1075 - return xerrors.Errorf("Value in field t.AuthorHandle was too long") 1076 - } 1077 1078 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.AuthorHandle))); err != nil { 1079 - return err 1080 - } 1081 - if _, err := cw.WriteString(string(t.AuthorHandle)); err != nil { 1082 - return err 1083 } 1084 return nil 1085 } ··· 1151 t.LRCID = uint64(extra) 1152 1153 } 1154 // t.StartedAt (string) (string) 1155 case "startedAt": 1156 ··· 1187 case "authorHandle": 1188 1189 { 1190 - sval, err := cbg.ReadStringWithMax(cr, 8192) 1191 if err != nil { 1192 return err 1193 } 1194 1195 - t.AuthorHandle = string(sval) 1196 } 1197 1198 default:
··· 959 } 960 961 cw := cbg.NewCborWriter(w) 962 + fieldCount := 6 963 + 964 + if t.AuthorHandle == nil { 965 + fieldCount-- 966 + } 967 968 if t.StartedAt == nil { 969 fieldCount-- ··· 1008 return err 1009 } 1010 1011 + // t.Author (string) (string) 1012 + if len("author") > 8192 { 1013 + return xerrors.Errorf("Value in field \"author\" was too long") 1014 + } 1015 + 1016 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("author"))); err != nil { 1017 + return err 1018 + } 1019 + if _, err := cw.WriteString(string("author")); err != nil { 1020 + return err 1021 + } 1022 + 1023 + if len(t.Author) > 8192 { 1024 + return xerrors.Errorf("Value in field t.Author was too long") 1025 + } 1026 + 1027 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Author))); err != nil { 1028 + return err 1029 + } 1030 + if _, err := cw.WriteString(string(t.Author)); err != nil { 1031 + return err 1032 + } 1033 + 1034 // t.StartedAt (string) (string) 1035 if t.StartedAt != nil { 1036 ··· 1087 } 1088 1089 // t.AuthorHandle (string) (string) 1090 + if t.AuthorHandle != nil { 1091 1092 + if len("authorHandle") > 8192 { 1093 + return xerrors.Errorf("Value in field \"authorHandle\" was too long") 1094 + } 1095 + 1096 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("authorHandle"))); err != nil { 1097 + return err 1098 + } 1099 + if _, err := cw.WriteString(string("authorHandle")); err != nil { 1100 + return err 1101 + } 1102 1103 + if t.AuthorHandle == nil { 1104 + if _, err := cw.Write(cbg.CborNull); err != nil { 1105 + return err 1106 + } 1107 + } else { 1108 + if len(*t.AuthorHandle) > 8192 { 1109 + return xerrors.Errorf("Value in field t.AuthorHandle was too long") 1110 + } 1111 1112 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.AuthorHandle))); err != nil { 1113 + return err 1114 + } 1115 + if _, err := cw.WriteString(string(*t.AuthorHandle)); err != nil { 1116 + return err 1117 + } 1118 + } 1119 } 1120 return nil 1121 } ··· 1187 t.LRCID = uint64(extra) 1188 1189 } 1190 + // t.Author (string) (string) 1191 + case "author": 1192 + 1193 + { 1194 + sval, err := cbg.ReadStringWithMax(cr, 8192) 1195 + if err != nil { 1196 + return err 1197 + } 1198 + 1199 + t.Author = string(sval) 1200 + } 1201 // t.StartedAt (string) (string) 1202 case "startedAt": 1203 ··· 1234 case "authorHandle": 1235 1236 { 1237 + b, err := cr.ReadByte() 1238 if err != nil { 1239 return err 1240 } 1241 + if b != cbg.CborNull[0] { 1242 + if err := cr.UnreadByte(); err != nil { 1243 + return err 1244 + } 1245 1246 + sval, err := cbg.ReadStringWithMax(cr, 8192) 1247 + if err != nil { 1248 + return err 1249 + } 1250 + 1251 + t.AuthorHandle = (*string)(&sval) 1252 + } 1253 } 1254 1255 default:
+2 -1
server/internal/lex/types.go
··· 41 LexiconTypeID string `json:"$type,const=org.xcvr.lrc.signet" cborgen:"$type,const=org.xcvr.lrc.signet"` 42 ChannelURI string `json:"channelURI" cborgen:"channelURI"` 43 LRCID uint64 `json:"lrcID" cborgen:"lrcID"` 44 - AuthorHandle string `json:"authorHandle" cborgen:"authorHandle"` 45 StartedAt *string `json:"startedAt,omitempty" cborgen:"startedAt,omitempty"` 46 } 47
··· 41 LexiconTypeID string `json:"$type,const=org.xcvr.lrc.signet" cborgen:"$type,const=org.xcvr.lrc.signet"` 42 ChannelURI string `json:"channelURI" cborgen:"channelURI"` 43 LRCID uint64 `json:"lrcID" cborgen:"lrcID"` 44 + Author string `json:"author" cborgen:"author"` 45 + AuthorHandle *string `json:"authorHandle,omitempty" cborgen:"authorHandle,omitempty"` 46 StartedAt *string `json:"startedAt,omitempty" cborgen:"startedAt,omitempty"` 47 } 48
+20 -8
server/internal/model/channel.go
··· 34 welcome string 35 server *lrcd.Server 36 lastID uint32 37 - initChan <-chan lrcpb.Event_Init 38 - mediainitChan <-chan lrcpb.Event_Mediainit 39 ctx context.Context 40 cancel func() 41 ··· 164 m.logger.Deprintln("i think the server should exist, so i'm making it") 165 var err error 166 lastID := cm.lastID 167 - initChan := make(chan lrcpb.Event_Init, 100) 168 - mediainitChan := make(chan lrcpb.Event_Mediainit, 100) 169 170 server, err := lrcd.NewServer( 171 lrcd.WithWelcome(cm.welcome), 172 lrcd.WithLogging(os.Stdout, true), 173 lrcd.WithInitialID(lastID), ··· 230 cm.logger.Println("this is a weird case!") 231 return 232 } 233 - err := m.rm.PostSignet(e, cm.uri, context.Background()) 234 if err != nil { 235 m.logger.Println("error posting signet: " + err.Error()) 236 } ··· 241 } 242 e := lrcpb.Event_Init{ 243 Init: &lrcpb.Init{ 244 - Id: me.Mediainit.Id, 245 - ExternalID: me.Mediainit.ExternalID, 246 }, 247 } 248 - err := m.rm.PostSignet(e, cm.uri, context.Background()) 249 if err != nil { 250 m.logger.Println("error posting signet: " + err.Error()) 251 }
··· 34 welcome string 35 server *lrcd.Server 36 lastID uint32 37 + initChan <-chan lrcd.InitChanMsg 38 + mediainitChan <-chan lrcd.MediaInitChanMsg 39 ctx context.Context 40 cancel func() 41 ··· 164 m.logger.Deprintln("i think the server should exist, so i'm making it") 165 var err error 166 lastID := cm.lastID 167 + initChan := make(chan lrcd.InitChanMsg, 100) 168 + mediainitChan := make(chan lrcd.MediaInitChanMsg, 100) 169 170 server, err := lrcd.NewServer( 171 + lrcd.WithResolver(func(externalID string, ctx context.Context) *string { 172 + did, err := m.store.FullResolveHandle(externalID, ctx) 173 + if err != nil { 174 + select { 175 + case <-ctx.Done(): 176 + return nil 177 + default: 178 + return &did 179 + } 180 + } 181 + return &did 182 + }), 183 lrcd.WithWelcome(cm.welcome), 184 lrcd.WithLogging(os.Stdout, true), 185 lrcd.WithInitialID(lastID), ··· 242 cm.logger.Println("this is a weird case!") 243 return 244 } 245 + err := m.rm.PostSignet(e.ResolvedId, e.Init, cm.uri, context.Background()) 246 if err != nil { 247 m.logger.Println("error posting signet: " + err.Error()) 248 } ··· 253 } 254 e := lrcpb.Event_Init{ 255 Init: &lrcpb.Init{ 256 + Id: me.Mediainit.Mediainit.Id, 257 + ExternalID: me.Mediainit.Mediainit.ExternalID, 258 }, 259 } 260 + err := m.rm.PostSignet(me.ResolvedId, e, cm.uri, context.Background()) 261 if err != nil { 262 m.logger.Println("error posting signet: " + err.Error()) 263 }
+1 -1
server/internal/model/channelLexiconStream.go
··· 104 } 105 sv := types.SignetView{ 106 URI: s.URI, 107 - IssuerHandle: ihandle, 108 ChannelURI: s.ChannelURI, 109 LrcId: s.MessageID, 110 AuthorHandle: s.AuthorHandle,
··· 104 } 105 sv := types.SignetView{ 106 URI: s.URI, 107 + Issuer: s.IssuerDID, 108 ChannelURI: s.ChannelURI, 109 LrcId: s.MessageID, 110 AuthorHandle: s.AuthorHandle,
+14 -7
server/internal/recordmanager/signet.go
··· 11 "time" 12 ) 13 14 - func (rm *RecordManager) PostSignet(e lrcpb.Event_Init, uri string, ctx context.Context) error { 15 - lsr, now, err := rm.validateSignet(e, uri) 16 if err != nil { 17 return errors.New("failed to validate signet: " + err.Error()) 18 } ··· 71 return rm.db.UpdateSignet(s, ctx) 72 } 73 74 - func (rm *RecordManager) validateSignet(e lrcpb.Event_Init, uri string) (*lex.SignetRecord, *time.Time, error) { 75 signet := lex.SignetRecord{} 76 handle := e.Init.ExternalID 77 - if handle == nil { 78 - h := "" 79 - handle = &h 80 } 81 - signet.AuthorHandle = *handle 82 if e.Init.Id == nil { 83 return nil, nil, errors.New("ID should not be nil") 84 } ··· 103 sr := types.Signet{ 104 URI: recorduri, 105 IssuerDID: atputils.GetMyDid(), 106 AuthorHandle: lsr.AuthorHandle, 107 ChannelURI: lsr.ChannelURI, 108 MessageID: id,
··· 11 "time" 12 ) 13 14 + func (rm *RecordManager) PostSignet(resolvedId *string, e lrcpb.Event_Init, uri string, ctx context.Context) error { 15 + lsr, now, err := rm.validateSignet(resolvedId, e, uri, ctx) 16 if err != nil { 17 return errors.New("failed to validate signet: " + err.Error()) 18 } ··· 71 return rm.db.UpdateSignet(s, ctx) 72 } 73 74 + func (rm *RecordManager) validateSignet(resolvedId *string, e lrcpb.Event_Init, uri string, ctx context.Context) (*lex.SignetRecord, *time.Time, error) { 75 signet := lex.SignetRecord{} 76 handle := e.Init.ExternalID 77 + if resolvedId == nil { 78 + if handle != nil { 79 + did, _ := rm.db.FullResolveHandle(*handle, ctx) 80 + signet.Author = did 81 + } else { 82 + signet.Author = "" 83 + } 84 + } else { 85 + signet.Author = *resolvedId 86 } 87 + signet.AuthorHandle = handle 88 if e.Init.Id == nil { 89 return nil, nil, errors.New("ID should not be nil") 90 } ··· 109 sr := types.Signet{ 110 URI: recorduri, 111 IssuerDID: atputils.GetMyDid(), 112 + Author: lsr.Author, 113 AuthorHandle: lsr.AuthorHandle, 114 ChannelURI: lsr.ChannelURI, 115 MessageID: id,
+80 -3
server/internal/types/lexicons.go
··· 1 package types 2 3 import ( 4 "encoding/json" 5 "rvcx/internal/lex" 6 "time" 7 ) ··· 111 type Signet struct { 112 URI string 113 IssuerDID string 114 - AuthorHandle string 115 ChannelURI string 116 MessageID uint32 117 CID string ··· 122 type SignetView struct { 123 Type string `json:"$type,const=org.xcvr.lrc.defs#signetView"` 124 URI string `json:"uri"` 125 - IssuerHandle string `json:"issuerHandle"` 126 ChannelURI string `json:"channelURI"` 127 LrcId uint32 `json:"lrcID"` 128 - AuthorHandle string `json:"authorHandle"` 129 StartedAt time.Time `json:"startedAt"` 130 } 131 ··· 286 Alias: (*Alias)(&m), 287 }) 288 }
··· 1 package types 2 3 import ( 4 + "bytes" 5 "encoding/json" 6 + "errors" 7 "rvcx/internal/lex" 8 "time" 9 ) ··· 113 type Signet struct { 114 URI string 115 IssuerDID string 116 + Author string 117 + AuthorHandle *string 118 ChannelURI string 119 MessageID uint32 120 CID string ··· 125 type SignetView struct { 126 Type string `json:"$type,const=org.xcvr.lrc.defs#signetView"` 127 URI string `json:"uri"` 128 + Issuer string `json:"issuer"` 129 ChannelURI string `json:"channelURI"` 130 LrcId uint32 `json:"lrcID"` 131 + Author string `json:"author"` 132 + AuthorHandle *string `json:"authorHandle,omitempty"` 133 StartedAt time.Time `json:"startedAt"` 134 } 135 ··· 290 Alias: (*Alias)(&m), 291 }) 292 } 293 + 294 + type SignedItemView interface { 295 + IsMedia() bool 296 + IsMessage() bool 297 + ToSignedMessageView() (*SignedMessageView, error) 298 + ToSignedMediaView() (*SignedMediaView, error) 299 + } 300 + 301 + func MarshalItems(items []SignedItemView) ([]byte, error) { 302 + bb := make([][]byte, 2*len(items)+1) 303 + bb[0] = []byte("[") 304 + for i, item := range items { 305 + if i != 0 { 306 + bb[2*i] = []byte(",") 307 + } 308 + if item.IsMedia() { 309 + smv, err := item.ToSignedMediaView() 310 + if err != nil { 311 + return nil, err 312 + } 313 + b, err := smv.MarshalJSON() 314 + if err != nil { 315 + return nil, err 316 + } 317 + bb[2*i+1] = b 318 + } else if item.IsMessage() { 319 + smv, err := item.ToSignedMessageView() 320 + if err != nil { 321 + return nil, err 322 + } 323 + b, err := smv.MarshalJSON() 324 + if err != nil { 325 + return nil, err 326 + } 327 + bb[2*i+1] = b 328 + } else { 329 + return nil, errors.New("item is neither media nor message") 330 + } 331 + } 332 + bb[2*len(items)] = []byte("]") 333 + return bytes.Join(bb, nil), nil 334 + } 335 + 336 + func (s SignedMediaView) IsMedia() bool { 337 + return true 338 + } 339 + 340 + func (s SignedMessageView) IsMedia() bool { 341 + return false 342 + } 343 + 344 + func (s SignedMediaView) IsMessage() bool { 345 + return false 346 + } 347 + 348 + func (s SignedMessageView) IsMessage() bool { 349 + return true 350 + } 351 + 352 + func (s SignedMediaView) ToSignedMessageView() (*SignedMessageView, error) { 353 + return nil, errors.New("i am not a message") 354 + } 355 + 356 + func (s SignedMessageView) ToSignedMediaView() (*SignedMediaView, error) { 357 + return nil, errors.New("i am not a media") 358 + } 359 + func (s SignedMessageView) ToSignedMessageView() (*SignedMessageView, error) { 360 + return &s, nil 361 + } 362 + 363 + func (s SignedMediaView) ToSignedMediaView() (*SignedMediaView, error) { 364 + return &s, nil 365 + }