auth.go
⎇
Raw
1package views
2
3import (
4 "strconv"
5
6 "hearthforge/internal/config"
7
8 g "maragu.dev/gomponents"
9 . "maragu.dev/gomponents/html"
10)
11
12// Login renders the sign-in page. errMsg and next may be empty. next is a
13// local path to return to after sign-in.
14func Login(cfg *config.Config, errMsg, next string) g.Node {
15 return Layout(Page{Title: "Sign in", Cfg: cfg},
16 Div(Class("auth-container"),
17 H1(Class("page-title"), g.Text("Sign in")),
18 g.If(errMsg != "", P(Class("form-error"), g.Text(errMsg))),
19 g.If(errMsg == "" && next != "", P(Class("form-success"),
20 g.Text("Please sign in again to continue."))),
21 Form(Method("POST"), Action("/login"), Class("auth-form"),
22 g.If(next != "", Input(Type("hidden"), Name("next"), Value(next))),
23 Div(Class("form-group"),
24 Label(For("username"), g.Text("Username")),
25 Input(ID("username"), Name("username"), Type("text"), Required(),
26 AutoComplete("username")),
27 ),
28 Div(ID("passkey-section"), Style("display:none"),
29 Button(ID("passkey-btn"), Class("btn btn-secondary btn-block"), Type("button"),
30 g.Text("Sign in with passkey")),
31 P(ID("passkey-error"), Style("display:none")),
32 Div(Class("auth-divider"), Span(g.Text("or"))),
33 ),
34 Div(Class("form-group"),
35 Label(For("password"), g.Text("Password")),
36 Input(ID("password"), Name("password"), Type("password"), Required(),
37 AutoComplete("current-password")),
38 ),
39 Button(Type("submit"), Class("btn btn-primary btn-block"), g.Text("Sign in with password")),
40 ),
41 P(Class("auth-footer"),
42 g.Text("Don't have an account? "), A(Href("/register"), g.Text("Register"))),
43 ),
44 Script(Type("module"), Src("/assets/passkey-login.js")),
45 )
46}
47
48// Register renders the registration page. pending shows the queue notice
49// instead of the form. question is the optional application question.
50func Register(cfg *config.Config, errMsg, question string, pending bool) g.Node {
51 if pending {
52 return Layout(Page{Title: "Register", Cfg: cfg},
53 Div(Class("auth-container"),
54 H1(Class("page-title"), g.Text("Create account")),
55 P(Class("form-success"), g.Text("Your account has been submitted for review. You will be able to log in once an admin approves it.")),
56 P(Class("auth-footer"),
57 g.Text("Already have an account? "), A(Href("/login"), g.Text("Sign in"))),
58 ),
59 )
60 }
61 return Layout(Page{Title: "Register", Cfg: cfg},
62 Div(Class("auth-container"),
63 H1(Class("page-title"), g.Text("Create account")),
64 g.If(errMsg != "", P(Class("form-error"), g.Text(errMsg))),
65 Form(Method("POST"), Action("/register"), Class("auth-form"),
66 Div(Class("form-group"),
67 Label(For("username"), g.Text("Username")),
68 Input(ID("username"), Name("username"), Type("text"), Required(),
69 AutoComplete("username"), MaxLength(strconv.Itoa(cfg.MaxUsernameBytes)),
70 Pattern("[a-zA-Z0-9_-]+"),
71 Title("Letters, numbers, hyphens and underscores only")),
72 ),
73 g.If(question != "", Div(Class("form-group"),
74 Label(For("application"), g.Text(question)),
75 Textarea(ID("application"), Name("application"), Rows("4"), Required(),
76 Placeholder("Your answer...")),
77 )),
78 Div(ID("passkey-section"), Style("display:none"),
79 Button(ID("passkey-register-btn"), Class("btn btn-secondary btn-block"),
80 Type("button"), g.Text("Register with passkey")),
81 P(ID("passkey-error"), Style("display:none")),
82 Div(Class("auth-divider"), Span(g.Text("or"))),
83 ),
84 Div(Class("form-group"),
85 Label(For("password"), g.Text("Password")),
86 Input(ID("password"), Name("password"), Type("password"),
87 AutoComplete("new-password"), MinLength("8"),
88 MaxLength(strconv.Itoa(cfg.MaxPasswordBytes))),
89 ),
90 Div(Class("form-group"),
91 Label(For("password2"), g.Text("Confirm password")),
92 Input(ID("password2"), Name("password2"), Type("password"),
93 AutoComplete("new-password"), MinLength("8"),
94 MaxLength(strconv.Itoa(cfg.MaxPasswordBytes))),
95 ),
96 Button(Type("submit"), Class("btn btn-primary btn-block"), g.Text("Register with password")),
97 ),
98 P(Class("auth-footer"),
99 g.Text("Already have an account? "), A(Href("/login"), g.Text("Sign in"))),
100 ),
101 Script(Type("module"), Src("/assets/passkey-register.js")),
102 )
103}
104