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/) 3 4 - this is just the backend, for a frontend, consider using [xcvr](https://github.com/rachel-mp4/xcvr/) 5 6 ## how to run 7 - ```{bash} 8 - $ cd server 9 - $ go run ./cmd 10 ``` 11 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 18 19 - ## endpoints 20 - GET http://localhost:8080/xrpc/getChannels - gets a list of all active channels 21 22 - POST http://localhost:8080/xrpc/initChannel - accepts json containing metadata about a channel and creates it 23 24 - ## servers 25 - servers are served on ephemeral ports at ws://localhost:PORT/ws 26 27 - in production, their ports are exposed as ws://xcvr.chat/PORT/ws, which gets proxied to ws://127.0.0.1:PORT/ws
··· 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! 5 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 10 11 ## how to run 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 34 ``` 35 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. 51 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. 57 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 71 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. 74 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 "errors" 6 "net/http" 7 "os" 8 "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 17 "github.com/joho/godotenv" 18 ) ··· 22 23 gdeerr := godotenv.Load("../.env") 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") 26 panic(gdeerr) 27 } 28 store, err := db.Init()
··· 5 "errors" 6 "net/http" 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" 15 "time" 16 17 "github.com/joho/godotenv" 18 ) ··· 22 23 gdeerr := godotenv.Load("../.env") 24 if gdeerr != nil { 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 panic(gdeerr) 27 } 28 store, err := db.Init()
+1 -1
server/gen/main.go
··· 2 3 import ( 4 cbg "github.com/whyrusleeping/cbor-gen" 5 - "xcvr-backend/internal/lex" 6 ) 7 8 func main() {
··· 2 3 import ( 4 cbg "github.com/whyrusleeping/cbor-gen" 5 + "rvcx/internal/lex" 6 ) 7 8 func main() {
+1 -1
server/go.mod
··· 1 - module xcvr-backend 2 3 go 1.24.2 4
··· 1 + module rvcx 2 3 go 1.24.2 4
+25 -6
server/internal/atplistener/jetstream.go
··· 9 "github.com/bluesky-social/jetstream/pkg/client" 10 "github.com/bluesky-social/jetstream/pkg/client/schedulers/sequential" 11 "github.com/bluesky-social/jetstream/pkg/models" 12 "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 ) 20 21 type Consumer struct { ··· 67 func (h *handler) HandleEvent(ctx context.Context, event *models.Event) error { 68 if event.Commit == nil { 69 return nil 70 } 71 72 switch event.Commit.Collection { ··· 318 func URI(event *models.Event) string { 319 return atputils.URI(event.Did, event.Commit.Collection, event.Commit.RKey) 320 }
··· 9 "github.com/bluesky-social/jetstream/pkg/client" 10 "github.com/bluesky-social/jetstream/pkg/client/schedulers/sequential" 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" 18 "time" 19 ) 20 21 type Consumer struct { ··· 67 func (h *handler) HandleEvent(ctx context.Context, event *models.Event) error { 68 if event.Commit == nil { 69 return nil 70 + } 71 + err := h.ensureIKnowYou(event.Did, ctx) 72 + if err != nil { 73 + return err 74 } 75 76 switch event.Commit.Collection { ··· 322 func URI(event *models.Event) string { 323 return atputils.URI(event.Did, event.Commit.Collection, event.Commit.RKey) 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 return did 43 } 44 45 func GetHandleFromDid(ctx context.Context, did string) (string, error) { 46 sdid, err := syntax.ParseDID(did) 47 if err != nil {
··· 42 return did 43 } 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 + 73 func GetHandleFromDid(ctx context.Context, did string) (string, error) { 74 sdid, err := syntax.ParseDID(did) 75 if err != nil {
+1 -1
server/internal/db/db.go
··· 5 "errors" 6 "fmt" 7 "os" 8 - "xcvr-backend/internal/types" 9 10 "github.com/jackc/pgx/v5/pgxpool" 11 )
··· 5 "errors" 6 "fmt" 7 "os" 8 + "rvcx/internal/types" 9 10 "github.com/jackc/pgx/v5/pgxpool" 11 )
+8 -3
server/internal/db/lexicon.go
··· 4 "context" 5 "errors" 6 "fmt" 7 "strings" 8 - "xcvr-backend/internal/types" 9 ) 10 11 - func (s *Store) InitializeProfile(did string, handle string, ctx context.Context) error { 12 _, err := s.pool.Exec(ctx, ` 13 INSERT INTO profiles ( 14 did, ··· 19 ) VALUES ( 20 $1, $2, $3, $4, $5 21 ) ON CONFLICT (did) DO NOTHING 22 - `, did, handle, "wanderer", "just setting up my xcvr", 3702605) 23 if err != nil { 24 return errors.New("i'm not sure what happened: " + err.Error()) 25 }
··· 4 "context" 5 "errors" 6 "fmt" 7 + "rvcx/internal/types" 8 "strings" 9 ) 10 11 + func (s *Store) InitializeProfile(did string, 12 + displayname *string, 13 + defaultnick *string, 14 + status *string, 15 + color *uint64, 16 + ctx context.Context) error { 17 _, err := s.pool.Exec(ctx, ` 18 INSERT INTO profiles ( 19 did, ··· 24 ) VALUES ( 25 $1, $2, $3, $4, $5 26 ) ON CONFLICT (did) DO NOTHING 27 + `, did, displayname, status, "just setting up my xcvr", 3702605) 28 if err != nil { 29 return errors.New("i'm not sure what happened: " + err.Error()) 30 }
+1 -1
server/internal/db/oauth.go
··· 4 "context" 5 "errors" 6 "fmt" 7 - "xcvr-backend/internal/types" 8 ) 9 10 func (s *Store) StoreOAuthRequest(req *types.OAuthRequest, ctx context.Context) error {
··· 4 "context" 5 "errors" 6 "fmt" 7 + "rvcx/internal/types" 8 ) 9 10 func (s *Store) StoreOAuthRequest(req *types.OAuthRequest, ctx context.Context) error {
+30 -16
server/internal/handler/handler.go
··· 5 "net/http" 6 7 "os" 8 - "xcvr-backend/internal/db" 9 - "xcvr-backend/internal/log" 10 - "xcvr-backend/internal/model" 11 - "xcvr-backend/internal/oauth" 12 ) 13 14 type Handler struct { ··· 28 clientmap := oauth.NewClientMap() 29 h := &Handler{db, sessionStore, mux, logger, oauthserv, xrpc, clientmap, model} 30 // lrc handlers 31 - mux.HandleFunc("GET /lrc/{user}/{rkey}/ws", h.acceptWebsocket) 32 mux.HandleFunc("DELETE /lrc/{user}/{rkey}/ws", h.deleteChannel) 33 mux.HandleFunc("POST /lrc/channel", h.postChannel) 34 mux.HandleFunc("POST /lrc/message", h.postMessage) 35 - // beep handlers 36 mux.HandleFunc("POST /xcvr/profile", h.postProfile) 37 mux.HandleFunc("POST /xcvr/beep", h.beep) 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) 44 // backend metadata handlers 45 - mux.HandleFunc(clientMetadataPath(), h.serveClientMetadata) 46 - mux.HandleFunc(clientTOSPath(), h.serveTOS) 47 - mux.HandleFunc(clientPolicyPath(), h.servePolicy) 48 // oauth handlers 49 - mux.HandleFunc(oauthJWKSPath(), h.serveJWKS) 50 mux.HandleFunc("POST /oauth/login", h.oauthLogin) 51 mux.HandleFunc("GET /oauth/whoami", h.getSession) 52 - mux.HandleFunc(oauthCallbackPath(), h.oauthCallback) 53 return h 54 } 55 ··· 86 } 87 h.router.ServeHTTP(w, r) 88 }) 89 } 90 91 func (h *Handler) Serve() http.Handler {
··· 5 "net/http" 6 7 "os" 8 + "rvcx/internal/db" 9 + "rvcx/internal/log" 10 + "rvcx/internal/model" 11 + "rvcx/internal/oauth" 12 ) 13 14 type Handler struct { ··· 28 clientmap := oauth.NewClientMap() 29 h := &Handler{db, sessionStore, mux, logger, oauthserv, xrpc, clientmap, model} 30 // lrc handlers 31 + mux.HandleFunc("GET /lrc/{user}/{rkey}/ws", h.WithCORS(h.acceptWebsocket)) 32 mux.HandleFunc("DELETE /lrc/{user}/{rkey}/ws", h.deleteChannel) 33 mux.HandleFunc("POST /lrc/channel", h.postChannel) 34 mux.HandleFunc("POST /lrc/message", h.postMessage) 35 + // xcvr handlers 36 mux.HandleFunc("POST /xcvr/profile", h.postProfile) 37 mux.HandleFunc("POST /xcvr/beep", h.beep) 38 // lexicon handlers 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 // backend metadata handlers 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 // oauth handlers 49 + mux.HandleFunc(oauthJWKSPath(), h.WithCORS(h.serveJWKS)) 50 mux.HandleFunc("POST /oauth/login", h.oauthLogin) 51 mux.HandleFunc("GET /oauth/whoami", h.getSession) 52 + mux.HandleFunc(oauthCallbackPath(), h.WithCORS(h.oauthCallback)) 53 return h 54 } 55 ··· 86 } 87 h.router.ServeHTTP(w, r) 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 + } 103 } 104 105 func (h *Handler) Serve() http.Handler {
+15 -5
server/internal/handler/lexiconHandlers.go
··· 1 package handler 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "net/http" 8 "strconv" 9 - "xcvr-backend/internal/types" 10 ) 11 12 func (h *Handler) getChannels(w http.ResponseWriter, r *http.Request) { ··· 78 var err error 79 did, err = h.db.ResolveHandle(handle, r.Context()) 80 if err != nil { 81 - h.serverError(w, err) 82 - return 83 } 84 } 85 url := fmt.Sprintf("/lrc/%s/%s/ws", did, rkey) ··· 101 var err error 102 did, err = h.db.ResolveHandle(handle, r.Context()) 103 if err != nil { 104 - h.serverError(w, err) 105 - return 106 } 107 } 108 h.serveProfileView(did, handle, w, r)
··· 1 package handler 2 3 import ( 4 + "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "net/http" 9 + "rvcx/internal/atputils" 10 + "rvcx/internal/types" 11 "strconv" 12 ) 13 14 func (h *Handler) getChannels(w http.ResponseWriter, r *http.Request) { ··· 80 var err error 81 did, err = h.db.ResolveHandle(handle, r.Context()) 82 if err != nil { 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()) 89 } 90 } 91 url := fmt.Sprintf("/lrc/%s/%s/ws", did, rkey) ··· 107 var err error 108 did, err = h.db.ResolveHandle(handle, r.Context()) 109 if err != nil { 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()) 116 } 117 } 118 h.serveProfileView(did, handle, w, r)
+13 -7
server/internal/handler/lrcHandlers.go
··· 1 package handler 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 - "github.com/bluesky-social/indigo/atproto/syntax" 8 - "github.com/rachel-mp4/lrcd" 9 "net/http" 10 "os" 11 "slices" 12 "time" 13 - "xcvr-backend/internal/atputils" 14 - "xcvr-backend/internal/lex" 15 - "xcvr-backend/internal/types" 16 ) 17 18 func (h *Handler) acceptWebsocket(w http.ResponseWriter, r *http.Request) { ··· 131 } 132 handle, err := h.db.ResolveDid(channel.DID, r.Context()) 133 if err != nil { 134 - h.serverError(w, errors.New("couldn't find handle")) 135 - return 136 } 137 rkey, _ := atputils.RkeyFromUri(channel.URI) 138 http.Redirect(w, r, fmt.Sprintf("/c/%s/%s", handle, rkey), http.StatusSeeOther)
··· 1 package handler 2 3 import ( 4 + "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "net/http" 9 "os" 10 + "rvcx/internal/atputils" 11 + "rvcx/internal/lex" 12 + "rvcx/internal/types" 13 "slices" 14 "time" 15 + 16 + "github.com/bluesky-social/indigo/atproto/syntax" 17 + "github.com/rachel-mp4/lrcd" 18 ) 19 20 func (h *Handler) acceptWebsocket(w http.ResponseWriter, r *http.Request) { ··· 133 } 134 handle, err := h.db.ResolveDid(channel.DID, r.Context()) 135 if err != nil { 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()) 142 } 143 rkey, _ := atputils.RkeyFromUri(channel.URI) 144 http.Redirect(w, r, fmt.Sprintf("/c/%s/%s", handle, rkey), http.StatusSeeOther)
+3 -2
server/internal/handler/metadataHandlers.go
··· 1 package handler 2 import ( 3 "fmt" 4 "net/http" 5 - "encoding/json" 6 "os" 7 - "xcvr-backend/internal/oauth" 8 ) 9 10 func (h *Handler) serveClientMetadata(w http.ResponseWriter, r *http.Request) {
··· 1 package handler 2 + 3 import ( 4 + "encoding/json" 5 "fmt" 6 "net/http" 7 "os" 8 + "rvcx/internal/oauth" 9 ) 10 11 func (h *Handler) serveClientMetadata(w http.ResponseWriter, r *http.Request) {
+13 -11
server/internal/handler/oauthHandlers.go
··· 8 "net/http" 9 "net/url" 10 "os" 11 - "xcvr-backend/internal/atputils" 12 - "xcvr-backend/internal/lex" 13 - "xcvr-backend/internal/oauth" 14 - "xcvr-backend/internal/types" 15 16 "github.com/gorilla/sessions" 17 "github.com/haileyok/atproto-oauth-golang/helpers" ··· 73 if err != nil { 74 h.logger.Deprintln("failed to store did handle: " + err.Error()) 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 }() 82 http.Redirect(w, r, u.String(), http.StatusFound) 83 } ··· 146 Color: &color, 147 } 148 client := h.setupClient(OauthSession) 149 - err = client.CreateXCVRProfile(defaultprofilerecord, context.Background()) 150 if err != nil { 151 - h.logger.Println("#that happened (something went wrong when creating profile) " + err.Error()) 152 } 153 }() 154
··· 8 "net/http" 9 "net/url" 10 "os" 11 + "rvcx/internal/atputils" 12 + "rvcx/internal/lex" 13 + "rvcx/internal/oauth" 14 + "rvcx/internal/types" 15 16 "github.com/gorilla/sessions" 17 "github.com/haileyok/atproto-oauth-golang/helpers" ··· 73 if err != nil { 74 h.logger.Deprintln("failed to store did handle: " + err.Error()) 75 } 76 }() 77 http.Redirect(w, r, u.String(), http.StatusFound) 78 } ··· 141 Color: &color, 142 } 143 client := h.setupClient(OauthSession) 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()) 151 if err != nil { 152 + h.logger.Deprintln("failed to initialize profile: " + err.Error()) 153 + return 154 } 155 }() 156
+5 -5
server/internal/handler/xcvrHandlers.go
··· 4 "encoding/json" 5 "errors" 6 "net/http" 7 - "xcvr-backend/internal/atputils" 8 - "xcvr-backend/internal/db" 9 - "xcvr-backend/internal/lex" 10 - "xcvr-backend/internal/types" 11 ) 12 13 func (h *Handler) postProfile(w http.ResponseWriter, r *http.Request) { ··· 76 h.serverError(w, err) 77 return 78 } 79 - err = client.UpdateXCVRProfile(profilerecord, r.Context()) 80 if err != nil { 81 h.serverError(w, err) 82 return
··· 4 "encoding/json" 5 "errors" 6 "net/http" 7 + "rvcx/internal/atputils" 8 + "rvcx/internal/db" 9 + "rvcx/internal/lex" 10 + "rvcx/internal/types" 11 ) 12 13 func (h *Handler) postProfile(w http.ResponseWriter, r *http.Request) { ··· 76 h.serverError(w, err) 77 return 78 } 79 + _, err = client.UpdateXCVRProfile(profilerecord, r.Context()) 80 if err != nil { 81 h.serverError(w, err) 82 return
+6 -6
server/internal/model/channel.go
··· 6 "github.com/bluesky-social/indigo/atproto/syntax" 7 "net/http" 8 "os" 9 "sync" 10 "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 18 "github.com/rachel-mp4/lrcd" 19 lrcpb "github.com/rachel-mp4/lrcproto/gen/go"
··· 6 "github.com/bluesky-social/indigo/atproto/syntax" 7 "net/http" 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" 15 "sync" 16 "time" 17 18 "github.com/rachel-mp4/lrcd" 19 lrcpb "github.com/rachel-mp4/lrcproto/gen/go"
+10 -4
server/internal/model/channelLexiconStream.go
··· 2 3 import ( 4 "context" 5 - "github.com/gorilla/websocket" 6 "net/http" 7 "time" 8 - "xcvr-backend/internal/types" 9 ) 10 11 type client struct { ··· 118 } 119 ihandle, err := m.store.ResolveDid(s.IssuerDID, context.Background()) 120 if err != nil { 121 - m.logger.Println("AAAAAAAAAAAAAAAAAAAAA") 122 - return 123 } 124 sv := types.SignetView{ 125 URI: s.URI,
··· 2 3 import ( 4 "context" 5 "net/http" 6 + "rvcx/internal/atputils" 7 + "rvcx/internal/types" 8 "time" 9 + 10 + "github.com/gorilla/websocket" 11 ) 12 13 type client struct { ··· 120 } 121 ihandle, err := m.store.ResolveDid(s.IssuerDID, context.Background()) 122 if err != nil { 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()) 129 } 130 sv := types.SignetView{ 131 URI: s.URI,
+32 -15
server/internal/oauth/oauthclient.go
··· 2 3 import ( 4 "context" 5 "errors" 6 "github.com/bluesky-social/indigo/api/atproto" 7 "github.com/bluesky-social/indigo/api/bsky" ··· 11 "github.com/haileyok/atproto-oauth-golang" 12 "github.com/haileyok/atproto-oauth-golang/helpers" 13 14 - "xcvr-backend/internal/db" 15 - "xcvr-backend/internal/lex" 16 - "xcvr-backend/internal/log" 17 - "xcvr-backend/internal/types" 18 ) 19 20 type OauthXRPCClient struct { ··· 78 return nil 79 } 80 81 - func (c *OauthXRPCClient) CreateXCVRProfile(profile lex.ProfileRecord, ctx context.Context) error { 82 authargs, err := c.getOauthSessionAuthArgs() 83 if err != nil { 84 - return errors.New("failed to get oauthsessionauthargs while making post: " + err.Error()) 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 } 93 rkey := "self" 94 input := atproto.RepoCreateRecord_Input{ ··· 100 var out atproto.RepoCreateRecord_Output 101 err = c.xrpc.Do(ctx, authargs, "POST", "application/json", "com.atproto.repo.createRecord", nil, input, &out) 102 if err != nil { 103 - return errors.New("oops! failed to create a profile: " + err.Error()) 104 } 105 - return nil 106 } 107 108 func (c *OauthXRPCClient) CreateXCVRChannel(channel *lex.ChannelRecord, ctx context.Context) (uri string, cid string, err error) { ··· 149 return 150 } 151 152 - func (c *OauthXRPCClient) UpdateXCVRProfile(profile lex.ProfileRecord, ctx context.Context) error { 153 authargs, err := c.getOauthSessionAuthArgs() 154 if err != nil { 155 - return errors.New("failed to get oauthsessionauthargs while making post: " + err.Error()) 156 } 157 getOut, err := getProfileRecord(authargs.PdsUrl, authargs.Did, ctx) 158 if err != nil { 159 - return errors.New("messed that up! " + err.Error()) 160 } 161 if getOut.Cid == nil { 162 return c.CreateXCVRProfile(profile, ctx) ··· 172 var out atproto.RepoPutRecord_Output 173 err = c.xrpc.Do(ctx, authargs, "POST", "application/json", "com.atproto.repo.putRecord", nil, input, &out) 174 if err != nil { 175 - return errors.New("oops! failed to update a profile: " + err.Error()) 176 } 177 - return nil 178 } 179 180 func getProfileRecord(pdsUrl string, did string, ctx context.Context) (*atproto.RepoGetRecord_Output, error) {
··· 2 3 import ( 4 "context" 5 + "encoding/json" 6 "errors" 7 "github.com/bluesky-social/indigo/api/atproto" 8 "github.com/bluesky-social/indigo/api/bsky" ··· 12 "github.com/haileyok/atproto-oauth-golang" 13 "github.com/haileyok/atproto-oauth-golang/helpers" 14 15 + "rvcx/internal/db" 16 + "rvcx/internal/lex" 17 + "rvcx/internal/log" 18 + "rvcx/internal/types" 19 ) 20 21 type OauthXRPCClient struct { ··· 79 return nil 80 } 81 82 + func (c *OauthXRPCClient) CreateXCVRProfile(profile lex.ProfileRecord, ctx context.Context) (p *lex.ProfileRecord, err error) { 83 authargs, err := c.getOauthSessionAuthArgs() 84 if err != nil { 85 + err = errors.New("failed to get oauthsessionauthargs while making post: " + err.Error()) 86 + return 87 } 88 getOut, err := getProfileRecord(authargs.PdsUrl, authargs.Did, ctx) 89 if err != nil { 90 + err = errors.New("failed to getProfileRecord while creating XCVR profile: " + err.Error()) 91 + return 92 } 93 if getOut.Cid != nil { 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 105 } 106 rkey := "self" 107 input := atproto.RepoCreateRecord_Input{ ··· 113 var out atproto.RepoCreateRecord_Output 114 err = c.xrpc.Do(ctx, authargs, "POST", "application/json", "com.atproto.repo.createRecord", nil, input, &out) 115 if err != nil { 116 + err = errors.New("oops! failed to create a profile: " + err.Error()) 117 + return 118 } 119 + return &profile, nil 120 } 121 122 func (c *OauthXRPCClient) CreateXCVRChannel(channel *lex.ChannelRecord, ctx context.Context) (uri string, cid string, err error) { ··· 163 return 164 } 165 166 + func (c *OauthXRPCClient) UpdateXCVRProfile(profile lex.ProfileRecord, ctx context.Context) (p *lex.ProfileRecord, err error) { 167 authargs, err := c.getOauthSessionAuthArgs() 168 if err != nil { 169 + err = errors.New("failed to get oauthsessionauthargs while making post: " + err.Error()) 170 + return 171 } 172 getOut, err := getProfileRecord(authargs.PdsUrl, authargs.Did, ctx) 173 if err != nil { 174 + err = errors.New("messed that up! " + err.Error()) 175 + return 176 } 177 if getOut.Cid == nil { 178 return c.CreateXCVRProfile(profile, ctx) ··· 188 var out atproto.RepoPutRecord_Output 189 err = c.xrpc.Do(ctx, authargs, "POST", "application/json", "com.atproto.repo.putRecord", nil, input, &out) 190 if err != nil { 191 + err = errors.New("oops! failed to update a profile: " + err.Error()) 192 + return 193 } 194 + return &profile, nil 195 } 196 197 func getProfileRecord(pdsUrl string, did string, ctx context.Context) (*atproto.RepoGetRecord_Output, error) {
+2 -2
server/internal/oauth/passwordclient.go
··· 8 "github.com/bluesky-social/indigo/atproto/client" 9 "github.com/bluesky-social/indigo/lex/util" 10 "os" 11 - "xcvr-backend/internal/lex" 12 - "xcvr-backend/internal/log" 13 ) 14 15 type PasswordClient struct {
··· 8 "github.com/bluesky-social/indigo/atproto/client" 9 "github.com/bluesky-social/indigo/lex/util" 10 "os" 11 + "rvcx/internal/lex" 12 + "rvcx/internal/log" 13 ) 14 15 type PasswordClient struct {
+2 -2
server/internal/oauth/service.go
··· 6 "errors" 7 "fmt" 8 "net/http" 9 "time" 10 - "xcvr-backend/internal/atputils" 11 - "xcvr-backend/internal/types" 12 13 atoauth "github.com/haileyok/atproto-oauth-golang" 14 "github.com/haileyok/atproto-oauth-golang/helpers"
··· 6 "errors" 7 "fmt" 8 "net/http" 9 + "rvcx/internal/atputils" 10 + "rvcx/internal/types" 11 "time" 12 13 atoauth "github.com/haileyok/atproto-oauth-golang" 14 "github.com/haileyok/atproto-oauth-golang/helpers"