feat: correo via banco mail, rol supervisor y lint en cero

This commit is contained in:
Nurfog committed 2026-09-15 17:28:52 -03:00
1 parent 2502de4e06
commit b1cddbac6c
15 files changed
+677 -107

No files matched your search

+77 -8
View File
@@ -339,18 +339,20 @@ export async function updateSla(
});
}
// ==================== Correo / plantillas ====================
// ==================== Correo / plantillas (banco único mail.* vía /api/email/*) ====================
export interface EmailTemplate {
key: string;
name: string;
subject: string;
bodyHtml: string;
module?: string;
isActive: boolean;
}
export interface EmailLogEntry {
id: number;
template: string;
module?: string;
ticketId?: number;
recipient: string;
subject: string;
@@ -359,28 +361,95 @@ export interface EmailLogEntry {
createdAt: string;
}
export interface EmailModule {
module: string;
isActive: boolean;
}
export async function getEmailTemplates() {
return fetchApi<EmailTemplate[]>("/api/helpdesk/email/templates");
return fetchApi<EmailTemplate[]>("/api/email/templates");
}
export async function createEmailTemplate(data: {
key: string;
name: string;
subject: string;
bodyHtml: string;
module?: string;
}) {
return fetchApi<{ key: string }>("/api/email/templates", {
method: "POST",
body: JSON.stringify(data),
});
}
export async function updateEmailTemplate(
key: string,
data: { subject?: string; bodyHtml?: string; isActive?: boolean }
) {
return fetchApi<void>(`/api/helpdesk/email/templates/${encodeURIComponent(key)}`, {
return fetchApi<void>(`/api/email/templates/${encodeURIComponent(key)}`, {
method: "PUT",
body: JSON.stringify(data),
});
}
export async function getEmailLog(limit = 50) {
return fetchApi<EmailLogEntry[]>(`/api/helpdesk/email/log?limit=${limit}`);
export async function getEmailLog(limit = 50, module = "helpdesk") {
const query = new URLSearchParams({ limit: String(limit) });
if (module) query.set("module", module);
return fetchApi<EmailLogEntry[]>(`/api/email/log?${query.toString()}`);
}
export async function getEmailModules() {
return fetchApi<EmailModule[]>("/api/email/modules");
}
export async function registerEmailModule(module: string) {
return fetchApi<{ module: string; apiKey: string }>("/api/email/modules", {
method: "POST",
body: JSON.stringify({ module }),
});
}
export async function revokeEmailModule(module: string) {
return fetchApi<{ module: string }>(`/api/email/modules/${encodeURIComponent(module)}`, {
method: "DELETE",
});
}
// Envío vía route handler propio (/api/correos) → POST /api/email/send con
// X-Module + X-Api-Key en el servidor (la clave nunca va en el bundle).
// Reenvía el Bearer de admin para que el route verifique el rol.
export async function sendModuleEmail(data: {
to: string | string[];
templateKey?: string;
subject?: string;
htmlBody?: string;
vars?: Record<string, string | null>;
}) {
const token =
typeof window !== "undefined" ? localStorage.getItem("helpdesk_token") : null;
const res = await fetch("/api/correos", {
method: "POST",
headers: {
"Content-Type": "application/json",
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
body: JSON.stringify(data),
});
const payload = await res.json().catch(() => ({}));
if (!res.ok) {
throw new Error(
(payload as { message?: string }).message || `Error de envío: ${res.status}`
);
}
return payload as { success: boolean; sent?: number; failed?: number; message?: string };
}
export async function sendTestEmail(to: string) {
return fetchApi<{ success: boolean; message?: string }>("/api/helpdesk/email/test", {
method: "POST",
body: JSON.stringify({ to }),
return sendModuleEmail({
to,
subject: "[HelpDesk] Prueba de correo",
htmlBody: `<p>Prueba de envío desde la Mesa de Ayuda ICHN (${new Date().toISOString().slice(0, 16).replace("T", " ")} UTC).</p><p>Vía banco <code>mail.templates</code> + <code>POST /api/email/send</code> (módulo helpdesk).</p>`,
});
}