From 688cfff89a7c57bf5e0abd7da4fb1180c51d1925 Mon Sep 17 00:00:00 2001 From: sirpiglr <49359077-sirpiglr@users.noreply.replit.com> Date: Sun, 7 Dec 2025 02:10:17 +0000 Subject: [PATCH] Add maintenance mode endpoint for Vercel deployments Create `api/admin/platform/maintenance.ts` to handle maintenance mode logic as a Vercel serverless function, including GET and POST requests for enabling/disabling and admin verification via Supabase. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 9203795e-937a-4306-b81d-b4d5c78c240e Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: eb559abf-e967-427e-a938-a9f28fc6407c Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7c94b7a0-29c7-4f2e-94ef-44b2153872b7/9203795e-937a-4306-b81d-b4d5c78c240e/qPXTzuE Replit-Helium-Checkpoint-Created: true --- api/admin/platform/maintenance.ts | 106 ++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 api/admin/platform/maintenance.ts diff --git a/api/admin/platform/maintenance.ts b/api/admin/platform/maintenance.ts new file mode 100644 index 00000000..0e808cd3 --- /dev/null +++ b/api/admin/platform/maintenance.ts @@ -0,0 +1,106 @@ +import type { VercelRequest, VercelResponse } from "@vercel/node"; +import { createClient } from "@supabase/supabase-js"; + +const supabaseUrl = process.env.VITE_SUPABASE_URL || ""; +const supabaseServiceRole = process.env.SUPABASE_SERVICE_ROLE || ""; + +let maintenanceModeCache: boolean | null = null; + +const ADMIN_ROLES = ["admin", "super_admin", "staff", "owner"]; + +async function verifyAdmin(token: string): Promise { + if (!supabaseUrl || !supabaseServiceRole) return false; + + const supabase = createClient(supabaseUrl, supabaseServiceRole); + + try { + const { data: { user }, error } = await supabase.auth.getUser(token); + if (error || !user) return false; + + const { data: roles } = await supabase + .from("user_roles") + .select("role") + .eq("user_id", user.id); + + if (!roles) return false; + + return roles.some(r => ADMIN_ROLES.includes(r.role?.toLowerCase())); + } catch { + return false; + } +} + +export default async function handler(req: VercelRequest, res: VercelResponse) { + res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + + const supabase = supabaseUrl && supabaseServiceRole + ? createClient(supabaseUrl, supabaseServiceRole) + : null; + + if (req.method === "GET") { + try { + if (supabase) { + const { data, error } = await supabase + .from("platform_settings") + .select("value") + .eq("key", "maintenance_mode") + .single(); + + if (!error && data) { + const isEnabled = data.value === true || data.value === "true"; + maintenanceModeCache = isEnabled; + return res.json({ maintenance_mode: isEnabled }); + } + } + + const envMaintenance = process.env.MAINTENANCE_MODE === "true"; + return res.json({ maintenance_mode: maintenanceModeCache ?? envMaintenance }); + } catch (e) { + const envMaintenance = process.env.MAINTENANCE_MODE === "true"; + return res.json({ maintenance_mode: maintenanceModeCache ?? envMaintenance }); + } + } + + if (req.method === "POST") { + const authHeader = req.headers.authorization; + const token = authHeader?.replace("Bearer ", ""); + + if (!token) { + return res.status(401).json({ error: "Unauthorized" }); + } + + const isAdmin = await verifyAdmin(token); + if (!isAdmin) { + return res.status(403).json({ error: "Forbidden - Admin access required" }); + } + + const { maintenance_mode } = req.body; + + if (typeof maintenance_mode !== "boolean") { + return res.status(400).json({ error: "maintenance_mode must be a boolean" }); + } + + try { + if (supabase) { + const { error } = await supabase + .from("platform_settings") + .upsert( + { key: "maintenance_mode", value: maintenance_mode, updated_at: new Date().toISOString() }, + { onConflict: "key" } + ); + + if (error) { + console.error("[Maintenance] DB error:", error); + } + } + + maintenanceModeCache = maintenance_mode; + return res.json({ maintenance_mode, success: true }); + } catch (e) { + maintenanceModeCache = maintenance_mode; + return res.json({ maintenance_mode, success: true }); + } + } + + return res.status(405).json({ error: "Method not allowed" }); +}