A vibe coded tangled fork which supports pijul.
at 7a9bd70992a22c366ce21db1bfa023727a96ec95 116 lines 3.1 kB view raw
1package state 2 3import ( 4 "fmt" 5 "net/http" 6 "strings" 7 8 "tangled.org/core/appview/pages" 9) 10 11func (s *State) Login(w http.ResponseWriter, r *http.Request) { 12 l := s.logger.With("handler", "Login") 13 14 switch r.Method { 15 case http.MethodGet: 16 returnURL := r.URL.Query().Get("return_url") 17 errorCode := r.URL.Query().Get("error") 18 addAccount := r.URL.Query().Get("mode") == "add_account" 19 20 registry := s.oauth.GetAccounts(r) 21 s.pages.Login(w, pages.LoginParams{ 22 ReturnUrl: returnURL, 23 ErrorCode: errorCode, 24 AddAccount: addAccount, 25 Accounts: registry.Accounts, 26 }) 27 case http.MethodPost: 28 handle := r.FormValue("handle") 29 returnURL := r.FormValue("return_url") 30 addAccount := r.FormValue("add_account") == "true" 31 32 // remove spaces around the handle, handles can't have spaces around them 33 handle = strings.TrimSpace(handle) 34 35 // when users copy their handle from bsky.app, it tends to have these characters around it: 36 // 37 // @nelind.dk: 38 // \u202a ensures that the handle is always rendered left to right and 39 // \u202c reverts that so the rest of the page renders however it should 40 handle = strings.TrimPrefix(handle, "\u202a") 41 handle = strings.TrimSuffix(handle, "\u202c") 42 43 // `@` is harmless 44 handle = strings.TrimPrefix(handle, "@") 45 46 // basic handle validation 47 if !strings.Contains(handle, ".") { 48 l.Error("invalid handle format", "raw", handle) 49 s.pages.Notice( 50 w, 51 "login-msg", 52 fmt.Sprintf("\"%s\" is an invalid handle. Did you mean %s.bsky.social or %s.tngl.sh?", handle, handle, handle), 53 ) 54 return 55 } 56 57 if err := s.oauth.SetAuthReturn(w, r, returnURL, addAccount); err != nil { 58 l.Error("failed to set auth return", "err", err) 59 } 60 61 redirectURL, err := s.oauth.ClientApp.StartAuthFlow(r.Context(), handle) 62 if err != nil { 63 l.Error("failed to start auth", "err", err) 64 s.pages.Notice( 65 w, 66 "login-msg", 67 fmt.Sprintf("Failed to start auth flow: %v", err), 68 ) 69 return 70 } 71 72 s.pages.HxRedirect(w, redirectURL) 73 } 74} 75 76func (s *State) Logout(w http.ResponseWriter, r *http.Request) { 77 l := s.logger.With("handler", "Logout") 78 79 currentUser := s.oauth.GetMultiAccountUser(r) 80 if currentUser == nil { 81 s.pages.HxRedirect(w, "/login") 82 return 83 } 84 85 currentDid := currentUser.Active.Did 86 87 var remainingAccounts []string 88 for _, acc := range currentUser.Accounts { 89 if acc.Did != currentDid { 90 remainingAccounts = append(remainingAccounts, acc.Did) 91 } 92 } 93 94 if err := s.oauth.RemoveAccount(w, r, currentDid); err != nil { 95 l.Error("failed to remove account from registry", "err", err) 96 } 97 98 if err := s.oauth.DeleteSession(w, r); err != nil { 99 l.Error("failed to delete session", "err", err) 100 } 101 102 if len(remainingAccounts) > 0 { 103 nextDid := remainingAccounts[0] 104 if err := s.oauth.SwitchAccount(w, r, nextDid); err != nil { 105 l.Error("failed to switch to next account", "err", err) 106 s.pages.HxRedirect(w, "/login") 107 return 108 } 109 l.Info("switched to next account after logout", "did", nextDid) 110 s.pages.HxRefresh(w) 111 return 112 } 113 114 l.Info("logged out last account") 115 s.pages.HxRedirect(w, "/login") 116}