@flowselections/core 1.0.30 → 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 +40 -1
- package/dist/auth/password-callback.d.ts +17 -0
- package/dist/auth/password-callback.d.ts.map +1 -0
- package/dist/auth/password-callback.js +39 -0
- package/dist/components/layout/ResetWachtwoordPage.d.ts.map +1 -1
- package/dist/components/layout/ResetWachtwoordPage.js +63 -13
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/shell-runtime.d.ts +10 -4
- package/dist/server/shell-runtime.d.ts.map +1 -1
- package/dist/server/shell-runtime.js +213 -22
- package/dist/supabase/client.d.ts.map +1 -1
- package/dist/supabase/client.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -213,7 +213,46 @@ De door het dashboard gegenereerde globale request-middleware roept per verzoek
|
|
|
213
213
|
|
|
214
214
|
```ts
|
|
215
215
|
import { runShellRuntime } from "@flowselections/core/server";
|
|
216
|
-
await runShellRuntime(); //
|
|
216
|
+
await runShellRuntime(); // bestaand gedrag, geeft null terug; geen croncontrole zonder context
|
|
217
217
|
```
|
|
218
218
|
|
|
219
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
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResetWachtwoordPage.d.ts","sourceRoot":"","sources":["../../../src/core/components/layout/ResetWachtwoordPage.tsx"],"names":[],"mappings":"
|
|
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 [
|
|
16
|
+
const [callbackType, setCallbackType] = useState(null);
|
|
17
|
+
const [isWaiting, setIsWaiting] = useState(true);
|
|
18
|
+
const callback = useRef(null);
|
|
16
19
|
useEffect(() => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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 (
|
|
50
|
-
return (_jsx("div", { className: "min-h-screen flex items-center justify-center bg-background p-4",
|
|
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
|
}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -8,5 +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, runShellRuntime, normalizeShellBaseUrl, } from "./shell-runtime.js";
|
|
11
|
+
export { getShellBaseUrl, applyShellBaseUrl, ensureCentralLink, ensureCronKeys, checkCronRequest, runShellRuntime, normalizeShellBaseUrl, } from "./shell-runtime.js";
|
|
12
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;AAI/D,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,oBAAoB,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"}
|
package/dist/server/index.js
CHANGED
|
@@ -8,4 +8,4 @@ export { getRoleFlags } from "../auth/roles.js";
|
|
|
8
8
|
export { handoverExchangeHandler, HANDOVER_MAX_BODY_BYTES, HANDOVER_MAX_CODE_LENGTH, } from "./handover-handler.js";
|
|
9
9
|
// Shell-adres en Central-koppeling (alleen server; aangeroepen door de
|
|
10
10
|
// door het dashboard gegenereerde globale request-middleware).
|
|
11
|
-
export { getShellBaseUrl, applyShellBaseUrl, ensureCentralLink, runShellRuntime, normalizeShellBaseUrl, } from "./shell-runtime.js";
|
|
11
|
+
export { getShellBaseUrl, applyShellBaseUrl, ensureCentralLink, ensureCronKeys, checkCronRequest, runShellRuntime, normalizeShellBaseUrl, } from "./shell-runtime.js";
|
|
@@ -13,12 +13,18 @@ export declare function getShellBaseUrl(): Promise<string | null>;
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function applyShellBaseUrl(): Promise<string | null>;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
* doen, waarschuwen, niet opnieuw tot het volgende uur.
|
|
16
|
+
* Na succes één uur wachten per isolate; na een fout vijf minuten.
|
|
18
17
|
*/
|
|
19
18
|
export declare function ensureCentralLink(): Promise<void>;
|
|
20
|
-
/**
|
|
21
|
-
export declare function
|
|
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>;
|
|
22
28
|
/** Alleen voor tests. */
|
|
23
29
|
export declare function __resetShellRuntimeForTests(): void;
|
|
24
30
|
//# sourceMappingURL=shell-runtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-runtime.d.ts","sourceRoot":"","sources":["../../src/core/server/shell-runtime.ts"],"names":[],"mappings":"
|
|
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"}
|
|
@@ -14,14 +14,26 @@ const BASE_URL_TTL_MS = 60000;
|
|
|
14
14
|
const LINK_INTERVAL_MS = 60 * 60000;
|
|
15
15
|
const TIMEOUT_MS = 5000;
|
|
16
16
|
const WARN_INTERVAL_MS = 5 * 60000;
|
|
17
|
+
const CRON_TTL_MS = 5 * 60000;
|
|
17
18
|
let baseUrlCache = null;
|
|
18
19
|
let baseUrlInflight = null;
|
|
19
|
-
let
|
|
20
|
+
let nextLinkCheck = 0;
|
|
20
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
|
+
];
|
|
21
32
|
const lastWarn = new Map();
|
|
22
33
|
function warn(key, message, detail) {
|
|
23
34
|
const now = Date.now();
|
|
24
|
-
|
|
35
|
+
const last = lastWarn.get(key);
|
|
36
|
+
if (last !== undefined && now - last < WARN_INTERVAL_MS)
|
|
25
37
|
return;
|
|
26
38
|
lastWarn.set(key, now);
|
|
27
39
|
console.warn(`[shell-runtime] ${message}`, detail ?? {});
|
|
@@ -70,11 +82,11 @@ async function readShellBaseUrl() {
|
|
|
70
82
|
warn("config", "SUPABASE_URL of SB_SERVICE_ROLE_KEY ontbreekt; shell-adres onbekend");
|
|
71
83
|
return null;
|
|
72
84
|
}
|
|
73
|
-
const res = await timedFetch(`${cfg.url}/rest/v1/module_runtime_config?select=shell_base_url&limit=1`, { method: "GET", headers: dbHeaders(cfg.key) });
|
|
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) });
|
|
74
86
|
if (!res.ok)
|
|
75
87
|
throw new Error(`database ${res.status}`);
|
|
76
88
|
const rows = (await res.json());
|
|
77
|
-
const value = normalizeShellBaseUrl(Array.isArray(rows) ? rows[0]?.
|
|
89
|
+
const value = normalizeShellBaseUrl(Array.isArray(rows) ? rows[0]?.value : null);
|
|
78
90
|
if (!value)
|
|
79
91
|
warn("empty", "module_runtime_config.shell_base_url ontbreekt of is geen https-adres");
|
|
80
92
|
return value;
|
|
@@ -122,8 +134,8 @@ export async function applyShellBaseUrl() {
|
|
|
122
134
|
const current = process.env.PUBLIC_BASE_URL;
|
|
123
135
|
if (current && normalizeShellBaseUrl(current) !== value) {
|
|
124
136
|
warn("mismatch", "PUBLIC_BASE_URL wijkt af van de database; databasewaarde gebruikt", {
|
|
125
|
-
env: current,
|
|
126
|
-
database: value,
|
|
137
|
+
env: hostOnly(current),
|
|
138
|
+
database: hostOnly(value),
|
|
127
139
|
});
|
|
128
140
|
}
|
|
129
141
|
process.env.PUBLIC_BASE_URL = value;
|
|
@@ -136,39 +148,47 @@ async function rpc(cfg, name, body) {
|
|
|
136
148
|
body: JSON.stringify(body),
|
|
137
149
|
});
|
|
138
150
|
}
|
|
151
|
+
function hostOnly(value) {
|
|
152
|
+
try {
|
|
153
|
+
return new URL(value).host;
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
139
159
|
async function linkOnce() {
|
|
140
160
|
const cfg = dbConfig();
|
|
141
161
|
const centralUrl = process.env.CENTRAL_AUTH_URL?.replace(/\/+$/, "");
|
|
142
162
|
const secret = process.env.CENTRAL_AUTH_SECRET;
|
|
143
163
|
if (!cfg || !centralUrl || !secret) {
|
|
144
164
|
warn("link-config", "configuratie voor de Central-koppeling ontbreekt");
|
|
145
|
-
return;
|
|
165
|
+
return false;
|
|
146
166
|
}
|
|
147
167
|
const statusRes = await rpc(cfg, "flowselections_central_status", {});
|
|
148
168
|
if (!statusRes.ok) {
|
|
149
169
|
warn("link-status", "flowselections_central_status niet beschikbaar", { status: statusRes.status });
|
|
150
|
-
return;
|
|
170
|
+
return false;
|
|
151
171
|
}
|
|
152
172
|
const raw = await statusRes.json();
|
|
153
173
|
const status = (Array.isArray(raw) ? raw[0] : raw);
|
|
154
|
-
if (!status) {
|
|
174
|
+
if (!status || typeof status.token_aanwezig !== "boolean") {
|
|
155
175
|
warn("link-status", "flowselections_central_status gaf geen resultaat");
|
|
156
|
-
return;
|
|
176
|
+
return false;
|
|
157
177
|
}
|
|
158
178
|
if (status.token_aanwezig !== false && status.laatste_status !== "http_401")
|
|
159
|
-
return;
|
|
179
|
+
return true;
|
|
160
180
|
const tokenRes = await timedFetch(`${centralUrl}/api/shell-host/read-token`, {
|
|
161
181
|
method: "POST",
|
|
162
182
|
headers: { Authorization: `Bearer ${secret}`, Accept: "application/json" },
|
|
163
183
|
});
|
|
164
184
|
if (!tokenRes.ok) {
|
|
165
185
|
warn("link-token", "leessleutel ophalen bij Central mislukt", { status: tokenRes.status });
|
|
166
|
-
return;
|
|
186
|
+
return false;
|
|
167
187
|
}
|
|
168
188
|
const { token } = (await tokenRes.json());
|
|
169
189
|
if (typeof token !== "string" || !token) {
|
|
170
190
|
warn("link-token", "Central gaf geen bruikbare leessleutel");
|
|
171
|
-
return;
|
|
191
|
+
return false;
|
|
172
192
|
}
|
|
173
193
|
const setRes = await rpc(cfg, "flowselections_set_central_link", {
|
|
174
194
|
p_central_url: centralUrl,
|
|
@@ -176,21 +196,23 @@ async function linkOnce() {
|
|
|
176
196
|
});
|
|
177
197
|
if (!setRes.ok) {
|
|
178
198
|
warn("link-set", "flowselections_set_central_link mislukt", { status: setRes.status });
|
|
199
|
+
return false;
|
|
179
200
|
}
|
|
201
|
+
return true;
|
|
180
202
|
}
|
|
181
203
|
/**
|
|
182
|
-
*
|
|
183
|
-
* doen, waarschuwen, niet opnieuw tot het volgende uur.
|
|
204
|
+
* Na succes één uur wachten per isolate; na een fout vijf minuten.
|
|
184
205
|
*/
|
|
185
206
|
export async function ensureCentralLink() {
|
|
186
207
|
const now = Date.now();
|
|
187
208
|
if (linkInflight)
|
|
188
209
|
return linkInflight;
|
|
189
|
-
if (
|
|
210
|
+
if (now < nextLinkCheck)
|
|
190
211
|
return;
|
|
191
|
-
lastLinkCheck = now;
|
|
192
212
|
linkInflight = linkOnce()
|
|
213
|
+
.then((ok) => { nextLinkCheck = Date.now() + (ok ? LINK_INTERVAL_MS : CRON_TTL_MS); })
|
|
193
214
|
.catch((e) => {
|
|
215
|
+
nextLinkCheck = Date.now() + CRON_TTL_MS;
|
|
194
216
|
warn("link-error", "Central-koppeling controleren mislukt", {
|
|
195
217
|
error: e instanceof Error ? e.name : "onbekend",
|
|
196
218
|
});
|
|
@@ -200,20 +222,189 @@ export async function ensureCentralLink() {
|
|
|
200
222
|
});
|
|
201
223
|
return linkInflight;
|
|
202
224
|
}
|
|
203
|
-
|
|
204
|
-
|
|
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) {
|
|
205
386
|
try {
|
|
206
|
-
await Promise.
|
|
387
|
+
await Promise.allSettled([
|
|
388
|
+
applyShellBaseUrl(), ensureCentralLink(), ...(ctx ? [ensureCronKeys()] : []),
|
|
389
|
+
]);
|
|
390
|
+
if (ctx)
|
|
391
|
+
return await checkCronRequest(ctx.request);
|
|
207
392
|
}
|
|
208
393
|
catch {
|
|
209
|
-
//
|
|
394
|
+
// Geen onbedoelde weigering bij configuratie- of runtimefouten.
|
|
210
395
|
}
|
|
396
|
+
return null;
|
|
211
397
|
}
|
|
212
398
|
/** Alleen voor tests. */
|
|
213
399
|
export function __resetShellRuntimeForTests() {
|
|
214
400
|
baseUrlCache = null;
|
|
215
401
|
baseUrlInflight = null;
|
|
216
|
-
|
|
402
|
+
nextLinkCheck = 0;
|
|
217
403
|
linkInflight = null;
|
|
404
|
+
nextCronKeyCheck = 0;
|
|
405
|
+
cronKeyInflight = null;
|
|
406
|
+
cronCache = null;
|
|
407
|
+
cronInflight = null;
|
|
408
|
+
comparisonKey = null;
|
|
218
409
|
lastWarn.clear();
|
|
219
410
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/supabase/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/supabase/client.ts"],"names":[],"mappings":"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"}
|
package/dist/supabase/client.js
CHANGED
|
@@ -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,
|