import { NextResponse } from "next/server"; import { auth } from "@clerk/nextjs/server"; import { eq, sql } from "@fitai/database"; import { db, users as usersTable, gyms as gymsTable } from "@fitai/database"; import { ensureUserSynced } from "@/lib/sync-user"; import { getDatabase } from "@/lib/database"; import log from "@/lib/logger"; async function ensureGymsTable() { await db.run(sql` CREATE TABLE IF NOT EXISTS gyms ( id TEXT PRIMARY KEY, name TEXT NOT NULL, location TEXT, status TEXT NOT NULL CHECK (status IN ('active','inactive')) DEFAULT 'active', admin_user_id TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ) `); const columns = await db.all(sql`PRAGMA table_info('gyms')`); const columnNames = new Set( (columns as Array<{ name?: string }>) .map((col) => col.name) .filter(Boolean), ); if (!columnNames.has("latitude")) { await db.run(sql`ALTER TABLE gyms ADD COLUMN latitude REAL`); } if (!columnNames.has("longitude")) { await db.run(sql`ALTER TABLE gyms ADD COLUMN longitude REAL`); } if (!columnNames.has("geofence_radius_meters")) { await db.run( sql`ALTER TABLE gyms ADD COLUMN geofence_radius_meters REAL NOT NULL DEFAULT 30`, ); } if (!columnNames.has("geofence_enabled")) { await db.run( sql`ALTER TABLE gyms ADD COLUMN geofence_enabled INTEGER NOT NULL DEFAULT 1`, ); } } // PATCH /api/gyms/[id] // Update gym details and geofence configuration export async function PATCH( request: Request, { params }: { params: Promise<{ id: string }> }, ) { try { const { id: gymId } = await params; const { userId } = await auth(); if (!userId) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const appDb = await getDatabase(); const currentUser = await ensureUserSynced(userId, appDb); if ( !currentUser || (currentUser.role !== "superAdmin" && currentUser.role !== "admin") ) { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } await ensureGymsTable(); const existingGym = await db .select() .from(gymsTable) .where(eq(gymsTable.id, gymId)) .get(); if (!existingGym) { return NextResponse.json({ error: "Gym not found" }, { status: 404 }); } if ( currentUser.role === "admin" && currentUser.gymId && currentUser.gymId !== gymId ) { return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } const body = await request.json().catch(() => null); if (!body || typeof body !== "object") { return NextResponse.json({ error: "Invalid body" }, { status: 400 }); } const latitude = body.latitude === undefined || body.latitude === null ? null : Number(body.latitude); const longitude = body.longitude === undefined || body.longitude === null ? null : Number(body.longitude); const geofenceRadiusMeters = body.geofenceRadiusMeters === undefined || body.geofenceRadiusMeters === null ? 30 : Number(body.geofenceRadiusMeters); const geofenceEnabled = body.geofenceEnabled === undefined ? true : Boolean(body.geofenceEnabled); if ( latitude !== null && (!Number.isFinite(latitude) || latitude < -90 || latitude > 90) ) { return NextResponse.json( { error: "latitude must be between -90 and 90" }, { status: 400 }, ); } if ( longitude !== null && (!Number.isFinite(longitude) || longitude < -180 || longitude > 180) ) { return NextResponse.json( { error: "longitude must be between -180 and 180" }, { status: 400 }, ); } if (!Number.isFinite(geofenceRadiusMeters) || geofenceRadiusMeters <= 0) { return NextResponse.json( { error: "geofenceRadiusMeters must be a positive number" }, { status: 400 }, ); } await db.run(sql` UPDATE gyms SET latitude = ${latitude}, longitude = ${longitude}, geofence_radius_meters = ${geofenceRadiusMeters}, geofence_enabled = ${geofenceEnabled ? 1 : 0}, updated_at = ${Math.floor(Date.now() / 1000)} WHERE id = ${gymId} `); const updatedRows = await db.all(sql` SELECT id, name, location, latitude, longitude, geofence_radius_meters as geofenceRadiusMeters, geofence_enabled as geofenceEnabled, status, admin_user_id as adminUserId, created_at as createdAt, updated_at as updatedAt FROM gyms WHERE id = ${gymId} LIMIT 1 `); const updated = updatedRows?.[0] ? { ...updatedRows[0], geofenceEnabled: typeof (updatedRows[0] as { geofenceEnabled?: unknown }) .geofenceEnabled === "boolean" ? (updatedRows[0] as { geofenceEnabled: boolean }).geofenceEnabled : Boolean( (updatedRows[0] as { geofenceEnabled?: unknown }) .geofenceEnabled, ), } : null; return NextResponse.json(updated); } catch (error) { log.error("Failed to update gym", error); return NextResponse.json( { error: "Internal Server Error" }, { status: 500 }, ); } } // DELETE /api/gyms/[id] // Delete a gym (soft delete - mark as inactive) export async function DELETE( request: Request, { params }: { params: Promise<{ id: string }> }, ) { try { const { id: gymId } = await params; const { userId } = await auth(); if (!userId) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const appDb = await getDatabase(); const currentUser = await ensureUserSynced(userId, appDb); // Only superAdmin can delete gyms if (!currentUser || currentUser.role !== "superAdmin") { return NextResponse.json( { error: "Forbidden - Only superAdmin can delete gyms" }, { status: 403 }, ); } await ensureGymsTable(); // Check if gym exists using Drizzle ORM const existingGym = await db .select() .from(gymsTable) .where(eq(gymsTable.id, gymId)) .get(); if (!existingGym) { return NextResponse.json({ error: "Gym not found" }, { status: 404 }); } // Soft delete - mark as inactive using Drizzle ORM await db .update(gymsTable) .set({ status: "inactive", updatedAt: new Date() }) .where(eq(gymsTable.id, gymId)); return NextResponse.json({ success: true, message: "Gym deleted successfully", }); } catch (error) { log.error("Failed to delete gym", error); return NextResponse.json( { error: "Internal Server Error" }, { status: 500 }, ); } }