@flowselections/floriday-authenticatie-module 1.0.53 → 1.0.55

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.
Files changed (46) hide show
  1. package/dist-lib/components/floriday/FlorydayConnectionPage.d.ts.map +1 -1
  2. package/dist-lib/components/floriday/FlorydayConnectionPage.js +2 -1
  3. package/dist-lib/components/floriday/FlorydayShellRoutesCard.d.ts +2 -0
  4. package/dist-lib/components/floriday/FlorydayShellRoutesCard.d.ts.map +1 -0
  5. package/dist-lib/components/floriday/FlorydayShellRoutesCard.js +22 -0
  6. package/dist-lib/components/settings/FlorydaySettingsCard.d.ts.map +1 -1
  7. package/dist-lib/components/settings/FlorydaySettingsCard.js +26 -48
  8. package/dist-lib/index.js +1 -1
  9. package/dist-lib/integrations/supabase/client.server.d.ts +2 -32082
  10. package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
  11. package/dist-lib/integrations/supabase/types.d.ts +1418 -6
  12. package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
  13. package/dist-lib/lib/cron/process-stock-queue.d.ts +44 -5
  14. package/dist-lib/lib/cron/process-stock-queue.d.ts.map +1 -1
  15. package/dist-lib/lib/cron/process-stock-queue.js +167 -96
  16. package/dist-lib/lib/floriday-gln.d.ts +6 -0
  17. package/dist-lib/lib/floriday-gln.d.ts.map +1 -0
  18. package/dist-lib/lib/floriday-gln.js +22 -0
  19. package/dist-lib/lib/floriday-own-config.d.ts +68 -0
  20. package/dist-lib/lib/floriday-own-config.d.ts.map +1 -0
  21. package/dist-lib/lib/floriday-own-config.js +73 -0
  22. package/dist-lib/lib/floriday-scopes.d.ts +7 -0
  23. package/dist-lib/lib/floriday-scopes.d.ts.map +1 -1
  24. package/dist-lib/lib/floriday-scopes.js +34 -0
  25. package/dist-lib/lib/floriday-sync.functions.d.ts.map +1 -1
  26. package/dist-lib/lib/floriday-sync.functions.js +12 -2
  27. package/dist-lib/lib/floriday.functions.d.ts +49 -12
  28. package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
  29. package/dist-lib/lib/floriday.functions.js +221 -63
  30. package/dist-lib/lib/gateway/diagnostics.d.ts.map +1 -1
  31. package/dist-lib/lib/gateway/diagnostics.js +12 -0
  32. package/dist-lib/lib/gateway/floriday-get.d.ts +55 -0
  33. package/dist-lib/lib/gateway/floriday-get.d.ts.map +1 -0
  34. package/dist-lib/lib/gateway/floriday-get.js +185 -0
  35. package/dist-lib/lib/gateway/index.d.ts +1 -0
  36. package/dist-lib/lib/gateway/index.d.ts.map +1 -1
  37. package/dist-lib/lib/gateway/index.js +1 -0
  38. package/dist-lib/lib/gateway/webhook-subscribe.d.ts.map +1 -1
  39. package/dist-lib/lib/gateway/webhook-subscribe.js +13 -1
  40. package/dist-lib/lib/shell-base.d.ts +2 -0
  41. package/dist-lib/lib/shell-base.d.ts.map +1 -0
  42. package/dist-lib/lib/shell-base.js +31 -0
  43. package/dist-lib/lib/shell-routes.d.ts +25 -0
  44. package/dist-lib/lib/shell-routes.d.ts.map +1 -0
  45. package/dist-lib/lib/shell-routes.js +127 -0
  46. package/package.json +6 -489
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floriday-get.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/floriday-get.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,eAAO,MAAM,uBAAuB;;;;CAInC,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,0BAA0B;;;;CAItC,CAAC;AAWF,QAAA,MAAM,aAAa;;;;iBAIjB,CAAC;AAMH,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACzD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7G,KAAK,OAAO,GAAG;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;CACpE,CAAC;AAEF,oFAAoF;AACpF,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,GACjC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAsB7D;AAyFD,wBAAsB,cAAc,CAClC,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,OAAO,EAChB,MAAM,SAAa,GAClB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAsB5D;AAID,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,CAAC,CAiBnB"}
@@ -0,0 +1,185 @@
1
+ // Gateway: beperkt Floriday-leespad (floridayGet).
2
+ // Alleen HMAC (verifyGatewayRequest), vaste padenlijst, actieve omgeving,
3
+ // rate-limit via token bucket, 403 → supply:read-melding, 429 → één retry.
4
+ import { z } from "zod";
5
+ import { verifyGatewayRequest } from "../floriday-gateway-hmac.js";
6
+ import { corsPreflight, withCors, resolveGatewayEnvironment } from "../floriday-gateway-auth.js";
7
+ import { florydayFetch, getFlorydayAccessToken, SUPPLIERS_API_VERSION } from "../floriday.functions.js";
8
+ import { acquireFlorydayRateLimit } from "../floriday-rate-limit.server.js";
9
+ import { FLORYDAY_SUPPLY_PERMISSION_MESSAGE, floridayErrorShowsMissingPermission, getFlorydayScopeForPath, scopesFromAccessToken, } from "../floriday-scopes.js";
10
+ export const BASE_SUPPLY_READ_BUCKET = {
11
+ bucket: "base-supply-read",
12
+ capacity: 1,
13
+ refillPerSec: 1,
14
+ };
15
+ /** Swagger: /trade-items/availabilities — 2 per seconde, burst 200. */
16
+ export const AVAILABILITIES_READ_BUCKET = {
17
+ bucket: "availabilities-read",
18
+ capacity: 200,
19
+ refillPerSec: 2,
20
+ };
21
+ const MAX_RETRY_AFTER_MS = 5000;
22
+ const baseSupplyQuery = z
23
+ .object({
24
+ year: z.coerce.number().int().min(2000).max(2100),
25
+ week: z.coerce.number().int().min(1).max(53),
26
+ })
27
+ .strict();
28
+ const requestSchema = z.object({
29
+ method: z.literal("GET").optional(),
30
+ path: z.string().min(1).max(200),
31
+ query: z.record(z.string(), z.unknown()).optional(),
32
+ });
33
+ const bodySchema = z.object({
34
+ requests: z.array(requestSchema).min(1).max(10),
35
+ });
36
+ /** Vaste lijst met toegestane leespaden. Geeft null + reden terug bij weigering. */
37
+ export function checkFloridayGetAllowed(req) {
38
+ const path = req.path.split("?")[0].replace(/\/+$/, "");
39
+ if (req.path.includes("?")) {
40
+ return { ok: false, error: "Querystring hoort in 'query', niet in 'path'" };
41
+ }
42
+ if (path === "/trade-items/base-supply") {
43
+ const q = baseSupplyQuery.safeParse(req.query ?? {});
44
+ if (!q.success) {
45
+ return { ok: false, error: "Alleen de queryparameters 'year' (2000-2100) en 'week' (1-53) zijn toegestaan" };
46
+ }
47
+ return {
48
+ ok: true,
49
+ value: { path, query: { year: String(q.data.year), week: String(q.data.week) }, bucket: BASE_SUPPLY_READ_BUCKET },
50
+ };
51
+ }
52
+ if (path === "/trade-items/availabilities") {
53
+ if (req.query && Object.keys(req.query).length > 0) {
54
+ return { ok: false, error: "Voor /trade-items/availabilities zijn geen queryparameters toegestaan" };
55
+ }
56
+ return { ok: true, value: { path, query: {}, bucket: AVAILABILITIES_READ_BUCKET } };
57
+ }
58
+ return { ok: false, error: `Pad niet toegestaan: ${path}` };
59
+ }
60
+ function parseRetryAfterMs(h) {
61
+ if (!h)
62
+ return 1000;
63
+ const s = Number(h);
64
+ if (Number.isFinite(s))
65
+ return Math.max(0, s * 1000);
66
+ const d = Date.parse(h);
67
+ return Number.isFinite(d) ? Math.max(0, d - Date.now()) : 1000;
68
+ }
69
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
70
+ async function fetchOne(supabase, env, a, caller) {
71
+ const qs = new URLSearchParams(a.query).toString();
72
+ const url = `/${SUPPLIERS_API_VERSION}${a.path}${qs ? `?${qs}` : ""}`;
73
+ const doCall = async () => {
74
+ await acquireFlorydayRateLimit(supabase, a.bucket);
75
+ return florydayFetch(supabase, env, url, {
76
+ method: "GET",
77
+ flowLog: { module: "gateway", caller: `gateway:${caller}`, action: "floriday-get" },
78
+ });
79
+ };
80
+ let res = await doCall();
81
+ if (res.status === 429) {
82
+ const waitMs = parseRetryAfterMs(res.headers.get("retry-after"));
83
+ if (waitMs <= MAX_RETRY_AFTER_MS) {
84
+ await sleep(waitMs);
85
+ res = await doCall();
86
+ }
87
+ if (res.status === 429) {
88
+ const retryAfterMs = parseRetryAfterMs(res.headers.get("retry-after"));
89
+ console.warn(`[gateway floriday-get] 429 op ${a.path}, retryAfterMs=${retryAfterMs}`);
90
+ return { ok: false, status: 429, path: a.path, error: "Floriday rate-limit (429)", retryAfterMs };
91
+ }
92
+ }
93
+ if (res.status === 403) {
94
+ const body = await res.text().catch(() => "");
95
+ const requested = getFlorydayScopeForPath(url, "GET");
96
+ const needed = String(requested ?? "")
97
+ .split(/\s+/)
98
+ .filter((x) => x && !x.startsWith("role:"));
99
+ let missingScope = null;
100
+ try {
101
+ const tok = await getFlorydayAccessToken(supabase, env, { scope: requested });
102
+ const scopes = scopesFromAccessToken(tok);
103
+ if (scopes)
104
+ missingScope = needed.find((n) => !scopes.includes(n)) ?? null;
105
+ }
106
+ catch {
107
+ /* geen bewijs → echte body tonen */
108
+ }
109
+ const bodyShowsPermission = floridayErrorShowsMissingPermission(body);
110
+ if (missingScope || bodyShowsPermission) {
111
+ const scopeName = missingScope ?? needed[0] ?? "onbekend";
112
+ console.warn(`[gateway floriday-get] 403 op ${a.path}: recht ${scopeName} ontbreekt (bewezen)`);
113
+ const message = scopeName === "supply:read"
114
+ ? FLORYDAY_SUPPLY_PERMISSION_MESSAGE
115
+ : `Floriday heeft het recht \`${scopeName}\` nog niet vrijgegeven voor deze koppeling. Laat dit recht in Floriday activeren en vernieuw daarna de verbinding.`;
116
+ return { ok: false, status: 403, path: a.path, error: message };
117
+ }
118
+ console.warn(`[gateway floriday-get] 403 op ${a.path}: ${body.slice(0, 300)}`);
119
+ return {
120
+ ok: false,
121
+ status: 403,
122
+ path: a.path,
123
+ error: `Floriday 403: ${body.slice(0, 300)}`,
124
+ floridayBody: body.slice(0, 2000),
125
+ };
126
+ }
127
+ const text = await res.text();
128
+ if (!res.ok) {
129
+ return { ok: false, status: res.status, path: a.path, error: `Floriday ${res.status}: ${text.slice(0, 300)}` };
130
+ }
131
+ let data = null;
132
+ try {
133
+ data = text ? JSON.parse(text) : null;
134
+ }
135
+ catch {
136
+ data = text;
137
+ }
138
+ return { ok: true, status: res.status, path: a.path, data };
139
+ }
140
+ export async function runFloridayGet(supabase, rawBody, caller = "external") {
141
+ const parsed = bodySchema.safeParse(rawBody);
142
+ if (!parsed.success) {
143
+ return { status: 400, body: { ok: false, error: "Ongeldig verzoek: verwacht { requests: [{ path, query }] }" } };
144
+ }
145
+ const allowed = [];
146
+ for (const r of parsed.data.requests) {
147
+ const c = checkFloridayGetAllowed(r);
148
+ if (!c.ok)
149
+ return { status: 400, body: { ok: false, error: c.error } };
150
+ allowed.push(c.value);
151
+ }
152
+ const environment = await resolveGatewayEnvironment(supabase);
153
+ const results = [];
154
+ for (const a of allowed) {
155
+ try {
156
+ results.push(await fetchOne(supabase, environment, a, caller));
157
+ }
158
+ catch (e) {
159
+ results.push({ ok: false, status: 502, path: a.path, error: String(e?.message ?? e) });
160
+ }
161
+ }
162
+ return { status: 200, body: { ok: true, environment, results } };
163
+ }
164
+ const _json = (data, init) => withCors(Response.json(data, init));
165
+ export async function handleFloridayGet(request, supabase) {
166
+ if (request.method === "OPTIONS")
167
+ return corsPreflight();
168
+ const auth = await verifyGatewayRequest(request);
169
+ if (!auth.ok)
170
+ return _json({ ok: false, error: auth.error }, { status: auth.status });
171
+ let body;
172
+ try {
173
+ body = JSON.parse(auth.rawBody || "{}");
174
+ }
175
+ catch {
176
+ return _json({ ok: false, error: "Body is geen geldige JSON" }, { status: 400 });
177
+ }
178
+ try {
179
+ const r = await runFloridayGet(supabase, body, auth.caller);
180
+ return _json(r.body, { status: r.status });
181
+ }
182
+ catch (e) {
183
+ return _json({ ok: false, error: String(e?.message ?? e) }, { status: 409 });
184
+ }
185
+ }
@@ -24,4 +24,5 @@ export * from "./trade-item-version.js";
24
24
  export * from "./webhook.js";
