Persist payment-webhook dedup; declare BTCPay required; scope CORS
Replace the in-memory dedup Sets in the BTCPay and Zaprite webhook handlers (and the BTCPay rescan path) with a persistent JSON-backed store (server/webhook-dedup.js). The in-memory sets were cleared on restart, so a duplicate webhook delivery straddling a relay restart could double-credit (BTCPay) or double-extend a subscription (Zaprite). The store atomically writes /data/processed-webhooks.json, namespaces keys per rail (storeId|invoiceId vs zaprite:orderId), and prunes entries older than 180 days (safely beyond any retry window). Also: - BTCPay is a required running dependency (operator decision). Config was already optional:false/kind:'running'; corrected the contradictory "optional" comment in the manifest to match. - Scope cors() to /relay/* only — off /admin/* and the same-origin dashboard, which don't need permissive CORS. - Add money-path unit tests (commitCredit/refundCredit/applyTierPromotion) and webhook-dedup tests (incl. the survives-a-restart guarantee). - Fix two AGENTS.md auth-doc drifts; refresh Current state. Version 0.2.125 -> 0.2.126.
This commit is contained in:
+7
-1
@@ -17,6 +17,7 @@ import { initCredits } from "./credits.js";
|
||||
import { initAuditLog } from "./audit-log.js";
|
||||
import { initJobCredits } from "./job-credits.js";
|
||||
import { initOutputStore } from "./output-store.js";
|
||||
import { initWebhookDedup } from "./webhook-dedup.js";
|
||||
import {
|
||||
setupAdminAuthMiddleware,
|
||||
setupAdminAuthRoutes,
|
||||
@@ -49,9 +50,14 @@ await initCredits({ dataDir: DATA_DIR });
|
||||
await initJobCredits({ dataDir: DATA_DIR });
|
||||
await initAuditLog({ dataDir: DATA_DIR });
|
||||
await initOutputStore({ dataDir: DATA_DIR });
|
||||
await initWebhookDedup({ dataDir: DATA_DIR });
|
||||
|
||||
const app = express();
|
||||
app.use(cors());
|
||||
// CORS only on /relay/* — those are the cross-origin clients (the Recaps
|
||||
// app + cloud server). /admin/* and the dashboard are served same-origin
|
||||
// to the operator's browser, so they don't need (and shouldn't get) the
|
||||
// permissive Access-Control-Allow-Origin a global cors() would apply.
|
||||
app.use("/relay", cors());
|
||||
app.use(cookieParser());
|
||||
|
||||
// Admin auth must run BEFORE the admin routes register so the cookie
|
||||
|
||||
Reference in New Issue
Block a user