78 lines
2.3 KiB
TypeScript
78 lines
2.3 KiB
TypeScript
import { prisma } from "@/lib/prisma";
|
|
import { Prisma } from "@/app/generated/prisma/client";
|
|
import type { EmailJobStatus } from "@/app/generated/prisma/enums";
|
|
|
|
/** Filter untuk halaman admin email log. Keduanya opsional, match `contains`. */
|
|
export interface EmailLogFilters {
|
|
to?: string;
|
|
template?: string;
|
|
}
|
|
|
|
const LIST_LIMIT = 100;
|
|
|
|
function buildWhere<T extends { to?: unknown; template?: unknown }>(
|
|
filters: EmailLogFilters
|
|
): T {
|
|
const where = {} as T;
|
|
if (filters.to) {
|
|
(where as { to?: unknown }).to = {
|
|
contains: filters.to,
|
|
mode: "insensitive",
|
|
};
|
|
}
|
|
if (filters.template) {
|
|
(where as { template?: unknown }).template = {
|
|
contains: filters.template,
|
|
mode: "insensitive",
|
|
};
|
|
}
|
|
return where;
|
|
}
|
|
|
|
export const emailRepo = {
|
|
/** EmailJob (retry queue) per status — terbaru dulu. */
|
|
async listJobs(statuses: EmailJobStatus[], filters: EmailLogFilters) {
|
|
const where = buildWhere<Prisma.EmailJobWhereInput>(filters);
|
|
where.status = { in: statuses };
|
|
return prisma.emailJob.findMany({
|
|
where,
|
|
orderBy: { updatedAt: "desc" },
|
|
take: LIST_LIMIT,
|
|
});
|
|
},
|
|
|
|
/** EmailSent (log email berhasil terkirim) — terbaru dulu. */
|
|
async listSent(filters: EmailLogFilters) {
|
|
const where = buildWhere<Prisma.EmailSentWhereInput>(filters);
|
|
return prisma.emailSent.findMany({
|
|
where,
|
|
orderBy: { sentAt: "desc" },
|
|
take: LIST_LIMIT,
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Statistik kesehatan pengiriman email — dipakai kartu ringkasan
|
|
* `/admin/emails` dan `/admin/system`.
|
|
* - `queued` : job menunggu dikirim (PENDING/PROCESSING).
|
|
* - `failed24h` : job gagal dalam 24 jam terakhir.
|
|
* - `deadLetter` : job gagal yang sudah habis 5 attempt — cron berhenti
|
|
* retry, butuh aksi manual admin.
|
|
*/
|
|
async stats() {
|
|
const since24h = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
const [queued, failed24h, deadLetter] = await Promise.all([
|
|
prisma.emailJob.count({
|
|
where: { status: { in: ["PENDING", "PROCESSING"] } },
|
|
}),
|
|
prisma.emailJob.count({
|
|
where: { status: "FAILED", updatedAt: { gte: since24h } },
|
|
}),
|
|
prisma.emailJob.count({
|
|
where: { status: "FAILED", attempts: { gte: 5 } },
|
|
}),
|
|
]);
|
|
return { queued, failed24h, deadLetter };
|
|
},
|
|
};
|