@flowselections/floriday-authenticatie-module 1.0.79 → 1.0.83
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 +29 -3
- package/dist-lib/components/floriday/FlorydayWebhooksCard.d.ts.map +1 -1
- package/dist-lib/components/floriday/FlorydayWebhooksCard.js +20 -2
- package/dist-lib/components/floriday/TokenHealthCard.d.ts.map +1 -1
- package/dist-lib/components/floriday/TokenHealthCard.js +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +495 -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 +14 -1
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.js +56 -28
- 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
|
@@ -9,7 +9,7 @@ import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware.js
|
|
|
9
9
|
import { FLORYDAY_CLOCK_SUPPLY_PERMISSION_MESSAGE, FLORYDAY_CONTRACT_PERMISSION_MESSAGE, FLORYDAY_SALES_STRATEGY_PERMISSION_MESSAGE, FLORYDAY_STANDARD_SCOPE, getFlorydayScopeForPath, isClockSupplyScope, isContractScope, isSalesStrategyScope, } from "./floriday-scopes.js";
|
|
10
10
|
export { validateGln } from "./floriday-gln.js";
|
|
11
11
|
import { decideGln, decidePreferredWarehouse, isOwnWarehouse, warehouseLabel } from "./floriday-own-config.js";
|
|
12
|
-
const FLORYDAY_ENDPOINTS = {
|
|
12
|
+
export const FLORYDAY_ENDPOINTS = {
|
|
13
13
|
staging: {
|
|
14
14
|
tokenUrl: "https://idm.staging.floriday.io/oauth2/ausmw6b47z1BnlHkw0h7/v1/token",
|
|
15
15
|
apiBaseUrl: "https://api.staging.floriday.io",
|
|
@@ -464,11 +464,16 @@ export async function florydayFetch(supabase, pathOrEnv, pathOrInit, maybeInit =
|
|
|
464
464
|
if (ok && flowLog?.countItems && response) {
|
|
465
465
|
try {
|
|
466
466
|
const json = await response.clone().json();
|
|
467
|
-
const
|
|
467
|
+
const all = Array.isArray(json)
|
|
468
468
|
? json
|
|
469
469
|
: Array.isArray(json?.results)
|
|
470
470
|
? json.results
|
|
471
471
|
: null;
|
|
472
|
+
// Ophaalronde: alleen items met een hoger volgnummer dan gevraagd
|
|
473
|
+
// tellen als wijziging (zelfde volgnummer = al verwerkt).
|
|
474
|
+
const items = all && typeof flowLog?.cursorFrom === "number"
|
|
475
|
+
? filterNewerThan(all, flowLog.cursorFrom)
|
|
476
|
+
: all;
|
|
472
477
|
itemCount = items ? items.length : null;
|
|
473
478
|
if (items)
|
|
474
479
|
syncItems = items;
|
|
@@ -533,7 +538,14 @@ export async function florydayFetch(supabase, pathOrEnv, pathOrInit, maybeInit =
|
|
|
533
538
|
return res;
|
|
534
539
|
}
|
|
535
540
|
}
|
|
536
|
-
|
|
541
|
+
if (res.ok) {
|
|
542
|
+
await writeLog(res.status, true, undefined, res);
|
|
543
|
+
}
|
|
544
|
+
else {
|
|
545
|
+
// Volledige foutinhoud (status, title, detail, traceId) + exacte URL in het logboek.
|
|
546
|
+
const body = await res.clone().text().catch(() => "");
|
|
547
|
+
await writeLog(res.status, false, `Floriday ${res.status} ${method} ${url}${body ? `: ${body.slice(0, 20000)}` : " (lege body)"}`, res);
|
|
548
|
+
}
|
|
537
549
|
return res;
|
|
538
550
|
}
|
|
539
551
|
// ─── Server functions exposed to the UI ─────────────────────────────────────
|
|
@@ -1333,17 +1345,26 @@ export const updateFlorydayPublishingSettings = createServerFn({ method: "POST"
|
|
|
1333
1345
|
// /<entity>/max-sequence, daarna /<entity>/sync/{seq}. Deze helper houdt
|
|
1334
1346
|
// de cursor in `floriday_sync_state` per (connection, entity).
|
|
1335
1347
|
const SEQUENCE_BATCH_CAP = 1000;
|
|
1336
|
-
//
|
|
1348
|
+
// Cursorregels (1.0.82)
|
|
1337
1349
|
// ----------------------------------------------------------------------------
|
|
1338
|
-
//
|
|
1339
|
-
//
|
|
1340
|
-
//
|
|
1341
|
-
//
|
|
1342
|
-
//
|
|
1343
|
-
|
|
1344
|
-
export
|
|
1345
|
-
|
|
1346
|
-
|
|
1350
|
+
// Swagger: "Your data is up to date when your given SequenceNumber is equal to
|
|
1351
|
+
// the received MaximumSequenceNumber." De cursor gaat daarom alleen vooruit,
|
|
1352
|
+
// naar het hoogste volgnummer dat echt is opgeslagen (nooit een marge terug).
|
|
1353
|
+
// Een later gepubliceerde wijziging krijgt altijd een hoger volgnummer en komt
|
|
1354
|
+
// zo vanzelf binnen. Items met een volgnummer <= de cursor zijn al verwerkt.
|
|
1355
|
+
/** Alleen items met een hoger volgnummer dan `cursor` (puur, testbaar). */
|
|
1356
|
+
export function filterNewerThan(items, cursor) {
|
|
1357
|
+
return items.filter((it) => {
|
|
1358
|
+
const s = Number(it?.sequenceNumber ?? it?.sequence_number);
|
|
1359
|
+
return !Number.isFinite(s) || s > cursor;
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
/** Hoogste volgnummer in een batch, minimaal `cursor`. */
|
|
1363
|
+
export function highestSequence(items, cursor) {
|
|
1364
|
+
return items.reduce((m, item) => {
|
|
1365
|
+
const s = Number(item?.sequenceNumber ?? item?.sequence_number ?? 0);
|
|
1366
|
+
return Number.isFinite(s) && s > m ? s : m;
|
|
1367
|
+
}, cursor);
|
|
1347
1368
|
}
|
|
1348
1369
|
async function fetchMaxSequence(supabase, env, maxSequenceEndpoint, entityType, caller) {
|
|
1349
1370
|
const res = await florydayFetch(supabase, env, maxSequenceEndpoint, {
|
|
@@ -1407,6 +1428,7 @@ export async function syncFloridayEntityBySequence(supabase, env, connectionId,
|
|
|
1407
1428
|
// in dat geval expliciet op maxSeq en markeren de rij als "backfill nodig".
|
|
1408
1429
|
const hasStateRow = stateRow != null;
|
|
1409
1430
|
const startCursor = stateRow?.last_sequence_number ?? 0;
|
|
1431
|
+
let savedCursor = startCursor;
|
|
1410
1432
|
try {
|
|
1411
1433
|
// Debug-logging: helpt 401's onderscheid maken tussen scope-issue,
|
|
1412
1434
|
// expired token en wrong env. Token zelf wordt NIET gelogd.
|
|
@@ -1452,30 +1474,36 @@ export async function syncFloridayEntityBySequence(supabase, env, connectionId,
|
|
|
1452
1474
|
break;
|
|
1453
1475
|
}
|
|
1454
1476
|
safety++;
|
|
1455
|
-
const
|
|
1477
|
+
const raw = await fetchBySequence(supabase, env, opts.bySequenceEndpointBase, cursor, opts.entityType, opts.caller);
|
|
1478
|
+
// Zelfde of lager volgnummer = al verwerkt: niet opnieuw doorgeven.
|
|
1479
|
+
const batch = filterNewerThan(raw, cursor);
|
|
1456
1480
|
if (batch.length === 0) {
|
|
1457
|
-
//
|
|
1458
|
-
//
|
|
1459
|
-
// veiligheidsmarge: een net gestempelde wijziging kan nog onderweg zijn.
|
|
1460
|
-
cursor = cursorWithMargin(maxSeq, startCursor);
|
|
1481
|
+
// Niets nieuws. Cursor blijft staan (niet vooruit springen naar maxSeq:
|
|
1482
|
+
// een nog niet gepubliceerde wijziging mag niet worden overgeslagen).
|
|
1461
1483
|
break;
|
|
1462
1484
|
}
|
|
1463
|
-
const highest = batch
|
|
1464
|
-
const s = Number(item?.sequenceNumber ?? item?.sequence_number ?? 0);
|
|
1465
|
-
return s > m ? s : m;
|
|
1466
|
-
}, cursor);
|
|
1485
|
+
const highest = highestSequence(batch, cursor);
|
|
1467
1486
|
await opts.onBatch(batch, highest);
|
|
1468
1487
|
totalSynced += batch.length;
|
|
1469
1488
|
if (highest <= cursor)
|
|
1470
1489
|
break;
|
|
1471
1490
|
cursor = highest;
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1491
|
+
// Na elke gelukte pagina vastleggen: mislukt een latere pagina, dan
|
|
1492
|
+
// blijft de cursor op het laatst opgeslagen item.
|
|
1493
|
+
await supabase
|
|
1494
|
+
.from("floriday_sync_state")
|
|
1495
|
+
.upsert({
|
|
1496
|
+
connection_id: connectionId,
|
|
1497
|
+
entity_type: opts.entityType,
|
|
1498
|
+
last_sequence_number: cursor,
|
|
1499
|
+
});
|
|
1500
|
+
savedCursor = cursor;
|
|
1501
|
+
// Volle pagina: in dezelfde ronde doorhalen tot maxSeq.
|
|
1502
|
+
if (raw.length < SEQUENCE_BATCH_CAP)
|
|
1476
1503
|
break;
|
|
1477
|
-
}
|
|
1478
1504
|
}
|
|
1505
|
+
if (cursor < maxSeq && safety >= 1000)
|
|
1506
|
+
partial = true;
|
|
1479
1507
|
const nowIso = new Date().toISOString();
|
|
1480
1508
|
await supabase
|
|
1481
1509
|
.from("floriday_sync_state")
|
|
@@ -1504,7 +1532,7 @@ export async function syncFloridayEntityBySequence(supabase, env, connectionId,
|
|
|
1504
1532
|
.upsert({
|
|
1505
1533
|
connection_id: connectionId,
|
|
1506
1534
|
entity_type: opts.entityType,
|
|
1507
|
-
last_sequence_number:
|
|
1535
|
+
last_sequence_number: savedCursor,
|
|
1508
1536
|
last_error: msg.slice(0, 500),
|
|
1509
1537
|
});
|
|
1510
1538
|
throw e;
|
|
@@ -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.83";
|
|
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.83";
|
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.83",
|
|
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",
|