Add user authentication and game persistence with SQLite
- User registration/login with bcrypt password hashing - SQLite database with goose migrations and sqlc-generated queries - Games and players persisted to database, resumable after restart - Guest play still supported alongside authenticated users - Auth UI components (login/register forms, auth header, guest banner)
This commit is contained in:
127
ui/auth.go
Normal file
127
ui/auth.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
func LoginView(usernameBind, passwordBind, loginKeyDown, loginClick h.H, errorMsg string) h.H {
|
||||
var errorEl h.H
|
||||
if errorMsg != "" {
|
||||
errorEl = h.P(h.Class("error"), h.Text(errorMsg))
|
||||
}
|
||||
|
||||
return h.Main(h.Class("container"),
|
||||
h.Div(h.Class("lobby"),
|
||||
h.H1(h.Text("Login")),
|
||||
h.P(h.Text("Sign in to your account")),
|
||||
errorEl,
|
||||
h.Form(
|
||||
h.FieldSet(
|
||||
h.Label(h.Text("Username"), h.Attr("for", "username")),
|
||||
h.Input(
|
||||
h.ID("username"),
|
||||
h.Type("text"),
|
||||
h.Placeholder("Enter your username"),
|
||||
usernameBind,
|
||||
h.Attr("required"),
|
||||
h.Attr("autofocus"),
|
||||
),
|
||||
h.Label(h.Text("Password"), h.Attr("for", "password")),
|
||||
h.Input(
|
||||
h.ID("password"),
|
||||
h.Type("password"),
|
||||
h.Placeholder("Enter your password"),
|
||||
passwordBind,
|
||||
h.Attr("required"),
|
||||
loginKeyDown,
|
||||
),
|
||||
),
|
||||
h.Button(
|
||||
h.Type("button"),
|
||||
h.Text("Login"),
|
||||
loginClick,
|
||||
),
|
||||
),
|
||||
h.P(
|
||||
h.Text("Don't have an account? "),
|
||||
h.A(h.Href("/register"), h.Text("Register")),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func RegisterView(usernameBind, passwordBind, confirmBind, registerKeyDown, registerClick h.H, errorMsg string) h.H {
|
||||
var errorEl h.H
|
||||
if errorMsg != "" {
|
||||
errorEl = h.P(h.Class("error"), h.Text(errorMsg))
|
||||
}
|
||||
|
||||
return h.Main(h.Class("container"),
|
||||
h.Div(h.Class("lobby"),
|
||||
h.H1(h.Text("Register")),
|
||||
h.P(h.Text("Create a new account")),
|
||||
errorEl,
|
||||
h.Form(
|
||||
h.FieldSet(
|
||||
h.Label(h.Text("Username"), h.Attr("for", "username")),
|
||||
h.Input(
|
||||
h.ID("username"),
|
||||
h.Type("text"),
|
||||
h.Placeholder("Choose a username"),
|
||||
usernameBind,
|
||||
h.Attr("required"),
|
||||
h.Attr("autofocus"),
|
||||
),
|
||||
h.Label(h.Text("Password"), h.Attr("for", "password")),
|
||||
h.Input(
|
||||
h.ID("password"),
|
||||
h.Type("password"),
|
||||
h.Placeholder("Choose a password (min 8 chars)"),
|
||||
passwordBind,
|
||||
h.Attr("required"),
|
||||
),
|
||||
h.Label(h.Text("Confirm Password"), h.Attr("for", "confirm")),
|
||||
h.Input(
|
||||
h.ID("confirm"),
|
||||
h.Type("password"),
|
||||
h.Placeholder("Confirm your password"),
|
||||
confirmBind,
|
||||
h.Attr("required"),
|
||||
registerKeyDown,
|
||||
),
|
||||
),
|
||||
h.Button(
|
||||
h.Type("button"),
|
||||
h.Text("Register"),
|
||||
registerClick,
|
||||
),
|
||||
),
|
||||
h.P(
|
||||
h.Text("Already have an account? "),
|
||||
h.A(h.Href("/login"), h.Text("Login")),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func AuthHeader(username string, logoutClick h.H) h.H {
|
||||
return h.Div(h.Class("auth-header"),
|
||||
h.Span(h.Text("Logged in as "), h.Strong(h.Text(username))),
|
||||
h.Button(
|
||||
h.Type("button"),
|
||||
h.Class("secondary outline small"),
|
||||
h.Text("Logout"),
|
||||
logoutClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func GuestBanner() h.H {
|
||||
return h.Div(h.Class("guest-banner"),
|
||||
h.Text("Playing as guest. "),
|
||||
h.A(h.Href("/login"), h.Text("Login")),
|
||||
h.Text(" or "),
|
||||
h.A(h.Href("/register"), h.Text("Register")),
|
||||
h.Text(" to save your games."),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user