backend for xcvr appview

update readme, rename to rvcx, add back cors, add additional checks for unknown didhandle

+271 -114
+67 -18
README.md
··· 1 - # xcvr-backend 2 - a backend service that allows the dynamic creation of lrc servers. see it live at [xcvr.chat](http://xcvr.chat/) 1 + # rvcx 2 + this is the backend for the xcvr appview. it uses lrcd to host the lrc servers, 3 + postgres to cache the atproto data, atproto-oauth-golang as an oauth client, 4 + and probably some other things i am forgetting! 3 5 4 - this is just the backend, for a frontend, consider using [xcvr](https://github.com/rachel-mp4/xcvr/) 6 + ## principles 7 + the xcvr atproto ecosystem has backend appviews which are simultaneously 8 + atproto actors who write signet records to their repos in order to have 9 + bidirectional confidence that off-protocol communication occured 5 10 6 11 ## how to run 7 - ```{bash} 8 - $ cd server 9 - $ go run ./cmd 12 + i don't really test this in development since i added oauth, this likely will 13 + change in the future 14 + 15 + in production, you must create a .env file that contains the following 16 + environment variables, setting secrets as appropriate 17 + 18 + ``` 19 + POSTGRES_USER=xcvr 20 + POSTGRES_PASSWORD=secret 21 + POSTGRES_DB=xcvrdb 22 + POSTGRES_PORT=15432 23 + MY_NAME=xcvr 24 + MY_IDENTITY=xcvr.org 25 + MY_SECRET=secret 26 + MY_METADATA_PATH=/meta/client-metadata.json 27 + MY_TOS_PATH=/meta/tos 28 + MY_POLICY_PATH=/meta/policy 29 + MY_LOGO_PATH=/public/xcvr.svg 30 + MY_OAUTH_CALLBACK=/oauth/callback 31 + MY_JWKS_PATH=/oauth/jwks.json 32 + SESSION_KEY=secret 33 + LRCD_SECRET=secret 10 34 ``` 11 35 12 - ## how to deploy 13 - 1. install nginx 14 - 2. copy xcvr.conf to /etc/nginx/conf.d/ (change the server_name directive to your domain name) 15 - 3. copy static files to /var/www/xcvr/ (if using xcvr for your frontend, then copy the contents of the dist directory generated by `npm run build` to the /var/www/xcvr/) 16 - 4. start nginx 17 - 5. run xcvr-backend 36 + the first four of course have to do with your postgres instance, i think the 37 + postgres port is just 15432 because i was having conflicts on mac, i don't 38 + think what you set it to is very important. they are also used in the migration 39 + scripts. migratedown and migrateup require you to install golang migrate, and 40 + psql requires you to install psql. MY_NAME, MY_IDENTITY, MY_METADATA_PATH, 41 + MY_TOS_PATH, MY_POLICY_PATH, MY_LOGO_PATH, MY_OAUTH_CALLBACK and MY_JWKS_PATH 42 + are used for oauth, MY_IDENTITY is the hostname, but also the atproto handle of 43 + the backend's repo. the MY_SECRET is variable is thus the backend's app 44 + password. in order to be an oauth client, you need a jwks that you serve at 45 + MY_JWKS_PATH, but of course you need to generate this using haileyok's 46 + atproto-oauth-golang, and then save jwks.json in the top level rvcx directory 47 + as jwks.json. SESSION_KEY and LRCD_SECRET are two more keys that you need to 48 + generate in addition to POSTGRES_PASSWORD. SESSION_KEY encrypts the oauth 49 + session data, and LRCD_SECRET is used to generate nonces that prevent anyone 50 + from submitting other people's unauthenticated messages. 18 51 19 - ## endpoints 20 - GET http://localhost:8080/xrpc/getChannels - gets a list of all active channels 52 + once you have your .env file, you then need to run `sudo docker-compose up -d` 53 + and then `sudo ./migrateup` if it is your first time running the server. if you 54 + need to reset the database, you can do `sudo docker-compose down --volumes`, of 55 + course, be careful with this, rvcx does not currently have a way to backfill 56 + the database!!! these commands are run in the main rcvx directory. 21 57 22 - POST http://localhost:8080/xrpc/initChannel - accepts json containing metadata about a channel and creates it 58 + then, you need to `cd server`, and then you can `go run ./cmd` to start the 59 + backend. 60 + 61 + i also have included my nginx configuration. after installing nginx, you need 62 + to put that in the conf.d folder, if you're on ubuntu. i think that nginx 63 + differs a bit depending on your distro, you can probably figure it out, i might 64 + be able to help troubleshoot but it'd probably the blind leading the blind 65 + there haha 66 + 67 + of course, this is just the backend, so alongside nginx, you should build the 68 + frontend and copy the files to the appropriate location. the frontend uses 69 + sveltekit and i just have it generate a static site bc i don't really care atp, 70 + setup is already contrived enough 23 71 24 - ## servers 25 - servers are served on ephemeral ports at ws://localhost:PORT/ws 72 + as with xcvr (frontend) i think this is under mit but i don't know enough 73 + apropos licensing at this moment in history to fully commit in that direction. 26 74 27 - in production, their ports are exposed as ws://xcvr.chat/PORT/ws, which gets proxied to ws://127.0.0.1:PORT/ws 75 + as for contributions, same thing i said in that other readme, i'd love help & 76 + you should let me know in advance so we don't make each other's lives hell :3
+8 -8
server/cmd/main.go
··· 5 5 "errors" 6 6 "net/http" 7 7 "os" 8 + "rvcx/internal/atplistener" 9 + "rvcx/internal/atputils" 10 + "rvcx/internal/db" 11 + "rvcx/internal/handler" 12 + "rvcx/internal/log" 13 + "rvcx/internal/model" 14 + "rvcx/internal/oauth" 8 15 "time" 9 - "xcvr-backend/internal/atplistener" 10 - "xcvr-backend/internal/atputils" 11 - "xcvr-backend/internal/db" 12 - "xcvr-backend/internal/handler" 13 - "xcvr-backend/internal/log" 14 - "xcvr-backend/internal/model" 15 - "xcvr-backend/internal/oauth" 16 16 17 17 "github.com/joho/godotenv" 18 18 ) ··· 22 22 23 23 gdeerr := godotenv.Load("../.env") 24 24 if gdeerr != nil { 25 - logger.Println("i think you should make a .env file in the xcvr-backend directory !\n\nExample contents:\n-------------------------------------------------------------------\nPOSTGRES_USER=xcvr\nPOSTGRES_PASSWORD=secret\nPOSTGRES_DB=xcvrdb\nPOSTGRES_PORT=15432\n-------------------------------------------------------------------\n\nGood luck !\n\n") 25 + logger.Println("i think you should make a .env file in the rvcx directory !\n\nExample contents:\n-------------------------------------------------------------------\nPOSTGRES_USER=xcvr\nPOSTGRES_PASSWORD=secret\nPOSTGRES_DB=xcvrdb\nPOSTGRES_PORT=15432\n-------------------------------------------------------------------\n\nGood luck !\n\n") 26 26 panic(gdeerr) 27 27 } 28 28 store, err := db.Init()
+1 -1
server/gen/main.go
··· 2 2 3 3 import ( 4 4 cbg "github.com/whyrusleeping/cbor-gen" 5 - "xcvr-backend/internal/lex" 5 + "rvcx/internal/lex" 6 6 ) 7 7 8 8 func main() {
+1 -1
server/go.mod
··· 1 - module xcvr-backend 1 + module rvcx 2 2 3 3 go 1.24.2 4 4
+25 -6
server/internal/atplistener/jetstream.go
··· 9 9 "github.com/bluesky-social/jetstream/pkg/client" 10 10 "github.com/bluesky-social/jetstream/pkg/client/schedulers/sequential" 11 11 "github.com/bluesky-social/jetstream/pkg/models" 12 + "rvcx/internal/atputils" 13 + "rvcx/internal/db" 14 + "rvcx/internal/lex" 15 + "rvcx/internal/log" 16 + "rvcx/internal/oauth" 17 + "rvcx/internal/types" 12 18 "time" 13 - "xcvr-backend/internal/atputils" 14 - "xcvr-backend/internal/db" 15 - "xcvr-backend/internal/lex" 16 - "xcvr-backend/internal/log" 17 - "xcvr-backend/internal/oauth" 18 - "xcvr-backend/internal/types" 19 19 ) 20 20 21 21 type Consumer struct { ··· 67 67 func (h *handler) HandleEvent(ctx context.Context, event *models.Event) error { 68 68 if event.Commit == nil { 69 69 return nil 70 + } 71 + err := h.ensureIKnowYou(event.Did, ctx) 72 + if err != nil { 73 + return err 70 74 } 71 75 72 76 switch event.Commit.Collection { ··· 318 322 func URI(event *models.Event) string { 319 323 return atputils.URI(event.Did, event.Commit.Collection, event.Commit.RKey) 320 324 } 325 + 326 + func (h *handler) ensureIKnowYou(did string, ctx context.Context) error { 327 + _, err := h.db.ResolveDid(did, ctx) 328 + if err != nil { 329 + handle, err := atputils.TryLookupDid(ctx, did) 330 + if err != nil { 331 + return errors.New("failed to lookup previously unknown user: " + err.Error()) 332 + } 333 + err = h.db.StoreDidHandle(did, handle, ctx) 334 + if err != nil { 335 + return errors.New("failed to store did_handle for a previously unknown user") 336 + } 337 + } 338 + return nil 339 + }
+28
server/internal/atputils/identity.go
··· 42 42 return did 43 43 } 44 44 45 + func TryLookupHandle(ctx context.Context, handle string) (did string, err error) { 46 + hdl, err := syntax.ParseHandle(handle) 47 + if err != nil { 48 + err = errors.New("handle failed to parse: " + err.Error()) 49 + return 50 + } 51 + id, err := identity.DefaultDirectory().LookupHandle(ctx, hdl) 52 + if err != nil { 53 + err = errors.New("handle failed to loopup: " + err.Error()) 54 + return 55 + } 56 + did = id.DID.String() 57 + return 58 + } 59 + 60 + func TryLookupDid(ctx context.Context, did string) (handle string, err error) { 61 + d, err := syntax.ParseDID(did) 62 + if err != nil { 63 + err = errors.New("did failed to parse: " + err.Error()) 64 + } 65 + id, err := identity.DefaultDirectory().LookupDID(ctx, d) 66 + if err != nil { 67 + err = errors.New("did failed to lookup: " + err.Error()) 68 + } 69 + handle = id.Handle.String() 70 + return 71 + } 72 + 45 73 func GetHandleFromDid(ctx context.Context, did string) (string, error) { 46 74 sdid, err := syntax.ParseDID(did) 47 75 if err != nil {
+1 -1
server/internal/db/db.go
··· 5 5 "errors" 6 6 "fmt" 7 7 "os" 8 - "xcvr-backend/internal/types" 8 + "rvcx/internal/types" 9 9 10 10 "github.com/jackc/pgx/v5/pgxpool" 11 11 )
+8 -3
server/internal/db/lexicon.go
··· 4 4 "context" 5 5 "errors" 6 6 "fmt" 7 + "rvcx/internal/types" 7 8 "strings" 8 - "xcvr-backend/internal/types" 9 9 ) 10 10 11 - func (s *Store) InitializeProfile(did string, handle string, ctx context.Context) error { 11 + func (s *Store) InitializeProfile(did string, 12 + displayname *string, 13 + defaultnick *string, 14 + status *string, 15 + color *uint64, 16 + ctx context.Context) error { 12 17 _, err := s.pool.Exec(ctx, ` 13 18 INSERT INTO profiles ( 14 19 did, ··· 19 24 ) VALUES ( 20 25 $1, $2, $3, $4, $5 21 26 ) ON CONFLICT (did) DO NOTHING 22 - `, did, handle, "wanderer", "just setting up my xcvr", 3702605) 27 + `, did, displayname, status, "just setting up my xcvr", 3702605) 23 28 if err != nil { 24 29 return errors.New("i'm not sure what happened: " + err.Error()) 25 30 }
+1 -1
server/internal/db/oauth.go
··· 4 4 "context" 5 5 "errors" 6 6 "fmt" 7 - "xcvr-backend/internal/types" 7 + "rvcx/internal/types" 8 8 ) 9 9 10 10 func (s *Store) StoreOAuthRequest(req *types.OAuthRequest, ctx context.Context) error {
+30 -16
server/internal/handler/handler.go
··· 5 5 "net/http" 6 6 7 7 "os" 8 - "xcvr-backend/internal/db" 9 - "xcvr-backend/internal/log" 10 - "xcvr-backend/internal/model" 11 - "xcvr-backend/internal/oauth" 8 + "rvcx/internal/db" 9 + "rvcx/internal/log" 10 + "rvcx/internal/model" 11 + "rvcx/internal/oauth" 12 12 ) 13 13 14 14 type Handler struct { ··· 28 28 clientmap := oauth.NewClientMap() 29 29 h := &Handler{db, sessionStore, mux, logger, oauthserv, xrpc, clientmap, model} 30 30 // lrc handlers 31 - mux.HandleFunc("GET /lrc/{user}/{rkey}/ws", h.acceptWebsocket) 31 + mux.HandleFunc("GET /lrc/{user}/{rkey}/ws", h.WithCORS(h.acceptWebsocket)) 32 32 mux.HandleFunc("DELETE /lrc/{user}/{rkey}/ws", h.deleteChannel) 33 33 mux.HandleFunc("POST /lrc/channel", h.postChannel) 34 34 mux.HandleFunc("POST /lrc/message", h.postMessage) 35 - // beep handlers 35 + // xcvr handlers 36 36 mux.HandleFunc("POST /xcvr/profile", h.postProfile) 37 37 mux.HandleFunc("POST /xcvr/beep", h.beep) 38 38 // lexicon handlers 39 - mux.HandleFunc("GET /xrpc/org.xcvr.feed.getChannels", h.getChannels) 40 - mux.HandleFunc("GET /xrpc/org.xcvr.lrc.getMessages", h.getMessages) 41 - mux.HandleFunc("GET /xrpc/org.xcvr.actor.resolveChannel", h.resolveChannel) 42 - mux.HandleFunc("GET /xrpc/org.xcvr.actor.getProfileView", h.getProfileView) 43 - mux.HandleFunc("GET /xrpc/org.xcvr.lrc.subscribeLexStream", h.subscribeLexStream) 39 + mux.HandleFunc("GET /xrpc/org.xcvr.feed.getChannels", h.WithCORS(h.getChannels)) 40 + mux.HandleFunc("GET /xrpc/org.xcvr.lrc.getMessages", h.WithCORS(h.getMessages)) 41 + mux.HandleFunc("GET /xrpc/org.xcvr.actor.resolveChannel", h.WithCORS(h.resolveChannel)) 42 + mux.HandleFunc("GET /xrpc/org.xcvr.actor.getProfileView", h.WithCORS(h.getProfileView)) 43 + mux.HandleFunc("GET /xrpc/org.xcvr.lrc.subscribeLexStream", h.WithCORS(h.subscribeLexStream)) 44 44 // backend metadata handlers 45 - mux.HandleFunc(clientMetadataPath(), h.serveClientMetadata) 46 - mux.HandleFunc(clientTOSPath(), h.serveTOS) 47 - mux.HandleFunc(clientPolicyPath(), h.servePolicy) 45 + mux.HandleFunc(clientMetadataPath(), h.WithCORS(h.serveClientMetadata)) 46 + mux.HandleFunc(clientTOSPath(), h.WithCORS(h.serveTOS)) 47 + mux.HandleFunc(clientPolicyPath(), h.WithCORS(h.servePolicy)) 48 48 // oauth handlers 49 - mux.HandleFunc(oauthJWKSPath(), h.serveJWKS) 49 + mux.HandleFunc(oauthJWKSPath(), h.WithCORS(h.serveJWKS)) 50 50 mux.HandleFunc("POST /oauth/login", h.oauthLogin) 51 51 mux.HandleFunc("GET /oauth/whoami", h.getSession) 52 - mux.HandleFunc(oauthCallbackPath(), h.oauthCallback) 52 + mux.HandleFunc(oauthCallbackPath(), h.WithCORS(h.oauthCallback)) 53 53 return h 54 54 } 55 55 ··· 86 86 } 87 87 h.router.ServeHTTP(w, r) 88 88 }) 89 + } 90 + 91 + func (h *Handler) WithCORS(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { 92 + return func(w http.ResponseWriter, r *http.Request) { 93 + w.Header().Set("Access-Control-Allow-Origin", "*") 94 + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") 95 + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorizaton, X-Requested-With, Sec-WebSocket-Protocol, Sec-WebSocket-Extensions, Sec-WebSocket-Key, Sec-WebSocket-Version") 96 + w.Header().Set("Access-Control-Allow-Credentials", "true") 97 + if r.Method == "Options" { 98 + w.WriteHeader(http.StatusOK) 99 + return 100 + } 101 + f(w, r) 102 + } 89 103 } 90 104 91 105 func (h *Handler) Serve() http.Handler {
+15 -5
server/internal/handler/lexiconHandlers.go
··· 1 1 package handler 2 2 3 3 import ( 4 + "context" 4 5 "encoding/json" 5 6 "errors" 6 7 "fmt" 7 8 "net/http" 9 + "rvcx/internal/atputils" 10 + "rvcx/internal/types" 8 11 "strconv" 9 - "xcvr-backend/internal/types" 10 12 ) 11 13 12 14 func (h *Handler) getChannels(w http.ResponseWriter, r *http.Request) { ··· 78 80 var err error 79 81 did, err = h.db.ResolveHandle(handle, r.Context()) 80 82 if err != nil { 81 - h.serverError(w, err) 82 - return 83 + did, err = atputils.TryLookupHandle(r.Context(), handle) 84 + if err != nil { 85 + h.serverError(w, errors.New("i think the handle might not exist?"+err.Error())) 86 + return 87 + } 88 + go h.db.StoreDidHandle(did, handle, context.Background()) 83 89 } 84 90 } 85 91 url := fmt.Sprintf("/lrc/%s/%s/ws", did, rkey) ··· 101 107 var err error 102 108 did, err = h.db.ResolveHandle(handle, r.Context()) 103 109 if err != nil { 104 - h.serverError(w, err) 105 - return 110 + did, err = atputils.TryLookupHandle(r.Context(), handle) 111 + if err != nil { 112 + h.serverError(w, errors.New("i think the handle might not exist?"+err.Error())) 113 + return 114 + } 115 + go h.db.StoreDidHandle(did, handle, context.Background()) 106 116 } 107 117 } 108 118 h.serveProfileView(did, handle, w, r)
+13 -7
server/internal/handler/lrcHandlers.go
··· 1 1 package handler 2 2 3 3 import ( 4 + "context" 4 5 "encoding/json" 5 6 "errors" 6 7 "fmt" 7 - "github.com/bluesky-social/indigo/atproto/syntax" 8 - "github.com/rachel-mp4/lrcd" 9 8 "net/http" 10 9 "os" 10 + "rvcx/internal/atputils" 11 + "rvcx/internal/lex" 12 + "rvcx/internal/types" 11 13 "slices" 12 14 "time" 13 - "xcvr-backend/internal/atputils" 14 - "xcvr-backend/internal/lex" 15 - "xcvr-backend/internal/types" 15 + 16 + "github.com/bluesky-social/indigo/atproto/syntax" 17 + "github.com/rachel-mp4/lrcd" 16 18 ) 17 19 18 20 func (h *Handler) acceptWebsocket(w http.ResponseWriter, r *http.Request) { ··· 131 133 } 132 134 handle, err := h.db.ResolveDid(channel.DID, r.Context()) 133 135 if err != nil { 134 - h.serverError(w, errors.New("couldn't find handle")) 135 - return 136 + handle, err = atputils.TryLookupDid(r.Context(), channel.DID) 137 + if err != nil { 138 + h.serverError(w, errors.New("couldn't find handle")) 139 + return 140 + } 141 + go h.db.StoreDidHandle(channel.DID, handle, context.Background()) 136 142 } 137 143 rkey, _ := atputils.RkeyFromUri(channel.URI) 138 144 http.Redirect(w, r, fmt.Sprintf("/c/%s/%s", handle, rkey), http.StatusSeeOther)
+3 -2
server/internal/handler/metadataHandlers.go
··· 1 1 package handler 2 + 2 3 import ( 4 + "encoding/json" 3 5 "fmt" 4 6 "net/http" 5 - "encoding/json" 6 7 "os" 7 - "xcvr-backend/internal/oauth" 8 + "rvcx/internal/oauth" 8 9 ) 9 10 10 11 func (h *Handler) serveClientMetadata(w http.ResponseWriter, r *http.Request) {
+13 -11
server/internal/handler/oauthHandlers.go
··· 8 8 "net/http" 9 9 "net/url" 10 10 "os" 11 - "xcvr-backend/internal/atputils" 12 - "xcvr-backend/internal/lex" 13 - "xcvr-backend/internal/oauth" 14 - "xcvr-backend/internal/types" 11 + "rvcx/internal/atputils" 12 + "rvcx/internal/lex" 13 + "rvcx/internal/oauth" 14 + "rvcx/internal/types" 15 15 16 16 "github.com/gorilla/sessions" 17 17 "github.com/haileyok/atproto-oauth-golang/helpers" ··· 73 73 if err != nil { 74 74 h.logger.Deprintln("failed to store did handle: " + err.Error()) 75 75 } 76 - err = h.db.InitializeProfile(res.DID, handle, context.Background()) 77 - h.logger.Deprintln("initializing....") 78 - if err != nil { 79 - h.logger.Deprintln("failed to initialize profile: " + err.Error()) 80 - } 81 76 }() 82 77 http.Redirect(w, r, u.String(), http.StatusFound) 83 78 } ··· 146 141 Color: &color, 147 142 } 148 143 client := h.setupClient(OauthSession) 149 - err = client.CreateXCVRProfile(defaultprofilerecord, context.Background()) 144 + pr, err := client.CreateXCVRProfile(defaultprofilerecord, context.Background()) 145 + if err != nil { 146 + h.logger.Println("i couldn't create their profile, which is bad_" + err.Error()) 147 + return 148 + } 149 + h.logger.Deprintln("initializing....") 150 + err = h.db.InitializeProfile(req.Did, pr.DisplayName, pr.DefaultNick, pr.Status, pr.Color, context.Background()) 150 151 if err != nil { 151 - h.logger.Println("#that happened (something went wrong when creating profile) " + err.Error()) 152 + h.logger.Deprintln("failed to initialize profile: " + err.Error()) 153 + return 152 154 } 153 155 }() 154 156
+5 -5
server/internal/handler/xcvrHandlers.go
··· 4 4 "encoding/json" 5 5 "errors" 6 6 "net/http" 7 - "xcvr-backend/internal/atputils" 8 - "xcvr-backend/internal/db" 9 - "xcvr-backend/internal/lex" 10 - "xcvr-backend/internal/types" 7 + "rvcx/internal/atputils" 8 + "rvcx/internal/db" 9 + "rvcx/internal/lex" 10 + "rvcx/internal/types" 11 11 ) 12 12 13 13 func (h *Handler) postProfile(w http.ResponseWriter, r *http.Request) { ··· 76 76 h.serverError(w, err) 77 77 return 78 78 } 79 - err = client.UpdateXCVRProfile(profilerecord, r.Context()) 79 + _, err = client.UpdateXCVRProfile(profilerecord, r.Context()) 80 80 if err != nil { 81 81 h.serverError(w, err) 82 82 return
+6 -6
server/internal/model/channel.go
··· 6 6 "github.com/bluesky-social/indigo/atproto/syntax" 7 7 "net/http" 8 8 "os" 9 + "rvcx/internal/atputils" 10 + "rvcx/internal/db" 11 + "rvcx/internal/lex" 12 + "rvcx/internal/log" 13 + "rvcx/internal/oauth" 14 + "rvcx/internal/types" 9 15 "sync" 10 16 "time" 11 - "xcvr-backend/internal/atputils" 12 - "xcvr-backend/internal/db" 13 - "xcvr-backend/internal/lex" 14 - "xcvr-backend/internal/log" 15 - "xcvr-backend/internal/oauth" 16 - "xcvr-backend/internal/types" 17 17 18 18 "github.com/rachel-mp4/lrcd" 19 19 lrcpb "github.com/rachel-mp4/lrcproto/gen/go"
+10 -4
server/internal/model/channelLexiconStream.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "github.com/gorilla/websocket" 6 5 "net/http" 6 + "rvcx/internal/atputils" 7 + "rvcx/internal/types" 7 8 "time" 8 - "xcvr-backend/internal/types" 9 + 10 + "github.com/gorilla/websocket" 9 11 ) 10 12 11 13 type client struct { ··· 118 120 } 119 121 ihandle, err := m.store.ResolveDid(s.IssuerDID, context.Background()) 120 122 if err != nil { 121 - m.logger.Println("AAAAAAAAAAAAAAAAAAAAA") 122 - return 123 + ihandle, err = atputils.TryLookupDid(context.Background(), s.IssuerDID) 124 + if err != nil { 125 + m.logger.Println("AAAAAAAAAAAAAAAAAAAAA") 126 + return 127 + } 128 + go m.store.StoreDidHandle(s.IssuerDID, ihandle, context.Background()) 123 129 } 124 130 sv := types.SignetView{ 125 131 URI: s.URI,
+32 -15
server/internal/oauth/oauthclient.go
··· 2 2 3 3 import ( 4 4 "context" 5 + "encoding/json" 5 6 "errors" 6 7 "github.com/bluesky-social/indigo/api/atproto" 7 8 "github.com/bluesky-social/indigo/api/bsky" ··· 11 12 "github.com/haileyok/atproto-oauth-golang" 12 13 "github.com/haileyok/atproto-oauth-golang/helpers" 13 14 14 - "xcvr-backend/internal/db" 15 - "xcvr-backend/internal/lex" 16 - "xcvr-backend/internal/log" 17 - "xcvr-backend/internal/types" 15 + "rvcx/internal/db" 16 + "rvcx/internal/lex" 17 + "rvcx/internal/log" 18 + "rvcx/internal/types" 18 19 ) 19 20 20 21 type OauthXRPCClient struct { ··· 78 79 return nil 79 80 } 80 81 81 - func (c *OauthXRPCClient) CreateXCVRProfile(profile lex.ProfileRecord, ctx context.Context) error { 82 + func (c *OauthXRPCClient) CreateXCVRProfile(profile lex.ProfileRecord, ctx context.Context) (p *lex.ProfileRecord, err error) { 82 83 authargs, err := c.getOauthSessionAuthArgs() 83 84 if err != nil { 84 - return errors.New("failed to get oauthsessionauthargs while making post: " + err.Error()) 85 + err = errors.New("failed to get oauthsessionauthargs while making post: " + err.Error()) 86 + return 85 87 } 86 88 getOut, err := getProfileRecord(authargs.PdsUrl, authargs.Did, ctx) 87 89 if err != nil { 88 - return errors.New("failed to getProfileRecord while creating XCVR profile: " + err.Error()) 90 + err = errors.New("failed to getProfileRecord while creating XCVR profile: " + err.Error()) 91 + return 89 92 } 90 93 if getOut.Cid != nil { 91 - return errors.New("there already is a profileRecord, I don't want to overwrite it") 94 + var jsonBytes []byte 95 + jsonBytes, err = json.Marshal(getOut.Value) 96 + if err != nil { 97 + return 98 + } 99 + var pro lex.ProfileRecord 100 + err = json.Unmarshal(jsonBytes, &pro) 101 + if err != nil { 102 + return 103 + } 104 + return &pro, nil 92 105 } 93 106 rkey := "self" 94 107 input := atproto.RepoCreateRecord_Input{ ··· 100 113 var out atproto.RepoCreateRecord_Output 101 114 err = c.xrpc.Do(ctx, authargs, "POST", "application/json", "com.atproto.repo.createRecord", nil, input, &out) 102 115 if err != nil { 103 - return errors.New("oops! failed to create a profile: " + err.Error()) 116 + err = errors.New("oops! failed to create a profile: " + err.Error()) 117 + return 104 118 } 105 - return nil 119 + return &profile, nil 106 120 } 107 121 108 122 func (c *OauthXRPCClient) CreateXCVRChannel(channel *lex.ChannelRecord, ctx context.Context) (uri string, cid string, err error) { ··· 149 163 return 150 164 } 151 165 152 - func (c *OauthXRPCClient) UpdateXCVRProfile(profile lex.ProfileRecord, ctx context.Context) error { 166 + func (c *OauthXRPCClient) UpdateXCVRProfile(profile lex.ProfileRecord, ctx context.Context) (p *lex.ProfileRecord, err error) { 153 167 authargs, err := c.getOauthSessionAuthArgs() 154 168 if err != nil { 155 - return errors.New("failed to get oauthsessionauthargs while making post: " + err.Error()) 169 + err = errors.New("failed to get oauthsessionauthargs while making post: " + err.Error()) 170 + return 156 171 } 157 172 getOut, err := getProfileRecord(authargs.PdsUrl, authargs.Did, ctx) 158 173 if err != nil { 159 - return errors.New("messed that up! " + err.Error()) 174 + err = errors.New("messed that up! " + err.Error()) 175 + return 160 176 } 161 177 if getOut.Cid == nil { 162 178 return c.CreateXCVRProfile(profile, ctx) ··· 172 188 var out atproto.RepoPutRecord_Output 173 189 err = c.xrpc.Do(ctx, authargs, "POST", "application/json", "com.atproto.repo.putRecord", nil, input, &out) 174 190 if err != nil { 175 - return errors.New("oops! failed to update a profile: " + err.Error()) 191 + err = errors.New("oops! failed to update a profile: " + err.Error()) 192 + return 176 193 } 177 - return nil 194 + return &profile, nil 178 195 } 179 196 180 197 func getProfileRecord(pdsUrl string, did string, ctx context.Context) (*atproto.RepoGetRecord_Output, error) {
+2 -2
server/internal/oauth/passwordclient.go
··· 8 8 "github.com/bluesky-social/indigo/atproto/client" 9 9 "github.com/bluesky-social/indigo/lex/util" 10 10 "os" 11 - "xcvr-backend/internal/lex" 12 - "xcvr-backend/internal/log" 11 + "rvcx/internal/lex" 12 + "rvcx/internal/log" 13 13 ) 14 14 15 15 type PasswordClient struct {
+2 -2
server/internal/oauth/service.go
··· 6 6 "errors" 7 7 "fmt" 8 8 "net/http" 9 + "rvcx/internal/atputils" 10 + "rvcx/internal/types" 9 11 "time" 10 - "xcvr-backend/internal/atputils" 11 - "xcvr-backend/internal/types" 12 12 13 13 atoauth "github.com/haileyok/atproto-oauth-golang" 14 14 "github.com/haileyok/atproto-oauth-golang/helpers"