153 lines
4.1 KiB
TypeScript
153 lines
4.1 KiB
TypeScript
import webpush from "web-push";
|
|
import type { ApprovalType, BudgetReleaseNotifyTarget } from "@prisma/client";
|
|
|
|
import { approvalLabel } from "@/lib/domain";
|
|
import prisma from "@/lib/prisma";
|
|
|
|
type PushTargetExpense = {
|
|
id: string;
|
|
title: string;
|
|
amount: number;
|
|
workingGroupId: string;
|
|
};
|
|
|
|
type PushTargetBudgetRelease = {
|
|
id: string;
|
|
name: string;
|
|
workingGroupId: string;
|
|
workingGroupName: string;
|
|
releasedAmount: number;
|
|
};
|
|
|
|
function configureWebPush() {
|
|
const publicKey = process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY;
|
|
const privateKey = process.env.VAPID_PRIVATE_KEY;
|
|
const subject = process.env.VAPID_SUBJECT;
|
|
|
|
if (!publicKey || !privateKey || !subject) {
|
|
return false;
|
|
}
|
|
|
|
webpush.setVapidDetails(subject, publicKey, privateKey);
|
|
return true;
|
|
}
|
|
|
|
export function getApprovalRole(approvalType: ApprovalType) {
|
|
switch (approvalType) {
|
|
case "CHAIR_A":
|
|
return "ORGA";
|
|
case "CHAIR_B":
|
|
return "BOARD";
|
|
case "FINANCE":
|
|
return "FINANCE";
|
|
}
|
|
}
|
|
|
|
export async function notifyApprovalRequest(expense: PushTargetExpense, approvalTypes: ApprovalType[]) {
|
|
if (!configureWebPush() || approvalTypes.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const roles = approvalTypes.map(getApprovalRole);
|
|
const subscriptions = await prisma.pushSubscription.findMany({
|
|
where: {
|
|
user: {
|
|
role: {
|
|
in: roles
|
|
}
|
|
}
|
|
},
|
|
include: {
|
|
user: {
|
|
select: {
|
|
role: true
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
await Promise.all(
|
|
subscriptions.map(async (subscription) => {
|
|
const approvalType = approvalTypes.find((type) => getApprovalRole(type) === subscription.user.role);
|
|
const payload = JSON.stringify({
|
|
title: "Freigabe angefragt",
|
|
body: `${expense.title} (${expense.amount.toFixed(2)} EUR) braucht ${approvalType ? approvalLabel(approvalType) : "deine Freigabe"}.`,
|
|
url: `/?expense=${encodeURIComponent(expense.id)}&group=${encodeURIComponent(expense.workingGroupId)}`,
|
|
tag: `approval-${expense.id}-${subscription.user.role}`
|
|
});
|
|
|
|
try {
|
|
await webpush.sendNotification(
|
|
{
|
|
endpoint: subscription.endpoint,
|
|
keys: {
|
|
p256dh: subscription.p256dh,
|
|
auth: subscription.auth
|
|
}
|
|
},
|
|
payload
|
|
);
|
|
} catch (error) {
|
|
const statusCode = typeof error === "object" && error && "statusCode" in error ? error.statusCode : null;
|
|
|
|
if (statusCode === 404 || statusCode === 410) {
|
|
await prisma.pushSubscription.delete({
|
|
where: {
|
|
endpoint: subscription.endpoint
|
|
}
|
|
});
|
|
}
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
export async function notifyBudgetRelease(budget: PushTargetBudgetRelease, target: BudgetReleaseNotifyTarget) {
|
|
if (!configureWebPush()) {
|
|
return;
|
|
}
|
|
|
|
const subscriptions = await prisma.pushSubscription.findMany({
|
|
where: {
|
|
user: {
|
|
workingGroupId: budget.workingGroupId,
|
|
...(target === "GROUP_MEMBERS_ONLY" ? { role: "MEMBER" } : {})
|
|
}
|
|
}
|
|
});
|
|
|
|
await Promise.all(
|
|
subscriptions.map(async (subscription) => {
|
|
const payload = JSON.stringify({
|
|
title: "Budget freigegeben",
|
|
body: `${budget.workingGroupName}: ${budget.name} wurde mit ${budget.releasedAmount.toFixed(2)} EUR freigegeben.`,
|
|
url: `/?budget=${encodeURIComponent(budget.id)}&group=${encodeURIComponent(budget.workingGroupId)}`,
|
|
tag: `budget-release-${budget.id}`
|
|
});
|
|
|
|
try {
|
|
await webpush.sendNotification(
|
|
{
|
|
endpoint: subscription.endpoint,
|
|
keys: {
|
|
p256dh: subscription.p256dh,
|
|
auth: subscription.auth
|
|
}
|
|
},
|
|
payload
|
|
);
|
|
} catch (error) {
|
|
const statusCode = typeof error === "object" && error && "statusCode" in error ? error.statusCode : null;
|
|
|
|
if (statusCode === 404 || statusCode === 410) {
|
|
await prisma.pushSubscription.delete({
|
|
where: {
|
|
endpoint: subscription.endpoint
|
|
}
|
|
});
|
|
}
|
|
}
|
|
})
|
|
);
|
|
}
|