25
25
  export * from "./webhook-process.js";
26
26
  export * from "./webhook-sns.js";
27
+ export * from "./floriday-get.js";
27
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC;AAC3B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,YAAY,CAAC;AAC3B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC"}
@@ -24,3 +24,4 @@ export * from "./trade-item-version.js";
24
24
  export * from "./webhook.js";
25
25
  export * from "./webhook-process.js";
26
26
  export * from "./webhook-sns.js";
27
+ export * from "./floriday-get.js";
@@ -1 +1 @@
1
- {"version":3,"file":"webhook-subscribe.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/webhook-subscribe.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,KAAK,GAAG,GAAG,SAAS,GAAG,MAAM,CAAC;AAM9B,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;CACtE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAUvH;AAQD,iFAAiF;AACjF,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBnF;AAuBD,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,GAAG,EAChB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACrG,OAAO,CAAC,YAAY,CAAC,CA2GvB;AA6BD,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE,cAAc;iBAKlC,MAAM;qBAc3C;AAED,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,CAWhH"}
1
+ {"version":3,"file":"webhook-subscribe.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/webhook-subscribe.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,KAAK,GAAG,GAAG,SAAS,GAAG,MAAM,CAAC;AAM9B,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;CACtE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAUvH;AAQD,iFAAiF;AACjF,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBnF;AAuBD,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,GAAG,EAChB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACrG,OAAO,CAAC,YAAY,CAAC,CA2GvB;AA6BD,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE,cAAc;iBAKlC,MAAM;qBAc3C;AAED,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsBhH"}
@@ -228,8 +228,20 @@ export async function handleEnsureFloridayWebhooks(request, supabase) {
228
228
  const { withJobLock } = await import("../cron/job-lock.js");
229
229
  return withJobLock(supabase, "floriday-sync:webhook-ensure", async () => {
230
230
  const results = await runEnsureFloridayWebhooks(supabase);
231
+ // Uurlijkse meelift: controleer of de Shell alle gateway-routes bedient.
232
+ const shellRoutes = await (async () => {
233
+ try {
234
+ const { checkShellRoutes, logShellRoutesReport } = await import("../shell-routes.js");
235
+ const r = await checkShellRoutes();
236
+ await logShellRoutesReport(supabase, r, "uurlijkse zelfcontrole");
237
+ return { ok: r.ok, missing: r.missing, error: r.error ?? null };
238
+ }
239
+ catch (e) {
240
+ return { ok: false, missing: [], error: String(e?.message ?? e) };
241
+ }
242
+ })();
231
243
  const { runWebhookRetryRound } = await import("./webhook.js");
232
244
  const retry = await runWebhookRetryRound(supabase).catch(() => ({ picked: 0 }));
233
- return Response.json({ ok: true, resource: "webhook-ensure", results, retried: retry.picked });
245
+ return Response.json({ ok: true, resource: "webhook-ensure", results, retried: retry.picked, shellRoutes });
234
246
  });
235
247
  }
