Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
import { Worker, type Job } from 'bullmq';
|
fix(ops): /health DB+Redis checks, validated env.REDIS_URL across workers, error_events 90d retention
Three audit-pass-#3 findings, all in the "wakes you at 3am" category.
- /api/public/health now runs DB SELECT 1 + Redis PING in parallel and
returns 503 + a degraded payload when either fails. Anonymous probes
(no X-Intake-Secret) still get a flat {status:'ok'} so generic uptime
monitors keep working; authenticated probes see the dep results.
- All worker entrypoints (ai, bulk, documents, email, export, import,
maintenance, notifications, reports, webhooks) and src/lib/redis.ts
now use env.REDIS_URL (Zod-validated at boot) instead of
process.env.REDIS_URL!. Previously a missing env let the app start
silently and fail at first job pickup.
- maintenance worker gains an `error-events-retention` case that
delete()s rows older than 90 days from error_events. scheduler.ts
registers it at 06:00 daily. Closes the contract from migration
0040 which declared the table "pruned at 90 days" but had no
implementation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 14:59:07 +02:00
|
|
|
import { env } from '@/lib/env';
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
|
|
|
|
|
import type { ConnectionOptions } from 'bullmq';
|
|
|
|
|
import { logger } from '@/lib/logger';
|
|
|
|
|
import { QUEUE_CONFIGS } from '@/lib/queue';
|
|
|
|
|
|
|
|
|
|
export const reportsWorker = new Worker(
|
|
|
|
|
'reports',
|
|
|
|
|
async (job: Job) => {
|
|
|
|
|
logger.info({ jobId: job.id, jobName: job.name }, 'Processing reports job');
|
|
|
|
|
|
|
|
|
|
switch (job.name) {
|
|
|
|
|
case 'report-scheduler': {
|
|
|
|
|
// Check scheduled_reports for reports due to run
|
|
|
|
|
const { db } = await import('@/lib/db');
|
|
|
|
|
const { scheduledReports } = await import('@/lib/db/schema/operations');
|
|
|
|
|
const { generatedReports } = await import('@/lib/db/schema/operations');
|
|
|
|
|
const { eq, and, lte } = await import('drizzle-orm');
|
|
|
|
|
|
|
|
|
|
const dueReports = await db
|
|
|
|
|
.select()
|
|
|
|
|
.from(scheduledReports)
|
|
|
|
|
.where(
|
fix(ops): /health DB+Redis checks, validated env.REDIS_URL across workers, error_events 90d retention
Three audit-pass-#3 findings, all in the "wakes you at 3am" category.
- /api/public/health now runs DB SELECT 1 + Redis PING in parallel and
returns 503 + a degraded payload when either fails. Anonymous probes
(no X-Intake-Secret) still get a flat {status:'ok'} so generic uptime
monitors keep working; authenticated probes see the dep results.
- All worker entrypoints (ai, bulk, documents, email, export, import,
maintenance, notifications, reports, webhooks) and src/lib/redis.ts
now use env.REDIS_URL (Zod-validated at boot) instead of
process.env.REDIS_URL!. Previously a missing env let the app start
silently and fail at first job pickup.
- maintenance worker gains an `error-events-retention` case that
delete()s rows older than 90 days from error_events. scheduler.ts
registers it at 06:00 daily. Closes the contract from migration
0040 which declared the table "pruned at 90 days" but had no
implementation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 14:59:07 +02:00
|
|
|
and(eq(scheduledReports.isActive, true), lte(scheduledReports.nextRunAt, new Date())),
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for (const report of dueReports) {
|
|
|
|
|
const { getQueue } = await import('@/lib/queue');
|
|
|
|
|
|
|
|
|
|
const [genReport] = await db
|
|
|
|
|
.insert(generatedReports)
|
|
|
|
|
.values({
|
|
|
|
|
portId: report.portId,
|
|
|
|
|
scheduledReportId: report.id,
|
|
|
|
|
reportType: report.reportType,
|
|
|
|
|
name: `${report.name} - ${new Date().toISOString().split('T')[0]}`,
|
|
|
|
|
status: 'queued',
|
|
|
|
|
parameters: (report.config as Record<string, unknown>) ?? {},
|
|
|
|
|
requestedBy: report.createdBy,
|
|
|
|
|
})
|
|
|
|
|
.returning();
|
|
|
|
|
|
|
|
|
|
if (genReport) {
|
|
|
|
|
await getQueue('reports').add('generate-report', {
|
|
|
|
|
reportJobId: genReport.id,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 'generate-report': {
|
|
|
|
|
const { reportJobId } = job.data as { reportJobId: string };
|
|
|
|
|
const { generateReport } = await import('@/lib/services/reports.service');
|
|
|
|
|
await generateReport(reportJobId);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
logger.warn({ jobName: job.name }, 'Unknown reports job');
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
fix(ops): /health DB+Redis checks, validated env.REDIS_URL across workers, error_events 90d retention
Three audit-pass-#3 findings, all in the "wakes you at 3am" category.
- /api/public/health now runs DB SELECT 1 + Redis PING in parallel and
returns 503 + a degraded payload when either fails. Anonymous probes
(no X-Intake-Secret) still get a flat {status:'ok'} so generic uptime
monitors keep working; authenticated probes see the dep results.
- All worker entrypoints (ai, bulk, documents, email, export, import,
maintenance, notifications, reports, webhooks) and src/lib/redis.ts
now use env.REDIS_URL (Zod-validated at boot) instead of
process.env.REDIS_URL!. Previously a missing env let the app start
silently and fail at first job pickup.
- maintenance worker gains an `error-events-retention` case that
delete()s rows older than 90 days from error_events. scheduler.ts
registers it at 06:00 daily. Closes the contract from migration
0040 which declared the table "pruned at 90 days" but had no
implementation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 14:59:07 +02:00
|
|
|
connection: { url: env.REDIS_URL } as ConnectionOptions,
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
concurrency: QUEUE_CONFIGS.reports.concurrency,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
reportsWorker.on('failed', (job, err) => {
|
|
|
|
|
logger.error({ jobId: job?.id, jobName: job?.name, err }, 'Reports job failed');
|
|
|
|
|
});
|