backend for xcvr appview
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

add stuff to write lexicons to user repos

rachel-mp4 3ec59c7c 5b25ba62

+1452 -65
-1
.gitignore
··· 26 26 27 27 jwks.json 28 28 29 - server/internal/lex/lexicons_cbor.go
+1 -2
lexicons/org/xcvr/lrc/signet.json
··· 21 21 }, 22 22 "author": { 23 23 "type": "string", 24 - "format": "did" 25 24 }, 26 25 "startedAt": { 27 26 "type": "string", ··· 31 30 } 32 31 } 33 32 } 34 - } 33 + }
+4 -1
server/gen/main.go
··· 7 7 8 8 func main() { 9 9 if err := cbg.WriteMapEncodersToFile("internal/lex/lexicons_cbor.go", "lex", 10 - lex.ProfileRecord{}); err != nil { 10 + lex.ProfileRecord{}, 11 + lex.ChannelRecord{}, 12 + lex.MessageRecord{}, 13 + lex.SignetRecord{}); err != nil { 11 14 panic(err) 12 15 } 13 16 }
+65 -2
server/internal/atputils/identity.go
··· 1 1 package atputils 2 + 2 3 import ( 3 4 "context" 5 + "encoding/json" 6 + "errors" 7 + "fmt" 4 8 "github.com/bluesky-social/indigo/atproto/identity" 5 9 "github.com/bluesky-social/indigo/atproto/syntax" 6 - "errors" 10 + "io" 11 + "net/http" 12 + "strings" 7 13 ) 8 14 9 15 func GetHandleFromDid(ctx context.Context, did string) (string, error) { ··· 26 32 return "", errors.New("handle did not parse: " + err.Error()) 27 33 } 28 34 resolver := identity.DefaultDirectory() 29 - ident, err := resolver.LookupHandle(ctx,shandle) 35 + ident, err := resolver.LookupHandle(ctx, shandle) 30 36 if err != nil { 31 37 return "", errors.New("failed to lookupHandle: " + err.Error()) 32 38 } 33 39 return ident.DID.String(), nil 34 40 } 41 + 42 + func GetPDSFromHandle(ctx context.Context, handle string) (string, error) { 43 + did, err := GetDidFromHandle(ctx, handle) 44 + if err != nil { 45 + return "", errors.New("failed to find did from handle in handle->pds: " + err.Error()) 46 + } 47 + return GetPDSFromDid(ctx, did, http.DefaultClient) 48 + } 49 + 50 + func GetPDSFromDid(ctx context.Context, did string, cli *http.Client) (string, error) { 51 + type Identity struct { 52 + Service []struct { 53 + ID string `json:"id"` 54 + Type string `json:"type"` 55 + ServiceEndpoint string `json:"serviceEndpoint"` 56 + } `json:"service"` 57 + } 58 + var url string 59 + if strings.HasPrefix(did, "did:plc:") { 60 + url = fmt.Sprintf("https://plc.directory/%s", did) 61 + } else if strings.HasPrefix(did, "did:web:") { 62 + url = fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")) 63 + } else { 64 + return "", errors.New("did type not supported") 65 + } 66 + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) 67 + if err != nil { 68 + return "", errors.New("error crafting request:" + err.Error()) 69 + } 70 + resp, err := cli.Do(req) 71 + if err != nil { 72 + return "", errors.New("error evaluating request:" + err.Error()) 73 + } 74 + defer resp.Body.Close() 75 + if resp.StatusCode != 200 { 76 + return "", errors.New("could not resolve did to service") 77 + } 78 + b, err := io.ReadAll(resp.Body) 79 + if err != nil { 80 + return "", errors.New("error reading response body:" + err.Error()) 81 + } 82 + var identity Identity 83 + err = json.Unmarshal(b, &identity) 84 + if err != nil { 85 + return "", errors.New("error unmarshaling to identity:" + err.Error()) 86 + } 87 + var service *string 88 + for _, svc := range identity.Service { 89 + if svc.ID == "#atproto_pds" { 90 + service = &svc.ServiceEndpoint 91 + } 92 + } 93 + if service == nil { 94 + return "", errors.New("could not find atproto_pds service in resolved did's services") 95 + } 96 + return *service, nil 97 + }
+20 -3
server/internal/handler/handler.go
··· 1 1 package handler 2 2 3 3 import ( 4 + "context" 4 5 "github.com/gorilla/sessions" 5 6 "net/http" 7 + 6 8 "os" 9 + "xcvr-backend/internal/atputils" 7 10 "xcvr-backend/internal/db" 11 + "xcvr-backend/internal/lex" 8 12 "xcvr-backend/internal/log" 9 13 "xcvr-backend/internal/oauth" 10 14 ) ··· 21 25 func New(db *db.Store, logger *log.Logger, oauthserv *oauth.Service) *Handler { 22 26 mux := http.NewServeMux() 23 27 sessionStore := sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY"))) 24 - xrpc := oauth.NewXRPCClient(db, logger) 28 + did, err := atputils.GetDidFromHandle(context.Background(), os.Getenv("MY_IDENTITY")) 29 + if err != nil { 30 + panic(err) 31 + } 32 + pdshost, err := atputils.GetPDSFromDid(context.Background(), did, http.DefaultClient) 33 + if err != nil { 34 + panic(err) 35 + } 36 + xrpc := oauth.NewXRPCClient(db, logger, pdshost, did) 37 + xrpc.CreateXCVRSignet(lex.SignetRecord{ 38 + ChannelURI: "beep.boop", 39 + LRCID: 11, 40 + Author: "sneep.snirp", 41 + }, context.Background()) 25 42 h := &Handler{db, sessionStore, mux, logger, oauthserv, xrpc} 26 43 // lrc handlers 27 44 mux.HandleFunc("GET /lrc/{user}/{rkey}/ws", h.acceptWebsocket) 28 - mux.HandleFunc("POST /lrc/channel", postChannel) 29 - mux.HandleFunc("POST /lrc/message", postMessage) 45 + mux.HandleFunc("POST /lrc/channel", h.postChannel) 46 + mux.HandleFunc("POST /lrc/message", h.postMessage) 30 47 // beep handlers 31 48 mux.HandleFunc("POST /xcvr/profile", h.postProfile) 32 49 mux.HandleFunc("POST /xcvr/beep", h.beep)
+4 -3
server/internal/handler/lrcHandlers.go
··· 1 1 package handler 2 + 2 3 import ( 4 + "fmt" 3 5 "net/http" 4 6 "xcvr-backend/internal/model" 5 - "fmt" 6 7 ) 7 8 8 9 func (h *Handler) acceptWebsocket(w http.ResponseWriter, r *http.Request) { ··· 19 20 f(w, r) 20 21 } 21 22 22 - func postChannel(w http.ResponseWriter, r *http.Request) { 23 + func (h *Handler) postChannel(w http.ResponseWriter, r *http.Request) { 23 24 24 25 } 25 26 26 - func postMessage(w http.ResponseWriter, r *http.Request) { 27 + func (h *Handler) postMessage(w http.ResponseWriter, r *http.Request) { 27 28 28 29 }
+1207
server/internal/lex/lexicons_cbor.go
··· 1 + // Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT. 2 + 3 + package lex 4 + 5 + import ( 6 + "fmt" 7 + "io" 8 + "math" 9 + "sort" 10 + 11 + util "github.com/bluesky-social/indigo/lex/util" 12 + cid "github.com/ipfs/go-cid" 13 + cbg "github.com/whyrusleeping/cbor-gen" 14 + xerrors "golang.org/x/xerrors" 15 + ) 16 + 17 + var _ = xerrors.Errorf 18 + var _ = cid.Undef 19 + var _ = math.E 20 + var _ = sort.Sort 21 + 22 + func (t *ProfileRecord) MarshalCBOR(w io.Writer) error { 23 + if t == nil { 24 + _, err := w.Write(cbg.CborNull) 25 + return err 26 + } 27 + 28 + cw := cbg.NewCborWriter(w) 29 + fieldCount := 6 30 + 31 + if t.DisplayName == nil { 32 + fieldCount-- 33 + } 34 + 35 + if t.DefaultNick == nil { 36 + fieldCount-- 37 + } 38 + 39 + if t.Status == nil { 40 + fieldCount-- 41 + } 42 + 43 + if t.Avatar == nil { 44 + fieldCount-- 45 + } 46 + 47 + if t.Color == nil { 48 + fieldCount-- 49 + } 50 + 51 + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { 52 + return err 53 + } 54 + 55 + // t.LexiconTypeID (string) (string) 56 + if len("$type") > 8192 { 57 + return xerrors.Errorf("Value in field \"$type\" was too long") 58 + } 59 + 60 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { 61 + return err 62 + } 63 + if _, err := cw.WriteString(string("$type")); err != nil { 64 + return err 65 + } 66 + 67 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("org.xcvr.actor.profile"))); err != nil { 68 + return err 69 + } 70 + if _, err := cw.WriteString(string("org.xcvr.actor.profile")); err != nil { 71 + return err 72 + } 73 + 74 + // t.Color (uint64) (uint64) 75 + if t.Color != nil { 76 + 77 + if len("color") > 8192 { 78 + return xerrors.Errorf("Value in field \"color\" was too long") 79 + } 80 + 81 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("color"))); err != nil { 82 + return err 83 + } 84 + if _, err := cw.WriteString(string("color")); err != nil { 85 + return err 86 + } 87 + 88 + if t.Color == nil { 89 + if _, err := cw.Write(cbg.CborNull); err != nil { 90 + return err 91 + } 92 + } else { 93 + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(*t.Color)); err != nil { 94 + return err 95 + } 96 + } 97 + 98 + } 99 + 100 + // t.Avatar (util.LexBlob) (struct) 101 + if t.Avatar != nil { 102 + 103 + if len("avatar") > 8192 { 104 + return xerrors.Errorf("Value in field \"avatar\" was too long") 105 + } 106 + 107 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("avatar"))); err != nil { 108 + return err 109 + } 110 + if _, err := cw.WriteString(string("avatar")); err != nil { 111 + return err 112 + } 113 + 114 + if err := t.Avatar.MarshalCBOR(cw); err != nil { 115 + return err 116 + } 117 + } 118 + 119 + // t.Status (string) (string) 120 + if t.Status != nil { 121 + 122 + if len("status") > 8192 { 123 + return xerrors.Errorf("Value in field \"status\" was too long") 124 + } 125 + 126 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("status"))); err != nil { 127 + return err 128 + } 129 + if _, err := cw.WriteString(string("status")); err != nil { 130 + return err 131 + } 132 + 133 + if t.Status == nil { 134 + if _, err := cw.Write(cbg.CborNull); err != nil { 135 + return err 136 + } 137 + } else { 138 + if len(*t.Status) > 8192 { 139 + return xerrors.Errorf("Value in field t.Status was too long") 140 + } 141 + 142 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Status))); err != nil { 143 + return err 144 + } 145 + if _, err := cw.WriteString(string(*t.Status)); err != nil { 146 + return err 147 + } 148 + } 149 + } 150 + 151 + // t.DefaultNick (string) (string) 152 + if t.DefaultNick != nil { 153 + 154 + if len("defaultNick") > 8192 { 155 + return xerrors.Errorf("Value in field \"defaultNick\" was too long") 156 + } 157 + 158 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("defaultNick"))); err != nil { 159 + return err 160 + } 161 + if _, err := cw.WriteString(string("defaultNick")); err != nil { 162 + return err 163 + } 164 + 165 + if t.DefaultNick == nil { 166 + if _, err := cw.Write(cbg.CborNull); err != nil { 167 + return err 168 + } 169 + } else { 170 + if len(*t.DefaultNick) > 8192 { 171 + return xerrors.Errorf("Value in field t.DefaultNick was too long") 172 + } 173 + 174 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.DefaultNick))); err != nil { 175 + return err 176 + } 177 + if _, err := cw.WriteString(string(*t.DefaultNick)); err != nil { 178 + return err 179 + } 180 + } 181 + } 182 + 183 + // t.DisplayName (string) (string) 184 + if t.DisplayName != nil { 185 + 186 + if len("displayName") > 8192 { 187 + return xerrors.Errorf("Value in field \"displayName\" was too long") 188 + } 189 + 190 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("displayName"))); err != nil { 191 + return err 192 + } 193 + if _, err := cw.WriteString(string("displayName")); err != nil { 194 + return err 195 + } 196 + 197 + if t.DisplayName == nil { 198 + if _, err := cw.Write(cbg.CborNull); err != nil { 199 + return err 200 + } 201 + } else { 202 + if len(*t.DisplayName) > 8192 { 203 + return xerrors.Errorf("Value in field t.DisplayName was too long") 204 + } 205 + 206 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.DisplayName))); err != nil { 207 + return err 208 + } 209 + if _, err := cw.WriteString(string(*t.DisplayName)); err != nil { 210 + return err 211 + } 212 + } 213 + } 214 + return nil 215 + } 216 + 217 + func (t *ProfileRecord) UnmarshalCBOR(r io.Reader) (err error) { 218 + *t = ProfileRecord{} 219 + 220 + cr := cbg.NewCborReader(r) 221 + 222 + maj, extra, err := cr.ReadHeader() 223 + if err != nil { 224 + return err 225 + } 226 + defer func() { 227 + if err == io.EOF { 228 + err = io.ErrUnexpectedEOF 229 + } 230 + }() 231 + 232 + if maj != cbg.MajMap { 233 + return fmt.Errorf("cbor input should be of type map") 234 + } 235 + 236 + if extra > cbg.MaxLength { 237 + return fmt.Errorf("ProfileRecord: map struct too large (%d)", extra) 238 + } 239 + 240 + n := extra 241 + 242 + nameBuf := make([]byte, 11) 243 + for i := uint64(0); i < n; i++ { 244 + nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192) 245 + if err != nil { 246 + return err 247 + } 248 + 249 + if !ok { 250 + // Field doesn't exist on this type, so ignore it 251 + if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { 252 + return err 253 + } 254 + continue 255 + } 256 + 257 + switch string(nameBuf[:nameLen]) { 258 + // t.LexiconTypeID (string) (string) 259 + case "$type": 260 + 261 + { 262 + sval, err := cbg.ReadStringWithMax(cr, 8192) 263 + if err != nil { 264 + return err 265 + } 266 + 267 + t.LexiconTypeID = string(sval) 268 + } 269 + // t.Color (uint64) (uint64) 270 + case "color": 271 + 272 + { 273 + 274 + b, err := cr.ReadByte() 275 + if err != nil { 276 + return err 277 + } 278 + if b != cbg.CborNull[0] { 279 + if err := cr.UnreadByte(); err != nil { 280 + return err 281 + } 282 + maj, extra, err = cr.ReadHeader() 283 + if err != nil { 284 + return err 285 + } 286 + if maj != cbg.MajUnsignedInt { 287 + return fmt.Errorf("wrong type for uint64 field") 288 + } 289 + typed := uint64(extra) 290 + t.Color = &typed 291 + } 292 + 293 + } 294 + // t.Avatar (util.LexBlob) (struct) 295 + case "avatar": 296 + 297 + { 298 + 299 + b, err := cr.ReadByte() 300 + if err != nil { 301 + return err 302 + } 303 + if b != cbg.CborNull[0] { 304 + if err := cr.UnreadByte(); err != nil { 305 + return err 306 + } 307 + t.Avatar = new(util.LexBlob) 308 + if err := t.Avatar.UnmarshalCBOR(cr); err != nil { 309 + return xerrors.Errorf("unmarshaling t.Avatar pointer: %w", err) 310 + } 311 + } 312 + 313 + } 314 + // t.Status (string) (string) 315 + case "status": 316 + 317 + { 318 + b, err := cr.ReadByte() 319 + if err != nil { 320 + return err 321 + } 322 + if b != cbg.CborNull[0] { 323 + if err := cr.UnreadByte(); err != nil { 324 + return err 325 + } 326 + 327 + sval, err := cbg.ReadStringWithMax(cr, 8192) 328 + if err != nil { 329 + return err 330 + } 331 + 332 + t.Status = (*string)(&sval) 333 + } 334 + } 335 + // t.DefaultNick (string) (string) 336 + case "defaultNick": 337 + 338 + { 339 + b, err := cr.ReadByte() 340 + if err != nil { 341 + return err 342 + } 343 + if b != cbg.CborNull[0] { 344 + if err := cr.UnreadByte(); err != nil { 345 + return err 346 + } 347 + 348 + sval, err := cbg.ReadStringWithMax(cr, 8192) 349 + if err != nil { 350 + return err 351 + } 352 + 353 + t.DefaultNick = (*string)(&sval) 354 + } 355 + } 356 + // t.DisplayName (string) (string) 357 + case "displayName": 358 + 359 + { 360 + b, err := cr.ReadByte() 361 + if err != nil { 362 + return err 363 + } 364 + if b != cbg.CborNull[0] { 365 + if err := cr.UnreadByte(); err != nil { 366 + return err 367 + } 368 + 369 + sval, err := cbg.ReadStringWithMax(cr, 8192) 370 + if err != nil { 371 + return err 372 + } 373 + 374 + t.DisplayName = (*string)(&sval) 375 + } 376 + } 377 + 378 + default: 379 + // Field doesn't exist on this type, so ignore it 380 + if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { 381 + return err 382 + } 383 + } 384 + } 385 + 386 + return nil 387 + } 388 + func (t *ChannelRecord) MarshalCBOR(w io.Writer) error { 389 + if t == nil { 390 + _, err := w.Write(cbg.CborNull) 391 + return err 392 + } 393 + 394 + cw := cbg.NewCborWriter(w) 395 + fieldCount := 5 396 + 397 + if t.Topic == nil { 398 + fieldCount-- 399 + } 400 + 401 + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { 402 + return err 403 + } 404 + 405 + // t.Host (string) (string) 406 + if len("host") > 8192 { 407 + return xerrors.Errorf("Value in field \"host\" was too long") 408 + } 409 + 410 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("host"))); err != nil { 411 + return err 412 + } 413 + if _, err := cw.WriteString(string("host")); err != nil { 414 + return err 415 + } 416 + 417 + if len(t.Host) > 8192 { 418 + return xerrors.Errorf("Value in field t.Host was too long") 419 + } 420 + 421 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Host))); err != nil { 422 + return err 423 + } 424 + if _, err := cw.WriteString(string(t.Host)); err != nil { 425 + return err 426 + } 427 + 428 + // t.LexiconTypeID (string) (string) 429 + if len("$type") > 8192 { 430 + return xerrors.Errorf("Value in field \"$type\" was too long") 431 + } 432 + 433 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { 434 + return err 435 + } 436 + if _, err := cw.WriteString(string("$type")); err != nil { 437 + return err 438 + } 439 + 440 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("org.xcvr.feed.channel"))); err != nil { 441 + return err 442 + } 443 + if _, err := cw.WriteString(string("org.xcvr.feed.channel")); err != nil { 444 + return err 445 + } 446 + 447 + // t.Title (string) (string) 448 + if len("title") > 8192 { 449 + return xerrors.Errorf("Value in field \"title\" was too long") 450 + } 451 + 452 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("title"))); err != nil { 453 + return err 454 + } 455 + if _, err := cw.WriteString(string("title")); err != nil { 456 + return err 457 + } 458 + 459 + if len(t.Title) > 8192 { 460 + return xerrors.Errorf("Value in field t.Title was too long") 461 + } 462 + 463 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Title))); err != nil { 464 + return err 465 + } 466 + if _, err := cw.WriteString(string(t.Title)); err != nil { 467 + return err 468 + } 469 + 470 + // t.Topic (string) (string) 471 + if t.Topic != nil { 472 + 473 + if len("topic") > 8192 { 474 + return xerrors.Errorf("Value in field \"topic\" was too long") 475 + } 476 + 477 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("topic"))); err != nil { 478 + return err 479 + } 480 + if _, err := cw.WriteString(string("topic")); err != nil { 481 + return err 482 + } 483 + 484 + if t.Topic == nil { 485 + if _, err := cw.Write(cbg.CborNull); err != nil { 486 + return err 487 + } 488 + } else { 489 + if len(*t.Topic) > 8192 { 490 + return xerrors.Errorf("Value in field t.Topic was too long") 491 + } 492 + 493 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Topic))); err != nil { 494 + return err 495 + } 496 + if _, err := cw.WriteString(string(*t.Topic)); err != nil { 497 + return err 498 + } 499 + } 500 + } 501 + 502 + // t.CreatedAt (string) (string) 503 + if len("createdAt") > 8192 { 504 + return xerrors.Errorf("Value in field \"createdAt\" was too long") 505 + } 506 + 507 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("createdAt"))); err != nil { 508 + return err 509 + } 510 + if _, err := cw.WriteString(string("createdAt")); err != nil { 511 + return err 512 + } 513 + 514 + if len(t.CreatedAt) > 8192 { 515 + return xerrors.Errorf("Value in field t.CreatedAt was too long") 516 + } 517 + 518 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.CreatedAt))); err != nil { 519 + return err 520 + } 521 + if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 522 + return err 523 + } 524 + return nil 525 + } 526 + 527 + func (t *ChannelRecord) UnmarshalCBOR(r io.Reader) (err error) { 528 + *t = ChannelRecord{} 529 + 530 + cr := cbg.NewCborReader(r) 531 + 532 + maj, extra, err := cr.ReadHeader() 533 + if err != nil { 534 + return err 535 + } 536 + defer func() { 537 + if err == io.EOF { 538 + err = io.ErrUnexpectedEOF 539 + } 540 + }() 541 + 542 + if maj != cbg.MajMap { 543 + return fmt.Errorf("cbor input should be of type map") 544 + } 545 + 546 + if extra > cbg.MaxLength { 547 + return fmt.Errorf("ChannelRecord: map struct too large (%d)", extra) 548 + } 549 + 550 + n := extra 551 + 552 + nameBuf := make([]byte, 9) 553 + for i := uint64(0); i < n; i++ { 554 + nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192) 555 + if err != nil { 556 + return err 557 + } 558 + 559 + if !ok { 560 + // Field doesn't exist on this type, so ignore it 561 + if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { 562 + return err 563 + } 564 + continue 565 + } 566 + 567 + switch string(nameBuf[:nameLen]) { 568 + // t.Host (string) (string) 569 + case "host": 570 + 571 + { 572 + sval, err := cbg.ReadStringWithMax(cr, 8192) 573 + if err != nil { 574 + return err 575 + } 576 + 577 + t.Host = string(sval) 578 + } 579 + // t.LexiconTypeID (string) (string) 580 + case "$type": 581 + 582 + { 583 + sval, err := cbg.ReadStringWithMax(cr, 8192) 584 + if err != nil { 585 + return err 586 + } 587 + 588 + t.LexiconTypeID = string(sval) 589 + } 590 + // t.Title (string) (string) 591 + case "title": 592 + 593 + { 594 + sval, err := cbg.ReadStringWithMax(cr, 8192) 595 + if err != nil { 596 + return err 597 + } 598 + 599 + t.Title = string(sval) 600 + } 601 + // t.Topic (string) (string) 602 + case "topic": 603 + 604 + { 605 + b, err := cr.ReadByte() 606 + if err != nil { 607 + return err 608 + } 609 + if b != cbg.CborNull[0] { 610 + if err := cr.UnreadByte(); err != nil { 611 + return err 612 + } 613 + 614 + sval, err := cbg.ReadStringWithMax(cr, 8192) 615 + if err != nil { 616 + return err 617 + } 618 + 619 + t.Topic = (*string)(&sval) 620 + } 621 + } 622 + // t.CreatedAt (string) (string) 623 + case "createdAt": 624 + 625 + { 626 + sval, err := cbg.ReadStringWithMax(cr, 8192) 627 + if err != nil { 628 + return err 629 + } 630 + 631 + t.CreatedAt = string(sval) 632 + } 633 + 634 + default: 635 + // Field doesn't exist on this type, so ignore it 636 + if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { 637 + return err 638 + } 639 + } 640 + } 641 + 642 + return nil 643 + } 644 + func (t *MessageRecord) MarshalCBOR(w io.Writer) error { 645 + if t == nil { 646 + _, err := w.Write(cbg.CborNull) 647 + return err 648 + } 649 + 650 + cw := cbg.NewCborWriter(w) 651 + fieldCount := 6 652 + 653 + if t.Nick == nil { 654 + fieldCount-- 655 + } 656 + 657 + if t.Color == nil { 658 + fieldCount-- 659 + } 660 + 661 + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { 662 + return err 663 + } 664 + 665 + // t.Body (string) (string) 666 + if len("body") > 8192 { 667 + return xerrors.Errorf("Value in field \"body\" was too long") 668 + } 669 + 670 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("body"))); err != nil { 671 + return err 672 + } 673 + if _, err := cw.WriteString(string("body")); err != nil { 674 + return err 675 + } 676 + 677 + if len(t.Body) > 8192 { 678 + return xerrors.Errorf("Value in field t.Body was too long") 679 + } 680 + 681 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Body))); err != nil { 682 + return err 683 + } 684 + if _, err := cw.WriteString(string(t.Body)); err != nil { 685 + return err 686 + } 687 + 688 + // t.Nick (string) (string) 689 + if t.Nick != nil { 690 + 691 + if len("nick") > 8192 { 692 + return xerrors.Errorf("Value in field \"nick\" was too long") 693 + } 694 + 695 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("nick"))); err != nil { 696 + return err 697 + } 698 + if _, err := cw.WriteString(string("nick")); err != nil { 699 + return err 700 + } 701 + 702 + if t.Nick == nil { 703 + if _, err := cw.Write(cbg.CborNull); err != nil { 704 + return err 705 + } 706 + } else { 707 + if len(*t.Nick) > 8192 { 708 + return xerrors.Errorf("Value in field t.Nick was too long") 709 + } 710 + 711 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Nick))); err != nil { 712 + return err 713 + } 714 + if _, err := cw.WriteString(string(*t.Nick)); err != nil { 715 + return err 716 + } 717 + } 718 + } 719 + 720 + // t.LexiconTypeID (string) (string) 721 + if len("$type") > 8192 { 722 + return xerrors.Errorf("Value in field \"$type\" was too long") 723 + } 724 + 725 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { 726 + return err 727 + } 728 + if _, err := cw.WriteString(string("$type")); err != nil { 729 + return err 730 + } 731 + 732 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("org.xcvr.lrc.message"))); err != nil { 733 + return err 734 + } 735 + if _, err := cw.WriteString(string("org.xcvr.lrc.message")); err != nil { 736 + return err 737 + } 738 + 739 + // t.Color (uint64) (uint64) 740 + if t.Color != nil { 741 + 742 + if len("color") > 8192 { 743 + return xerrors.Errorf("Value in field \"color\" was too long") 744 + } 745 + 746 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("color"))); err != nil { 747 + return err 748 + } 749 + if _, err := cw.WriteString(string("color")); err != nil { 750 + return err 751 + } 752 + 753 + if t.Color == nil { 754 + if _, err := cw.Write(cbg.CborNull); err != nil { 755 + return err 756 + } 757 + } else { 758 + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(*t.Color)); err != nil { 759 + return err 760 + } 761 + } 762 + 763 + } 764 + 765 + // t.PostedAt (string) (string) 766 + if len("postedAt") > 8192 { 767 + return xerrors.Errorf("Value in field \"postedAt\" was too long") 768 + } 769 + 770 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("postedAt"))); err != nil { 771 + return err 772 + } 773 + if _, err := cw.WriteString(string("postedAt")); err != nil { 774 + return err 775 + } 776 + 777 + if len(t.PostedAt) > 8192 { 778 + return xerrors.Errorf("Value in field t.PostedAt was too long") 779 + } 780 + 781 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.PostedAt))); err != nil { 782 + return err 783 + } 784 + if _, err := cw.WriteString(string(t.PostedAt)); err != nil { 785 + return err 786 + } 787 + 788 + // t.SignetURI (string) (string) 789 + if len("signetURI") > 8192 { 790 + return xerrors.Errorf("Value in field \"signetURI\" was too long") 791 + } 792 + 793 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("signetURI"))); err != nil { 794 + return err 795 + } 796 + if _, err := cw.WriteString(string("signetURI")); err != nil { 797 + return err 798 + } 799 + 800 + if len(t.SignetURI) > 8192 { 801 + return xerrors.Errorf("Value in field t.SignetURI was too long") 802 + } 803 + 804 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.SignetURI))); err != nil { 805 + return err 806 + } 807 + if _, err := cw.WriteString(string(t.SignetURI)); err != nil { 808 + return err 809 + } 810 + return nil 811 + } 812 + 813 + func (t *MessageRecord) UnmarshalCBOR(r io.Reader) (err error) { 814 + *t = MessageRecord{} 815 + 816 + cr := cbg.NewCborReader(r) 817 + 818 + maj, extra, err := cr.ReadHeader() 819 + if err != nil { 820 + return err 821 + } 822 + defer func() { 823 + if err == io.EOF { 824 + err = io.ErrUnexpectedEOF 825 + } 826 + }() 827 + 828 + if maj != cbg.MajMap { 829 + return fmt.Errorf("cbor input should be of type map") 830 + } 831 + 832 + if extra > cbg.MaxLength { 833 + return fmt.Errorf("MessageRecord: map struct too large (%d)", extra) 834 + } 835 + 836 + n := extra 837 + 838 + nameBuf := make([]byte, 9) 839 + for i := uint64(0); i < n; i++ { 840 + nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192) 841 + if err != nil { 842 + return err 843 + } 844 + 845 + if !ok { 846 + // Field doesn't exist on this type, so ignore it 847 + if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { 848 + return err 849 + } 850 + continue 851 + } 852 + 853 + switch string(nameBuf[:nameLen]) { 854 + // t.Body (string) (string) 855 + case "body": 856 + 857 + { 858 + sval, err := cbg.ReadStringWithMax(cr, 8192) 859 + if err != nil { 860 + return err 861 + } 862 + 863 + t.Body = string(sval) 864 + } 865 + // t.Nick (string) (string) 866 + case "nick": 867 + 868 + { 869 + b, err := cr.ReadByte() 870 + if err != nil { 871 + return err 872 + } 873 + if b != cbg.CborNull[0] { 874 + if err := cr.UnreadByte(); err != nil { 875 + return err 876 + } 877 + 878 + sval, err := cbg.ReadStringWithMax(cr, 8192) 879 + if err != nil { 880 + return err 881 + } 882 + 883 + t.Nick = (*string)(&sval) 884 + } 885 + } 886 + // t.LexiconTypeID (string) (string) 887 + case "$type": 888 + 889 + { 890 + sval, err := cbg.ReadStringWithMax(cr, 8192) 891 + if err != nil { 892 + return err 893 + } 894 + 895 + t.LexiconTypeID = string(sval) 896 + } 897 + // t.Color (uint64) (uint64) 898 + case "color": 899 + 900 + { 901 + 902 + b, err := cr.ReadByte() 903 + if err != nil { 904 + return err 905 + } 906 + if b != cbg.CborNull[0] { 907 + if err := cr.UnreadByte(); err != nil { 908 + return err 909 + } 910 + maj, extra, err = cr.ReadHeader() 911 + if err != nil { 912 + return err 913 + } 914 + if maj != cbg.MajUnsignedInt { 915 + return fmt.Errorf("wrong type for uint64 field") 916 + } 917 + typed := uint64(extra) 918 + t.Color = &typed 919 + } 920 + 921 + } 922 + // t.PostedAt (string) (string) 923 + case "postedAt": 924 + 925 + { 926 + sval, err := cbg.ReadStringWithMax(cr, 8192) 927 + if err != nil { 928 + return err 929 + } 930 + 931 + t.PostedAt = string(sval) 932 + } 933 + // t.SignetURI (string) (string) 934 + case "signetURI": 935 + 936 + { 937 + sval, err := cbg.ReadStringWithMax(cr, 8192) 938 + if err != nil { 939 + return err 940 + } 941 + 942 + t.SignetURI = string(sval) 943 + } 944 + 945 + default: 946 + // Field doesn't exist on this type, so ignore it 947 + if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { 948 + return err 949 + } 950 + } 951 + } 952 + 953 + return nil 954 + } 955 + func (t *SignetRecord) MarshalCBOR(w io.Writer) error { 956 + if t == nil { 957 + _, err := w.Write(cbg.CborNull) 958 + return err 959 + } 960 + 961 + cw := cbg.NewCborWriter(w) 962 + fieldCount := 5 963 + 964 + if t.StartedAt == nil { 965 + fieldCount-- 966 + } 967 + 968 + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { 969 + return err 970 + } 971 + 972 + // t.Author (string) (string) 973 + if len("nick") > 8192 { 974 + return xerrors.Errorf("Value in field \"nick\" was too long") 975 + } 976 + 977 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("nick"))); err != nil { 978 + return err 979 + } 980 + if _, err := cw.WriteString(string("nick")); err != nil { 981 + return err 982 + } 983 + 984 + if len(t.Author) > 8192 { 985 + return xerrors.Errorf("Value in field t.Author was too long") 986 + } 987 + 988 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Author))); err != nil { 989 + return err 990 + } 991 + if _, err := cw.WriteString(string(t.Author)); err != nil { 992 + return err 993 + } 994 + 995 + // t.LexiconTypeID (string) (string) 996 + if len("$type") > 8192 { 997 + return xerrors.Errorf("Value in field \"$type\" was too long") 998 + } 999 + 1000 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { 1001 + return err 1002 + } 1003 + if _, err := cw.WriteString(string("$type")); err != nil { 1004 + return err 1005 + } 1006 + 1007 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("org.xcvr.lrc.signet"))); err != nil { 1008 + return err 1009 + } 1010 + if _, err := cw.WriteString(string("org.xcvr.lrc.signet")); err != nil { 1011 + return err 1012 + } 1013 + 1014 + // t.LRCID (uint64) (uint64) 1015 + if len("lrcID") > 8192 { 1016 + return xerrors.Errorf("Value in field \"lrcID\" was too long") 1017 + } 1018 + 1019 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("lrcID"))); err != nil { 1020 + return err 1021 + } 1022 + if _, err := cw.WriteString(string("lrcID")); err != nil { 1023 + return err 1024 + } 1025 + 1026 + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.LRCID)); err != nil { 1027 + return err 1028 + } 1029 + 1030 + // t.StartedAt (string) (string) 1031 + if t.StartedAt != nil { 1032 + 1033 + if len("startedAt") > 8192 { 1034 + return xerrors.Errorf("Value in field \"startedAt\" was too long") 1035 + } 1036 + 1037 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("startedAt"))); err != nil { 1038 + return err 1039 + } 1040 + if _, err := cw.WriteString(string("startedAt")); err != nil { 1041 + return err 1042 + } 1043 + 1044 + if t.StartedAt == nil { 1045 + if _, err := cw.Write(cbg.CborNull); err != nil { 1046 + return err 1047 + } 1048 + } else { 1049 + if len(*t.StartedAt) > 8192 { 1050 + return xerrors.Errorf("Value in field t.StartedAt was too long") 1051 + } 1052 + 1053 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.StartedAt))); err != nil { 1054 + return err 1055 + } 1056 + if _, err := cw.WriteString(string(*t.StartedAt)); err != nil { 1057 + return err 1058 + } 1059 + } 1060 + } 1061 + 1062 + // t.ChannelURI (string) (string) 1063 + if len("channelURI") > 8192 { 1064 + return xerrors.Errorf("Value in field \"channelURI\" was too long") 1065 + } 1066 + 1067 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("channelURI"))); err != nil { 1068 + return err 1069 + } 1070 + if _, err := cw.WriteString(string("channelURI")); err != nil { 1071 + return err 1072 + } 1073 + 1074 + if len(t.ChannelURI) > 8192 { 1075 + return xerrors.Errorf("Value in field t.ChannelURI was too long") 1076 + } 1077 + 1078 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.ChannelURI))); err != nil { 1079 + return err 1080 + } 1081 + if _, err := cw.WriteString(string(t.ChannelURI)); err != nil { 1082 + return err 1083 + } 1084 + return nil 1085 + } 1086 + 1087 + func (t *SignetRecord) UnmarshalCBOR(r io.Reader) (err error) { 1088 + *t = SignetRecord{} 1089 + 1090 + cr := cbg.NewCborReader(r) 1091 + 1092 + maj, extra, err := cr.ReadHeader() 1093 + if err != nil { 1094 + return err 1095 + } 1096 + defer func() { 1097 + if err == io.EOF { 1098 + err = io.ErrUnexpectedEOF 1099 + } 1100 + }() 1101 + 1102 + if maj != cbg.MajMap { 1103 + return fmt.Errorf("cbor input should be of type map") 1104 + } 1105 + 1106 + if extra > cbg.MaxLength { 1107 + return fmt.Errorf("SignetRecord: map struct too large (%d)", extra) 1108 + } 1109 + 1110 + n := extra 1111 + 1112 + nameBuf := make([]byte, 10) 1113 + for i := uint64(0); i < n; i++ { 1114 + nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 8192) 1115 + if err != nil { 1116 + return err 1117 + } 1118 + 1119 + if !ok { 1120 + // Field doesn't exist on this type, so ignore it 1121 + if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { 1122 + return err 1123 + } 1124 + continue 1125 + } 1126 + 1127 + switch string(nameBuf[:nameLen]) { 1128 + // t.Author (string) (string) 1129 + case "nick": 1130 + 1131 + { 1132 + sval, err := cbg.ReadStringWithMax(cr, 8192) 1133 + if err != nil { 1134 + return err 1135 + } 1136 + 1137 + t.Author = string(sval) 1138 + } 1139 + // t.LexiconTypeID (string) (string) 1140 + case "$type": 1141 + 1142 + { 1143 + sval, err := cbg.ReadStringWithMax(cr, 8192) 1144 + if err != nil { 1145 + return err 1146 + } 1147 + 1148 + t.LexiconTypeID = string(sval) 1149 + } 1150 + // t.LRCID (uint64) (uint64) 1151 + case "lrcID": 1152 + 1153 + { 1154 + 1155 + maj, extra, err = cr.ReadHeader() 1156 + if err != nil { 1157 + return err 1158 + } 1159 + if maj != cbg.MajUnsignedInt { 1160 + return fmt.Errorf("wrong type for uint64 field") 1161 + } 1162 + t.LRCID = uint64(extra) 1163 + 1164 + } 1165 + // t.StartedAt (string) (string) 1166 + case "startedAt": 1167 + 1168 + { 1169 + b, err := cr.ReadByte() 1170 + if err != nil { 1171 + return err 1172 + } 1173 + if b != cbg.CborNull[0] { 1174 + if err := cr.UnreadByte(); err != nil { 1175 + return err 1176 + } 1177 + 1178 + sval, err := cbg.ReadStringWithMax(cr, 8192) 1179 + if err != nil { 1180 + return err 1181 + } 1182 + 1183 + t.StartedAt = (*string)(&sval) 1184 + } 1185 + } 1186 + // t.ChannelURI (string) (string) 1187 + case "channelURI": 1188 + 1189 + { 1190 + sval, err := cbg.ReadStringWithMax(cr, 8192) 1191 + if err != nil { 1192 + return err 1193 + } 1194 + 1195 + t.ChannelURI = string(sval) 1196 + } 1197 + 1198 + default: 1199 + // Field doesn't exist on this type, so ignore it 1200 + if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { 1201 + return err 1202 + } 1203 + } 1204 + } 1205 + 1206 + return nil 1207 + }
+29
server/internal/lex/types.go
··· 3 3 import ( 4 4 "github.com/bluesky-social/indigo/lex/util" 5 5 ) 6 + 6 7 func init() { 7 8 util.RegisterType("org.xcvr.actor.profile", &ProfileRecord{}) 9 + util.RegisterType("org.xcvr.feed.channel", &ChannelRecord{}) 10 + util.RegisterType("org.xcvr.lrc.message", &MessageRecord{}) 11 + util.RegisterType("org.xcvr.lrc.signet", &SignetRecord{}) 8 12 } 9 13 10 14 type ProfileRecord struct { ··· 15 19 Avatar *util.LexBlob `json:"avatar,omitempty" cborgen:"avatar,omitempty"` 16 20 Color *uint64 `json:"color,omitempty" cborgen:"color,omitempty"` 17 21 } 22 + 23 + type ChannelRecord struct { 24 + LexiconTypeID string `json:"$type,const=org.xcvr.feed.channel" cborgen:"$type,const=org.xcvr.feed.channel"` 25 + Title string `json:"title" cborgen:"title"` 26 + Topic *string `json:"topic,omitempty" cborgen:"topic,omitempty"` 27 + CreatedAt string `json:"createdAt" cborgen:"createdAt"` 28 + Host string `json:"host" cborgen:"host"` 29 + } 30 + 31 + type MessageRecord struct { 32 + LexiconTypeID string `json:"$type,const=org.xcvr.lrc.message" cborgen:"$type,const=org.xcvr.lrc.message"` 33 + SignetURI string `json:"signetURI" cborgen:"signetURI"` 34 + Body string `json:"body" cborgen:"body"` 35 + Nick *string `json:"nick,omitempty" cborgen:"nick,omitempty"` 36 + Color *uint64 `json:"color,omitempty" cborgen:"color,omitempty"` 37 + PostedAt string `json:"postedAt" cborgen:"postedAt"` 38 + } 39 + 40 + type SignetRecord struct { 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:"nick"` 45 + StartedAt *string `json:"startedAt,omitempty" cborgen:"startedAt,omitempty"` 46 + }
+1 -48
server/internal/oauth/service.go
··· 5 5 "encoding/json" 6 6 "errors" 7 7 "fmt" 8 - "io" 9 8 "net/http" 10 - "strings" 11 9 "time" 12 10 "xcvr-backend/internal/atputils" 13 11 "xcvr-backend/internal/types" ··· 111 109 } 112 110 113 111 func (s *Service) resolveService(ctx context.Context, did string) (string, error) { 114 - type Identity struct { 115 - Service []struct { 116 - ID string `json:"id"` 117 - Type string `json:"type"` 118 - ServiceEndpoint string `json:"serviceEndpoint"` 119 - } `json:"service"` 120 - } 121 - var url string 122 - if strings.HasPrefix(did, "did:plc:") { 123 - url = fmt.Sprintf("https://plc.directory/%s", did) 124 - } else if strings.HasPrefix(did, "did:web:") { 125 - url = fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")) 126 - } else { 127 - return "", errors.New("did type not supported") 128 - } 129 - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) 130 - if err != nil { 131 - return "", errors.New("error crafting request:" + err.Error()) 132 - } 133 - resp, err := s.http.Do(req) 134 - if err != nil { 135 - return "", errors.New("error evaluating request:" + err.Error()) 136 - } 137 - defer resp.Body.Close() 138 - if resp.StatusCode != 200 { 139 - return "", errors.New("could not resolve did to service") 140 - } 141 - b, err := io.ReadAll(resp.Body) 142 - if err != nil { 143 - return "", errors.New("error reading response body:" + err.Error()) 144 - } 145 - var identity Identity 146 - err = json.Unmarshal(b, &identity) 147 - if err != nil { 148 - return "", errors.New("error unmarshaling to identity:" + err.Error()) 149 - } 150 - var service *string 151 - for _, svc := range identity.Service { 152 - if svc.ID == "#atproto_pds" { 153 - service = &svc.ServiceEndpoint 154 - } 155 - } 156 - if service == nil { 157 - return "", errors.New("could not find atproto_pds service in resolved did's services") 158 - } 159 - return *service, nil 112 + return atputils.GetPDSFromDid(ctx, did, s.http) 160 113 } 161 114 162 115 // func (s *Service) resolveHandle(handle string) (string, error) {
+121 -5
server/internal/oauth/xrpcclient.go
··· 3 3 import ( 4 4 "context" 5 5 "errors" 6 + "fmt" 6 7 "github.com/bluesky-social/indigo/api/atproto" 7 8 "github.com/bluesky-social/indigo/api/bsky" 8 9 "github.com/bluesky-social/indigo/atproto/client" ··· 10 11 "github.com/bluesky-social/indigo/lex/util" 11 12 "github.com/haileyok/atproto-oauth-golang" 12 13 "github.com/haileyok/atproto-oauth-golang/helpers" 14 + "os" 13 15 14 16 "xcvr-backend/internal/db" 15 17 "xcvr-backend/internal/lex" ··· 18 20 ) 19 21 20 22 type Client struct { 21 - xrpccli *oauth.XrpcClient 23 + xrpccli *oauth.XrpcClient 24 + xcvrcli *client.APIClient 25 + accessjwt *string 26 + refreshjwt *string 27 + did *string 28 + logger *log.Logger 22 29 } 23 30 24 - func NewXRPCClient(s *db.Store, l *log.Logger) *Client { 31 + func NewXRPCClient(s *db.Store, l *log.Logger, host string, did string) *Client { 25 32 return &Client{ 26 33 xrpccli: &oauth.XrpcClient{ 27 34 OnDpopPdsNonceChanged: func(did, newNonce string) { ··· 30 37 l.Deprintln(err.Error()) 31 38 } 32 39 }, 33 - }, 40 + }, xcvrcli: client.NewAPIClient(host), did: &did, 34 41 } 35 42 } 36 43 ··· 76 83 if err != nil { 77 84 return errors.New("failed to get oauthsessionauthargs while making post: " + err.Error()) 78 85 } 86 + getOut, err := getProfileRecord(authargs.PdsUrl, authargs.Did, ctx) 87 + if err != nil { 88 + return errors.New("failed to getProfileRecord while creating XCVR profile: " + err.Error()) 89 + } 90 + if getOut.Cid != nil { 91 + return errors.New("there already is a profileRecord, I don't want to overwrite it") 92 + } 79 93 rkey := "self" 80 94 input := atproto.RepoCreateRecord_Input{ 81 95 Collection: "org.xcvr.actor.profile", ··· 91 105 return nil 92 106 } 93 107 108 + func (c *Client) CreateXCVRChannel(channel lex.ChannelRecord, s *types.Session, ctx context.Context) error { 109 + authargs, err := getOauthSessionAuthArgs(s) 110 + if err != nil { 111 + return errors.New("yikers! couldn't createXCVRChannel: " + err.Error()) 112 + } 113 + input := atproto.RepoCreateRecord_Input{ 114 + Collection: "org.xcvr.feed.channel", 115 + Repo: authargs.Did, 116 + Record: &util.LexiconTypeDecoder{Val: &channel}, 117 + } 118 + var out atproto.RepoCreateRecord_Output 119 + err = c.xrpccli.Do(ctx, authargs, "POST", "application/json", "com.atproto.repo.createRecord", nil, input, &out) 120 + if err != nil { 121 + return errors.New("that's not good! failed to create a XCVRChannel: " + err.Error()) 122 + } 123 + return nil 124 + } 125 + 126 + func (c *Client) CreateXCVRMessage(message lex.MessageRecord, s *types.Session, ctx context.Context) error { 127 + authargs, err := getOauthSessionAuthArgs(s) 128 + if err != nil { 129 + return errors.New("uh oh... I couldn't make a XCVRMessage: " + err.Error()) 130 + } 131 + input := atproto.RepoCreateRecord_Input{ 132 + Collection: "org.xcvr.lrc.message", 133 + Repo: authargs.Did, 134 + Record: &util.LexiconTypeDecoder{Val: &message}, 135 + } 136 + var out atproto.RepoCreateRecord_Output 137 + err = c.xrpccli.Do(ctx, authargs, "POST", "application/json", "com.atproto.repo.createRecord", nil, input, &out) 138 + if err != nil { 139 + return errors.New("i've got a bad feeling aobut this... failed to create XCVRMessage: " + err.Error()) 140 + } 141 + return nil 142 + } 143 + 144 + func (c *Client) CreateSession(ctx context.Context) error { 145 + c.logger.Deprintln("creating session...") 146 + secret := os.Getenv("MY_SECRET") 147 + identity := os.Getenv("MY_IDENTITY") 148 + params := map[string]any{ 149 + "identifier": &identity, 150 + "password": &secret, 151 + } 152 + var out atproto.ServerCreateSession_Output 153 + err := c.xcvrcli.LexDo(ctx, "POST", "application/json", "com.atproto.server.createSession", params, nil, out) 154 + if err != nil { 155 + return errors.New("I couldn't create a session: " + err.Error()) 156 + } 157 + c.accessjwt = &out.AccessJwt 158 + c.refreshjwt = &out.RefreshJwt 159 + c.logger.Deprintln("created session!") 160 + return nil 161 + } 162 + 163 + func (c *Client) RefreshSession(ctx context.Context) error { 164 + c.logger.Deprintln("refreshing session") 165 + c.xcvrcli.Headers.Set("Authorization", fmt.Sprintf("Bearer %s", *c.refreshjwt)) 166 + var out atproto.ServerRefreshSession_Output 167 + err := c.xcvrcli.LexDo(ctx, "POST", "application/json", "com.atproto.server.refreshSession", nil, nil, out) 168 + if err != nil { 169 + c.logger.Println("FAILED TO REFRESH RESSION") 170 + return errors.New("failed to refresh session! " + err.Error()) 171 + } 172 + c.accessjwt = &out.AccessJwt 173 + c.refreshjwt = &out.RefreshJwt 174 + c.logger.Deprintln("refreshed session!") 175 + return nil 176 + } 177 + 178 + func (c *Client) CreateXCVRSignet(signet lex.SignetRecord, ctx context.Context) error { 179 + if c.accessjwt == nil { 180 + return errors.New("must create a session first") 181 + } 182 + c.xcvrcli.Headers.Set("Authorization", fmt.Sprintf("Bearer %s", *c.accessjwt)) 183 + input := atproto.RepoCreateRecord_Input{ 184 + Collection: "org.xcvr.lrc.signet", 185 + Repo: *c.did, 186 + Record: &util.LexiconTypeDecoder{Val: &signet}, 187 + } 188 + var out atproto.RepoCreateRecord_Output 189 + err := c.xcvrcli.LexDo(ctx, "POST", "application/json", "com.atproto.repo.createRecord", nil, input, out) 190 + if err != nil { 191 + err1 := err.Error() 192 + err = c.RefreshSession(ctx) 193 + if err != nil { 194 + return errors.New("failed to refresh session while creating signet! first " + err1 + " then " + err.Error()) 195 + } 196 + c.xcvrcli.Headers.Set("Authorization", fmt.Sprintf("Bearer %s", *c.accessjwt)) 197 + out = atproto.RepoCreateRecord_Output{} 198 + err = c.xcvrcli.LexDo(ctx, "POST", "application/json", "com.atproto.repo.createRecord", nil, input, out) 199 + if err != nil { 200 + return errors.New("not good, failed to create signet after failing then refreshing session! first " + err1 + " then " + err.Error()) 201 + } 202 + } 203 + return nil 204 + } 205 + 94 206 func (c *Client) UpdateXCVRProfile(profile lex.ProfileRecord, s *types.Session, ctx context.Context) error { 95 207 authargs, err := getOauthSessionAuthArgs(s) 96 208 if err != nil { 97 209 return errors.New("failed to get oauthsessionauthargs while making post: " + err.Error()) 98 210 } 99 - cli := client.NewAPIClient(authargs.PdsUrl) 100 - getOut, err := atproto.RepoGetRecord(ctx, cli, "", "org.xcvr.actor.profile", authargs.Did, "self") 211 + getOut, err := getProfileRecord(authargs.PdsUrl, authargs.Did, ctx) 101 212 if err != nil { 102 213 return errors.New("messed that up! " + err.Error()) 103 214 } ··· 119 230 } 120 231 return nil 121 232 } 233 + 234 + func getProfileRecord(pdsUrl string, did string, ctx context.Context) (*atproto.RepoGetRecord_Output, error) { 235 + cli := client.NewAPIClient(pdsUrl) 236 + return atproto.RepoGetRecord(ctx, cli, "", "org.xcvr.actor.profile", did, "self") 237 + }