mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:50:43 +00:00
perf(push): lazy load web push runtime
This commit is contained in:
@@ -23,10 +23,10 @@ vi.mock("../config/paths.js", () => ({
|
||||
// Stub web-push so we don't make real HTTP requests.
|
||||
vi.mock("web-push", () => ({
|
||||
default: {
|
||||
generateVAPIDKeys: () => ({
|
||||
generateVAPIDKeys: vi.fn(() => ({
|
||||
publicKey: "test-public-key-base64url",
|
||||
privateKey: "test-private-key-base64url",
|
||||
}),
|
||||
})),
|
||||
setVapidDetails: vi.fn(),
|
||||
sendNotification: vi.fn().mockResolvedValue({ statusCode: 201 }),
|
||||
},
|
||||
@@ -52,6 +52,7 @@ describe("resolveVapidKeys", () => {
|
||||
const keys2 = await resolveVapidKeys(tmpDir);
|
||||
expect(keys2.publicKey).toBe(keys.publicKey);
|
||||
expect(keys2.privateKey).toBe(keys.privateKey);
|
||||
expect(vi.mocked(webPush.generateVAPIDKeys)).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("prefers env vars over persisted keys", async () => {
|
||||
@@ -67,6 +68,7 @@ describe("resolveVapidKeys", () => {
|
||||
expect(keys.publicKey).toBe("env-public");
|
||||
expect(keys.privateKey).toBe("env-private");
|
||||
expect(keys.subject).toBe("mailto:env@test.com");
|
||||
expect(vi.mocked(webPush.generateVAPIDKeys)).toHaveBeenCalledTimes(1);
|
||||
} finally {
|
||||
delete process.env.OPENCLAW_VAPID_PUBLIC_KEY;
|
||||
delete process.env.OPENCLAW_VAPID_PRIVATE_KEY;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
import path from "node:path";
|
||||
import webPush from "web-push";
|
||||
import { resolveStateDir } from "../config/paths.js";
|
||||
import { createAsyncLock, readJsonFile, writeJsonAtomic } from "./json-files.js";
|
||||
|
||||
@@ -41,6 +40,18 @@ const DEFAULT_VAPID_SUBJECT = "mailto:openclaw@localhost";
|
||||
|
||||
const withLock = createAsyncLock();
|
||||
|
||||
type WebPushRuntime = typeof import("web-push");
|
||||
type WebPushRuntimeModule = WebPushRuntime & { default?: WebPushRuntime };
|
||||
|
||||
let webPushRuntimePromise: Promise<WebPushRuntime> | undefined;
|
||||
|
||||
async function loadWebPushRuntime(): Promise<WebPushRuntime> {
|
||||
webPushRuntimePromise ??= import("web-push").then(
|
||||
(mod: WebPushRuntimeModule) => mod.default ?? mod,
|
||||
);
|
||||
return await webPushRuntimePromise;
|
||||
}
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
function resolveWebPushStatePath(baseDir?: string): string {
|
||||
@@ -115,6 +126,7 @@ export async function resolveVapidKeys(baseDir?: string): Promise<VapidKeyPair>
|
||||
};
|
||||
}
|
||||
|
||||
const webPush = await loadWebPushRuntime();
|
||||
const keys = webPush.generateVAPIDKeys();
|
||||
const pair: VapidKeyPair = {
|
||||
publicKey: keys.publicKey,
|
||||
@@ -238,7 +250,7 @@ export type WebPushPayload = {
|
||||
url?: string;
|
||||
};
|
||||
|
||||
function applyVapidDetails(keys: VapidKeyPair): void {
|
||||
function applyVapidDetails(webPush: WebPushRuntime, keys: VapidKeyPair): void {
|
||||
webPush.setVapidDetails(keys.subject, keys.publicKey, keys.privateKey);
|
||||
}
|
||||
|
||||
@@ -248,12 +260,14 @@ export async function sendWebPushNotification(
|
||||
vapidKeys?: VapidKeyPair,
|
||||
): Promise<WebPushSendResult> {
|
||||
const keys = vapidKeys ?? (await resolveVapidKeys());
|
||||
applyVapidDetails(keys);
|
||||
const webPush = await loadWebPushRuntime();
|
||||
applyVapidDetails(webPush, keys);
|
||||
|
||||
return sendPreparedWebPushNotification(subscription, payload);
|
||||
return sendPreparedWebPushNotification(webPush, subscription, payload);
|
||||
}
|
||||
|
||||
async function sendPreparedWebPushNotification(
|
||||
webPush: WebPushRuntime,
|
||||
subscription: WebPushSubscription,
|
||||
payload: WebPushPayload,
|
||||
): Promise<WebPushSendResult> {
|
||||
@@ -300,12 +314,13 @@ export async function broadcastWebPush(
|
||||
}
|
||||
|
||||
const vapidKeys = await resolveVapidKeys(baseDir);
|
||||
const webPush = await loadWebPushRuntime();
|
||||
|
||||
// Set VAPID details once before fanning out concurrent sends.
|
||||
applyVapidDetails(vapidKeys);
|
||||
applyVapidDetails(webPush, vapidKeys);
|
||||
|
||||
const results = await Promise.allSettled(
|
||||
subscriptions.map((sub) => sendPreparedWebPushNotification(sub, payload)),
|
||||
subscriptions.map((sub) => sendPreparedWebPushNotification(webPush, sub, payload)),
|
||||
);
|
||||
|
||||
const mapped = results.map((r, i) =>
|
||||
|
||||
Reference in New Issue
Block a user