package handlers import ( "crypto/rand" "database/sql" "encoding/hex" "encoding/json" "net/http" "time" ) type OnboardingSession struct { ID string `json:"id"` Steps []string `json:"steps"` StartedAt time.Time `json:"started_at"` Status string `json:"status"` } type OnboardingHandler struct { DB *sql.DB } func NewOnboardingHandler(db *sql.DB) *OnboardingHandler { return &OnboardingHandler{DB: db} } // Init creates the onboarding_sessions table if it does not exist. func (h *OnboardingHandler) Init() error { _, err := h.DB.Exec(` CREATE TABLE IF NOT EXISTS onboarding_sessions ( id TEXT PRIMARY KEY, steps TEXT NOT NULL, started_at TIMESTAMPTZ NOT NULL, status TEXT NOT NULL DEFAULT 'pending' ) `) return err } // ServeHTTP routes POST /api/v1/onboarding/start. func (h *OnboardingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { writeError(w, http.StatusMethodNotAllowed, "method not allowed") return } h.start(w, r) } type startRequest struct { Steps []string `json:"steps"` } func (h *OnboardingHandler) start(w http.ResponseWriter, r *http.Request) { var req startRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } if len(req.Steps) == 0 { writeError(w, http.StatusBadRequest, "steps must not be empty") return } id, err := newID() if err != nil { writeError(w, http.StatusInternalServerError, "internal error") return } stepsJSON, err := json.Marshal(req.Steps) if err != nil { writeError(w, http.StatusInternalServerError, "internal error") return } now := time.Now() _, err = h.DB.ExecContext(r.Context(), `INSERT INTO onboarding_sessions (id, steps, started_at, status) VALUES ($1, $2, $3, $4)`, id, string(stepsJSON), now, "pending") if err != nil { writeError(w, http.StatusInternalServerError, "internal error") return } writeJSON(w, http.StatusCreated, OnboardingSession{ ID: id, Steps: req.Steps, StartedAt: now, Status: "pending", }) } func newID() (string, error) { b := make([]byte, 16) if _, err := rand.Read(b); err != nil { return "", err } return hex.EncodeToString(b), nil }