feat: implement comprehensive notification engine with policies, preferences, and automated dispatching services
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
import { logDone, logError, logIntent } from "../../config/logger.js";
|
||||
|
||||
export interface DispatchResult {
|
||||
success: boolean;
|
||||
providerResponse?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
function formatResponseFailure(endpoint: string, data: unknown): string {
|
||||
const serialized = JSON.stringify(data);
|
||||
return `${endpoint} returned success=false${serialized && serialized !== "{}" ? ` data=${serialized}` : " without error details"}`;
|
||||
}
|
||||
|
||||
function getApiBaseUrl(): string {
|
||||
const raw = process.env.API_URL || "";
|
||||
|
||||
if (!raw.trim()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
return new URL(raw).origin;
|
||||
} catch {
|
||||
return raw.replace(/\/$/, "");
|
||||
}
|
||||
}
|
||||
|
||||
function buildApiUrl(path: string): string {
|
||||
return `${getApiBaseUrl()}${path}`;
|
||||
}
|
||||
|
||||
function formatDispatchError(error: unknown): string {
|
||||
if (typeof error === "object" && error !== null && "isAxiosError" in error) {
|
||||
const axiosError = error as {
|
||||
message?: string;
|
||||
response?: { status?: number; data?: unknown };
|
||||
};
|
||||
const details = [
|
||||
axiosError.response?.status ? `status=${axiosError.response.status}` : undefined,
|
||||
axiosError.response?.data !== undefined ? `data=${JSON.stringify(axiosError.response.data)}` : undefined,
|
||||
axiosError.message ? `message=${axiosError.message}` : undefined,
|
||||
].filter(Boolean);
|
||||
|
||||
return details.join(" ") || "Axios request failed without details";
|
||||
}
|
||||
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
export async function dispatchWhatsApp(params: {
|
||||
companyId: string;
|
||||
appointmentId: string;
|
||||
type: string;
|
||||
systemToken: string;
|
||||
phoneNumber: string;
|
||||
message: string;
|
||||
companyOwnerId: string;
|
||||
}): Promise<DispatchResult> {
|
||||
try {
|
||||
if (!params.phoneNumber.trim() || !params.message.trim() || !params.companyOwnerId.trim()) {
|
||||
return { success: false, error: "WhatsApp dispatch requires phoneNumber, message, and companyOwnerId" };
|
||||
}
|
||||
|
||||
// Send worker-rendered content directly so cancellation jobs survive appointment deletion.
|
||||
const axios = (await import("axios")).default;
|
||||
const response = await axios.post(
|
||||
buildApiUrl("/notifications/send-wap"),
|
||||
{
|
||||
systemToken: params.systemToken,
|
||||
phoneNumber: params.phoneNumber,
|
||||
message: params.message,
|
||||
companyId: params.companyId,
|
||||
sessionUser: params.companyOwnerId,
|
||||
},
|
||||
{
|
||||
headers: { "Content-Type": "application/json" },
|
||||
}
|
||||
);
|
||||
|
||||
logDone(`WhatsApp dispatched for company ${params.companyId}`);
|
||||
return { success: true, providerResponse: JSON.stringify(response.data) };
|
||||
} catch (error) {
|
||||
const msg = formatDispatchError(error);
|
||||
logError(`WhatsApp dispatch failed for company ${params.companyId}:`, msg);
|
||||
return { success: false, error: msg };
|
||||
}
|
||||
}
|
||||
|
||||
export async function dispatchEmail(params: {
|
||||
appointmentId: string;
|
||||
systemToken: string;
|
||||
type: string;
|
||||
email: string;
|
||||
subject: string;
|
||||
message: string;
|
||||
}): Promise<DispatchResult> {
|
||||
try {
|
||||
if (!params.email.trim() || !params.subject.trim() || !params.message.trim()) {
|
||||
return { success: false, error: "Email dispatch requires email, subject, and message" };
|
||||
}
|
||||
|
||||
// Send worker-rendered content directly so cancellation jobs survive appointment deletion.
|
||||
const axios = (await import("axios")).default;
|
||||
const response = await axios.post(
|
||||
buildApiUrl("/notifications/send-email"),
|
||||
{
|
||||
systemToken: params.systemToken,
|
||||
email: params.email,
|
||||
subject: params.subject,
|
||||
message: params.message,
|
||||
},
|
||||
{
|
||||
headers: { "Content-Type": "application/json" },
|
||||
}
|
||||
);
|
||||
|
||||
const responseData = response.data as { success?: boolean };
|
||||
if (responseData.success === false) {
|
||||
return {
|
||||
success: false,
|
||||
error: formatResponseFailure("notifications/send-email", response.data),
|
||||
};
|
||||
}
|
||||
|
||||
logDone(`Email dispatched to ${params.email}`);
|
||||
return { success: true, providerResponse: JSON.stringify(response.data) };
|
||||
} catch (error) {
|
||||
const msg = formatDispatchError(error);
|
||||
logError(`Email dispatch failed for ${params.email}:`, msg);
|
||||
return { success: false, error: msg };
|
||||
}
|
||||
}
|
||||
|
||||
export async function dispatchSystem(params: {
|
||||
systemToken: string;
|
||||
userId: string;
|
||||
subject: string;
|
||||
message: string;
|
||||
companyId?: string;
|
||||
serviceId?: number;
|
||||
conversationId?: string;
|
||||
type?: string;
|
||||
code?: string;
|
||||
}): Promise<DispatchResult> {
|
||||
try {
|
||||
if (!params.userId.trim() || !params.subject.trim() || !params.message.trim()) {
|
||||
return { success: false, error: "System dispatch requires userId, subject, and message" };
|
||||
}
|
||||
|
||||
// Delegate to the server API for system notification
|
||||
const axios = (await import("axios")).default;
|
||||
const response = await axios.post(
|
||||
buildApiUrl("/systemnotifications/create"),
|
||||
{
|
||||
userId: params.userId,
|
||||
subject: params.subject,
|
||||
message: params.message,
|
||||
companyId: params.companyId,
|
||||
serviceId: params.serviceId,
|
||||
conversationId: params.conversationId,
|
||||
type: params.type,
|
||||
code: params.code,
|
||||
systemToken: params.systemToken,
|
||||
},
|
||||
{
|
||||
headers: { "Content-Type": "application/json" },
|
||||
}
|
||||
);
|
||||
|
||||
const responseData = response.data as { success?: boolean };
|
||||
if (responseData.success === false) {
|
||||
return {
|
||||
success: false,
|
||||
error: formatResponseFailure("systemnotifications/create", response.data),
|
||||
};
|
||||
}
|
||||
|
||||
logDone(`System notification dispatched to user ${params.userId}`);
|
||||
return { success: true, providerResponse: JSON.stringify(response.data) };
|
||||
} catch (error) {
|
||||
const msg = formatDispatchError(error);
|
||||
logError(`System notification dispatch failed for user ${params.userId}:`, msg);
|
||||
return { success: false, error: msg };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user