@flowselections/floriday-verkoop-module 1.0.8 → 1.0.10
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/FlorydaySyncHealthBanner.d.ts +9 -0
- package/dist-lib/components/floriday/FlorydaySyncHealthBanner.d.ts.map +1 -0
- package/dist-lib/components/floriday/FlorydaySyncHealthBanner.js +26 -0
- package/dist-lib/components/verkoop/CreateOrderPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/CreateOrderPage.js +6 -9
- package/dist-lib/components/verkoop/VerkoopOrdersPage.d.ts.map +1 -1
- package/dist-lib/components/verkoop/VerkoopOrdersPage.js +2 -1
- package/dist-lib/integrations/supabase/auth-middleware.d.ts +2797 -144
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +2797 -144
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +2797 -144
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +2870 -134
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +2 -0
- package/dist-lib/lib/floriday-gateway.server.d.ts +42 -0
- package/dist-lib/lib/floriday-gateway.server.d.ts.map +1 -0
- package/dist-lib/lib/floriday-gateway.server.js +217 -0
- package/dist-lib/lib/floriday-public.functions.d.ts +65056 -0
- package/dist-lib/lib/floriday-public.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-public.functions.js +210 -0
- package/dist-lib/lib/floriday-sync.d.ts.map +1 -1
- package/dist-lib/lib/floriday-sync.js +18 -79
- package/dist-lib/lib/floriday-writes.functions.d.ts +46453 -0
- package/dist-lib/lib/floriday-writes.functions.d.ts.map +1 -0
- package/dist-lib/lib/floriday-writes.functions.js +110 -0
- package/dist-lib/lib/salesorders.functions.d.ts +17670 -9710
- package/dist-lib/lib/salesorders.functions.d.ts.map +1 -1
- package/dist-lib/lib/salesorders.functions.js +44 -86
- package/dist-lib/lib/tradeitems.functions.d.ts +2797 -144
- package/dist-lib/lib/tradeitems.functions.d.ts.map +1 -1
- package/dist-lib/lib/useSalesOrdersQuery.d.ts.map +1 -1
- package/dist-lib/lib/useSalesOrdersQuery.js +11 -25
- package/package.json +2 -2
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// Public-read helpers (server functions) — used by all other modules.
|
|
2
|
+
// Reads gaan via de lokale Supabase-cache; geen directe Floriday API-calls.
|
|
3
|
+
import { createServerFn } from "@tanstack/react-start";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware";
|
|
6
|
+
const EnvSchema = z.enum(["staging", "live"]);
|
|
7
|
+
// Toegestane cache-entities en hun tabel-mapping.
|
|
8
|
+
const ENTITY_TABLE = {
|
|
9
|
+
"sales-orders": "floriday_salesorders_cache",
|
|
10
|
+
"trade-items": "floriday_trade_items_cache",
|
|
11
|
+
"customer-organizations": "floriday_customers_cache",
|
|
12
|
+
carriers: "floriday_carriers_cache",
|
|
13
|
+
"fulfillment-orders": "floriday_fulfillment_orders_cache",
|
|
14
|
+
"delivery-orders": "floriday_delivery_orders_cache",
|
|
15
|
+
"clock-supplies": "floriday_clock_supplies_cache",
|
|
16
|
+
"clock-presales": "floriday_clock_presales_cache",
|
|
17
|
+
contracts: "floriday_contracts_cache",
|
|
18
|
+
"blanket-orders": "floriday_blanket_orders_cache",
|
|
19
|
+
warehouses: "floriday_warehouses_cache",
|
|
20
|
+
"delivery-locations": "floriday_delivery_locations_cache",
|
|
21
|
+
packings: "floriday_packings_cache",
|
|
22
|
+
connections: "floriday_connections_cache",
|
|
23
|
+
"customer-stickers": "floriday_customer_stickers_cache",
|
|
24
|
+
"additional-services": "floriday_additional_services_cache",
|
|
25
|
+
"commercial-service-types": "floriday_commercial_service_types_cache",
|
|
26
|
+
collections: "floriday_collections_cache",
|
|
27
|
+
"price-groups": "floriday_price_groups_cache",
|
|
28
|
+
"delivery-condition-sets": "floriday_delivery_condition_sets_cache",
|
|
29
|
+
"sales-strategies": "floriday_sales_strategies_cache",
|
|
30
|
+
"base-items": "floriday_base_items_cache",
|
|
31
|
+
"contract-trade-item-groups": "floriday_contract_trade_item_groups_cache",
|
|
32
|
+
};
|
|
33
|
+
const CachedListSchema = z.object({
|
|
34
|
+
environment: EnvSchema,
|
|
35
|
+
entity: z.string().min(1),
|
|
36
|
+
limit: z.number().int().positive().max(5000).optional(),
|
|
37
|
+
});
|
|
38
|
+
export const getFlorydayCachedList = createServerFn({ method: "POST" })
|
|
39
|
+
.middleware([requireSupabaseAuth])
|
|
40
|
+
.inputValidator((d) => CachedListSchema.parse(d))
|
|
41
|
+
.handler(async ({ data, context }) => {
|
|
42
|
+
const table = ENTITY_TABLE[data.entity];
|
|
43
|
+
if (!table) {
|
|
44
|
+
throw new Error(`Onbekende entity "${data.entity}" — toegestane waarden: ${Object.keys(ENTITY_TABLE).join(", ")}`);
|
|
45
|
+
}
|
|
46
|
+
const limit = data.limit ?? 1000;
|
|
47
|
+
let q = context.supabase.from(table).select("*").limit(limit);
|
|
48
|
+
// Most cache-tables have an is_deleted column; filter when present.
|
|
49
|
+
const hasIsDeleted = [
|
|
50
|
+
"floriday_salesorders_cache",
|
|
51
|
+
"floriday_trade_items_cache",
|
|
52
|
+
].includes(table);
|
|
53
|
+
if (hasIsDeleted)
|
|
54
|
+
q = q.eq("is_deleted", false);
|
|
55
|
+
const { data: rows, error } = await q;
|
|
56
|
+
if (error)
|
|
57
|
+
throw new Error(`${table} lezen faalde: ${error.message}`);
|
|
58
|
+
return { entity: data.entity, rows: rows ?? [] };
|
|
59
|
+
});
|
|
60
|
+
const CodeListSchema = z.object({
|
|
61
|
+
environment: EnvSchema,
|
|
62
|
+
listName: z.string().min(1),
|
|
63
|
+
});
|
|
64
|
+
export const getFlorydayCodeList = createServerFn({ method: "POST" })
|
|
65
|
+
.middleware([requireSupabaseAuth])
|
|
66
|
+
.inputValidator((d) => CodeListSchema.parse(d))
|
|
67
|
+
.handler(async ({ data, context }) => {
|
|
68
|
+
const { data: rows, error } = await context.supabase
|
|
69
|
+
.from("floriday_code_lists_cache")
|
|
70
|
+
.select("code, label, payload")
|
|
71
|
+
.eq("environment", data.environment)
|
|
72
|
+
.eq("list_name", data.listName);
|
|
73
|
+
if (error)
|
|
74
|
+
throw new Error(`code-list lezen faalde: ${error.message}`);
|
|
75
|
+
return { listName: data.listName, items: rows ?? [] };
|
|
76
|
+
});
|
|
77
|
+
export const getFlorydayAllowedValues = createServerFn({ method: "POST" })
|
|
78
|
+
.middleware([requireSupabaseAuth])
|
|
79
|
+
.inputValidator((d) => CodeListSchema.parse(d))
|
|
80
|
+
.handler(async ({ data, context }) => {
|
|
81
|
+
const { data: rows, error } = await context.supabase
|
|
82
|
+
.from("floriday_code_lists_cache")
|
|
83
|
+
.select("code")
|
|
84
|
+
.eq("environment", data.environment)
|
|
85
|
+
.eq("list_name", data.listName);
|
|
86
|
+
if (error)
|
|
87
|
+
throw new Error(`allowed-values lezen faalde: ${error.message}`);
|
|
88
|
+
return { listName: data.listName, values: (rows ?? []).map((r) => String(r.code)) };
|
|
89
|
+
});
|
|
90
|
+
const AssertSchema = z.object({
|
|
91
|
+
environment: EnvSchema,
|
|
92
|
+
listName: z.string().min(1),
|
|
93
|
+
value: z.string(),
|
|
94
|
+
});
|
|
95
|
+
export const assertFlorydayValue = createServerFn({ method: "POST" })
|
|
96
|
+
.middleware([requireSupabaseAuth])
|
|
97
|
+
.inputValidator((d) => AssertSchema.parse(d))
|
|
98
|
+
.handler(async ({ data, context }) => {
|
|
99
|
+
const { data: rows, error } = await context.supabase
|
|
100
|
+
.from("floriday_code_lists_cache")
|
|
101
|
+
.select("code")
|
|
102
|
+
.eq("environment", data.environment)
|
|
103
|
+
.eq("list_name", data.listName);
|
|
104
|
+
if (error)
|
|
105
|
+
throw new Error(`assert lezen faalde: ${error.message}`);
|
|
106
|
+
const allowed = (rows ?? []).map((r) => String(r.code));
|
|
107
|
+
if (allowed.length === 0) {
|
|
108
|
+
return { ok: false, error: `Lijst "${data.listName}" leeg of niet gecached`, allowed };
|
|
109
|
+
}
|
|
110
|
+
if (allowed.includes(data.value))
|
|
111
|
+
return { ok: true, allowed };
|
|
112
|
+
return {
|
|
113
|
+
ok: false,
|
|
114
|
+
error: `Waarde "${data.value}" niet toegestaan in "${data.listName}"`,
|
|
115
|
+
allowed,
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
const VbnSchema = z.object({
|
|
119
|
+
environment: EnvSchema,
|
|
120
|
+
code: z.string().optional(),
|
|
121
|
+
search: z.string().optional(),
|
|
122
|
+
});
|
|
123
|
+
export const getFlorydayVbnProduct = createServerFn({ method: "POST" })
|
|
124
|
+
.middleware([requireSupabaseAuth])
|
|
125
|
+
.inputValidator((d) => VbnSchema.parse(d))
|
|
126
|
+
.handler(async ({ data, context }) => {
|
|
127
|
+
let q = context.supabase
|
|
128
|
+
.from("floriday_code_lists_cache")
|
|
129
|
+
.select("code, label, payload")
|
|
130
|
+
.eq("environment", data.environment)
|
|
131
|
+
.eq("list_name", "vbn-products");
|
|
132
|
+
if (data.code)
|
|
133
|
+
q = q.eq("code", data.code);
|
|
134
|
+
else if (data.search)
|
|
135
|
+
q = q.ilike("label", `%${data.search}%`).limit(50);
|
|
136
|
+
else
|
|
137
|
+
q = q.limit(50);
|
|
138
|
+
const { data: rows, error } = await q;
|
|
139
|
+
if (error)
|
|
140
|
+
throw new Error(`vbn-products lezen faalde: ${error.message}`);
|
|
141
|
+
return { items: rows ?? [] };
|
|
142
|
+
});
|
|
143
|
+
const SchemaArgs = z.object({
|
|
144
|
+
resource: z.string().min(1),
|
|
145
|
+
version: z.string().optional(),
|
|
146
|
+
});
|
|
147
|
+
export const getFlorydayResourceSchema = createServerFn({ method: "POST" })
|
|
148
|
+
.middleware([requireSupabaseAuth])
|
|
149
|
+
.inputValidator((d) => SchemaArgs.parse(d))
|
|
150
|
+
.handler(async ({ data, context }) => {
|
|
151
|
+
let q = context.supabase
|
|
152
|
+
.from("floriday_resource_schemas")
|
|
153
|
+
.select("resource, version, schema, enums, fetched_at")
|
|
154
|
+
.eq("resource", data.resource);
|
|
155
|
+
if (data.version)
|
|
156
|
+
q = q.eq("version", data.version);
|
|
157
|
+
const { data: rows, error } = await q.order("fetched_at", { ascending: false }).limit(1);
|
|
158
|
+
if (error)
|
|
159
|
+
throw new Error(`resource-schema lezen faalde: ${error.message}`);
|
|
160
|
+
return rows?.[0] ?? null;
|
|
161
|
+
});
|
|
162
|
+
const SyncHealthSchema = z.object({
|
|
163
|
+
environment: EnvSchema,
|
|
164
|
+
resources: z.array(z.string()).min(1),
|
|
165
|
+
});
|
|
166
|
+
// Voor de FlorydaySyncHealthBanner — leest sync-state voor de actieve connectie
|
|
167
|
+
// van de gevraagde environment en geeft per resource laatste sync-info terug.
|
|
168
|
+
export const getFlorydaySyncHealth = createServerFn({ method: "POST" })
|
|
169
|
+
.middleware([requireSupabaseAuth])
|
|
170
|
+
.inputValidator((d) => SyncHealthSchema.parse(d))
|
|
171
|
+
.handler(async ({ data, context }) => {
|
|
172
|
+
const dbEnv = data.environment === "live" ? "production" : "staging";
|
|
173
|
+
const { data: conns } = await context.supabase
|
|
174
|
+
.from("floriday_connections")
|
|
175
|
+
.select("id")
|
|
176
|
+
.eq("environment", dbEnv)
|
|
177
|
+
.eq("is_active", true)
|
|
178
|
+
.limit(1);
|
|
179
|
+
const connectionId = (conns ?? [])[0]?.id ?? null;
|
|
180
|
+
if (!connectionId) {
|
|
181
|
+
return { connectionId: null, resources: [] };
|
|
182
|
+
}
|
|
183
|
+
// Map resource → entity_type used in floriday_sync_state.
|
|
184
|
+
const RES_TO_ENTITY = {
|
|
185
|
+
"sales-orders": "salesorders",
|
|
186
|
+
"trade-items": "tradeitems",
|
|
187
|
+
"customer-organizations": "customers",
|
|
188
|
+
"fulfillment-orders": "fulfillment-orders",
|
|
189
|
+
};
|
|
190
|
+
const { data: rows } = await context.supabase
|
|
191
|
+
.from("floriday_sync_state")
|
|
192
|
+
.select("entity_type, last_sequence_number, last_synced_at, last_success_at, last_error")
|
|
193
|
+
.eq("connection_id", connectionId);
|
|
194
|
+
const stateByEntity = new Map((rows ?? []).map((r) => [String(r.entity_type), r]));
|
|
195
|
+
return {
|
|
196
|
+
connectionId,
|
|
197
|
+
resources: data.resources.map((res) => {
|
|
198
|
+
const entity = RES_TO_ENTITY[res] ?? res;
|
|
199
|
+
const s = stateByEntity.get(entity) ?? null;
|
|
200
|
+
return {
|
|
201
|
+
resource: res,
|
|
202
|
+
entityType: entity,
|
|
203
|
+
lastSyncedAt: s?.last_synced_at ?? null,
|
|
204
|
+
lastSuccessAt: s?.last_success_at ?? null,
|
|
205
|
+
lastSequenceNumber: s?.last_sequence_number ?? null,
|
|
206
|
+
lastError: s?.last_error ?? null,
|
|
207
|
+
};
|
|
208
|
+
}),
|
|
209
|
+
};
|
|
210
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floriday-sync.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,eAAO,MAAM,kBAAkB;;;CAGrB,CAAC;AAEX,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW,CAAC,IAAI;IAC/B,UAAU,EAAE,qBAAqB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"floriday-sync.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,eAAO,MAAM,kBAAkB;;;CAGrB,CAAC;AAEX,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW,CAAC,IAAI;IAC/B,UAAU,EAAE,qBAAqB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAkCD,wBAAsB,cAAc,CAAC,IAAI,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACnE,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,GACtB,OAAO,CAAC,UAAU,CAAC,CA+FrB;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAOlC"}
|
|
@@ -2,76 +2,6 @@ export const FLORIDAY_API_BASES = {
|
|
|
2
2
|
staging: "https://api.staging.floriday.io/suppliers-api-2026v1",
|
|
3
3
|
production: "https://api.floriday.io/suppliers-api-2026v1",
|
|
4
4
|
};
|
|
5
|
-
function apiBaseFor(environment) {
|
|
6
|
-
return environment === "production" || environment === "prod"
|
|
7
|
-
? FLORIDAY_API_BASES.production
|
|
8
|
-
: FLORIDAY_API_BASES.staging;
|
|
9
|
-
}
|
|
10
|
-
// URL of the auth-module's token-refresh endpoint (shared Supabase project).
|
|
11
|
-
// On 401/403 we POST here, then re-read the freshly written token from
|
|
12
|
-
// floriday_connections. We never run our own OAuth flow or write tokens.
|
|
13
|
-
const AUTH_MODULE_REFRESH_URL = "https://project--vmicscahrnzpmhagztmx.lovable.app/api/public/cron/refresh-floriday-tokens";
|
|
14
|
-
async function reloadConnection(supabase, connectionId) {
|
|
15
|
-
const { data, error } = await supabase
|
|
16
|
-
.from("floriday_connections")
|
|
17
|
-
.select("id, environment, access_token, api_key, is_active")
|
|
18
|
-
.eq("id", connectionId)
|
|
19
|
-
.maybeSingle();
|
|
20
|
-
if (error || !data) {
|
|
21
|
-
throw new Error(`Floriday connectie ${connectionId} herladen mislukt: ${error?.message ?? "not found"}`);
|
|
22
|
-
}
|
|
23
|
-
return data;
|
|
24
|
-
}
|
|
25
|
-
async function triggerAuthModuleRefresh() {
|
|
26
|
-
try {
|
|
27
|
-
await fetch(AUTH_MODULE_REFRESH_URL, {
|
|
28
|
-
method: "POST",
|
|
29
|
-
headers: { "Content-Type": "application/json" },
|
|
30
|
-
body: "{}",
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
// best-effort; we'll re-read the token regardless and let the retry surface a clear error
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
async function floridayGet(supabase, conn, path) {
|
|
38
|
-
let active = conn;
|
|
39
|
-
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
40
|
-
if (!active.access_token) {
|
|
41
|
-
// No token yet — kick the auth-module once, then re-read.
|
|
42
|
-
await triggerAuthModuleRefresh();
|
|
43
|
-
active = await reloadConnection(supabase, active.id);
|
|
44
|
-
if (!active.access_token) {
|
|
45
|
-
throw new Error(`Floriday connectie ${active.id} heeft geen access_token (auth-module heeft nog niet ververst)`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const url = `${apiBaseFor(active.environment)}${path}`;
|
|
49
|
-
const res = await fetch(url, {
|
|
50
|
-
method: "GET",
|
|
51
|
-
headers: {
|
|
52
|
-
Accept: "application/json",
|
|
53
|
-
Authorization: `Bearer ${active.access_token}`,
|
|
54
|
-
...(active.api_key ? { "x-api-key": active.api_key } : {}),
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
const text = await res.text();
|
|
58
|
-
if (res.ok) {
|
|
59
|
-
return {
|
|
60
|
-
result: (text ? JSON.parse(text) : null),
|
|
61
|
-
connection: active,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
// 401/403 → trigger auth-module refresh, re-read token, retry once.
|
|
65
|
-
if ((res.status === 401 || res.status === 403) && attempt === 0) {
|
|
66
|
-
await triggerAuthModuleRefresh();
|
|
67
|
-
active = await reloadConnection(supabase, active.id);
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
throw new Error(`Floriday GET ${path} faalde [${res.status}]: ${text.slice(0, 400)}`);
|
|
71
|
-
}
|
|
72
|
-
// Unreachable, but keeps TypeScript happy.
|
|
73
|
-
throw new Error(`Floriday GET ${path} faalde na retry`);
|
|
74
|
-
}
|
|
75
5
|
async function readLastSequence(supabase, connectionId, entityType) {
|
|
76
6
|
const { data } = await supabase
|
|
77
7
|
.from("floriday_sync_state")
|
|
@@ -91,27 +21,37 @@ async function writeLastSequence(supabase, connectionId, entityType, lastSequenc
|
|
|
91
21
|
}, { onConflict: "connection_id,entity_type" });
|
|
92
22
|
}
|
|
93
23
|
export async function syncBySequence(supabase, opts) {
|
|
24
|
+
// Lazy import vermijdt circular dependency tussen sync en gateway.
|
|
25
|
+
const { florydayFetch } = await import("./floriday-gateway.server");
|
|
94
26
|
const { connection, entityType, maxEndpoint, syncEndpointBase, cacheTable, conflictTarget, mapRow } = opts;
|
|
95
27
|
let active = connection;
|
|
96
|
-
|
|
97
|
-
|
|
28
|
+
const maxCall = await florydayFetch(supabase, active, {
|
|
29
|
+
method: "GET",
|
|
30
|
+
path: maxEndpoint,
|
|
31
|
+
});
|
|
32
|
+
if (!maxCall.ok) {
|
|
33
|
+
throw new Error(`Floriday GET ${maxEndpoint} faalde [${maxCall.status}]: ${maxCall.rawText.slice(0, 400)}`);
|
|
34
|
+
}
|
|
98
35
|
active = maxCall.connection;
|
|
99
|
-
const maxRes = maxCall.
|
|
36
|
+
const maxRes = maxCall.data;
|
|
100
37
|
const maxSequence = Number((typeof maxRes === "number" ? maxRes : maxRes?.maxSequenceNumber ?? maxRes?.sequenceNumber ?? 0) ?? 0);
|
|
101
38
|
const fromSequence = await readLastSequence(supabase, active.id, entityType);
|
|
102
39
|
let cursor = fromSequence;
|
|
103
40
|
let synced = 0;
|
|
104
41
|
let safetyHops = 0;
|
|
105
42
|
const MAX_HOPS = 1000;
|
|
106
|
-
// Per Floriday-spec: lege batches zijn toegestaan (server-side filter).
|
|
107
|
-
// Doorgaan tot cursor de (eventueel opgeschoven) maxSequence bereikt.
|
|
108
43
|
let effectiveMax = maxSequence;
|
|
109
44
|
while (cursor < effectiveMax && safetyHops < MAX_HOPS) {
|
|
110
45
|
safetyHops += 1;
|
|
111
|
-
const batchCall = await
|
|
46
|
+
const batchCall = await florydayFetch(supabase, active, {
|
|
47
|
+
method: "GET",
|
|
48
|
+
path: `${syncEndpointBase}/${cursor}`,
|
|
49
|
+
});
|
|
50
|
+
if (!batchCall.ok) {
|
|
51
|
+
throw new Error(`Floriday GET ${syncEndpointBase}/${cursor} faalde [${batchCall.status}]: ${batchCall.rawText.slice(0, 400)}`);
|
|
52
|
+
}
|
|
112
53
|
active = batchCall.connection;
|
|
113
|
-
const batch = batchCall.
|
|
114
|
-
// Response-shape soepel parsen.
|
|
54
|
+
const batch = batchCall.data;
|
|
115
55
|
let list = [];
|
|
116
56
|
let respSeq = null;
|
|
117
57
|
let respMaxSeq = null;
|
|
@@ -147,7 +87,6 @@ export async function syncBySequence(supabase, opts) {
|
|
|
147
87
|
nextCursor = respMaxSeq;
|
|
148
88
|
}
|
|
149
89
|
else if (list.length === 0) {
|
|
150
|
-
// Geen vooruitgang: voorkom oneindige loop.
|
|
151
90
|
break;
|
|
152
91
|
}
|
|
153
92
|
if (nextCursor <= cursor)
|