134 lines
3.7 KiB
TypeScript
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 }
|
|
);
|
|
}
|
|
}
|