@flowselections/core 1.0.29 → 1.0.31

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/README.md CHANGED
@@ -206,3 +206,53 @@ Ook: `resetPasswordRoute({ landing })` (`/reset-wachtwoord`), `landingRoute({ la
206
206
  `prepublishOnly` draait `build:pkg`, dat `dist/` eerst leegmaakt, opnieuw bouwt en met
207
207
  `scripts/check-pkg.mjs` controleert dat `dist/` precies het pakket bevat. `npm publish` publiceert dus nooit
208
208
  een preview-build.
209
+
210
+ ## Shell-adres en Central-koppeling (`./server`)
211
+
212
+ De door het dashboard gegenereerde globale request-middleware roept per verzoek aan:
213
+
214
+ ```ts
215
+ import { runShellRuntime } from "@flowselections/core/server";
216
+ await runShellRuntime(); // bestaand gedrag, geeft null terug; geen croncontrole zonder context
217
+ ```
218
+
219
+ Vereist `SUPABASE_URL`, `SB_SERVICE_ROLE_KEY`, `CENTRAL_AUTH_URL`, `CENTRAL_AUTH_SECRET`. Gooit nooit; de leessleutel wordt nooit gelogd of teruggegeven.
220
+
221
+ ### Centrale croncontrole (dashboardaansluiting)
222
+
223
+ Het dashboard past `system-files.js`, `buildBody`, voor het systeembestand
224
+ `src/lib/shell-runtime-middleware.ts` aan. Houd deze middleware als eerste:
225
+
226
+ ```ts
227
+ createMiddleware({ type: "request" }).server(async ({ request, pathname, next }) => {
228
+ const { runShellRuntime } = await import("@flowselections/core/server");
229
+ const denied = await runShellRuntime({ request, pathname }); return denied ?? next();
230
+ });
231
+ ```
232
+
233
+ `ensureCronKeys()` synchroniseert `SUPABASE_PUBLISHABLE_KEY ?? SUPABASE_ANON_KEY`
234
+ (zonder omringende aanhalingstekens) via `flowselections_set_anon_key`.
235
+ Central-koppeling en sleutelsynchronisatie wachten één uur na succes en vijf
236
+ minuten na een mislukking, per isolate. Geen sleutels in logs of responses.
237
+
238
+ `checkCronRequest(request)` controleert uitsluitend exacte paden uit
239
+ `flowselections_cron_paths()`, met `x-flowselections-cron-key` tegen
240
+ `flowselections_cron_secret()`. Native Web Crypto verifieert de vergelijking.
241
+ Paden, sleutel en configuratie worden vijf minuten gecachet; een afwijkende
242
+ sleutel veroorzaakt één herlezing vóór de beslissing. Gelijktijdige herlezingen
243
+ delen een Promise; daarna blijven herhaalde mismatches tot de volgende cacheverversing
244
+ zonder extra RPC's, om een verzoekenstorm te voorkomen. Ontbrekende RPC's, netwerkfouten of ongeldige antwoorden laten
245
+ verzoeken door en worden begrensd gemeld; deze laag vervangt geen modulebeveiliging.
246
+
247
+ `module_runtime_config` heeft `key`/`value`-rijen: `shell_base_url`,
248
+ `cron_auth_mode` (`compat` standaard, of `afdwingen`) en
249
+ `cron_auth_exempt_paths` (komma-gescheiden extra padprefixen). In `compat` komt
250
+ alleen een waarschuwing; in `afdwingen` een 401 vóór de handler. De drie vaste
251
+ uitzonderingsprefixen blijven altijd gelden, ook bij een lege configuratie:
252
+
253
+ - `/api/public/print/queue-tick`
254
+ - `/api/public/hooks/logistics-shipping-note-now`
255
+ - `/api/public/cron/ping`
256
+
257
+ De eigen engine-tick en `verkoop_cron_post` staan buiten deze centrale padenlijst.
258
+ Er worden geen requestheaders herschreven en geen modules gewijzigd.
@@ -0,0 +1,17 @@
1
+ /** URL metadata is only a correlation signal; Supabase validates the session. */
2
+ export type PasswordCallback = {
3
+ type: "invite" | "recovery";
4
+ sub: string;
5
+ };
6
+ export declare const PASSWORD_CALLBACK_WAIT_MS = 5000;
7
+ export declare function readPasswordCallback(hash: string): PasswordCallback | null;
8
+ /** Called immediately before createClient can consume/clear the URL hash. */
9
+ export declare function capturePasswordCallback(): void;
10
+ export declare function getPasswordCallback(): PasswordCallback | null;
11
+ export declare function clearPasswordCallback(): void;
12
+ export declare function matchingPasswordCallback(signal: PasswordCallback | null, session: {
13
+ user: {
14
+ id: string;
15
+ };
16
+ } | null): PasswordCallback["type"] | null;
17
+ //# sourceMappingURL=password-callback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-callback.d.ts","sourceRoot":"","sources":["../../src/core/auth/password-callback.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAG9C,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAiB1E;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,IAAI,IAAI,CAG9C;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,IAAI,CAE7D;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,GAAG,IAAI,EAC/B,OAAO,EAAE;IAAE,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,GACvC,gBAAgB,CAAC,MAAM,CAAC,GAAG,IAAI,CAEjC"}
@@ -0,0 +1,39 @@
1
+ export const PASSWORD_CALLBACK_WAIT_MS = 5000;
2
+ let callback = null;
3
+ export function readPasswordCallback(hash) {
4
+ const params = new URLSearchParams(hash.replace(/^#/, ""));
5
+ const type = params.get("type");
6
+ if (type !== "invite" && type !== "recovery")
7
+ return null;
8
+ try {
9
+ // Decode only the payload inline. Never retain the access token.
10
+ const payload = params.get("access_token")?.split(".")[1];
11
+ params.delete("access_token");
12
+ if (!payload)
13
+ return null;
14
+ const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
15
+ const bytes = Uint8Array.from(atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, "=")), (c) => c.charCodeAt(0));
16
+ const decoded = JSON.parse(new TextDecoder().decode(bytes));
17
+ if (typeof decoded !== "object" || decoded === null || !("sub" in decoded))
18
+ return null;
19
+ return typeof decoded.sub === "string" && decoded.sub.length > 0 ? { type, sub: decoded.sub } : null;
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ /** Called immediately before createClient can consume/clear the URL hash. */
26
+ export function capturePasswordCallback() {
27
+ if (typeof window === "undefined")
28
+ return;
29
+ callback = readPasswordCallback(window.location.hash);
30
+ }
31
+ export function getPasswordCallback() {
32
+ return callback;
33
+ }
34
+ export function clearPasswordCallback() {
35
+ callback = null;
36
+ }
37
+ export function matchingPasswordCallback(signal, session) {
38
+ return signal && session?.user.id === signal.sub ? signal.type : null;
39
+ }
@@ -23,7 +23,7 @@ export interface ModuleAccessContext {
23
23
  /**
24
24
  * Sinds 1.0.23. Moduletoegang voor een pad. Wacht max. 3 s; fout, time-out,
25
25
  * vrijgesteld pad of geen modulerij → `edit` (fail-open).
26
- * Zichtbaarheid, geen beveiliging.
26
+ * Superadmin → altijd `edit`; admin volgt de rechten. Zichtbaarheid, geen beveiliging.
27
27
  */
28
28
  export declare function resolveModuleAccess(path: string, opts?: {
29
29
  userId?: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"redirect.d.ts","sourceRoot":"","sources":["../../src/core/auth/redirect.ts"],"names":[],"mappings":"AAEA,OAAO,EAA6B,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAG5F;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAazE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED,oEAAoE;AACpE,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,eAAe,CAAC;CAC/B;AAUD;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CAAO,GACnF,OAAO,CAAC,eAAe,CAAC,CAM1B;AAED,yFAAyF;AACzF,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAC/E,OAAO,CAAC,OAAO,CAAC,CAElB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,EACtC,SAAS,SAAW,GACnB,IAAI,GAAG,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuB3D"}
1
+ {"version":3,"file":"redirect.d.ts","sourceRoot":"","sources":["../../src/core/auth/redirect.ts"],"names":[],"mappings":"AAEA,OAAO,EAA6B,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAG5F;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAazE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED,oEAAoE;AACpE,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,eAAe,CAAC;CAC/B;AAWD;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CAAO,GACnF,OAAO,CAAC,eAAe,CAAC,CAM1B;AAED,yFAAyF;AACzF,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAC/E,OAAO,CAAC,OAAO,CAAC,CAElB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,EACtC,SAAS,SAAW,GACnB,IAAI,GAAG,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuB3D"}
@@ -38,13 +38,14 @@ export function readRedirectParam(fallback) {
38
38
  function pathOf(location) {
39
39
  return location.pathname ?? location.href.split("?")[0].split("#")[0];
40
40
  }
41
+ /** Alleen superadmin heeft een vrijbrief voor modulerechten; admin volgt de rechten. */
41
42
  function isAdminRoles(roles) {
42
- return Array.isArray(roles) && (roles.includes("admin") || roles.includes("superadmin"));
43
+ return Array.isArray(roles) && roles.includes("superadmin");
43
44
  }
44
45
  /**
45
46
  * Sinds 1.0.23. Moduletoegang voor een pad. Wacht max. 3 s; fout, time-out,
46
47
  * vrijgesteld pad of geen modulerij → `edit` (fail-open).
47
- * Zichtbaarheid, geen beveiliging.
48
+ * Superadmin → altijd `edit`; admin volgt de rechten. Zichtbaarheid, geen beveiliging.
48
49
  */
49
50
  export async function resolveModuleAccess(path, opts = {}) {
50
51
  const key = navKey(path);
@@ -76,9 +76,9 @@ const coreCards = [
76
76
  // ─── Main export ──────────────────────────────────────────────────────────────
77
77
  export function InstellingenPage({ modules }) {
78
78
  // Sinds 1.0.23: modulekaarten erven het recht van hun module (of featureId).
79
- // Core-kaarten altijd. Laden/fout/admin → tonen. Zichtbaarheid, geen beveiliging.
79
+ // Core-kaarten altijd. Laden/fout/superadmin → tonen. Zichtbaarheid, geen beveiliging.
80
80
  const snap = usePermissionSnapshot();
81
- const filterOn = snap.status === "ready" && !snap.roles.includes("admin") && !snap.roles.includes("superadmin");
81
+ const filterOn = snap.status === "ready" && !snap.roles.includes("superadmin");
82
82
  const moduleCards = modules.flatMap((m) => m.settingsCards.filter((card) => !filterOn ||
83
83
  resolveLevel(snap.rows, card.moduleId ?? moduleKey(m), card.featureId ?? null) !== "none"));
84
84
  const allCards = [...coreCards, ...moduleCards].sort((a, b) => a.order - b.order);
@@ -1 +1 @@
1
- {"version":3,"file":"ResetWachtwoordPage.d.ts","sourceRoot":"","sources":["../../../src/core/components/layout/ResetWachtwoordPage.tsx"],"names":[],"mappings":"AASA,UAAU,wBAAwB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,UAAU,EAAE,SAAoB,EAAE,EAAE,wBAAwB,2CAwFjG"}
1
+ {"version":3,"file":"ResetWachtwoordPage.d.ts","sourceRoot":"","sources":["../../../src/core/components/layout/ResetWachtwoordPage.tsx"],"names":[],"mappings":"AAUA,UAAU,wBAAwB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,UAAU,EAAE,SAAoB,EAAE,EAAE,wBAAwB,2CA0IjG"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect } from "react";
2
+ import { useState, useEffect, useRef } from "react";
3
3
  import { useRouter } from "@tanstack/react-router";
4
4
  import { Button } from "../ui/button.js";
5
5
  import { Input } from "../ui/input.js";
@@ -7,23 +7,58 @@ import { Label } from "../ui/label.js";
7
7
  import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../ui/card.js";
8
8
  import { toast } from "sonner";
9
9
  import { supabase } from "../../supabase/client.js";
10
+ import { clearPasswordCallback, getPasswordCallback, matchingPasswordCallback, PASSWORD_CALLBACK_WAIT_MS } from "../../auth/password-callback.js";
10
11
  export function ResetWachtwoordPage({ redirectTo, loginPath = "/login" }) {
11
12
  const router = useRouter();
12
13
  const [password, setPassword] = useState("");
13
14
  const [confirmPassword, setConfirmPassword] = useState("");
14
15
  const [isLoading, setIsLoading] = useState(false);
15
- const [isReady, setIsReady] = useState(false);
16
+ const [callbackType, setCallbackType] = useState(null);
17
+ const [isWaiting, setIsWaiting] = useState(true);
18
+ const callback = useRef(null);
16
19
  useEffect(() => {
17
- const hash = window.location.hash;
18
- if (hash && hash.includes("type=recovery")) {
19
- setIsReady(true);
20
- }
21
- const { data: { subscription } } = supabase.auth.onAuthStateChange((event) => {
22
- if (event === "PASSWORD_RECOVERY") {
23
- setIsReady(true);
20
+ let active = true;
21
+ let accepted = false;
22
+ let timer;
23
+ // Accessing auth initializes the lazy client and captures the URL first.
24
+ const auth = supabase.auth;
25
+ callback.current = callback.current ?? getPasswordCallback();
26
+ const signal = callback.current;
27
+ const acceptSession = (session) => {
28
+ if (!active)
29
+ return;
30
+ const type = matchingPasswordCallback(signal, session);
31
+ setCallbackType(type);
32
+ if (type) {
33
+ accepted = true;
34
+ clearTimeout(timer);
35
+ clearPasswordCallback();
36
+ setIsWaiting(false);
37
+ }
38
+ else if (accepted) {
39
+ // A later sign-out or account switch invalidates this callback.
40
+ active = false;
41
+ callback.current = null;
42
+ setIsWaiting(false);
24
43
  }
44
+ };
45
+ const { data: { subscription } } = auth.onAuthStateChange((_event, session) => {
46
+ acceptSession(session);
25
47
  });
26
- return () => subscription.unsubscribe();
48
+ timer = setTimeout(() => {
49
+ active = false;
50
+ clearPasswordCallback();
51
+ callback.current = null;
52
+ setCallbackType(null);
53
+ setIsWaiting(false);
54
+ }, PASSWORD_CALLBACK_WAIT_MS);
55
+ void auth.getSession().then(({ data: { session }, error }) => {
56
+ if (!error)
57
+ acceptSession(session);
58
+ }).catch(() => {
59
+ // The bounded wait also covers session initialization/network failures.
60
+ });
61
+ return () => { active = false; clearTimeout(timer); clearPasswordCallback(); subscription.unsubscribe(); };
27
62
  }, []);
28
63
  const handleSubmit = async (e) => {
29
64
  e.preventDefault();
@@ -35,19 +70,34 @@ export function ResetWachtwoordPage({ redirectTo, loginPath = "/login" }) {
35
70
  toast.error("Wachtwoorden komen niet overeen");
36
71
  return;
37
72
  }
73
+ if (!callbackType)
74
+ return;
38
75
  setIsLoading(true);
76
+ const { data: { session }, error: sessionError } = await supabase.auth.getSession();
77
+ if (sessionError || !matchingPasswordCallback(callback.current, session)) {
78
+ clearPasswordCallback();
79
+ callback.current = null;
80
+ setCallbackType(null);
81
+ setIsLoading(false);
82
+ return;
83
+ }
39
84
  const { error } = await supabase.auth.updateUser({ password });
40
85
  setIsLoading(false);
41
86
  if (error) {
42
87
  toast.error("Fout bij het instellen van het nieuwe wachtwoord");
43
88
  }
44
89
  else {
90
+ clearPasswordCallback();
91
+ callback.current = null;
45
92
  toast.success("Wachtwoord succesvol gewijzigd!");
46
93
  await router.navigate({ href: redirectTo });
47
94
  }
48
95
  };
49
- if (!isReady) {
50
- return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-background p-4", children: _jsxs(Card, { className: "w-full max-w-md", children: [_jsxs(CardHeader, { className: "text-center", children: [_jsx(CardTitle, { "data-empty-state": true, className: "text-2xl", children: "Ongeldige link" }), _jsx(CardDescription, { children: "Deze reset-link is ongeldig of verlopen. Vraag een nieuwe aan via de inlogpagina." })] }), _jsx(CardFooter, { className: "justify-center", children: _jsx(Button, { onClick: () => router.navigate({ href: loginPath }), children: "Naar inlogpagina" }) })] }) }));
96
+ if (isWaiting) {
97
+ return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-background p-4", role: "status", "aria-label": "Link controleren", children: _jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-b-2 border-primary motion-reduce:animate-none" }) }));
98
+ }
99
+ if (!callbackType) {
100
+ return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-background p-4", children: _jsxs(Card, { className: "w-full max-w-md", children: [_jsxs(CardHeader, { className: "text-center", children: [_jsx(CardTitle, { "data-empty-state": true, className: "text-2xl", children: "Ongeldige link" }), _jsx(CardDescription, { children: "Deze link is verlopen of al gebruikt. Vraag een nieuwe uitnodiging aan, of gebruik Wachtwoord vergeten op de inlogpagina." })] }), _jsx(CardFooter, { className: "justify-center", children: _jsx(Button, { onClick: () => router.navigate({ href: loginPath }), children: "Naar inlogpagina" }) })] }) }));
51
101
  }
52
- return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-background p-4", children: _jsxs(Card, { className: "w-full max-w-md", children: [_jsxs(CardHeader, { className: "text-center", children: [_jsx(CardTitle, { className: "text-2xl", children: "Nieuw wachtwoord instellen" }), _jsx(CardDescription, { children: "Kies een nieuw wachtwoord voor je account" })] }), _jsxs("form", { onSubmit: handleSubmit, children: [_jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "password", children: "Nieuw wachtwoord" }), _jsx(Input, { id: "password", type: "password", value: password, onChange: (e) => setPassword(e.target.value), required: true, minLength: 6, maxLength: 128 })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "confirmPassword", children: "Bevestig wachtwoord" }), _jsx(Input, { id: "confirmPassword", type: "password", value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), required: true, minLength: 6, maxLength: 128 })] })] }), _jsx(CardFooter, { children: _jsx(Button, { type: "submit", className: "w-full", disabled: isLoading, children: isLoading ? "Opslaan..." : "Wachtwoord opslaan" }) })] })] }) }));
102
+ return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-background p-4", children: _jsxs(Card, { className: "w-full max-w-md", children: [_jsxs(CardHeader, { className: "text-center", children: [_jsx(CardTitle, { className: "text-2xl", children: callbackType === "invite" ? "Welkom!" : "Nieuw wachtwoord instellen" }), _jsx(CardDescription, { children: callbackType === "invite" ? "Kies een wachtwoord om je account te activeren." : "Kies een nieuw wachtwoord voor je account" })] }), _jsxs("form", { onSubmit: handleSubmit, children: [_jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "password", children: "Nieuw wachtwoord" }), _jsx(Input, { id: "password", type: "password", value: password, onChange: (e) => setPassword(e.target.value), required: true, minLength: 6, maxLength: 128 })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "confirmPassword", children: "Bevestig wachtwoord" }), _jsx(Input, { id: "confirmPassword", type: "password", value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), required: true, minLength: 6, maxLength: 128 })] })] }), _jsx(CardFooter, { children: _jsx(Button, { type: "submit", className: "w-full", disabled: isLoading, children: isLoading ? "Opslaan..." : "Wachtwoord opslaan" }) })] })] }) }));
53
103
  }
@@ -194,7 +194,7 @@ export declare function useMyModulePermissions(): {
194
194
  };
195
195
  /**
196
196
  * Rechtenniveau voor één module (optioneel één feature). Tijdens laden, bij
197
- * een fout en voor admin/superadmin: `edit`. Zichtbaarheid, geen beveiliging.
197
+ * een fout en voor superadmin: `edit`. Zichtbaarheid, geen beveiliging.
198
198
  */
199
199
  export declare function useModulePermission(moduleId: string, featureId?: string | null): {
200
200
  level: PermissionLevel;
@@ -221,7 +221,7 @@ export interface CanProps {
221
221
  /** Sinds 1.0.23. Rendert children als `useCan` toestaat. Zichtbaarheid, geen beveiliging. */
222
222
  export declare function Can({ module, feature, level, fallback, children }: CanProps): import("react").FunctionComponentElement<import("react").FragmentProps>;
223
223
  /**
224
- * ModuleListTransform die zijbalkitems met `none` weglaat. Laden/fout/admin →
224
+ * ModuleListTransform die zijbalkitems met `none` weglaat. Laden/fout/superadmin →
225
225
  * ongewijzigd. Zichtbaarheid, geen beveiliging.
226
226
  */
227
227
  export declare function useModulePermissionTransform(): ModuleListTransform;
@@ -1 +1 @@
1
- {"version":3,"file":"useModulePermission.d.ts","sourceRoot":"","sources":["../../src/core/permissions/useModulePermission.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAIL,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACrB,MAAM,cAAc,CAAC;AAGtB,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAiBzE;;;;;;;GAOG;AACH,wBAAgB,sBAAsB;;;IAuBlC,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAApE,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAApE,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAApE,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAApE,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAApE,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;EAIvE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;;;;;EAW9E;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,GAAE,eAAwB;;;;EAGlG;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,6FAA6F;AAC7F,wBAAgB,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAc,EAAE,QAAe,EAAE,QAAQ,EAAE,EAAE,QAAQ,2EAG3F;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,IAAI,mBAAmB,CASlE;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,OAAO,UAAO;;;EAQjF"}
1
+ {"version":3,"file":"useModulePermission.d.ts","sourceRoot":"","sources":["../../src/core/permissions/useModulePermission.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAIL,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACrB,MAAM,cAAc,CAAC;AAGtB,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAkBzE;;;;;;;GAOG;AACH,wBAAgB,sBAAsB;;;IAuBlC,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAApE,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAApE,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAApE,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAApE,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAApE,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;EAIvE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;;;;;EAW9E;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,GAAE,eAAwB;;;;EAGlG;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,6FAA6F;AAC7F,wBAAgB,GAAG,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAc,EAAE,QAAe,EAAE,QAAQ,EAAE,EAAE,QAAQ,2EAG3F;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,IAAI,mBAAmB,CASlE;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,OAAO,UAAO;;;EAQjF"}
@@ -9,7 +9,8 @@ function getFallbackClient() {
9
9
  return fallbackClient;
10
10
  }
11
11
  function rolesAreAdmin(roles) {
12
- return roles.includes("admin") || roles.includes("superadmin");
12
+ // Alleen superadmin heeft een vrijbrief; admin volgt de rechten.
13
+ return roles.includes("superadmin");
13
14
  }
14
15
  /** Snapshot → bekend? Laden = onbekend; fout/time-out = bekend (leeg → allow). */
15
16
  function isKnown(s) {
@@ -49,7 +50,7 @@ export function useMyModulePermissions() {
49
50
  }
50
51
  /**
51
52
  * Rechtenniveau voor één module (optioneel één feature). Tijdens laden, bij
52
- * een fout en voor admin/superadmin: `edit`. Zichtbaarheid, geen beveiliging.
53
+ * een fout en voor superadmin: `edit`. Zichtbaarheid, geen beveiliging.
53
54
  */
54
55
  export function useModulePermission(moduleId, featureId) {
55
56
  const snap = usePermissionSnapshot();
@@ -76,7 +77,7 @@ export function Can({ module, feature, level = "view", fallback = null, children
76
77
  return createElement(Fragment, null, allowed ? children : fallback);
77
78
  }
78
79
  /**
79
- * ModuleListTransform die zijbalkitems met `none` weglaat. Laden/fout/admin →
80
+ * ModuleListTransform die zijbalkitems met `none` weglaat. Laden/fout/superadmin →
80
81
  * ongewijzigd. Zichtbaarheid, geen beveiliging.
81
82
  */
82
83
  export function useModulePermissionTransform() {
@@ -8,4 +8,5 @@ export { getRoleFlags } from "../auth/roles.js";
8
8
  export { handoverExchangeHandler, HANDOVER_MAX_BODY_BYTES, HANDOVER_MAX_CODE_LENGTH, } from "./handover-handler.js";
9
9
  export type { HandoverExchangeHandlerOptions } from "./handover-handler.js";
10
10
  export type { PermissionLevel } from "./permissions.server.js";
11
+ export { getShellBaseUrl, applyShellBaseUrl, ensureCentralLink, ensureCronKeys, checkCronRequest, runShellRuntime, normalizeShellBaseUrl, } from "./shell-runtime.js";
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACpG,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAClF,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACpG,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAClF,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,EACrB,2BAA2B,EAC3B,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI/D,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,qBAAqB,GACtB,MAAM,oBAAoB,CAAC"}
@@ -6,3 +6,6 @@ export { CentralAuthConfigError, ProviderNotConnectedError, ProviderUpstreamErro
6
6
  export { requireModulePermission, isSuperadminServer } from "./permissions.server.js";
7
7
  export { getRoleFlags } from "../auth/roles.js";
8
8
  export { handoverExchangeHandler, HANDOVER_MAX_BODY_BYTES, HANDOVER_MAX_CODE_LENGTH, } from "./handover-handler.js";
9
+ // Shell-adres en Central-koppeling (alleen server; aangeroepen door de
10
+ // door het dashboard gegenereerde globale request-middleware).
11
+ export { getShellBaseUrl, applyShellBaseUrl, ensureCentralLink, ensureCronKeys, checkCronRequest, runShellRuntime, normalizeShellBaseUrl, } from "./shell-runtime.js";
@@ -0,0 +1,30 @@
1
+ /** Absolute https-origin zonder pad, of null. */
2
+ export declare function normalizeShellBaseUrl(raw: unknown): string | null;
3
+ /**
4
+ * Het shell-adres uit de klantdatabase (60 s cache per isolate). Bij een fout:
5
+ * de laatst bekende waarde plus waarschuwing. Valt nooit terug op het adres
6
+ * van het binnenkomende verzoek.
7
+ */
8
+ export declare function getShellBaseUrl(): Promise<string | null>;
9
+ /**
10
+ * Zet process.env.PUBLIC_BASE_URL uit de database. De database wint; een
11
+ * afwijkende env-waarde geeft een waarschuwing. Zonder databasewaarde blijft
12
+ * de env ongemoeid (host-afhankelijke code faalt zoals nu).
13
+ */
14
+ export declare function applyShellBaseUrl(): Promise<string | null>;
15
+ /**
16
+ * Na succes één uur wachten per isolate; na een fout vijf minuten.
17
+ */
18
+ export declare function ensureCentralLink(): Promise<void>;
19
+ /** Synchroniseert de shell-publishable key; uur na succes, vijf minuten na fout. */
20
+ export declare function ensureCronKeys(): Promise<void>;
21
+ /** Alleen geregistreerde cronpaden; alle lees-/runtimefouten laten door. */
22
+ export declare function checkCronRequest(request: Request): Promise<Response | null>;
23
+ /** Zonder context hetzelfde gedrag als voorheen. Alleen bewuste cronweigering geeft 401. */
24
+ export declare function runShellRuntime(ctx?: {
25
+ request: Request;
26
+ pathname: string;
27
+ }): Promise<Response | null>;
28
+ /** Alleen voor tests. */
29
+ export declare function __resetShellRuntimeForTests(): void;
30
+ //# sourceMappingURL=shell-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-runtime.d.ts","sourceRoot":"","sources":["../../src/core/server/shell-runtime.ts"],"names":[],"mappings":"AAqEA,iDAAiD;AACjD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CASjE;AAmBD;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqB9D;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYhE;AA+DD;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAgBvD;AA4BD,oFAAoF;AACpF,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAWpD;AAyFD,4EAA4E;AAC5E,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAuBjF;AAED,4FAA4F;AAC5F,wBAAsB,eAAe,CAAC,GAAG,CAAC,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAU5G;AAED,yBAAyB;AACzB,wBAAgB,2BAA2B,SAW1C"}
@@ -0,0 +1,410 @@
1
+ // Server-only: shell-adres en Central-koppeling via de klantdatabase.
2
+ // Uitsluitend bereikbaar via het "./server"-subpad. Geen serverpakket-imports:
3
+ // alleen fetch en process.env, gelezen binnen de functies.
4
+ //
5
+ // - getShellBaseUrl(): module_runtime_config.shell_base_url, 60 s cache per isolate.
6
+ // - applyShellBaseUrl(): zet process.env.PUBLIC_BASE_URL per verzoek (database wint).
7
+ // - ensureCentralLink(): hooguit eens per uur per isolate; haalt zo nodig de
8
+ // (idempotente) leessleutel bij Central en geeft hem aan de database.
9
+ // - runShellRuntime(): beide, bedoeld voor de door het dashboard gegenereerde
10
+ // globale request-middleware.
11
+ //
12
+ // De leessleutel wordt nooit gelogd, nooit teruggegeven en nooit bewaard.
13
+ const BASE_URL_TTL_MS = 60000;
14
+ const LINK_INTERVAL_MS = 60 * 60000;
15
+ const TIMEOUT_MS = 5000;
16
+ const WARN_INTERVAL_MS = 5 * 60000;
17
+ const CRON_TTL_MS = 5 * 60000;
18
+ let baseUrlCache = null;
19
+ let baseUrlInflight = null;
20
+ let nextLinkCheck = 0;
21
+ let linkInflight = null;
22
+ let nextCronKeyCheck = 0;
23
+ let cronKeyInflight = null;
24
+ let cronCache = null;
25
+ let cronInflight = null;
26
+ let comparisonKey = null;
27
+ const DEFAULT_EXEMPT = [
28
+ "/api/public/print/queue-tick",
29
+ "/api/public/hooks/logistics-shipping-note-now",
30
+ "/api/public/cron/ping",
31
+ ];
32
+ const lastWarn = new Map();
33
+ function warn(key, message, detail) {
34
+ const now = Date.now();
35
+ const last = lastWarn.get(key);
36
+ if (last !== undefined && now - last < WARN_INTERVAL_MS)
37
+ return;
38
+ lastWarn.set(key, now);
39
+ console.warn(`[shell-runtime] ${message}`, detail ?? {});
40
+ }
41
+ function dbConfig() {
42
+ const url = process.env.SUPABASE_URL;
43
+ const key = process.env.SB_SERVICE_ROLE_KEY;
44
+ if (!url || !key)
45
+ return null;
46
+ return { url: url.replace(/\/+$/, ""), key };
47
+ }
48
+ function dbHeaders(key) {
49
+ const h = { apikey: key, "Content-Type": "application/json", Accept: "application/json" };
50
+ // Oude JWT-sleutels als Bearer; nieuwe sb_secret_-sleutels zijn geen JWT.
51
+ if (key.split(".").length === 3)
52
+ h.Authorization = `Bearer ${key}`;
53
+ return h;
54
+ }
55
+ async function timedFetch(url, init) {
56
+ const ctrl = new AbortController();
57
+ const t = setTimeout(() => ctrl.abort(), TIMEOUT_MS);
58
+ try {
59
+ return await fetch(url, { ...init, signal: ctrl.signal, redirect: "error" });
60
+ }
61
+ finally {
62
+ clearTimeout(t);
63
+ }
64
+ }
65
+ /** Absolute https-origin zonder pad, of null. */
66
+ export function normalizeShellBaseUrl(raw) {
67
+ if (typeof raw !== "string" || !raw.trim())
68
+ return null;
69
+ try {
70
+ const u = new URL(raw.trim());
71
+ if (u.protocol !== "https:" || u.username || u.password)
72
+ return null;
73
+ return u.origin;
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
79
+ async function readShellBaseUrl() {
80
+ const cfg = dbConfig();
81
+ if (!cfg) {
82
+ warn("config", "SUPABASE_URL of SB_SERVICE_ROLE_KEY ontbreekt; shell-adres onbekend");
83
+ return null;
84
+ }
85
+ const res = await timedFetch(`${cfg.url}/rest/v1/module_runtime_config?select=value&key=eq.shell_base_url&limit=1`, { method: "GET", headers: dbHeaders(cfg.key) });
86
+ if (!res.ok)
87
+ throw new Error(`database ${res.status}`);
88
+ const rows = (await res.json());
89
+ const value = normalizeShellBaseUrl(Array.isArray(rows) ? rows[0]?.value : null);
90
+ if (!value)
91
+ warn("empty", "module_runtime_config.shell_base_url ontbreekt of is geen https-adres");
92
+ return value;
93
+ }
94
+ /**
95
+ * Het shell-adres uit de klantdatabase (60 s cache per isolate). Bij een fout:
96
+ * de laatst bekende waarde plus waarschuwing. Valt nooit terug op het adres
97
+ * van het binnenkomende verzoek.
98
+ */
99
+ export async function getShellBaseUrl() {
100
+ const now = Date.now();
101
+ if (baseUrlCache && now - baseUrlCache.at < BASE_URL_TTL_MS)
102
+ return baseUrlCache.value;
103
+ if (baseUrlInflight)
104
+ return baseUrlInflight;
105
+ baseUrlInflight = (async () => {
106
+ try {
107
+ const value = await readShellBaseUrl();
108
+ baseUrlCache = { value, at: Date.now() };
109
+ return value;
110
+ }
111
+ catch (e) {
112
+ warn("read", "shell-adres lezen mislukt; laatst bekende waarde gebruikt", {
113
+ error: e instanceof Error ? e.message : "onbekend",
114
+ });
115
+ const last = baseUrlCache?.value ?? null;
116
+ baseUrlCache = { value: last, at: Date.now() };
117
+ return last;
118
+ }
119
+ finally {
120
+ baseUrlInflight = null;
121
+ }
122
+ })();
123
+ return baseUrlInflight;
124
+ }
125
+ /**
126
+ * Zet process.env.PUBLIC_BASE_URL uit de database. De database wint; een
127
+ * afwijkende env-waarde geeft een waarschuwing. Zonder databasewaarde blijft
128
+ * de env ongemoeid (host-afhankelijke code faalt zoals nu).
129
+ */
130
+ export async function applyShellBaseUrl() {
131
+ const value = await getShellBaseUrl();
132
+ if (!value)
133
+ return null;
134
+ const current = process.env.PUBLIC_BASE_URL;
135
+ if (current && normalizeShellBaseUrl(current) !== value) {
136
+ warn("mismatch", "PUBLIC_BASE_URL wijkt af van de database; databasewaarde gebruikt", {
137
+ env: hostOnly(current),
138
+ database: hostOnly(value),
139
+ });
140
+ }
141
+ process.env.PUBLIC_BASE_URL = value;
142
+ return value;
143
+ }
144
+ async function rpc(cfg, name, body) {
145
+ return timedFetch(`${cfg.url}/rest/v1/rpc/${name}`, {
146
+ method: "POST",
147
+ headers: dbHeaders(cfg.key),
148
+ body: JSON.stringify(body),
149
+ });
150
+ }
151
+ function hostOnly(value) {
152
+ try {
153
+ return new URL(value).host;
154
+ }
155
+ catch {
156
+ return null;
157
+ }
158
+ }
159
+ async function linkOnce() {
160
+ const cfg = dbConfig();
161
+ const centralUrl = process.env.CENTRAL_AUTH_URL?.replace(/\/+$/, "");
162
+ const secret = process.env.CENTRAL_AUTH_SECRET;
163
+ if (!cfg || !centralUrl || !secret) {
164
+ warn("link-config", "configuratie voor de Central-koppeling ontbreekt");
165
+ return false;
166
+ }
167
+ const statusRes = await rpc(cfg, "flowselections_central_status", {});
168
+ if (!statusRes.ok) {
169
+ warn("link-status", "flowselections_central_status niet beschikbaar", { status: statusRes.status });
170
+ return false;
171
+ }
172
+ const raw = await statusRes.json();
173
+ const status = (Array.isArray(raw) ? raw[0] : raw);
174
+ if (!status || typeof status.token_aanwezig !== "boolean") {
175
+ warn("link-status", "flowselections_central_status gaf geen resultaat");
176
+ return false;
177
+ }
178
+ if (status.token_aanwezig !== false && status.laatste_status !== "http_401")
179
+ return true;
180
+ const tokenRes = await timedFetch(`${centralUrl}/api/shell-host/read-token`, {
181
+ method: "POST",
182
+ headers: { Authorization: `Bearer ${secret}`, Accept: "application/json" },
183
+ });
184
+ if (!tokenRes.ok) {
185
+ warn("link-token", "leessleutel ophalen bij Central mislukt", { status: tokenRes.status });
186
+ return false;
187
+ }
188
+ const { token } = (await tokenRes.json());
189
+ if (typeof token !== "string" || !token) {
190
+ warn("link-token", "Central gaf geen bruikbare leessleutel");
191
+ return false;
192
+ }
193
+ const setRes = await rpc(cfg, "flowselections_set_central_link", {
194
+ p_central_url: centralUrl,
195
+ p_read_token: token,
196
+ });
197
+ if (!setRes.ok) {
198
+ warn("link-set", "flowselections_set_central_link mislukt", { status: setRes.status });
199
+ return false;
200
+ }
201
+ return true;
202
+ }
203
+ /**
204
+ * Na succes één uur wachten per isolate; na een fout vijf minuten.
205
+ */
206
+ export async function ensureCentralLink() {
207
+ const now = Date.now();
208
+ if (linkInflight)
209
+ return linkInflight;
210
+ if (now < nextLinkCheck)
211
+ return;
212
+ linkInflight = linkOnce()
213
+ .then((ok) => { nextLinkCheck = Date.now() + (ok ? LINK_INTERVAL_MS : CRON_TTL_MS); })
214
+ .catch((e) => {
215
+ nextLinkCheck = Date.now() + CRON_TTL_MS;
216
+ warn("link-error", "Central-koppeling controleren mislukt", {
217
+ error: e instanceof Error ? e.name : "onbekend",
218
+ });
219
+ })
220
+ .finally(() => {
221
+ linkInflight = null;
222
+ });
223
+ return linkInflight;
224
+ }
225
+ async function cronKeysOnce() {
226
+ const cfg = dbConfig();
227
+ const raw = process.env.SUPABASE_PUBLISHABLE_KEY ?? process.env.SUPABASE_ANON_KEY;
228
+ if (!cfg || !raw) {
229
+ warn("cron-key-config", "configuratie voor cronsleutels ontbreekt");
230
+ return false;
231
+ }
232
+ const key = raw.trim().replace(/^(["'])(.*)\1$/, "$2").trim();
233
+ if (!key) {
234
+ warn("cron-key-config", "publishable key ontbreekt");
235
+ return false;
236
+ }
237
+ const res = await rpc(cfg, "flowselections_set_anon_key", { p_key: key });
238
+ if (!res.ok) {
239
+ warn("cron-key-rpc", "flowselections_set_anon_key niet beschikbaar of mislukt", { status: res.status });
240
+ return false;
241
+ }
242
+ const result = await res.json();
243
+ if (result !== "ongewijzigd" && result !== "bijgewerkt") {
244
+ warn("cron-key-result", "flowselections_set_anon_key gaf geen geldig resultaat");
245
+ return false;
246
+ }
247
+ if (result === "bijgewerkt")
248
+ console.info("[shell-runtime] cronsleutel bijgewerkt");
249
+ return true;
250
+ }
251
+ /** Synchroniseert de shell-publishable key; uur na succes, vijf minuten na fout. */
252
+ export async function ensureCronKeys() {
253
+ if (cronKeyInflight)
254
+ return cronKeyInflight;
255
+ if (Date.now() < nextCronKeyCheck)
256
+ return;
257
+ cronKeyInflight = cronKeysOnce()
258
+ .then((ok) => { nextCronKeyCheck = Date.now() + (ok ? LINK_INTERVAL_MS : CRON_TTL_MS); })
259
+ .catch(() => {
260
+ nextCronKeyCheck = Date.now() + CRON_TTL_MS;
261
+ warn("cron-key-error", "cronsleutel synchroniseren mislukt");
262
+ })
263
+ .finally(() => { cronKeyInflight = null; });
264
+ return cronKeyInflight;
265
+ }
266
+ async function readCronConfig() {
267
+ const cfg = dbConfig();
268
+ if (!cfg) {
269
+ warn("cron-config", "databaseconfiguratie voor croncontrole ontbreekt");
270
+ return null;
271
+ }
272
+ const pathsRes = await rpc(cfg, "flowselections_cron_paths", {});
273
+ if (!pathsRes.ok) {
274
+ warn("cron-paths", "flowselections_cron_paths niet beschikbaar", { status: pathsRes.status });
275
+ return null;
276
+ }
277
+ const paths = await pathsRes.json();
278
+ if (!Array.isArray(paths) || !paths.every((p) => typeof p === "string" && p.startsWith("/"))) {
279
+ warn("cron-paths-result", "cronpaden hebben een ongeldig formaat");
280
+ return null;
281
+ }
282
+ if (!paths.length)
283
+ return { paths: [], secret: "", mode: "compat", exempt: DEFAULT_EXEMPT };
284
+ const [secretRes, configRes] = await Promise.all([
285
+ rpc(cfg, "flowselections_cron_secret", {}),
286
+ timedFetch(`${cfg.url}/rest/v1/module_runtime_config?select=key,value&key=in.(cron_auth_mode,cron_auth_exempt_paths)`, { method: "GET", headers: dbHeaders(cfg.key) }),
287
+ ]);
288
+ if (!secretRes.ok || !configRes.ok) {
289
+ warn("cron-read", "cronsleutel of cronconfiguratie niet beschikbaar", {
290
+ secretStatus: secretRes.status, configStatus: configRes.status,
291
+ });
292
+ return null;
293
+ }
294
+ const secret = await secretRes.json();
295
+ const rows = await configRes.json();
296
+ if (typeof secret !== "string" || !secret || !Array.isArray(rows)) {
297
+ warn("cron-result", "cronconfiguratie heeft een ongeldig formaat");
298
+ return null;
299
+ }
300
+ const config = new Map();
301
+ for (const row of rows) {
302
+ if (!row || typeof row !== "object" || typeof row.key !== "string" || typeof row.value !== "string") {
303
+ warn("cron-result", "cronconfiguratie heeft een ongeldig formaat");
304
+ return null;
305
+ }
306
+ config.set(row.key, row.value);
307
+ }
308
+ const mode = config.get("cron_auth_mode") ?? "compat";
309
+ if (mode !== "compat" && mode !== "afdwingen") {
310
+ warn("cron-mode", "onbekende cronmodus; controle overgeslagen");
311
+ return null;
312
+ }
313
+ const extra = (config.get("cron_auth_exempt_paths") ?? "").split(",").map((p) => p.trim()).filter(Boolean);
314
+ if (extra.some((p) => !p.startsWith("/"))) {
315
+ warn("cron-exempt", "ongeldig uitzonderingsprefix; controle overgeslagen");
316
+ return null;
317
+ }
318
+ return { paths, secret, mode, exempt: [...DEFAULT_EXEMPT, ...extra] };
319
+ }
320
+ async function getCronConfig(force = false, previous) {
321
+ if (cronInflight)
322
+ return cronInflight;
323
+ if (force && cronCache && Date.now() - cronCache.at < CRON_TTL_MS &&
324
+ (cronCache.value !== previous || cronCache.mismatchRefreshed))
325
+ return cronCache.value;
326
+ if (!force && cronCache && Date.now() - cronCache.at < CRON_TTL_MS)
327
+ return cronCache.value;
328
+ cronCache = null;
329
+ cronInflight = readCronConfig()
330
+ .catch(() => {
331
+ warn("cron-error", "croncontrole lezen mislukt");
332
+ return null;
333
+ })
334
+ .then((value) => {
335
+ cronCache = { value, at: Date.now(), mismatchRefreshed: force };
336
+ return value;
337
+ })
338
+ .finally(() => { cronInflight = null; });
339
+ return cronInflight;
340
+ }
341
+ // Native Web Crypto verifies a fixed-size HMAC tag in constant time. No Node imports.
342
+ async function sameSecret(expected, supplied) {
343
+ comparisonKey ?? (comparisonKey = crypto.subtle.generateKey({ name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"]));
344
+ const key = await comparisonKey;
345
+ const encoder = new TextEncoder();
346
+ const tag = await crypto.subtle.sign("HMAC", key, encoder.encode(expected));
347
+ return crypto.subtle.verify("HMAC", key, tag, encoder.encode(supplied));
348
+ }
349
+ function needsCronCheck(config, pathname) {
350
+ return config.paths.includes(pathname) && !config.exempt.some((prefix) => pathname.startsWith(prefix));
351
+ }
352
+ /** Alleen geregistreerde cronpaden; alle lees-/runtimefouten laten door. */
353
+ export async function checkCronRequest(request) {
354
+ try {
355
+ const pathname = new URL(request.url).pathname;
356
+ // Permanently mixed-use routes never require a cron secret, even on DB errors.
357
+ if (DEFAULT_EXEMPT.some((prefix) => pathname.startsWith(prefix)))
358
+ return null;
359
+ let config = await getCronConfig();
360
+ if (!config || !needsCronCheck(config, pathname))
361
+ return null;
362
+ const supplied = request.headers.get("x-flowselections-cron-key");
363
+ if (supplied !== null && await sameSecret(config.secret, supplied))
364
+ return null;
365
+ if (supplied !== null) {
366
+ config = await getCronConfig(true, config);
367
+ if (!config || !needsCronCheck(config, pathname))
368
+ return null;
369
+ if (await sameSecret(config.secret, supplied))
370
+ return null;
371
+ }
372
+ if (config.mode === "afdwingen")
373
+ return new Response("Unauthorized", { status: 401 });
374
+ warn("cron-auth", "cronaanroep zonder geldige platformsleutel", {
375
+ path: pathname, reason: supplied === null ? "ontbreekt" : "wijkt af",
376
+ });
377
+ return null;
378
+ }
379
+ catch {
380
+ warn("cron-check", "croncontrole mislukt; verzoek doorgelaten");
381
+ return null;
382
+ }
383
+ }
384
+ /** Zonder context hetzelfde gedrag als voorheen. Alleen bewuste cronweigering geeft 401. */
385
+ export async function runShellRuntime(ctx) {
386
+ try {
387
+ await Promise.allSettled([
388
+ applyShellBaseUrl(), ensureCentralLink(), ...(ctx ? [ensureCronKeys()] : []),
389
+ ]);
390
+ if (ctx)
391
+ return await checkCronRequest(ctx.request);
392
+ }
393
+ catch {
394
+ // Geen onbedoelde weigering bij configuratie- of runtimefouten.
395
+ }
396
+ return null;
397
+ }
398
+ /** Alleen voor tests. */
399
+ export function __resetShellRuntimeForTests() {
400
+ baseUrlCache = null;
401
+ baseUrlInflight = null;
402
+ nextLinkCheck = 0;
403
+ linkInflight = null;
404
+ nextCronKeyCheck = 0;
405
+ cronKeyInflight = null;
406
+ cronCache = null;
407
+ cronInflight = null;
408
+ comparisonKey = null;
409
+ lastWarn.clear();
410
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/supabase/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/supabase/client.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,13 @@
1
1
  // This file is automatically generated. Do not edit it directly.
2
2
  import { createClient } from '@supabase/supabase-js';
3
+ import { capturePasswordCallback } from '../auth/password-callback.js';
3
4
  function createSupabaseClient() {
4
5
  const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL || process.env.SUPABASE_URL;
5
6
  const SUPABASE_PUBLISHABLE_KEY = import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY || process.env.SUPABASE_PUBLISHABLE_KEY;
6
7
  if (!SUPABASE_URL || !SUPABASE_PUBLISHABLE_KEY) {
7
8
  throw new Error('Missing Supabase environment variables. Ensure VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY (or SUPABASE_URL/SUPABASE_PUBLISHABLE_KEY) are set.');
8
9
  }
10
+ capturePasswordCallback();
9
11
  return createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, {
10
12
  auth: {
11
13
  storage: typeof window !== 'undefined' ? window.localStorage : undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/core",
3
- "version": "1.0.29",
3
+ "version": "1.0.31",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -99,7 +99,7 @@
99
99
  },
100
100
  "devDependencies": {
101
101
  "@eslint/js": "^9.32.0",
102
- "@lovable.dev/vite-tanstack-config": "2.25.3",
102
+ "@lovable.dev/vite-tanstack-config": "2.26.5",
103
103
  "@types/node": "^22.16.5",
104
104
  "@types/react": "^19.2.0",
105
105
  "@types/react-dom": "^19.2.0",