import { NextRequest, NextResponse } from 'next/server'; import { db, policySettings } from '@/lib/db'; import { bulkPolicySettingsSchema, type BulkPolicySettingsInput, } from '@/lib/validators/policySettings'; import { env } from '@/lib/env.mjs'; import { eq } from 'drizzle-orm'; /** * POST /api/policy-settings * Bulk upsert policy settings from n8n workflows * * **Security**: Requires X-API-SECRET header matching POLICY_API_SECRET env var */ export async function POST(request: NextRequest) { try { // T020: Validate API Secret const apiSecret = request.headers.get('X-API-SECRET'); if (!apiSecret || apiSecret !== env.POLICY_API_SECRET) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } // T022: Parse and validate request body const body = await request.json(); const validationResult = bulkPolicySettingsSchema.safeParse(body); if (!validationResult.success) { return NextResponse.json( { error: 'Validation failed', details: validationResult.error.issues.map((err) => ({ field: err.path.join('.'), message: err.message, })), }, { status: 400 } ); } const { settings } = validationResult.data as BulkPolicySettingsInput; // T021: Bulk upsert with onConflictDoUpdate let upsertedCount = 0; for (const setting of settings) { await db .insert(policySettings) .values({ tenantId: setting.tenantId, policyName: setting.policyName, policyType: setting.policyType, settingName: setting.settingName, settingValue: setting.settingValue, graphPolicyId: setting.graphPolicyId, lastSyncedAt: new Date(), }) .onConflictDoUpdate({ target: [ policySettings.tenantId, policySettings.graphPolicyId, policySettings.settingName, ], set: { policyName: setting.policyName, policyType: setting.policyType, settingValue: setting.settingValue, lastSyncedAt: new Date(), }, }); upsertedCount++; } return NextResponse.json({ success: true, upsertedCount, message: `${upsertedCount} settings upserted successfully`, }); } catch (error) { console.error('Policy settings upsert failed:', error); return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ); } } /** * DELETE /api/policy-settings?tenantId=xxx * Delete all policy settings for a tenant * * **Security**: Requires X-API-SECRET header */ export async function DELETE(request: NextRequest) { try { // T024: Validate API Secret const apiSecret = request.headers.get('X-API-SECRET'); if (!apiSecret || apiSecret !== env.POLICY_API_SECRET) { return NextResponse.json( { error: 'Unauthorized' }, { status: 401 } ); } const { searchParams } = new URL(request.url); const tenantId = searchParams.get('tenantId'); if (!tenantId) { return NextResponse.json( { error: 'tenantId query parameter is required' }, { status: 400 } ); } const result = await db .delete(policySettings) .where(eq(policySettings.tenantId, tenantId)); return NextResponse.json({ success: true, deletedCount: result.rowCount ?? 0, message: `${result.rowCount ?? 0} settings deleted for tenant`, }); } catch (error) { console.error('Policy settings deletion failed:', error); return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ); } }