package routes import ( "errors" "html/template" "net/http" "net/url" "shelves/backend/auth" "shelves/backend/errorsx" "shelves/backend/forms" "shelves/backend/httpx" "shelves/backend/templates" ) type loginForm struct { password string redirectTo string } type loginFormErrors struct { password error } func loginRenderView(f loginForm, e loginFormErrors) template.HTML { body := templates.Form{ Action: "/login", Fields: []templates.Field{ templates.Field{ Label: "Password", Type: "password", Placeholder: "Password", Name: "password", Error: errorsx.String(e.password), }, templates.Field{ Type: "hidden", Name: "redirectTo", Value: f.redirectTo, }, }, } return templates.PageBase{Title: "Login", Body: body.HTML()}.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.Session.IsAdmin { httpx.SeeOther(w, redirectTo) return } html(w, loginRenderView(loginForm{redirectTo: redirectTo}, loginFormErrors{})) } 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)) return } if ctx.Session.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)) 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) if !ctx.Session.IsAdmin { httpx.SeeOther(w, "/") return } sessionId := ctx.Session.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.SeeOther(w, "/") }