@flowselections/floriday-authenticatie-module 1.0.16 → 1.0.18
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/FlorydayConnectionPage.d.ts.map +1 -1
- package/dist-lib/components/floriday/FlorydayConnectionPage.js +2 -1
- package/dist-lib/components/settings/FloricodeSettingsCard.d.ts +2 -0
- package/dist-lib/components/settings/FloricodeSettingsCard.d.ts.map +1 -0
- package/dist-lib/components/settings/FloricodeSettingsCard.js +235 -0
- package/dist-lib/components/settings/FlorydaySettingsCard.js +1 -1
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +2 -0
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +484 -3
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +484 -3
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +484 -3
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +492 -3
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/floricode-credentials.server.d.ts +21 -0
- package/dist-lib/lib/floricode-credentials.server.d.ts.map +1 -0
- package/dist-lib/lib/floricode-credentials.server.js +158 -0
- package/dist-lib/lib/floricode-product-features.d.ts.map +1 -1
- package/dist-lib/lib/floricode-product-features.js +19 -33
- package/dist-lib/lib/floricode-regulatory-features.d.ts.map +1 -1
- package/dist-lib/lib/floricode-regulatory-features.js +51 -41
- package/dist-lib/lib/floricode-sync.server.d.ts +35 -0
- package/dist-lib/lib/floricode-sync.server.d.ts.map +1 -0
- package/dist-lib/lib/floricode-sync.server.js +211 -0
- package/dist-lib/lib/floricode.functions.d.ts +78555 -0
- package/dist-lib/lib/floricode.functions.d.ts.map +1 -0
- package/dist-lib/lib/floricode.functions.js +178 -0
- package/dist-lib/lib/floriday-auction-letters.functions.d.ts +484 -3
- package/dist-lib/lib/floriday-auction-letters.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-auto-process.server.d.ts +34 -0
- package/dist-lib/lib/floriday-auto-process.server.d.ts.map +1 -0
- package/dist-lib/lib/floriday-auto-process.server.js +365 -0
- package/dist-lib/lib/floriday-fulfillment-lookup.server.d.ts +19 -0
- package/dist-lib/lib/floriday-fulfillment-lookup.server.d.ts.map +1 -0
- package/dist-lib/lib/floriday-fulfillment-lookup.server.js +130 -0
- package/dist-lib/lib/floriday-gateway-auth.d.ts +24 -0
- package/dist-lib/lib/floriday-gateway-auth.d.ts.map +1 -0
- package/dist-lib/lib/floriday-gateway-auth.js +127 -0
- package/dist-lib/lib/floriday-labels.functions.d.ts +484 -3
- package/dist-lib/lib/floriday-labels.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-labels.functions.js +3 -2
- package/dist-lib/lib/floriday-public.functions.d.ts +2904 -18
- package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-rate-limit.server.d.ts +21 -0
- package/dist-lib/lib/floriday-rate-limit.server.d.ts.map +1 -0
- package/dist-lib/lib/floriday-rate-limit.server.js +75 -0
- package/dist-lib/lib/floriday-stickers.functions.d.ts +484 -3
- package/dist-lib/lib/floriday-stickers.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-sync.functions.d.ts +15972 -99
- package/dist-lib/lib/floriday-sync.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.d.ts +2952 -42
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.js +29 -4
- package/dist-lib/lib/floriday.functions.d.ts +9054 -198
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.js +39 -4
- package/dist-lib/lib/planning-helpers.d.ts +30 -0
- package/dist-lib/lib/planning-helpers.d.ts.map +1 -0
- package/dist-lib/lib/planning-helpers.js +62 -0
- package/package.json +5 -3
- package/public/flowselections-assets/floriday-authenticatie/floricode-logo.png +0 -0
- package/public/flowselections-assets/floriday-authenticatie/floriday-logo.png +0 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Auto-fulfillment state-machine voor Floriday sales orders (2026v1).
|
|
3
|
+
// ----------------------------------------------------------------------------
|
|
4
|
+
// Belangrijkste verschil met de vorige versie: deze module doet GEEN eigen
|
|
5
|
+
// `acknowledge` of `create-delivery-order` meer. Beide worden door Floriday
|
|
6
|
+
// zelf afgehandeld nadat de klant de order committeert (na
|
|
7
|
+
// `cancellationDeadline`). Deze module is een pure observer die één actie
|
|
8
|
+
// uitvoert: het versturen van de fulfillment-order zodra alles klaarstaat.
|
|
9
|
+
//
|
|
10
|
+
// Statussen:
|
|
11
|
+
// waiting_for_commit — cancellationDeadline nog niet verstreken
|
|
12
|
+
// waiting_for_delivery_order — commit rond, maar Floriday heeft nog geen
|
|
13
|
+
// delivery-order gepushed (wachten op sync)
|
|
14
|
+
// waiting — timing (autoProcessMoment) nog niet bereikt
|
|
15
|
+
// sending_fulfillment — POST /fulfillment-orders bezig
|
|
16
|
+
// done — fulfillment-order verzonden
|
|
17
|
+
// error — onherstelbaar
|
|
18
|
+
// ============================================================================
|
|
19
|
+
import { florydayWrite, getActiveFlorydayCredentials, getActiveFlorydayEnvironment, } from "./floriday.functions";
|
|
20
|
+
import { computeAutoProcessDate, isPickdayToday } from "./planning-helpers";
|
|
21
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
22
|
+
async function readSalesOrderFromCache(supabase, connectionId, salesOrderId) {
|
|
23
|
+
const { data } = await supabase
|
|
24
|
+
.from("floriday_salesorders_cache")
|
|
25
|
+
.select("floriday_id, data")
|
|
26
|
+
.eq("connection_id", connectionId)
|
|
27
|
+
.eq("floriday_id", salesOrderId)
|
|
28
|
+
.maybeSingle();
|
|
29
|
+
return data;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Zoek de delivery-order in cache waarvan één van de fulfillmentRequests naar
|
|
33
|
+
* deze sales-order verwijst. Retourneert zowel de delivery-order als de
|
|
34
|
+
* matchende fulfillmentRequest(s).
|
|
35
|
+
*/
|
|
36
|
+
async function findDeliveryOrderForSalesOrder(supabase, connectionId, salesOrderId) {
|
|
37
|
+
// Floriday bewaart de link in data.fulfillmentRequests[].salesOrderId.
|
|
38
|
+
// We gebruiken jsonb containment via .contains() zodat Postgres een GIN
|
|
39
|
+
// index kan raken; geen full-table scan.
|
|
40
|
+
const { data, error } = await supabase
|
|
41
|
+
.from("floriday_delivery_orders_cache")
|
|
42
|
+
.select("floriday_id, data")
|
|
43
|
+
.eq("connection_id", connectionId)
|
|
44
|
+
.contains("data", {
|
|
45
|
+
fulfillmentRequests: [{ salesOrderId }],
|
|
46
|
+
})
|
|
47
|
+
.limit(1);
|
|
48
|
+
if (error || !data || data.length === 0)
|
|
49
|
+
return null;
|
|
50
|
+
const row = data[0];
|
|
51
|
+
const requests = Array.isArray(row.data?.fulfillmentRequests)
|
|
52
|
+
? row.data.fulfillmentRequests.filter((r) => r?.salesOrderId === salesOrderId)
|
|
53
|
+
: [];
|
|
54
|
+
return {
|
|
55
|
+
deliveryOrderId: row.floriday_id,
|
|
56
|
+
data: row.data,
|
|
57
|
+
fulfillmentRequests: requests,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function hasFulfillmentOrder(supabase, connectionId, deliveryOrderId) {
|
|
61
|
+
const { data } = await supabase
|
|
62
|
+
.from("floriday_fulfillment_orders_cache")
|
|
63
|
+
.select("floriday_id")
|
|
64
|
+
.eq("connection_id", connectionId)
|
|
65
|
+
.eq("delivery_order_id", deliveryOrderId)
|
|
66
|
+
.maybeSingle();
|
|
67
|
+
return data?.floriday_id ?? null;
|
|
68
|
+
}
|
|
69
|
+
async function loadAfhandelingSettings(supabase) {
|
|
70
|
+
const { data } = await supabase
|
|
71
|
+
.from("afhandeling_settings")
|
|
72
|
+
.select("auto_process_paused, pick_weekdays, max_workdays_earlier_pick, extra_pick_days_with_stickers")
|
|
73
|
+
.order("updated_at", { ascending: false })
|
|
74
|
+
.limit(1)
|
|
75
|
+
.maybeSingle();
|
|
76
|
+
return (data ?? {
|
|
77
|
+
auto_process_paused: false,
|
|
78
|
+
pick_weekdays: [1, 2, 3, 4, 5],
|
|
79
|
+
max_workdays_earlier_pick: 1,
|
|
80
|
+
extra_pick_days_with_stickers: 1,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
async function logPhase(supabase, connectionId, salesOrderId, status, extra = {}) {
|
|
84
|
+
await supabase.rpc("upsert_auto_fulfillment_log", {
|
|
85
|
+
p_connection_id: connectionId,
|
|
86
|
+
p_sales_order_id: salesOrderId,
|
|
87
|
+
p_status: status,
|
|
88
|
+
p_reason: extra.reason ?? null,
|
|
89
|
+
p_error: extra.error ?? null,
|
|
90
|
+
p_fulfillment_order_id: extra.fulfillmentOrderId ?? null,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Haal `numberOfPackages` uit een fulfillmentRequest, met een reeks
|
|
95
|
+
* fallbacks voor licht afwijkende schema-varianten.
|
|
96
|
+
*/
|
|
97
|
+
function extractPackageCount(req) {
|
|
98
|
+
const candidates = [
|
|
99
|
+
req?.numberOfPackages,
|
|
100
|
+
req?.packageCount,
|
|
101
|
+
req?.numberOfLoadCarriers,
|
|
102
|
+
req?.quantity,
|
|
103
|
+
];
|
|
104
|
+
for (const c of candidates) {
|
|
105
|
+
const n = Number(c);
|
|
106
|
+
if (Number.isFinite(n) && n > 0)
|
|
107
|
+
return n;
|
|
108
|
+
}
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
// ─── Publieke API ───────────────────────────────────────────────────────────
|
|
112
|
+
/**
|
|
113
|
+
* Voert de state-machine één stap uit voor een enkele sales-order. Idempotent
|
|
114
|
+
* en veilig te herhalen; alleen `sending_fulfillment` doet een write naar
|
|
115
|
+
* Floriday.
|
|
116
|
+
*/
|
|
117
|
+
export async function runAutoProcess(supabase, input) {
|
|
118
|
+
const salesOrderId = input.salesOrderId.trim();
|
|
119
|
+
if (!salesOrderId) {
|
|
120
|
+
return {
|
|
121
|
+
ok: false,
|
|
122
|
+
status: "error",
|
|
123
|
+
salesOrderId,
|
|
124
|
+
error: "salesOrderId is verplicht",
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const env = input.environment ?? (await getActiveFlorydayEnvironment(supabase));
|
|
128
|
+
const conn = await getActiveFlorydayCredentials(supabase, env);
|
|
129
|
+
if (!conn) {
|
|
130
|
+
return {
|
|
131
|
+
ok: false,
|
|
132
|
+
status: "error",
|
|
133
|
+
salesOrderId,
|
|
134
|
+
error: `Geen actieve Floriday-koppeling voor ${env}`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const cacheRow = await readSalesOrderFromCache(supabase, conn.id, salesOrderId);
|
|
138
|
+
if (!cacheRow) {
|
|
139
|
+
await logPhase(supabase, conn.id, salesOrderId, "error", {
|
|
140
|
+
error: "Sales order niet in cache",
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
ok: false,
|
|
144
|
+
status: "error",
|
|
145
|
+
salesOrderId,
|
|
146
|
+
error: "Sales order niet gevonden in cache",
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const raw = cacheRow.data ?? {};
|
|
150
|
+
// ─ Guard 1: Order geannuleerd? ────────────────────────────────────────────
|
|
151
|
+
if (raw.automaticallyCancelledOn) {
|
|
152
|
+
return {
|
|
153
|
+
ok: false,
|
|
154
|
+
status: "error",
|
|
155
|
+
salesOrderId,
|
|
156
|
+
error: `Order automatisch geannuleerd op ${raw.automaticallyCancelledOn}`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// ─ Guard 2: Cancellation-deadline nog niet verstreken ────────────────────
|
|
160
|
+
const cancellationDeadline = raw.cancellationDeadline ?? raw.commitmentDeadline ?? null;
|
|
161
|
+
if (!input.force && cancellationDeadline) {
|
|
162
|
+
const deadline = new Date(cancellationDeadline);
|
|
163
|
+
if (Number.isFinite(deadline.getTime()) && deadline.getTime() > Date.now()) {
|
|
164
|
+
await logPhase(supabase, conn.id, salesOrderId, "waiting_for_commit", {
|
|
165
|
+
reason: `deadline_at_${cancellationDeadline}`,
|
|
166
|
+
});
|
|
167
|
+
return {
|
|
168
|
+
ok: true,
|
|
169
|
+
status: "waiting_for_commit",
|
|
170
|
+
salesOrderId,
|
|
171
|
+
cancellationDeadline,
|
|
172
|
+
reason: `deadline_at_${cancellationDeadline}`,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// ─ Guard 3: Delivery-order al door Floriday gepushed? ─────────────────────
|
|
177
|
+
const doMatch = await findDeliveryOrderForSalesOrder(supabase, conn.id, salesOrderId);
|
|
178
|
+
if (!doMatch) {
|
|
179
|
+
await logPhase(supabase, conn.id, salesOrderId, "waiting_for_delivery_order", {
|
|
180
|
+
reason: "no_delivery_order_in_cache_yet",
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
ok: true,
|
|
184
|
+
status: "waiting_for_delivery_order",
|
|
185
|
+
salesOrderId,
|
|
186
|
+
cancellationDeadline,
|
|
187
|
+
reason: "no_delivery_order_in_cache_yet",
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const deliveryOrderId = doMatch.deliveryOrderId;
|
|
191
|
+
// ─ Idempotency: fulfillment-order al verzonden? ──────────────────────────
|
|
192
|
+
const existingFo = await hasFulfillmentOrder(supabase, conn.id, deliveryOrderId);
|
|
193
|
+
if (existingFo) {
|
|
194
|
+
return {
|
|
195
|
+
ok: true,
|
|
196
|
+
status: "done",
|
|
197
|
+
salesOrderId,
|
|
198
|
+
deliveryOrderId,
|
|
199
|
+
fulfillmentOrderId: existingFo,
|
|
200
|
+
reason: "already_sent",
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
// ─ Guard 4: Timing (autoProcessMoment) ───────────────────────────────────
|
|
204
|
+
const deliveryDate = raw.deliveryDate ?? raw.requestedDeliveryDate ?? doMatch.data?.deliveryDate ?? null;
|
|
205
|
+
const requiresStickers = Boolean(raw.requiresStickers);
|
|
206
|
+
if (!input.force && deliveryDate) {
|
|
207
|
+
const settings = await loadAfhandelingSettings(supabase);
|
|
208
|
+
if (settings.auto_process_paused) {
|
|
209
|
+
return {
|
|
210
|
+
ok: true,
|
|
211
|
+
status: "waiting",
|
|
212
|
+
salesOrderId,
|
|
213
|
+
deliveryOrderId,
|
|
214
|
+
reason: "auto_process_paused",
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const { moment, reason } = computeAutoProcessDate({
|
|
218
|
+
deliveryDate,
|
|
219
|
+
maxWorkdaysEarlierPick: settings.max_workdays_earlier_pick ?? 1,
|
|
220
|
+
pickWeekdays: settings.pick_weekdays ?? [1, 2, 3, 4, 5],
|
|
221
|
+
extraStickerDaysWithStickers: settings.extra_pick_days_with_stickers ?? 0,
|
|
222
|
+
requiresStickers,
|
|
223
|
+
});
|
|
224
|
+
if (moment && moment.getTime() > Date.now()) {
|
|
225
|
+
return {
|
|
226
|
+
ok: true,
|
|
227
|
+
status: "waiting",
|
|
228
|
+
salesOrderId,
|
|
229
|
+
deliveryOrderId,
|
|
230
|
+
reason: reason ?? "before_auto_process_moment",
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// ─ Actie: POST /fulfillment-orders ───────────────────────────────────────
|
|
235
|
+
await logPhase(supabase, conn.id, salesOrderId, "sending_fulfillment");
|
|
236
|
+
const fulfillmentRequests = doMatch.fulfillmentRequests
|
|
237
|
+
.map((r) => {
|
|
238
|
+
const fulfillmentRequestId = r?.fulfillmentRequestId ?? r?.id ?? r?.requestId;
|
|
239
|
+
if (!fulfillmentRequestId)
|
|
240
|
+
return null;
|
|
241
|
+
return {
|
|
242
|
+
deliveryOrderId,
|
|
243
|
+
fulfillmentRequestId,
|
|
244
|
+
numberOfPackages: extractPackageCount(r),
|
|
245
|
+
};
|
|
246
|
+
})
|
|
247
|
+
.filter(Boolean);
|
|
248
|
+
if (fulfillmentRequests.length === 0) {
|
|
249
|
+
await logPhase(supabase, conn.id, salesOrderId, "error", {
|
|
250
|
+
error: "Delivery-order bevat geen fulfillmentRequests voor deze sales-order",
|
|
251
|
+
});
|
|
252
|
+
return {
|
|
253
|
+
ok: false,
|
|
254
|
+
status: "error",
|
|
255
|
+
salesOrderId,
|
|
256
|
+
deliveryOrderId,
|
|
257
|
+
error: "Geen fulfillmentRequests gevonden in delivery-order",
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
const body = {
|
|
261
|
+
loadCarrierItems: [{ fulfillmentRequests }],
|
|
262
|
+
};
|
|
263
|
+
const foResult = await florydayWrite(supabase, env, {
|
|
264
|
+
module: "auto-process",
|
|
265
|
+
action: "send-fulfillment-order",
|
|
266
|
+
method: "POST",
|
|
267
|
+
path: "/fulfillment-orders",
|
|
268
|
+
body,
|
|
269
|
+
referenceId: deliveryOrderId,
|
|
270
|
+
idempotencyKey: `do:${deliveryOrderId}:fulfillment-order`,
|
|
271
|
+
});
|
|
272
|
+
if (!foResult.ok) {
|
|
273
|
+
await logPhase(supabase, conn.id, salesOrderId, "error", {
|
|
274
|
+
error: foResult.error ?? "Versturen fulfillment-order mislukt",
|
|
275
|
+
});
|
|
276
|
+
return {
|
|
277
|
+
ok: false,
|
|
278
|
+
status: "error",
|
|
279
|
+
salesOrderId,
|
|
280
|
+
deliveryOrderId,
|
|
281
|
+
error: foResult.error ?? "Versturen fulfillment-order mislukt",
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
const fulfillmentOrderId = foResult.data?.fulfillmentOrderId ??
|
|
285
|
+
foResult.data?.id ??
|
|
286
|
+
foResult.data?.floridayId;
|
|
287
|
+
await logPhase(supabase, conn.id, salesOrderId, "done", { fulfillmentOrderId });
|
|
288
|
+
return {
|
|
289
|
+
ok: true,
|
|
290
|
+
status: "done",
|
|
291
|
+
salesOrderId,
|
|
292
|
+
deliveryOrderId,
|
|
293
|
+
fulfillmentOrderId,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Cron-scan: sales-orders waarvan (a) de cancellation-deadline verstreken is,
|
|
298
|
+
* (b) er een delivery-order in cache staat en (c) nog geen fulfillment-order
|
|
299
|
+
* verzonden is → trigger `runAutoProcess`.
|
|
300
|
+
*/
|
|
301
|
+
export async function scanAndTriggerAutoProcess(supabase) {
|
|
302
|
+
const env = await getActiveFlorydayEnvironment(supabase);
|
|
303
|
+
const conn = await getActiveFlorydayCredentials(supabase, env);
|
|
304
|
+
if (!conn)
|
|
305
|
+
return { scanned: 0, triggered: 0, results: [] };
|
|
306
|
+
const settings = await loadAfhandelingSettings(supabase);
|
|
307
|
+
if (settings.auto_process_paused)
|
|
308
|
+
return { scanned: 0, triggered: 0, results: [] };
|
|
309
|
+
// Vandaag geen pickdag? Alleen spoedorders (computeAutoProcessDate laat die
|
|
310
|
+
// via `outside_pickday` alsnog door).
|
|
311
|
+
void isPickdayToday(settings.pick_weekdays ?? [1, 2, 3, 4, 5]);
|
|
312
|
+
const nowIso = new Date().toISOString();
|
|
313
|
+
// Filter aan de bron: order niet geannuleerd, deadline verstreken. De
|
|
314
|
+
// "delivery-order aanwezig?" en "fulfillment-order al verzonden?" checks
|
|
315
|
+
// gebeuren per-order in runAutoProcess (blijven zo idempotent).
|
|
316
|
+
const { data: orders } = await supabase
|
|
317
|
+
.from("floriday_salesorders_cache")
|
|
318
|
+
.select("floriday_id, data")
|
|
319
|
+
.eq("connection_id", conn.id)
|
|
320
|
+
.eq("is_deleted", false)
|
|
321
|
+
.is("data->>automaticallyCancelledOn", null)
|
|
322
|
+
.lt("data->>cancellationDeadline", nowIso)
|
|
323
|
+
.limit(500);
|
|
324
|
+
const candidates = orders ?? [];
|
|
325
|
+
// Circuit-breaker: sla orders over die 5× of vaker op 'error' zijn geëindigd.
|
|
326
|
+
// Handmatige retry via runAutoProcess({ force: true }) reset attempts vanzelf
|
|
327
|
+
// omdat de nieuwe status ≠ 'error' is.
|
|
328
|
+
const ids = candidates.map((c) => c.floriday_id);
|
|
329
|
+
let blocked = new Set();
|
|
330
|
+
if (ids.length > 0) {
|
|
331
|
+
const { data: logs } = await supabase
|
|
332
|
+
.from("floriday_auto_fulfillment_log")
|
|
333
|
+
.select("sales_order_id, status, attempts")
|
|
334
|
+
.eq("connection_id", conn.id)
|
|
335
|
+
.in("sales_order_id", ids);
|
|
336
|
+
blocked = new Set((logs ?? [])
|
|
337
|
+
.filter((l) => l.status === "error" && (l.attempts ?? 0) >= 5)
|
|
338
|
+
.map((l) => l.sales_order_id));
|
|
339
|
+
}
|
|
340
|
+
const filtered = candidates.filter((c) => !blocked.has(c.floriday_id));
|
|
341
|
+
const results = [];
|
|
342
|
+
const concurrency = 5;
|
|
343
|
+
let idx = 0;
|
|
344
|
+
async function worker() {
|
|
345
|
+
while (idx < filtered.length) {
|
|
346
|
+
const i = idx++;
|
|
347
|
+
const row = filtered[i];
|
|
348
|
+
const outcome = await runAutoProcess(supabase, {
|
|
349
|
+
salesOrderId: row.floriday_id,
|
|
350
|
+
environment: env,
|
|
351
|
+
}).catch((e) => ({
|
|
352
|
+
ok: false,
|
|
353
|
+
status: "error",
|
|
354
|
+
salesOrderId: row.floriday_id,
|
|
355
|
+
error: String(e?.message ?? e),
|
|
356
|
+
}));
|
|
357
|
+
results.push(outcome);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
await Promise.all(Array.from({ length: concurrency }, worker));
|
|
361
|
+
const triggered = results.filter((r) => r.status !== "waiting" &&
|
|
362
|
+
r.status !== "waiting_for_commit" &&
|
|
363
|
+
r.status !== "waiting_for_delivery_order").length;
|
|
364
|
+
return { scanned: candidates.length, triggered, results };
|
|
365
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import { type FlorydayEnvironment } from "./floriday.functions";
|
|
3
|
+
export interface LookupInput {
|
|
4
|
+
environment: FlorydayEnvironment;
|
|
5
|
+
fulfillmentOrderId?: string | null;
|
|
6
|
+
deliveryOrderId?: string | null;
|
|
7
|
+
salesOrderId?: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface LookupResult {
|
|
10
|
+
fulfillmentOrderIds: string[];
|
|
11
|
+
source: "input" | "cache" | "delivery-order" | "sales-order" | "sales-order-cache";
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Retourneert alle bekende fulfillmentOrderIds voor de gegeven referenties.
|
|
15
|
+
* Wanneer `fulfillmentOrderId` gegeven is wordt die direct teruggegeven —
|
|
16
|
+
* er wordt niets extra bevraagd.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveFulfillmentOrderIds(supabase: SupabaseClient, input: LookupInput): Promise<LookupResult>;
|
|
19
|
+
//# sourceMappingURL=floriday-fulfillment-lookup.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-fulfillment-lookup.server.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-fulfillment-lookup.server.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,mBAAmB,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,MAAM,EACF,OAAO,GACP,OAAO,GACP,gBAAgB,GAChB,aAAa,GACb,mBAAmB,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,YAAY,CAAC,CAoGvB"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Resolve een fulfillmentOrderId vanuit deliveryOrderId of salesOrderId.
|
|
3
|
+
// ----------------------------------------------------------------------------
|
|
4
|
+
// Modules als Verkoop en Afhandeling kennen doorgaans alleen de sales-order
|
|
5
|
+
// of delivery-order ID. Voor afleverbrieven hebben we een fulfillmentOrderId
|
|
6
|
+
// nodig. Deze helper zoekt hem op — eerst in de lokale cache, daarna
|
|
7
|
+
// on-demand via de Floriday-API.
|
|
8
|
+
// ============================================================================
|
|
9
|
+
import { SUPPLIERS_API_VERSION, florydayFetch, getActiveFlorydayCredentials, } from "./floriday.functions";
|
|
10
|
+
/**
|
|
11
|
+
* Retourneert alle bekende fulfillmentOrderIds voor de gegeven referenties.
|
|
12
|
+
* Wanneer `fulfillmentOrderId` gegeven is wordt die direct teruggegeven —
|
|
13
|
+
* er wordt niets extra bevraagd.
|
|
14
|
+
*/
|
|
15
|
+
export async function resolveFulfillmentOrderIds(supabase, input) {
|
|
16
|
+
if (input.fulfillmentOrderId?.trim()) {
|
|
17
|
+
return { fulfillmentOrderIds: [input.fulfillmentOrderId.trim()], source: "input" };
|
|
18
|
+
}
|
|
19
|
+
const env = input.environment;
|
|
20
|
+
const conn = await getActiveFlorydayCredentials(supabase, env);
|
|
21
|
+
if (!conn)
|
|
22
|
+
throw new Error(`Geen actieve Floriday-koppeling voor ${env}`);
|
|
23
|
+
// 1) deliveryOrderId → cache-lookup
|
|
24
|
+
if (input.deliveryOrderId?.trim()) {
|
|
25
|
+
const deliveryOrderId = input.deliveryOrderId.trim();
|
|
26
|
+
const { data: cached } = await supabase
|
|
27
|
+
.from("floriday_fulfillment_orders_cache")
|
|
28
|
+
.select("floriday_id")
|
|
29
|
+
.eq("connection_id", conn.id)
|
|
30
|
+
.eq("delivery_order_id", deliveryOrderId)
|
|
31
|
+
.eq("is_deleted", false);
|
|
32
|
+
const cachedIds = (cached ?? [])
|
|
33
|
+
.map((r) => String(r.floriday_id))
|
|
34
|
+
.filter(Boolean);
|
|
35
|
+
if (cachedIds.length > 0) {
|
|
36
|
+
return { fulfillmentOrderIds: cachedIds, source: "cache" };
|
|
37
|
+
}
|
|
38
|
+
// Cache-miss → live ophalen bij Floriday
|
|
39
|
+
const ids = await fetchFulfillmentIdsForDeliveryOrder(supabase, env, deliveryOrderId);
|
|
40
|
+
if (ids.length === 0) {
|
|
41
|
+
throw new Error(`Geen fulfillment-order gevonden voor delivery-order ${deliveryOrderId}`);
|
|
42
|
+
}
|
|
43
|
+
return { fulfillmentOrderIds: ids, source: "delivery-order" };
|
|
44
|
+
}
|
|
45
|
+
// 2) salesOrderId → fulfillment-orders cache doorzoeken.
|
|
46
|
+
// In Floriday 2026v1 verwijst de fulfillment-order naar de sales-order
|
|
47
|
+
// (via loadCarriers[].loadCarrierItems[].salesOrderId), niet andersom.
|
|
48
|
+
if (input.salesOrderId?.trim()) {
|
|
49
|
+
const salesOrderId = input.salesOrderId.trim();
|
|
50
|
+
// Eerst cache raadplegen
|
|
51
|
+
let matched = await findFulfillmentIdsForSalesOrderInCache(supabase, conn.id, salesOrderId);
|
|
52
|
+
// Cache-miss? Triggert een korte on-demand delta-sync (fulfillment-orders
|
|
53
|
+
// groeit meestal met 0-enkele items per minuut) en probeer opnieuw. Zo
|
|
54
|
+
// hoeven we niet op de volgende cron-tick te wachten.
|
|
55
|
+
if (matched.length === 0) {
|
|
56
|
+
try {
|
|
57
|
+
const { runFloridayResourceSync } = await import("./floriday-sync.functions");
|
|
58
|
+
await runFloridayResourceSync(supabase, env, "fulfillment-orders");
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
console.warn("[fulfillment-lookup] on-demand sync faalde:", e);
|
|
62
|
+
}
|
|
63
|
+
matched = await findFulfillmentIdsForSalesOrderInCache(supabase, conn.id, salesOrderId);
|
|
64
|
+
}
|
|
65
|
+
if (matched.length > 0) {
|
|
66
|
+
return { fulfillmentOrderIds: matched, source: "sales-order-cache" };
|
|
67
|
+
}
|
|
68
|
+
// Nog steeds niets. Onderscheid maken tussen "order onbekend" en
|
|
69
|
+
// "order bekend, maar Floriday heeft nog geen fulfillment aangemaakt".
|
|
70
|
+
const { data: soRow } = await supabase
|
|
71
|
+
.from("floriday_salesorders_cache")
|
|
72
|
+
.select("floriday_id")
|
|
73
|
+
.eq("connection_id", conn.id)
|
|
74
|
+
.eq("floriday_id", salesOrderId)
|
|
75
|
+
.maybeSingle();
|
|
76
|
+
if (!soRow) {
|
|
77
|
+
throw new Error(`Deze order (${salesOrderId}) is nog niet gesynchroniseerd vanuit Floriday. Probeer het over een minuut opnieuw.`);
|
|
78
|
+
}
|
|
79
|
+
throw new Error(`Floriday heeft voor deze order nog geen afleverbrief aangemaakt. Open de order in het Floriday-portaal en wijs de order toe aan een trolley of afleverronde. Zodra dat gedaan is verschijnt de afleverbrief hier automatisch (binnen ongeveer een minuut).`);
|
|
80
|
+
}
|
|
81
|
+
throw new Error("Geef ten minste één van fulfillmentOrderId, deliveryOrderId of salesOrderId op");
|
|
82
|
+
}
|
|
83
|
+
async function fetchFulfillmentIdsForDeliveryOrder(supabase, env, deliveryOrderId) {
|
|
84
|
+
const res = await florydayFetch(supabase, env, `/${SUPPLIERS_API_VERSION}/delivery-orders/${encodeURIComponent(deliveryOrderId)}`, { cache: "no-store" });
|
|
85
|
+
if (!res.ok) {
|
|
86
|
+
const body = await res.text().catch(() => "");
|
|
87
|
+
throw new Error(`Delivery-order ${res.status}: ${body.slice(0, 200)}`);
|
|
88
|
+
}
|
|
89
|
+
const json = await res.json();
|
|
90
|
+
const ids = [];
|
|
91
|
+
const push = (v) => {
|
|
92
|
+
if (typeof v === "string" && v)
|
|
93
|
+
ids.push(v);
|
|
94
|
+
};
|
|
95
|
+
push(json?.fulfillmentOrderId);
|
|
96
|
+
if (Array.isArray(json?.fulfillmentOrderIds)) {
|
|
97
|
+
for (const id of json.fulfillmentOrderIds)
|
|
98
|
+
push(id);
|
|
99
|
+
}
|
|
100
|
+
if (Array.isArray(json?.fulfillmentOrders)) {
|
|
101
|
+
for (const fo of json.fulfillmentOrders) {
|
|
102
|
+
push(fo?.fulfillmentOrderId ?? fo?.id);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return Array.from(new Set(ids));
|
|
106
|
+
}
|
|
107
|
+
async function findFulfillmentIdsForSalesOrderInCache(supabase, connectionId, salesOrderId) {
|
|
108
|
+
const { data: candidates } = await supabase
|
|
109
|
+
.from("floriday_fulfillment_orders_cache")
|
|
110
|
+
.select("floriday_id, data")
|
|
111
|
+
.eq("connection_id", connectionId)
|
|
112
|
+
.eq("is_deleted", false)
|
|
113
|
+
.ilike("data", `%${salesOrderId}%`);
|
|
114
|
+
const matched = new Set();
|
|
115
|
+
for (const row of candidates ?? []) {
|
|
116
|
+
const data = row.data;
|
|
117
|
+
const carriers = Array.isArray(data?.loadCarriers)
|
|
118
|
+
? data.loadCarriers
|
|
119
|
+
: [];
|
|
120
|
+
const hit = carriers.some((lc) => Array.isArray(lc?.loadCarrierItems)
|
|
121
|
+
? lc.loadCarrierItems.some((it) => it?.salesOrderId === salesOrderId)
|
|
122
|
+
: false);
|
|
123
|
+
if (hit) {
|
|
124
|
+
const id = String(row.floriday_id ?? data?.fulfillmentOrderId ?? "");
|
|
125
|
+
if (id)
|
|
126
|
+
matched.add(id);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return Array.from(matched);
|
|
130
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { GatewayAuthFail, GatewayAuthOk, GatewayBinaryAuthOk } from "./floriday-gateway-hmac";
|
|
2
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
3
|
+
import { type FlorydayEnvironment } from "./floriday.functions";
|
|
4
|
+
/**
|
|
5
|
+
* Resolveert welke Floriday-omgeving voor deze gateway-call gebruikt moet worden.
|
|
6
|
+
*
|
|
7
|
+
* Regels:
|
|
8
|
+
* 1. Als de caller een omgeving opgeeft én die heeft een actieve koppeling → gebruik die.
|
|
9
|
+
* 2. Anders → val terug op de in deze module actieve omgeving (`floriday_settings.active_environment`).
|
|
10
|
+
* 3. Als ook die geen actieve koppeling heeft → gooi een duidelijke fout.
|
|
11
|
+
*
|
|
12
|
+
* Op deze manier crasht de gateway niet meer wanneer een externe module `live`
|
|
13
|
+
* stuurt terwijl alleen `staging` is ingericht (of omgekeerd).
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveGatewayEnvironment(supabase: SupabaseClient, requested?: FlorydayEnvironment | string | null): Promise<FlorydayEnvironment>;
|
|
16
|
+
export declare const GATEWAY_CORS_HEADERS: Record<string, string>;
|
|
17
|
+
export declare function corsPreflight(): Response;
|
|
18
|
+
export declare function withCors(response: Response): Response;
|
|
19
|
+
/**
|
|
20
|
+
* Accepteert HMAC of Supabase Bearer. Retourneert de raw body voor JSON.parse.
|
|
21
|
+
*/
|
|
22
|
+
export declare function verifyGatewayCaller(request: Request): Promise<GatewayAuthOk | GatewayAuthFail>;
|
|
23
|
+
export declare function verifyGatewayBinaryCaller(request: Request): Promise<GatewayBinaryAuthOk | GatewayAuthFail>;
|
|
24
|
+
//# sourceMappingURL=floriday-gateway-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-gateway-auth.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-gateway-auth.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,eAAe,EACf,aAAa,EACb,mBAAmB,EACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,sBAAsB,CAAC;AAE9B;;;;;;;;;;GAUG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,cAAc,EACxB,SAAS,CAAC,EAAE,mBAAmB,GAAG,MAAM,GAAG,IAAI,GAC9C,OAAO,CAAC,mBAAmB,CAAC,CAyB9B;AAED,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAMvD,CAAC;AAEF,wBAAgB,aAAa,IAAI,QAAQ,CAExC;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,CAQrD;AAgCD;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,aAAa,GAAG,eAAe,CAAC,CAuB1C;AAGD,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,mBAAmB,GAAG,eAAe,CAAC,CAahD"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Dual-mode auth voor de PDF-gateway routes.
|
|
3
|
+
// ----------------------------------------------------------------------------
|
|
4
|
+
// Accepteert:
|
|
5
|
+
// - HMAC-handtekening (server-to-server callers met shared secret), of
|
|
6
|
+
// - Supabase Bearer JWT (ingelogde gebruiker in een andere Lovable-module).
|
|
7
|
+
//
|
|
8
|
+
// Alleen bedoeld voor de lees-/PDF-endpoints. Schrijf-endpoints blijven
|
|
9
|
+
// exclusief HMAC.
|
|
10
|
+
// ============================================================================
|
|
11
|
+
import { createClient } from "@supabase/supabase-js";
|
|
12
|
+
import { verifyGatewayRequest, verifyGatewayBinaryRequest } from "./floriday-gateway-hmac";
|
|
13
|
+
import { getActiveFlorydayCredentials, getActiveFlorydayEnvironment, } from "./floriday.functions";
|
|
14
|
+
/**
|
|
15
|
+
* Resolveert welke Floriday-omgeving voor deze gateway-call gebruikt moet worden.
|
|
16
|
+
*
|
|
17
|
+
* Regels:
|
|
18
|
+
* 1. Als de caller een omgeving opgeeft én die heeft een actieve koppeling → gebruik die.
|
|
19
|
+
* 2. Anders → val terug op de in deze module actieve omgeving (`floriday_settings.active_environment`).
|
|
20
|
+
* 3. Als ook die geen actieve koppeling heeft → gooi een duidelijke fout.
|
|
21
|
+
*
|
|
22
|
+
* Op deze manier crasht de gateway niet meer wanneer een externe module `live`
|
|
23
|
+
* stuurt terwijl alleen `staging` is ingericht (of omgekeerd).
|
|
24
|
+
*/
|
|
25
|
+
export async function resolveGatewayEnvironment(supabase, requested) {
|
|
26
|
+
const wanted = requested === "staging" || requested === "live"
|
|
27
|
+
? requested
|
|
28
|
+
: null;
|
|
29
|
+
if (wanted) {
|
|
30
|
+
const row = await getActiveFlorydayCredentials(supabase, wanted);
|
|
31
|
+
if (row)
|
|
32
|
+
return wanted;
|
|
33
|
+
}
|
|
34
|
+
const active = await getActiveFlorydayEnvironment(supabase);
|
|
35
|
+
const activeRow = await getActiveFlorydayCredentials(supabase, active);
|
|
36
|
+
if (!activeRow) {
|
|
37
|
+
throw new Error("Er is geen actieve Floriday-koppeling geconfigureerd. Open het beheerscherm en verbind Floriday.");
|
|
38
|
+
}
|
|
39
|
+
if (wanted && wanted !== active) {
|
|
40
|
+
console.warn(`[gateway] gevraagde omgeving "${wanted}" heeft geen actieve koppeling; val terug op "${active}"`);
|
|
41
|
+
}
|
|
42
|
+
return active;
|
|
43
|
+
}
|
|
44
|
+
export const GATEWAY_CORS_HEADERS = {
|
|
45
|
+
"Access-Control-Allow-Origin": "*",
|
|
46
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
47
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, apikey, x-floriday-timestamp, x-floriday-signature, x-floriday-caller",
|
|
48
|
+
"Access-Control-Max-Age": "86400",
|
|
49
|
+
};
|
|
50
|
+
export function corsPreflight() {
|
|
51
|
+
return new Response(null, { status: 204, headers: GATEWAY_CORS_HEADERS });
|
|
52
|
+
}
|
|
53
|
+
export function withCors(response) {
|
|
54
|
+
const headers = new Headers(response.headers);
|
|
55
|
+
for (const [k, v] of Object.entries(GATEWAY_CORS_HEADERS))
|
|
56
|
+
headers.set(k, v);
|
|
57
|
+
return new Response(response.body, {
|
|
58
|
+
status: response.status,
|
|
59
|
+
statusText: response.statusText,
|
|
60
|
+
headers,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async function verifySupabaseBearer(request) {
|
|
64
|
+
const authHeader = request.headers.get("authorization");
|
|
65
|
+
if (!authHeader || !authHeader.toLowerCase().startsWith("bearer "))
|
|
66
|
+
return null;
|
|
67
|
+
const token = authHeader.slice(7).trim();
|
|
68
|
+
if (!token)
|
|
69
|
+
return null;
|
|
70
|
+
const SUPABASE_URL = process.env.SUPABASE_URL;
|
|
71
|
+
const SUPABASE_PUBLISHABLE_KEY = process.env.SUPABASE_PUBLISHABLE_KEY;
|
|
72
|
+
if (!SUPABASE_URL || !SUPABASE_PUBLISHABLE_KEY) {
|
|
73
|
+
return { ok: false, status: 500, error: "Supabase niet geconfigureerd op gateway" };
|
|
74
|
+
}
|
|
75
|
+
const supabase = createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, {
|
|
76
|
+
auth: { storage: undefined, persistSession: false, autoRefreshToken: false },
|
|
77
|
+
});
|
|
78
|
+
try {
|
|
79
|
+
const { data, error } = await supabase.auth.getClaims(token);
|
|
80
|
+
if (error || !data?.claims?.sub) {
|
|
81
|
+
return { ok: false, status: 401, error: "Ongeldige of verlopen sessie" };
|
|
82
|
+
}
|
|
83
|
+
return { ok: true, userId: String(data.claims.sub) };
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return { ok: false, status: 401, error: "Sessie kon niet worden geverifieerd" };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Accepteert HMAC of Supabase Bearer. Retourneert de raw body voor JSON.parse.
|
|
91
|
+
*/
|
|
92
|
+
export async function verifyGatewayCaller(request) {
|
|
93
|
+
// 1) apikey-header met publishable key (bv. pg_cron via net.http_post)?
|
|
94
|
+
const apiKey = request.headers.get("apikey");
|
|
95
|
+
if (apiKey && apiKey === process.env.SUPABASE_PUBLISHABLE_KEY) {
|
|
96
|
+
const rawBody = await request.text();
|
|
97
|
+
return { ok: true, caller: "cron:pg_cron", rawBody };
|
|
98
|
+
}
|
|
99
|
+
// 2) Supabase Bearer? Dan die weg gebruiken.
|
|
100
|
+
const bearer = await verifySupabaseBearer(request);
|
|
101
|
+
if (bearer && bearer.ok) {
|
|
102
|
+
const rawBody = await request.text();
|
|
103
|
+
return { ok: true, caller: `user:${bearer.userId}`, rawBody };
|
|
104
|
+
}
|
|
105
|
+
if (bearer && !bearer.ok) {
|
|
106
|
+
const hasHmac = request.headers.get("x-floriday-signature") &&
|
|
107
|
+
request.headers.get("x-floriday-timestamp");
|
|
108
|
+
if (!hasHmac)
|
|
109
|
+
return bearer;
|
|
110
|
+
}
|
|
111
|
+
// 3) Val terug op HMAC.
|
|
112
|
+
return verifyGatewayRequest(request);
|
|
113
|
+
}
|
|
114
|
+
export async function verifyGatewayBinaryCaller(request) {
|
|
115
|
+
const bearer = await verifySupabaseBearer(request);
|
|
116
|
+
if (bearer && bearer.ok) {
|
|
117
|
+
const bodyBytes = new Uint8Array(await request.arrayBuffer());
|
|
118
|
+
return { ok: true, caller: `user:${bearer.userId}`, bodyBytes };
|
|
119
|
+
}
|
|
120
|
+
if (bearer && !bearer.ok) {
|
|
121
|
+
const hasHmac = request.headers.get("x-floriday-signature") &&
|
|
122
|
+
request.headers.get("x-floriday-timestamp");
|
|
123
|
+
if (!hasHmac)
|
|
124
|
+
return bearer;
|
|
125
|
+
}
|
|
126
|
+
return verifyGatewayBinaryRequest(request);
|
|
127
|
+
}
|