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 httpx.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 := httpx.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 := httpx.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 := httpx.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) }