新增设置页
This commit is contained in:
@@ -283,4 +283,20 @@ export async function getCaregiverActivity(caregiverId: string) {
|
||||
conversationTurns,
|
||||
toolCalls,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getCaregiverSettingsOverview(caregiverId: string) {
|
||||
const [deviceCount, pushSubscriptionCount] = await prisma.$transaction([
|
||||
prisma.deviceBinding.count({
|
||||
where: { caregiverId },
|
||||
}),
|
||||
prisma.pushSubscription.count({
|
||||
where: { caregiverId },
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
deviceCount,
|
||||
pushSubscriptionCount,
|
||||
};
|
||||
}
|
||||
+119
-32
@@ -13,6 +13,20 @@ type SerializablePushSubscription = {
|
||||
};
|
||||
};
|
||||
|
||||
type StoredPushSubscription = {
|
||||
endpoint: string;
|
||||
expirationTime: Date | null;
|
||||
p256dh: string;
|
||||
auth: string;
|
||||
};
|
||||
|
||||
type PushDeliverySummary = {
|
||||
targetedCount: number;
|
||||
sentCount: number;
|
||||
removedCount: number;
|
||||
failedCount: number;
|
||||
};
|
||||
|
||||
const APP_BASE_URL = (
|
||||
process.env.NEXT_PUBLIC_APP_URL || "https://digital-human.xn--876a.net"
|
||||
).replace(/\/+$/, "");
|
||||
@@ -119,6 +133,70 @@ export async function removePushSubscription(input: {
|
||||
});
|
||||
}
|
||||
|
||||
async function deliverPushToSubscriptions(input: {
|
||||
subscriptions: StoredPushSubscription[];
|
||||
payload: string;
|
||||
topic: string;
|
||||
}) {
|
||||
if (input.subscriptions.length === 0) {
|
||||
return {
|
||||
targetedCount: 0,
|
||||
sentCount: 0,
|
||||
removedCount: 0,
|
||||
failedCount: 0,
|
||||
} satisfies PushDeliverySummary;
|
||||
}
|
||||
|
||||
ensureVapidDetails();
|
||||
|
||||
const results = await Promise.all(
|
||||
input.subscriptions.map(async (subscription) => {
|
||||
try {
|
||||
await webpush.sendNotification(
|
||||
{
|
||||
endpoint: subscription.endpoint,
|
||||
expirationTime: subscription.expirationTime?.getTime() || null,
|
||||
keys: {
|
||||
p256dh: subscription.p256dh,
|
||||
auth: subscription.auth,
|
||||
},
|
||||
},
|
||||
input.payload,
|
||||
{
|
||||
TTL: 60 * 30,
|
||||
urgency: "high",
|
||||
topic: input.topic,
|
||||
},
|
||||
);
|
||||
|
||||
return "sent" as const;
|
||||
} catch (error) {
|
||||
const statusCode =
|
||||
error && typeof error === "object" && "statusCode" in error
|
||||
? Number((error as { statusCode?: number }).statusCode)
|
||||
: 0;
|
||||
|
||||
if (statusCode === 404 || statusCode === 410) {
|
||||
await prisma.pushSubscription.deleteMany({
|
||||
where: { endpoint: subscription.endpoint },
|
||||
});
|
||||
|
||||
return "removed" as const;
|
||||
}
|
||||
|
||||
return "failed" as const;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
targetedCount: input.subscriptions.length,
|
||||
sentCount: results.filter((result) => result === "sent").length,
|
||||
removedCount: results.filter((result) => result === "removed").length,
|
||||
failedCount: results.filter((result) => result === "failed").length,
|
||||
} satisfies PushDeliverySummary;
|
||||
}
|
||||
|
||||
export async function sendIncomingMessagePush(publicId: number) {
|
||||
if (!hasPushConfiguration()) {
|
||||
return;
|
||||
@@ -172,37 +250,46 @@ export async function sendIncomingMessagePush(publicId: number) {
|
||||
tag: `message-${message.publicId}`,
|
||||
});
|
||||
|
||||
await Promise.allSettled(
|
||||
subscriptions.map(async (subscription) => {
|
||||
try {
|
||||
await webpush.sendNotification(
|
||||
{
|
||||
endpoint: subscription.endpoint,
|
||||
expirationTime: subscription.expirationTime?.getTime() || null,
|
||||
keys: {
|
||||
p256dh: subscription.p256dh,
|
||||
auth: subscription.auth,
|
||||
},
|
||||
},
|
||||
payload,
|
||||
{
|
||||
TTL: 60 * 30,
|
||||
urgency: "high",
|
||||
topic: `msg-${message.publicId}`,
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
const statusCode =
|
||||
error && typeof error === "object" && "statusCode" in error
|
||||
? Number((error as { statusCode?: number }).statusCode)
|
||||
: 0;
|
||||
await deliverPushToSubscriptions({
|
||||
subscriptions,
|
||||
payload,
|
||||
topic: `msg-${message.publicId}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (statusCode === 404 || statusCode === 410) {
|
||||
await prisma.pushSubscription.deleteMany({
|
||||
where: { endpoint: subscription.endpoint },
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
export async function sendCaregiverTestPush(input: {
|
||||
caregiverId: string;
|
||||
caregiverLabel: string;
|
||||
}) {
|
||||
if (!hasPushConfiguration()) {
|
||||
throw new Error("当前环境还没有配置推送服务。",
|
||||
);
|
||||
}
|
||||
|
||||
const subscriptions = await prisma.pushSubscription.findMany({
|
||||
where: {
|
||||
caregiverId: input.caregiverId,
|
||||
},
|
||||
select: {
|
||||
endpoint: true,
|
||||
expirationTime: true,
|
||||
p256dh: true,
|
||||
auth: true,
|
||||
},
|
||||
});
|
||||
|
||||
const payload = JSON.stringify({
|
||||
title: "安智伴推送测试",
|
||||
body: `${input.caregiverLabel} 账号下的通知链路工作正常。`,
|
||||
url: absoluteUrl("/settings"),
|
||||
icon: absoluteUrl("/pwa/icon-192x192.png"),
|
||||
badge: absoluteUrl("/pwa/badge-96x96.png"),
|
||||
tag: `push-test-${input.caregiverId}`,
|
||||
});
|
||||
|
||||
return deliverPushToSubscriptions({
|
||||
subscriptions,
|
||||
payload,
|
||||
topic: `test-${input.caregiverId.slice(0, 20)}`,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user