2025-08-04 17:45:44 +02:00
|
|
|
import type { Override } from "TypeHelpers.type";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { TRPCError } from "@trpc/server";
|
|
|
|
|
import { add, set } from "date-fns";
|
|
|
|
|
import type { NewEventQueue } from "~/schemas/public/EventQueue";
|
2025-08-04 17:45:44 +02:00
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
type MinimalSMSData,
|
|
|
|
|
SendMessage,
|
2025-08-04 17:45:44 +02:00
|
|
|
} from "~/server/controllers/skebby.controller";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { db } from "~/server/db";
|
|
|
|
|
import { NewMail, type NewMailProps } from "~/server/services/mailer";
|
2025-08-04 17:45:44 +02:00
|
|
|
|
|
|
|
|
type Email_EventData = {
|
2025-08-28 18:27:07 +02:00
|
|
|
tipo: "email";
|
|
|
|
|
data: NewMailProps;
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type SMS_EventData = {
|
2025-08-28 18:27:07 +02:00
|
|
|
tipo: "sms";
|
|
|
|
|
data: MinimalSMSData;
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const canSendNotification = () => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const d = new Date();
|
|
|
|
|
const hour = d.getHours();
|
|
|
|
|
return hour >= 9 && hour <= 20;
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const nextTimeForNotification = () => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const d = new Date();
|
|
|
|
|
const targetDay = d.getHours() < 9 ? d : add(d, { days: 1 });
|
|
|
|
|
const lock_expires = set(targetDay, {
|
|
|
|
|
hours: 9,
|
|
|
|
|
minutes: 0,
|
|
|
|
|
});
|
|
|
|
|
return lock_expires;
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type EventDataTypes = Email_EventData | SMS_EventData;
|
|
|
|
|
|
|
|
|
|
type NewEventQueueProps = Override<NewEventQueue, { data: EventDataTypes }>;
|
|
|
|
|
|
|
|
|
|
export const addEventToQueue = async (data: NewEventQueueProps) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
try {
|
|
|
|
|
await db.insertInto("event_queue").values(data).execute();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
|
message: `Error while adding event to queue, ${(e as Error).message}`,
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const getEventsFromQueue = async () => {
|
2025-08-28 18:27:07 +02:00
|
|
|
try {
|
|
|
|
|
const events = await db
|
|
|
|
|
.selectFrom("event_queue")
|
|
|
|
|
.selectAll()
|
|
|
|
|
.where("status", "=", "pending")
|
|
|
|
|
.where("event_queue.lock_expires", "<=", new Date())
|
|
|
|
|
.execute();
|
|
|
|
|
return events;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
throw new TRPCError({
|
|
|
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
|
|
|
message: `Error while getting events from queue, ${(e as Error).message}`,
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const processEvents = async () => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const events = await getEventsFromQueue();
|
|
|
|
|
if (events.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (const e of events) {
|
|
|
|
|
const data = e.data as EventDataTypes;
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
switch (data.tipo) {
|
|
|
|
|
case "email":
|
|
|
|
|
await NewMail({
|
|
|
|
|
...data.data,
|
|
|
|
|
});
|
|
|
|
|
break;
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
case "sms":
|
|
|
|
|
await SendMessage(data.data);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// Mark event as processed
|
|
|
|
|
await db
|
|
|
|
|
.deleteFrom("event_queue")
|
|
|
|
|
.where("event_id", "=", e.event_id)
|
|
|
|
|
.execute();
|
|
|
|
|
}
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|