@flowselections/floriday-authenticatie-module 1.0.88 → 1.0.91
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/floriday/FlorydayConnectionPage.d.ts.map +1 -1
- package/dist-lib/components/floriday/FlorydayConnectionPage.js +2 -1
- package/dist-lib/components/settings/FloricodeHealthBanner.d.ts +5 -0
- package/dist-lib/components/settings/FloricodeHealthBanner.d.ts.map +1 -0
- package/dist-lib/components/settings/FloricodeHealthBanner.js +27 -0
- package/dist-lib/components/settings/FloricodeSettingsCard.d.ts.map +1 -1
- package/dist-lib/components/settings/FloricodeSettingsCard.js +17 -2
- package/dist-lib/index.d.ts +1 -0
- package/dist-lib/index.d.ts.map +1 -1
- package/dist-lib/index.js +1 -0
- package/dist-lib/integrations/supabase/types.d.ts +419 -0
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/lib/cron/sync-floricode-all.d.ts.map +1 -1
- package/dist-lib/lib/cron/sync-floricode-all.js +0 -2
- package/dist-lib/lib/customer-sticker-fields.d.ts +5 -0
- package/dist-lib/lib/customer-sticker-fields.d.ts.map +1 -1
- package/dist-lib/lib/customer-sticker-fields.js +7 -0
- package/dist-lib/lib/floricode-regulatory-features.d.ts +1 -0
- package/dist-lib/lib/floricode-regulatory-features.d.ts.map +1 -1
- package/dist-lib/lib/floricode-regulatory-features.js +27 -12
- package/dist-lib/lib/floricode-sync-plan.d.ts +64 -0
- package/dist-lib/lib/floricode-sync-plan.d.ts.map +1 -0
- package/dist-lib/lib/floricode-sync-plan.js +54 -0
- package/dist-lib/lib/floricode-sync.server.d.ts +39 -7
- package/dist-lib/lib/floricode-sync.server.d.ts.map +1 -1
- package/dist-lib/lib/floricode-sync.server.js +131 -86
- package/dist-lib/lib/floricode.functions.d.ts +14 -0
- package/dist-lib/lib/floricode.functions.d.ts.map +1 -1
- package/dist-lib/lib/floricode.functions.js +26 -9
- package/dist-lib/lib/floriday-stickers.functions.js +2 -2
- package/dist-lib/lib/gateway/webhook-subscribe.d.ts.map +1 -1
- package/dist-lib/lib/gateway/webhook-subscribe.js +11 -1
- package/dist-lib/lib/module-version.d.ts +1 -1
- package/dist-lib/lib/module-version.js +1 -1
- package/package.json +1 -1
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// ============================================================================
|
|
14
14
|
import { supabaseAdmin } from "../integrations/supabase/client.server.js";
|
|
15
15
|
import { getFloricodeAccessToken } from "./floricode-credentials.server.js";
|
|
16
|
+
import { FLORICODE_ROUND_BUDGET_MS, floricodeCronSchedule, isIncomplete, laterIso, nightlyDue, stateAfterPage, } from "./floricode-sync-plan.js";
|
|
16
17
|
export const FLORICODE_RESOURCES = [
|
|
17
18
|
// VBN
|
|
18
19
|
{ resource: "FeatureType", endpoint: "/v2/VBN/FeatureType", scope: "vbn_featuretype:read", keyColumns: ["id"] },
|
|
@@ -38,14 +39,23 @@ function buildRecordId(def, row) {
|
|
|
38
39
|
return def.keyColumns.map((k) => String(row[k] ?? "")).join("|");
|
|
39
40
|
}
|
|
40
41
|
async function fetchPage(def, token, opts) {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
const build = (withCount) => {
|
|
43
|
+
const url = new URL(BASE_URL + def.endpoint);
|
|
44
|
+
url.searchParams.set("$top", String(opts.top));
|
|
45
|
+
url.searchParams.set("$skip", String(opts.skip));
|
|
46
|
+
url.searchParams.set("$orderby", ["change_date_time", ...def.keyColumns].join(","));
|
|
47
|
+
if (opts.sinceIso)
|
|
48
|
+
url.searchParams.set("$filter", `change_date_time gt ${opts.sinceIso}`);
|
|
49
|
+
if (withCount)
|
|
50
|
+
url.searchParams.set("$count", "true");
|
|
51
|
+
return url.toString();
|
|
52
|
+
};
|
|
53
|
+
// Totaal alleen op de eerste pagina vragen; ondersteunt de API $count niet, dan zonder.
|
|
54
|
+
const wantCount = opts.skip === 0;
|
|
55
|
+
let res = await fetch(build(wantCount), { headers: { authorization: `Bearer ${token}` } });
|
|
56
|
+
if (wantCount && res.status === 400) {
|
|
57
|
+
res = await fetch(build(false), { headers: { authorization: `Bearer ${token}` } });
|
|
47
58
|
}
|
|
48
|
-
const res = await fetch(url.toString(), { headers: { authorization: `Bearer ${token}` } });
|
|
49
59
|
const json = await res.json().catch(() => ({}));
|
|
50
60
|
if (!res.ok) {
|
|
51
61
|
const upstream = json?.error?.message ?? json?.error ?? res.statusText;
|
|
@@ -57,7 +67,8 @@ async function fetchPage(def, token, opts) {
|
|
|
57
67
|
}
|
|
58
68
|
throw new Error(`Floricode ${def.resource} pagina mislukt (${res.status}): ${upstream}`);
|
|
59
69
|
}
|
|
60
|
-
|
|
70
|
+
const c = Number(json?.["@odata.count"]);
|
|
71
|
+
return { rows: Array.isArray(json?.value) ? json.value : [], total: Number.isFinite(c) ? c : null };
|
|
61
72
|
}
|
|
62
73
|
async function upsertRows(def, rows) {
|
|
63
74
|
if (rows.length === 0)
|
|
@@ -95,117 +106,151 @@ async function upsertRows(def, rows) {
|
|
|
95
106
|
}
|
|
96
107
|
return done;
|
|
97
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Haalt pagina's op tot de bron compleet is of het tijdsbudget op is.
|
|
111
|
+
* Positie (cursor_skip) gaat alleen vooruit na een opgeslagen pagina; de
|
|
112
|
+
* watermerkdatum verschuift pas als de bron compleet is (zie stateAfterPage).
|
|
113
|
+
*/
|
|
98
114
|
export async function syncFloricodeResource(resource, opts = {}) {
|
|
99
115
|
const def = FLORICODE_RESOURCES.find((r) => r.resource === resource);
|
|
100
116
|
if (!def)
|
|
101
117
|
throw new Error(`Onbekende Floricode-resource: ${resource}`);
|
|
102
118
|
const batchSize = opts.batchSize ?? DEFAULT_BATCH;
|
|
103
|
-
const maxBatches =
|
|
119
|
+
const maxBatches = opts.maxBatches && opts.maxBatches > 0 ? opts.maxBatches : Infinity;
|
|
120
|
+
const deadline = opts.deadline ?? Date.now() + FLORICODE_ROUND_BUDGET_MS;
|
|
104
121
|
const resume = opts.resume ?? true;
|
|
105
122
|
const startedAt = Date.now();
|
|
106
123
|
const { data: state } = await supabaseAdmin
|
|
107
124
|
.from("floricode_sync_state")
|
|
108
|
-
.select("cursor_skip, last_change_date_time")
|
|
125
|
+
.select("cursor_skip, last_change_date_time, pending_change_date_time, status")
|
|
109
126
|
.eq("resource", def.resource)
|
|
110
127
|
.maybeSingle();
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
128
|
+
const s = state;
|
|
129
|
+
const resuming = resume && (s?.status === "running" || s?.status === "error");
|
|
130
|
+
let skip = resuming ? Number(s?.cursor_skip ?? 0) : 0;
|
|
131
|
+
const sinceIso = s?.last_change_date_time ?? null;
|
|
132
|
+
let pending = resuming ? s?.pending_change_date_time ?? null : null;
|
|
133
|
+
const save = async (patch) => {
|
|
134
|
+
const { error } = await supabaseAdmin.from("floricode_sync_state").upsert({ resource: def.resource, updated_at: new Date().toISOString(), ...patch }, { onConflict: "resource" });
|
|
135
|
+
if (error)
|
|
136
|
+
throw new Error(`Status ${def.resource} opslaan mislukt: ${error.message}`);
|
|
137
|
+
};
|
|
138
|
+
await save({ status: "running", error_message: null, cursor_skip: skip, pending_change_date_time: pending });
|
|
122
139
|
let fetched = 0;
|
|
123
140
|
let upserted = 0;
|
|
124
141
|
let batches = 0;
|
|
125
|
-
let latestChange = sinceIso;
|
|
126
142
|
let finished = false;
|
|
143
|
+
let watermark = sinceIso;
|
|
127
144
|
try {
|
|
128
|
-
|
|
129
|
-
|
|
145
|
+
const token = await getFloricodeAccessToken(def.scope);
|
|
146
|
+
while (batches < maxBatches && Date.now() < deadline) {
|
|
147
|
+
const { rows: page, total } = await fetchPage(def, token, { skip, top: batchSize, sinceIso });
|
|
130
148
|
batches++;
|
|
131
|
-
if (page.length === 0) {
|
|
132
|
-
finished = true;
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
149
|
fetched += page.length;
|
|
136
150
|
upserted += await upsertRows(def, page);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
// Worker-timeout hervat vanaf de laatste geslaagde pagina.
|
|
145
|
-
await supabaseAdmin
|
|
146
|
-
.from("floricode_sync_state")
|
|
147
|
-
.upsert({
|
|
148
|
-
resource: def.resource,
|
|
149
|
-
status: "running",
|
|
151
|
+
let pageMax = null;
|
|
152
|
+
for (const row of page)
|
|
153
|
+
pageMax = laterIso(pageMax, row?.change_date_time ?? null);
|
|
154
|
+
const next = stateAfterPage({ startWatermark: sinceIso, pending, pageMax, skipBefore: skip, pageSize: page.length, batchSize });
|
|
155
|
+
const now = new Date().toISOString();
|
|
156
|
+
await save({
|
|
157
|
+
status: next.status,
|
|
150
158
|
error_message: null,
|
|
151
|
-
cursor_skip:
|
|
152
|
-
last_change_date_time:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
159
|
+
cursor_skip: next.cursor_skip,
|
|
160
|
+
last_change_date_time: next.last_change_date_time,
|
|
161
|
+
pending_change_date_time: next.pending_change_date_time,
|
|
162
|
+
last_sync_at: now,
|
|
163
|
+
...(total != null && !sinceIso ? { total_count: total } : {}),
|
|
164
|
+
...(next.finished ? { completed_at: now } : {}),
|
|
165
|
+
});
|
|
166
|
+
skip = next.cursor_skip;
|
|
167
|
+
pending = next.pending_change_date_time;
|
|
168
|
+
watermark = next.last_change_date_time;
|
|
169
|
+
if (next.finished) {
|
|
157
170
|
finished = true;
|
|
158
171
|
break;
|
|
159
172
|
}
|
|
160
173
|
}
|
|
161
|
-
await supabaseAdmin
|
|
162
|
-
.from("floricode_sync_state")
|
|
163
|
-
.upsert({
|
|
164
|
-
resource: def.resource,
|
|
165
|
-
status: "ok",
|
|
166
|
-
error_message: null,
|
|
167
|
-
last_sync_at: new Date().toISOString(),
|
|
168
|
-
cursor_skip: finished ? 0 : skip,
|
|
169
|
-
last_change_date_time: finished ? latestChange : sinceIso,
|
|
170
|
-
updated_at: new Date().toISOString(),
|
|
171
|
-
}, { onConflict: "resource" });
|
|
172
174
|
}
|
|
173
175
|
catch (e) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
.upsert({
|
|
177
|
-
resource: def.resource,
|
|
178
|
-
status: "error",
|
|
179
|
-
error_message: String(e?.message ?? e),
|
|
180
|
-
cursor_skip: skip,
|
|
181
|
-
updated_at: new Date().toISOString(),
|
|
182
|
-
}, { onConflict: "resource" });
|
|
176
|
+
// Positie blijft op de laatste gelukte pagina; status 'error', volgende ronde hervat.
|
|
177
|
+
await save({ status: "error", error_message: String(e?.message ?? e) }).catch(() => { });
|
|
183
178
|
throw e;
|
|
184
179
|
}
|
|
185
|
-
return {
|
|
186
|
-
ok: true,
|
|
187
|
-
resource: def.resource,
|
|
188
|
-
fetched,
|
|
189
|
-
upserted,
|
|
190
|
-
batches,
|
|
191
|
-
finished,
|
|
192
|
-
nextWatermark: latestChange,
|
|
193
|
-
durationMs: Date.now() - startedAt,
|
|
194
|
-
};
|
|
180
|
+
return { ok: true, resource: def.resource, fetched, upserted, batches, finished, nextWatermark: watermark, durationMs: Date.now() - startedAt };
|
|
195
181
|
}
|
|
196
|
-
|
|
182
|
+
/**
|
|
183
|
+
* Eén cron-tik: eerst onvolledige bronnen afmaken binnen het tijdsbudget;
|
|
184
|
+
* zijn alle bronnen compleet, dan hooguit eens per 20 uur alleen wijzigingen.
|
|
185
|
+
* Stelt daarna het cron-schema in (elke 10 min zolang iets bezig is, anders 's nachts).
|
|
186
|
+
*/
|
|
187
|
+
export async function syncAllFloricodeResources(opts = {}) {
|
|
188
|
+
const deadline = Date.now() + (opts.budgetMs ?? FLORICODE_ROUND_BUDGET_MS);
|
|
189
|
+
const all = [...FLORICODE_RESOURCES.map((r) => r.resource), "RegulatoryFeatureType", "RegulationType"];
|
|
190
|
+
const readStates = async () => {
|
|
191
|
+
const { data } = await supabaseAdmin.from("floricode_sync_state").select("resource,status,completed_at,last_sync_at").in("resource", all);
|
|
192
|
+
return new Map((data ?? []).map((r) => [r.resource, r]));
|
|
193
|
+
};
|
|
194
|
+
const states = await readStates();
|
|
195
|
+
const incompleteBefore = all.filter((r) => isIncomplete(states.get(r)?.status));
|
|
196
|
+
const nightly = incompleteBefore.length === 0 &&
|
|
197
|
+
all.some((r) => nightlyDue(states.get(r)?.completed_at ?? states.get(r)?.last_sync_at ?? null, Date.now()));
|
|
198
|
+
const todo = incompleteBefore.length > 0 ? incompleteBefore : nightly ? all : [];
|
|
197
199
|
const results = [];
|
|
198
|
-
|
|
200
|
+
const sb = supabaseAdmin;
|
|
201
|
+
for (const resource of todo) {
|
|
202
|
+
if (Date.now() >= deadline)
|
|
203
|
+
break;
|
|
199
204
|
try {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
+
if (resource === "RegulationType") {
|
|
206
|
+
const { syncFloricodeRegulationTypes } = await import("./floricode-regulatory-features.js");
|
|
207
|
+
const r = await syncFloricodeRegulationTypes(sb);
|
|
208
|
+
results.push({ resource, ok: true, fetched: r?.upserted ?? 0, upserted: r?.upserted ?? 0, batches: 1, finished: true, nextWatermark: null, durationMs: 0 });
|
|
209
|
+
}
|
|
210
|
+
else if (resource === "RegulatoryFeatureType") {
|
|
211
|
+
const { syncFloricodeRegulatoryFeatures } = await import("./floricode-regulatory-features.js");
|
|
212
|
+
const r = await syncFloricodeRegulatoryFeatures(sb, { deadline });
|
|
213
|
+
results.push({ resource, ok: true, fetched: r.fetched, upserted: r.upserted, batches: r.batches, finished: r.completed, nextWatermark: null, durationMs: r.durationMs });
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
results.push(await syncFloricodeResource(resource, { batchSize: opts.batchSize, deadline }));
|
|
217
|
+
}
|
|
205
218
|
}
|
|
206
219
|
catch (e) {
|
|
207
|
-
results.push({ resource
|
|
220
|
+
results.push({ resource, ok: false, error: String(e?.message ?? e) });
|
|
208
221
|
}
|
|
209
222
|
}
|
|
210
|
-
|
|
223
|
+
const after = await readStates();
|
|
224
|
+
const incomplete = all.filter((r) => isIncomplete(after.get(r)?.status));
|
|
225
|
+
const schedule = floricodeCronSchedule(incomplete.length > 0);
|
|
226
|
+
const { error } = await supabaseAdmin.rpc("floricode_sync_cron_set_schedule", { p_schedule: schedule });
|
|
227
|
+
return { ok: true, results, incomplete, schedule: error ? null : schedule };
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Plant de Floricode-ronde in als die nog niet bestaat (uurlijks aangeroepen).
|
|
231
|
+
* Adres via resolveVerifiedFlorydayCronUrl; ontbreekt het Shell-adres, dan
|
|
232
|
+
* niet inplannen (fail closed) — de stilstand-melding wordt daar vastgelegd.
|
|
233
|
+
*/
|
|
234
|
+
export async function ensureFloricodeSyncCron(opts = {}) {
|
|
235
|
+
const { data } = await supabaseAdmin.rpc("floricode_sync_cron_status");
|
|
236
|
+
const row = Array.isArray(data) ? data[0] : data;
|
|
237
|
+
if (row?.scheduled && !opts.force)
|
|
238
|
+
return { ok: true, scheduled: true, schedule: row.schedule ?? null };
|
|
239
|
+
const apikey = process.env.SUPABASE_PUBLISHABLE_KEY;
|
|
240
|
+
if (!apikey)
|
|
241
|
+
return { ok: false, scheduled: false, schedule: null, error: "SUPABASE_PUBLISHABLE_KEY ontbreekt" };
|
|
242
|
+
try {
|
|
243
|
+
const { resolveVerifiedFlorydayCronUrl } = await import("./floriday.functions.js");
|
|
244
|
+
const url = await resolveVerifiedFlorydayCronUrl("/api/public/cron/sync-floricode-all");
|
|
245
|
+
const { data: st } = await supabaseAdmin.from("floricode_sync_state").select("status");
|
|
246
|
+
const anyIncomplete = (st ?? []).length === 0 || (st ?? []).some((r) => isIncomplete(r.status));
|
|
247
|
+
const schedule = floricodeCronSchedule(anyIncomplete);
|
|
248
|
+
const { error } = await supabaseAdmin.rpc("floricode_sync_cron_schedule", { p_url: url, p_apikey: apikey, p_schedule: schedule });
|
|
249
|
+
if (error)
|
|
250
|
+
return { ok: false, scheduled: false, schedule: null, error: error.message };
|
|
251
|
+
return { ok: true, scheduled: true, schedule };
|
|
252
|
+
}
|
|
253
|
+
catch (e) {
|
|
254
|
+
return { ok: false, scheduled: false, schedule: null, error: String(e?.message ?? e) };
|
|
255
|
+
}
|
|
211
256
|
}
|
|
@@ -46,6 +46,8 @@ export interface FloricodeResourceStatus {
|
|
|
46
46
|
cursorSkip: number | null;
|
|
47
47
|
errorMessage: string | null;
|
|
48
48
|
rowCount: number;
|
|
49
|
+
completedAt: string | null;
|
|
50
|
+
totalCount: number | null;
|
|
49
51
|
}
|
|
50
52
|
export declare const getFloricodeSyncStatus: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
|
|
51
53
|
supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
|
|
@@ -69,4 +71,16 @@ export declare const resetFloricodeResourceCursor: import("@tanstack/start-clien
|
|
|
69
71
|
ok: true;
|
|
70
72
|
resource: string;
|
|
71
73
|
}>>;
|
|
74
|
+
/** Stand van de automatische Floricode-ronde (ingepland? Shell-adres?). */
|
|
75
|
+
export declare const getFloricodeCronStatus: import("@tanstack/start-client-core").OptionalFetcher<readonly [import("@tanstack/start-client-core").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
|
|
76
|
+
supabase: import("../integrations/supabase/auth-middleware.js").AuthedSupabaseClient;
|
|
77
|
+
userId: string;
|
|
78
|
+
claims: import("@supabase/auth-js").JwtPayload;
|
|
79
|
+
}, undefined, undefined, undefined>], undefined, Promise<{
|
|
80
|
+
scheduled: boolean;
|
|
81
|
+
schedule: string | null;
|
|
82
|
+
lastRunAt: string | null;
|
|
83
|
+
lastRunStatus: string | null;
|
|
84
|
+
shellAddressMissing: boolean;
|
|
85
|
+
}>>;
|
|
72
86
|
//# sourceMappingURL=floricode.functions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floricode.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floricode.functions.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,eAAO,MAAM,oBAAoB;;;;4EAgC7B,CAAC;AAEL,eAAO,MAAM,wBAAwB;;;;;;;;;GAgCjC,CAAC;AAEL,eAAO,MAAM,yBAAyB;;;;;;GAmBlC,CAAC;AAEL,eAAO,MAAM,uBAAuB;;;;;;;;GAKhC,CAAC;AAEL,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"floricode.functions.d.ts","sourceRoot":"","sources":["../../src/lib/floricode.functions.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,eAAO,MAAM,oBAAoB;;;;4EAgC7B,CAAC;AAEL,eAAO,MAAM,wBAAwB;;;;;;;;;GAgCjC,CAAC;AAEL,eAAO,MAAM,yBAAyB;;;;;;GAmBlC,CAAC;AAEL,eAAO,MAAM,uBAAuB;;;;;;;;GAKhC,CAAC;AAEL,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,eAAO,MAAM,sBAAsB;;;;oFAkD/B,CAAC;AAEL,eAAO,MAAM,4BAA4B;;;;;;gBAyBrC,CAAC;AAEL,eAAO,MAAM,4BAA4B;;;;;;;;;GAoBrC,CAAC;AAEL,2EAA2E;AAC3E,eAAO,MAAM,sBAAsB;;;;;;;;;;GAe/B,CAAC"}
|
|
@@ -98,7 +98,7 @@ export const getFloricodeSyncStatus = createServerFn({ method: "GET" })
|
|
|
98
98
|
const { FLORICODE_RESOURCES } = await import("./floricode-sync.server.js");
|
|
99
99
|
const stateRows = (await supabaseAdmin
|
|
100
100
|
.from("floricode_sync_state")
|
|
101
|
-
.select("resource, status, last_sync_at, last_change_date_time, cursor_skip, error_message")).data;
|
|
101
|
+
.select("resource, status, last_sync_at, last_change_date_time, cursor_skip, error_message, completed_at, total_count")).data;
|
|
102
102
|
let countRows = [];
|
|
103
103
|
try {
|
|
104
104
|
const res = await supabaseAdmin.rpc("get_floricode_record_counts");
|
|
@@ -130,6 +130,8 @@ export const getFloricodeSyncStatus = createServerFn({ method: "GET" })
|
|
|
130
130
|
cursorSkip: s?.cursor_skip ?? null,
|
|
131
131
|
errorMessage: s?.error_message ?? null,
|
|
132
132
|
rowCount: countByRes.get(resource) ?? 0,
|
|
133
|
+
completedAt: s?.completed_at ?? null,
|
|
134
|
+
totalCount: s?.total_count != null ? Number(s.total_count) : null,
|
|
133
135
|
});
|
|
134
136
|
};
|
|
135
137
|
// Include the known registry plus the two pre-existing resources
|
|
@@ -150,7 +152,7 @@ export const triggerFloricodeResourceSync = createServerFn({ method: "POST" })
|
|
|
150
152
|
if (resource === "RegulatoryFeatureType") {
|
|
151
153
|
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
152
154
|
const { syncFloricodeRegulatoryFeatures } = await import("./floricode-regulatory-features.js");
|
|
153
|
-
const r = await syncFloricodeRegulatoryFeatures(supabaseAdmin, {
|
|
155
|
+
const r = await syncFloricodeRegulatoryFeatures(supabaseAdmin, {});
|
|
154
156
|
return { ok: true, resource, ...r };
|
|
155
157
|
}
|
|
156
158
|
if (resource === "ProductFeature") {
|
|
@@ -160,15 +162,13 @@ export const triggerFloricodeResourceSync = createServerFn({ method: "POST" })
|
|
|
160
162
|
return { ok: true, resource, ...r };
|
|
161
163
|
}
|
|
162
164
|
if (resource === "RegulationType") {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
message: "RegulationType wordt automatisch bijgewerkt tijdens de RegulatoryFeatureType-synchronisatie.",
|
|
168
|
-
};
|
|
165
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
166
|
+
const { syncFloricodeRegulationTypes } = await import("./floricode-regulatory-features.js");
|
|
167
|
+
const r = await syncFloricodeRegulationTypes(supabaseAdmin);
|
|
168
|
+
return { ...r, ok: true, resource };
|
|
169
169
|
}
|
|
170
170
|
const { syncFloricodeResource } = await import("./floricode-sync.server.js");
|
|
171
|
-
return syncFloricodeResource(resource, {
|
|
171
|
+
return syncFloricodeResource(resource, {});
|
|
172
172
|
});
|
|
173
173
|
export const resetFloricodeResourceCursor = createServerFn({ method: "POST" })
|
|
174
174
|
.middleware([requireSupabaseAuth])
|
|
@@ -189,3 +189,20 @@ export const resetFloricodeResourceCursor = createServerFn({ method: "POST" })
|
|
|
189
189
|
throw new Error(error.message);
|
|
190
190
|
return { ok: true, resource: data.resource };
|
|
191
191
|
});
|
|
192
|
+
/** Stand van de automatische Floricode-ronde (ingepland? Shell-adres?). */
|
|
193
|
+
export const getFloricodeCronStatus = createServerFn({ method: "GET" })
|
|
194
|
+
.middleware([requireSupabaseAuth])
|
|
195
|
+
.handler(async () => {
|
|
196
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
197
|
+
const { data } = await supabaseAdmin.rpc("floricode_sync_cron_status");
|
|
198
|
+
const row = Array.isArray(data) ? data[0] : data;
|
|
199
|
+
const { readCronBaseStall } = await import("./floriday.functions.js");
|
|
200
|
+
const stall = await readCronBaseStall().catch(() => ({ stalled: false, since: null, message: null }));
|
|
201
|
+
return {
|
|
202
|
+
scheduled: Boolean(row?.scheduled),
|
|
203
|
+
schedule: row?.schedule ?? null,
|
|
204
|
+
lastRunAt: row?.last_run_at ?? null,
|
|
205
|
+
lastRunStatus: row?.last_run_status ?? null,
|
|
206
|
+
shellAddressMissing: Boolean(stall.stalled),
|
|
207
|
+
};
|
|
208
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { customerStickerFields } from "./customer-sticker-fields.js";
|
|
1
|
+
import { customerStickerFields, customerStickerPdfPath } from "./customer-sticker-fields.js";
|
|
2
2
|
import { storeVersionedDocument } from "./document-version.js";
|
|
3
3
|
// ============================================================================
|
|
4
4
|
// Floriday customer stickers — lazy PDF fetch + storage cache
|
|
@@ -53,7 +53,7 @@ async function fetchStickerToStorage(supabase, env, connectionId, stickerId) {
|
|
|
53
53
|
}
|
|
54
54
|
// 2) Miss → PDF ophalen bij Floriday. De API levert óf een binaire PDF,
|
|
55
55
|
// óf een JSON-object met base64-data. We ondersteunen beide.
|
|
56
|
-
const path =
|
|
56
|
+
const path = customerStickerPdfPath(SUPPLIERS_API_VERSION, stickerId);
|
|
57
57
|
const res = await florydayFetch(supabase, env, path, {
|
|
58
58
|
cache: "no-store",
|
|
59
59
|
headers: { Accept: "application/pdf, application/json" },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-subscribe.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/webhook-subscribe.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,KAAK,GAAG,GAAG,SAAS,GAAG,MAAM,CAAC;AAM9B,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;CACtE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAUvH;AAiBD,iFAAiF;AACjF,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBnF;AAuBD,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,GAAG,EAChB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACrG,OAAO,CAAC,YAAY,CAAC,CAmHvB;AA6BD,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE,cAAc;iBAKlC,MAAM;qBAc3C;AAED,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"webhook-subscribe.d.ts","sourceRoot":"","sources":["../../../src/lib/gateway/webhook-subscribe.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,KAAK,GAAG,GAAG,SAAS,GAAG,MAAM,CAAC;AAM9B,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;CACtE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAUvH;AAiBD,iFAAiF;AACjF,wBAAsB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBnF;AAuBD,wBAAsB,iCAAiC,CACrD,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,GAAG,EAChB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACrG,OAAO,CAAC,YAAY,CAAC,CAmHvB;AA6BD,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE,cAAc;iBAKlC,MAAM;qBAc3C;AAED,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,CA+BhH"}
|
|
@@ -260,8 +260,18 @@ export async function handleEnsureFloridayWebhooks(request, supabase) {
|
|
|
260
260
|
return { ok: false, missing: [], error: String(e?.message ?? e) };
|
|
261
261
|
}
|
|
262
262
|
})();
|
|
263
|
+
// Uurlijkse meelift: Floricode-ronde ingepland houden.
|
|
264
|
+
const floricodeCron = await (async () => {
|
|
265
|
+
try {
|
|
266
|
+
const { ensureFloricodeSyncCron } = await import("../floricode-sync.server.js");
|
|
267
|
+
return await ensureFloricodeSyncCron();
|
|
268
|
+
}
|
|
269
|
+
catch (e) {
|
|
270
|
+
return { ok: false, scheduled: false, schedule: null, error: String(e?.message ?? e) };
|
|
271
|
+
}
|
|
272
|
+
})();
|
|
263
273
|
const { runWebhookRetryRound } = await import("./webhook.js");
|
|
264
274
|
const retry = await runWebhookRetryRound(supabase).catch(() => ({ picked: 0 }));
|
|
265
|
-
return Response.json({ ok: true, resource: "webhook-ensure", results, retried: retry.picked, shellRoutes });
|
|
275
|
+
return Response.json({ ok: true, resource: "webhook-ensure", results, retried: retry.picked, shellRoutes, floricodeCron });
|
|
266
276
|
});
|
|
267
277
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const MODULE_VERSION = "1.0.
|
|
1
|
+
export declare const MODULE_VERSION = "1.0.91";
|
|
2
2
|
//# sourceMappingURL=module-version.d.ts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// GEGENEREERD door scripts/write-module-version.mjs uit package.json — niet handmatig wijzigen.
|
|
2
|
-
export const MODULE_VERSION = "1.0.
|
|
2
|
+
export const MODULE_VERSION = "1.0.91";
|