package auth import ( "errors" "html/template" "net/http" "strconv" "time" webtemplates "github.com/domagojzecevic/cammonitor/internal/web/templates" "github.com/go-chi/chi/v5" ) type Handler struct { store *Store sessionTTL time.Duration } func NewHandler(store *Store, sessionTTL time.Duration) *Handler { return &Handler{store: store, sessionTTL: sessionTTL} } func (h *Handler) LoginPage(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") _ = loginTemplate.ExecuteTemplate(w, webtemplates.Login, nil) } func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { http.Error(w, "invalid form", http.StatusBadRequest) return } user, err := h.store.Authenticate(r.FormValue("username"), r.FormValue("password")) if errors.Is(err, ErrInvalidCredentials) { http.Error(w, "invalid credentials", http.StatusUnauthorized) return } if err != nil { http.Error(w, "login failed", http.StatusInternalServerError) return } token, err := h.store.CreateSession(user.ID, h.sessionTTL) if err != nil { http.Error(w, "create session failed", http.StatusInternalServerError) return } http.SetCookie(w, &http.Cookie{ Name: sessionCookieName, Value: token, Path: "/", Expires: time.Now().UTC().Add(h.sessionTTL), HttpOnly: true, SameSite: http.SameSiteLaxMode, }) http.Redirect(w, r, "/", http.StatusSeeOther) } func (h *Handler) Logout(w http.ResponseWriter, r *http.Request) { if cookie, err := r.Cookie(sessionCookieName); err == nil { _ = h.store.DeleteSession(cookie.Value) } http.SetCookie(w, &http.Cookie{ Name: sessionCookieName, Value: "", Path: "/", Expires: time.Unix(0, 0), MaxAge: -1, HttpOnly: true, SameSite: http.SameSiteLaxMode, }) http.Redirect(w, r, "/login", http.StatusSeeOther) } func (h *Handler) UsersPage(w http.ResponseWriter, _ *http.Request) { users, err := h.store.ListUsers() if err != nil { http.Error(w, "list users failed", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") _ = adminUsersTemplate.ExecuteTemplate(w, webtemplates.AdminUsers, struct { Users []User }{Users: users}) } func (h *Handler) CreateUser(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { http.Error(w, "invalid form", http.StatusBadRequest) return } if err := h.store.CreateUser(r.FormValue("username"), r.FormValue("password"), r.FormValue("is_admin") == "on"); err != nil { http.Error(w, "create user failed", http.StatusBadRequest) return } http.Redirect(w, r, "/admin/users", http.StatusSeeOther) } func (h *Handler) DeleteUser(w http.ResponseWriter, r *http.Request) { id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { http.Error(w, "invalid user id", http.StatusBadRequest) return } if err := h.store.DeleteUser(id); err != nil { http.Error(w, "delete user failed", http.StatusInternalServerError) return } http.Redirect(w, r, "/admin/users", http.StatusSeeOther) } var loginTemplate = template.Must(template.ParseFS(webtemplates.FS, webtemplates.Login)) var adminUsersTemplate = template.Must(template.ParseFS(webtemplates.FS, webtemplates.AdminUsers))