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.
65func (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
63func (s *Server) login(w http.ResponseWriter, r *http.Request) { 73func (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 @@
6with your registered key:</p> 6with 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
11public 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
15the terminal:</p>
16<pre class="message">ssh git@{{.Host}} register --username you --invite &lt;code&gt;</pre>
17{{else}}<p>This instance is not accepting new accounts.</p>{{end}}
9{{end}} 18{{end}}