tenantpilot/app/api/policy-settings/route.ts
2025-12-05 22:06:22 +01:00

134 lines
3.7 KiB
TypeScript

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 }
);
}
}