feat: implement automated job cleanup, update notification presets, and upgrade Baileys library
This commit is contained in:
@@ -8,8 +8,17 @@ const POLL_INTERVAL_MS = 5000;
|
||||
const BASE_RETRY_DELAY_MS = 5000;
|
||||
const MAX_JITTER_MS = 3000;
|
||||
const THROTTLE_RETRY_DELAY_MS = 8000;
|
||||
const DEFAULT_CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
|
||||
const SUPPORTED_APPOINTMENT_NOTIFICATION_TYPES = new Set(["creation", "reminder", "cancellation"]);
|
||||
|
||||
function resolveCleanupIntervalMs(): number {
|
||||
const raw = process.env.NOTIFICATION_JOB_CLEANUP_INTERVAL_MS;
|
||||
if (!raw) return DEFAULT_CLEANUP_INTERVAL_MS;
|
||||
|
||||
const parsed = Number(raw);
|
||||
return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_CLEANUP_INTERVAL_MS;
|
||||
}
|
||||
|
||||
function resolveAppointmentNotificationType(type: unknown): string {
|
||||
return typeof type === "string" && SUPPORTED_APPOINTMENT_NOTIFICATION_TYPES.has(type) ? type : "reminder";
|
||||
}
|
||||
@@ -28,6 +37,10 @@ export function calculateNextRetryAt(attempt: number, now: Date): Date {
|
||||
return new Date(now.getTime() + delay + jitter);
|
||||
}
|
||||
|
||||
export function buildStartOfDay(date: Date): Date {
|
||||
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||
}
|
||||
|
||||
function getJobId(job: { id?: unknown; _id?: unknown; appointmentId?: unknown; channel?: unknown; get?: (path: string) => unknown }): string {
|
||||
const value = job.id || job._id || job.get?.("_id");
|
||||
if (value) return String(value);
|
||||
@@ -41,24 +54,53 @@ export class JobProcessor {
|
||||
private jobModel: NotificationJobModel;
|
||||
private throttle: WhatsAppThrottleClass;
|
||||
private polling = false;
|
||||
private cleanupTimeout?: NodeJS.Timeout;
|
||||
private cleanupIntervalMs: number;
|
||||
|
||||
constructor() {
|
||||
this.jobModel = new NotificationJobModel();
|
||||
this.throttle = new WhatsAppThrottleClass();
|
||||
this.cleanupIntervalMs = resolveCleanupIntervalMs();
|
||||
}
|
||||
|
||||
public async startPolling(): Promise<void> {
|
||||
if (this.polling) return;
|
||||
this.polling = true;
|
||||
logIntent("JobProcessor: starting poll loop");
|
||||
await this.cleanupOldJobs();
|
||||
this.scheduleCleanup();
|
||||
this.poll();
|
||||
}
|
||||
|
||||
public stopPolling(): void {
|
||||
this.polling = false;
|
||||
if (this.cleanupTimeout) {
|
||||
clearTimeout(this.cleanupTimeout);
|
||||
this.cleanupTimeout = undefined;
|
||||
}
|
||||
logIntent("JobProcessor: stopping poll loop");
|
||||
}
|
||||
|
||||
private scheduleCleanup(): void {
|
||||
if (!this.polling || this.cleanupIntervalMs <= 0) return;
|
||||
|
||||
this.cleanupTimeout = setTimeout(async () => {
|
||||
await this.cleanupOldJobs();
|
||||
this.scheduleCleanup();
|
||||
}, this.cleanupIntervalMs);
|
||||
}
|
||||
|
||||
private async cleanupOldJobs(now = new Date()): Promise<void> {
|
||||
const cutoff = buildStartOfDay(now);
|
||||
|
||||
try {
|
||||
const deletedCount = await this.jobModel.deleteBeforeScheduledAt(cutoff);
|
||||
logDone(`JobProcessor: cleaned ${deletedCount} old notification jobs before ${cutoff.toISOString()}`);
|
||||
} catch (error) {
|
||||
logError(`JobProcessor: cleanup error before ${cutoff.toISOString()}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
private async poll(): Promise<void> {
|
||||
if (!this.polling) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user