68 lines
2.2 KiB
TypeScript
68 lines
2.2 KiB
TypeScript
|
|
import { NextResponse } from 'next/server';
|
||
|
|
|
||
|
|
import { withAuth, withPermission } from '@/lib/api/helpers';
|
||
|
|
import { parseBody } from '@/lib/api/route-helpers';
|
||
|
|
import { errorResponse } from '@/lib/errors';
|
||
|
|
import {
|
||
|
|
getSalesEmailConfig,
|
||
|
|
getSalesImapConfig,
|
||
|
|
getSalesContentConfig,
|
||
|
|
redactSalesConfigForResponse,
|
||
|
|
updateSalesEmailConfig,
|
||
|
|
} from '@/lib/services/sales-email-config.service';
|
||
|
|
import { updateSalesEmailConfigSchema } from '@/lib/validators/sales-email-config';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* GET /api/v1/admin/email/sales-config
|
||
|
|
*
|
||
|
|
* Returns the redacted view of the sales-email config. Per §14.10
|
||
|
|
* "Permission escalation: who configures SMTP credentials?", reps cannot
|
||
|
|
* see the decrypted password — the response only carries `*PassIsSet`
|
||
|
|
* boolean markers.
|
||
|
|
*/
|
||
|
|
export const GET = withAuth(
|
||
|
|
withPermission('admin', 'manage_settings', async (_req, ctx) => {
|
||
|
|
try {
|
||
|
|
const [email, imap, content] = await Promise.all([
|
||
|
|
getSalesEmailConfig(ctx.portId),
|
||
|
|
getSalesImapConfig(ctx.portId),
|
||
|
|
getSalesContentConfig(ctx.portId),
|
||
|
|
]);
|
||
|
|
const redacted = redactSalesConfigForResponse(email, imap, content);
|
||
|
|
return NextResponse.json({ data: redacted });
|
||
|
|
} catch (error) {
|
||
|
|
return errorResponse(error);
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* PATCH /api/v1/admin/email/sales-config
|
||
|
|
*
|
||
|
|
* Per-port admin only. Encrypts SMTP/IMAP passwords via AES-256-GCM before
|
||
|
|
* storage; the API never returns decrypted secrets (mirror enforcement on
|
||
|
|
* the GET handler).
|
||
|
|
*/
|
||
|
|
export const PATCH = withAuth(
|
||
|
|
withPermission('admin', 'manage_settings', async (req, ctx) => {
|
||
|
|
try {
|
||
|
|
const input = await parseBody(req, updateSalesEmailConfigSchema);
|
||
|
|
await updateSalesEmailConfig(ctx.portId, input, {
|
||
|
|
userId: ctx.userId,
|
||
|
|
portId: ctx.portId,
|
||
|
|
ipAddress: ctx.ipAddress,
|
||
|
|
userAgent: ctx.userAgent,
|
||
|
|
});
|
||
|
|
// Return the freshly-redacted view so the UI can re-render.
|
||
|
|
const [email, imap, content] = await Promise.all([
|
||
|
|
getSalesEmailConfig(ctx.portId),
|
||
|
|
getSalesImapConfig(ctx.portId),
|
||
|
|
getSalesContentConfig(ctx.portId),
|
||
|
|
]);
|
||
|
|
return NextResponse.json({ data: redactSalesConfigForResponse(email, imap, content) });
|
||
|
|
} catch (error) {
|
||
|
|
return errorResponse(error);
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
);
|