@flowselections/floriday-authenticatie-module 1.0.31 → 1.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/components/TemplatePage.d.ts +1 -1
- package/dist-lib/components/TemplatePage.d.ts.map +1 -1
- package/dist-lib/components/floriday/FlorydayConnectionPage.d.ts +1 -1
- package/dist-lib/components/floriday/FlorydayConnectionPage.d.ts.map +1 -1
- package/dist-lib/components/floriday/FlorydaySyncHealthBanner.d.ts +1 -1
- package/dist-lib/components/floriday/FlorydaySyncHealthBanner.d.ts.map +1 -1
- package/dist-lib/components/floriday/FlorydaySyncJobsCard.d.ts +1 -1
- package/dist-lib/components/floriday/FlorydaySyncJobsCard.d.ts.map +1 -1
- package/dist-lib/components/floriday/TokenHealthCard.d.ts +1 -1
- package/dist-lib/components/floriday/TokenHealthCard.d.ts.map +1 -1
- package/dist-lib/components/settings/FloricodeSettingsCard.d.ts +1 -1
- package/dist-lib/components/settings/FloricodeSettingsCard.d.ts.map +1 -1
- package/dist-lib/components/settings/FlorydaySettingsCard.d.ts +1 -1
- package/dist-lib/components/settings/FlorydaySettingsCard.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +652 -221
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +652 -221
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +652 -221
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +653 -228
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -12
- package/dist-lib/lib/central-auth.server.d.ts +26 -1
- package/dist-lib/lib/central-auth.server.d.ts.map +1 -1
- package/dist-lib/lib/central-auth.server.js +69 -3
- package/dist-lib/lib/cron/index.d.ts +1 -0
- package/dist-lib/lib/cron/index.d.ts.map +1 -1
- package/dist-lib/lib/cron/index.js +1 -0
- package/dist-lib/lib/cron/process-stock-queue.d.ts +43 -0
- package/dist-lib/lib/cron/process-stock-queue.d.ts.map +1 -0
- package/dist-lib/lib/cron/process-stock-queue.js +218 -0
- package/dist-lib/lib/cron/refresh-floriday-tokens.d.ts.map +1 -1
- package/dist-lib/lib/cron/refresh-floriday-tokens.js +27 -148
- package/dist-lib/lib/error-page.js +30 -30
- package/dist-lib/lib/floricode.functions.d.ts +4557 -1540
- package/dist-lib/lib/floricode.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-auction-letters.functions.d.ts +652 -221
- package/dist-lib/lib/floriday-auction-letters.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-labels.functions.d.ts +652 -221
- package/dist-lib/lib/floriday-labels.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-public.functions.d.ts +3912 -1362
- package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-stickers.functions.d.ts +652 -221
- package/dist-lib/lib/floriday-stickers.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-sync.functions.d.ts +20181 -6820
- package/dist-lib/lib/floriday-sync.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday-writes.functions.d.ts +3906 -1320
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.d.ts +32289 -4066
- package/dist-lib/lib/floriday.functions.d.ts.map +1 -1
- package/dist-lib/lib/floriday.functions.js +111 -45
- package/package.json +483 -475
- package/dist-lib/assets/floriday-logo.png.asset.json +0 -11
|
@@ -8,18 +8,7 @@ export const Constants = {
|
|
|
8
8
|
"klaar",
|
|
9
9
|
"mislukt",
|
|
10
10
|
],
|
|
11
|
-
app_role: [
|
|
12
|
-
"admin",
|
|
13
|
-
"manager",
|
|
14
|
-
"warehouse",
|
|
15
|
-
"sales",
|
|
16
|
-
"developer",
|
|
17
|
-
"receptie",
|
|
18
|
-
"monteur",
|
|
19
|
-
"planner",
|
|
20
|
-
"administratie",
|
|
21
|
-
"ai_admin",
|
|
22
|
-
],
|
|
11
|
+
app_role: ["admin", "planner", "monteur", "administratie", "ai_admin"],
|
|
23
12
|
doc_layout_type: [
|
|
24
13
|
"pakbon",
|
|
25
14
|
"factuur",
|
|
@@ -2,6 +2,9 @@ export type CentralFlorydayTokenResult = {
|
|
|
2
2
|
ok: true;
|
|
3
3
|
access_token: string;
|
|
4
4
|
expires_at: string;
|
|
5
|
+
reauthorize_required?: boolean;
|
|
6
|
+
missing_scopes?: string[];
|
|
7
|
+
scope_state?: string | null;
|
|
5
8
|
raw: {
|
|
6
9
|
status: number;
|
|
7
10
|
body: string;
|
|
@@ -13,7 +16,29 @@ export type CentralFlorydayTokenResult = {
|
|
|
13
16
|
body: string;
|
|
14
17
|
};
|
|
15
18
|
};
|
|
16
|
-
export declare function getFlorydayTokenFromCentral(environment: "staging" | "live", scope
|
|
19
|
+
export declare function getFlorydayTokenFromCentral(environment: "staging" | "live", scope?: string, accountKey?: string): Promise<CentralFlorydayTokenResult>;
|
|
20
|
+
export type CentralAccountsResult = {
|
|
21
|
+
ok: true;
|
|
22
|
+
accounts: any[];
|
|
23
|
+
raw: {
|
|
24
|
+
status: number;
|
|
25
|
+
body: string;
|
|
26
|
+
};
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
raw: {
|
|
30
|
+
status: number;
|
|
31
|
+
body: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export declare function getFlorydayAccountsFromCentral(environment: "staging" | "live"): Promise<CentralAccountsResult>;
|
|
35
|
+
export declare function revokeFlorydayAtCentral(environment: "staging" | "live", accountKey?: string): Promise<{
|
|
36
|
+
ok: boolean;
|
|
37
|
+
raw: {
|
|
38
|
+
status: number;
|
|
39
|
+
body: string;
|
|
40
|
+
};
|
|
41
|
+
}>;
|
|
17
42
|
export type CentralSecretResult = {
|
|
18
43
|
ok: true;
|
|
19
44
|
secret_name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"central-auth.server.d.ts","sourceRoot":"","sources":["../../src/lib/central-auth.server.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,0BAA0B,GAClC;IACE,EAAE,EAAE,IAAI,CAAC;IACT,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC,CAAC;
|
|
1
|
+
{"version":3,"file":"central-auth.server.d.ts","sourceRoot":"","sources":["../../src/lib/central-auth.server.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,0BAA0B,GAClC;IACE,EAAE,EAAE,IAAI,CAAC;IACT,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC,CAAC;AAON,wBAAsB,2BAA2B,CAC/C,WAAW,EAAE,SAAS,GAAG,MAAM,EAC/B,KAAK,CAAC,EAAE,MAAM,EACd,UAAU,SAAY,GACrB,OAAO,CAAC,0BAA0B,CAAC,CAmFrC;AAOD,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,GAAG,EAAE,CAAC;IAAC,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACpE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAEzD,wBAAsB,8BAA8B,CAClD,WAAW,EAAE,SAAS,GAAG,MAAM,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CA4BhC;AAED,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,SAAS,GAAG,MAAM,EAC/B,UAAU,SAAY,GACrB,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC,CAuBjE;AAmBD,MAAM,MAAM,mBAAmB,GAC3B;IACE,EAAE,EAAE,IAAI,CAAC;IACT,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC,CAAC;AAEN,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,mBAAmB,CAAC,CAgD9B"}
|
|
@@ -17,13 +17,23 @@
|
|
|
17
17
|
//
|
|
18
18
|
// NIET importeren vanuit client-code. De .server.ts-suffix houdt dit bestand
|
|
19
19
|
// buiten de client-bundle (zoals bij client.server.ts).
|
|
20
|
-
|
|
20
|
+
/** Verwijdert de tokenwaarde uit een central-body vóór logging. */
|
|
21
|
+
function redactTokenBody(body) {
|
|
22
|
+
return body.replace(/("access_token"\s*:\s*")[^"]*(")/g, "$1<redacted>$2").slice(0, 500);
|
|
23
|
+
}
|
|
24
|
+
export async function getFlorydayTokenFromCentral(environment, scope, accountKey = "default") {
|
|
21
25
|
const baseUrl = process.env.CENTRAL_AUTH_URL;
|
|
22
26
|
const secret = process.env.CENTRAL_AUTH_SECRET;
|
|
23
27
|
if (!baseUrl || !secret) {
|
|
24
28
|
throw new Error("Missing CENTRAL_AUTH_URL or CENTRAL_AUTH_SECRET environment variable.");
|
|
25
29
|
}
|
|
26
30
|
const url = `${baseUrl.replace(/\/+$/, "")}/api/token/floriday`;
|
|
31
|
+
// Contract van Central Auth: environment + account_key zijn verplicht in de
|
|
32
|
+
// praktijk (staging en live zijn aparte connecties). `scope` alleen meesturen
|
|
33
|
+
// wanneer er expliciet een smallere set dan de standaard wordt gevraagd.
|
|
34
|
+
const payload = { environment, account_key: accountKey };
|
|
35
|
+
if (scope)
|
|
36
|
+
payload.scope = scope;
|
|
27
37
|
// Netwerkfout (fetch throwt vóór er een response is) → doorgooien, er is
|
|
28
38
|
// geen raw om te retourneren. Alle andere paden gaan via raw.
|
|
29
39
|
const res = await fetch(url, {
|
|
@@ -32,7 +42,7 @@ export async function getFlorydayTokenFromCentral(environment, scope) {
|
|
|
32
42
|
Authorization: `Bearer ${secret}`,
|
|
33
43
|
"Content-Type": "application/json",
|
|
34
44
|
},
|
|
35
|
-
body: JSON.stringify(
|
|
45
|
+
body: JSON.stringify(payload),
|
|
36
46
|
});
|
|
37
47
|
// Response ALTIJD eerst als tekst lezen — raw.body moet op elk pad gevuld
|
|
38
48
|
// zijn (2xx én niet-ok). Bij niet-ok is dit het enige wat stap B heeft om
|
|
@@ -40,6 +50,7 @@ export async function getFlorydayTokenFromCentral(environment, scope) {
|
|
|
40
50
|
// { "error": "upstream_error" }).
|
|
41
51
|
const text = await res.text();
|
|
42
52
|
const raw = { status: res.status, body: text };
|
|
53
|
+
console.log(`[central-auth] POST /api/token/floriday env=${environment} -> ${res.status} ${redactTokenBody(text)}`);
|
|
43
54
|
if (!res.ok) {
|
|
44
55
|
return { ok: false, raw };
|
|
45
56
|
}
|
|
@@ -61,7 +72,62 @@ export async function getFlorydayTokenFromCentral(environment, scope) {
|
|
|
61
72
|
if (!expires_at) {
|
|
62
73
|
return { ok: false, raw };
|
|
63
74
|
}
|
|
64
|
-
return {
|
|
75
|
+
return {
|
|
76
|
+
ok: true,
|
|
77
|
+
access_token: json.access_token,
|
|
78
|
+
expires_at,
|
|
79
|
+
reauthorize_required: typeof json.reauthorize_required === "boolean" ? json.reauthorize_required : false,
|
|
80
|
+
missing_scopes: Array.isArray(json.missing_scopes)
|
|
81
|
+
? json.missing_scopes.filter((s) => typeof s === "string")
|
|
82
|
+
: [],
|
|
83
|
+
scope_state: typeof json.scope_state === "string" ? json.scope_state : null,
|
|
84
|
+
raw,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export async function getFlorydayAccountsFromCentral(environment) {
|
|
88
|
+
const baseUrl = process.env.CENTRAL_AUTH_URL;
|
|
89
|
+
const secret = process.env.CENTRAL_AUTH_SECRET;
|
|
90
|
+
if (!baseUrl || !secret) {
|
|
91
|
+
throw new Error("Missing CENTRAL_AUTH_URL or CENTRAL_AUTH_SECRET environment variable.");
|
|
92
|
+
}
|
|
93
|
+
const url = `${baseUrl.replace(/\/+$/, "")}/api/accounts/floriday?environment=${encodeURIComponent(environment)}`;
|
|
94
|
+
const res = await fetch(url, {
|
|
95
|
+
method: "GET",
|
|
96
|
+
headers: { Authorization: `Bearer ${secret}` },
|
|
97
|
+
});
|
|
98
|
+
const text = await res.text();
|
|
99
|
+
const raw = { status: res.status, body: text };
|
|
100
|
+
console.log(`[central-auth] GET /api/accounts/floriday env=${environment} -> ${res.status} ${text.slice(0, 500)}`);
|
|
101
|
+
if (!res.ok)
|
|
102
|
+
return { ok: false, raw };
|
|
103
|
+
try {
|
|
104
|
+
const json = JSON.parse(text);
|
|
105
|
+
if (!Array.isArray(json.accounts))
|
|
106
|
+
return { ok: false, raw };
|
|
107
|
+
return { ok: true, accounts: json.accounts, raw };
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return { ok: false, raw };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export async function revokeFlorydayAtCentral(environment, accountKey = "default") {
|
|
114
|
+
const baseUrl = process.env.CENTRAL_AUTH_URL;
|
|
115
|
+
const secret = process.env.CENTRAL_AUTH_SECRET;
|
|
116
|
+
if (!baseUrl || !secret) {
|
|
117
|
+
throw new Error("Missing CENTRAL_AUTH_URL or CENTRAL_AUTH_SECRET environment variable.");
|
|
118
|
+
}
|
|
119
|
+
const url = `${baseUrl.replace(/\/+$/, "")}/api/revoke/floriday`;
|
|
120
|
+
const res = await fetch(url, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers: {
|
|
123
|
+
Authorization: `Bearer ${secret}`,
|
|
124
|
+
"Content-Type": "application/json",
|
|
125
|
+
},
|
|
126
|
+
body: JSON.stringify({ account_key: accountKey, environment }),
|
|
127
|
+
});
|
|
128
|
+
const text = await res.text();
|
|
129
|
+
console.log(`[central-auth] POST /api/revoke/floriday env=${environment} -> ${res.status} ${text.slice(0, 500)}`);
|
|
130
|
+
return { ok: res.ok, raw: { status: res.status, body: text } };
|
|
65
131
|
}
|
|
66
132
|
export async function getSecretFromCentral(name) {
|
|
67
133
|
const baseUrl = process.env.CENTRAL_AUTH_URL;
|
|
@@ -32,4 +32,5 @@ export { handleRefreshFloricodeCodelists } from "./refresh-floricode-codelists.j
|
|
|
32
32
|
export { handleRefreshFlorydayReferenceData } from "./refresh-floriday-reference-data.js";
|
|
33
33
|
export { handleRefreshFlorydayTokens } from "./refresh-floriday-tokens.js";
|
|
34
34
|
export { handleVerifyFloricodeCharacteristicsCoverage } from "./verify-floricode-characteristics-coverage.js";
|
|
35
|
+
export { handleProcessStockQueue, runStockQueueRound, maybeTickStockQueue } from "./process-stock-queue.js";
|
|
35
36
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/cron/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAK5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAI9D,eAAO,MAAM,4BAA4B,GAAI,KAAK,OAAO,EAAE,IAAI,cAAc,sBACzB,CAAC;AACrD,eAAO,MAAM,mBAAmB,GAAI,KAAK,OAAO,EAAE,IAAI,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/cron/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -47,3 +47,4 @@ export { handleRefreshFloricodeCodelists } from "./refresh-floricode-codelists.j
|
|
|
47
47
|
export { handleRefreshFlorydayReferenceData } from "./refresh-floriday-reference-data.js";
|
|
48
48
|
export { handleRefreshFlorydayTokens } from "./refresh-floriday-tokens.js";
|
|
49
49
|
export { handleVerifyFloricodeCharacteristicsCoverage } from "./verify-floricode-characteristics-coverage.js";
|
|
50
|
+
export { handleProcessStockQueue, runStockQueueRound, maybeTickStockQueue } from "./process-stock-queue.js";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import { type FlorydayEnvironment } from "../floriday.functions.js";
|
|
3
|
+
type StockQueueRow = {
|
|
4
|
+
id: string;
|
|
5
|
+
connection_id: string;
|
|
6
|
+
trade_item_id: string;
|
|
7
|
+
number_of_pieces: number | null;
|
|
8
|
+
desired_available: boolean | null;
|
|
9
|
+
attempts: number | null;
|
|
10
|
+
};
|
|
11
|
+
/** ISO-weeknummer van vandaag. */
|
|
12
|
+
export declare function isoWeekParts(input?: string | Date): {
|
|
13
|
+
year: number;
|
|
14
|
+
week: number;
|
|
15
|
+
};
|
|
16
|
+
/** Tijdelijk (netwerk/5xx/429) versus definitief (gegevensfout). */
|
|
17
|
+
export declare function isPermanentError(message: string): boolean;
|
|
18
|
+
export declare function isRateLimited(message: string): boolean;
|
|
19
|
+
type PushOutcome = {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
permanent?: boolean;
|
|
22
|
+
error?: string;
|
|
23
|
+
attemptId?: string | null;
|
|
24
|
+
quantitySkipped?: string;
|
|
25
|
+
};
|
|
26
|
+
/** Zet één wachtrijregel door naar Floriday. Gooit nooit. */
|
|
27
|
+
export declare function pushStockRow(supabase: SupabaseClient, env: FlorydayEnvironment, row: StockQueueRow): Promise<PushOutcome>;
|
|
28
|
+
/** Eén verwerkingsronde. Retourneert een compacte samenvatting. */
|
|
29
|
+
export declare function runStockQueueRound(supabase: SupabaseClient, limit?: number): Promise<{
|
|
30
|
+
ok: true;
|
|
31
|
+
claimed: number;
|
|
32
|
+
succeeded: number;
|
|
33
|
+
failed: number;
|
|
34
|
+
rateLimited: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Cron-handler, zelfde vorm en beveiliging als de overige cron-routes:
|
|
38
|
+
* `apikey`-header moet gelijk zijn aan `SUPABASE_PUBLISHABLE_KEY`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function handleProcessStockQueue(request: Request, supabase: SupabaseClient): Promise<Response>;
|
|
41
|
+
export declare function maybeTickStockQueue(supabase: SupabaseClient): void;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=process-stock-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-stock-queue.d.ts","sourceRoot":"","sources":["../../../src/lib/cron/process-stock-queue.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAIlC,KAAK,aAAa,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,kCAAkC;AAClC,wBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAYlF;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CASzD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEtD;AAmCD,KAAK,WAAW,GAAG;IACjB,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,6DAA6D;AAC7D,wBAAsB,YAAY,CAChC,QAAQ,EAAE,cAAc,EACxB,GAAG,EAAE,mBAAmB,EACxB,GAAG,EAAE,aAAa,GACjB,OAAO,CAAC,WAAW,CAAC,CA6FtB;AAED,mEAAmE;AACnE,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,cAAc,EACxB,KAAK,SAAa,GACjB,OAAO,CAAC;IACT,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC,CA4CD;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,CAAC,CAiBnB;AAOD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAOlE"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Vaste verwerker voor de Floriday-voorraadwachtrij.
|
|
3
|
+
// ----------------------------------------------------------------------------
|
|
4
|
+
// Deze module neemt de voorraadsync volledig over van de voorraadmodule.
|
|
5
|
+
// Werkwijze per ronde:
|
|
6
|
+
// 1. `claim_floriday_stock_queue` claimt atomair max. N regels (lock 5 min).
|
|
7
|
+
// 2. Per regel: werkelijk aantal stuks (weekaanbod) + beschikbaarheids-
|
|
8
|
+
// schakelaar via `florydayWrite` — dus met de tokens van deze module.
|
|
9
|
+
// 3. `complete_floriday_stock_queue` schrijft het resultaat terug met
|
|
10
|
+
// exponentiële backoff (1 → 30 min, max 8 pogingen) en onderscheid
|
|
11
|
+
// tussen tijdelijke en definitieve fouten.
|
|
12
|
+
// Bij een snelheidslimiet (429) breekt de ronde af; de volgende cron-tick
|
|
13
|
+
// pakt het weer op.
|
|
14
|
+
// ============================================================================
|
|
15
|
+
import { florydayWrite, getActiveFlorydayEnvironment, } from "../floriday.functions.js";
|
|
16
|
+
const BATCH_SIZE = 25;
|
|
17
|
+
/** ISO-weeknummer van vandaag. */
|
|
18
|
+
export function isoWeekParts(input) {
|
|
19
|
+
const parsed = input ? new Date(input) : new Date();
|
|
20
|
+
const source = Number.isNaN(parsed.getTime()) ? new Date() : parsed;
|
|
21
|
+
const date = new Date(Date.UTC(source.getUTCFullYear(), source.getUTCMonth(), source.getUTCDate()));
|
|
22
|
+
const day = date.getUTCDay() || 7;
|
|
23
|
+
date.setUTCDate(date.getUTCDate() + 4 - day);
|
|
24
|
+
const year = date.getUTCFullYear();
|
|
25
|
+
const yearStart = new Date(Date.UTC(year, 0, 1));
|
|
26
|
+
const week = Math.ceil(((date.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
|
|
27
|
+
return { year, week };
|
|
28
|
+
}
|
|
29
|
+
/** Tijdelijk (netwerk/5xx/429) versus definitief (gegevensfout). */
|
|
30
|
+
export function isPermanentError(message) {
|
|
31
|
+
const m = message.toLowerCase();
|
|
32
|
+
if (/\b(429|500|502|503|504)\b/.test(m))
|
|
33
|
+
return false;
|
|
34
|
+
if (/timeout|timed out|network|fetch failed|econn|socket|rate limit|too many requests/.test(m)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (/\b(400|401|403|404|409|422)\b/.test(m))
|
|
38
|
+
return true;
|
|
39
|
+
if (/unauthorized|forbidden|not found|validation|invalid/.test(m))
|
|
40
|
+
return true;
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
export function isRateLimited(message) {
|
|
44
|
+
return /\b429\b|rate limit|too many requests/i.test(message);
|
|
45
|
+
}
|
|
46
|
+
async function resolveWarehouseId(supabase, connectionId, tradeItemId, cachedItem) {
|
|
47
|
+
const { data: pref } = await supabase
|
|
48
|
+
.from("trade_item_warehouse_preferences")
|
|
49
|
+
.select("warehouse_id")
|
|
50
|
+
.eq("connection_id", connectionId)
|
|
51
|
+
.eq("trade_item_id", tradeItemId)
|
|
52
|
+
.maybeSingle();
|
|
53
|
+
const direct = pref?.warehouse_id ?? cachedItem?.warehouseId ?? cachedItem?.warehouse?.warehouseId;
|
|
54
|
+
if (direct)
|
|
55
|
+
return direct;
|
|
56
|
+
const { data: whs } = await supabase
|
|
57
|
+
.from("floriday_warehouses_cache")
|
|
58
|
+
.select("floriday_id, data")
|
|
59
|
+
.eq("connection_id", connectionId)
|
|
60
|
+
.limit(200);
|
|
61
|
+
const own = (whs ?? []).filter((w) => w?.data?.isDeleted !== true &&
|
|
62
|
+
w?.data?.isExternal !== true &&
|
|
63
|
+
w?.data?.hasAccess !== false);
|
|
64
|
+
const chosen = own.find((w) => w?.data?.isDefault === true) ?? own[0];
|
|
65
|
+
return chosen?.floriday_id ?? chosen?.data?.warehouseId;
|
|
66
|
+
}
|
|
67
|
+
/** Zet één wachtrijregel door naar Floriday. Gooit nooit. */
|
|
68
|
+
export async function pushStockRow(supabase, env, row) {
|
|
69
|
+
const pieces = Math.max(0, Number(row.number_of_pieces ?? 0) || 0);
|
|
70
|
+
const available = row.desired_available ?? pieces > 0;
|
|
71
|
+
try {
|
|
72
|
+
const { data: cached } = await supabase
|
|
73
|
+
.from("floriday_trade_items_cache")
|
|
74
|
+
.select("data")
|
|
75
|
+
.eq("connection_id", row.connection_id)
|
|
76
|
+
.eq("floriday_id", row.trade_item_id)
|
|
77
|
+
.maybeSingle();
|
|
78
|
+
if (!cached) {
|
|
79
|
+
return {
|
|
80
|
+
ok: false,
|
|
81
|
+
permanent: true,
|
|
82
|
+
error: "Dit artikel staat nog niet in de lokale Floriday-cache. Synchroniseer de artikelen en probeer opnieuw.",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const item = cached.data ?? {};
|
|
86
|
+
const warehouseId = await resolveWarehouseId(supabase, row.connection_id, row.trade_item_id, item);
|
|
87
|
+
// 1. Werkelijk aantal stuks via het weekaanbod van de lopende week.
|
|
88
|
+
const { year, week } = isoWeekParts();
|
|
89
|
+
const { data: supply } = await supabase
|
|
90
|
+
.from("trade_item_supply_lines")
|
|
91
|
+
.select("price_per_piece, currency")
|
|
92
|
+
.eq("connection_id", row.connection_id)
|
|
93
|
+
.eq("trade_item_id", row.trade_item_id)
|
|
94
|
+
.order("updated_at", { ascending: false })
|
|
95
|
+
.limit(1)
|
|
96
|
+
.maybeSingle();
|
|
97
|
+
const pricePerPiece = Number(supply?.price_per_piece ?? NaN);
|
|
98
|
+
let quantitySkipped;
|
|
99
|
+
if (Number.isFinite(pricePerPiece) && pricePerPiece > 0) {
|
|
100
|
+
const qty = await florydayWrite(supabase, env, {
|
|
101
|
+
module: "inventory",
|
|
102
|
+
action: "edit-weekly-base-supply",
|
|
103
|
+
method: "PUT",
|
|
104
|
+
path: `/suppliers-api-2026v1/trade-items/${row.trade_item_id}/base-supply/${year}/${week}`,
|
|
105
|
+
referenceId: row.trade_item_id,
|
|
106
|
+
idempotencyKey: `inventory:weekly-base-supply:${row.trade_item_id}:${year}:${week}:${pieces}`,
|
|
107
|
+
body: {
|
|
108
|
+
basePricePerPiece: {
|
|
109
|
+
currency: supply?.currency ?? "EUR",
|
|
110
|
+
value: pricePerPiece,
|
|
111
|
+
},
|
|
112
|
+
numberOfPieces: pieces,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
if (!qty.ok) {
|
|
116
|
+
const message = qty.error ?? `HTTP ${qty.status}`;
|
|
117
|
+
return { ok: false, permanent: isPermanentError(message), error: message };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
quantitySkipped =
|
|
122
|
+
"Aantal niet doorgezet: er is nog geen prijs bekend voor dit artikel.";
|
|
123
|
+
}
|
|
124
|
+
// 2. Beschikbaarheidsschakelaar.
|
|
125
|
+
const body = { available };
|
|
126
|
+
if (warehouseId)
|
|
127
|
+
body.warehouseId = warehouseId;
|
|
128
|
+
const res = await florydayWrite(supabase, env, {
|
|
129
|
+
module: "inventory",
|
|
130
|
+
action: "publish-continuous-stock",
|
|
131
|
+
method: "PUT",
|
|
132
|
+
path: `/suppliers-api-2026v1/trade-items/${row.trade_item_id}/continuous-stock`,
|
|
133
|
+
referenceId: row.trade_item_id,
|
|
134
|
+
idempotencyKey: `inventory:continuous-stock:${row.trade_item_id}:${available}:${pieces}`,
|
|
135
|
+
body,
|
|
136
|
+
});
|
|
137
|
+
if (!res.ok) {
|
|
138
|
+
const message = res.error ?? `HTTP ${res.status}`;
|
|
139
|
+
return { ok: false, permanent: isPermanentError(message), error: message };
|
|
140
|
+
}
|
|
141
|
+
return { ok: true, attemptId: res.attemptId ?? null, quantitySkipped };
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
145
|
+
return { ok: false, permanent: isPermanentError(message), error: message };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** Eén verwerkingsronde. Retourneert een compacte samenvatting. */
|
|
149
|
+
export async function runStockQueueRound(supabase, limit = BATCH_SIZE) {
|
|
150
|
+
const env = await getActiveFlorydayEnvironment(supabase);
|
|
151
|
+
const { data: claimed, error } = await supabase.rpc("claim_floriday_stock_queue", { p_limit: limit, p_worker: "auth-module" });
|
|
152
|
+
if (error)
|
|
153
|
+
throw new Error(error.message);
|
|
154
|
+
const rows = (claimed ?? []);
|
|
155
|
+
let succeeded = 0;
|
|
156
|
+
let failed = 0;
|
|
157
|
+
let rateLimited = false;
|
|
158
|
+
for (const row of rows) {
|
|
159
|
+
if (rateLimited) {
|
|
160
|
+
// Ronde afgebroken: regel meteen vrijgeven voor de volgende tick.
|
|
161
|
+
await supabase.rpc("complete_floriday_stock_queue", {
|
|
162
|
+
p_id: row.id,
|
|
163
|
+
p_ok: false,
|
|
164
|
+
p_error: "Snelheidslimiet van Floriday bereikt; volgende ronde opnieuw.",
|
|
165
|
+
p_permanent: false,
|
|
166
|
+
p_attempt_id: null,
|
|
167
|
+
});
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const outcome = await pushStockRow(supabase, env, row);
|
|
171
|
+
if (outcome.ok)
|
|
172
|
+
succeeded += 1;
|
|
173
|
+
else {
|
|
174
|
+
failed += 1;
|
|
175
|
+
if (outcome.error && isRateLimited(outcome.error))
|
|
176
|
+
rateLimited = true;
|
|
177
|
+
}
|
|
178
|
+
await supabase.rpc("complete_floriday_stock_queue", {
|
|
179
|
+
p_id: row.id,
|
|
180
|
+
p_ok: outcome.ok,
|
|
181
|
+
p_error: outcome.ok ? outcome.quantitySkipped ?? null : outcome.error ?? "Onbekende fout",
|
|
182
|
+
p_permanent: outcome.permanent ?? false,
|
|
183
|
+
p_attempt_id: outcome.attemptId ?? null,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return { ok: true, claimed: rows.length, succeeded, failed, rateLimited };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Cron-handler, zelfde vorm en beveiliging als de overige cron-routes:
|
|
190
|
+
* `apikey`-header moet gelijk zijn aan `SUPABASE_PUBLISHABLE_KEY`.
|
|
191
|
+
*/
|
|
192
|
+
export async function handleProcessStockQueue(request, supabase) {
|
|
193
|
+
const apikey = request.headers.get("apikey") ?? request.headers.get("x-api-key");
|
|
194
|
+
const expected = process.env.SUPABASE_PUBLISHABLE_KEY;
|
|
195
|
+
if (!expected || !apikey || apikey !== expected) {
|
|
196
|
+
return new Response("Unauthorized", { status: 401 });
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
const summary = await runStockQueueRound(supabase);
|
|
200
|
+
return Response.json(summary);
|
|
201
|
+
}
|
|
202
|
+
catch (e) {
|
|
203
|
+
console.error("[stock-queue] ronde mislukt:", e?.message ?? e);
|
|
204
|
+
return Response.json({ ok: false, error: e?.message ?? "Onbekende fout" }, { status: 500 });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Vangnet: mag vanuit gewoon verkeer worden aangeroepen (fire-and-forget).
|
|
209
|
+
* Draait hoogstens één ronde per minuut per instantie.
|
|
210
|
+
*/
|
|
211
|
+
let lastTick = 0;
|
|
212
|
+
export function maybeTickStockQueue(supabase) {
|
|
213
|
+
const now = Date.now();
|
|
214
|
+
if (now - lastTick < 60000)
|
|
215
|
+
return;
|
|
216
|
+
lastTick = now;
|
|
217
|
+
void runStockQueueRound(supabase, 10).catch((e) => console.warn("[stock-queue] vangnet-ronde mislukt:", e?.message ?? e));
|
|
218
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refresh-floriday-tokens.d.ts","sourceRoot":"","sources":["../../../src/lib/cron/refresh-floriday-tokens.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"refresh-floriday-tokens.d.ts","sourceRoot":"","sources":["../../../src/lib/cron/refresh-floriday-tokens.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,QAAQ,CAAC,CAqBnB"}
|
|
@@ -1,151 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return "";
|
|
13
|
-
let s = String(input).normalize("NFKC");
|
|
14
|
-
s = s.replace(/[\u200B-\u200D\uFEFF]/g, "");
|
|
15
|
-
s = s.replace(/[\u202A-\u202E\u2066-\u2069]/g, "");
|
|
16
|
-
s = s.replace(/[\u00A0\u2007\u202F]/g, " ");
|
|
17
|
-
s = s.replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"');
|
|
18
|
-
s = s.replace(/[\u0000-\u001F\u007F]/g, "");
|
|
19
|
-
return s.trim();
|
|
20
|
-
}
|
|
21
|
-
async function refreshOne(admin, row) {
|
|
22
|
-
const res = await fetch(FLORYDAY_ENDPOINTS[row.environment], {
|
|
23
|
-
method: "POST",
|
|
24
|
-
headers: {
|
|
25
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
26
|
-
Accept: "application/json",
|
|
27
|
-
},
|
|
28
|
-
body: new URLSearchParams({
|
|
29
|
-
grant_type: "client_credentials",
|
|
30
|
-
client_id: sanitize(row.client_id),
|
|
31
|
-
client_secret: sanitize(row.client_secret),
|
|
32
|
-
scope: DEFAULT_SCOPE,
|
|
33
|
-
}).toString(),
|
|
34
|
-
});
|
|
35
|
-
const text = await res.text();
|
|
36
|
-
if (!res.ok) {
|
|
37
|
-
throw new Error(`Token request failed [${res.status}]: ${text.slice(0, 200)}`);
|
|
38
|
-
}
|
|
39
|
-
const json = JSON.parse(text);
|
|
40
|
-
const newExpiresAt = new Date(Date.now() + json.expires_in * 1000).toISOString();
|
|
41
|
-
const { error } = await admin
|
|
42
|
-
.from("floriday_connections")
|
|
43
|
-
.update({ access_token: json.access_token, token_expires_at: newExpiresAt })
|
|
44
|
-
.eq("id", row.id);
|
|
45
|
-
if (error)
|
|
46
|
-
throw new Error(`DB update failed: ${error.message}`);
|
|
47
|
-
return { id: row.id, environment: row.environment, expires_at: newExpiresAt };
|
|
48
|
-
}
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// refresh-floriday-tokens — bewust een no-op-statusendpoint.
|
|
3
|
+
// ----------------------------------------------------------------------------
|
|
4
|
+
// Central Auth is de token-broker: die cachet tokens en grant automatisch een
|
|
5
|
+
// nieuw token zodra het verloopt. Deze module doet daarom zelf géén grant bij
|
|
6
|
+
// Floriday, bewaart geen tokens en heeft geen eigen refresh-timer.
|
|
7
|
+
//
|
|
8
|
+
// De route en het serverRoutes-manifest blijven bestaan zodat shells die deze
|
|
9
|
+
// cron al hebben ingeplanned niet breken; de handler bevestigt alleen dat er
|
|
10
|
+
// niets te doen is en ruimt oude logregels op.
|
|
11
|
+
// ============================================================================
|
|
49
12
|
export async function handleRefreshFlorydayTokens(request, supabase) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (!expected || !apikey || apikey !== expected) {
|
|
56
|
-
return new Response("Unauthorized", { status: 401 });
|
|
57
|
-
}
|
|
58
|
-
const supabaseAdmin = supabase;
|
|
59
|
-
const { data: rows, error } = await supabaseAdmin
|
|
60
|
-
.from("floriday_connections")
|
|
61
|
-
.select("id, environment, client_id, client_secret, token_expires_at")
|
|
62
|
-
.eq("is_active", true);
|
|
63
|
-
if (error) {
|
|
64
|
-
await supabaseAdmin.from("floriday_token_refresh_log").insert({
|
|
65
|
-
connection_id: null,
|
|
66
|
-
environment: "staging",
|
|
67
|
-
ok: false,
|
|
68
|
-
error: `DB select failed: ${error.message}`.slice(0, 500),
|
|
69
|
-
});
|
|
70
|
-
return Response.json({ ok: false, error: error.message }, { status: 500 });
|
|
71
|
-
}
|
|
72
|
-
const now = Date.now();
|
|
73
|
-
const due = (rows ?? []).filter((r) => {
|
|
74
|
-
if (!r.client_id || !r.client_secret)
|
|
75
|
-
return false;
|
|
76
|
-
if (!r.token_expires_at)
|
|
77
|
-
return true;
|
|
78
|
-
return Date.parse(r.token_expires_at) - now < REFRESH_WINDOW_MS;
|
|
79
|
-
});
|
|
80
|
-
const results = [];
|
|
81
|
-
if (due.length === 0 && (rows?.length ?? 0) > 0) {
|
|
82
|
-
await supabaseAdmin.from("floriday_token_refresh_log").insert({
|
|
83
|
-
connection_id: rows[0].id,
|
|
84
|
-
environment: rows[0].environment,
|
|
85
|
-
ok: true,
|
|
86
|
-
expires_at: rows[0].token_expires_at,
|
|
87
|
-
error: "heartbeat: nog niet binnen refresh-window",
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
for (const r of due) {
|
|
91
|
-
try {
|
|
92
|
-
const out = await refreshOne(supabaseAdmin, {
|
|
93
|
-
id: r.id,
|
|
94
|
-
environment: r.environment,
|
|
95
|
-
client_id: r.client_id,
|
|
96
|
-
client_secret: r.client_secret,
|
|
97
|
-
});
|
|
98
|
-
results.push({ ...out, ok: true });
|
|
99
|
-
await supabaseAdmin.from("floriday_token_refresh_log").insert({
|
|
100
|
-
connection_id: r.id,
|
|
101
|
-
environment: r.environment,
|
|
102
|
-
ok: true,
|
|
103
|
-
expires_at: out.expires_at,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
catch (e) {
|
|
107
|
-
const msg = e?.message ?? "Onbekende fout";
|
|
108
|
-
results.push({
|
|
109
|
-
id: r.id,
|
|
110
|
-
environment: r.environment,
|
|
111
|
-
ok: false,
|
|
112
|
-
error: msg,
|
|
113
|
-
});
|
|
114
|
-
await supabaseAdmin.from("floriday_token_refresh_log").insert({
|
|
115
|
-
connection_id: r.id,
|
|
116
|
-
environment: r.environment,
|
|
117
|
-
ok: false,
|
|
118
|
-
error: String(msg).slice(0, 500),
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
await supabaseAdmin
|
|
123
|
-
.from("floriday_token_refresh_log")
|
|
124
|
-
.delete()
|
|
125
|
-
.lt("attempted_at", new Date(Date.now() - 14 * 24 * 60 * 60 * 1000).toISOString());
|
|
126
|
-
return Response.json({
|
|
127
|
-
ok: true,
|
|
128
|
-
checked: rows?.length ?? 0,
|
|
129
|
-
refreshed: results.filter((r) => r.ok).length,
|
|
130
|
-
failed: results.filter((r) => !r.ok).length,
|
|
131
|
-
results,
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
catch (e) {
|
|
135
|
-
const msg = String(e?.message ?? e);
|
|
136
|
-
console.error("[cron] refresh-floriday-tokens crashed:", msg);
|
|
137
|
-
// best-effort log
|
|
138
|
-
try {
|
|
139
|
-
await supabase.from("floriday_token_refresh_log").insert({
|
|
140
|
-
connection_id: null,
|
|
141
|
-
environment: "staging",
|
|
142
|
-
ok: false,
|
|
143
|
-
error: `handler crash: ${msg}`.slice(0, 500),
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
catch {
|
|
147
|
-
// ignore
|
|
148
|
-
}
|
|
149
|
-
return Response.json({ ok: false, error: msg }, { status: 500 });
|
|
13
|
+
// Zelfde beveiliging als de overige cron-routes.
|
|
14
|
+
const apikey = request.headers.get("apikey") ?? request.headers.get("x-api-key");
|
|
15
|
+
const expected = process.env.SUPABASE_PUBLISHABLE_KEY;
|
|
16
|
+
if (!expected || !apikey || apikey !== expected) {
|
|
17
|
+
return new Response("Unauthorized", { status: 401 });
|
|
150
18
|
}
|
|
19
|
+
// Opruimen van de historie blijft nuttig voor de statusweergave.
|
|
20
|
+
await supabase
|
|
21
|
+
.from("floriday_token_refresh_log")
|
|
22
|
+
.delete()
|
|
23
|
+
.lt("attempted_at", new Date(Date.now() - 14 * 24 * 60 * 60 * 1000).toISOString())
|
|
24
|
+
.then(() => undefined, () => undefined);
|
|
25
|
+
return Response.json({
|
|
26
|
+
ok: true,
|
|
27
|
+
skipped: true,
|
|
28
|
+
reason: "Tokens worden door Central Auth beheerd; deze module vernieuwt niets zelf.",
|
|
29
|
+
});
|
|
151
30
|
}
|