88 lines
2.5 KiB
TypeScript
88 lines
2.5 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
|
|
import { createMemberNotificationService } from '@kit/member-management/services';
|
|
import { getLogger } from '@kit/shared/logger';
|
|
import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';
|
|
|
|
const CRON_SECRET = process.env.CRON_SECRET;
|
|
|
|
/**
|
|
* Internal cron endpoint for member scheduled jobs.
|
|
* Called hourly by pg_cron or external scheduler.
|
|
*
|
|
* POST /api/internal/cron/member-jobs
|
|
* Header: Authorization: Bearer <CRON_SECRET>
|
|
*/
|
|
export async function POST(request: Request) {
|
|
const logger = await getLogger();
|
|
|
|
// Verify cron secret
|
|
const authHeader = request.headers.get('authorization');
|
|
const token = authHeader?.replace('Bearer ', '');
|
|
|
|
if (!CRON_SECRET || token !== CRON_SECRET) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
try {
|
|
const client = getSupabaseServerAdminClient();
|
|
const notificationService = createMemberNotificationService(client);
|
|
|
|
// 1. Process pending notification queue
|
|
const queueResult = await notificationService.processPendingNotifications();
|
|
|
|
// 2. Run scheduled jobs for all accounts with enabled jobs
|
|
const { data: accounts } = await (client.from as any)(
|
|
'scheduled_job_configs',
|
|
)
|
|
.select('account_id')
|
|
.eq('is_enabled', true)
|
|
.or(`next_run_at.is.null,next_run_at.lte.${new Date().toISOString()}`);
|
|
|
|
const uniqueAccountIds = [
|
|
...new Set((accounts ?? []).map((a: any) => a.account_id)),
|
|
] as string[];
|
|
|
|
const jobResults: Record<string, unknown> = {};
|
|
|
|
for (const accountId of uniqueAccountIds) {
|
|
try {
|
|
const result = await notificationService.runScheduledJobs(accountId);
|
|
jobResults[accountId] = result;
|
|
} catch (e) {
|
|
logger.error(
|
|
{ accountId, error: e, context: 'cron-member-jobs' },
|
|
'Failed to run jobs for account',
|
|
);
|
|
jobResults[accountId] = {
|
|
error: e instanceof Error ? e.message : 'Unknown error',
|
|
};
|
|
}
|
|
}
|
|
|
|
const summary = {
|
|
timestamp: new Date().toISOString(),
|
|
queue: queueResult,
|
|
accounts_processed: uniqueAccountIds.length,
|
|
jobs: jobResults,
|
|
};
|
|
|
|
logger.info(
|
|
{ context: 'cron-member-jobs', ...summary },
|
|
'Member cron jobs completed',
|
|
);
|
|
|
|
return NextResponse.json(summary);
|
|
} catch (err) {
|
|
logger.error(
|
|
{ error: err, context: 'cron-member-jobs' },
|
|
'Cron job failed',
|
|
);
|
|
|
|
return NextResponse.json(
|
|
{ error: 'Internal server error' },
|
|
{ status: 500 },
|
|
);
|
|
}
|
|
}
|