package routes
import (
"errors"
"html/template"
"net/http"
"net/url"
"git.soup.land/soup/shelves/internal/auth"
"git.soup.land/soup/shelves/internal/errorsx"
"git.soup.land/soup/shelves/internal/forms"
"git.soup.land/soup/shelves/internal/httpx"
"git.soup.land/soup/shelves/internal/templates/components"
)
type loginForm struct {
password string
redirectTo string
}
type loginFormErrors struct {
password error
}
func loginRenderView(f loginForm, e loginFormErrors, ctx Ctx) template.HTML {
formHtml := components.Form{
Action: "/login",
Fields: []components.Field{
components.Field{
Label: "Password",
Type: "password",
Placeholder: "Password",
Name: "password",
Error: errorsx.String(e.password),
},
components.Field{
Type: "hidden",
Name: "redirectTo",
Value: f.redirectTo,
},
},
}.HTML()
page := components.Page{
Title: "Login",
SessionInfo: ctx.SessionInfo,
Body: formHtml,
}
return page.HTML()
}
func getRedirectTo(r *http.Request) string {
redirectTo := r.URL.Query().Get("redirectTo")
if redirectTo == "" {
redirectTo = "/"
}
redirectTo, err := url.QueryUnescape(redirectTo)
if err != nil {
redirectTo = ""
}
return redirectTo
}
func loginGet(w http.ResponseWriter, r *http.Request) {
ctx := getCtx(r)
redirectTo := getRedirectTo(r)
if ctx.SessionInfo.IsAdmin {
httpx.SeeOther(w, redirectTo)
return
}
html(w, loginRenderView(loginForm{redirectTo: redirectTo}, loginFormErrors{}, ctx))
}
func loginParseForm(f *loginForm, e *loginFormErrors, vs url.Values, v *forms.Validator) {
f.password = vs.Get("password")
f.redirectTo = vs.Get("redirectTo")
}
func loginPost(w http.ResponseWriter, r *http.Request) {
ctx := getCtx(r)
form := loginForm{}
errs := loginFormErrors{}
failed := forms.ParseFormData(r, func(vs url.Values, v *forms.Validator) {
loginParseForm(&form, &errs, vs, v)
})
if failed {
httpx.BadRequest(w)
html(w, loginRenderView(form, errs, ctx))
return
}
if ctx.SessionInfo.IsAdmin {
httpx.SeeOther(w, form.redirectTo)
return
}
success, sessionId, err := auth.Login(ctx.DB, form.password)
if err != nil {
httpx.InternalServerError(w, err)
return
}
if !success {
httpx.BadRequest(w)
errs.password = errors.New("Incorrect password")
html(w, loginRenderView(form, errs, ctx))
return
}
cookie := http.Cookie{Name: "SHELVES_OWNER_SESSION_ID", Value: sessionId}
cookie.Secure = true
cookie.HttpOnly = true
cookie.SameSite = http.SameSiteStrictMode
cookie.MaxAge = 60 * 60 * 24 * 7 // 7 days
http.SetCookie(w, &cookie)
httpx.SeeOther(w, form.redirectTo)
}
func loginDelete(w http.ResponseWriter, r *http.Request) {
ctx := getCtx(r)
httpx.HxRefresh(w)
if !ctx.SessionInfo.IsAdmin {
httpx.OK(w)
return
}
sessionId := ctx.SessionInfo.SessionId
err := auth.SessionDelete(ctx.DB, sessionId)
if err != nil {
httpx.InternalServerError(w, err)
return
}
cookie := http.Cookie{Name: "SHELVES_OWNER_SESSION_ID", Value: ""}
cookie.Secure = true
cookie.HttpOnly = true
cookie.SameSite = http.SameSiteStrictMode
cookie.MaxAge = -1
http.SetCookie(w, &cookie)
httpx.OK(w)
}