feat: private r2 bucket and secure sockets
This commit is contained in:
@@ -5,13 +5,18 @@
|
||||
* WebSocket connection to our Soketi (Pusher-compatible) server so the
|
||||
* bell badge + a toast update in realtime.
|
||||
*
|
||||
* Transport: subscribes to the PUBLIC channel `notifications.{userId}` and
|
||||
* binds the `notification` event the BFF triggers on each follow-request /
|
||||
* Transport: subscribes to the PRIVATE channel `private-notifications.{userId}`
|
||||
* and binds the `notification` event the BFF triggers on each follow-request /
|
||||
* follow-accept. The event is a lightweight nudge — we bump the unread
|
||||
* count locally and toast — while the authoritative list/count still come
|
||||
* from the REST endpoints. (Public channel for now; once Soketi App auth
|
||||
* is configured, switch to a `private-` channel + a /broadcasting/auth
|
||||
* endpoint — only the channel name and a Pusher `authEndpoint` change.)
|
||||
* from the REST endpoints.
|
||||
*
|
||||
* The channel is private so a subscription requires a signature the BFF only
|
||||
* issues to that user; on the old public channel anyone who knew a user's id
|
||||
* could listen in. Signing goes through `channelAuthorizer` (see
|
||||
* lib/realtime.ts). The BFF still publishes to the old public channel too, so
|
||||
* a service-worker-cached bundle running this file's previous version keeps
|
||||
* working until it updates.
|
||||
*
|
||||
* Config via NEXT_PUBLIC_SOKETI_* env vars; when the key/host are absent
|
||||
* realtime silently disables and the bell falls back to its fetched count.
|
||||
@@ -26,6 +31,7 @@ import {
|
||||
} from "react";
|
||||
import { toast } from "sonner";
|
||||
import { useAuth } from "@/components/auth/auth-provider";
|
||||
import { channelAuthorizer } from "@/lib/realtime";
|
||||
import { feed, notifications } from "@/lib/social";
|
||||
import { sadhna } from "@/lib/sadhna";
|
||||
|
||||
@@ -145,7 +151,7 @@ export function NotificationsProvider({
|
||||
const port = Number(process.env.NEXT_PUBLIC_SOKETI_PORT || "443");
|
||||
const forceTLS =
|
||||
(process.env.NEXT_PUBLIC_SOKETI_FORCE_TLS ?? "true") !== "false";
|
||||
const channelName = `notifications.${userId}`;
|
||||
const channelName = `private-notifications.${userId}`;
|
||||
|
||||
let cancelled = false;
|
||||
// Dynamic import keeps pusher-js out of SSR (it touches window) and the
|
||||
@@ -162,8 +168,16 @@ export function NotificationsProvider({
|
||||
disableStats: true,
|
||||
// Ignored when wsHost is set, but required by the type.
|
||||
cluster: "",
|
||||
// Private channels are refused without a signature from the BFF.
|
||||
channelAuthorization: { customHandler: channelAuthorizer },
|
||||
});
|
||||
const channel = pusher.subscribe(channelName);
|
||||
// Authorization can legitimately fail (expired session, realtime not
|
||||
// configured). Realtime is a nudge, not the source of truth, so degrade
|
||||
// quietly — the bell keeps working off its fetched count.
|
||||
channel.bind("pusher:subscription_error", (status: unknown) => {
|
||||
console.warn("notifications: realtime subscription failed", status);
|
||||
});
|
||||
channel.bind("notification", (data: IncomingEvent) => {
|
||||
setUnread((u) => u + 1);
|
||||
setLastEventAt(Date.now());
|
||||
|
||||
Reference in New Issue
Block a user