feat(commission) : add push notifications and unread ticket titles
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import "server-only";
|
||||
|
||||
import { and, eq, ne, or } from "drizzle-orm";
|
||||
import webPush from "web-push";
|
||||
import { getDb } from "@/db";
|
||||
import { commissionPushSubscriptions, users } from "@/db/schema";
|
||||
|
||||
export function pushPublicKey() {
|
||||
return process.env.WEB_PUSH_PUBLIC_KEY && process.env.WEB_PUSH_PRIVATE_KEY && process.env.WEB_PUSH_SUBJECT
|
||||
? process.env.WEB_PUSH_PUBLIC_KEY : null;
|
||||
}
|
||||
|
||||
export function validPushEndpoint(value: string) {
|
||||
try {
|
||||
const url = new URL(value);
|
||||
const host = url.hostname.toLowerCase();
|
||||
return url.protocol === "https:" && !url.username && !url.password &&
|
||||
(host === "fcm.googleapis.com" || host === "updates.push.services.mozilla.com" ||
|
||||
host === "web.push.apple.com" || host.endsWith(".push.apple.com") ||
|
||||
host.endsWith(".notify.windows.com"));
|
||||
} catch { return false; }
|
||||
}
|
||||
|
||||
export async function sendCommissionMessagePush(message: {
|
||||
id: string; ticketId: string; ticketTitle: string; customerId: string; authorId: string;
|
||||
authorName: string; text: string | null;
|
||||
}) {
|
||||
if (!pushPublicKey()) return;
|
||||
const subscriptions = await getDb().select({ subscription: commissionPushSubscriptions })
|
||||
.from(commissionPushSubscriptions)
|
||||
.innerJoin(users, eq(users.id, commissionPushSubscriptions.userId))
|
||||
.where(and(ne(commissionPushSubscriptions.userId, message.authorId), eq(users.banned, false),
|
||||
or(and(eq(commissionPushSubscriptions.scope, "customer"),
|
||||
eq(commissionPushSubscriptions.userId, message.customerId)),
|
||||
and(eq(commissionPushSubscriptions.scope, "admin"), eq(users.role, "admin"),
|
||||
eq(users.emailVerified, true)))));
|
||||
webPush.setVapidDetails(process.env.WEB_PUSH_SUBJECT!, process.env.WEB_PUSH_PUBLIC_KEY!,
|
||||
process.env.WEB_PUSH_PRIVATE_KEY!);
|
||||
for (let offset = 0; offset < subscriptions.length; offset += 10) {
|
||||
await Promise.all(subscriptions.slice(offset, offset + 10).map(async ({ subscription }) => {
|
||||
const payload = JSON.stringify({
|
||||
id: message.id, title: `${message.authorName} · ${message.ticketTitle}`,
|
||||
body: message.text?.slice(0, 300) || "ส่งรูปภาพ",
|
||||
url: `/${subscription.scope === "admin" ? "admin/commission" : "commission/tickets"}/${message.ticketId}`,
|
||||
});
|
||||
try {
|
||||
await webPush.sendNotification({ endpoint: subscription.endpoint,
|
||||
keys: { p256dh: subscription.p256dh, auth: subscription.auth } }, payload,
|
||||
{ TTL: 60 * 60 * 24, timeout: 5000 });
|
||||
} catch (cause) {
|
||||
if (cause && typeof cause === "object" && "statusCode" in cause &&
|
||||
(cause.statusCode === 404 || cause.statusCode === 410))
|
||||
await getDb().delete(commissionPushSubscriptions)
|
||||
.where(eq(commissionPushSubscriptions.endpoint, subscription.endpoint));
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user