package main import ( "context" "database/sql" "embed" "net/http" "strings" "git.wntrmute.dev/mc/mcdsl/auth" "git.wntrmute.dev/mc/mcdsl/csrf" "git.wntrmute.dev/mc/mcdsl/web" "git.wntrmute.dev/kyle/kls/links" "github.com/go-chi/chi/v5" ) type server struct { db *sql.DB auth *auth.Authenticator csrf *csrf.Protect } //go:embed templates/*.html var templateFiles embed.FS //go:embed static var staticFiles embed.FS type page struct { Username string Short string URLs []*links.URL } func (srv *server) handleLoginPage(w http.ResponseWriter, r *http.Request) { web.RenderTemplate(w, templateFiles, "login.html", page{}, srv.csrf.TemplateFunc(w)) } func (srv *server) handleLogin(w http.ResponseWriter, r *http.Request) { username := r.FormValue("username") password := r.FormValue("password") totpCode := r.FormValue("totp_code") token, _, err := srv.auth.Login(username, password, totpCode) if err != nil { web.RenderTemplate(w, templateFiles, "login.html", page{Short: "Invalid credentials"}, srv.csrf.TemplateFunc(w)) return } web.SetSessionCookie(w, cookieName, token) http.Redirect(w, r, "/", http.StatusFound) } func (srv *server) handleLogout(w http.ResponseWriter, r *http.Request) { token := web.GetSessionToken(r, cookieName) if token != "" { _ = srv.auth.Logout(token) } web.ClearSessionCookie(w, cookieName) http.Redirect(w, r, "/login", http.StatusFound) } func (srv *server) handleIndex(w http.ResponseWriter, r *http.Request) { info := auth.TokenInfoFromContext(r.Context()) web.RenderTemplate(w, templateFiles, "index.html", page{Username: info.Username}, srv.csrf.TemplateFunc(w)) } func (srv *server) postURL(w http.ResponseWriter, r *http.Request) { info := auth.TokenInfoFromContext(r.Context()) err := r.ParseForm() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } stripQuery := boolean(r.FormValue("strip")) url := r.FormValue("value") if len(url) == 0 { http.Error(w, "invalid URL", http.StatusBadRequest) return } url, err = links.CleanString(url, stripQuery) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } ctx := context.Background() short, err := links.StoreURL(ctx, srv.db, url) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } web.RenderTemplate(w, templateFiles, "index.html", page{Username: info.Username, Short: short}, srv.csrf.TemplateFunc(w)) } func (srv *server) redirect(w http.ResponseWriter, r *http.Request) { short := chi.URLParam(r, "short") if short == "" { short = strings.TrimPrefix(r.URL.Path, "/") } u, err := links.RetrieveURL(context.Background(), srv.db, short) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } http.Redirect(w, r, u, http.StatusFound) } func (srv *server) listAll(w http.ResponseWriter, r *http.Request) { info := auth.TokenInfoFromContext(r.Context()) allPosts, err := links.FetchAll(context.Background(), srv.db) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } web.RenderTemplate(w, templateFiles, "list.html", page{ Username: info.Username, URLs: allPosts, }, srv.csrf.TemplateFunc(w)) } func boolean(s string) bool { switch s { case "on", "true", "True", "yes", "Yes": return true default: return false } }