From 492f7060ba54516a37428581d23c24b5039d3975 Mon Sep 17 00:00:00 2001 From: Anirudh Oppiliappan Date: Thu, 08 May 2025 08:00:59 +0000 Subject: [PATCH] appview: swap out old auth service for oauth Also does some driveby config refactoring. --- .air/appview.toml | 2 +- appview/config.go | 47 +++++++++++++++++++++++++++++++++++++---------- appview/consts.go | 3 +++ appview/db/db.go | 26 ++++++++++++++++++++++++++ appview/db/oauth.go | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ appview/middleware/middleware.go | 63 +++++---------------------------------------------------------- appview/oauth/oauth.go | 2 ++ appview/pages/pages.go | 78 +++++++++++++++++++++++++++++++++++++++++------------------------------------- appview/pages/templates/user/oauthlogin.html | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ appview/settings/settings.go | 45 +++++++++++++++++++++++++++------------------ appview/state/artifact.go | 29 +++++++++++++++++++---------- appview/state/follow.go | 12 ++++++++---- appview/state/git_http.go | 4 ++-- appview/state/middleware.go | 4 ++-- appview/state/profile.go | 29 +++++++++++++++++------------ appview/state/pull.go | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------- appview/state/repo.go | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------ appview/state/repo_util.go | 6 +++--- appview/state/router.go | 53 ++++++++++++++++++++++++++++++++++------------------- appview/state/star.go | 12 ++++++++---- appview/state/state.go | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------- appview/tid.go | 2 +- cmd/appview/main.go | 4 ++-- 23 file(s) changed, 754 insertion(s)(+), 379 deletion(s)(-) diff --git a/.air/appview.toml b/.air/appview.toml --- a/.air/appview.toml +++ b/.air/appview.toml @@ -1,6 +1,6 @@ [build] cmd = "tailwindcss -i input.css -o ./appview/pages/static/tw.css && go build -o .bin/app ./cmd/appview/main.go" -bin = ".bin/app" +bin = ";set -o allexport && source .env && set +o allexport; .bin/app" root = "." exclude_regex = [".*_templ.go"] diff --git a/appview/config.go b/appview/config.go --- a/appview/config.go +++ b/appview/config.go @@ -6,17 +6,44 @@ "github.com/sethvargo/go-envconfig" ) +type CoreConfig struct { + CookieSecret string `env:"COOKIE_SECRET, default=00000000000000000000000000000000"` + DbPath string `env:"DB_PATH, default=appview.db"` + ListenAddr string `env:"LISTEN_ADDR, default=0.0.0.0:3000"` + AppviewHost string `env:"APPVIEW_HOST, default=https://tangled.sh"` + Dev bool `env:"DEV, default=false"` +} + +type OAuthConfig struct { + Jwks string `env:"JWKS"` + ServerMetadataUrl string `env:"SERVER_METADATA_URL"` +} + +type JetstreamConfig struct { + Endpoint string `env:"ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"` +} + +type ResendConfig struct { + ApiKey string `env:"API_KEY"` +} + +type CamoConfig struct { + Host string `env:"HOST, default=https://camo.tangled.sh"` + SharedSecret string `env:"SHARED_SECRET"` +} + +type AvatarConfig struct { + Host string `env:"HOST, default=https://avatar.tangled.sh"` + SharedSecret string `env:"SHARED_SECRET"` +} + type Config struct { - CookieSecret string `env:"TANGLED_COOKIE_SECRET, default=00000000000000000000000000000000"` - DbPath string `env:"TANGLED_DB_PATH, default=appview.db"` - ListenAddr string `env:"TANGLED_LISTEN_ADDR, default=0.0.0.0:3000"` - Dev bool `env:"TANGLED_DEV, default=false"` - JetstreamEndpoint string `env:"TANGLED_JETSTREAM_ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"` - ResendApiKey string `env:"TANGLED_RESEND_API_KEY"` - CamoHost string `env:"TANGLED_CAMO_HOST, default=https://camo.tangled.sh"` - CamoSharedSecret string `env:"TANGLED_CAMO_SHARED_SECRET"` - AvatarSharedSecret string `env:"TANGLED_AVATAR_SHARED_SECRET"` - AvatarHost string `env:"TANGLED_AVATAR_HOST, default=https://avatar.tangled.sh"` + Core CoreConfig `env:",prefix=TANGLED_"` + Jetstream JetstreamConfig `env:",prefix=TANGLED_JETSTREAM_"` + Resend ResendConfig `env:",prefix=TANGLED_RESEND_"` + Camo CamoConfig `env:",prefix=TANGLED_CAMO_"` + Avatar AvatarConfig `env:",prefix=TANGLED_AVATAR_"` + OAuth OAuthConfig `env:",prefix=TANGLED_OAUTH_"` } func LoadConfig(ctx context.Context) (*Config, error) { diff --git a/appview/consts.go b/appview/consts.go --- a/appview/consts.go +++ b/appview/consts.go @@ -9,4 +9,7 @@ SessionAccessJwt = "accessJwt" SessionRefreshJwt = "refreshJwt" SessionExpiry = "expiry" SessionAuthenticated = "authenticated" + + SessionDpopPrivateJwk = "dpopPrivateJwk" + SessionDpopAuthServerNonce = "dpopAuthServerNonce" ) diff --git a/appview/db/db.go b/appview/db/db.go --- a/appview/db/db.go +++ b/appview/db/db.go @@ -288,6 +288,32 @@ foreign key (did) references profile(did) on delete cascade, foreign key (at_uri) references repos(at_uri) on delete cascade ); + create table if not exists oauth_requests ( + id integer primary key autoincrement, + auth_server_iss text not null, + state text not null, + did text not null, + handle text not null, + pds_url text not null, + pkce_verifier text not null, + dpop_auth_server_nonce text not null, + dpop_private_jwk text not null + ); + + create table if not exists oauth_sessions ( + id integer primary key autoincrement, + did text not null, + handle text not null, + pds_url text not null, + auth_server_iss text not null, + access_jwt text not null, + refresh_jwt text not null, + dpop_pds_nonce text, + dpop_auth_server_nonce text not null, + dpop_private_jwk text not null, + expiry text not null + ); + create table if not exists migrations ( id integer primary key autoincrement, name text unique diff --git a/appview/db/oauth.go b/appview/db/oauth.go new file mode 100644 --- /dev/null +++ b/appview/db/oauth.go @@ -0,0 +1,173 @@ +package db + +type OAuthRequest struct { + ID uint + AuthserverIss string + Handle string + State string + Did string + PdsUrl string + PkceVerifier string + DpopAuthserverNonce string + DpopPrivateJwk string +} + +func SaveOAuthRequest(e Execer, oauthRequest OAuthRequest) error { + _, err := e.Exec(` + insert into oauth_requests ( + auth_server_iss, + state, + handle, + did, + pds_url, + pkce_verifier, + dpop_auth_server_nonce, + dpop_private_jwk + ) values (?, ?, ?, ?, ?, ?, ?, ?)`, + oauthRequest.AuthserverIss, + oauthRequest.State, + oauthRequest.Handle, + oauthRequest.Did, + oauthRequest.PdsUrl, + oauthRequest.PkceVerifier, + oauthRequest.DpopAuthserverNonce, + oauthRequest.DpopPrivateJwk, + ) + return err +} + +func GetOAuthRequestByState(e Execer, state string) (OAuthRequest, error) { + var req OAuthRequest + err := e.QueryRow(` + select + id, + auth_server_iss, + handle, + state, + did, + pds_url, + pkce_verifier, + dpop_auth_server_nonce, + dpop_private_jwk + from oauth_requests + where state = ?`, state).Scan( + &req.ID, + &req.AuthserverIss, + &req.Handle, + &req.State, + &req.Did, + &req.PdsUrl, + &req.PkceVerifier, + &req.DpopAuthserverNonce, + &req.DpopPrivateJwk, + ) + return req, err +} + +func DeleteOAuthRequestByState(e Execer, state string) error { + _, err := e.Exec(` + delete from oauth_requests + where state = ?`, state) + return err +} + +type OAuthSession struct { + ID uint + Handle string + Did string + PdsUrl string + AccessJwt string + RefreshJwt string + AuthServerIss string + DpopPdsNonce string + DpopAuthserverNonce string + DpopPrivateJwk string + Expiry string +} + +func SaveOAuthSession(e Execer, session OAuthSession) error { + _, err := e.Exec(` + insert into oauth_sessions ( + did, + handle, + pds_url, + access_jwt, + refresh_jwt, + auth_server_iss, + dpop_auth_server_nonce, + dpop_private_jwk, + expiry + ) values (?, ?, ?, ?, ?, ?, ?, ?, ?)`, + session.Did, + session.Handle, + session.PdsUrl, + session.AccessJwt, + session.RefreshJwt, + session.AuthServerIss, + session.DpopAuthserverNonce, + session.DpopPrivateJwk, + session.Expiry, + ) + return err +} + +func RefreshOAuthSession(e Execer, did string, accessJwt, refreshJwt, expiry string) error { + _, err := e.Exec(` + update oauth_sessions + set access_jwt = ?, refresh_jwt = ?, expiry = ? + where did = ?`, + accessJwt, + refreshJwt, + expiry, + did, + ) + return err +} + +func GetOAuthSessionByDid(e Execer, did string) (*OAuthSession, error) { + var session OAuthSession + err := e.QueryRow(` + select + id, + did, + handle, + pds_url, + access_jwt, + refresh_jwt, + auth_server_iss, + dpop_auth_server_nonce, + dpop_private_jwk, + expiry + from oauth_sessions + where did = ?`, did).Scan( + &session.ID, + &session.Did, + &session.Handle, + &session.PdsUrl, + &session.AccessJwt, + &session.RefreshJwt, + &session.AuthServerIss, + &session.DpopAuthserverNonce, + &session.DpopPrivateJwk, + &session.Expiry, + ) + return &session, err +} + +func DeleteOAuthSessionByDid(e Execer, did string) error { + _, err := e.Exec(` + delete from oauth_sessions + where did = ?`, did) + return err +} + +func UpdateDpopPdsNonce(e Execer, did string, dpopPdsNonce string) error { + _, err := e.Exec(` + update oauth_sessions + set dpop_pds_nonce = ? + where did = ?`, + dpopPdsNonce, + did, + ) + return err +} diff --git a/appview/middleware/middleware.go b/appview/middleware/middleware.go --- a/appview/middleware/middleware.go +++ b/appview/middleware/middleware.go @@ -5,18 +5,14 @@ "context" "log" "net/http" "strconv" - "time" - comatproto "github.com/bluesky-social/indigo/api/atproto" - "github.com/bluesky-social/indigo/xrpc" - "tangled.sh/tangled.sh/core/appview" - "tangled.sh/tangled.sh/core/appview/auth" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pagination" ) type Middleware func(http.Handler) http.Handler -func AuthMiddleware(a *auth.Auth) Middleware { +func AuthMiddleware(a *oauth.OAuth) Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { redirectFunc := func(w http.ResponseWriter, r *http.Request) { @@ -29,66 +25,17 @@ w.WriteHeader(http.StatusOK) } } - session, err := a.GetSession(r) - if session.IsNew || err != nil { + _, auth, err := a.GetSession(r) + if err != nil { log.Printf("not logged in, redirecting") redirectFunc(w, r) return } - authorized, ok := session.Values[appview.SessionAuthenticated].(bool) - if !ok || !authorized { + if !auth { log.Printf("not logged in, redirecting") redirectFunc(w, r) return - } - - // refresh if nearing expiry - // TODO: dedup with /login - expiryStr := session.Values[appview.SessionExpiry].(string) - expiry, err := time.Parse(time.RFC3339, expiryStr) - if err != nil { - log.Println("invalid expiry time", err) - redirectFunc(w, r) - return - } - pdsUrl, ok1 := session.Values[appview.SessionPds].(string) - did, ok2 := session.Values[appview.SessionDid].(string) - refreshJwt, ok3 := session.Values[appview.SessionRefreshJwt].(string) - - if !ok1 || !ok2 || !ok3 { - log.Println("invalid expiry time", err) - redirectFunc(w, r) - return - } - - if time.Now().After(expiry) { - log.Println("token expired, refreshing ...") - - client := xrpc.Client{ - Host: pdsUrl, - Auth: &xrpc.AuthInfo{ - Did: did, - AccessJwt: refreshJwt, - RefreshJwt: refreshJwt, - }, - } - atSession, err := comatproto.ServerRefreshSession(r.Context(), &client) - if err != nil { - log.Println("failed to refresh session", err) - redirectFunc(w, r) - return - } - - sessionish := auth.RefreshSessionWrapper{atSession} - - err = a.StoreSession(r, w, &sessionish, pdsUrl) - if err != nil { - log.Printf("failed to store session for did: %s\n: %s", atSession.Did, err) - return - } - - log.Println("successfully refreshed token") } next.ServeHTTP(w, r) diff --git a/appview/oauth/oauth.go b/appview/oauth/oauth.go --- a/appview/oauth/oauth.go +++ b/appview/oauth/oauth.go @@ -48,6 +48,8 @@ return err } userSession.Values[appview.SessionDid] = oreq.Did + userSession.Values[appview.SessionHandle] = oreq.Handle + userSession.Values[appview.SessionPds] = oreq.PdsUrl userSession.Values[appview.SessionAuthenticated] = true err = userSession.Save(r, w) if err != nil { diff --git a/appview/pages/pages.go b/appview/pages/pages.go --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -16,8 +16,8 @@ "path/filepath" "strings" "tangled.sh/tangled.sh/core/appview" - "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pages/markup" "tangled.sh/tangled.sh/core/appview/pages/repoinfo" "tangled.sh/tangled.sh/core/appview/pagination" @@ -48,14 +48,14 @@ func NewPages(config *appview.Config) *Pages { // initialized with safe defaults, can be overriden per use rctx := &markup.RenderContext{ - IsDev: config.Dev, - CamoUrl: config.CamoHost, - CamoSecret: config.CamoSharedSecret, + IsDev: config.Core.Dev, + CamoUrl: config.Camo.Host, + CamoSecret: config.Camo.SharedSecret, } p := &Pages{ t: make(map[string]*template.Template), - dev: config.Dev, + dev: config.Core.Dev, embedFS: Files, rctx: rctx, templateDir: "appview/pages", @@ -249,8 +249,12 @@ func (p *Pages) Login(w io.Writer, params LoginParams) error { return p.executePlain("user/login", w, params) } +func (p *Pages) OAuthLogin(w io.Writer, params LoginParams) error { + return p.executePlain("user/oauthlogin", w, params) +} + type TimelineParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Timeline []db.TimelineEvent DidHandleMap map[string]string } @@ -260,7 +264,7 @@ return p.execute("timeline", w, params) } type SettingsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User PubKeys []db.PublicKey Emails []db.Email } @@ -270,7 +274,7 @@ return p.execute("settings", w, params) } type KnotsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Registrations []db.Registration } @@ -279,7 +283,7 @@ return p.execute("knots", w, params) } type KnotParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User DidHandleMap map[string]string Registration *db.Registration Members []string @@ -291,7 +295,7 @@ return p.execute("knot", w, params) } type NewRepoParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Knots []string } @@ -300,7 +304,7 @@ return p.execute("repo/new", w, params) } type ForkRepoParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Knots []string RepoInfo repoinfo.RepoInfo } @@ -310,7 +314,7 @@ return p.execute("repo/fork", w, params) } type ProfilePageParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Repos []db.Repo CollaboratingRepos []db.Repo ProfileTimeline *db.ProfileTimeline @@ -335,7 +339,7 @@ return p.execute("user/profile", w, params) } type ReposPageParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Repos []db.Repo Card ProfileCard @@ -356,7 +360,7 @@ return p.executePlain("user/fragments/follow", w, params) } type EditBioParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Profile *db.Profile } @@ -365,7 +369,7 @@ return p.executePlain("user/fragments/editBio", w, params) } type EditPinsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User Profile *db.Profile AllRepos []PinnedRepo DidHandleMap map[string]string @@ -403,7 +407,7 @@ return p.executePlain("repo/fragments/repoDescription", w, params) } type RepoIndexParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string TagMap map[string][]string @@ -444,7 +448,7 @@ return p.executeRepo("repo/index", w, params) } type RepoLogParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo TagMap map[string][]string types.RepoLogResponse @@ -458,7 +462,7 @@ return p.executeRepo("repo/log", w, params) } type RepoCommitParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string EmailToDidOrHandle map[string]string @@ -472,7 +476,7 @@ return p.executeRepo("repo/commit", w, params) } type RepoTreeParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string BreadCrumbs [][]string @@ -508,7 +512,7 @@ return p.execute("repo/tree", w, params) } type RepoBranchesParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string types.RepoBranchesResponse @@ -520,7 +524,7 @@ return p.executeRepo("repo/branches", w, params) } type RepoTagsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string types.RepoTagsResponse @@ -534,7 +538,7 @@ return p.executeRepo("repo/tags", w, params) } type RepoArtifactParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Artifact db.Artifact } @@ -544,7 +548,7 @@ return p.executePlain("repo/fragments/artifact", w, params) } type RepoBlobParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string BreadCrumbs [][]string @@ -606,7 +610,7 @@ Role string } type RepoSettingsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Collaborators []Collaborator Active string @@ -622,7 +626,7 @@ return p.executeRepo("repo/settings", w, params) } type RepoIssuesParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string Issues []db.Issue @@ -637,7 +641,7 @@ return p.executeRepo("repo/issues/issues", w, params) } type RepoSingleIssueParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string Issue db.Issue @@ -659,7 +663,7 @@ return p.execute("repo/issues/issue", w, params) } type RepoNewIssueParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string } @@ -670,7 +674,7 @@ return p.executeRepo("repo/issues/new", w, params) } type EditIssueCommentParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Issue *db.Issue Comment *db.Comment @@ -681,7 +685,7 @@ return p.executePlain("repo/issues/fragments/editIssueComment", w, params) } type SingleIssueCommentParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User DidHandleMap map[string]string RepoInfo repoinfo.RepoInfo Issue *db.Issue @@ -693,7 +697,7 @@ return p.executePlain("repo/issues/fragments/issueComment", w, params) } type RepoNewPullParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Branches []types.Branch Active string @@ -705,7 +709,7 @@ return p.executeRepo("repo/pulls/new", w, params) } type RepoPullsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Pulls []*db.Pull Active string @@ -737,7 +741,7 @@ return r == Unknown } type RepoSinglePullParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Active string DidHandleMap map[string]string @@ -752,7 +756,7 @@ return p.executeRepo("repo/pulls/pull", w, params) } type RepoPullPatchParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User DidHandleMap map[string]string RepoInfo repoinfo.RepoInfo Pull *db.Pull @@ -767,7 +771,7 @@ return p.execute("repo/pulls/patch", w, params) } type RepoPullInterdiffParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User DidHandleMap map[string]string RepoInfo repoinfo.RepoInfo Pull *db.Pull @@ -817,7 +821,7 @@ return p.executePlain("repo/pulls/fragments/pullCompareForksBranches", w, params) } type PullResubmitParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Pull *db.Pull SubmissionId int @@ -828,7 +832,7 @@ return p.executePlain("repo/pulls/fragments/pullResubmit", w, params) } type PullActionsParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Pull *db.Pull RoundNumber int @@ -841,7 +845,7 @@ return p.executePlain("repo/pulls/fragments/pullActions", w, params) } type PullNewCommentParams struct { - LoggedInUser *auth.User + LoggedInUser *oauth.User RepoInfo repoinfo.RepoInfo Pull *db.Pull RoundNumber int diff --git a/appview/pages/templates/user/oauthlogin.html b/appview/pages/templates/user/oauthlogin.html new file mode 100644 --- /dev/null +++ b/appview/pages/templates/user/oauthlogin.html @@ -0,0 +1,71 @@ +{{ define "user/oauthlogin" }} + + + + + + + + login + + +
+

+ tangled +

+

+ tightly-knit social coding. +

+
+
+ + + + Use your + Bluesky handle to log + in. You will then be redirected to your PDS to + complete authentication. + +
+ + +
+

+ Join our Discord or + IRC channel: + #tangled on Libera Chat. +

+

+
+ + +{{ end }} diff --git a/appview/settings/settings.go b/appview/settings/settings.go --- a/appview/settings/settings.go +++ b/appview/settings/settings.go @@ -13,10 +13,10 @@ "github.com/go-chi/chi/v5" "tangled.sh/tangled.sh/core/api/tangled" "tangled.sh/tangled.sh/core/appview" - "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" "tangled.sh/tangled.sh/core/appview/email" "tangled.sh/tangled.sh/core/appview/middleware" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pages" comatproto "github.com/bluesky-social/indigo/api/atproto" @@ -27,7 +27,7 @@ ) type Settings struct { Db *db.DB - Auth *auth.Auth + OAuth *oauth.OAuth Pages *pages.Pages Config *appview.Config } @@ -35,7 +35,7 @@ func (s *Settings) Router() http.Handler { r := chi.NewRouter() - r.Use(middleware.AuthMiddleware(s.Auth)) + r.Use(middleware.AuthMiddleware(s.OAuth)) r.Get("/", s.settings) @@ -56,7 +56,7 @@ return r } func (s *Settings) settings(w http.ResponseWriter, r *http.Request) { - user := s.Auth.GetUser(r) + user := s.OAuth.GetUser(r) pubKeys, err := db.GetPublicKeys(s.Db, user.Did) if err != nil { log.Println(err) @@ -79,7 +79,7 @@ func (s *Settings) buildVerificationEmail(emailAddr, did, code string) email.Email { verifyURL := s.verifyUrl(did, emailAddr, code) return email.Email{ - APIKey: s.Config.ResendApiKey, + APIKey: s.Config.Resend.ApiKey, From: "noreply@notifs.tangled.sh", To: emailAddr, Subject: "Verify your Tangled email", @@ -111,7 +111,7 @@ s.Pages.Notice(w, "settings-emails", "Unimplemented.") log.Println("unimplemented") return case http.MethodPut: - did := s.Auth.GetDid(r) + did := s.OAuth.GetDid(r) emAddr := r.FormValue("email") emAddr = strings.TrimSpace(emAddr) @@ -174,7 +174,7 @@ s.Pages.Notice(w, "settings-emails-success", "Click the link in the email we sent you to verify your email address.") return case http.MethodDelete: - did := s.Auth.GetDid(r) + did := s.OAuth.GetDid(r) emailAddr := r.FormValue("email") emailAddr = strings.TrimSpace(emailAddr) @@ -207,8 +207,8 @@ } func (s *Settings) verifyUrl(did string, email string, code string) string { var appUrl string - if s.Config.Dev { - appUrl = "http://" + s.Config.ListenAddr + if s.Config.Core.Dev { + appUrl = "http://" + s.Config.Core.ListenAddr } else { appUrl = "https://tangled.sh" } @@ -252,7 +252,7 @@ s.Pages.Notice(w, "settings-emails-error", "Invalid request method.") return } - did := s.Auth.GetDid(r) + did := s.OAuth.GetDid(r) emAddr := r.FormValue("email") emAddr = strings.TrimSpace(emAddr) @@ -323,7 +323,7 @@ s.Pages.Notice(w, "settings-emails-success", "Verification email resent. Click the link in the email we sent you to verify your email address.") } func (s *Settings) emailsPrimary(w http.ResponseWriter, r *http.Request) { - did := s.Auth.GetDid(r) + did := s.OAuth.GetDid(r) emailAddr := r.FormValue("email") emailAddr = strings.TrimSpace(emailAddr) @@ -348,13 +348,17 @@ s.Pages.Notice(w, "settings-keys", "Unimplemented.") log.Println("unimplemented") return case http.MethodPut: - did := s.Auth.GetDid(r) + did := s.OAuth.GetDid(r) key := r.FormValue("key") key = strings.TrimSpace(key) name := r.FormValue("name") - client, _ := s.Auth.AuthorizedClient(r) + client, err := s.OAuth.AuthorizedClient(r) + if err != nil { + s.Pages.Notice(w, "settings-keys", "Failed to authorize. Try again later.") + return + } - _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key)) + _, _, _, _, err = ssh.ParseAuthorizedKey([]byte(key)) if err != nil { log.Printf("parsing public key: %s", err) s.Pages.Notice(w, "settings-keys", "That doesn't look like a valid public key. Make sure it's a public key.") @@ -378,7 +382,7 @@ return } // store in pds too - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.PublicKeyNSID, Repo: did, Rkey: rkey, @@ -409,7 +413,7 @@ s.Pages.HxLocation(w, "/settings") return case http.MethodDelete: - did := s.Auth.GetDid(r) + did := s.OAuth.GetDid(r) q := r.URL.Query() name := q.Get("name") @@ -420,7 +424,12 @@ log.Println(name) log.Println(rkey) log.Println(key) - client, _ := s.Auth.AuthorizedClient(r) + client, err := s.OAuth.AuthorizedClient(r) + if err != nil { + log.Printf("failed to authorize client: %s", err) + s.Pages.Notice(w, "settings-keys", "Failed to authorize client.") + return + } if err := db.DeletePublicKey(s.Db, did, name, key); err != nil { log.Printf("removing public key: %s", err) @@ -430,7 +439,7 @@ } if rkey != "" { // remove from pds too - _, err := comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ + _, err := client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ Collection: tangled.PublicKeyNSID, Repo: did, Rkey: rkey, diff --git a/appview/state/artifact.go b/appview/state/artifact.go --- a/appview/state/artifact.go +++ b/appview/state/artifact.go @@ -22,7 +22,7 @@ ) // TODO: proper statuses here on early exit func (s *State) AttachArtifact(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) tagParam := chi.URLParam(r, "tag") f, err := s.fullyResolvedRepo(r) if err != nil { @@ -46,9 +46,14 @@ return } defer file.Close() - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "upload", "failed to get authorized client") + return + } - uploadBlobResp, err := comatproto.RepoUploadBlob(r.Context(), client, file) + uploadBlobResp, err := client.RepoUploadBlob(r.Context(), file) if err != nil { log.Println("failed to upload blob", err) s.pages.Notice(w, "upload", "Failed to upload blob to your PDS. Try again later.") @@ -60,7 +65,7 @@ rkey := appview.TID() createdAt := time.Now() - putRecordResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + putRecordResp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoArtifactNSID, Repo: user.Did, Rkey: rkey, @@ -140,7 +145,11 @@ s.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + return + } artifacts, err := db.GetArtifact( s.db, @@ -159,7 +168,7 @@ } artifact := artifacts[0] - getBlobResp, err := comatproto.SyncGetBlob(r.Context(), client, artifact.BlobCid.String(), artifact.Did) + getBlobResp, err := client.SyncGetBlob(r.Context(), artifact.BlobCid.String(), artifact.Did) if err != nil { log.Println("failed to get blob from pds", err) return @@ -171,7 +180,7 @@ } // TODO: proper statuses here on early exit func (s *State) DeleteArtifact(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) tagParam := chi.URLParam(r, "tag") filename := chi.URLParam(r, "file") f, err := s.fullyResolvedRepo(r) @@ -180,7 +189,7 @@ log.Println("failed to get repo and knot", err) return } - client, _ := s.auth.AuthorizedClient(r) + client, _ := s.oauth.AuthorizedClient(r) tag := plumbing.NewHash(tagParam) @@ -208,7 +217,7 @@ s.pages.Notice(w, "remove", "Unauthorized deletion of artifact.") return } - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ + _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ Collection: tangled.RepoArtifactNSID, Repo: user.Did, Rkey: artifact.Rkey, @@ -254,7 +263,7 @@ if err != nil { return nil, err } - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { return nil, err } diff --git a/appview/state/follow.go b/appview/state/follow.go --- a/appview/state/follow.go +++ b/appview/state/follow.go @@ -14,7 +14,7 @@ "tangled.sh/tangled.sh/core/appview/pages" ) func (s *State) Follow(w http.ResponseWriter, r *http.Request) { - currentUser := s.auth.GetUser(r) + currentUser := s.oauth.GetUser(r) subject := r.URL.Query().Get("subject") if subject == "" { @@ -32,13 +32,17 @@ log.Println("cant follow or unfollow yourself") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to authorize client") + return + } switch r.Method { case http.MethodPost: createdAt := time.Now().Format(time.RFC3339) rkey := appview.TID() - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.GraphFollowNSID, Repo: currentUser.Did, Rkey: rkey, @@ -75,7 +79,7 @@ log.Println("failed to get follow relationship") return } - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ + _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ Collection: tangled.GraphFollowNSID, Repo: currentUser.Did, Rkey: follow.Rkey, diff --git a/appview/state/git_http.go b/appview/state/git_http.go --- a/appview/state/git_http.go +++ b/appview/state/git_http.go @@ -15,7 +15,7 @@ knot := r.Context().Value("knot").(string) repo := chi.URLParam(r, "repo") scheme := "https" - if s.config.Dev { + if s.config.Core.Dev { scheme = "http" } targetURL := fmt.Sprintf("%s://%s/%s/%s/info/refs?%s", scheme, knot, user.DID, repo, r.URL.RawQuery) @@ -52,7 +52,7 @@ knot := r.Context().Value("knot").(string) repo := chi.URLParam(r, "repo") scheme := "https" - if s.config.Dev { + if s.config.Core.Dev { scheme = "http" } targetURL := fmt.Sprintf("%s://%s/%s/%s/git-upload-pack?%s", scheme, knot, user.DID, repo, r.URL.RawQuery) diff --git a/appview/state/middleware.go b/appview/state/middleware.go --- a/appview/state/middleware.go +++ b/appview/state/middleware.go @@ -20,7 +20,7 @@ func knotRoleMiddleware(s *State, group string) middleware.Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // requires auth also - actor := s.auth.GetUser(r) + actor := s.oauth.GetUser(r) if actor == nil { // we need a logged in user log.Printf("not logged in, redirecting") @@ -54,7 +54,7 @@ func RepoPermissionMiddleware(s *State, requiredPerm string) middleware.Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // requires auth also - actor := s.auth.GetUser(r) + actor := s.oauth.GetUser(r) if actor == nil { // we need a logged in user log.Printf("not logged in, redirecting") diff --git a/appview/state/profile.go b/appview/state/profile.go --- a/appview/state/profile.go +++ b/appview/state/profile.go @@ -119,7 +119,7 @@ if err != nil { log.Printf("getting follow stats repos for %s: %s", ident.DID.String(), err) } - loggedInUser := s.auth.GetUser(r) + loggedInUser := s.oauth.GetUser(r) followStatus := db.IsNotFollowing if loggedInUser != nil { followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String()) @@ -161,7 +161,7 @@ if err != nil { log.Printf("getting repos for %s: %s", ident.DID.String(), err) } - loggedInUser := s.auth.GetUser(r) + loggedInUser := s.oauth.GetUser(r) followStatus := db.IsNotFollowing if loggedInUser != nil { followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String()) @@ -190,15 +190,15 @@ }) } func (s *State) GetAvatarUri(handle string) string { - secret := s.config.AvatarSharedSecret + secret := s.config.Avatar.SharedSecret h := hmac.New(sha256.New, []byte(secret)) h.Write([]byte(handle)) signature := hex.EncodeToString(h.Sum(nil)) - return fmt.Sprintf("%s/%s/%s", s.config.AvatarHost, signature, handle) + return fmt.Sprintf("%s/%s/%s", s.config.Avatar.Host, signature, handle) } func (s *State) UpdateProfileBio(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) err := r.ParseForm() if err != nil { @@ -246,7 +246,7 @@ return } func (s *State) UpdateProfilePins(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) err := r.ParseForm() if err != nil { @@ -286,7 +286,7 @@ return } func (s *State) updateProfile(profile *db.Profile, w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) tx, err := s.db.BeginTx(r.Context(), nil) if err != nil { log.Println("failed to start transaction", err) @@ -294,7 +294,12 @@ s.pages.Notice(w, "update-profile", "Failed to update profile, try again later.") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "update-profile", "Failed to update profile, try again later.") + return + } // yeah... lexgen dose not support syntax.ATURI in the record for some reason, // nor does it support exact size arrays @@ -308,13 +313,13 @@ for _, v := range profile.Stats { vanityStats = append(vanityStats, string(v.Kind)) } - ex, _ := comatproto.RepoGetRecord(r.Context(), client, "", tangled.ActorProfileNSID, user.Did, "self") + ex, _ := client.RepoGetRecord(r.Context(), "", tangled.ActorProfileNSID, user.Did, "self") var cid *string if ex != nil { cid = ex.Cid } - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.ActorProfileNSID, Repo: user.Did, Rkey: "self", @@ -347,7 +352,7 @@ return } func (s *State) EditBioFragment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) profile, err := db.GetProfile(s.db, user.Did) if err != nil { @@ -361,7 +366,7 @@ }) } func (s *State) EditPinsFragment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) profile, err := db.GetProfile(s.db, user.Did) if err != nil { diff --git a/appview/state/pull.go b/appview/state/pull.go --- a/appview/state/pull.go +++ b/appview/state/pull.go @@ -13,8 +13,8 @@ "time" "tangled.sh/tangled.sh/core/api/tangled" "tangled.sh/tangled.sh/core/appview" - "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pages" "tangled.sh/tangled.sh/core/patchutil" "tangled.sh/tangled.sh/core/types" @@ -29,7 +29,7 @@ // htmx fragment func (s *State) PullActions(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -73,7 +73,7 @@ } } func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -143,7 +143,7 @@ Error: "failed to check merge status: this knot is unregistered", } } - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) if err != nil { log.Printf("failed to setup signed client for %s; ignoring: %v", f.Knot, err) return types.MergeCheckResponse{ @@ -215,7 +215,7 @@ ownerDid = f.OwnerDid() repoName = f.RepoName } - us, err := NewUnsignedClient(knot, s.config.Dev) + us, err := NewUnsignedClient(knot, s.config.Core.Dev) if err != nil { log.Printf("failed to setup client for %s; ignoring: %v", knot, err) return pages.Unknown @@ -250,7 +250,7 @@ return pages.ShouldNotResubmit } func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -298,7 +298,7 @@ } func (s *State) RepoPullInterdiff(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { @@ -355,7 +355,7 @@ interdiff := patchutil.Interdiff(previousPatch, currentPatch) s.pages.RepoPullInterdiffPage(w, pages.RepoPullInterdiffParams{ - LoggedInUser: s.auth.GetUser(r), + LoggedInUser: s.oauth.GetUser(r), RepoInfo: f.RepoInfo(s, user), Pull: pull, Round: roundIdInt, @@ -397,7 +397,7 @@ w.Write([]byte(pull.Submissions[roundIdInt].Patch)) } func (s *State) RepoPulls(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) params := r.URL.Query() state := db.PullOpen @@ -451,7 +451,7 @@ } } s.pages.RepoPulls(w, pages.RepoPullsParams{ - LoggedInUser: s.auth.GetUser(r), + LoggedInUser: s.oauth.GetUser(r), RepoInfo: f.RepoInfo(s, user), Pulls: pulls, DidHandleMap: didHandleMap, @@ -461,7 +461,7 @@ return } func (s *State) PullComment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -519,8 +519,13 @@ return } atUri := f.RepoAt.String() - client, _ := s.auth.AuthorizedClient(r) - atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "pull-comment", "Failed to create comment.") + return + } + atResp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoPullCommentNSID, Repo: user.Did, Rkey: appview.TID(), @@ -568,7 +573,7 @@ } } func (s *State) NewPull(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -577,7 +582,7 @@ } switch r.Method { case http.MethodGet: - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client for %s", f.Knot) s.pages.Error503(w) @@ -646,7 +651,7 @@ s.pages.Notice(w, "pull", "Cannot select both patch and source branch.") return } - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client to %s: %v", f.Knot, err) s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.") @@ -689,7 +694,7 @@ return } } -func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, sourceBranch string) { +func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, sourceBranch string) { pullSource := &db.PullSource{ Branch: sourceBranch, } @@ -698,7 +703,7 @@ Branch: sourceBranch, } // Generate a patch using /compare - ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev) + ksClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create signed client for %s: %s", f.Knot, err) s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") @@ -723,7 +728,7 @@ s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource) } -func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch string) { +func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, patch string) { if !patchutil.IsPatchValid(patch) { s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") return @@ -732,7 +737,7 @@ s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil) } -func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, forkRepo string, title, body, targetBranch, sourceBranch string) { +func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, forkRepo string, title, body, targetBranch, sourceBranch string) { fork, err := db.GetForkByDid(s.db, user.Did, forkRepo) if errors.Is(err, sql.ErrNoRows) { s.pages.Notice(w, "pull", "No such fork.") @@ -750,14 +755,14 @@ s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") return } - sc, err := NewSignedClient(fork.Knot, secret, s.config.Dev) + sc, err := NewSignedClient(fork.Knot, secret, s.config.Core.Dev) if err != nil { log.Println("failed to create signed client:", err) s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") return } - us, err := NewUnsignedClient(fork.Knot, s.config.Dev) + us, err := NewUnsignedClient(fork.Knot, s.config.Core.Dev) if err != nil { log.Println("failed to create unsigned client:", err) s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") @@ -816,7 +821,7 @@ func (s *State) createPullRequest( w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, - user *auth.User, + user *oauth.User, title, body, targetBranch string, patch string, sourceRev string, @@ -870,7 +875,12 @@ log.Println("failed to create pull request", err) s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") + return + } pullId, err := db.NextPullId(s.db, f.RepoAt) if err != nil { log.Println("failed to get pull id", err) @@ -878,7 +888,7 @@ s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") return } - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoPullNSID, Repo: user.Did, Rkey: rkey, @@ -929,7 +939,7 @@ } } func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -942,14 +952,14 @@ }) } func (s *State) CompareBranchesFragment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) return } - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client for %s", f.Knot) s.pages.Error503(w) @@ -982,7 +992,7 @@ }) } func (s *State) CompareForksFragment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1002,7 +1012,7 @@ }) } func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { @@ -1019,7 +1029,7 @@ log.Println("failed to get repo", user.Did, forkVal) return } - sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Dev) + sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client for %s", repo.Knot) s.pages.Error503(w) @@ -1046,7 +1056,7 @@ log.Println("failed to parse source branches response:", err) return } - targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Dev) + targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client for target knot %s", f.Knot) s.pages.Error503(w) @@ -1081,7 +1091,7 @@ }) } func (s *State) ResubmitPull(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1117,7 +1127,7 @@ } } func (s *State) resubmitPatch(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) pull, ok := r.Context().Value("pull").(*db.Pull) if !ok { @@ -1159,16 +1169,21 @@ log.Println("failed to resubmit pull request", err) s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull request. Try again later.") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") + return + } - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey) if err != nil { // failed to get record s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.") return } - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoPullNSID, Repo: user.Did, Rkey: pull.Rkey, @@ -1200,7 +1215,7 @@ return } func (s *State) resubmitBranch(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) pull, ok := r.Context().Value("pull").(*db.Pull) if !ok { @@ -1227,7 +1242,7 @@ w.WriteHeader(http.StatusUnauthorized) return } - ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev) + ksClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create client for %s: %s", f.Knot, err) s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") @@ -1268,9 +1283,14 @@ log.Println("failed to create pull request", err) s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to authorize client") + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") + return + } - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey) if err != nil { // failed to get record s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.") @@ -1280,7 +1300,7 @@ recordPullSource := &tangled.RepoPull_Source{ Branch: pull.PullSource.Branch, } - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoPullNSID, Repo: user.Did, Rkey: pull.Rkey, @@ -1313,7 +1333,7 @@ return } func (s *State) resubmitFork(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) pull, ok := r.Context().Value("pull").(*db.Pull) if !ok { @@ -1342,7 +1362,7 @@ return } // extract patch by performing compare - ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Dev) + ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create client for %s: %s", forkRepo.Knot, err) s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") @@ -1357,7 +1377,7 @@ return } // update the hidden tracking branch to latest - signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Dev) + signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Core.Dev) if err != nil { log.Printf("failed to create signed client for %s: %s", forkRepo.Knot, err) s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") @@ -1406,9 +1426,14 @@ log.Println("failed to create pull request", err) s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get client") + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") + return + } - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey) if err != nil { // failed to get record s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.") @@ -1420,7 +1445,7 @@ recordPullSource := &tangled.RepoPull_Source{ Branch: pull.PullSource.Branch, Repo: &repoAt, } - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoPullNSID, Repo: user.Did, Rkey: pull.Rkey, @@ -1503,7 +1528,7 @@ if err != nil { log.Printf("failed to get primary email: %s", err) } - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) if err != nil { log.Printf("failed to create signed client for %s: %s", f.Knot, err) s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") @@ -1533,7 +1558,7 @@ } } func (s *State) ClosePull(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { @@ -1587,7 +1612,7 @@ return } func (s *State) ReopenPull(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { diff --git a/appview/state/repo.go b/appview/state/repo.go --- a/appview/state/repo.go +++ b/appview/state/repo.go @@ -18,8 +18,8 @@ "time" "tangled.sh/tangled.sh/core/api/tangled" "tangled.sh/tangled.sh/core/appview" - "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pages" "tangled.sh/tangled.sh/core/appview/pages/markup" "tangled.sh/tangled.sh/core/appview/pages/repoinfo" @@ -45,7 +45,7 @@ log.Println("failed to fully resolve repo", err) return } - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client for %s", f.Knot) s.pages.Error503(w) @@ -119,7 +119,7 @@ branchesTrunc := result.Branches[:min(branchCount, len(result.Branches))] emails := uniqueEmails(commitsTrunc) - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.RepoIndexPage(w, pages.RepoIndexParams{ LoggedInUser: user, RepoInfo: f.RepoInfo(s, user), @@ -150,7 +150,7 @@ } ref := chi.URLParam(r, "ref") - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Println("failed to create unsigned client", err) return @@ -190,7 +190,7 @@ } tagMap[hash] = append(tagMap[hash], tag.Name) } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.RepoLog(w, pages.RepoLogParams{ LoggedInUser: user, TagMap: tagMap, @@ -209,7 +209,7 @@ w.WriteHeader(http.StatusBadRequest) return } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.EditRepoDescriptionFragment(w, pages.RepoDescriptionParams{ RepoInfo: f.RepoInfo(s, user), }) @@ -232,7 +232,7 @@ w.WriteHeader(http.StatusInternalServerError) return } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) switch r.Method { case http.MethodGet: @@ -241,9 +241,14 @@ RepoInfo: f.RepoInfo(s, user), }) return case http.MethodPut: - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) newDescription := r.FormValue("description") - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get client") + s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") + return + } // optimistic update err = db.UpdateDescription(s.db, string(repoAt), newDescription) @@ -256,13 +261,13 @@ // this is a bit of a pain because the golang atproto impl does not allow nil SwapRecord field // // SwapRecord is optional and should happen automagically, but given that it does not, we have to perform two requests - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoNSID, user.Did, rkey) + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoNSID, user.Did, rkey) if err != nil { // failed to get record s.pages.Notice(w, "repo-notice", "Failed to update description, no record found on PDS.") return } - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoNSID, Repo: user.Did, Rkey: rkey, @@ -303,7 +308,7 @@ return } ref := chi.URLParam(r, "ref") protocol := "http" - if !s.config.Dev { + if !s.config.Core.Dev { protocol = "https" } @@ -331,7 +336,7 @@ log.Println("failed to parse response:", err) return } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.RepoCommit(w, pages.RepoCommitParams{ LoggedInUser: user, RepoInfo: f.RepoInfo(s, user), @@ -351,7 +356,7 @@ ref := chi.URLParam(r, "ref") treePath := chi.URLParam(r, "*") protocol := "http" - if !s.config.Dev { + if !s.config.Core.Dev { protocol = "https" } resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/tree/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, treePath)) @@ -380,7 +385,7 @@ http.Redirect(w, r, fmt.Sprintf("/%s/blob/%s/%s", f.OwnerSlashRepo(), ref, result.Parent), http.StatusFound) return } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) var breadcrumbs [][]string breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), ref)}) @@ -411,7 +416,7 @@ log.Println("failed to get repo and knot", err) return } - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Println("failed to create unsigned client", err) return @@ -451,7 +456,7 @@ danglingArtifacts = append(danglingArtifacts, a) } } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.RepoTags(w, pages.RepoTagsParams{ LoggedInUser: user, RepoInfo: f.RepoInfo(s, user), @@ -469,7 +474,7 @@ log.Println("failed to get repo and knot", err) return } - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Println("failed to create unsigned client", err) return @@ -511,7 +516,7 @@ } return strings.Compare(a.Name, b.Name) * -1 }) - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.RepoBranches(w, pages.RepoBranchesParams{ LoggedInUser: user, RepoInfo: f.RepoInfo(s, user), @@ -530,7 +535,7 @@ ref := chi.URLParam(r, "ref") filePath := chi.URLParam(r, "*") protocol := "http" - if !s.config.Dev { + if !s.config.Core.Dev { protocol = "https" } resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath)) @@ -568,7 +573,7 @@ renderToggle = true showRendered = r.URL.Query().Get("code") != "true" } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) s.pages.RepoBlob(w, pages.RepoBlobParams{ LoggedInUser: user, RepoInfo: f.RepoInfo(s, user), @@ -591,7 +596,7 @@ ref := chi.URLParam(r, "ref") filePath := chi.URLParam(r, "*") protocol := "http" - if !s.config.Dev { + if !s.config.Core.Dev { protocol = "https" } resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath)) @@ -652,7 +657,7 @@ log.Printf("no key found for domain %s: %s\n", f.Knot, err) return } - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) if err != nil { log.Println("failed to create client to ", f.Knot) return @@ -714,7 +719,7 @@ } func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { @@ -723,9 +728,13 @@ return } // remove record from pds - xrpcClient, _ := s.auth.AuthorizedClient(r) + xrpcClient, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + return + } repoRkey := f.RepoAt.RecordKey().String() - _, err = comatproto.RepoDeleteRecord(r.Context(), xrpcClient, &comatproto.RepoDeleteRecord_Input{ + _, err = xrpcClient.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ Collection: tangled.RepoNSID, Repo: user.Did, Rkey: repoRkey, @@ -743,7 +752,7 @@ log.Printf("no key found for domain %s: %s\n", f.Knot, err) return } - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) if err != nil { log.Println("failed to create client to ", f.Knot) return @@ -838,7 +847,7 @@ log.Printf("no key found for domain %s: %s\n", f.Knot, err) return } - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) if err != nil { log.Println("failed to create client to ", f.Knot) return @@ -868,7 +877,7 @@ switch r.Method { case http.MethodGet: // for now, this is just pubkeys - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) repoCollaborators, err := f.Collaborators(r.Context(), s) if err != nil { log.Println("failed to get collaborators", err) @@ -884,7 +893,7 @@ } var branchNames []string var defaultBranch string - us, err := NewUnsignedClient(f.Knot, s.config.Dev) + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) if err != nil { log.Println("failed to create unsigned client", err) } else { @@ -1008,7 +1017,7 @@ return collaborators, nil } -func (f *FullyResolvedRepo) RepoInfo(s *State, u *auth.User) repoinfo.RepoInfo { +func (f *FullyResolvedRepo) RepoInfo(s *State, u *oauth.User) repoinfo.RepoInfo { isStarred := false if u != nil { isStarred = db.GetStarStatus(s.db, u.Did, syntax.ATURI(f.RepoAt)) @@ -1051,7 +1060,7 @@ } knot := f.Knot var disableFork bool - us, err := NewUnsignedClient(knot, s.config.Dev) + us, err := NewUnsignedClient(knot, s.config.Core.Dev) if err != nil { log.Printf("failed to create unsigned client for %s: %v", knot, err) } else { @@ -1105,7 +1114,7 @@ return repoInfo } func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1159,7 +1168,7 @@ } func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1195,8 +1204,12 @@ if isIssueOwner || isCollaborator { closed := tangled.RepoIssueStateClosed - client, _ := s.auth.AuthorizedClient(r) - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + return + } + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoIssueStateNSID, Repo: user.Did, Rkey: appview.TID(), @@ -1214,7 +1227,7 @@ s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") return } - err := db.CloseIssue(s.db, f.RepoAt, issueIdInt) + err = db.CloseIssue(s.db, f.RepoAt, issueIdInt) if err != nil { log.Println("failed to close issue", err) s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") @@ -1231,7 +1244,7 @@ } } func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1279,7 +1292,7 @@ } } func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1330,8 +1343,13 @@ return } atUri := f.RepoAt.String() - client, _ := s.auth.AuthorizedClient(r) - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "issue-comment", "Failed to create comment.") + return + } + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoIssueCommentNSID, Repo: user.Did, Rkey: rkey, @@ -1358,7 +1376,7 @@ } } func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1417,7 +1435,7 @@ }) } func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1469,7 +1487,12 @@ }) case http.MethodPost: // extract form value newBody := r.FormValue("body") - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "issue-comment", "Failed to create comment.") + return + } rkey := comment.Rkey // optimistic update @@ -1484,7 +1507,7 @@ // rkey is optional, it was introduced later if comment.Rkey != "" { // update the record on pds - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, rkey) + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoIssueCommentNSID, user.Did, rkey) if err != nil { // failed to get record log.Println(err, rkey) @@ -1499,7 +1522,7 @@ issueAt := record["issue"].(string) createdAt := record["createdAt"].(string) commentIdInt64 := int64(commentIdInt) - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoIssueCommentNSID, Repo: user.Did, Rkey: rkey, @@ -1542,7 +1565,7 @@ } func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1599,8 +1622,13 @@ } // delete from pds if comment.Rkey != "" { - client, _ := s.auth.AuthorizedClient(r) - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "issue-comment", "Failed to delete comment.") + return + } + _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ Collection: tangled.GraphFollowNSID, Repo: user.Did, Rkey: comment.Rkey, @@ -1647,7 +1675,7 @@ log.Println("failed to get page") page = pagination.FirstPage() } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Println("failed to get repo and knot", err) @@ -1676,7 +1704,7 @@ } } s.pages.RepoIssues(w, pages.RepoIssuesParams{ - LoggedInUser: s.auth.GetUser(r), + LoggedInUser: s.oauth.GetUser(r), RepoInfo: f.RepoInfo(s, user), Issues: issues, DidHandleMap: didHandleMap, @@ -1687,7 +1715,7 @@ return } func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { @@ -1735,9 +1763,14 @@ s.pages.Notice(w, "issues", "Failed to create issue.") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "issues", "Failed to create issue.") + return + } atUri := f.RepoAt.String() - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoIssueNSID, Repo: user.Did, Rkey: appview.TID(), @@ -1770,7 +1803,7 @@ } } func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) f, err := s.fullyResolvedRepo(r) if err != nil { log.Printf("failed to resolve source repo: %v", err) @@ -1779,7 +1812,7 @@ } switch r.Method { case http.MethodGet: - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) knots, err := s.enforcer.GetDomainsForUser(user.Did) if err != nil { s.pages.Notice(w, "repo", "Invalid user account.") @@ -1829,14 +1862,14 @@ s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", knot)) return } - client, err := NewSignedClient(knot, secret, s.config.Dev) + client, err := NewSignedClient(knot, secret, s.config.Core.Dev) if err != nil { s.pages.Notice(w, "repo", "Failed to reach knot server.") return } var uri string - if s.config.Dev { + if s.config.Core.Dev { uri = "http" } else { uri = "https" @@ -1883,10 +1916,15 @@ case http.StatusNoContent: // continue } - xrpcClient, _ := s.auth.AuthorizedClient(r) + xrpcClient, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to get authorized client", err) + s.pages.Notice(w, "repo", "Failed to create repository.") + return + } createdAt := time.Now().Format(time.RFC3339) - atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{ + atresp, err := xrpcClient.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoNSID, Repo: user.Did, Rkey: rkey, diff --git a/appview/state/repo_util.go b/appview/state/repo_util.go --- a/appview/state/repo_util.go +++ b/appview/state/repo_util.go @@ -12,8 +12,8 @@ "github.com/bluesky-social/indigo/atproto/identity" "github.com/bluesky-social/indigo/atproto/syntax" "github.com/go-chi/chi/v5" "github.com/go-git/go-git/v5/plumbing/object" - "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pages/repoinfo" ) @@ -45,7 +45,7 @@ ref := chi.URLParam(r, "ref") if ref == "" { - us, err := NewUnsignedClient(knot, s.config.Dev) + us, err := NewUnsignedClient(knot, s.config.Core.Dev) if err != nil { return nil, err } @@ -73,7 +73,7 @@ Ref: ref, }, nil } -func RolesInRepo(s *State, u *auth.User, f *FullyResolvedRepo) repoinfo.RolesInRepo { +func RolesInRepo(s *State, u *oauth.User, f *FullyResolvedRepo) repoinfo.RolesInRepo { if u != nil { r := s.enforcer.GetPermissionsInRepo(u.Did, f.Knot, f.DidSlashRepo()) return repoinfo.RolesInRepo{r} diff --git a/appview/state/router.go b/appview/state/router.go --- a/appview/state/router.go +++ b/appview/state/router.go @@ -5,7 +5,9 @@ "net/http" "strings" "github.com/go-chi/chi/v5" + "github.com/gorilla/sessions" "tangled.sh/tangled.sh/core/appview/middleware" + oauthhandler "tangled.sh/tangled.sh/core/appview/oauth/handler" "tangled.sh/tangled.sh/core/appview/settings" "tangled.sh/tangled.sh/core/appview/state/userutil" ) @@ -67,7 +69,7 @@ r.Get("/branches", s.RepoBranches) r.Route("/tags", func(r chi.Router) { r.Get("/", s.RepoTags) r.Route("/{tag}", func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) // require auth to download for now r.Get("/download/{file}", s.DownloadArtifact) @@ -90,7 +92,7 @@ r.With(middleware.Paginate).Get("/", s.RepoIssues) r.Get("/{issue}", s.RepoSingleIssue) r.Group(func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) r.Get("/new", s.NewIssue) r.Post("/new", s.NewIssue) r.Post("/{issue}/comment", s.NewIssueComment) @@ -106,14 +108,14 @@ }) }) r.Route("/fork", func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) r.Get("/", s.ForkRepo) r.Post("/", s.ForkRepo) }) r.Route("/pulls", func(r chi.Router) { r.Get("/", s.RepoPulls) - r.With(middleware.AuthMiddleware(s.auth)).Route("/new", func(r chi.Router) { + r.With(middleware.AuthMiddleware(s.oauth)).Route("/new", func(r chi.Router) { r.Get("/", s.NewPull) r.Get("/patch-upload", s.PatchUploadFragment) r.Post("/validate-patch", s.ValidatePatch) @@ -131,7 +133,7 @@ r.Route("/round/{round}", func(r chi.Router) { r.Get("/", s.RepoPullPatch) r.Get("/interdiff", s.RepoPullInterdiff) r.Get("/actions", s.PullActions) - r.With(middleware.AuthMiddleware(s.auth)).Route("/comment", func(r chi.Router) { + r.With(middleware.AuthMiddleware(s.oauth)).Route("/comment", func(r chi.Router) { r.Get("/", s.PullComment) r.Post("/", s.PullComment) }) @@ -142,7 +144,7 @@ r.Get("/", s.RepoPullPatchRaw) }) r.Group(func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) r.Route("/resubmit", func(r chi.Router) { r.Get("/", s.ResubmitPull) r.Post("/", s.ResubmitPull) @@ -165,7 +167,7 @@ r.Post("/git-upload-pack", s.UploadPack) // settings routes, needs auth r.Group(func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) // repo description can only be edited by owner r.With(RepoPermissionMiddleware(s, "repo:owner")).Route("/description", func(r chi.Router) { r.Put("/", s.RepoDescription) @@ -196,15 +198,15 @@ r.Handle("/static/*", s.pages.Static()) r.Get("/", s.Timeline) - r.With(middleware.AuthMiddleware(s.auth)).Post("/logout", s.Logout) + r.With(middleware.AuthMiddleware(s.oauth)).Post("/logout", s.Logout) - r.Route("/login", func(r chi.Router) { - r.Get("/", s.Login) - r.Post("/", s.Login) - }) + // r.Route("/login", func(r chi.Router) { + // r.Get("/", s.Login) + // r.Post("/", s.Login) + // }) r.Route("/knots", func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) r.Get("/", s.Knots) r.Post("/key", s.RegistrationKey) @@ -222,25 +224,25 @@ }) r.Route("/repo", func(r chi.Router) { r.Route("/new", func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) r.Get("/", s.NewRepo) r.Post("/", s.NewRepo) }) // r.Post("/import", s.ImportRepo) }) - r.With(middleware.AuthMiddleware(s.auth)).Route("/follow", func(r chi.Router) { + r.With(middleware.AuthMiddleware(s.oauth)).Route("/follow", func(r chi.Router) { r.Post("/", s.Follow) r.Delete("/", s.Follow) }) - r.With(middleware.AuthMiddleware(s.auth)).Route("/star", func(r chi.Router) { + r.With(middleware.AuthMiddleware(s.oauth)).Route("/star", func(r chi.Router) { r.Post("/", s.Star) r.Delete("/", s.Star) }) r.Route("/profile", func(r chi.Router) { - r.Use(middleware.AuthMiddleware(s.auth)) + r.Use(middleware.AuthMiddleware(s.oauth)) r.Get("/edit-bio", s.EditBioFragment) r.Get("/edit-pins", s.EditPinsFragment) r.Post("/bio", s.UpdateProfileBio) @@ -248,7 +250,7 @@ r.Post("/pins", s.UpdateProfilePins) }) r.Mount("/settings", s.SettingsRouter()) - + r.Mount("/oauth", s.OAuthRouter()) r.Get("/keys/{user}", s.Keys) r.NotFound(func(w http.ResponseWriter, r *http.Request) { @@ -257,10 +259,23 @@ }) return r } +func (s *State) OAuthRouter() http.Handler { + oauth := &oauthhandler.OAuthHandler{ + Config: s.config, + Pages: s.pages, + Resolver: s.resolver, + Db: s.db, + Store: sessions.NewCookieStore([]byte(s.config.Core.CookieSecret)), + OAuth: s.oauth, + } + + return oauth.Router() +} + func (s *State) SettingsRouter() http.Handler { settings := &settings.Settings{ Db: s.db, - Auth: s.auth, + OAuth: s.oauth, Pages: s.pages, Config: s.config, } diff --git a/appview/state/star.go b/appview/state/star.go --- a/appview/state/star.go +++ b/appview/state/star.go @@ -15,7 +15,7 @@ "tangled.sh/tangled.sh/core/appview/pages" ) func (s *State) Star(w http.ResponseWriter, r *http.Request) { - currentUser := s.auth.GetUser(r) + currentUser := s.oauth.GetUser(r) subject := r.URL.Query().Get("subject") if subject == "" { @@ -29,13 +29,17 @@ log.Println("invalid form") return } - client, _ := s.auth.AuthorizedClient(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + log.Println("failed to authorize client", err) + return + } switch r.Method { case http.MethodPost: createdAt := time.Now().Format(time.RFC3339) rkey := appview.TID() - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.FeedStarNSID, Repo: currentUser.Did, Rkey: rkey, @@ -80,7 +84,7 @@ log.Println("failed to get star relationship") return } - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ + _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ Collection: tangled.FeedStarNSID, Repo: currentUser.Did, Rkey: star.Rkey, diff --git a/appview/state/state.go b/appview/state/state.go --- a/appview/state/state.go +++ b/appview/state/state.go @@ -21,6 +21,7 @@ "tangled.sh/tangled.sh/core/api/tangled" "tangled.sh/tangled.sh/core/appview" "tangled.sh/tangled.sh/core/appview/auth" "tangled.sh/tangled.sh/core/appview/db" + "tangled.sh/tangled.sh/core/appview/oauth" "tangled.sh/tangled.sh/core/appview/pages" "tangled.sh/tangled.sh/core/jetstream" "tangled.sh/tangled.sh/core/rbac" @@ -29,8 +30,9 @@ type State struct { db *db.DB auth *auth.Auth + oauth *oauth.OAuth enforcer *rbac.Enforcer - tidClock *syntax.TIDClock + tidClock syntax.TIDClock pages *pages.Pages resolver *appview.Resolver jc *jetstream.JetstreamClient @@ -38,17 +40,17 @@ config *appview.Config } func Make(config *appview.Config) (*State, error) { - d, err := db.Make(config.DbPath) + d, err := db.Make(config.Core.DbPath) if err != nil { return nil, err } - auth, err := auth.Make(config.CookieSecret) + auth, err := auth.Make(config.Core.CookieSecret) if err != nil { return nil, err } - enforcer, err := rbac.NewEnforcer(config.DbPath) + enforcer, err := rbac.NewEnforcer(config.Core.DbPath) if err != nil { return nil, err } @@ -58,10 +60,12 @@ pgs := pages.NewPages(config) resolver := appview.NewResolver() + + oauth := oauth.NewOAuth(d, config) wrapper := db.DbWrapper{d} jc, err := jetstream.NewJetstreamClient( - config.JetstreamEndpoint, + config.Jetstream.Endpoint, "appview", []string{ tangled.GraphFollowNSID, @@ -86,6 +90,7 @@ state := &State{ d, auth, + oauth, enforcer, clock, pgs, @@ -101,90 +106,90 @@ func TID(c *syntax.TIDClock) string { return c.Next().String() } -func (s *State) Login(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() +// func (s *State) Login(w http.ResponseWriter, r *http.Request) { +// ctx := r.Context() - switch r.Method { - case http.MethodGet: - err := s.pages.Login(w, pages.LoginParams{}) - if err != nil { - log.Printf("rendering login page: %s", err) - } +// switch r.Method { +// case http.MethodGet: +// err := s.pages.Login(w, pages.LoginParams{}) +// if err != nil { +// log.Printf("rendering login page: %s", err) +// } - return - case http.MethodPost: - handle := strings.TrimPrefix(r.FormValue("handle"), "@") - appPassword := r.FormValue("app_password") +// return +// case http.MethodPost: +// handle := strings.TrimPrefix(r.FormValue("handle"), "@") +// appPassword := r.FormValue("app_password") - resolved, err := s.resolver.ResolveIdent(ctx, handle) - if err != nil { - log.Println("failed to resolve handle:", err) - s.pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle)) - return - } +// resolved, err := s.resolver.ResolveIdent(ctx, handle) +// if err != nil { +// log.Println("failed to resolve handle:", err) +// s.pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle)) +// return +// } - atSession, err := s.auth.CreateInitialSession(ctx, resolved, appPassword) - if err != nil { - s.pages.Notice(w, "login-msg", "Invalid handle or password.") - return - } - sessionish := auth.CreateSessionWrapper{ServerCreateSession_Output: atSession} +// atSession, err := s.oauth.CreateInitialSession(ctx, resolved, appPassword) +// if err != nil { +// s.pages.Notice(w, "login-msg", "Invalid handle or password.") +// return +// } +// sessionish := auth.CreateSessionWrapper{ServerCreateSession_Output: atSession} - err = s.auth.StoreSession(r, w, &sessionish, resolved.PDSEndpoint()) - if err != nil { - s.pages.Notice(w, "login-msg", "Failed to login, try again later.") - return - } +// err = s.oauth.StoreSession(r, w, &sessionish, resolved.PDSEndpoint()) +// if err != nil { +// s.pages.Notice(w, "login-msg", "Failed to login, try again later.") +// return +// } - log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did) +// log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did) - did := resolved.DID.String() - defaultKnot := "knot1.tangled.sh" +// did := resolved.DID.String() +// defaultKnot := "knot1.tangled.sh" - go func() { - log.Printf("adding %s to default knot", did) - err = s.enforcer.AddMember(defaultKnot, did) - if err != nil { - log.Println("failed to add user to knot1.tangled.sh: ", err) - return - } - err = s.enforcer.E.SavePolicy() - if err != nil { - log.Println("failed to add user to knot1.tangled.sh: ", err) - return - } +// go func() { +// log.Printf("adding %s to default knot", did) +// err = s.enforcer.AddMember(defaultKnot, did) +// if err != nil { +// log.Println("failed to add user to knot1.tangled.sh: ", err) +// return +// } +// err = s.enforcer.E.SavePolicy() +// if err != nil { +// log.Println("failed to add user to knot1.tangled.sh: ", err) +// return +// } - secret, err := db.GetRegistrationKey(s.db, defaultKnot) - if err != nil { - log.Println("failed to get registration key for knot1.tangled.sh") - return - } - signedClient, err := NewSignedClient(defaultKnot, secret, s.config.Dev) - resp, err := signedClient.AddMember(did) - if err != nil { - log.Println("failed to add user to knot1.tangled.sh: ", err) - return - } +// secret, err := db.GetRegistrationKey(s.db, defaultKnot) +// if err != nil { +// log.Println("failed to get registration key for knot1.tangled.sh") +// return +// } +// signedClient, err := NewSignedClient(defaultKnot, secret, s.config.Core.Dev) +// resp, err := signedClient.AddMember(did) +// if err != nil { +// log.Println("failed to add user to knot1.tangled.sh: ", err) +// return +// } - if resp.StatusCode != http.StatusNoContent { - log.Println("failed to add user to knot1.tangled.sh: ", resp.StatusCode) - return - } - }() +// if resp.StatusCode != http.StatusNoContent { +// log.Println("failed to add user to knot1.tangled.sh: ", resp.StatusCode) +// return +// } +// }() - s.pages.HxRedirect(w, "/") - return - } -} +// s.pages.HxRedirect(w, "/") +// return +// } +// } func (s *State) Logout(w http.ResponseWriter, r *http.Request) { - s.auth.ClearSession(r, w) + s.oauth.ClearSession(r, w) w.Header().Set("HX-Redirect", "/login") w.WriteHeader(http.StatusSeeOther) } func (s *State) Timeline(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) timeline, err := db.MakeTimeline(s.db) if err != nil { @@ -235,7 +240,7 @@ // list open registrations under this did return case http.MethodPost: - session, err := s.auth.Store.Get(r, appview.SessionName) + session, err := s.oauth.Store.Get(r, appview.SessionName) if err != nil || session.IsNew { log.Println("unauthorized attempt to generate registration key") http.Error(w, "Forbidden", http.StatusUnauthorized) @@ -297,7 +302,7 @@ } // create a signed request and check if a node responds to that func (s *State) InitKnotServer(w http.ResponseWriter, r *http.Request) { - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) domain := chi.URLParam(r, "domain") if domain == "" { @@ -312,7 +317,7 @@ log.Printf("no key found for domain %s: %s\n", domain, err) return } - client, err := NewSignedClient(domain, secret, s.config.Dev) + client, err := NewSignedClient(domain, secret, s.config.Core.Dev) if err != nil { log.Println("failed to create client to ", domain) } @@ -421,7 +426,7 @@ http.Error(w, "malformed url", http.StatusBadRequest) return } - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) reg, err := db.RegistrationByDomain(s.db, domain) if err != nil { w.Write([]byte("failed to pull up registration info")) @@ -469,7 +474,7 @@ // get knots registered by this user func (s *State) Knots(w http.ResponseWriter, r *http.Request) { // for now, this is just pubkeys - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) registrations, err := db.RegistrationsByDid(s.db, user.Did) if err != nil { log.Println(err) @@ -522,10 +527,14 @@ } log.Printf("adding %s to %s\n", subjectIdentity.Handle.String(), domain) // announce this relation into the firehose, store into owners' pds - client, _ := s.auth.AuthorizedClient(r) - currentUser := s.auth.GetUser(r) + client, err := s.oauth.AuthorizedClient(r) + if err != nil { + http.Error(w, "failed to authorize client", http.StatusInternalServerError) + return + } + currentUser := s.oauth.GetUser(r) createdAt := time.Now().Format(time.RFC3339) - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.KnotMemberNSID, Repo: currentUser.Did, Rkey: appview.TID(), @@ -550,7 +559,7 @@ log.Printf("no key found for domain %s: %s\n", domain, err) return } - ksClient, err := NewSignedClient(domain, secret, s.config.Dev) + ksClient, err := NewSignedClient(domain, secret, s.config.Core.Dev) if err != nil { log.Println("failed to create client to ", domain) return @@ -614,7 +623,7 @@ func (s *State) NewRepo(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) knots, err := s.enforcer.GetDomainsForUser(user.Did) if err != nil { s.pages.Notice(w, "repo", "Invalid user account.") @@ -627,7 +636,7 @@ Knots: knots, }) case http.MethodPost: - user := s.auth.GetUser(r) + user := s.oauth.GetUser(r) domain := r.FormValue("domain") if domain == "" { @@ -671,7 +680,7 @@ s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", domain)) return } - client, err := NewSignedClient(domain, secret, s.config.Dev) + client, err := NewSignedClient(domain, secret, s.config.Core.Dev) if err != nil { s.pages.Notice(w, "repo", "Failed to connect to knot server.") return @@ -686,10 +695,14 @@ Rkey: rkey, Description: description, } - xrpcClient, _ := s.auth.AuthorizedClient(r) + xrpcClient, err := s.oauth.AuthorizedClient(r) + if err != nil { + s.pages.Notice(w, "repo", "Failed to write record to PDS.") + return + } createdAt := time.Now().Format(time.RFC3339) - atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{ + atresp, err := xrpcClient.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ Collection: tangled.RepoNSID, Repo: user.Did, Rkey: rkey, diff --git a/appview/tid.go b/appview/tid.go --- a/appview/tid.go +++ b/appview/tid.go @@ -4,7 +4,7 @@ import ( "github.com/bluesky-social/indigo/atproto/syntax" ) -var c *syntax.TIDClock = syntax.NewTIDClock(0) +var c syntax.TIDClock = syntax.NewTIDClock(0) func TID() string { return c.Next().String() diff --git a/cmd/appview/main.go b/cmd/appview/main.go --- a/cmd/appview/main.go +++ b/cmd/appview/main.go @@ -26,6 +26,6 @@ if err != nil { log.Fatal(err) } - log.Println("starting server on", c.ListenAddr) - log.Println(http.ListenAndServe(c.ListenAddr, state.Router())) + log.Println("starting server on", c.Core.ListenAddr) + log.Println(http.ListenAndServe(c.Core.ListenAddr, state.Router())) } -- tangled.sh