package featuremodules import ( "context" "log" "production/internal/auth" "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v5/pgxpool" ) type Handler struct { db *pgxpool.Pool } func NewHandler(db *pgxpool.Pool) *Handler { return &Handler{db: db} } type moduleRow struct { Key string `json:"key"` Enabled bool `json:"enabled"` } // List is reachable by any authenticated staff (nav-building needs it on // every login), unlike Set which is gated to the "settings" permission at // the route level in main.go. func (h *Handler) List(c *fiber.Ctx) error { rows, err := h.db.Query(context.Background(), `SELECT key, enabled FROM feature_modules ORDER BY key`) if err != nil { log.Printf("featuremodules: list failed: %v", err) return c.Status(500).JSON(fiber.Map{"error": "internal error"}) } defer rows.Close() modules := []moduleRow{} for rows.Next() { var m moduleRow if err := rows.Scan(&m.Key, &m.Enabled); err != nil { log.Printf("featuremodules: scan failed: %v", err) return c.Status(500).JSON(fiber.Map{"error": "internal error"}) } modules = append(modules, m) } return c.JSON(modules) } // Set toggles one module. Unknown keys 400 rather than silently no-op-ing // (INSERT ... ON CONFLICT would let a typo'd key from a stale frontend // build create a phantom row nothing ever reads) — see isValidKey's // Go-side allowlist. func (h *Handler) Set(c *fiber.Ctx) error { key := c.Params("key") if !isValidKey(key) { return c.Status(400).JSON(fiber.Map{"error": "unknown module key"}) } var body struct { Enabled *bool `json:"enabled"` } if err := c.BodyParser(&body); err != nil || body.Enabled == nil { return c.Status(400).JSON(fiber.Map{"error": "enabled is required"}) } tag, err := h.db.Exec(context.Background(), `UPDATE feature_modules SET enabled = $1, updated_at = NOW(), updated_by_staff_name = $2 WHERE key = $3`, *body.Enabled, auth.StaffName(c), key) if err != nil { log.Printf("featuremodules: set failed: %v", err) return c.Status(500).JSON(fiber.Map{"error": "internal error"}) } if tag.RowsAffected() == 0 { return c.Status(404).JSON(fiber.Map{"error": "module not found"}) } return h.List(c) }