/** * Admin storage status + connection test. Super-admin only. * * GET /api/v1/admin/storage — current backend + capacity stats * POST /api/v1/admin/storage/test — exercise list/put/get/delete on s3 */ import { NextResponse } from 'next/server'; import { requireSuperAdmin, withAuth } from '@/lib/api/helpers'; import { errorResponse } from '@/lib/errors'; import { TABLES_WITH_STORAGE_KEYS } from '@/lib/storage/migrate'; import { getStorageBackend } from '@/lib/storage'; import { S3Backend } from '@/lib/storage/s3'; import { db } from '@/lib/db'; import { sql } from 'drizzle-orm'; export const runtime = 'nodejs'; export const GET = withAuth(async (_req, ctx) => { try { requireSuperAdmin(ctx, 'admin.storage.read'); const backend = await getStorageBackend(); // Aggregate row count + total bytes across every storage-bearing table. let fileCount = 0; const totalBytes = 0; for (const tbl of TABLES_WITH_STORAGE_KEYS) { const result = await db.execute( sql.raw( `SELECT COUNT(*)::bigint AS n FROM ${tbl.table} WHERE ${tbl.keyColumn} IS NOT NULL`, ), ); const rows = ( Array.isArray(result) ? result : ((result as { rows?: unknown[] }).rows ?? []) ) as Array<{ n: number | string }>; fileCount += Number(rows[0]?.n ?? 0); } return NextResponse.json({ data: { backend: backend.name, fileCount, totalBytes, tablesTracked: TABLES_WITH_STORAGE_KEYS.map((t) => t.table), }, }); } catch (error) { return errorResponse(error); } }); export const POST = withAuth(async (_req, ctx) => { try { requireSuperAdmin(ctx, 'admin.storage.test'); const backend = await getStorageBackend(); if (!(backend instanceof S3Backend)) { return NextResponse.json( { ok: false, error: 'Test connection only available for S3 backend' }, { status: 400 }, ); } const result = await backend.healthCheck(); return NextResponse.json(result); } catch (error) { return errorResponse(error); } });