Files
famapp/src/app/settings/push-actions.ts
T
ginnoir 19308be768 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
2026-06-01 17:45:20 -05:00

53 lines
1.6 KiB
TypeScript

"use server";
import { and, eq } from "drizzle-orm";
import { db } from "@/lib/db";
import { pushSubscriptions } from "@/modules/_core/schema";
import { getCurrentSession } from "@/lib/session";
import { sendPushToEndpoint } from "@/modules/_core/push";
type PushSubscriptionJSON = {
endpoint: string;
keys: { p256dh: string; auth: string };
};
export async function subscribeToPush(sub: PushSubscriptionJSON, userAgent: string): Promise<void> {
const { user } = await getCurrentSession();
await db
.insert(pushSubscriptions)
.values({
userId: user.id,
endpoint: sub.endpoint,
p256dh: sub.keys.p256dh,
auth: sub.keys.auth,
userAgent: userAgent.slice(0, 512),
})
.onConflictDoUpdate({
target: pushSubscriptions.endpoint,
set: { p256dh: sub.keys.p256dh, auth: sub.keys.auth },
});
}
export async function unsubscribeFromPush(endpoint: string): Promise<void> {
const { user } = await getCurrentSession();
await db
.delete(pushSubscriptions)
.where(and(eq(pushSubscriptions.userId, user.id), eq(pushSubscriptions.endpoint, endpoint)));
}
export async function sendTestNotification(endpoint: string): Promise<void> {
const { user } = await getCurrentSession();
// Verify the endpoint belongs to the current user before sending.
const [owned] = await db
.select({ id: pushSubscriptions.id })
.from(pushSubscriptions)
.where(and(eq(pushSubscriptions.userId, user.id), eq(pushSubscriptions.endpoint, endpoint)))
.limit(1);
if (!owned) return;
await sendPushToEndpoint(endpoint, {
title: "famapp test",
body: "Push notifications are working!",
url: "/settings",
});
}