web: the login page tells a new visitor how to register !123
merged
merged by cmc on 2026-08-30 16:22 UTC
· krz/gitbay:login-register-hint into main
3 files changed, +33 −9
Layout: unified · split
e2e/websignup_test.go
+12
| @@ -23,6 +23,11 @@ func TestWebSignup(t *testing.T) { |
| 23 | if status != 200 || !strings.Contains(body, "invite-only") || !strings.Contains(body, `name="key"`) { |
23 | if status != 200 || !strings.Contains(body, "invite-only") || !strings.Contains(body, `name="key"`) { |
| 24 | t.Fatalf("register form: %d\n%s", status, body) |
24 | t.Fatalf("register form: %d\n%s", status, body) |
| 25 | } |
25 | } |
| |
26 | // The login page tells a brand-new visitor how to get an account. |
| |
27 | status, body = inst.get(t, "/login") |
| |
28 | if status != 200 || !strings.Contains(body, `href="/register"`) || !strings.Contains(body, "invite-only") { |
| |
29 | t.Fatalf("login signup hint: %d\n%s", status, body) |
| |
30 | } |
| 26 | |
31 | |
| 27 | // Invite issued over the admin path; redeemed through the browser. |
32 | // Invite issued over the admin path; redeemed through the browser. |
| 28 | inst.admin(t, "admin", "invite", "--email", "erin@example.test") |
33 | inst.admin(t, "admin", "invite", "--email", "erin@example.test") |
| @@ -76,4 +81,11 @@ func TestWebSignupClosedInstance(t *testing.T) { |
| 76 | if strings.Contains(body, `href="/register"`) { |
81 | if strings.Contains(body, `href="/register"`) { |
| 77 | t.Fatal("closed landing advertises signup") |
82 | t.Fatal("closed landing advertises signup") |
| 78 | } |
83 | } |
| |
84 | _, body = inst.get(t, "/login") |
| |
85 | if strings.Contains(body, `href="/register"`) { |
| |
86 | t.Fatal("closed login page advertises signup") |
| |
87 | } |
| |
88 | if !strings.Contains(body, "not accepting new accounts") { |
| |
89 | t.Fatalf("closed login page says nothing about accounts:\n%s", body) |
| |
90 | } |
| 79 | } |
91 | } |
internal/httpd/accounts.go
+12 −9
| @@ -60,22 +60,25 @@ func (s *Server) checkOrigin(h http.HandlerFunc) http.HandlerFunc { |
| 60 | } |
60 | } |
| 61 | } |
61 | } |
| 62 | |
62 | |
| |
63 | // renderLogin draws the login page. Mode carries the registration mode so |
| |
64 | // the page can tell a brand-new visitor how to get an account. |
| |
65 | func (s *Server) renderLogin(w http.ResponseWriter, errMsg string) { |
| |
66 | s.render(w, "login.html", struct { |
| |
67 | basePage |
| |
68 | Mode string // closed | invite | open |
| |
69 | Error string |
| |
70 | }{basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}, s.cfg.Registration.Mode, errMsg}) |
| |
71 | } |
| |
72 | |
| 63 | func (s *Server) login(w http.ResponseWriter, r *http.Request) { |
73 | func (s *Server) login(w http.ResponseWriter, r *http.Request) { |
| 64 | token := r.URL.Query().Get("token") |
74 | token := r.URL.Query().Get("token") |
| 65 | if token == "" { |
75 | if token == "" { |
| 66 | s.render(w, "login.html", struct { |
76 | s.renderLogin(w, "") |
| 67 | basePage |
| |
| 68 | Error string |
| |
| 69 | }{basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}, ""}) |
| |
| 70 | return |
77 | return |
| 71 | } |
78 | } |
| 72 | userID, err := s.st.ConsumeLoginToken(store.HashToken(token)) |
79 | userID, err := s.st.ConsumeLoginToken(store.HashToken(token)) |
| 73 | if err != nil { |
80 | if err != nil { |
| 74 | s.render(w, "login.html", struct { |
81 | s.renderLogin(w, "that login link is invalid, expired, or already used — mint a new one") |
| 75 | basePage |
| |
| 76 | Error string |
| |
| 77 | }{basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}, |
| |
| 78 | "that login link is invalid, expired, or already used — mint a new one"}) |
| |
| 79 | return |
82 | return |
| 80 | } |
83 | } |
| 81 | sessTok, sessHash, err := store.NewToken() |
84 | sessTok, sessHash, err := store.NewToken() |
internal/web/templates/login.html
+9
| @@ -6,4 +6,13 @@ |
| 6 | with your registered key:</p> |
6 | with your registered key:</p> |
| 7 | <pre class="message">ssh git@{{.Host}} web login</pre> |
7 | <pre class="message">ssh git@{{.Host}} web login</pre> |
| 8 | <p>then open the printed URL within five minutes.</p> |
8 | <p>then open the printed URL within five minutes.</p> |
| |
9 | <h2>New here?</h2> |
| |
10 | {{if eq .Mode "open"}}<p><a href="/register">Create an account</a> — pick a username, paste your SSH |
| |
11 | public key, verify your email. Or from the terminal:</p> |
| |
12 | <pre class="message">ssh git@{{.Host}} register --username you --email you@example.org</pre> |
| |
13 | {{else if eq .Mode "invite"}}<p>This instance is invite-only. With a code from an admin, |
| |
14 | <a href="/register">create an account</a> — pick a username, paste your SSH public key. Or from |
| |
15 | the terminal:</p> |
| |
16 | <pre class="message">ssh git@{{.Host}} register --username you --invite <code></pre> |
| |
17 | {{else}}<p>This instance is not accepting new accounts.</p>{{end}} |
| 9 | {{end}} |
18 | {{end}} |