@flowselections/floriday-authenticatie-module 1.0.79 → 1.0.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/floriday/FlorydayApiLogPage.d.ts.map +1 -1
- package/dist-lib/components/floriday/FlorydayApiLogPage.js +1 -1
- package/dist-lib/components/floriday/FlorydayWebhooksCard.d.ts.map +1 -1
- package/dist-lib/components/floriday/FlorydayWebhooksCard.js +20 -2
- package/dist-lib/integrations/supabase/types.d.ts +157 -2
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/floriday-sync.functions.d.ts +24 -192
- package/dist-lib/lib/floriday-sync.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-webhooks.functions.d.ts +37 -0
- package/dist-lib/lib/floriday-webhooks.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-webhooks.functions.js +95 -2
- package/dist-lib/lib/floriday.functions.d.ts +10 -0
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.js +9 -2
- package/dist-lib/lib/gateway/customer-stickers.d.ts +12 -8
- package/dist-lib/lib/gateway/customer-stickers.d.ts.map +1 -1
- package/dist-lib/lib/gateway/customer-stickers.js +190 -37
- package/dist-lib/lib/gateway/webhook.d.ts +17 -2
- package/dist-lib/lib/gateway/webhook.d.ts.map +1 -1
- package/dist-lib/lib/gateway/webhook.js +81 -6
- package/dist-lib/lib/module-version.d.ts +1 -1
- package/dist-lib/lib/module-version.js +1 -1
- package/package.json +2 -2
|
@@ -39,18 +39,22 @@ export interface StickerUploadRequest {
|
|
|
39
39
|
expired: boolean;
|
|
40
40
|
canRespond: boolean;
|
|
41
41
|
}
|
|
42
|
+
export declare const UPLOAD_REQUESTS_RECHECK_MS: number;
|
|
43
|
+
export type UploadRequestsProbe = "ok" | "no_sequence" | "unavailable" | "error";
|
|
44
|
+
/** Pure beslisregel op de twee HTTP-statussen (syncStatus null = niet geprobeerd). */
|
|
45
|
+
export declare function decideUploadRequestsProbe(maxSeqStatus: number, syncStatus: number | null): UploadRequestsProbe;
|
|
46
|
+
/** Pure regel: overslaan zolang de laatste "niet beschikbaar" jonger is dan 24 uur. */
|
|
47
|
+
export declare function shouldSkipUnavailable(state: {
|
|
48
|
+
status?: string;
|
|
49
|
+
checkedAt?: string;
|
|
50
|
+
} | null, now?: number): boolean;
|
|
51
|
+
export declare const uploadRequestsStateKey: (connectionId: string, env: FlorydayEnvironment) => string;
|
|
42
52
|
/** Inhaalronde op de sequence-sync; ook gebruikt door de sticker-cron. */
|
|
43
53
|
export declare function syncStickerUploadRequests(supabase: SupabaseClient, env: FlorydayEnvironment, connectionId: string, opts?: {
|
|
44
54
|
maxBatches?: number;
|
|
45
55
|
deadlineMs?: number;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
fromSequence: number;
|
|
49
|
-
toSequence: number;
|
|
50
|
-
durationMs: number;
|
|
51
|
-
backfillNeeded?: boolean;
|
|
52
|
-
partial?: boolean;
|
|
53
|
-
}>;
|
|
56
|
+
force?: boolean;
|
|
57
|
+
}): Promise<Record<string, unknown>>;
|
|
54
58
|
/** Pure regel: alleen PENDING en niet verlopen mag beantwoord worden. */
|
|
55
59
|
export declare function canRespondToUploadRequest(status: string | null, expiresOn: string | null, now?: number): {
|
|
56
60
|
expired: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"customer-stickers.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/customer-stickers.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAML,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAQlC,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IACjD,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG,CAAC,CAAC,GAClB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAWlH;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE;IAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAAC,WAAW,EAAE,mBAAmB,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACjF,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAkC3H;AAMD,MAAM,WAAW,oBAAoB;IACnC,8BAA8B,EAAE,MAAM,CAAC;IACvC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,sBAAsB,EAAE,OAAO,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,0EAA0E;AAC1E,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,cAAc,EACxB,GAAG,EAAE,mBAAmB,EACxB,YAAY,EAAE,MAAM,EACpB,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO
|
|
1
|
+
{"version":3,"file":"customer-stickers.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/customer-stickers.ts"],"names":[],"mappings":"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"}
|
|
@@ -61,46 +61,199 @@ export async function runCustomerStickersHandled(supabase, input) {
|
|
|
61
61
|
}
|
|
62
62
|
return { ok: true, stickerIds: ids };
|
|
63
63
|
}
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Beschikbaarheid van de upload-requests-endpoints.
|
|
66
|
+
// De swagger kent op current-max-sequence geen 404 (alleen 401/403/423/5xx),
|
|
67
|
+
// dus een 404 is geen bewijs dat het endpoint ontbreekt. Volgorde:
|
|
68
|
+
// 1. current-max-sequence 200 → normale sync.
|
|
69
|
+
// 2. 404 → probeer sync/0?limitResult=1. 200 → sync vanaf 0 ("geen
|
|
70
|
+
// volgnummer bekend"), geen fout.
|
|
71
|
+
// 3. beide 404 → "niet beschikbaar in deze omgeving" met foutinhoud; pas na
|
|
72
|
+
// 24 uur opnieuw proberen.
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
export const UPLOAD_REQUESTS_RECHECK_MS = 24 * 3600000;
|
|
75
|
+
/** Pure beslisregel op de twee HTTP-statussen (syncStatus null = niet geprobeerd). */
|
|
76
|
+
export function decideUploadRequestsProbe(maxSeqStatus, syncStatus) {
|
|
77
|
+
if (maxSeqStatus >= 200 && maxSeqStatus < 300)
|
|
78
|
+
return "ok";
|
|
79
|
+
if (maxSeqStatus !== 404)
|
|
80
|
+
return "error";
|
|
81
|
+
if (syncStatus === null)
|
|
82
|
+
return "error";
|
|
83
|
+
if (syncStatus >= 200 && syncStatus < 300)
|
|
84
|
+
return "no_sequence";
|
|
85
|
+
if (syncStatus === 404)
|
|
86
|
+
return "unavailable";
|
|
87
|
+
return "error";
|
|
88
|
+
}
|
|
89
|
+
/** Pure regel: overslaan zolang de laatste "niet beschikbaar" jonger is dan 24 uur. */
|
|
90
|
+
export function shouldSkipUnavailable(state, now = Date.now()) {
|
|
91
|
+
if (!state || state.status !== "unavailable" || !state.checkedAt)
|
|
92
|
+
return false;
|
|
93
|
+
return now - Date.parse(state.checkedAt) < UPLOAD_REQUESTS_RECHECK_MS;
|
|
94
|
+
}
|
|
95
|
+
export const uploadRequestsStateKey = (connectionId, env) => `floriday_upload_requests_availability:${env}:${connectionId}`;
|
|
96
|
+
async function readAvailability(supabase, key) {
|
|
97
|
+
const { data } = await supabase.from("module_runtime_config").select("value").eq("key", key).maybeSingle();
|
|
98
|
+
try {
|
|
99
|
+
return data?.value ? JSON.parse(data.value) : null;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function writeAvailability(supabase, key, value) {
|
|
106
|
+
await supabase.from("module_runtime_config")
|
|
107
|
+
.upsert({ key, value: JSON.stringify(value), updated_at: new Date().toISOString() }, { onConflict: "key" });
|
|
108
|
+
}
|
|
109
|
+
async function describe(res, url) {
|
|
110
|
+
const text = await res.text().catch(() => "");
|
|
111
|
+
let j = null;
|
|
112
|
+
try {
|
|
113
|
+
j = JSON.parse(text);
|
|
114
|
+
}
|
|
115
|
+
catch { /* geen JSON */ }
|
|
116
|
+
let host = null;
|
|
117
|
+
try {
|
|
118
|
+
host = new URL(url).host;
|
|
119
|
+
}
|
|
120
|
+
catch { /* relatief */ }
|
|
121
|
+
return {
|
|
122
|
+
status: res.status,
|
|
123
|
+
title: j?.title ?? null,
|
|
124
|
+
detail: j?.detail ?? null,
|
|
125
|
+
traceId: j?.traceId ?? j?.traceid ?? res.headers.get("traceparent") ?? null,
|
|
126
|
+
url,
|
|
127
|
+
host,
|
|
128
|
+
body: text.slice(0, 4000),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
64
131
|
/** Inhaalronde op de sequence-sync; ook gebruikt door de sticker-cron. */
|
|
65
132
|
export async function syncStickerUploadRequests(supabase, env, connectionId, opts = {}) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
133
|
+
const key = uploadRequestsStateKey(connectionId, env);
|
|
134
|
+
const state = await readAvailability(supabase, key);
|
|
135
|
+
if (!opts.force && shouldSkipUnavailable(state)) {
|
|
136
|
+
return { skipped: "unavailable", since: state.since ?? state.checkedAt, nextCheckAt: new Date(Date.parse(state.checkedAt) + UPLOAD_REQUESTS_RECHECK_MS).toISOString() };
|
|
137
|
+
}
|
|
138
|
+
const { FLORYDAY_ENDPOINTS } = await import("../floriday.functions.js");
|
|
139
|
+
const base = FLORYDAY_ENDPOINTS[env].apiBaseUrl;
|
|
140
|
+
const maxPath = `/${SUPPLIERS_API_VERSION}/customer-sticker-upload-requests/current-max-sequence`;
|
|
141
|
+
const syncBase = `/${SUPPLIERS_API_VERSION}/customer-sticker-upload-requests/sync`;
|
|
142
|
+
const maxRes = await florydayFetch(supabase, env, maxPath, {
|
|
143
|
+
cache: "no-store",
|
|
144
|
+
flowLog: { module: "floriday-sync", caller: "customer-sticker-upload-requests", action: "controle:customer-sticker-upload-requests", kind: "poll-check", countItems: false },
|
|
145
|
+
});
|
|
146
|
+
let maxSeqDetail = null;
|
|
147
|
+
let syncDetail = null;
|
|
148
|
+
let probeStatus = null;
|
|
149
|
+
if (!maxRes.ok) {
|
|
150
|
+
maxSeqDetail = await describe(maxRes, base + maxPath);
|
|
151
|
+
if (maxRes.status === 404) {
|
|
152
|
+
const probeRes = await florydayFetch(supabase, env, `${syncBase}/0?limitResult=1`, {
|
|
153
|
+
cache: "no-store",
|
|
154
|
+
flowLog: { module: "floriday-sync", caller: "customer-sticker-upload-requests", action: "proef:customer-sticker-upload-requests sync/0", kind: "poll-check", countItems: false },
|
|
155
|
+
});
|
|
156
|
+
probeStatus = probeRes.status;
|
|
157
|
+
if (!probeRes.ok)
|
|
158
|
+
syncDetail = await describe(probeRes, `${base}${syncBase}/0?limitResult=1`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const decision = decideUploadRequestsProbe(maxRes.status, probeStatus);
|
|
162
|
+
const nowIso = new Date().toISOString();
|
|
163
|
+
if (decision === "unavailable") {
|
|
164
|
+
const first = state?.status !== "unavailable";
|
|
165
|
+
await writeAvailability(supabase, key, { status: "unavailable", since: first ? nowIso : state.since, checkedAt: nowIso, maxSequence: maxSeqDetail, sync0: syncDetail });
|
|
166
|
+
if (first) {
|
|
167
|
+
const { logFlorydayCall } = await import("../floriday-api-log.server.js");
|
|
168
|
+
await logFlorydayCall({
|
|
169
|
+
direction: "inbound", environment: env, connectionId, method: "GET", path: maxPath, httpStatus: 404, ok: false,
|
|
170
|
+
durationMs: 0, module: "floriday-sync", caller: "customer-sticker-upload-requests",
|
|
171
|
+
action: "niet beschikbaar:customer-sticker-upload-requests", kind: "status",
|
|
172
|
+
error: `Stickeraanvragen niet beschikbaar in deze omgeving (beide routes 404). Volgende controle over 24 uur. Foutinhoud: ${JSON.stringify({ maxSequence: maxSeqDetail, sync0: syncDetail })}`,
|
|
173
|
+
}).catch(() => undefined);
|
|
174
|
+
}
|
|
175
|
+
return { skipped: "unavailable", maxSequence: maxSeqDetail, sync0: syncDetail };
|
|
176
|
+
}
|
|
177
|
+
if (decision === "error") {
|
|
178
|
+
throw new Error(`Stickeraanvragen: Floriday ${maxRes.status}${maxSeqDetail ? ` ${JSON.stringify(maxSeqDetail)}` : ""}`);
|
|
179
|
+
}
|
|
180
|
+
if (state?.status === "unavailable" || state?.status === "no_sequence" || decision === "no_sequence") {
|
|
181
|
+
await writeAvailability(supabase, key, { status: decision, checkedAt: nowIso, ...(decision === "no_sequence" ? { maxSequence: maxSeqDetail } : {}) });
|
|
182
|
+
}
|
|
183
|
+
const onBatch = async (items) => {
|
|
184
|
+
const stickerIds = Array.from(new Set(items.map((i) => i?.customerStickerId).filter(Boolean).map(String)));
|
|
185
|
+
const soByS = new Map();
|
|
186
|
+
if (stickerIds.length) {
|
|
187
|
+
const { data } = await supabase.from("floriday_customer_stickers_cache")
|
|
188
|
+
.select("floriday_id, sales_order_id").eq("connection_id", connectionId).in("floriday_id", stickerIds);
|
|
189
|
+
for (const r of (data ?? []))
|
|
190
|
+
soByS.set(String(r.floriday_id), r.sales_order_id ?? null);
|
|
191
|
+
}
|
|
192
|
+
const rows = items
|
|
193
|
+
.filter((i) => i?.customerStickerUploadRequestId)
|
|
194
|
+
.map((i) => ({
|
|
195
|
+
connection_id: connectionId,
|
|
196
|
+
floriday_id: String(i.customerStickerUploadRequestId),
|
|
197
|
+
customer_sticker_id: i.customerStickerId ?? null,
|
|
198
|
+
// Orderkoppeling via de sticker (op id); het schema heeft geen salesOrderId op de aanvraag.
|
|
199
|
+
sales_order_id: i.customerStickerId ? soByS.get(String(i.customerStickerId)) ?? null : null,
|
|
200
|
+
status: i.status ?? null,
|
|
201
|
+
expires_on: i.expiresOn ?? null,
|
|
202
|
+
responded_on: i.respondedOn ?? null,
|
|
203
|
+
sequence_number: Number(i.sequenceNumber ?? 0),
|
|
204
|
+
data: i,
|
|
205
|
+
fetched_at: new Date().toISOString(),
|
|
206
|
+
}));
|
|
207
|
+
if (rows.length) {
|
|
208
|
+
const { error } = await supabase.from("floriday_customer_sticker_upload_requests_cache")
|
|
209
|
+
.upsert(rows, { onConflict: "connection_id,floriday_id" });
|
|
210
|
+
if (error)
|
|
211
|
+
throw new Error(`Stickeraanvragen opslaan: ${error.message}`);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
if (decision === "ok") {
|
|
215
|
+
return syncFloridayEntityBySequence(supabase, env, connectionId, {
|
|
216
|
+
entityType: "customer-sticker-upload-requests",
|
|
217
|
+
maxSequenceEndpoint: maxPath,
|
|
218
|
+
bySequenceEndpointBase: syncBase,
|
|
219
|
+
maxBatches: opts.maxBatches,
|
|
220
|
+
deadlineMs: opts.deadlineMs,
|
|
221
|
+
onBatch,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
// no_sequence: zelf pagineren vanaf de eigen cursor (start 0), tot een lege of kleine pagina.
|
|
225
|
+
const { data: st } = await supabase.from("floriday_sync_state").select("last_sequence_number")
|
|
226
|
+
.eq("connection_id", connectionId).eq("entity_type", "customer-sticker-upload-requests").maybeSingle();
|
|
227
|
+
let cursor = Number(st?.last_sequence_number ?? 0);
|
|
228
|
+
const from = cursor;
|
|
229
|
+
let synced = 0;
|
|
230
|
+
for (let i = 0; i < (opts.maxBatches ?? 20); i++) {
|
|
231
|
+
if (opts.deadlineMs && Date.now() >= opts.deadlineMs)
|
|
232
|
+
break;
|
|
233
|
+
const r = await florydayFetch(supabase, env, `${syncBase}/${cursor}`, {
|
|
234
|
+
cache: "no-store",
|
|
235
|
+
flowLog: { module: "floriday-sync", caller: "customer-sticker-upload-requests", action: "ophalen:customer-sticker-upload-requests", kind: "poll-fetch", cursorFrom: cursor, countItems: true },
|
|
236
|
+
});
|
|
237
|
+
if (!r.ok)
|
|
238
|
+
throw new Error(`Stickeraanvragen sync/${cursor}: Floriday ${r.status}`);
|
|
239
|
+
const j = await r.json().catch(() => null);
|
|
240
|
+
const items = Array.isArray(j) ? j : Array.isArray(j?.results) ? j.results : [];
|
|
241
|
+
if (items.length === 0)
|
|
242
|
+
break;
|
|
243
|
+
await onBatch(items);
|
|
244
|
+
synced += items.length;
|
|
245
|
+
const highest = items.reduce((m, it) => Math.max(m, Number(it?.sequenceNumber ?? 0)), cursor);
|
|
246
|
+
if (highest <= cursor)
|
|
247
|
+
break;
|
|
248
|
+
cursor = highest;
|
|
249
|
+
if (items.length < 1000)
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
await supabase.from("floriday_sync_state").upsert({
|
|
253
|
+
connection_id: connectionId, entity_type: "customer-sticker-upload-requests", last_sequence_number: cursor,
|
|
254
|
+
last_synced_at: nowIso, last_success_at: nowIso, last_items_processed: synced, last_error: null,
|
|
103
255
|
});
|
|
256
|
+
return { synced, fromSequence: from, toSequence: cursor, mode: "no_sequence" };
|
|
104
257
|
}
|
|
105
258
|
/** Pure regel: alleen PENDING en niet verlopen mag beantwoord worden. */
|
|
106
259
|
export function canRespondToUploadRequest(status, expiresOn, now = Date.now()) {
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
2
|
export declare const WEBHOOK_SUBSCRIPTIONS_TABLE = "floriday_webhook_subscriptions";
|
|
3
3
|
export declare function sha256HexString(s: string): Promise<string>;
|
|
4
|
+
/** Adres zoals de afzender het aanriep (achter een proxy via x-forwarded-host/proto). */
|
|
5
|
+
export declare function originalRequestUrl(request: Request): string;
|
|
4
6
|
export interface WebhookResult {
|
|
5
7
|
status: number;
|
|
6
8
|
payload: unknown;
|
|
7
9
|
}
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
+
export interface WebhookCallContext {
|
|
11
|
+
/** Volledig adres waarop de melding binnenkwam (incl. token). */
|
|
12
|
+
callbackUrl?: string | null;
|
|
13
|
+
route?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Token in een callback-adres maskeren: eerste 4 en laatste 4 tekens. */
|
|
16
|
+
export declare function maskCallbackUrl(url: string): string;
|
|
17
|
+
export declare const STALE_CALLBACK_PREFIX = "floriday_webhook_stale_callback:";
|
|
18
|
+
/**
|
|
19
|
+
* Onbekend token: het volledige adres bewaren in module_runtime_config
|
|
20
|
+
* (alleen service-role leesbaar), met teller en eerste/laatste moment.
|
|
21
|
+
*/
|
|
22
|
+
export declare function recordStaleCallback(supabase: SupabaseClient, url: string, route: string): Promise<string>;
|
|
23
|
+
export declare function runFloridayWebhook(supabase: SupabaseClient, token: string, rawBody: string, ctx?: WebhookCallContext): Promise<WebhookResult>;
|
|
24
|
+
export declare function handleFloridayWebhook(request: Request, supabase: SupabaseClient, token: string, route?: string): Promise<Response>;
|
|
10
25
|
/**
|
|
11
26
|
* Shell-geschikte variant zonder padparameter: POST
|
|
12
27
|
* /api/public/gateway/webhook-in?t=<token>. Zelfde verificatie (token-hash,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/webhook.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAW5D,eAAO,MAAM,2BAA2B,mCAAmC,CAAC;AAE5E,wBAAsB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGhE;AA4CD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/webhook.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAW5D,eAAO,MAAM,2BAA2B,mCAAmC,CAAC;AAE5E,wBAAsB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGhE;AA4CD,yFAAyF;AACzF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAO3D;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKnD;AAED,eAAO,MAAM,qBAAqB,qCAAqC,CAAC;AAExE;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoB/G;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,kBAAuB,GAC3B,OAAO,CAAC,aAAa,CAAC,CA6KxB;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,MAAM,EACb,KAAK,SAAoB,GACxB,OAAO,CAAC,QAAQ,CAAC,CAcnB;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,CAAC,CAGnB;AAKD,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAoBhG;AAED,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,CAAC,CAWnB"}
|
|
@@ -53,9 +53,64 @@ async function logRejected(supabase, reason, connectionId, payload) {
|
|
|
53
53
|
/* loggen mag de respons niet breken */
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
/** Adres zoals de afzender het aanriep (achter een proxy via x-forwarded-host/proto). */
|
|
57
|
+
export function originalRequestUrl(request) {
|
|
58
|
+
const u = new URL(request.url);
|
|
59
|
+
const host = request.headers.get("x-forwarded-host")?.split(",")[0]?.trim();
|
|
60
|
+
const proto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim();
|
|
61
|
+
if (host)
|
|
62
|
+
u.host = host;
|
|
63
|
+
if (proto === "https" || proto === "http")
|
|
64
|
+
u.protocol = proto + ":";
|
|
65
|
+
return u.toString();
|
|
66
|
+
}
|
|
67
|
+
/** Token in een callback-adres maskeren: eerste 4 en laatste 4 tekens. */
|
|
68
|
+
export function maskCallbackUrl(url) {
|
|
69
|
+
const mask = (t) => (t.length <= 8 ? "****" : `${t.slice(0, 4)}…${t.slice(-4)}`);
|
|
70
|
+
return url
|
|
71
|
+
.replace(/([?&]t=)([^&#]+)/, (_m, a, t) => a + mask(t))
|
|
72
|
+
.replace(/(\/webhook\/)([^/?#]+)/, (_m, a, t) => a + mask(t));
|
|
73
|
+
}
|
|
74
|
+
export const STALE_CALLBACK_PREFIX = "floriday_webhook_stale_callback:";
|
|
75
|
+
/**
|
|
76
|
+
* Onbekend token: het volledige adres bewaren in module_runtime_config
|
|
77
|
+
* (alleen service-role leesbaar), met teller en eerste/laatste moment.
|
|
78
|
+
*/
|
|
79
|
+
export async function recordStaleCallback(supabase, url, route) {
|
|
80
|
+
const keyHash = (await sha256HexString(url)).slice(0, 32);
|
|
81
|
+
const key = STALE_CALLBACK_PREFIX + keyHash;
|
|
82
|
+
try {
|
|
83
|
+
const { data } = await supabase.from("module_runtime_config").select("value").eq("key", key).maybeSingle();
|
|
84
|
+
let prev = null;
|
|
85
|
+
try {
|
|
86
|
+
prev = data?.value ? JSON.parse(data.value) : null;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
prev = null;
|
|
90
|
+
}
|
|
91
|
+
const now = new Date().toISOString();
|
|
92
|
+
const value = {
|
|
93
|
+
callbackUrl: url,
|
|
94
|
+
route,
|
|
95
|
+
count: (prev?.count ?? 0) + 1,
|
|
96
|
+
firstAt: prev?.firstAt ?? now,
|
|
97
|
+
lastAt: now,
|
|
98
|
+
unsubscribe: prev?.unsubscribe ?? null,
|
|
99
|
+
};
|
|
100
|
+
await supabase.from("module_runtime_config")
|
|
101
|
+
.upsert({ key, value: JSON.stringify(value), updated_at: now }, { onConflict: "key" });
|
|
102
|
+
}
|
|
103
|
+
catch { /* vastleggen mag de respons niet breken */ }
|
|
104
|
+
return keyHash;
|
|
105
|
+
}
|
|
106
|
+
export async function runFloridayWebhook(supabase, token, rawBody, ctx = {}) {
|
|
107
|
+
const route = ctx.route ?? "webhook";
|
|
108
|
+
if (!token || token.length < 32) {
|
|
109
|
+
await logRejected(supabase, "Ongeldig of ontbrekend callback-token", null, {
|
|
110
|
+
route, callbackUrlMasked: ctx.callbackUrl ? maskCallbackUrl(ctx.callbackUrl) : null,
|
|
111
|
+
});
|
|
58
112
|
return { status: 404, payload: { ok: false } };
|
|
113
|
+
}
|
|
59
114
|
const tokenHash = await sha256HexString(token);
|
|
60
115
|
const { data: sub } = await supabase
|
|
61
116
|
.from(WEBHOOK_SUBSCRIPTIONS_TABLE)
|
|
@@ -63,7 +118,24 @@ export async function runFloridayWebhook(supabase, token, rawBody) {
|
|
|
63
118
|
.eq("callback_token_hash", tokenHash)
|
|
64
119
|
.maybeSingle();
|
|
65
120
|
if (!sub) {
|
|
66
|
-
|
|
121
|
+
let parsed = null;
|
|
122
|
+
try {
|
|
123
|
+
parsed = rawBody ? JSON.parse(rawBody) : null;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
parsed = null;
|
|
127
|
+
}
|
|
128
|
+
const callbackKey = ctx.callbackUrl ? await recordStaleCallback(supabase, ctx.callbackUrl, route) : null;
|
|
129
|
+
await logRejected(supabase, "Onbekend callback-token", null, {
|
|
130
|
+
route,
|
|
131
|
+
reason: "Onbekend callback-token",
|
|
132
|
+
callbackUrlMasked: ctx.callbackUrl ? maskCallbackUrl(ctx.callbackUrl) : null,
|
|
133
|
+
callbackKey,
|
|
134
|
+
messageType: pick(parsed, "Type") ?? null,
|
|
135
|
+
topicArn: pick(parsed, "TopicArn") ?? null,
|
|
136
|
+
aggregateType: pick(parsed, "aggregateType", "AggregateType") ?? null,
|
|
137
|
+
eventType: pick(parsed, "eventType", "EventType") ?? null,
|
|
138
|
+
});
|
|
67
139
|
return { status: 404, payload: { ok: false } };
|
|
68
140
|
}
|
|
69
141
|
let body;
|
|
@@ -196,14 +268,17 @@ export async function runFloridayWebhook(supabase, token, rawBody) {
|
|
|
196
268
|
// Altijd 200 na vastleggen.
|
|
197
269
|
return { status: 200, payload: { ok: true, ...summary } };
|
|
198
270
|
}
|
|
199
|
-
export async function handleFloridayWebhook(request, supabase, token) {
|
|
271
|
+
export async function handleFloridayWebhook(request, supabase, token, route = "webhook/<token>") {
|
|
200
272
|
if (request.method !== "POST")
|
|
201
273
|
return new Response("Method Not Allowed", { status: 405 });
|
|
202
274
|
try {
|
|
203
275
|
const raw = await request.text();
|
|
204
276
|
if (raw.length > 256000)
|
|
205
277
|
return new Response("Payload Too Large", { status: 413 });
|
|
206
|
-
const { status, payload } = await runFloridayWebhook(supabase, token, raw
|
|
278
|
+
const { status, payload } = await runFloridayWebhook(supabase, token, raw, {
|
|
279
|
+
callbackUrl: originalRequestUrl(request),
|
|
280
|
+
route,
|
|
281
|
+
});
|
|
207
282
|
return Response.json(payload, { status });
|
|
208
283
|
}
|
|
209
284
|
catch (e) {
|
|
@@ -218,7 +293,7 @@ export async function handleFloridayWebhook(request, supabase, token) {
|
|
|
218
293
|
*/
|
|
219
294
|
export async function handleFloridayWebhookIn(request, supabase) {
|
|
220
295
|
const token = new URL(request.url).searchParams.get("t") ?? "";
|
|
221
|
-
return handleFloridayWebhook(request, supabase, token);
|
|
296
|
+
return handleFloridayWebhook(request, supabase, token, "webhook-in");
|
|
222
297
|
}
|
|
223
298
|
// ---------------------------------------------------------------------------
|
|
224
299
|
// Herhaalronde voor mislukte events (cron, elke minuut).
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const MODULE_VERSION = "1.0.
|
|
1
|
+
export declare const MODULE_VERSION = "1.0.81";
|
|
2
2
|
//# sourceMappingURL=module-version.d.ts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// GEGENEREERD door scripts/write-module-version.mjs uit package.json — niet handmatig wijzigen.
|
|
2
|
-
export const MODULE_VERSION = "1.0.
|
|
2
|
+
export const MODULE_VERSION = "1.0.81";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowselections/floriday-authenticatie-module",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.81",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist-lib/index.js",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"@cloudflare/vite-plugin": "^1.25.5",
|
|
69
69
|
"@rsbuild/core": "^1.0.0",
|
|
70
70
|
"@tailwindcss/vite": "^4.2.1",
|
|
71
|
-
"@tanstack/router-plugin": "
|
|
71
|
+
"@tanstack/router-plugin": "1.167.33",
|
|
72
72
|
"tailwindcss": "^4.2.1",
|
|
73
73
|
"tw-animate-css": "^1.3.4",
|
|
74
74
|
"vite-tsconfig-paths": "^6.0.2",
|