summaryrefslogtreecommitdiff
path: root/api/handlers.go
diff options
context:
space:
mode:
Diffstat (limited to 'api/handlers.go')
-rw-r--r--api/handlers.go166
1 files changed, 166 insertions, 0 deletions
diff --git a/api/handlers.go b/api/handlers.go
new file mode 100644
index 0000000..0d23bdb
--- /dev/null
+++ b/api/handlers.go
@@ -0,0 +1,166 @@
+package api
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+ "time"
+
+ "wikiapiserver/db"
+)
+
+const defaultTimeout = 5 * time.Second
+
+// Handler holds the DB dependency for all HTTP handlers.
+type Handler struct {
+ db *db.DB
+}
+
+// NewHandler creates a Handler backed by the given DB.
+func NewHandler(database *db.DB) *Handler {
+ return &Handler{db: database}
+}
+
+// --- request/response types ---
+
+type errResp struct {
+ Error string `json:"error"`
+}
+
+type registerReq struct {
+ Username string `json:"username"`
+ Password string `json:"password"`
+}
+
+type loginReq struct {
+ Username string `json:"username"`
+ Password string `json:"password"`
+}
+
+type refreshReq struct {
+ RefreshToken string `json:"refresh_token"`
+}
+
+// --- helper writers ---
+
+func writeJSON(w http.ResponseWriter, code int, v any) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(code)
+ json.NewEncoder(w).Encode(v) //nolint:errcheck
+}
+
+func badRequest(w http.ResponseWriter, msg string) {
+ writeJSON(w, http.StatusBadRequest, errResp{Error: msg})
+}
+
+func unauthorized(w http.ResponseWriter) {
+ writeJSON(w, http.StatusUnauthorized, errResp{Error: "unauthorized"})
+}
+
+func serverError(w http.ResponseWriter, msg string) {
+ writeJSON(w, http.StatusInternalServerError, errResp{Error: msg})
+}
+
+// --- Register: POST /register ---
+
+func (h *Handler) Register(w http.ResponseWriter, r *http.Request) {
+ ctx, cancel := context.WithTimeout(r.Context(), defaultTimeout)
+ defer cancel()
+
+ var req registerReq
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ badRequest(w, "invalid JSON")
+ return
+ }
+
+ if req.Username == "" || req.Password == "" {
+ badRequest(w, "username and password are required")
+ return
+ }
+
+ acct, err := h.db.CreateAccount(ctx, req.Username, req.Password)
+ if err != nil {
+ if err.Error() == "username already exists" {
+ badRequest(w, "username already exists")
+ return
+ }
+ serverError(w, "could not create account")
+ return
+ }
+
+ writeJSON(w, http.StatusCreated, acct)
+}
+
+// --- Login: POST /login ---
+
+func (h *Handler) Login(w http.ResponseWriter, r *http.Request) {
+ ctx, cancel := context.WithTimeout(r.Context(), defaultTimeout)
+ defer cancel()
+
+ var req loginReq
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ badRequest(w, "invalid JSON")
+ return
+ }
+
+ if req.Username == "" || req.Password == "" {
+ badRequest(w, "username and password are required")
+ return
+ }
+
+ acct, err := h.db.Authenticate(ctx, req.Username, req.Password)
+ if err != nil {
+ if err.Error() == "invalid credentials" {
+ unauthorized(w)
+ return
+ }
+ serverError(w, "authentication failed")
+ return
+ }
+
+ writeJSON(w, http.StatusOK, acct)
+}
+
+// --- Refresh: POST /refresh ---
+
+func (h *Handler) Refresh(w http.ResponseWriter, r *http.Request) {
+ ctx, cancel := context.WithTimeout(r.Context(), defaultTimeout)
+ defer cancel()
+
+ var req refreshReq
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ badRequest(w, "invalid JSON")
+ return
+ }
+
+ if req.RefreshToken == "" {
+ badRequest(w, "refresh_token is required")
+ return
+ }
+
+ acct, err := h.db.RefreshByToken(ctx, req.RefreshToken)
+ if err != nil {
+ if err.Error() == "invalid refresh token" {
+ unauthorized(w)
+ return
+ }
+ serverError(w, "could not refresh token")
+ return
+ }
+
+ writeJSON(w, http.StatusOK, acct)
+}
+
+// --- Health: GET /health ---
+
+func (h *Handler) Health(w http.ResponseWriter, r *http.Request) {
+ ctx, cancel := context.WithTimeout(r.Context(), defaultTimeout)
+ defer cancel()
+
+ if err := h.db.Ping(ctx); err != nil {
+ writeJSON(w, http.StatusServiceUnavailable, errResp{Error: "database unavailable"})
+ return
+ }
+
+ writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
+}