Monorepo for Tangled tangled.org

appview: swap out old auth service for oauth

Also does some driveby config refactoring.

anirudh.fi 492f7060 ed9740a1

verified
+1 -1
.air/appview.toml
··· 1 1 [build] 2 2 cmd = "tailwindcss -i input.css -o ./appview/pages/static/tw.css && go build -o .bin/app ./cmd/appview/main.go" 3 - bin = ".bin/app" 3 + bin = ";set -o allexport && source .env && set +o allexport; .bin/app" 4 4 root = "." 5 5 6 6 exclude_regex = [".*_templ.go"]
+37 -10
appview/config.go
··· 6 6 "github.com/sethvargo/go-envconfig" 7 7 ) 8 8 9 + type CoreConfig struct { 10 + CookieSecret string `env:"COOKIE_SECRET, default=00000000000000000000000000000000"` 11 + DbPath string `env:"DB_PATH, default=appview.db"` 12 + ListenAddr string `env:"LISTEN_ADDR, default=0.0.0.0:3000"` 13 + AppviewHost string `env:"APPVIEW_HOST, default=https://tangled.sh"` 14 + Dev bool `env:"DEV, default=false"` 15 + } 16 + 17 + type OAuthConfig struct { 18 + Jwks string `env:"JWKS"` 19 + ServerMetadataUrl string `env:"SERVER_METADATA_URL"` 20 + } 21 + 22 + type JetstreamConfig struct { 23 + Endpoint string `env:"ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"` 24 + } 25 + 26 + type ResendConfig struct { 27 + ApiKey string `env:"API_KEY"` 28 + } 29 + 30 + type CamoConfig struct { 31 + Host string `env:"HOST, default=https://camo.tangled.sh"` 32 + SharedSecret string `env:"SHARED_SECRET"` 33 + } 34 + 35 + type AvatarConfig struct { 36 + Host string `env:"HOST, default=https://avatar.tangled.sh"` 37 + SharedSecret string `env:"SHARED_SECRET"` 38 + } 39 + 9 40 type Config struct { 10 - CookieSecret string `env:"TANGLED_COOKIE_SECRET, default=00000000000000000000000000000000"` 11 - DbPath string `env:"TANGLED_DB_PATH, default=appview.db"` 12 - ListenAddr string `env:"TANGLED_LISTEN_ADDR, default=0.0.0.0:3000"` 13 - Dev bool `env:"TANGLED_DEV, default=false"` 14 - JetstreamEndpoint string `env:"TANGLED_JETSTREAM_ENDPOINT, default=wss://jetstream1.us-east.bsky.network/subscribe"` 15 - ResendApiKey string `env:"TANGLED_RESEND_API_KEY"` 16 - CamoHost string `env:"TANGLED_CAMO_HOST, default=https://camo.tangled.sh"` 17 - CamoSharedSecret string `env:"TANGLED_CAMO_SHARED_SECRET"` 18 - AvatarSharedSecret string `env:"TANGLED_AVATAR_SHARED_SECRET"` 19 - AvatarHost string `env:"TANGLED_AVATAR_HOST, default=https://avatar.tangled.sh"` 41 + Core CoreConfig `env:",prefix=TANGLED_"` 42 + Jetstream JetstreamConfig `env:",prefix=TANGLED_JETSTREAM_"` 43 + Resend ResendConfig `env:",prefix=TANGLED_RESEND_"` 44 + Camo CamoConfig `env:",prefix=TANGLED_CAMO_"` 45 + Avatar AvatarConfig `env:",prefix=TANGLED_AVATAR_"` 46 + OAuth OAuthConfig `env:",prefix=TANGLED_OAUTH_"` 20 47 } 21 48 22 49 func LoadConfig(ctx context.Context) (*Config, error) {
+3
appview/consts.go
··· 9 9 SessionRefreshJwt = "refreshJwt" 10 10 SessionExpiry = "expiry" 11 11 SessionAuthenticated = "authenticated" 12 + 13 + SessionDpopPrivateJwk = "dpopPrivateJwk" 14 + SessionDpopAuthServerNonce = "dpopAuthServerNonce" 12 15 )
+26
appview/db/db.go
··· 288 288 foreign key (at_uri) references repos(at_uri) on delete cascade 289 289 ); 290 290 291 + create table if not exists oauth_requests ( 292 + id integer primary key autoincrement, 293 + auth_server_iss text not null, 294 + state text not null, 295 + did text not null, 296 + handle text not null, 297 + pds_url text not null, 298 + pkce_verifier text not null, 299 + dpop_auth_server_nonce text not null, 300 + dpop_private_jwk text not null 301 + ); 302 + 303 + create table if not exists oauth_sessions ( 304 + id integer primary key autoincrement, 305 + did text not null, 306 + handle text not null, 307 + pds_url text not null, 308 + auth_server_iss text not null, 309 + access_jwt text not null, 310 + refresh_jwt text not null, 311 + dpop_pds_nonce text, 312 + dpop_auth_server_nonce text not null, 313 + dpop_private_jwk text not null, 314 + expiry text not null 315 + ); 316 + 291 317 create table if not exists migrations ( 292 318 id integer primary key autoincrement, 293 319 name text unique
+173
appview/db/oauth.go
··· 1 + package db 2 + 3 + type OAuthRequest struct { 4 + ID uint 5 + AuthserverIss string 6 + Handle string 7 + State string 8 + Did string 9 + PdsUrl string 10 + PkceVerifier string 11 + DpopAuthserverNonce string 12 + DpopPrivateJwk string 13 + } 14 + 15 + func SaveOAuthRequest(e Execer, oauthRequest OAuthRequest) error { 16 + _, err := e.Exec(` 17 + insert into oauth_requests ( 18 + auth_server_iss, 19 + state, 20 + handle, 21 + did, 22 + pds_url, 23 + pkce_verifier, 24 + dpop_auth_server_nonce, 25 + dpop_private_jwk 26 + ) values (?, ?, ?, ?, ?, ?, ?, ?)`, 27 + oauthRequest.AuthserverIss, 28 + oauthRequest.State, 29 + oauthRequest.Handle, 30 + oauthRequest.Did, 31 + oauthRequest.PdsUrl, 32 + oauthRequest.PkceVerifier, 33 + oauthRequest.DpopAuthserverNonce, 34 + oauthRequest.DpopPrivateJwk, 35 + ) 36 + return err 37 + } 38 + 39 + func GetOAuthRequestByState(e Execer, state string) (OAuthRequest, error) { 40 + var req OAuthRequest 41 + err := e.QueryRow(` 42 + select 43 + id, 44 + auth_server_iss, 45 + handle, 46 + state, 47 + did, 48 + pds_url, 49 + pkce_verifier, 50 + dpop_auth_server_nonce, 51 + dpop_private_jwk 52 + from oauth_requests 53 + where state = ?`, state).Scan( 54 + &req.ID, 55 + &req.AuthserverIss, 56 + &req.Handle, 57 + &req.State, 58 + &req.Did, 59 + &req.PdsUrl, 60 + &req.PkceVerifier, 61 + &req.DpopAuthserverNonce, 62 + &req.DpopPrivateJwk, 63 + ) 64 + return req, err 65 + } 66 + 67 + func DeleteOAuthRequestByState(e Execer, state string) error { 68 + _, err := e.Exec(` 69 + delete from oauth_requests 70 + where state = ?`, state) 71 + return err 72 + } 73 + 74 + type OAuthSession struct { 75 + ID uint 76 + Handle string 77 + Did string 78 + PdsUrl string 79 + AccessJwt string 80 + RefreshJwt string 81 + AuthServerIss string 82 + DpopPdsNonce string 83 + DpopAuthserverNonce string 84 + DpopPrivateJwk string 85 + Expiry string 86 + } 87 + 88 + func SaveOAuthSession(e Execer, session OAuthSession) error { 89 + _, err := e.Exec(` 90 + insert into oauth_sessions ( 91 + did, 92 + handle, 93 + pds_url, 94 + access_jwt, 95 + refresh_jwt, 96 + auth_server_iss, 97 + dpop_auth_server_nonce, 98 + dpop_private_jwk, 99 + expiry 100 + ) values (?, ?, ?, ?, ?, ?, ?, ?, ?)`, 101 + session.Did, 102 + session.Handle, 103 + session.PdsUrl, 104 + session.AccessJwt, 105 + session.RefreshJwt, 106 + session.AuthServerIss, 107 + session.DpopAuthserverNonce, 108 + session.DpopPrivateJwk, 109 + session.Expiry, 110 + ) 111 + return err 112 + } 113 + 114 + func RefreshOAuthSession(e Execer, did string, accessJwt, refreshJwt, expiry string) error { 115 + _, err := e.Exec(` 116 + update oauth_sessions 117 + set access_jwt = ?, refresh_jwt = ?, expiry = ? 118 + where did = ?`, 119 + accessJwt, 120 + refreshJwt, 121 + expiry, 122 + did, 123 + ) 124 + return err 125 + } 126 + 127 + func GetOAuthSessionByDid(e Execer, did string) (*OAuthSession, error) { 128 + var session OAuthSession 129 + err := e.QueryRow(` 130 + select 131 + id, 132 + did, 133 + handle, 134 + pds_url, 135 + access_jwt, 136 + refresh_jwt, 137 + auth_server_iss, 138 + dpop_auth_server_nonce, 139 + dpop_private_jwk, 140 + expiry 141 + from oauth_sessions 142 + where did = ?`, did).Scan( 143 + &session.ID, 144 + &session.Did, 145 + &session.Handle, 146 + &session.PdsUrl, 147 + &session.AccessJwt, 148 + &session.RefreshJwt, 149 + &session.AuthServerIss, 150 + &session.DpopAuthserverNonce, 151 + &session.DpopPrivateJwk, 152 + &session.Expiry, 153 + ) 154 + return &session, err 155 + } 156 + 157 + func DeleteOAuthSessionByDid(e Execer, did string) error { 158 + _, err := e.Exec(` 159 + delete from oauth_sessions 160 + where did = ?`, did) 161 + return err 162 + } 163 + 164 + func UpdateDpopPdsNonce(e Execer, did string, dpopPdsNonce string) error { 165 + _, err := e.Exec(` 166 + update oauth_sessions 167 + set dpop_pds_nonce = ? 168 + where did = ?`, 169 + dpopPdsNonce, 170 + did, 171 + ) 172 + return err 173 + }
+5 -58
appview/middleware/middleware.go
··· 5 5 "log" 6 6 "net/http" 7 7 "strconv" 8 - "time" 9 8 10 - comatproto "github.com/bluesky-social/indigo/api/atproto" 11 - "github.com/bluesky-social/indigo/xrpc" 12 - "tangled.sh/tangled.sh/core/appview" 13 - "tangled.sh/tangled.sh/core/appview/auth" 9 + "tangled.sh/tangled.sh/core/appview/oauth" 14 10 "tangled.sh/tangled.sh/core/appview/pagination" 15 11 ) 16 12 17 13 type Middleware func(http.Handler) http.Handler 18 14 19 - func AuthMiddleware(a *auth.Auth) Middleware { 15 + func AuthMiddleware(a *oauth.OAuth) Middleware { 20 16 return func(next http.Handler) http.Handler { 21 17 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 18 redirectFunc := func(w http.ResponseWriter, r *http.Request) { ··· 29 25 } 30 26 } 31 27 32 - session, err := a.GetSession(r) 33 - if session.IsNew || err != nil { 28 + _, auth, err := a.GetSession(r) 29 + if err != nil { 34 30 log.Printf("not logged in, redirecting") 35 31 redirectFunc(w, r) 36 32 return 37 33 } 38 34 39 - authorized, ok := session.Values[appview.SessionAuthenticated].(bool) 40 - if !ok || !authorized { 35 + if !auth { 41 36 log.Printf("not logged in, redirecting") 42 37 redirectFunc(w, r) 43 38 return 44 - } 45 - 46 - // refresh if nearing expiry 47 - // TODO: dedup with /login 48 - expiryStr := session.Values[appview.SessionExpiry].(string) 49 - expiry, err := time.Parse(time.RFC3339, expiryStr) 50 - if err != nil { 51 - log.Println("invalid expiry time", err) 52 - redirectFunc(w, r) 53 - return 54 - } 55 - pdsUrl, ok1 := session.Values[appview.SessionPds].(string) 56 - did, ok2 := session.Values[appview.SessionDid].(string) 57 - refreshJwt, ok3 := session.Values[appview.SessionRefreshJwt].(string) 58 - 59 - if !ok1 || !ok2 || !ok3 { 60 - log.Println("invalid expiry time", err) 61 - redirectFunc(w, r) 62 - return 63 - } 64 - 65 - if time.Now().After(expiry) { 66 - log.Println("token expired, refreshing ...") 67 - 68 - client := xrpc.Client{ 69 - Host: pdsUrl, 70 - Auth: &xrpc.AuthInfo{ 71 - Did: did, 72 - AccessJwt: refreshJwt, 73 - RefreshJwt: refreshJwt, 74 - }, 75 - } 76 - atSession, err := comatproto.ServerRefreshSession(r.Context(), &client) 77 - if err != nil { 78 - log.Println("failed to refresh session", err) 79 - redirectFunc(w, r) 80 - return 81 - } 82 - 83 - sessionish := auth.RefreshSessionWrapper{atSession} 84 - 85 - err = a.StoreSession(r, w, &sessionish, pdsUrl) 86 - if err != nil { 87 - log.Printf("failed to store session for did: %s\n: %s", atSession.Did, err) 88 - return 89 - } 90 - 91 - log.Println("successfully refreshed token") 92 39 } 93 40 94 41 next.ServeHTTP(w, r)
+2
appview/oauth/oauth.go
··· 48 48 } 49 49 50 50 userSession.Values[appview.SessionDid] = oreq.Did 51 + userSession.Values[appview.SessionHandle] = oreq.Handle 52 + userSession.Values[appview.SessionPds] = oreq.PdsUrl 51 53 userSession.Values[appview.SessionAuthenticated] = true 52 54 err = userSession.Save(r, w) 53 55 if err != nil {
+41 -37
appview/pages/pages.go
··· 16 16 "strings" 17 17 18 18 "tangled.sh/tangled.sh/core/appview" 19 - "tangled.sh/tangled.sh/core/appview/auth" 20 19 "tangled.sh/tangled.sh/core/appview/db" 20 + "tangled.sh/tangled.sh/core/appview/oauth" 21 21 "tangled.sh/tangled.sh/core/appview/pages/markup" 22 22 "tangled.sh/tangled.sh/core/appview/pages/repoinfo" 23 23 "tangled.sh/tangled.sh/core/appview/pagination" ··· 48 48 func NewPages(config *appview.Config) *Pages { 49 49 // initialized with safe defaults, can be overriden per use 50 50 rctx := &markup.RenderContext{ 51 - IsDev: config.Dev, 52 - CamoUrl: config.CamoHost, 53 - CamoSecret: config.CamoSharedSecret, 51 + IsDev: config.Core.Dev, 52 + CamoUrl: config.Camo.Host, 53 + CamoSecret: config.Camo.SharedSecret, 54 54 } 55 55 56 56 p := &Pages{ 57 57 t: make(map[string]*template.Template), 58 - dev: config.Dev, 58 + dev: config.Core.Dev, 59 59 embedFS: Files, 60 60 rctx: rctx, 61 61 templateDir: "appview/pages", ··· 249 249 return p.executePlain("user/login", w, params) 250 250 } 251 251 252 + func (p *Pages) OAuthLogin(w io.Writer, params LoginParams) error { 253 + return p.executePlain("user/oauthlogin", w, params) 254 + } 255 + 252 256 type TimelineParams struct { 253 - LoggedInUser *auth.User 257 + LoggedInUser *oauth.User 254 258 Timeline []db.TimelineEvent 255 259 DidHandleMap map[string]string 256 260 } ··· 260 264 } 261 265 262 266 type SettingsParams struct { 263 - LoggedInUser *auth.User 267 + LoggedInUser *oauth.User 264 268 PubKeys []db.PublicKey 265 269 Emails []db.Email 266 270 } ··· 270 274 } 271 275 272 276 type KnotsParams struct { 273 - LoggedInUser *auth.User 277 + LoggedInUser *oauth.User 274 278 Registrations []db.Registration 275 279 } 276 280 ··· 279 283 } 280 284 281 285 type KnotParams struct { 282 - LoggedInUser *auth.User 286 + LoggedInUser *oauth.User 283 287 DidHandleMap map[string]string 284 288 Registration *db.Registration 285 289 Members []string ··· 291 295 } 292 296 293 297 type NewRepoParams struct { 294 - LoggedInUser *auth.User 298 + LoggedInUser *oauth.User 295 299 Knots []string 296 300 } 297 301 ··· 300 304 } 301 305 302 306 type ForkRepoParams struct { 303 - LoggedInUser *auth.User 307 + LoggedInUser *oauth.User 304 308 Knots []string 305 309 RepoInfo repoinfo.RepoInfo 306 310 } ··· 310 314 } 311 315 312 316 type ProfilePageParams struct { 313 - LoggedInUser *auth.User 317 + LoggedInUser *oauth.User 314 318 Repos []db.Repo 315 319 CollaboratingRepos []db.Repo 316 320 ProfileTimeline *db.ProfileTimeline ··· 335 339 } 336 340 337 341 type ReposPageParams struct { 338 - LoggedInUser *auth.User 342 + LoggedInUser *oauth.User 339 343 Repos []db.Repo 340 344 Card ProfileCard 341 345 ··· 356 360 } 357 361 358 362 type EditBioParams struct { 359 - LoggedInUser *auth.User 363 + LoggedInUser *oauth.User 360 364 Profile *db.Profile 361 365 } 362 366 ··· 365 369 } 366 370 367 371 type EditPinsParams struct { 368 - LoggedInUser *auth.User 372 + LoggedInUser *oauth.User 369 373 Profile *db.Profile 370 374 AllRepos []PinnedRepo 371 375 DidHandleMap map[string]string ··· 403 407 } 404 408 405 409 type RepoIndexParams struct { 406 - LoggedInUser *auth.User 410 + LoggedInUser *oauth.User 407 411 RepoInfo repoinfo.RepoInfo 408 412 Active string 409 413 TagMap map[string][]string ··· 444 448 } 445 449 446 450 type RepoLogParams struct { 447 - LoggedInUser *auth.User 451 + LoggedInUser *oauth.User 448 452 RepoInfo repoinfo.RepoInfo 449 453 TagMap map[string][]string 450 454 types.RepoLogResponse ··· 458 462 } 459 463 460 464 type RepoCommitParams struct { 461 - LoggedInUser *auth.User 465 + LoggedInUser *oauth.User 462 466 RepoInfo repoinfo.RepoInfo 463 467 Active string 464 468 EmailToDidOrHandle map[string]string ··· 472 476 } 473 477 474 478 type RepoTreeParams struct { 475 - LoggedInUser *auth.User 479 + LoggedInUser *oauth.User 476 480 RepoInfo repoinfo.RepoInfo 477 481 Active string 478 482 BreadCrumbs [][]string ··· 508 512 } 509 513 510 514 type RepoBranchesParams struct { 511 - LoggedInUser *auth.User 515 + LoggedInUser *oauth.User 512 516 RepoInfo repoinfo.RepoInfo 513 517 Active string 514 518 types.RepoBranchesResponse ··· 520 524 } 521 525 522 526 type RepoTagsParams struct { 523 - LoggedInUser *auth.User 527 + LoggedInUser *oauth.User 524 528 RepoInfo repoinfo.RepoInfo 525 529 Active string 526 530 types.RepoTagsResponse ··· 534 538 } 535 539 536 540 type RepoArtifactParams struct { 537 - LoggedInUser *auth.User 541 + LoggedInUser *oauth.User 538 542 RepoInfo repoinfo.RepoInfo 539 543 Artifact db.Artifact 540 544 } ··· 544 548 } 545 549 546 550 type RepoBlobParams struct { 547 - LoggedInUser *auth.User 551 + LoggedInUser *oauth.User 548 552 RepoInfo repoinfo.RepoInfo 549 553 Active string 550 554 BreadCrumbs [][]string ··· 606 610 } 607 611 608 612 type RepoSettingsParams struct { 609 - LoggedInUser *auth.User 613 + LoggedInUser *oauth.User 610 614 RepoInfo repoinfo.RepoInfo 611 615 Collaborators []Collaborator 612 616 Active string ··· 622 626 } 623 627 624 628 type RepoIssuesParams struct { 625 - LoggedInUser *auth.User 629 + LoggedInUser *oauth.User 626 630 RepoInfo repoinfo.RepoInfo 627 631 Active string 628 632 Issues []db.Issue ··· 637 641 } 638 642 639 643 type RepoSingleIssueParams struct { 640 - LoggedInUser *auth.User 644 + LoggedInUser *oauth.User 641 645 RepoInfo repoinfo.RepoInfo 642 646 Active string 643 647 Issue db.Issue ··· 659 663 } 660 664 661 665 type RepoNewIssueParams struct { 662 - LoggedInUser *auth.User 666 + LoggedInUser *oauth.User 663 667 RepoInfo repoinfo.RepoInfo 664 668 Active string 665 669 } ··· 670 674 } 671 675 672 676 type EditIssueCommentParams struct { 673 - LoggedInUser *auth.User 677 + LoggedInUser *oauth.User 674 678 RepoInfo repoinfo.RepoInfo 675 679 Issue *db.Issue 676 680 Comment *db.Comment ··· 681 685 } 682 686 683 687 type SingleIssueCommentParams struct { 684 - LoggedInUser *auth.User 688 + LoggedInUser *oauth.User 685 689 DidHandleMap map[string]string 686 690 RepoInfo repoinfo.RepoInfo 687 691 Issue *db.Issue ··· 693 697 } 694 698 695 699 type RepoNewPullParams struct { 696 - LoggedInUser *auth.User 700 + LoggedInUser *oauth.User 697 701 RepoInfo repoinfo.RepoInfo 698 702 Branches []types.Branch 699 703 Active string ··· 705 709 } 706 710 707 711 type RepoPullsParams struct { 708 - LoggedInUser *auth.User 712 + LoggedInUser *oauth.User 709 713 RepoInfo repoinfo.RepoInfo 710 714 Pulls []*db.Pull 711 715 Active string ··· 737 741 } 738 742 739 743 type RepoSinglePullParams struct { 740 - LoggedInUser *auth.User 744 + LoggedInUser *oauth.User 741 745 RepoInfo repoinfo.RepoInfo 742 746 Active string 743 747 DidHandleMap map[string]string ··· 752 756 } 753 757 754 758 type RepoPullPatchParams struct { 755 - LoggedInUser *auth.User 759 + LoggedInUser *oauth.User 756 760 DidHandleMap map[string]string 757 761 RepoInfo repoinfo.RepoInfo 758 762 Pull *db.Pull ··· 767 771 } 768 772 769 773 type RepoPullInterdiffParams struct { 770 - LoggedInUser *auth.User 774 + LoggedInUser *oauth.User 771 775 DidHandleMap map[string]string 772 776 RepoInfo repoinfo.RepoInfo 773 777 Pull *db.Pull ··· 817 821 } 818 822 819 823 type PullResubmitParams struct { 820 - LoggedInUser *auth.User 824 + LoggedInUser *oauth.User 821 825 RepoInfo repoinfo.RepoInfo 822 826 Pull *db.Pull 823 827 SubmissionId int ··· 828 832 } 829 833 830 834 type PullActionsParams struct { 831 - LoggedInUser *auth.User 835 + LoggedInUser *oauth.User 832 836 RepoInfo repoinfo.RepoInfo 833 837 Pull *db.Pull 834 838 RoundNumber int ··· 841 845 } 842 846 843 847 type PullNewCommentParams struct { 844 - LoggedInUser *auth.User 848 + LoggedInUser *oauth.User 845 849 RepoInfo repoinfo.RepoInfo 846 850 Pull *db.Pull 847 851 RoundNumber int
+71
appview/pages/templates/user/oauthlogin.html
··· 1 + {{ define "user/oauthlogin" }} 2 + <!doctype html> 3 + <html lang="en" class="dark:bg-gray-900"> 4 + <head> 5 + <meta charset="UTF-8" /> 6 + <meta 7 + name="viewport" 8 + content="width=device-width, initial-scale=1.0" 9 + /> 10 + <script src="/static/htmx.min.js"></script> 11 + <link 12 + rel="stylesheet" 13 + href="/static/tw.css?{{ cssContentHash }}" 14 + type="text/css" 15 + /> 16 + <title>login</title> 17 + </head> 18 + <body class="flex items-center justify-center min-h-screen"> 19 + <main class="max-w-7xl px-6 -mt-4"> 20 + <h1 21 + class="text-center text-2xl font-semibold italic dark:text-white" 22 + > 23 + tangled 24 + </h1> 25 + <h2 class="text-center text-xl italic dark:text-white"> 26 + tightly-knit social coding. 27 + </h2> 28 + <form 29 + class="w-full mt-4" 30 + hx-post="/oauth/login" 31 + hx-swap="none" 32 + hx-disabled-elt="this" 33 + > 34 + <div class="flex flex-col"> 35 + <label for="handle">handle</label> 36 + <input 37 + type="text" 38 + id="handle" 39 + name="handle" 40 + tabindex="1" 41 + required 42 + /> 43 + <span class="text-xs text-gray-500 mt-1"> 44 + Use your 45 + <a href="https://bsky.app">Bluesky</a> handle to log 46 + in. You will then be redirected to your PDS to 47 + complete authentication. 48 + </span> 49 + </div> 50 + 51 + <button 52 + class="btn w-full my-2 mt-6" 53 + type="submit" 54 + id="login-button" 55 + tabindex="3" 56 + > 57 + <span>login</span> 58 + </button> 59 + </form> 60 + <p class="text-sm text-gray-500"> 61 + Join our <a href="https://chat.tangled.sh">Discord</a> or 62 + IRC channel: 63 + <a href="https://web.libera.chat/#tangled" 64 + ><code>#tangled</code> on Libera Chat</a 65 + >. 66 + </p> 67 + <p id="login-msg" class="error w-full"></p> 68 + </main> 69 + </body> 70 + </html> 71 + {{ end }}
+27 -18
appview/settings/settings.go
··· 13 13 "github.com/go-chi/chi/v5" 14 14 "tangled.sh/tangled.sh/core/api/tangled" 15 15 "tangled.sh/tangled.sh/core/appview" 16 - "tangled.sh/tangled.sh/core/appview/auth" 17 16 "tangled.sh/tangled.sh/core/appview/db" 18 17 "tangled.sh/tangled.sh/core/appview/email" 19 18 "tangled.sh/tangled.sh/core/appview/middleware" 19 + "tangled.sh/tangled.sh/core/appview/oauth" 20 20 "tangled.sh/tangled.sh/core/appview/pages" 21 21 22 22 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 27 27 28 28 type Settings struct { 29 29 Db *db.DB 30 - Auth *auth.Auth 30 + OAuth *oauth.OAuth 31 31 Pages *pages.Pages 32 32 Config *appview.Config 33 33 } ··· 35 35 func (s *Settings) Router() http.Handler { 36 36 r := chi.NewRouter() 37 37 38 - r.Use(middleware.AuthMiddleware(s.Auth)) 38 + r.Use(middleware.AuthMiddleware(s.OAuth)) 39 39 40 40 r.Get("/", s.settings) 41 41 ··· 56 56 } 57 57 58 58 func (s *Settings) settings(w http.ResponseWriter, r *http.Request) { 59 - user := s.Auth.GetUser(r) 59 + user := s.OAuth.GetUser(r) 60 60 pubKeys, err := db.GetPublicKeys(s.Db, user.Did) 61 61 if err != nil { 62 62 log.Println(err) ··· 79 79 verifyURL := s.verifyUrl(did, emailAddr, code) 80 80 81 81 return email.Email{ 82 - APIKey: s.Config.ResendApiKey, 82 + APIKey: s.Config.Resend.ApiKey, 83 83 From: "noreply@notifs.tangled.sh", 84 84 To: emailAddr, 85 85 Subject: "Verify your Tangled email", ··· 111 111 log.Println("unimplemented") 112 112 return 113 113 case http.MethodPut: 114 - did := s.Auth.GetDid(r) 114 + did := s.OAuth.GetDid(r) 115 115 emAddr := r.FormValue("email") 116 116 emAddr = strings.TrimSpace(emAddr) 117 117 ··· 174 174 s.Pages.Notice(w, "settings-emails-success", "Click the link in the email we sent you to verify your email address.") 175 175 return 176 176 case http.MethodDelete: 177 - did := s.Auth.GetDid(r) 177 + did := s.OAuth.GetDid(r) 178 178 emailAddr := r.FormValue("email") 179 179 emailAddr = strings.TrimSpace(emailAddr) 180 180 ··· 207 207 208 208 func (s *Settings) verifyUrl(did string, email string, code string) string { 209 209 var appUrl string 210 - if s.Config.Dev { 211 - appUrl = "http://" + s.Config.ListenAddr 210 + if s.Config.Core.Dev { 211 + appUrl = "http://" + s.Config.Core.ListenAddr 212 212 } else { 213 213 appUrl = "https://tangled.sh" 214 214 } ··· 252 252 return 253 253 } 254 254 255 - did := s.Auth.GetDid(r) 255 + did := s.OAuth.GetDid(r) 256 256 emAddr := r.FormValue("email") 257 257 emAddr = strings.TrimSpace(emAddr) 258 258 ··· 323 323 } 324 324 325 325 func (s *Settings) emailsPrimary(w http.ResponseWriter, r *http.Request) { 326 - did := s.Auth.GetDid(r) 326 + did := s.OAuth.GetDid(r) 327 327 emailAddr := r.FormValue("email") 328 328 emailAddr = strings.TrimSpace(emailAddr) 329 329 ··· 348 348 log.Println("unimplemented") 349 349 return 350 350 case http.MethodPut: 351 - did := s.Auth.GetDid(r) 351 + did := s.OAuth.GetDid(r) 352 352 key := r.FormValue("key") 353 353 key = strings.TrimSpace(key) 354 354 name := r.FormValue("name") 355 - client, _ := s.Auth.AuthorizedClient(r) 355 + client, err := s.OAuth.AuthorizedClient(r) 356 + if err != nil { 357 + s.Pages.Notice(w, "settings-keys", "Failed to authorize. Try again later.") 358 + return 359 + } 356 360 357 - _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key)) 361 + _, _, _, _, err = ssh.ParseAuthorizedKey([]byte(key)) 358 362 if err != nil { 359 363 log.Printf("parsing public key: %s", err) 360 364 s.Pages.Notice(w, "settings-keys", "That doesn't look like a valid public key. Make sure it's a <strong>public</strong> key.") ··· 378 382 } 379 383 380 384 // store in pds too 381 - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 385 + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 382 386 Collection: tangled.PublicKeyNSID, 383 387 Repo: did, 384 388 Rkey: rkey, ··· 409 413 return 410 414 411 415 case http.MethodDelete: 412 - did := s.Auth.GetDid(r) 416 + did := s.OAuth.GetDid(r) 413 417 q := r.URL.Query() 414 418 415 419 name := q.Get("name") ··· 420 424 log.Println(rkey) 421 425 log.Println(key) 422 426 423 - client, _ := s.Auth.AuthorizedClient(r) 427 + client, err := s.OAuth.AuthorizedClient(r) 428 + if err != nil { 429 + log.Printf("failed to authorize client: %s", err) 430 + s.Pages.Notice(w, "settings-keys", "Failed to authorize client.") 431 + return 432 + } 424 433 425 434 if err := db.DeletePublicKey(s.Db, did, name, key); err != nil { 426 435 log.Printf("removing public key: %s", err) ··· 430 439 431 440 if rkey != "" { 432 441 // remove from pds too 433 - _, err := comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ 442 + _, err := client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ 434 443 Collection: tangled.PublicKeyNSID, 435 444 Repo: did, 436 445 Rkey: rkey,
+19 -10
appview/state/artifact.go
··· 22 22 23 23 // TODO: proper statuses here on early exit 24 24 func (s *State) AttachArtifact(w http.ResponseWriter, r *http.Request) { 25 - user := s.auth.GetUser(r) 25 + user := s.oauth.GetUser(r) 26 26 tagParam := chi.URLParam(r, "tag") 27 27 f, err := s.fullyResolvedRepo(r) 28 28 if err != nil { ··· 46 46 } 47 47 defer file.Close() 48 48 49 - client, _ := s.auth.AuthorizedClient(r) 49 + client, err := s.oauth.AuthorizedClient(r) 50 + if err != nil { 51 + log.Println("failed to get authorized client", err) 52 + s.pages.Notice(w, "upload", "failed to get authorized client") 53 + return 54 + } 50 55 51 - uploadBlobResp, err := comatproto.RepoUploadBlob(r.Context(), client, file) 56 + uploadBlobResp, err := client.RepoUploadBlob(r.Context(), file) 52 57 if err != nil { 53 58 log.Println("failed to upload blob", err) 54 59 s.pages.Notice(w, "upload", "Failed to upload blob to your PDS. Try again later.") ··· 60 65 rkey := appview.TID() 61 66 createdAt := time.Now() 62 67 63 - putRecordResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 68 + putRecordResp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 64 69 Collection: tangled.RepoArtifactNSID, 65 70 Repo: user.Did, 66 71 Rkey: rkey, ··· 140 145 return 141 146 } 142 147 143 - client, _ := s.auth.AuthorizedClient(r) 148 + client, err := s.oauth.AuthorizedClient(r) 149 + if err != nil { 150 + log.Println("failed to get authorized client", err) 151 + return 152 + } 144 153 145 154 artifacts, err := db.GetArtifact( 146 155 s.db, ··· 159 168 160 169 artifact := artifacts[0] 161 170 162 - getBlobResp, err := comatproto.SyncGetBlob(r.Context(), client, artifact.BlobCid.String(), artifact.Did) 171 + getBlobResp, err := client.SyncGetBlob(r.Context(), artifact.BlobCid.String(), artifact.Did) 163 172 if err != nil { 164 173 log.Println("failed to get blob from pds", err) 165 174 return ··· 171 180 172 181 // TODO: proper statuses here on early exit 173 182 func (s *State) DeleteArtifact(w http.ResponseWriter, r *http.Request) { 174 - user := s.auth.GetUser(r) 183 + user := s.oauth.GetUser(r) 175 184 tagParam := chi.URLParam(r, "tag") 176 185 filename := chi.URLParam(r, "file") 177 186 f, err := s.fullyResolvedRepo(r) ··· 180 189 return 181 190 } 182 191 183 - client, _ := s.auth.AuthorizedClient(r) 192 + client, _ := s.oauth.AuthorizedClient(r) 184 193 185 194 tag := plumbing.NewHash(tagParam) 186 195 ··· 208 217 return 209 218 } 210 219 211 - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ 220 + _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ 212 221 Collection: tangled.RepoArtifactNSID, 213 222 Repo: user.Did, 214 223 Rkey: artifact.Rkey, ··· 254 263 return nil, err 255 264 } 256 265 257 - us, err := NewUnsignedClient(f.Knot, s.config.Dev) 266 + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) 258 267 if err != nil { 259 268 return nil, err 260 269 }
+8 -4
appview/state/follow.go
··· 14 14 ) 15 15 16 16 func (s *State) Follow(w http.ResponseWriter, r *http.Request) { 17 - currentUser := s.auth.GetUser(r) 17 + currentUser := s.oauth.GetUser(r) 18 18 19 19 subject := r.URL.Query().Get("subject") 20 20 if subject == "" { ··· 32 32 return 33 33 } 34 34 35 - client, _ := s.auth.AuthorizedClient(r) 35 + client, err := s.oauth.AuthorizedClient(r) 36 + if err != nil { 37 + log.Println("failed to authorize client") 38 + return 39 + } 36 40 37 41 switch r.Method { 38 42 case http.MethodPost: 39 43 createdAt := time.Now().Format(time.RFC3339) 40 44 rkey := appview.TID() 41 - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 45 + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 42 46 Collection: tangled.GraphFollowNSID, 43 47 Repo: currentUser.Did, 44 48 Rkey: rkey, ··· 75 79 return 76 80 } 77 81 78 - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ 82 + _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ 79 83 Collection: tangled.GraphFollowNSID, 80 84 Repo: currentUser.Did, 81 85 Rkey: follow.Rkey,
+2 -2
appview/state/git_http.go
··· 15 15 repo := chi.URLParam(r, "repo") 16 16 17 17 scheme := "https" 18 - if s.config.Dev { 18 + if s.config.Core.Dev { 19 19 scheme = "http" 20 20 } 21 21 targetURL := fmt.Sprintf("%s://%s/%s/%s/info/refs?%s", scheme, knot, user.DID, repo, r.URL.RawQuery) ··· 52 52 repo := chi.URLParam(r, "repo") 53 53 54 54 scheme := "https" 55 - if s.config.Dev { 55 + if s.config.Core.Dev { 56 56 scheme = "http" 57 57 } 58 58 targetURL := fmt.Sprintf("%s://%s/%s/%s/git-upload-pack?%s", scheme, knot, user.DID, repo, r.URL.RawQuery)
+2 -2
appview/state/middleware.go
··· 20 20 return func(next http.Handler) http.Handler { 21 21 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 22 22 // requires auth also 23 - actor := s.auth.GetUser(r) 23 + actor := s.oauth.GetUser(r) 24 24 if actor == nil { 25 25 // we need a logged in user 26 26 log.Printf("not logged in, redirecting") ··· 54 54 return func(next http.Handler) http.Handler { 55 55 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 56 56 // requires auth also 57 - actor := s.auth.GetUser(r) 57 + actor := s.oauth.GetUser(r) 58 58 if actor == nil { 59 59 // we need a logged in user 60 60 log.Printf("not logged in, redirecting")
+17 -12
appview/state/profile.go
··· 119 119 log.Printf("getting follow stats repos for %s: %s", ident.DID.String(), err) 120 120 } 121 121 122 - loggedInUser := s.auth.GetUser(r) 122 + loggedInUser := s.oauth.GetUser(r) 123 123 followStatus := db.IsNotFollowing 124 124 if loggedInUser != nil { 125 125 followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String()) ··· 161 161 log.Printf("getting repos for %s: %s", ident.DID.String(), err) 162 162 } 163 163 164 - loggedInUser := s.auth.GetUser(r) 164 + loggedInUser := s.oauth.GetUser(r) 165 165 followStatus := db.IsNotFollowing 166 166 if loggedInUser != nil { 167 167 followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, ident.DID.String()) ··· 190 190 } 191 191 192 192 func (s *State) GetAvatarUri(handle string) string { 193 - secret := s.config.AvatarSharedSecret 193 + secret := s.config.Avatar.SharedSecret 194 194 h := hmac.New(sha256.New, []byte(secret)) 195 195 h.Write([]byte(handle)) 196 196 signature := hex.EncodeToString(h.Sum(nil)) 197 - return fmt.Sprintf("%s/%s/%s", s.config.AvatarHost, signature, handle) 197 + return fmt.Sprintf("%s/%s/%s", s.config.Avatar.Host, signature, handle) 198 198 } 199 199 200 200 func (s *State) UpdateProfileBio(w http.ResponseWriter, r *http.Request) { 201 - user := s.auth.GetUser(r) 201 + user := s.oauth.GetUser(r) 202 202 203 203 err := r.ParseForm() 204 204 if err != nil { ··· 246 246 } 247 247 248 248 func (s *State) UpdateProfilePins(w http.ResponseWriter, r *http.Request) { 249 - user := s.auth.GetUser(r) 249 + user := s.oauth.GetUser(r) 250 250 251 251 err := r.ParseForm() 252 252 if err != nil { ··· 286 286 } 287 287 288 288 func (s *State) updateProfile(profile *db.Profile, w http.ResponseWriter, r *http.Request) { 289 - user := s.auth.GetUser(r) 289 + user := s.oauth.GetUser(r) 290 290 tx, err := s.db.BeginTx(r.Context(), nil) 291 291 if err != nil { 292 292 log.Println("failed to start transaction", err) ··· 294 294 return 295 295 } 296 296 297 - client, _ := s.auth.AuthorizedClient(r) 297 + client, err := s.oauth.AuthorizedClient(r) 298 + if err != nil { 299 + log.Println("failed to get authorized client", err) 300 + s.pages.Notice(w, "update-profile", "Failed to update profile, try again later.") 301 + return 302 + } 298 303 299 304 // yeah... lexgen dose not support syntax.ATURI in the record for some reason, 300 305 // nor does it support exact size arrays ··· 308 313 vanityStats = append(vanityStats, string(v.Kind)) 309 314 } 310 315 311 - ex, _ := comatproto.RepoGetRecord(r.Context(), client, "", tangled.ActorProfileNSID, user.Did, "self") 316 + ex, _ := client.RepoGetRecord(r.Context(), "", tangled.ActorProfileNSID, user.Did, "self") 312 317 var cid *string 313 318 if ex != nil { 314 319 cid = ex.Cid 315 320 } 316 321 317 - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 322 + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 318 323 Collection: tangled.ActorProfileNSID, 319 324 Repo: user.Did, 320 325 Rkey: "self", ··· 347 352 } 348 353 349 354 func (s *State) EditBioFragment(w http.ResponseWriter, r *http.Request) { 350 - user := s.auth.GetUser(r) 355 + user := s.oauth.GetUser(r) 351 356 352 357 profile, err := db.GetProfile(s.db, user.Did) 353 358 if err != nil { ··· 361 366 } 362 367 363 368 func (s *State) EditPinsFragment(w http.ResponseWriter, r *http.Request) { 364 - user := s.auth.GetUser(r) 369 + user := s.oauth.GetUser(r) 365 370 366 371 profile, err := db.GetProfile(s.db, user.Did) 367 372 if err != nil {
+76 -51
appview/state/pull.go
··· 13 13 14 14 "tangled.sh/tangled.sh/core/api/tangled" 15 15 "tangled.sh/tangled.sh/core/appview" 16 - "tangled.sh/tangled.sh/core/appview/auth" 17 16 "tangled.sh/tangled.sh/core/appview/db" 17 + "tangled.sh/tangled.sh/core/appview/oauth" 18 18 "tangled.sh/tangled.sh/core/appview/pages" 19 19 "tangled.sh/tangled.sh/core/patchutil" 20 20 "tangled.sh/tangled.sh/core/types" ··· 29 29 func (s *State) PullActions(w http.ResponseWriter, r *http.Request) { 30 30 switch r.Method { 31 31 case http.MethodGet: 32 - user := s.auth.GetUser(r) 32 + user := s.oauth.GetUser(r) 33 33 f, err := s.fullyResolvedRepo(r) 34 34 if err != nil { 35 35 log.Println("failed to get repo and knot", err) ··· 73 73 } 74 74 75 75 func (s *State) RepoSinglePull(w http.ResponseWriter, r *http.Request) { 76 - user := s.auth.GetUser(r) 76 + user := s.oauth.GetUser(r) 77 77 f, err := s.fullyResolvedRepo(r) 78 78 if err != nil { 79 79 log.Println("failed to get repo and knot", err) ··· 143 143 } 144 144 } 145 145 146 - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) 146 + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) 147 147 if err != nil { 148 148 log.Printf("failed to setup signed client for %s; ignoring: %v", f.Knot, err) 149 149 return types.MergeCheckResponse{ ··· 215 215 repoName = f.RepoName 216 216 } 217 217 218 - us, err := NewUnsignedClient(knot, s.config.Dev) 218 + us, err := NewUnsignedClient(knot, s.config.Core.Dev) 219 219 if err != nil { 220 220 log.Printf("failed to setup client for %s; ignoring: %v", knot, err) 221 221 return pages.Unknown ··· 250 250 } 251 251 252 252 func (s *State) RepoPullPatch(w http.ResponseWriter, r *http.Request) { 253 - user := s.auth.GetUser(r) 253 + user := s.oauth.GetUser(r) 254 254 f, err := s.fullyResolvedRepo(r) 255 255 if err != nil { 256 256 log.Println("failed to get repo and knot", err) ··· 298 298 } 299 299 300 300 func (s *State) RepoPullInterdiff(w http.ResponseWriter, r *http.Request) { 301 - user := s.auth.GetUser(r) 301 + user := s.oauth.GetUser(r) 302 302 303 303 f, err := s.fullyResolvedRepo(r) 304 304 if err != nil { ··· 355 355 interdiff := patchutil.Interdiff(previousPatch, currentPatch) 356 356 357 357 s.pages.RepoPullInterdiffPage(w, pages.RepoPullInterdiffParams{ 358 - LoggedInUser: s.auth.GetUser(r), 358 + LoggedInUser: s.oauth.GetUser(r), 359 359 RepoInfo: f.RepoInfo(s, user), 360 360 Pull: pull, 361 361 Round: roundIdInt, ··· 397 397 } 398 398 399 399 func (s *State) RepoPulls(w http.ResponseWriter, r *http.Request) { 400 - user := s.auth.GetUser(r) 400 + user := s.oauth.GetUser(r) 401 401 params := r.URL.Query() 402 402 403 403 state := db.PullOpen ··· 451 451 } 452 452 453 453 s.pages.RepoPulls(w, pages.RepoPullsParams{ 454 - LoggedInUser: s.auth.GetUser(r), 454 + LoggedInUser: s.oauth.GetUser(r), 455 455 RepoInfo: f.RepoInfo(s, user), 456 456 Pulls: pulls, 457 457 DidHandleMap: didHandleMap, ··· 461 461 } 462 462 463 463 func (s *State) PullComment(w http.ResponseWriter, r *http.Request) { 464 - user := s.auth.GetUser(r) 464 + user := s.oauth.GetUser(r) 465 465 f, err := s.fullyResolvedRepo(r) 466 466 if err != nil { 467 467 log.Println("failed to get repo and knot", err) ··· 519 519 } 520 520 521 521 atUri := f.RepoAt.String() 522 - client, _ := s.auth.AuthorizedClient(r) 523 - atResp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 522 + client, err := s.oauth.AuthorizedClient(r) 523 + if err != nil { 524 + log.Println("failed to get authorized client", err) 525 + s.pages.Notice(w, "pull-comment", "Failed to create comment.") 526 + return 527 + } 528 + atResp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 524 529 Collection: tangled.RepoPullCommentNSID, 525 530 Repo: user.Did, 526 531 Rkey: appview.TID(), ··· 568 573 } 569 574 570 575 func (s *State) NewPull(w http.ResponseWriter, r *http.Request) { 571 - user := s.auth.GetUser(r) 576 + user := s.oauth.GetUser(r) 572 577 f, err := s.fullyResolvedRepo(r) 573 578 if err != nil { 574 579 log.Println("failed to get repo and knot", err) ··· 577 582 578 583 switch r.Method { 579 584 case http.MethodGet: 580 - us, err := NewUnsignedClient(f.Knot, s.config.Dev) 585 + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) 581 586 if err != nil { 582 587 log.Printf("failed to create unsigned client for %s", f.Knot) 583 588 s.pages.Error503(w) ··· 646 651 return 647 652 } 648 653 649 - us, err := NewUnsignedClient(f.Knot, s.config.Dev) 654 + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) 650 655 if err != nil { 651 656 log.Printf("failed to create unsigned client to %s: %v", f.Knot, err) 652 657 s.pages.Notice(w, "pull", "Failed to create a pull request. Try again later.") ··· 689 694 } 690 695 } 691 696 692 - func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, sourceBranch string) { 697 + func (s *State) handleBranchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, sourceBranch string) { 693 698 pullSource := &db.PullSource{ 694 699 Branch: sourceBranch, 695 700 } ··· 698 703 } 699 704 700 705 // Generate a patch using /compare 701 - ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev) 706 + ksClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) 702 707 if err != nil { 703 708 log.Printf("failed to create signed client for %s: %s", f.Knot, err) 704 709 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") ··· 723 728 s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, sourceRev, pullSource, recordPullSource) 724 729 } 725 730 726 - func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, title, body, targetBranch, patch string) { 731 + func (s *State) handlePatchBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, title, body, targetBranch, patch string) { 727 732 if !patchutil.IsPatchValid(patch) { 728 733 s.pages.Notice(w, "pull", "Invalid patch format. Please provide a valid diff.") 729 734 return ··· 732 737 s.createPullRequest(w, r, f, user, title, body, targetBranch, patch, "", nil, nil) 733 738 } 734 739 735 - func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *auth.User, forkRepo string, title, body, targetBranch, sourceBranch string) { 740 + func (s *State) handleForkBasedPull(w http.ResponseWriter, r *http.Request, f *FullyResolvedRepo, user *oauth.User, forkRepo string, title, body, targetBranch, sourceBranch string) { 736 741 fork, err := db.GetForkByDid(s.db, user.Did, forkRepo) 737 742 if errors.Is(err, sql.ErrNoRows) { 738 743 s.pages.Notice(w, "pull", "No such fork.") ··· 750 755 return 751 756 } 752 757 753 - sc, err := NewSignedClient(fork.Knot, secret, s.config.Dev) 758 + sc, err := NewSignedClient(fork.Knot, secret, s.config.Core.Dev) 754 759 if err != nil { 755 760 log.Println("failed to create signed client:", err) 756 761 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 757 762 return 758 763 } 759 764 760 - us, err := NewUnsignedClient(fork.Knot, s.config.Dev) 765 + us, err := NewUnsignedClient(fork.Knot, s.config.Core.Dev) 761 766 if err != nil { 762 767 log.Println("failed to create unsigned client:", err) 763 768 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") ··· 816 821 w http.ResponseWriter, 817 822 r *http.Request, 818 823 f *FullyResolvedRepo, 819 - user *auth.User, 824 + user *oauth.User, 820 825 title, body, targetBranch string, 821 826 patch string, 822 827 sourceRev string, ··· 870 875 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 871 876 return 872 877 } 873 - client, _ := s.auth.AuthorizedClient(r) 878 + client, err := s.oauth.AuthorizedClient(r) 879 + if err != nil { 880 + log.Println("failed to get authorized client", err) 881 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 882 + return 883 + } 874 884 pullId, err := db.NextPullId(s.db, f.RepoAt) 875 885 if err != nil { 876 886 log.Println("failed to get pull id", err) ··· 878 888 return 879 889 } 880 890 881 - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 891 + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 882 892 Collection: tangled.RepoPullNSID, 883 893 Repo: user.Did, 884 894 Rkey: rkey, ··· 929 939 } 930 940 931 941 func (s *State) PatchUploadFragment(w http.ResponseWriter, r *http.Request) { 932 - user := s.auth.GetUser(r) 942 + user := s.oauth.GetUser(r) 933 943 f, err := s.fullyResolvedRepo(r) 934 944 if err != nil { 935 945 log.Println("failed to get repo and knot", err) ··· 942 952 } 943 953 944 954 func (s *State) CompareBranchesFragment(w http.ResponseWriter, r *http.Request) { 945 - user := s.auth.GetUser(r) 955 + user := s.oauth.GetUser(r) 946 956 f, err := s.fullyResolvedRepo(r) 947 957 if err != nil { 948 958 log.Println("failed to get repo and knot", err) 949 959 return 950 960 } 951 961 952 - us, err := NewUnsignedClient(f.Knot, s.config.Dev) 962 + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) 953 963 if err != nil { 954 964 log.Printf("failed to create unsigned client for %s", f.Knot) 955 965 s.pages.Error503(w) ··· 982 992 } 983 993 984 994 func (s *State) CompareForksFragment(w http.ResponseWriter, r *http.Request) { 985 - user := s.auth.GetUser(r) 995 + user := s.oauth.GetUser(r) 986 996 f, err := s.fullyResolvedRepo(r) 987 997 if err != nil { 988 998 log.Println("failed to get repo and knot", err) ··· 1002 1012 } 1003 1013 1004 1014 func (s *State) CompareForksBranchesFragment(w http.ResponseWriter, r *http.Request) { 1005 - user := s.auth.GetUser(r) 1015 + user := s.oauth.GetUser(r) 1006 1016 1007 1017 f, err := s.fullyResolvedRepo(r) 1008 1018 if err != nil { ··· 1019 1029 return 1020 1030 } 1021 1031 1022 - sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Dev) 1032 + sourceBranchesClient, err := NewUnsignedClient(repo.Knot, s.config.Core.Dev) 1023 1033 if err != nil { 1024 1034 log.Printf("failed to create unsigned client for %s", repo.Knot) 1025 1035 s.pages.Error503(w) ··· 1046 1056 return 1047 1057 } 1048 1058 1049 - targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Dev) 1059 + targetBranchesClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) 1050 1060 if err != nil { 1051 1061 log.Printf("failed to create unsigned client for target knot %s", f.Knot) 1052 1062 s.pages.Error503(w) ··· 1081 1091 } 1082 1092 1083 1093 func (s *State) ResubmitPull(w http.ResponseWriter, r *http.Request) { 1084 - user := s.auth.GetUser(r) 1094 + user := s.oauth.GetUser(r) 1085 1095 f, err := s.fullyResolvedRepo(r) 1086 1096 if err != nil { 1087 1097 log.Println("failed to get repo and knot", err) ··· 1117 1127 } 1118 1128 1119 1129 func (s *State) resubmitPatch(w http.ResponseWriter, r *http.Request) { 1120 - user := s.auth.GetUser(r) 1130 + user := s.oauth.GetUser(r) 1121 1131 1122 1132 pull, ok := r.Context().Value("pull").(*db.Pull) 1123 1133 if !ok { ··· 1159 1169 s.pages.Notice(w, "resubmit-error", "Failed to resubmit pull request. Try again later.") 1160 1170 return 1161 1171 } 1162 - client, _ := s.auth.AuthorizedClient(r) 1172 + client, err := s.oauth.AuthorizedClient(r) 1173 + if err != nil { 1174 + log.Println("failed to get authorized client", err) 1175 + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1176 + return 1177 + } 1163 1178 1164 - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) 1179 + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey) 1165 1180 if err != nil { 1166 1181 // failed to get record 1167 1182 s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.") 1168 1183 return 1169 1184 } 1170 1185 1171 - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1186 + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 1172 1187 Collection: tangled.RepoPullNSID, 1173 1188 Repo: user.Did, 1174 1189 Rkey: pull.Rkey, ··· 1200 1215 } 1201 1216 1202 1217 func (s *State) resubmitBranch(w http.ResponseWriter, r *http.Request) { 1203 - user := s.auth.GetUser(r) 1218 + user := s.oauth.GetUser(r) 1204 1219 1205 1220 pull, ok := r.Context().Value("pull").(*db.Pull) 1206 1221 if !ok { ··· 1227 1242 return 1228 1243 } 1229 1244 1230 - ksClient, err := NewUnsignedClient(f.Knot, s.config.Dev) 1245 + ksClient, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) 1231 1246 if err != nil { 1232 1247 log.Printf("failed to create client for %s: %s", f.Knot, err) 1233 1248 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") ··· 1268 1283 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1269 1284 return 1270 1285 } 1271 - client, _ := s.auth.AuthorizedClient(r) 1286 + client, err := s.oauth.AuthorizedClient(r) 1287 + if err != nil { 1288 + log.Println("failed to authorize client") 1289 + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1290 + return 1291 + } 1272 1292 1273 - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) 1293 + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey) 1274 1294 if err != nil { 1275 1295 // failed to get record 1276 1296 s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.") ··· 1280 1300 recordPullSource := &tangled.RepoPull_Source{ 1281 1301 Branch: pull.PullSource.Branch, 1282 1302 } 1283 - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1303 + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 1284 1304 Collection: tangled.RepoPullNSID, 1285 1305 Repo: user.Did, 1286 1306 Rkey: pull.Rkey, ··· 1313 1333 } 1314 1334 1315 1335 func (s *State) resubmitFork(w http.ResponseWriter, r *http.Request) { 1316 - user := s.auth.GetUser(r) 1336 + user := s.oauth.GetUser(r) 1317 1337 1318 1338 pull, ok := r.Context().Value("pull").(*db.Pull) 1319 1339 if !ok { ··· 1342 1362 } 1343 1363 1344 1364 // extract patch by performing compare 1345 - ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Dev) 1365 + ksClient, err := NewUnsignedClient(forkRepo.Knot, s.config.Core.Dev) 1346 1366 if err != nil { 1347 1367 log.Printf("failed to create client for %s: %s", forkRepo.Knot, err) 1348 1368 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") ··· 1357 1377 } 1358 1378 1359 1379 // update the hidden tracking branch to latest 1360 - signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Dev) 1380 + signedClient, err := NewSignedClient(forkRepo.Knot, secret, s.config.Core.Dev) 1361 1381 if err != nil { 1362 1382 log.Printf("failed to create signed client for %s: %s", forkRepo.Knot, err) 1363 1383 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") ··· 1406 1426 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1407 1427 return 1408 1428 } 1409 - client, _ := s.auth.AuthorizedClient(r) 1429 + client, err := s.oauth.AuthorizedClient(r) 1430 + if err != nil { 1431 + log.Println("failed to get client") 1432 + s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1433 + return 1434 + } 1410 1435 1411 - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, user.Did, pull.Rkey) 1436 + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoPullNSID, user.Did, pull.Rkey) 1412 1437 if err != nil { 1413 1438 // failed to get record 1414 1439 s.pages.Notice(w, "resubmit-error", "Failed to update pull, no record found on PDS.") ··· 1420 1445 Branch: pull.PullSource.Branch, 1421 1446 Repo: &repoAt, 1422 1447 } 1423 - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1448 + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 1424 1449 Collection: tangled.RepoPullNSID, 1425 1450 Repo: user.Did, 1426 1451 Rkey: pull.Rkey, ··· 1503 1528 log.Printf("failed to get primary email: %s", err) 1504 1529 } 1505 1530 1506 - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) 1531 + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) 1507 1532 if err != nil { 1508 1533 log.Printf("failed to create signed client for %s: %s", f.Knot, err) 1509 1534 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") ··· 1533 1558 } 1534 1559 1535 1560 func (s *State) ClosePull(w http.ResponseWriter, r *http.Request) { 1536 - user := s.auth.GetUser(r) 1561 + user := s.oauth.GetUser(r) 1537 1562 1538 1563 f, err := s.fullyResolvedRepo(r) 1539 1564 if err != nil { ··· 1587 1612 } 1588 1613 1589 1614 func (s *State) ReopenPull(w http.ResponseWriter, r *http.Request) { 1590 - user := s.auth.GetUser(r) 1615 + user := s.oauth.GetUser(r) 1591 1616 1592 1617 f, err := s.fullyResolvedRepo(r) 1593 1618 if err != nil {
+98 -60
appview/state/repo.go
··· 18 18 19 19 "tangled.sh/tangled.sh/core/api/tangled" 20 20 "tangled.sh/tangled.sh/core/appview" 21 - "tangled.sh/tangled.sh/core/appview/auth" 22 21 "tangled.sh/tangled.sh/core/appview/db" 22 + "tangled.sh/tangled.sh/core/appview/oauth" 23 23 "tangled.sh/tangled.sh/core/appview/pages" 24 24 "tangled.sh/tangled.sh/core/appview/pages/markup" 25 25 "tangled.sh/tangled.sh/core/appview/pages/repoinfo" ··· 45 45 return 46 46 } 47 47 48 - us, err := NewUnsignedClient(f.Knot, s.config.Dev) 48 + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) 49 49 if err != nil { 50 50 log.Printf("failed to create unsigned client for %s", f.Knot) 51 51 s.pages.Error503(w) ··· 119 119 120 120 emails := uniqueEmails(commitsTrunc) 121 121 122 - user := s.auth.GetUser(r) 122 + user := s.oauth.GetUser(r) 123 123 s.pages.RepoIndexPage(w, pages.RepoIndexParams{ 124 124 LoggedInUser: user, 125 125 RepoInfo: f.RepoInfo(s, user), ··· 150 150 151 151 ref := chi.URLParam(r, "ref") 152 152 153 - us, err := NewUnsignedClient(f.Knot, s.config.Dev) 153 + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) 154 154 if err != nil { 155 155 log.Println("failed to create unsigned client", err) 156 156 return ··· 190 190 tagMap[hash] = append(tagMap[hash], tag.Name) 191 191 } 192 192 193 - user := s.auth.GetUser(r) 193 + user := s.oauth.GetUser(r) 194 194 s.pages.RepoLog(w, pages.RepoLogParams{ 195 195 LoggedInUser: user, 196 196 TagMap: tagMap, ··· 209 209 return 210 210 } 211 211 212 - user := s.auth.GetUser(r) 212 + user := s.oauth.GetUser(r) 213 213 s.pages.EditRepoDescriptionFragment(w, pages.RepoDescriptionParams{ 214 214 RepoInfo: f.RepoInfo(s, user), 215 215 }) ··· 232 232 return 233 233 } 234 234 235 - user := s.auth.GetUser(r) 235 + user := s.oauth.GetUser(r) 236 236 237 237 switch r.Method { 238 238 case http.MethodGet: ··· 241 241 }) 242 242 return 243 243 case http.MethodPut: 244 - user := s.auth.GetUser(r) 244 + user := s.oauth.GetUser(r) 245 245 newDescription := r.FormValue("description") 246 - client, _ := s.auth.AuthorizedClient(r) 246 + client, err := s.oauth.AuthorizedClient(r) 247 + if err != nil { 248 + log.Println("failed to get client") 249 + s.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 250 + return 251 + } 247 252 248 253 // optimistic update 249 254 err = db.UpdateDescription(s.db, string(repoAt), newDescription) ··· 256 261 // this is a bit of a pain because the golang atproto impl does not allow nil SwapRecord field 257 262 // 258 263 // SwapRecord is optional and should happen automagically, but given that it does not, we have to perform two requests 259 - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoNSID, user.Did, rkey) 264 + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoNSID, user.Did, rkey) 260 265 if err != nil { 261 266 // failed to get record 262 267 s.pages.Notice(w, "repo-notice", "Failed to update description, no record found on PDS.") 263 268 return 264 269 } 265 - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 270 + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 266 271 Collection: tangled.RepoNSID, 267 272 Repo: user.Did, 268 273 Rkey: rkey, ··· 303 308 } 304 309 ref := chi.URLParam(r, "ref") 305 310 protocol := "http" 306 - if !s.config.Dev { 311 + if !s.config.Core.Dev { 307 312 protocol = "https" 308 313 } 309 314 ··· 331 336 return 332 337 } 333 338 334 - user := s.auth.GetUser(r) 339 + user := s.oauth.GetUser(r) 335 340 s.pages.RepoCommit(w, pages.RepoCommitParams{ 336 341 LoggedInUser: user, 337 342 RepoInfo: f.RepoInfo(s, user), ··· 351 356 ref := chi.URLParam(r, "ref") 352 357 treePath := chi.URLParam(r, "*") 353 358 protocol := "http" 354 - if !s.config.Dev { 359 + if !s.config.Core.Dev { 355 360 protocol = "https" 356 361 } 357 362 resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/tree/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, treePath)) ··· 380 385 return 381 386 } 382 387 383 - user := s.auth.GetUser(r) 388 + user := s.oauth.GetUser(r) 384 389 385 390 var breadcrumbs [][]string 386 391 breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/tree/%s", f.OwnerSlashRepo(), ref)}) ··· 411 416 return 412 417 } 413 418 414 - us, err := NewUnsignedClient(f.Knot, s.config.Dev) 419 + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) 415 420 if err != nil { 416 421 log.Println("failed to create unsigned client", err) 417 422 return ··· 451 456 } 452 457 } 453 458 454 - user := s.auth.GetUser(r) 459 + user := s.oauth.GetUser(r) 455 460 s.pages.RepoTags(w, pages.RepoTagsParams{ 456 461 LoggedInUser: user, 457 462 RepoInfo: f.RepoInfo(s, user), ··· 469 474 return 470 475 } 471 476 472 - us, err := NewUnsignedClient(f.Knot, s.config.Dev) 477 + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) 473 478 if err != nil { 474 479 log.Println("failed to create unsigned client", err) 475 480 return ··· 511 516 return strings.Compare(a.Name, b.Name) * -1 512 517 }) 513 518 514 - user := s.auth.GetUser(r) 519 + user := s.oauth.GetUser(r) 515 520 s.pages.RepoBranches(w, pages.RepoBranchesParams{ 516 521 LoggedInUser: user, 517 522 RepoInfo: f.RepoInfo(s, user), ··· 530 535 ref := chi.URLParam(r, "ref") 531 536 filePath := chi.URLParam(r, "*") 532 537 protocol := "http" 533 - if !s.config.Dev { 538 + if !s.config.Core.Dev { 534 539 protocol = "https" 535 540 } 536 541 resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath)) ··· 568 573 showRendered = r.URL.Query().Get("code") != "true" 569 574 } 570 575 571 - user := s.auth.GetUser(r) 576 + user := s.oauth.GetUser(r) 572 577 s.pages.RepoBlob(w, pages.RepoBlobParams{ 573 578 LoggedInUser: user, 574 579 RepoInfo: f.RepoInfo(s, user), ··· 591 596 filePath := chi.URLParam(r, "*") 592 597 593 598 protocol := "http" 594 - if !s.config.Dev { 599 + if !s.config.Core.Dev { 595 600 protocol = "https" 596 601 } 597 602 resp, err := http.Get(fmt.Sprintf("%s://%s/%s/%s/blob/%s/%s", protocol, f.Knot, f.OwnerDid(), f.RepoName, ref, filePath)) ··· 652 657 return 653 658 } 654 659 655 - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) 660 + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) 656 661 if err != nil { 657 662 log.Println("failed to create client to ", f.Knot) 658 663 return ··· 714 719 } 715 720 716 721 func (s *State) DeleteRepo(w http.ResponseWriter, r *http.Request) { 717 - user := s.auth.GetUser(r) 722 + user := s.oauth.GetUser(r) 718 723 719 724 f, err := s.fullyResolvedRepo(r) 720 725 if err != nil { ··· 723 728 } 724 729 725 730 // remove record from pds 726 - xrpcClient, _ := s.auth.AuthorizedClient(r) 731 + xrpcClient, err := s.oauth.AuthorizedClient(r) 732 + if err != nil { 733 + log.Println("failed to get authorized client", err) 734 + return 735 + } 727 736 repoRkey := f.RepoAt.RecordKey().String() 728 - _, err = comatproto.RepoDeleteRecord(r.Context(), xrpcClient, &comatproto.RepoDeleteRecord_Input{ 737 + _, err = xrpcClient.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ 729 738 Collection: tangled.RepoNSID, 730 739 Repo: user.Did, 731 740 Rkey: repoRkey, ··· 743 752 return 744 753 } 745 754 746 - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) 755 + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) 747 756 if err != nil { 748 757 log.Println("failed to create client to ", f.Knot) 749 758 return ··· 838 847 return 839 848 } 840 849 841 - ksClient, err := NewSignedClient(f.Knot, secret, s.config.Dev) 850 + ksClient, err := NewSignedClient(f.Knot, secret, s.config.Core.Dev) 842 851 if err != nil { 843 852 log.Println("failed to create client to ", f.Knot) 844 853 return ··· 868 877 switch r.Method { 869 878 case http.MethodGet: 870 879 // for now, this is just pubkeys 871 - user := s.auth.GetUser(r) 880 + user := s.oauth.GetUser(r) 872 881 repoCollaborators, err := f.Collaborators(r.Context(), s) 873 882 if err != nil { 874 883 log.Println("failed to get collaborators", err) ··· 884 893 885 894 var branchNames []string 886 895 var defaultBranch string 887 - us, err := NewUnsignedClient(f.Knot, s.config.Dev) 896 + us, err := NewUnsignedClient(f.Knot, s.config.Core.Dev) 888 897 if err != nil { 889 898 log.Println("failed to create unsigned client", err) 890 899 } else { ··· 1008 1017 return collaborators, nil 1009 1018 } 1010 1019 1011 - func (f *FullyResolvedRepo) RepoInfo(s *State, u *auth.User) repoinfo.RepoInfo { 1020 + func (f *FullyResolvedRepo) RepoInfo(s *State, u *oauth.User) repoinfo.RepoInfo { 1012 1021 isStarred := false 1013 1022 if u != nil { 1014 1023 isStarred = db.GetStarStatus(s.db, u.Did, syntax.ATURI(f.RepoAt)) ··· 1051 1060 1052 1061 knot := f.Knot 1053 1062 var disableFork bool 1054 - us, err := NewUnsignedClient(knot, s.config.Dev) 1063 + us, err := NewUnsignedClient(knot, s.config.Core.Dev) 1055 1064 if err != nil { 1056 1065 log.Printf("failed to create unsigned client for %s: %v", knot, err) 1057 1066 } else { ··· 1105 1114 } 1106 1115 1107 1116 func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) { 1108 - user := s.auth.GetUser(r) 1117 + user := s.oauth.GetUser(r) 1109 1118 f, err := s.fullyResolvedRepo(r) 1110 1119 if err != nil { 1111 1120 log.Println("failed to get repo and knot", err) ··· 1159 1168 } 1160 1169 1161 1170 func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) { 1162 - user := s.auth.GetUser(r) 1171 + user := s.oauth.GetUser(r) 1163 1172 f, err := s.fullyResolvedRepo(r) 1164 1173 if err != nil { 1165 1174 log.Println("failed to get repo and knot", err) ··· 1195 1204 1196 1205 closed := tangled.RepoIssueStateClosed 1197 1206 1198 - client, _ := s.auth.AuthorizedClient(r) 1199 - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1207 + client, err := s.oauth.AuthorizedClient(r) 1208 + if err != nil { 1209 + log.Println("failed to get authorized client", err) 1210 + return 1211 + } 1212 + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 1200 1213 Collection: tangled.RepoIssueStateNSID, 1201 1214 Repo: user.Did, 1202 1215 Rkey: appview.TID(), ··· 1214 1227 return 1215 1228 } 1216 1229 1217 - err := db.CloseIssue(s.db, f.RepoAt, issueIdInt) 1230 + err = db.CloseIssue(s.db, f.RepoAt, issueIdInt) 1218 1231 if err != nil { 1219 1232 log.Println("failed to close issue", err) 1220 1233 s.pages.Notice(w, "issue-action", "Failed to close issue. Try again later.") ··· 1231 1244 } 1232 1245 1233 1246 func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) { 1234 - user := s.auth.GetUser(r) 1247 + user := s.oauth.GetUser(r) 1235 1248 f, err := s.fullyResolvedRepo(r) 1236 1249 if err != nil { 1237 1250 log.Println("failed to get repo and knot", err) ··· 1279 1292 } 1280 1293 1281 1294 func (s *State) NewIssueComment(w http.ResponseWriter, r *http.Request) { 1282 - user := s.auth.GetUser(r) 1295 + user := s.oauth.GetUser(r) 1283 1296 f, err := s.fullyResolvedRepo(r) 1284 1297 if err != nil { 1285 1298 log.Println("failed to get repo and knot", err) ··· 1330 1343 } 1331 1344 1332 1345 atUri := f.RepoAt.String() 1333 - client, _ := s.auth.AuthorizedClient(r) 1334 - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1346 + client, err := s.oauth.AuthorizedClient(r) 1347 + if err != nil { 1348 + log.Println("failed to get authorized client", err) 1349 + s.pages.Notice(w, "issue-comment", "Failed to create comment.") 1350 + return 1351 + } 1352 + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 1335 1353 Collection: tangled.RepoIssueCommentNSID, 1336 1354 Repo: user.Did, 1337 1355 Rkey: rkey, ··· 1358 1376 } 1359 1377 1360 1378 func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) { 1361 - user := s.auth.GetUser(r) 1379 + user := s.oauth.GetUser(r) 1362 1380 f, err := s.fullyResolvedRepo(r) 1363 1381 if err != nil { 1364 1382 log.Println("failed to get repo and knot", err) ··· 1417 1435 } 1418 1436 1419 1437 func (s *State) EditIssueComment(w http.ResponseWriter, r *http.Request) { 1420 - user := s.auth.GetUser(r) 1438 + user := s.oauth.GetUser(r) 1421 1439 f, err := s.fullyResolvedRepo(r) 1422 1440 if err != nil { 1423 1441 log.Println("failed to get repo and knot", err) ··· 1469 1487 case http.MethodPost: 1470 1488 // extract form value 1471 1489 newBody := r.FormValue("body") 1472 - client, _ := s.auth.AuthorizedClient(r) 1490 + client, err := s.oauth.AuthorizedClient(r) 1491 + if err != nil { 1492 + log.Println("failed to get authorized client", err) 1493 + s.pages.Notice(w, "issue-comment", "Failed to create comment.") 1494 + return 1495 + } 1473 1496 rkey := comment.Rkey 1474 1497 1475 1498 // optimistic update ··· 1484 1507 // rkey is optional, it was introduced later 1485 1508 if comment.Rkey != "" { 1486 1509 // update the record on pds 1487 - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoIssueCommentNSID, user.Did, rkey) 1510 + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoIssueCommentNSID, user.Did, rkey) 1488 1511 if err != nil { 1489 1512 // failed to get record 1490 1513 log.Println(err, rkey) ··· 1499 1522 createdAt := record["createdAt"].(string) 1500 1523 commentIdInt64 := int64(commentIdInt) 1501 1524 1502 - _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1525 + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 1503 1526 Collection: tangled.RepoIssueCommentNSID, 1504 1527 Repo: user.Did, 1505 1528 Rkey: rkey, ··· 1542 1565 } 1543 1566 1544 1567 func (s *State) DeleteIssueComment(w http.ResponseWriter, r *http.Request) { 1545 - user := s.auth.GetUser(r) 1568 + user := s.oauth.GetUser(r) 1546 1569 f, err := s.fullyResolvedRepo(r) 1547 1570 if err != nil { 1548 1571 log.Println("failed to get repo and knot", err) ··· 1599 1622 1600 1623 // delete from pds 1601 1624 if comment.Rkey != "" { 1602 - client, _ := s.auth.AuthorizedClient(r) 1603 - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ 1625 + client, err := s.oauth.AuthorizedClient(r) 1626 + if err != nil { 1627 + log.Println("failed to get authorized client", err) 1628 + s.pages.Notice(w, "issue-comment", "Failed to delete comment.") 1629 + return 1630 + } 1631 + _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ 1604 1632 Collection: tangled.GraphFollowNSID, 1605 1633 Repo: user.Did, 1606 1634 Rkey: comment.Rkey, ··· 1647 1675 page = pagination.FirstPage() 1648 1676 } 1649 1677 1650 - user := s.auth.GetUser(r) 1678 + user := s.oauth.GetUser(r) 1651 1679 f, err := s.fullyResolvedRepo(r) 1652 1680 if err != nil { 1653 1681 log.Println("failed to get repo and knot", err) ··· 1676 1704 } 1677 1705 1678 1706 s.pages.RepoIssues(w, pages.RepoIssuesParams{ 1679 - LoggedInUser: s.auth.GetUser(r), 1707 + LoggedInUser: s.oauth.GetUser(r), 1680 1708 RepoInfo: f.RepoInfo(s, user), 1681 1709 Issues: issues, 1682 1710 DidHandleMap: didHandleMap, ··· 1687 1715 } 1688 1716 1689 1717 func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) { 1690 - user := s.auth.GetUser(r) 1718 + user := s.oauth.GetUser(r) 1691 1719 1692 1720 f, err := s.fullyResolvedRepo(r) 1693 1721 if err != nil { ··· 1735 1763 return 1736 1764 } 1737 1765 1738 - client, _ := s.auth.AuthorizedClient(r) 1766 + client, err := s.oauth.AuthorizedClient(r) 1767 + if err != nil { 1768 + log.Println("failed to get authorized client", err) 1769 + s.pages.Notice(w, "issues", "Failed to create issue.") 1770 + return 1771 + } 1739 1772 atUri := f.RepoAt.String() 1740 - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 1773 + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 1741 1774 Collection: tangled.RepoIssueNSID, 1742 1775 Repo: user.Did, 1743 1776 Rkey: appview.TID(), ··· 1770 1803 } 1771 1804 1772 1805 func (s *State) ForkRepo(w http.ResponseWriter, r *http.Request) { 1773 - user := s.auth.GetUser(r) 1806 + user := s.oauth.GetUser(r) 1774 1807 f, err := s.fullyResolvedRepo(r) 1775 1808 if err != nil { 1776 1809 log.Printf("failed to resolve source repo: %v", err) ··· 1779 1812 1780 1813 switch r.Method { 1781 1814 case http.MethodGet: 1782 - user := s.auth.GetUser(r) 1815 + user := s.oauth.GetUser(r) 1783 1816 knots, err := s.enforcer.GetDomainsForUser(user.Did) 1784 1817 if err != nil { 1785 1818 s.pages.Notice(w, "repo", "Invalid user account.") ··· 1829 1862 return 1830 1863 } 1831 1864 1832 - client, err := NewSignedClient(knot, secret, s.config.Dev) 1865 + client, err := NewSignedClient(knot, secret, s.config.Core.Dev) 1833 1866 if err != nil { 1834 1867 s.pages.Notice(w, "repo", "Failed to reach knot server.") 1835 1868 return 1836 1869 } 1837 1870 1838 1871 var uri string 1839 - if s.config.Dev { 1872 + if s.config.Core.Dev { 1840 1873 uri = "http" 1841 1874 } else { 1842 1875 uri = "https" ··· 1883 1916 // continue 1884 1917 } 1885 1918 1886 - xrpcClient, _ := s.auth.AuthorizedClient(r) 1919 + xrpcClient, err := s.oauth.AuthorizedClient(r) 1920 + if err != nil { 1921 + log.Println("failed to get authorized client", err) 1922 + s.pages.Notice(w, "repo", "Failed to create repository.") 1923 + return 1924 + } 1887 1925 1888 1926 createdAt := time.Now().Format(time.RFC3339) 1889 - atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{ 1927 + atresp, err := xrpcClient.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 1890 1928 Collection: tangled.RepoNSID, 1891 1929 Repo: user.Did, 1892 1930 Rkey: rkey,
+3 -3
appview/state/repo_util.go
··· 12 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 13 "github.com/go-chi/chi/v5" 14 14 "github.com/go-git/go-git/v5/plumbing/object" 15 - "tangled.sh/tangled.sh/core/appview/auth" 16 15 "tangled.sh/tangled.sh/core/appview/db" 16 + "tangled.sh/tangled.sh/core/appview/oauth" 17 17 "tangled.sh/tangled.sh/core/appview/pages/repoinfo" 18 18 ) 19 19 ··· 45 45 ref := chi.URLParam(r, "ref") 46 46 47 47 if ref == "" { 48 - us, err := NewUnsignedClient(knot, s.config.Dev) 48 + us, err := NewUnsignedClient(knot, s.config.Core.Dev) 49 49 if err != nil { 50 50 return nil, err 51 51 } ··· 73 73 }, nil 74 74 } 75 75 76 - func RolesInRepo(s *State, u *auth.User, f *FullyResolvedRepo) repoinfo.RolesInRepo { 76 + func RolesInRepo(s *State, u *oauth.User, f *FullyResolvedRepo) repoinfo.RolesInRepo { 77 77 if u != nil { 78 78 r := s.enforcer.GetPermissionsInRepo(u.Did, f.Knot, f.DidSlashRepo()) 79 79 return repoinfo.RolesInRepo{r}
+34 -19
appview/state/router.go
··· 5 5 "strings" 6 6 7 7 "github.com/go-chi/chi/v5" 8 + "github.com/gorilla/sessions" 8 9 "tangled.sh/tangled.sh/core/appview/middleware" 10 + oauthhandler "tangled.sh/tangled.sh/core/appview/oauth/handler" 9 11 "tangled.sh/tangled.sh/core/appview/settings" 10 12 "tangled.sh/tangled.sh/core/appview/state/userutil" 11 13 ) ··· 67 69 r.Route("/tags", func(r chi.Router) { 68 70 r.Get("/", s.RepoTags) 69 71 r.Route("/{tag}", func(r chi.Router) { 70 - r.Use(middleware.AuthMiddleware(s.auth)) 72 + r.Use(middleware.AuthMiddleware(s.oauth)) 71 73 // require auth to download for now 72 74 r.Get("/download/{file}", s.DownloadArtifact) 73 75 ··· 90 92 r.Get("/{issue}", s.RepoSingleIssue) 91 93 92 94 r.Group(func(r chi.Router) { 93 - r.Use(middleware.AuthMiddleware(s.auth)) 95 + r.Use(middleware.AuthMiddleware(s.oauth)) 94 96 r.Get("/new", s.NewIssue) 95 97 r.Post("/new", s.NewIssue) 96 98 r.Post("/{issue}/comment", s.NewIssueComment) ··· 106 108 }) 107 109 108 110 r.Route("/fork", func(r chi.Router) { 109 - r.Use(middleware.AuthMiddleware(s.auth)) 111 + r.Use(middleware.AuthMiddleware(s.oauth)) 110 112 r.Get("/", s.ForkRepo) 111 113 r.Post("/", s.ForkRepo) 112 114 }) 113 115 114 116 r.Route("/pulls", func(r chi.Router) { 115 117 r.Get("/", s.RepoPulls) 116 - r.With(middleware.AuthMiddleware(s.auth)).Route("/new", func(r chi.Router) { 118 + r.With(middleware.AuthMiddleware(s.oauth)).Route("/new", func(r chi.Router) { 117 119 r.Get("/", s.NewPull) 118 120 r.Get("/patch-upload", s.PatchUploadFragment) 119 121 r.Post("/validate-patch", s.ValidatePatch) ··· 131 133 r.Get("/", s.RepoPullPatch) 132 134 r.Get("/interdiff", s.RepoPullInterdiff) 133 135 r.Get("/actions", s.PullActions) 134 - r.With(middleware.AuthMiddleware(s.auth)).Route("/comment", func(r chi.Router) { 136 + r.With(middleware.AuthMiddleware(s.oauth)).Route("/comment", func(r chi.Router) { 135 137 r.Get("/", s.PullComment) 136 138 r.Post("/", s.PullComment) 137 139 }) ··· 142 144 }) 143 145 144 146 r.Group(func(r chi.Router) { 145 - r.Use(middleware.AuthMiddleware(s.auth)) 147 + r.Use(middleware.AuthMiddleware(s.oauth)) 146 148 r.Route("/resubmit", func(r chi.Router) { 147 149 r.Get("/", s.ResubmitPull) 148 150 r.Post("/", s.ResubmitPull) ··· 165 167 166 168 // settings routes, needs auth 167 169 r.Group(func(r chi.Router) { 168 - r.Use(middleware.AuthMiddleware(s.auth)) 170 + r.Use(middleware.AuthMiddleware(s.oauth)) 169 171 // repo description can only be edited by owner 170 172 r.With(RepoPermissionMiddleware(s, "repo:owner")).Route("/description", func(r chi.Router) { 171 173 r.Put("/", s.RepoDescription) ··· 196 198 197 199 r.Get("/", s.Timeline) 198 200 199 - r.With(middleware.AuthMiddleware(s.auth)).Post("/logout", s.Logout) 201 + r.With(middleware.AuthMiddleware(s.oauth)).Post("/logout", s.Logout) 200 202 201 - r.Route("/login", func(r chi.Router) { 202 - r.Get("/", s.Login) 203 - r.Post("/", s.Login) 204 - }) 203 + // r.Route("/login", func(r chi.Router) { 204 + // r.Get("/", s.Login) 205 + // r.Post("/", s.Login) 206 + // }) 205 207 206 208 r.Route("/knots", func(r chi.Router) { 207 - r.Use(middleware.AuthMiddleware(s.auth)) 209 + r.Use(middleware.AuthMiddleware(s.oauth)) 208 210 r.Get("/", s.Knots) 209 211 r.Post("/key", s.RegistrationKey) 210 212 ··· 222 224 223 225 r.Route("/repo", func(r chi.Router) { 224 226 r.Route("/new", func(r chi.Router) { 225 - r.Use(middleware.AuthMiddleware(s.auth)) 227 + r.Use(middleware.AuthMiddleware(s.oauth)) 226 228 r.Get("/", s.NewRepo) 227 229 r.Post("/", s.NewRepo) 228 230 }) 229 231 // r.Post("/import", s.ImportRepo) 230 232 }) 231 233 232 - r.With(middleware.AuthMiddleware(s.auth)).Route("/follow", func(r chi.Router) { 234 + r.With(middleware.AuthMiddleware(s.oauth)).Route("/follow", func(r chi.Router) { 233 235 r.Post("/", s.Follow) 234 236 r.Delete("/", s.Follow) 235 237 }) 236 238 237 - r.With(middleware.AuthMiddleware(s.auth)).Route("/star", func(r chi.Router) { 239 + r.With(middleware.AuthMiddleware(s.oauth)).Route("/star", func(r chi.Router) { 238 240 r.Post("/", s.Star) 239 241 r.Delete("/", s.Star) 240 242 }) 241 243 242 244 r.Route("/profile", func(r chi.Router) { 243 - r.Use(middleware.AuthMiddleware(s.auth)) 245 + r.Use(middleware.AuthMiddleware(s.oauth)) 244 246 r.Get("/edit-bio", s.EditBioFragment) 245 247 r.Get("/edit-pins", s.EditPinsFragment) 246 248 r.Post("/bio", s.UpdateProfileBio) ··· 248 250 }) 249 251 250 252 r.Mount("/settings", s.SettingsRouter()) 251 - 253 + r.Mount("/oauth", s.OAuthRouter()) 252 254 r.Get("/keys/{user}", s.Keys) 253 255 254 256 r.NotFound(func(w http.ResponseWriter, r *http.Request) { ··· 257 259 return r 258 260 } 259 261 262 + func (s *State) OAuthRouter() http.Handler { 263 + oauth := &oauthhandler.OAuthHandler{ 264 + Config: s.config, 265 + Pages: s.pages, 266 + Resolver: s.resolver, 267 + Db: s.db, 268 + Store: sessions.NewCookieStore([]byte(s.config.Core.CookieSecret)), 269 + OAuth: s.oauth, 270 + } 271 + 272 + return oauth.Router() 273 + } 274 + 260 275 func (s *State) SettingsRouter() http.Handler { 261 276 settings := &settings.Settings{ 262 277 Db: s.db, 263 - Auth: s.auth, 278 + OAuth: s.oauth, 264 279 Pages: s.pages, 265 280 Config: s.config, 266 281 }
+8 -4
appview/state/star.go
··· 15 15 ) 16 16 17 17 func (s *State) Star(w http.ResponseWriter, r *http.Request) { 18 - currentUser := s.auth.GetUser(r) 18 + currentUser := s.oauth.GetUser(r) 19 19 20 20 subject := r.URL.Query().Get("subject") 21 21 if subject == "" { ··· 29 29 return 30 30 } 31 31 32 - client, _ := s.auth.AuthorizedClient(r) 32 + client, err := s.oauth.AuthorizedClient(r) 33 + if err != nil { 34 + log.Println("failed to authorize client", err) 35 + return 36 + } 33 37 34 38 switch r.Method { 35 39 case http.MethodPost: 36 40 createdAt := time.Now().Format(time.RFC3339) 37 41 rkey := appview.TID() 38 - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 42 + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 39 43 Collection: tangled.FeedStarNSID, 40 44 Repo: currentUser.Did, 41 45 Rkey: rkey, ··· 80 84 return 81 85 } 82 86 83 - _, err = comatproto.RepoDeleteRecord(r.Context(), client, &comatproto.RepoDeleteRecord_Input{ 87 + _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ 84 88 Collection: tangled.FeedStarNSID, 85 89 Repo: currentUser.Did, 86 90 Rkey: star.Rkey,
+98 -85
appview/state/state.go
··· 21 21 "tangled.sh/tangled.sh/core/appview" 22 22 "tangled.sh/tangled.sh/core/appview/auth" 23 23 "tangled.sh/tangled.sh/core/appview/db" 24 + "tangled.sh/tangled.sh/core/appview/oauth" 24 25 "tangled.sh/tangled.sh/core/appview/pages" 25 26 "tangled.sh/tangled.sh/core/jetstream" 26 27 "tangled.sh/tangled.sh/core/rbac" ··· 29 30 type State struct { 30 31 db *db.DB 31 32 auth *auth.Auth 33 + oauth *oauth.OAuth 32 34 enforcer *rbac.Enforcer 33 - tidClock *syntax.TIDClock 35 + tidClock syntax.TIDClock 34 36 pages *pages.Pages 35 37 resolver *appview.Resolver 36 38 jc *jetstream.JetstreamClient ··· 38 40 } 39 41 40 42 func Make(config *appview.Config) (*State, error) { 41 - d, err := db.Make(config.DbPath) 43 + d, err := db.Make(config.Core.DbPath) 42 44 if err != nil { 43 45 return nil, err 44 46 } 45 47 46 - auth, err := auth.Make(config.CookieSecret) 48 + auth, err := auth.Make(config.Core.CookieSecret) 47 49 if err != nil { 48 50 return nil, err 49 51 } 50 52 51 - enforcer, err := rbac.NewEnforcer(config.DbPath) 53 + enforcer, err := rbac.NewEnforcer(config.Core.DbPath) 52 54 if err != nil { 53 55 return nil, err 54 56 } ··· 58 60 pgs := pages.NewPages(config) 59 61 60 62 resolver := appview.NewResolver() 63 + 64 + oauth := oauth.NewOAuth(d, config) 61 65 62 66 wrapper := db.DbWrapper{d} 63 67 jc, err := jetstream.NewJetstreamClient( 64 - config.JetstreamEndpoint, 68 + config.Jetstream.Endpoint, 65 69 "appview", 66 70 []string{ 67 71 tangled.GraphFollowNSID, ··· 86 90 state := &State{ 87 91 d, 88 92 auth, 93 + oauth, 89 94 enforcer, 90 95 clock, 91 96 pgs, ··· 101 106 return c.Next().String() 102 107 } 103 108 104 - func (s *State) Login(w http.ResponseWriter, r *http.Request) { 105 - ctx := r.Context() 109 + // func (s *State) Login(w http.ResponseWriter, r *http.Request) { 110 + // ctx := r.Context() 106 111 107 - switch r.Method { 108 - case http.MethodGet: 109 - err := s.pages.Login(w, pages.LoginParams{}) 110 - if err != nil { 111 - log.Printf("rendering login page: %s", err) 112 - } 112 + // switch r.Method { 113 + // case http.MethodGet: 114 + // err := s.pages.Login(w, pages.LoginParams{}) 115 + // if err != nil { 116 + // log.Printf("rendering login page: %s", err) 117 + // } 113 118 114 - return 115 - case http.MethodPost: 116 - handle := strings.TrimPrefix(r.FormValue("handle"), "@") 117 - appPassword := r.FormValue("app_password") 119 + // return 120 + // case http.MethodPost: 121 + // handle := strings.TrimPrefix(r.FormValue("handle"), "@") 122 + // appPassword := r.FormValue("app_password") 118 123 119 - resolved, err := s.resolver.ResolveIdent(ctx, handle) 120 - if err != nil { 121 - log.Println("failed to resolve handle:", err) 122 - s.pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle)) 123 - return 124 - } 124 + // resolved, err := s.resolver.ResolveIdent(ctx, handle) 125 + // if err != nil { 126 + // log.Println("failed to resolve handle:", err) 127 + // s.pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle)) 128 + // return 129 + // } 125 130 126 - atSession, err := s.auth.CreateInitialSession(ctx, resolved, appPassword) 127 - if err != nil { 128 - s.pages.Notice(w, "login-msg", "Invalid handle or password.") 129 - return 130 - } 131 - sessionish := auth.CreateSessionWrapper{ServerCreateSession_Output: atSession} 131 + // atSession, err := s.oauth.CreateInitialSession(ctx, resolved, appPassword) 132 + // if err != nil { 133 + // s.pages.Notice(w, "login-msg", "Invalid handle or password.") 134 + // return 135 + // } 136 + // sessionish := auth.CreateSessionWrapper{ServerCreateSession_Output: atSession} 132 137 133 - err = s.auth.StoreSession(r, w, &sessionish, resolved.PDSEndpoint()) 134 - if err != nil { 135 - s.pages.Notice(w, "login-msg", "Failed to login, try again later.") 136 - return 137 - } 138 + // err = s.oauth.StoreSession(r, w, &sessionish, resolved.PDSEndpoint()) 139 + // if err != nil { 140 + // s.pages.Notice(w, "login-msg", "Failed to login, try again later.") 141 + // return 142 + // } 138 143 139 - log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did) 144 + // log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did) 140 145 141 - did := resolved.DID.String() 142 - defaultKnot := "knot1.tangled.sh" 146 + // did := resolved.DID.String() 147 + // defaultKnot := "knot1.tangled.sh" 143 148 144 - go func() { 145 - log.Printf("adding %s to default knot", did) 146 - err = s.enforcer.AddMember(defaultKnot, did) 147 - if err != nil { 148 - log.Println("failed to add user to knot1.tangled.sh: ", err) 149 - return 150 - } 151 - err = s.enforcer.E.SavePolicy() 152 - if err != nil { 153 - log.Println("failed to add user to knot1.tangled.sh: ", err) 154 - return 155 - } 149 + // go func() { 150 + // log.Printf("adding %s to default knot", did) 151 + // err = s.enforcer.AddMember(defaultKnot, did) 152 + // if err != nil { 153 + // log.Println("failed to add user to knot1.tangled.sh: ", err) 154 + // return 155 + // } 156 + // err = s.enforcer.E.SavePolicy() 157 + // if err != nil { 158 + // log.Println("failed to add user to knot1.tangled.sh: ", err) 159 + // return 160 + // } 156 161 157 - secret, err := db.GetRegistrationKey(s.db, defaultKnot) 158 - if err != nil { 159 - log.Println("failed to get registration key for knot1.tangled.sh") 160 - return 161 - } 162 - signedClient, err := NewSignedClient(defaultKnot, secret, s.config.Dev) 163 - resp, err := signedClient.AddMember(did) 164 - if err != nil { 165 - log.Println("failed to add user to knot1.tangled.sh: ", err) 166 - return 167 - } 162 + // secret, err := db.GetRegistrationKey(s.db, defaultKnot) 163 + // if err != nil { 164 + // log.Println("failed to get registration key for knot1.tangled.sh") 165 + // return 166 + // } 167 + // signedClient, err := NewSignedClient(defaultKnot, secret, s.config.Core.Dev) 168 + // resp, err := signedClient.AddMember(did) 169 + // if err != nil { 170 + // log.Println("failed to add user to knot1.tangled.sh: ", err) 171 + // return 172 + // } 168 173 169 - if resp.StatusCode != http.StatusNoContent { 170 - log.Println("failed to add user to knot1.tangled.sh: ", resp.StatusCode) 171 - return 172 - } 173 - }() 174 + // if resp.StatusCode != http.StatusNoContent { 175 + // log.Println("failed to add user to knot1.tangled.sh: ", resp.StatusCode) 176 + // return 177 + // } 178 + // }() 174 179 175 - s.pages.HxRedirect(w, "/") 176 - return 177 - } 178 - } 180 + // s.pages.HxRedirect(w, "/") 181 + // return 182 + // } 183 + // } 179 184 180 185 func (s *State) Logout(w http.ResponseWriter, r *http.Request) { 181 - s.auth.ClearSession(r, w) 186 + s.oauth.ClearSession(r, w) 182 187 w.Header().Set("HX-Redirect", "/login") 183 188 w.WriteHeader(http.StatusSeeOther) 184 189 } 185 190 186 191 func (s *State) Timeline(w http.ResponseWriter, r *http.Request) { 187 - user := s.auth.GetUser(r) 192 + user := s.oauth.GetUser(r) 188 193 189 194 timeline, err := db.MakeTimeline(s.db) 190 195 if err != nil { ··· 235 240 236 241 return 237 242 case http.MethodPost: 238 - session, err := s.auth.Store.Get(r, appview.SessionName) 243 + session, err := s.oauth.Store.Get(r, appview.SessionName) 239 244 if err != nil || session.IsNew { 240 245 log.Println("unauthorized attempt to generate registration key") 241 246 http.Error(w, "Forbidden", http.StatusUnauthorized) ··· 297 302 298 303 // create a signed request and check if a node responds to that 299 304 func (s *State) InitKnotServer(w http.ResponseWriter, r *http.Request) { 300 - user := s.auth.GetUser(r) 305 + user := s.oauth.GetUser(r) 301 306 302 307 domain := chi.URLParam(r, "domain") 303 308 if domain == "" { ··· 312 317 return 313 318 } 314 319 315 - client, err := NewSignedClient(domain, secret, s.config.Dev) 320 + client, err := NewSignedClient(domain, secret, s.config.Core.Dev) 316 321 if err != nil { 317 322 log.Println("failed to create client to ", domain) 318 323 } ··· 421 426 return 422 427 } 423 428 424 - user := s.auth.GetUser(r) 429 + user := s.oauth.GetUser(r) 425 430 reg, err := db.RegistrationByDomain(s.db, domain) 426 431 if err != nil { 427 432 w.Write([]byte("failed to pull up registration info")) ··· 469 474 // get knots registered by this user 470 475 func (s *State) Knots(w http.ResponseWriter, r *http.Request) { 471 476 // for now, this is just pubkeys 472 - user := s.auth.GetUser(r) 477 + user := s.oauth.GetUser(r) 473 478 registrations, err := db.RegistrationsByDid(s.db, user.Did) 474 479 if err != nil { 475 480 log.Println(err) ··· 522 527 log.Printf("adding %s to %s\n", subjectIdentity.Handle.String(), domain) 523 528 524 529 // announce this relation into the firehose, store into owners' pds 525 - client, _ := s.auth.AuthorizedClient(r) 526 - currentUser := s.auth.GetUser(r) 530 + client, err := s.oauth.AuthorizedClient(r) 531 + if err != nil { 532 + http.Error(w, "failed to authorize client", http.StatusInternalServerError) 533 + return 534 + } 535 + currentUser := s.oauth.GetUser(r) 527 536 createdAt := time.Now().Format(time.RFC3339) 528 - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 537 + resp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 529 538 Collection: tangled.KnotMemberNSID, 530 539 Repo: currentUser.Did, 531 540 Rkey: appview.TID(), ··· 550 559 return 551 560 } 552 561 553 - ksClient, err := NewSignedClient(domain, secret, s.config.Dev) 562 + ksClient, err := NewSignedClient(domain, secret, s.config.Core.Dev) 554 563 if err != nil { 555 564 log.Println("failed to create client to ", domain) 556 565 return ··· 614 623 func (s *State) NewRepo(w http.ResponseWriter, r *http.Request) { 615 624 switch r.Method { 616 625 case http.MethodGet: 617 - user := s.auth.GetUser(r) 626 + user := s.oauth.GetUser(r) 618 627 knots, err := s.enforcer.GetDomainsForUser(user.Did) 619 628 if err != nil { 620 629 s.pages.Notice(w, "repo", "Invalid user account.") ··· 627 636 }) 628 637 629 638 case http.MethodPost: 630 - user := s.auth.GetUser(r) 639 + user := s.oauth.GetUser(r) 631 640 632 641 domain := r.FormValue("domain") 633 642 if domain == "" { ··· 671 680 return 672 681 } 673 682 674 - client, err := NewSignedClient(domain, secret, s.config.Dev) 683 + client, err := NewSignedClient(domain, secret, s.config.Core.Dev) 675 684 if err != nil { 676 685 s.pages.Notice(w, "repo", "Failed to connect to knot server.") 677 686 return ··· 686 695 Description: description, 687 696 } 688 697 689 - xrpcClient, _ := s.auth.AuthorizedClient(r) 698 + xrpcClient, err := s.oauth.AuthorizedClient(r) 699 + if err != nil { 700 + s.pages.Notice(w, "repo", "Failed to write record to PDS.") 701 + return 702 + } 690 703 691 704 createdAt := time.Now().Format(time.RFC3339) 692 - atresp, err := comatproto.RepoPutRecord(r.Context(), xrpcClient, &comatproto.RepoPutRecord_Input{ 705 + atresp, err := xrpcClient.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 693 706 Collection: tangled.RepoNSID, 694 707 Repo: user.Did, 695 708 Rkey: rkey,
+1 -1
appview/tid.go
··· 4 4 "github.com/bluesky-social/indigo/atproto/syntax" 5 5 ) 6 6 7 - var c *syntax.TIDClock = syntax.NewTIDClock(0) 7 + var c syntax.TIDClock = syntax.NewTIDClock(0) 8 8 9 9 func TID() string { 10 10 return c.Next().String()
+2 -2
cmd/appview/main.go
··· 26 26 log.Fatal(err) 27 27 } 28 28 29 - log.Println("starting server on", c.ListenAddr) 30 - log.Println(http.ListenAndServe(c.ListenAddr, state.Router())) 29 + log.Println("starting server on", c.Core.ListenAddr) 30 + log.Println(http.ListenAndServe(c.Core.ListenAddr, state.Router())) 31 31 }