feat: add test digest mode (?test=true) + group company sees all entries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,15 @@
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { runDigest } from "@/core/notifications";
|
import { runDigest } from "@/core/notifications";
|
||||||
|
import { sendTestDigest } from "@/core/notifications/notification-service";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /api/notifications/digest
|
* POST /api/notifications/digest
|
||||||
*
|
*
|
||||||
* Server-to-server endpoint called by N8N cron.
|
* Server-to-server endpoint called by N8N cron.
|
||||||
* Auth via Authorization: Bearer <NOTIFICATION_CRON_SECRET>
|
* Auth via Authorization: Bearer <NOTIFICATION_CRON_SECRET>
|
||||||
|
*
|
||||||
|
* Query params:
|
||||||
|
* ?test=true — send a test email with sample data to all subscribers
|
||||||
*/
|
*/
|
||||||
export async function POST(request: Request) {
|
export async function POST(request: Request) {
|
||||||
const secret = process.env.NOTIFICATION_CRON_SECRET;
|
const secret = process.env.NOTIFICATION_CRON_SECRET;
|
||||||
@@ -24,7 +28,10 @@ export async function POST(request: Request) {
|
|||||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await runDigest();
|
const url = new URL(request.url);
|
||||||
|
const isTest = url.searchParams.get("test") === "true";
|
||||||
|
|
||||||
|
const result = isTest ? await sendTestDigest() : await runDigest();
|
||||||
|
|
||||||
return NextResponse.json(result, {
|
return NextResponse.json(result, {
|
||||||
status: result.success ? 200 : 500,
|
status: result.success ? 200 : 500,
|
||||||
|
|||||||
@@ -74,7 +74,11 @@ function buildCompanyDigest(
|
|||||||
entries: RegistryEntry[],
|
entries: RegistryEntry[],
|
||||||
company: CompanyId,
|
company: CompanyId,
|
||||||
): DigestSection[] {
|
): DigestSection[] {
|
||||||
const companyEntries = entries.filter((e) => e.company === company);
|
// "group" users see ALL entries across all companies
|
||||||
|
const companyEntries =
|
||||||
|
company === "group"
|
||||||
|
? entries
|
||||||
|
: entries.filter((e) => e.company === company);
|
||||||
const sections: DigestSection[] = [];
|
const sections: DigestSection[] = [];
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -385,3 +389,107 @@ export async function runDigest(): Promise<DigestResult> {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Test digest — sends sample email to all subscribers ──
|
||||||
|
|
||||||
|
export async function sendTestDigest(): Promise<DigestResult> {
|
||||||
|
const result: DigestResult = {
|
||||||
|
success: true,
|
||||||
|
totalEmails: 0,
|
||||||
|
errors: [],
|
||||||
|
companySummary: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const preferences = await getAllPreferences();
|
||||||
|
|
||||||
|
if (preferences.length === 0) {
|
||||||
|
return { ...result, errors: ["Nu exista preferinte de notificare configurate"] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const today = new Date().toISOString().slice(0, 10);
|
||||||
|
|
||||||
|
// Sample data for testing
|
||||||
|
const testSections: DigestSection[] = [
|
||||||
|
{
|
||||||
|
type: "deadline-urgent",
|
||||||
|
title: "Termene urgente (5 zile sau mai putin)",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
entryNumber: "BTG-0001/2026",
|
||||||
|
subject: "[TEST] Certificat de urbanism - str. Exemplu nr. 10",
|
||||||
|
label: "Emitere CU (30 zile lucratoare)",
|
||||||
|
dueDate: today,
|
||||||
|
daysRemaining: 2,
|
||||||
|
color: "yellow",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
entryNumber: "BTG-0005/2026",
|
||||||
|
subject: "[TEST] Aviz ISU - Proiect rezidential",
|
||||||
|
label: "Raspuns aviz ISU (15 zile)",
|
||||||
|
dueDate: today,
|
||||||
|
daysRemaining: 5,
|
||||||
|
color: "yellow",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "deadline-overdue",
|
||||||
|
title: "Termene depasite",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
entryNumber: "BTG-0003/2026",
|
||||||
|
subject: "[TEST] Autorizatie construire - bloc P+4",
|
||||||
|
label: "Emitere AC (30 zile lucratoare)",
|
||||||
|
dueDate: "2026-03-01",
|
||||||
|
daysRemaining: -10,
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "document-expiry",
|
||||||
|
title: "Documente care expira",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
entryNumber: "BTG-0010/2025",
|
||||||
|
subject: "[TEST] CU nr. 123/2025 - proiect mixt",
|
||||||
|
label: "Expira curand",
|
||||||
|
dueDate: "2026-03-25",
|
||||||
|
daysRemaining: 14,
|
||||||
|
color: "yellow",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const pref of preferences) {
|
||||||
|
if (pref.globalOptOut) continue;
|
||||||
|
|
||||||
|
const companyName = COMPANY_LABELS[pref.company] ?? pref.company;
|
||||||
|
const html = renderDigestHtml(testSections, companyName, today);
|
||||||
|
const subject = `[ArchiTools TEST] 4 alerte - ${companyName} (${formatDateRo(today)})`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendEmail({ to: pref.email, subject, html });
|
||||||
|
result.totalEmails++;
|
||||||
|
} catch (err) {
|
||||||
|
result.errors.push(
|
||||||
|
`Eroare trimitere catre ${pref.email}: ${err instanceof Error ? err.message : String(err)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.companySummary["test"] = {
|
||||||
|
emails: result.totalEmails,
|
||||||
|
sections: testSections.length,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
result.success = false;
|
||||||
|
result.errors.push(
|
||||||
|
`Eroare test digest: ${err instanceof Error ? err.message : String(err)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user