Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | 25x 2x 3x 3x 1x 4x 4x 4x 4x 4x 4x 2x 2x 3x 3x 2x 2x 1x 2x 2x 1x 1x 6x 6x 6x 6x 5x | import type { Dal } from "../../dal";
import type { WaTemplate, NewWaTemplate } from "../../db/schema";
import type {
WhatsAppClient,
WhatsAppTemplateCreateRequest,
WhatsAppTemplateStatusWebhook,
} from "../../lib/whatsapp";
import { NotFoundError } from "../../lib/errors";
export class TemplateService {
constructor(private dal: Dal) {}
async list(): Promise<WaTemplate[]> {
return this.dal.waTemplates.findAll();
}
async getById(id: number): Promise<WaTemplate> {
const template = await this.dal.waTemplates.findById(id);
if (!template) throw new NotFoundError("Template", String(id));
return template;
}
/**
* Sync templates from Meta API to local DB.
* Upserts by name+language.
*/
async sync(whatsAppClient: WhatsAppClient): Promise<number> {
const response = await whatsAppClient.listTemplates();
let synced = 0;
for (const metaTemplate of response.data) {
await this.dal.waTemplates.upsertByName({
metaTemplateId: metaTemplate.id,
name: metaTemplate.name,
language: metaTemplate.language,
category: metaTemplate.category as NewWaTemplate["category"],
status: metaTemplate.status as NewWaTemplate["status"],
components: JSON.stringify(metaTemplate.components),
rejectionReason: metaTemplate.rejected_reason ?? null,
});
synced++;
}
return synced;
}
/**
* Create template on Meta + store locally.
*/
async create(
data: WhatsAppTemplateCreateRequest,
whatsAppClient: WhatsAppClient,
): Promise<WaTemplate> {
const result = await whatsAppClient.createTemplate(data);
return this.dal.waTemplates.create({
metaTemplateId: result.id,
name: data.name,
language: data.language,
category: data.category as NewWaTemplate["category"],
status: "PENDING",
components: JSON.stringify(data.components),
});
}
/**
* Update template locally (Meta only allows editing rejected templates).
*/
async update(
id: number,
data: Partial<Pick<WaTemplate, "components" | "rejectionReason">>,
): Promise<WaTemplate> {
const template = await this.dal.waTemplates.findById(id);
if (!template) throw new NotFoundError("Template", String(id));
const updated = await this.dal.waTemplates.update(id, data);
if (!updated) throw new NotFoundError("Template", String(id));
return updated;
}
/**
* Delete from Meta + local DB.
*/
async delete(id: number, whatsAppClient: WhatsAppClient): Promise<void> {
const template = await this.dal.waTemplates.findById(id);
if (!template) throw new NotFoundError("Template", String(id));
await whatsAppClient.deleteTemplate(template.name);
await this.dal.waTemplates.delete(id);
}
/**
* Handle template status webhook from Meta.
*/
async handleStatusWebhook(
event: WhatsAppTemplateStatusWebhook,
): Promise<void> {
const statusMap: Record<string, NewWaTemplate["status"]> = {
APPROVED: "APPROVED",
REJECTED: "REJECTED",
PENDING_DELETION: "DISABLED",
DISABLED: "DISABLED",
PAUSED: "PAUSED",
IN_APPEAL: "PENDING",
};
const newStatus = statusMap[event.event] ?? "PENDING";
const template = await this.dal.waTemplates.findByName(
event.message_template_name,
event.message_template_language,
);
if (template) {
await this.dal.waTemplates.update(template.id, {
status: newStatus,
rejectionReason: event.reason ?? null,
});
}
}
}
|