main pico / pkg / apps / pgs / auto_form.go
Antonio Mika  ·  2026-10-25
 1package pgs
 2
 3import (
 4	"net/http"
 5
 6	"github.com/picosh/pico/pkg/shared/router"
 7)
 8
 9func handleAutoForm(w http.ResponseWriter, r *http.Request, cfg *Config) {
10	formName := r.PathValue("fname")
11	if r.Method != http.MethodPost {
12		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
13		return
14	}
15
16	err := r.ParseForm()
17	if err != nil {
18		cfg.Logger.Error("failed to parse auto form", "err", err)
19		http.Error(w, "failed to parse auto form", http.StatusBadRequest)
20		return
21	}
22
23	formValues := make(map[string]interface{})
24	for key, values := range r.PostForm {
25		if len(values) == 1 {
26			formValues[key] = values[0]
27		} else if len(values) > 1 {
28			formValues[key] = values
29		}
30	}
31
32	subdomain := router.GetSubdomainFromRequest(r, cfg.Domain, cfg.TxtPrefix)
33	props, err := router.GetProjectFromSubdomain(subdomain)
34	if err != nil {
35		cfg.Logger.Error("could not get project from subdomain", "subdomain", subdomain, "err", err)
36		http.Error(w, "not found", http.StatusNotFound)
37		return
38	}
39
40	user, err := cfg.DB.FindUserByName(props.Username)
41	if err != nil {
42		cfg.Logger.Error("user not found", "username", props.Username)
43		http.Error(w, "not found", http.StatusNotFound)
44		return
45	}
46	router.SetUser(r, user.Name)
47
48	ff, err := cfg.DB.FindFeature(user.ID, "plus")
49	if err != nil || (ff != nil && !ff.IsValid()) {
50		cfg.Logger.Error("pico+ required for auto-forms", "username", props.Username)
51		http.Error(w, "pico+ required for auto-forms", http.StatusPaymentRequired)
52		return
53	}
54
55	err = cfg.DB.InsertFormEntry(user.ID, formName, formValues)
56	if err != nil {
57		cfg.Logger.Error("failed to save form data", "err", err)
58		http.Error(w, "failed to save form data", http.StatusInternalServerError)
59		return
60	}
61
62	serveAutoFormSubmitted(w, r, cfg)
63}
64
65type FormData struct {
66	Error string
67}
68
69func serveAutoFormSubmitted(w http.ResponseWriter, r *http.Request, cfg *Config) {
70	errorMsg := r.URL.Query().Get("error")
71	data := loginFormData{
72		Error: errorMsg,
73	}
74
75	w.WriteHeader(http.StatusUnprocessableEntity)
76
77	ts, err := router.RenderTemplate(staticDir, []string{cfg.StaticPath("html/auto_form.page.tmpl")})
78	if err != nil {
79		cfg.Logger.Error("could not render auto form template", "err", err.Error())
80		http.Error(w, "Server error", http.StatusInternalServerError)
81		return
82	}
83
84	err = ts.Execute(w, data)
85	if err != nil {
86		cfg.Logger.Error("could not execute login template", "err", err.Error())
87		http.Error(w, "Server error", http.StatusInternalServerError)
88	}
89}