@flowselections/floriday-authenticatie-module 1.0.31 → 1.0.35
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
|
@@ -104,65 +104,56 @@ export async function getActiveFlorydayCredentials(supabase, env) {
|
|
|
104
104
|
throw new Error(`Kan Floriday-credentials niet laden: ${error.message}`);
|
|
105
105
|
return data;
|
|
106
106
|
}
|
|
107
|
+
// In-memory tokencache. Central Auth is de token-broker: die cachet en grant
|
|
108
|
+
// zelf opnieuw. Deze module bewaart daarom niets in de database en heeft geen
|
|
109
|
+
// eigen refresh-timer; hoogstens een procesgebonden cache tot kort voor
|
|
110
|
+
// `expires_at`. Scoped tokens (smallere scope dan de standaard) worden bewust
|
|
111
|
+
// NIET gecacht — die worden per gebruik opgevraagd.
|
|
112
|
+
const tokenCache = new Map();
|
|
107
113
|
export async function getFlorydayAccessToken(supabase, env, options = {}) {
|
|
108
|
-
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
109
114
|
const row = await getActiveFlorydayCredentials(supabase, env);
|
|
110
|
-
if (!row)
|
|
111
|
-
throw new Error(`Geen actieve Floriday-koppeling voor ${env}`);
|
|
112
115
|
const requestedScope = options.scope ?? DEFAULT_SCOPE;
|
|
113
|
-
const
|
|
116
|
+
const isDefaultScope = requestedScope === DEFAULT_SCOPE;
|
|
117
|
+
const cacheKey = `${env}:${requestedScope}`;
|
|
114
118
|
const now = Date.now();
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// Concurrency-guard: vlak voor de tokenrequest opnieuw lezen — een parallelle worker
|
|
120
|
-
// kan zojuist een fresh token hebben geschreven en dan hoeven we niet nóg eens te refreshen.
|
|
121
|
-
if (canReuseStoredToken && !options.forceRefresh) {
|
|
122
|
-
const { data: fresh } = await supabaseAdmin
|
|
123
|
-
.from("floriday_connections")
|
|
124
|
-
.select("access_token, token_expires_at")
|
|
125
|
-
.eq("id", row.id)
|
|
126
|
-
.maybeSingle();
|
|
127
|
-
const freshExp = fresh?.token_expires_at ? Date.parse(fresh.token_expires_at) : 0;
|
|
128
|
-
if (fresh?.access_token && freshExp - TOKEN_REFRESH_LEEWAY_S * 1000 > now) {
|
|
129
|
-
return fresh.access_token;
|
|
119
|
+
if (isDefaultScope && !options.forceRefresh) {
|
|
120
|
+
const hit = tokenCache.get(cacheKey);
|
|
121
|
+
if (hit && hit.expiresAt - TOKEN_REFRESH_LEEWAY_S * 1000 > now) {
|
|
122
|
+
return hit.token;
|
|
130
123
|
}
|
|
131
124
|
}
|
|
132
|
-
let accessToken;
|
|
133
|
-
let newExpiresAt;
|
|
134
125
|
try {
|
|
135
126
|
const { getFlorydayTokenFromCentral } = await import("./central-auth.server.js");
|
|
136
|
-
const result = await getFlorydayTokenFromCentral(env,
|
|
127
|
+
const result = await getFlorydayTokenFromCentral(env, options.scope, "default");
|
|
137
128
|
if (!result.ok) {
|
|
138
129
|
throw createFlorydayTokenError(result.raw.status, result.raw.body, requestedScope);
|
|
139
130
|
}
|
|
140
|
-
accessToken = result.access_token;
|
|
141
|
-
newExpiresAt = result.expires_at;
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
.
|
|
146
|
-
|
|
131
|
+
const accessToken = result.access_token;
|
|
132
|
+
const newExpiresAt = result.expires_at;
|
|
133
|
+
if (isDefaultScope) {
|
|
134
|
+
tokenCache.set(cacheKey, {
|
|
135
|
+
token: accessToken,
|
|
136
|
+
expiresAt: Date.parse(newExpiresAt) || now,
|
|
137
|
+
});
|
|
147
138
|
}
|
|
148
139
|
// Best-effort logging (faalt nooit hard).
|
|
149
140
|
await supabase.from("floriday_token_refresh_log").insert({
|
|
150
|
-
connection_id: row
|
|
141
|
+
connection_id: row?.id ?? null,
|
|
151
142
|
environment: env,
|
|
152
143
|
ok: true,
|
|
153
144
|
expires_at: newExpiresAt,
|
|
154
145
|
}).then(() => undefined, () => undefined);
|
|
146
|
+
return accessToken;
|
|
155
147
|
}
|
|
156
148
|
catch (e) {
|
|
157
149
|
await supabase.from("floriday_token_refresh_log").insert({
|
|
158
|
-
connection_id: row
|
|
150
|
+
connection_id: row?.id ?? null,
|
|
159
151
|
environment: env,
|
|
160
152
|
ok: false,
|
|
161
153
|
error: String(e?.message ?? e).slice(0, 500),
|
|
162
154
|
}).then(() => undefined, () => undefined);
|
|
163
155
|
throw e;
|
|
164
156
|
}
|
|
165
|
-
return accessToken;
|
|
166
157
|
}
|
|
167
158
|
function createFlorydayTokenError(status, body, scope) {
|
|
168
159
|
const normalized = body.toLowerCase();
|
|
@@ -181,8 +172,38 @@ function createFlorydayTokenError(status, body, scope) {
|
|
|
181
172
|
return new Error(FLORYDAY_CONTRACT_PERMISSION_MESSAGE);
|
|
182
173
|
}
|
|
183
174
|
}
|
|
175
|
+
// Central Auth antwoordt opaak en in het Engels; hier vertalen we naar een
|
|
176
|
+
// melding waar een beheerder iets mee kan. Status én body blijven gelogd in
|
|
177
|
+
// central-auth.server.ts.
|
|
178
|
+
const centralMessage = mapCentralAuthError(status, normalized);
|
|
179
|
+
if (centralMessage)
|
|
180
|
+
return new Error(centralMessage);
|
|
184
181
|
return new Error(`Floriday token-aanvraag mislukt [${status}]: ${body.slice(0, 300)}`);
|
|
185
182
|
}
|
|
183
|
+
function mapCentralAuthError(status, normalizedBody) {
|
|
184
|
+
if (status === 401) {
|
|
185
|
+
return "Central Auth weigert dit bearer-secret. Controleer CENTRAL_AUTH_SECRET van deze omgeving.";
|
|
186
|
+
}
|
|
187
|
+
if (status === 404) {
|
|
188
|
+
return "Central Auth is niet gevonden op dit adres. Controleer CENTRAL_AUTH_URL (basis-URL van de Central Auth-module, zonder pad).";
|
|
189
|
+
}
|
|
190
|
+
if (status === 409 || normalizedBody.includes("not_connected")) {
|
|
191
|
+
return "Er is nog geen actieve Floriday-credential voor deze omgeving in Central Auth. De beheerder moet die daar invoeren.";
|
|
192
|
+
}
|
|
193
|
+
if (status === 400 && normalizedBody.includes("ambiguous_account")) {
|
|
194
|
+
return "Central Auth vond meerdere Floriday-accounts voor deze omgeving; er moet een specifieke account_key worden meegestuurd.";
|
|
195
|
+
}
|
|
196
|
+
if (status === 400) {
|
|
197
|
+
return "Central Auth wees de aanvraag af als ongeldig (ongeldige omgeving of scope-formaat).";
|
|
198
|
+
}
|
|
199
|
+
if (status === 502 || normalizedBody.includes("upstream_error")) {
|
|
200
|
+
return "Floriday weigerde de tokenaanvraag via Central Auth. Controleer de Floriday-credential van deze omgeving in Central Auth.";
|
|
201
|
+
}
|
|
202
|
+
if (status >= 500) {
|
|
203
|
+
return "Central Auth is tijdelijk niet beschikbaar; de volgende ronde probeert het automatisch opnieuw.";
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
186
207
|
async function fetchAndStoreFlorydayOrganizationName(supabase, env, connectionId) {
|
|
187
208
|
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
188
209
|
try {
|
|
@@ -324,9 +345,28 @@ export const getFlorydayTokenHealth = createServerFn({ method: "GET" })
|
|
|
324
345
|
.select("connection_id, attempted_at, ok, error, expires_at")
|
|
325
346
|
.order("attempted_at", { ascending: false })
|
|
326
347
|
.limit(20);
|
|
348
|
+
// Central Auth is de bron van de tokenstatus. Bij client_credentials
|
|
349
|
+
// betekent een verlopen expiry alleen "recent niet gebruikt" — nooit
|
|
350
|
+
// "kapot" — dus geven we die informatie apart mee.
|
|
351
|
+
const { getFlorydayAccountsFromCentral } = await import("./central-auth.server.js");
|
|
352
|
+
const central = {};
|
|
353
|
+
for (const env of ["staging", "live"]) {
|
|
354
|
+
try {
|
|
355
|
+
const res = await getFlorydayAccountsFromCentral(env);
|
|
356
|
+
central[env] = res.ok
|
|
357
|
+
? { ok: true, accounts: res.accounts }
|
|
358
|
+
: { ok: false, status: res.raw.status };
|
|
359
|
+
}
|
|
360
|
+
catch (e) {
|
|
361
|
+
central[env] = { ok: false, error: String(e?.message ?? e).slice(0, 200) };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
327
364
|
return {
|
|
328
365
|
connections: conns ?? [],
|
|
329
366
|
recent: log ?? [],
|
|
367
|
+
central,
|
|
368
|
+
broker: "central-auth",
|
|
369
|
+
note: "Tokens worden door Central Auth beheerd; een verlopen expiry betekent alleen dat het token recent niet is gebruikt.",
|
|
330
370
|
};
|
|
331
371
|
});
|
|
332
372
|
export const forceRefreshFlorydayToken = createServerFn({ method: "POST" })
|
|
@@ -338,15 +378,17 @@ export const forceRefreshFlorydayToken = createServerFn({ method: "POST" })
|
|
|
338
378
|
return input;
|
|
339
379
|
})
|
|
340
380
|
.handler(async ({ data, context }) => {
|
|
341
|
-
|
|
381
|
+
// Vraagt Central Auth om een fris token; niets wordt in de database bewaard.
|
|
342
382
|
await getFlorydayAccessToken(context.supabase, data.environment, { forceRefresh: true });
|
|
343
|
-
const { data:
|
|
344
|
-
.from("
|
|
345
|
-
.select("
|
|
383
|
+
const { data: log } = await context.supabase
|
|
384
|
+
.from("floriday_token_refresh_log")
|
|
385
|
+
.select("expires_at")
|
|
346
386
|
.eq("environment", data.environment)
|
|
347
|
-
.eq("
|
|
387
|
+
.eq("ok", true)
|
|
388
|
+
.order("attempted_at", { ascending: false })
|
|
389
|
+
.limit(1)
|
|
348
390
|
.maybeSingle();
|
|
349
|
-
return { ok: true, expires_at:
|
|
391
|
+
return { ok: true, expires_at: log?.expires_at ?? null };
|
|
350
392
|
});
|
|
351
393
|
export const upsertFlorydayConnection = createServerFn({ method: "POST" })
|
|
352
394
|
.middleware([requireSupabaseAuth])
|
|
@@ -533,14 +575,15 @@ export const testFlorydayConnection = createServerFn({ method: "POST" })
|
|
|
533
575
|
* Volgorde (eerste hit wint):
|
|
534
576
|
* 1. `FLORIDAY_CRON_BASE_URL` — custom domain of override
|
|
535
577
|
* 2. `LOVABLE_PROJECT_ID` → `https://project--{id}.lovable.app`
|
|
536
|
-
*
|
|
578
|
+
*
|
|
579
|
+
* Geen hardcoded fallback: in een nieuwe shell moet dit zichtbaar falen in
|
|
580
|
+
* plaats van stil naar een ander project te schrijven.
|
|
537
581
|
*
|
|
538
582
|
* Bewust NIET terugvallen op `VITE_SUPABASE_PROJECT_ID` /
|
|
539
583
|
* `SUPABASE_PROJECT_ID`: die bevatten de Supabase project-ref, niet de
|
|
540
584
|
* Lovable project-ID — gebruik daarvan was de oorzaak van een stille
|
|
541
585
|
* cron-mismatch waardoor tokens nooit werden ververst.
|
|
542
586
|
*/
|
|
543
|
-
const HARDCODED_LOVABLE_PROJECT_ID = "27b6733b-2e2f-4518-b204-681958559055";
|
|
544
587
|
const MANAGED_FLORYDAY_CRON_JOB_NAMES = new Set([
|
|
545
588
|
"refresh-floriday-tokens-hourly",
|
|
546
589
|
"floriday-connections-sync-twice-daily",
|
|
@@ -569,12 +612,14 @@ const MANAGED_FLORYDAY_CRON_JOB_NAMES = new Set([
|
|
|
569
612
|
"floriday-contract-trade-item-groups-sync-hourly",
|
|
570
613
|
// Referentiedata (schema + code-lijsten)
|
|
571
614
|
"floriday-reference-data-refresh-daily",
|
|
615
|
+
// Voorraad-wachtrij
|
|
616
|
+
"floriday-stock-queue-process",
|
|
572
617
|
]);
|
|
573
618
|
export function resolveFlorydayCronBaseUrl() {
|
|
574
619
|
const custom = process.env.FLORIDAY_CRON_BASE_URL?.trim();
|
|
575
620
|
if (custom)
|
|
576
621
|
return custom.replace(/\/+$/, "");
|
|
577
|
-
const lovableId = process.env.LOVABLE_PROJECT_ID?.trim()
|
|
622
|
+
const lovableId = process.env.LOVABLE_PROJECT_ID?.trim();
|
|
578
623
|
if (!lovableId) {
|
|
579
624
|
throw new Error("Kan cron-URL niet bepalen: zet FLORIDAY_CRON_BASE_URL of LOVABLE_PROJECT_ID");
|
|
580
625
|
}
|
|
@@ -599,7 +644,10 @@ export const setupFlorydayTokenRefreshCron = createServerFn({ method: "POST" })
|
|
|
599
644
|
export const getFlorydaySettings = createServerFn({ method: "GET" })
|
|
600
645
|
.middleware([requireSupabaseAuth])
|
|
601
646
|
.handler(async ({ context }) => {
|
|
602
|
-
|
|
647
|
+
// `floriday_settings` staat onder default-deny RLS; lezen met de user-client
|
|
648
|
+
// levert stil 0 rijen op (en dus de "staging"-fallback). Service-role lezen.
|
|
649
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
650
|
+
const env = await getActiveFlorydayEnvironment(supabaseAdmin);
|
|
603
651
|
return { active_environment: env };
|
|
604
652
|
});
|
|
605
653
|
export const setFlorydayActiveEnvironment = createServerFn({ method: "POST" })
|
|
@@ -611,7 +659,8 @@ export const setFlorydayActiveEnvironment = createServerFn({ method: "POST" })
|
|
|
611
659
|
return input;
|
|
612
660
|
})
|
|
613
661
|
.handler(async ({ data, context }) => {
|
|
614
|
-
const {
|
|
662
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
663
|
+
const { error } = await supabaseAdmin
|
|
615
664
|
.from("floriday_settings")
|
|
616
665
|
.upsert({ id: 1, active_environment: data.environment });
|
|
617
666
|
if (error)
|
|
@@ -996,6 +1045,22 @@ export const setupFlorydayConnectionsSyncCron = createServerFn({ method: "POST"
|
|
|
996
1045
|
const { jobid, url } = await scheduleFlorydayConnectionsSyncInternal(context.supabase);
|
|
997
1046
|
return { ok: true, jobid, url };
|
|
998
1047
|
});
|
|
1048
|
+
async function scheduleFlorydayStockQueueInternal(supabase) {
|
|
1049
|
+
const apikey = process.env.SUPABASE_PUBLISHABLE_KEY ?? process.env.VITE_SUPABASE_PUBLISHABLE_KEY;
|
|
1050
|
+
if (!apikey)
|
|
1051
|
+
throw new Error("Supabase anon key ontbreekt in server-env");
|
|
1052
|
+
const url = `${resolveFlorydayCronBaseUrl()}/api/public/cron/process-stock-queue`;
|
|
1053
|
+
const { data, error } = await supabase.rpc("schedule_floriday_stock_queue_process", { p_url: url, p_apikey: apikey });
|
|
1054
|
+
if (error)
|
|
1055
|
+
throw new Error(error.message);
|
|
1056
|
+
return { jobid: data, url };
|
|
1057
|
+
}
|
|
1058
|
+
export const setupFlorydayStockQueueCron = createServerFn({ method: "POST" })
|
|
1059
|
+
.middleware([requireSupabaseAuth])
|
|
1060
|
+
.handler(async ({ context }) => {
|
|
1061
|
+
const { jobid, url } = await scheduleFlorydayStockQueueInternal(context.supabase);
|
|
1062
|
+
return { ok: true, jobid, url };
|
|
1063
|
+
});
|
|
999
1064
|
/**
|
|
1000
1065
|
* Geeft een snapshot van de geplande Floriday-cron jobs zodat de UI kan
|
|
1001
1066
|
* tonen welke URL is ingepland (zo zien we direct als er een verkeerde
|
|
@@ -1021,7 +1086,7 @@ export const getFlorydayCronStatus = createServerFn({ method: "GET" })
|
|
|
1021
1086
|
return { ok: true, jobs, expectedBase };
|
|
1022
1087
|
});
|
|
1023
1088
|
/**
|
|
1024
|
-
* Herinstalleert
|
|
1089
|
+
* Herinstalleert de Floriday-crons in één call met de correct
|
|
1025
1090
|
* gedetecteerde base-URL. Idempotent: bestaande jobs worden overschreven.
|
|
1026
1091
|
*/
|
|
1027
1092
|
export const reinstallFlorydayCrons = createServerFn({ method: "POST" })
|
|
@@ -1029,7 +1094,8 @@ export const reinstallFlorydayCrons = createServerFn({ method: "POST" })
|
|
|
1029
1094
|
.handler(async ({ context }) => {
|
|
1030
1095
|
const token = await scheduleFlorydayTokenRefreshInternal(context.supabase);
|
|
1031
1096
|
const conn = await scheduleFlorydayConnectionsSyncInternal(context.supabase);
|
|
1032
|
-
|
|
1097
|
+
const stockQueue = await scheduleFlorydayStockQueueInternal(context.supabase);
|
|
1098
|
+
return { ok: true, token, connections: conn, stockQueue };
|
|
1033
1099
|
});
|
|
1034
1100
|
function redactHeadersForWriteLog(headers) {
|
|
1035
1101
|
const out = {};
|