159 lines
4.2 KiB
Go
159 lines
4.2 KiB
Go
package accountHandler
|
|
|
|
import (
|
|
"database/sql"
|
|
"net/http"
|
|
"strings"
|
|
|
|
httphelpers "synlotto-website/internal/helpers/http"
|
|
securityHelpers "synlotto-website/internal/helpers/security"
|
|
templateHelpers "synlotto-website/internal/helpers/template"
|
|
auditlogStorage "synlotto-website/internal/storage/auditlog"
|
|
usersStorage "synlotto-website/internal/storage/users"
|
|
|
|
"synlotto-website/internal/logging"
|
|
"synlotto-website/internal/models"
|
|
"synlotto-website/internal/platform/bootstrap"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/justinas/nosurf"
|
|
)
|
|
|
|
type registerForm struct {
|
|
Username string
|
|
Email string
|
|
Password string
|
|
PasswordConfirm string
|
|
AcceptTerms bool
|
|
}
|
|
|
|
func SignupGet(c *gin.Context) {
|
|
app := c.MustGet("app").(*bootstrap.App)
|
|
sm := app.SessionManager
|
|
|
|
ctx := templateHelpers.TemplateContext(c.Writer, c.Request, models.TemplateData{})
|
|
if f := sm.PopString(c.Request.Context(), "flash"); f != "" {
|
|
ctx["Flash"] = f
|
|
}
|
|
ctx["CSRFToken"] = nosurf.Token(c.Request)
|
|
|
|
if v := sm.Pop(c.Request.Context(), "register.form"); v != nil {
|
|
if fm, ok := v.(map[string]string); ok {
|
|
ctx["Form"] = fm
|
|
}
|
|
}
|
|
if v := sm.Pop(c.Request.Context(), "register.errors"); v != nil {
|
|
if errs, ok := v.(map[string]string); ok {
|
|
ctx["Errors"] = errs
|
|
}
|
|
}
|
|
|
|
tmpl := templateHelpers.LoadTemplateFiles("layout.html", "web/templates/account/signup.html")
|
|
|
|
c.Status(http.StatusOK)
|
|
if err := tmpl.ExecuteTemplate(c.Writer, "layout", ctx); err != nil {
|
|
logging.Info("❌ Template render error (register): %v", err)
|
|
c.String(http.StatusInternalServerError, "Error rendering register page")
|
|
}
|
|
}
|
|
|
|
func SignupPost(c *gin.Context) {
|
|
app := c.MustGet("app").(*bootstrap.App)
|
|
sm := app.SessionManager
|
|
db := app.DB
|
|
|
|
r := c.Request
|
|
|
|
form := registerForm{
|
|
Username: strings.TrimSpace(r.FormValue("username")),
|
|
Email: strings.TrimSpace(r.FormValue("email")),
|
|
Password: r.FormValue("password"),
|
|
PasswordConfirm: r.FormValue("password_confirm"),
|
|
AcceptTerms: r.FormValue("accept_terms") == "on",
|
|
}
|
|
|
|
errors := validateRegisterForm(db, form)
|
|
if len(errors) > 0 {
|
|
formMap := map[string]string{
|
|
"username": form.Username,
|
|
"email": form.Email,
|
|
"accept_terms": func() string {
|
|
if form.AcceptTerms {
|
|
return "on"
|
|
}
|
|
return ""
|
|
}(),
|
|
}
|
|
sm.Put(r.Context(), "register.form", formMap)
|
|
sm.Put(r.Context(), "register.errors", errors)
|
|
sm.Put(r.Context(), "flash", "Please fix the highlighted errors.")
|
|
|
|
c.Redirect(http.StatusSeeOther, "/account/signup")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
hash, err := securityHelpers.HashPassword(form.Password)
|
|
if err != nil {
|
|
logging.Info("❌ Hash error: %v", err)
|
|
sm.Put(r.Context(), "flash", "Something went wrong. Please try again.")
|
|
c.Redirect(http.StatusSeeOther, "/account/signup")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
id, err := usersStorage.CreateUser(db, form.Username, form.Email, hash)
|
|
if err != nil {
|
|
logging.Info("❌ CreateUser error: %v", err)
|
|
sm.Put(r.Context(), "flash", "That username or email is already taken.")
|
|
c.Redirect(http.StatusSeeOther, "/account/signup")
|
|
c.Abort()
|
|
return
|
|
}
|
|
|
|
auditlogStorage.LogSignup(
|
|
db,
|
|
id,
|
|
form.Username,
|
|
form.Email,
|
|
httphelpers.ClientIP(r),
|
|
r.UserAgent(),
|
|
)
|
|
|
|
sm.Put(r.Context(), "flash", "Account created. You can log in now.")
|
|
c.Redirect(http.StatusSeeOther, "/account/login")
|
|
c.Abort()
|
|
}
|
|
|
|
func validateRegisterForm(db *sql.DB, f registerForm) map[string]string {
|
|
errs := make(map[string]string)
|
|
|
|
if f.Username == "" || len(f.Username) < 3 {
|
|
errs["username"] = "Username must be at least 3 characters."
|
|
} else if usersStorage.UsernameExists(db, f.Username) {
|
|
errs["username"] = "Username is already in use."
|
|
}
|
|
|
|
if f.Email == "" || !looksLikeEmail(f.Email) {
|
|
errs["email"] = "Please enter a valid email."
|
|
} else if usersStorage.EmailExists(db, f.Email) {
|
|
errs["email"] = "Email is already registered."
|
|
}
|
|
|
|
if len(f.Password) < 8 {
|
|
errs["password"] = "Password must be at least 8 characters."
|
|
}
|
|
if f.Password != f.PasswordConfirm {
|
|
errs["password_confirm"] = "Passwords do not match."
|
|
}
|
|
if !f.AcceptTerms {
|
|
errs["accept_terms"] = "You must accept the terms."
|
|
}
|
|
return errs
|
|
}
|
|
|
|
func looksLikeEmail(s string) bool {
|
|
|
|
return strings.Count(s, "@") == 1 && strings.Contains(s, ".")
|
|
}
|