// Package api implements the JSON API layer at /api/v1/ for the mobile app. // // All handlers return JSON and accept Bearer token auth (or session cookie). // This package mirrors the same business logic as the HTML handlers but exposes // it through a structured JSON contract. package api import ( "database/sql" "encoding/json" "log/slog" "net/http" "atmoquest/features/auth" "atmoquest/internal/apitoken" "atmoquest/internal/connection" "atmoquest/internal/push" ) // Handlers holds the shared dependencies for all API endpoints. type Handlers struct { DB *sql.DB Auth *auth.Handlers ConnQueue *connection.Queue Tokens *apitoken.Generator Push *push.Sender } // NewHandlers wires the API feature. func NewHandlers(conn *sql.DB, authH *auth.Handlers, connQueue *connection.Queue, tokens *apitoken.Generator, pusher *push.Sender) *Handlers { return &Handlers{DB: conn, Auth: authH, ConnQueue: connQueue, Tokens: tokens, Push: pusher} } // --- JSON helpers ----------------------------------------------------------- // writeJSON encodes v as JSON with the given status code. func writeJSON(w http.ResponseWriter, status int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) if err := json.NewEncoder(w).Encode(v); err != nil { slog.Warn("api: encode response", "err", err) } } // writeError writes a JSON error response. func writeError(w http.ResponseWriter, status int, msg string) { writeJSON(w, status, map[string]string{"error": msg}) } // decodeBody reads a JSON request body, capped at 64 KB. func decodeBody(r *http.Request, v any) error { return json.NewDecoder(http.MaxBytesReader(nil, r.Body, 64*1024)).Decode(v) }