@@ -0,0 +1,2 @@
1
+ export declare function normalizeShellBase(raw: unknown): string | null;
2
+ //# sourceMappingURL=shell-base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-base.d.ts","sourceRoot":"","sources":["../../src/lib/shell-base.ts"],"names":[],"mappings":"AAGA,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAkB9D"}
@@ -0,0 +1,31 @@
1
+ // Strenge normalisatie van een shell-host uit Central Auth.
2
+ // Alleen `https://<domeinnaam>`: kleine letters, geen pad/poort/query,
3
+ // geen localhost of IP-adres. Anders null (= niet schrijven).
4
+ export function normalizeShellBase(raw) {
5
+ if (typeof raw !== "string")
6
+ return null;
7
+ const t = raw.trim();
8
+ if (!t)
9
+ return null;
10
+ let u;
11
+ try {
12
+ u = new URL(/^[a-z]+:\/\//i.test(t) ? t : `https://${t}`);
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ if (u.protocol !== "https:")
18
+ return null;
19
+ if (u.port || u.username || u.password || u.search || u.hash)
20
+ return null;
21
+ if (u.pathname && u.pathname !== "/")
22
+ return null;
23
+ const host = u.hostname.toLowerCase();
24
+ if (host === "localhost" || host.endsWith(".localhost"))
25
+ return null;
26
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(host) || host.includes(":"))
27
+ return null;
28
+ if (!/^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/.test(host))
29
+ return null;
30
+ return `https://${host}`;
31
+ }
@@ -0,0 +1,25 @@
1
+ /** Moet gelijk zijn aan de gateway-routes in package.json (bewaakt door test). */
2
+ export declare const SHELL_GATEWAY_ROUTES: readonly ["/api/public/gateway/active-environment", "/api/public/gateway/auto-process", "/api/public/gateway/cached-list", "/api/public/gateway/code-list", "/api/public/gateway/cron/scan", "/api/public/gateway/diagnostics", "/api/public/gateway/floricode-records", "/api/public/gateway/fulfillment-order/send", "/api/public/gateway/fulfillment-order/status", "/api/public/gateway/media", "/api/public/gateway/media-upload", "/api/public/gateway/pdf", "/api/public/gateway/pdf/bulk", "/api/public/gateway/pdf/bulk-merged", "/api/public/gateway/product-characteristics", "/api/public/gateway/schema", "/api/public/gateway/supply-line", "/api/public/gateway/floriday-get", "/api/public/gateway/write", "/api/public/gateway/trade-item-version"];
3
+ export type ShellRouteState = {
4
+ path: string;
5
+ present: boolean;
6
+ status: number | null;
7
+ reason: string | null;
8
+ };
9
+ export type ShellRoutesReport = {
10
+ ok: boolean;
11
+ shellHost: string | null;
12
+ checkedAt: string;
13
+ missing: string[];
14
+ routes: ShellRouteState[];
15
+ advice: string | null;
16
+ error?: string;
17
+ };
18
+ /** Controleert alle gateway-routes op `base` (standaard: shell-host uit Central Auth). */
19
+ export declare function checkShellRoutes(opts?: {
20
+ base?: string | null;
21
+ force?: boolean;
22
+ }): Promise<ShellRoutesReport>;
23
+ /** Legt een ontbrekende-routes-uitkomst vast in het schrijflogboek (alleen bij ontbreken). */
24
+ export declare function logShellRoutesReport(supabase: any, report: ShellRoutesReport, reason: string): Promise<void>;
25
+ //# sourceMappingURL=shell-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-routes.d.ts","sourceRoot":"","sources":["../../src/lib/shell-routes.ts"],"names":[],"mappings":"AAYA,kFAAkF;AAClF,eAAO,MAAM,oBAAoB,suBAqBvB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAyBF,0FAA0F;AAC1F,wBAAsB,gBAAgB,CAAC,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA4CvH;AAED,8FAA8F;AAC9F,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBlH"}
@@ -0,0 +1,127 @@
1
+ // ============================================================================
2
+ // Controle: bedient de Shell de gateway-routes van deze module?
3
+ // ----------------------------------------------------------------------------
4
+ // De Shell biedt alleen de routes aan die in het geïnstalleerde pakket staan
5
+ // (package.json → flowselections.serverRoutes). Een nieuwe route bestaat daar
6
+ // pas na uitbrengen + installeren. Deze controle roept elke gateway-route op
7
+ // de shell-host aan ZONDER handtekening: onze eigen route antwoordt met een
8
+ // JSON-weigering (401/400), een ontbrekende route geeft de Shell-404 (HTML).
9
+ // Resultaat 5 minuten gecachet per shell-host. Nooit gegevens, nooit schrijven
10
+ // naar Floriday.
11
+ // ============================================================================
12
+ /** Moet gelijk zijn aan de gateway-routes in package.json (bewaakt door test). */
13
+ export const SHELL_GATEWAY_ROUTES = [
14
+ "/api/public/gateway/active-environment",
15
+ "/api/public/gateway/auto-process",
16
+ "/api/public/gateway/cached-list",
17
+ "/api/public/gateway/code-list",
18
+ "/api/public/gateway/cron/scan",
19
+ "/api/public/gateway/diagnostics",
20
+ "/api/public/gateway/floricode-records",
21
+ "/api/public/gateway/fulfillment-order/send",
22
+ "/api/public/gateway/fulfillment-order/status",
23
+ "/api/public/gateway/media",
24
+ "/api/public/gateway/media-upload",
25
+ "/api/public/gateway/pdf",
26
+ "/api/public/gateway/pdf/bulk",
27
+ "/api/public/gateway/pdf/bulk-merged",
28
+ "/api/public/gateway/product-characteristics",
29
+ "/api/public/gateway/schema",
30
+ "/api/public/gateway/supply-line",
31
+ "/api/public/gateway/floriday-get",
32
+ "/api/public/gateway/write",
33
+ "/api/public/gateway/trade-item-version",
34
+ ];
35
+ const TTL_MS = 5 * 60 * 1000;
36
+ const cache = new Map();
37
+ async function probe(base, path) {
38
+ try {
39
+ const res = await fetch(`${base}${path}`, {
40
+ method: "POST",
41
+ headers: { "content-type": "application/json" },
42
+ body: "{}",
43
+ signal: AbortSignal.timeout(8000),
44
+ });
45
+ const ct = res.headers.get("content-type") ?? "";
46
+ await res.text().catch(() => "");
47
+ const isHtml = ct.includes("text/html");
48
+ if (res.status === 404 || isHtml) {
49
+ return { path, present: false, status: res.status, reason: "Shell kent deze route niet (404/website-pagina)" };
50
+ }
51
+ return { path, present: true, status: res.status, reason: null };
52
+ }
53
+ catch (e) {
54
+ return { path, present: false, status: null, reason: `Niet bereikbaar: ${e?.message ?? e}` };
55
+ }
56
+ }
57
+ /** Controleert alle gateway-routes op `base` (standaard: shell-host uit Central Auth). */
58
+ export async function checkShellRoutes(opts = {}) {
59
+ let base = opts.base ?? null;
60
+ if (!base) {
61
+ try {
62
+ const { getShellHostFromCentral } = await import("./central-auth.server.js");
63
+ base = await getShellHostFromCentral();
64
+ }
65
+ catch (e) {
66
+ return {
67
+ ok: false, shellHost: null, checkedAt: new Date().toISOString(), missing: [], routes: [],
68
+ advice: null, error: `Shell-host niet op te vragen: ${e?.message ?? e}`,
69
+ };
70
+ }
71
+ }
72
+ if (!base) {
73
+ return {
74
+ ok: false, shellHost: null, checkedAt: new Date().toISOString(), missing: [], routes: [],
75
+ advice: null, error: "Central Auth geeft geen shell-host terug",
76
+ };
77
+ }
78
+ base = base.replace(/\/+$/, "");
79
+ const hit = cache.get(base);
80
+ if (!opts.force && hit && Date.now() - hit.at < TTL_MS)
81
+ return hit.report;
82
+ const routes = [];
83
+ for (let i = 0; i < SHELL_GATEWAY_ROUTES.length; i += 5) {
84
+ const chunk = SHELL_GATEWAY_ROUTES.slice(i, i + 5);
85
+ routes.push(...(await Promise.all(chunk.map((p) => probe(base, p)))));
86
+ }
87
+ const missing = routes.filter((r) => !r.present).map((r) => r.path);
88
+ const report = {
89
+ ok: missing.length === 0,
90
+ shellHost: base,
91
+ checkedAt: new Date().toISOString(),
92
+ missing,
93
+ routes,
94
+ advice: missing.length
95
+ ? "Ontbreekt in de Shell: de developer moet een nieuwe versie van de Floriday-module uitbrengen en in de Shell installeren."
96
+ : null,
97
+ };
98
+ if (missing.length) {
99
+ console.error(`[shell-routes] ${base} mist ${missing.length} route(s): ${missing.join(", ")}`);
100
+ }
101
+ cache.set(base, { at: Date.now(), report });
102
+ return report;
103
+ }
104
+ /** Legt een ontbrekende-routes-uitkomst vast in het schrijflogboek (alleen bij ontbreken). */
105
+ export async function logShellRoutesReport(supabase, report, reason) {
106
+ if (report.ok && !report.error)
107
+ return;
108
+ try {
109
+ await supabase.from("floriday_write_log").insert({
110
+ connection_id: null,
111
+ module: "floriday-authenticatie",
112
+ action: "shell-routes-check",
113
+ resource: "shell-routes",
114
+ floriday_id: null,
115
+ request: { reason, shellHost: report.shellHost },
116
+ response: { missing: report.missing, error: report.error ?? null },
117
+ http_status: 0,
118
+ ok: false,
119
+ error: report.error ?? `${report.missing.length} route(s) ontbreken in de Shell – ${report.advice}`,
120
+ idempotency_key: null,
121
+ caller: "shell-routes-check",
122
+ });
123
+ }
124
+ catch (e) {
125
+ console.error("[shell-routes] loggen mislukt:", e?.message ?? e);
126
+ }
127
+ }