feat: add dev startup script, fix push notifications, and scope test sends to device
- pnpm dev:local/dev:reset: orchestrate DB container, migrations, seed, and Next.js dev server in one command; Caddy snippet + docs for HTTPS via dev.ginnoir.com - Fix dev login on HTTPS: set both authjs.session-token and __Secure-authjs.session-token so Auth.js finds the session regardless of cookie name resolution - Suppress hydration mismatch on <html> caused by pre-paint script changing data-nav before React hydrates - VAPID startup warning if keys not configured; remove dead NEXT_PUBLIC_VAPID_PUBLIC_KEY var - PushOptIn: hydrate subscription state on mount; reuse existing subscription on iOS to avoid redundant prompts - sendPushToEndpoint: new function to send to a single device endpoint - sendTestNotification: scoped to the calling device's endpoint (ownership-verified) instead of all user subscriptions
This commit is contained in:
@@ -22,6 +22,6 @@ export type { QuickAddItem, SerializedQuickAddItem, SerializedWidgetMeta } from
|
||||
export { logActivity, logShareActivity } from "./activity";
|
||||
export { createShareLink, resolveShareToken, revokeShareLink } from "./share";
|
||||
export type { ShareLinkCapabilities, CreateShareLinkResult } from "./share";
|
||||
export { sendPush } from "./push";
|
||||
export { sendPush, sendPushToEndpoint } from "./push";
|
||||
export { notify } from "./notify";
|
||||
export { scheduleReminder, cancelReminder, listReminders, startReminderWorker } from "./reminders";
|
||||
|
||||
@@ -14,6 +14,32 @@ function ensureVapidConfigured() {
|
||||
webPush.setVapidDetails(subject, publicKey, privateKey);
|
||||
}
|
||||
|
||||
export async function sendPushToEndpoint(
|
||||
endpoint: string,
|
||||
payload: { title: string; body: string; url?: string },
|
||||
) {
|
||||
ensureVapidConfigured();
|
||||
const [sub] = await db
|
||||
.select()
|
||||
.from(pushSubscriptions)
|
||||
.where(eq(pushSubscriptions.endpoint, endpoint))
|
||||
.limit(1);
|
||||
if (!sub) return;
|
||||
try {
|
||||
await webPush.sendNotification(
|
||||
{ endpoint: sub.endpoint, keys: { p256dh: sub.p256dh, auth: sub.auth } },
|
||||
JSON.stringify({ title: payload.title, body: payload.body, url: payload.url ?? "/" }),
|
||||
);
|
||||
} catch (err) {
|
||||
const status = (err as { statusCode?: number }).statusCode;
|
||||
if (status === 404 || status === 410) {
|
||||
await db.delete(pushSubscriptions).where(eq(pushSubscriptions.endpoint, endpoint));
|
||||
} else {
|
||||
logger.error({ err }, "push delivery failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendPush(
|
||||
userId: string,
|
||||
payload: { title: string; body: string; url?: string },
|
||||
|
||||
Reference in New Issue
Block a user