@flowselections/floriday-verkoop-module 1.0.8 → 1.0.9
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 +2667 -169
- package/dist-lib/integrations/supabase/auth-middleware.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.d.ts +2667 -169
- package/dist-lib/integrations/supabase/client.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/client.server.d.ts +2667 -169
- package/dist-lib/integrations/supabase/client.server.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.d.ts +2736 -155
- 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 +222 -0
- package/dist-lib/lib/floriday-public.functions.d.ts +63971 -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 +45678 -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 +17178 -9683
- 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 +2667 -169
- 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 +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import { type FloridayConnectionRow } from "./floriday-sync";
|
|
3
|
+
export type FlorydayEnvironment = "staging" | "live";
|
|
4
|
+
export declare function apiBaseFor(env: FlorydayEnvironment | string | null | undefined): string;
|
|
5
|
+
export declare function pickActiveConnection(supabase: SupabaseClient, env: FlorydayEnvironment | string): Promise<FloridayConnectionRow & {
|
|
6
|
+
gln_code?: string | null;
|
|
7
|
+
}>;
|
|
8
|
+
export interface FlorydayFetchResult<T = unknown> {
|
|
9
|
+
ok: boolean;
|
|
10
|
+
status: number;
|
|
11
|
+
data: T | null;
|
|
12
|
+
rawText: string;
|
|
13
|
+
connection: FloridayConnectionRow;
|
|
14
|
+
}
|
|
15
|
+
/** Low-level fetch with token-refresh + retry-once on 401/403. */
|
|
16
|
+
export declare function florydayFetch<T = unknown>(supabase: SupabaseClient, conn: FloridayConnectionRow, init: {
|
|
17
|
+
method: string;
|
|
18
|
+
path: string;
|
|
19
|
+
body?: unknown;
|
|
20
|
+
}): Promise<FlorydayFetchResult<T>>;
|
|
21
|
+
export interface EnumCheck {
|
|
22
|
+
field: string;
|
|
23
|
+
listName: string;
|
|
24
|
+
}
|
|
25
|
+
export interface FlorydayWriteArgs {
|
|
26
|
+
module: string;
|
|
27
|
+
action: string;
|
|
28
|
+
method: "POST" | "PUT" | "PATCH" | "DELETE";
|
|
29
|
+
path: string;
|
|
30
|
+
referenceId?: string | null;
|
|
31
|
+
body?: unknown;
|
|
32
|
+
validateEnums?: EnumCheck[];
|
|
33
|
+
}
|
|
34
|
+
export interface FlorydayWriteResult {
|
|
35
|
+
ok: boolean;
|
|
36
|
+
status: number;
|
|
37
|
+
data?: any;
|
|
38
|
+
error?: string;
|
|
39
|
+
attemptId?: string;
|
|
40
|
+
}
|
|
41
|
+
export declare function florydayWrite(supabase: SupabaseClient, env: FlorydayEnvironment | string, args: FlorydayWriteArgs, callerUserId?: string | null): Promise<FlorydayWriteResult>;
|
|
42
|
+
//# sourceMappingURL=floriday-gateway.server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floriday-gateway.server.d.ts","sourceRoot":"","sources":["../../src/lib/floriday-gateway.server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAEjF,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,MAAM,CAAC;AAKrD,wBAAgB,UAAU,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAIvF;AAMD,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,cAAc,EACxB,GAAG,EAAE,mBAAmB,GAAG,MAAM,GAChC,OAAO,CAAC,qBAAqB,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAa/D;AA6BD,MAAM,WAAW,mBAAmB,CAAC,CAAC,GAAG,OAAO;IAC9C,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,qBAAqB,CAAC;CACnC;AAED,kEAAkE;AAClE,wBAAsB,aAAa,CAAC,CAAC,GAAG,OAAO,EAC7C,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,qBAAqB,EAC3B,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GACrD,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CA8CjC;AAGD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AA4DD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,aAAa,CAAC,EAAE,SAAS,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAcD,wBAAsB,aAAa,CACjC,QAAQ,EAAE,cAAc,EACxB,GAAG,EAAE,mBAAmB,GAAG,MAAM,EACjC,IAAI,EAAE,iBAAiB,EACvB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAC3B,OAAO,CAAC,mBAAmB,CAAC,CA6D9B"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// Internal Floriday gateway — server-only.
|
|
2
|
+
// One place that owns: connection-selection, token-refresh + retry-on-401,
|
|
3
|
+
// enum validation, idempotency-key, and write-log persistence.
|
|
4
|
+
// Other modules MUST NOT call fetch on Floriday directly; they use the helpers
|
|
5
|
+
// in floriday-writes.functions.ts and floriday-public.functions.ts.
|
|
6
|
+
import { createHash } from "crypto";
|
|
7
|
+
import { FLORIDAY_API_BASES } from "./floriday-sync";
|
|
8
|
+
const AUTH_MODULE_REFRESH_URL = "https://project--vmicscahrnzpmhagztmx.lovable.app/api/public/cron/refresh-floriday-tokens";
|
|
9
|
+
export function apiBaseFor(env) {
|
|
10
|
+
return env === "live" || env === "production" || env === "prod"
|
|
11
|
+
? FLORIDAY_API_BASES.production
|
|
12
|
+
: FLORIDAY_API_BASES.staging;
|
|
13
|
+
}
|
|
14
|
+
function normalizeEnvKey(env) {
|
|
15
|
+
return env === "live" || env === "production" || env === "prod" ? "production" : "staging";
|
|
16
|
+
}
|
|
17
|
+
export async function pickActiveConnection(supabase, env) {
|
|
18
|
+
const dbEnv = normalizeEnvKey(env);
|
|
19
|
+
const { data, error } = await supabase
|
|
20
|
+
.from("floriday_connections")
|
|
21
|
+
.select("id, environment, access_token, api_key, is_active, gln_code")
|
|
22
|
+
.eq("environment", dbEnv)
|
|
23
|
+
.eq("is_active", true)
|
|
24
|
+
.order("updated_at", { ascending: false })
|
|
25
|
+
.limit(1)
|
|
26
|
+
.maybeSingle();
|
|
27
|
+
if (error)
|
|
28
|
+
throw new Error(`Floriday connectie ophalen mislukt: ${error.message}`);
|
|
29
|
+
if (!data)
|
|
30
|
+
throw new Error(`Geen actieve Floriday connectie voor "${dbEnv}"`);
|
|
31
|
+
return data;
|
|
32
|
+
}
|
|
33
|
+
async function triggerAuthModuleRefresh() {
|
|
34
|
+
try {
|
|
35
|
+
await fetch(AUTH_MODULE_REFRESH_URL, {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: { "Content-Type": "application/json" },
|
|
38
|
+
body: "{}",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// best-effort — caller re-reads the token regardless
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function reloadConnection(supabase, connectionId) {
|
|
46
|
+
const { data, error } = await supabase
|
|
47
|
+
.from("floriday_connections")
|
|
48
|
+
.select("id, environment, access_token, api_key, is_active")
|
|
49
|
+
.eq("id", connectionId)
|
|
50
|
+
.maybeSingle();
|
|
51
|
+
if (error || !data) {
|
|
52
|
+
throw new Error(`Floriday connectie ${connectionId} herladen mislukt: ${error?.message ?? "not found"}`);
|
|
53
|
+
}
|
|
54
|
+
return data;
|
|
55
|
+
}
|
|
56
|
+
/** Low-level fetch with token-refresh + retry-once on 401/403. */
|
|
57
|
+
export async function florydayFetch(supabase, conn, init) {
|
|
58
|
+
let active = conn;
|
|
59
|
+
let lastRes = null;
|
|
60
|
+
let lastText = "";
|
|
61
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
62
|
+
if (!active.access_token) {
|
|
63
|
+
await triggerAuthModuleRefresh();
|
|
64
|
+
active = await reloadConnection(supabase, active.id);
|
|
65
|
+
if (!active.access_token) {
|
|
66
|
+
throw new Error(`Floriday connectie ${active.id} heeft geen access_token`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const url = `${apiBaseFor(active.environment)}${init.path}`;
|
|
70
|
+
const res = await fetch(url, {
|
|
71
|
+
method: init.method,
|
|
72
|
+
headers: {
|
|
73
|
+
Accept: "application/json",
|
|
74
|
+
...(init.body !== undefined ? { "Content-Type": "application/json" } : {}),
|
|
75
|
+
Authorization: `Bearer ${active.access_token}`,
|
|
76
|
+
...(active.api_key ? { "x-api-key": active.api_key } : {}),
|
|
77
|
+
},
|
|
78
|
+
body: init.body !== undefined ? JSON.stringify(init.body) : undefined,
|
|
79
|
+
});
|
|
80
|
+
const text = await res.text();
|
|
81
|
+
lastRes = res;
|
|
82
|
+
lastText = text;
|
|
83
|
+
if (res.ok) {
|
|
84
|
+
const parsed = text ? JSON.parse(text) : null;
|
|
85
|
+
return { ok: true, status: res.status, data: parsed, rawText: text, connection: active };
|
|
86
|
+
}
|
|
87
|
+
if ((res.status === 401 || res.status === 403) && attempt === 0) {
|
|
88
|
+
await triggerAuthModuleRefresh();
|
|
89
|
+
active = await reloadConnection(supabase, active.id);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
ok: false,
|
|
96
|
+
status: lastRes?.status ?? 0,
|
|
97
|
+
data: null,
|
|
98
|
+
rawText: lastText,
|
|
99
|
+
connection: active,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async function fetchAllowedValues(supabase, env, listName) {
|
|
103
|
+
const dbEnv = normalizeEnvKey(env) === "production" ? "live" : "staging";
|
|
104
|
+
const { data, error } = await supabase
|
|
105
|
+
.from("floriday_code_lists_cache")
|
|
106
|
+
.select("code")
|
|
107
|
+
.eq("environment", dbEnv)
|
|
108
|
+
.eq("list_name", listName);
|
|
109
|
+
if (error)
|
|
110
|
+
throw new Error(`code-lists lezen faalde: ${error.message}`);
|
|
111
|
+
return (data ?? []).map((r) => String(r.code));
|
|
112
|
+
}
|
|
113
|
+
function readPathValues(body, path) {
|
|
114
|
+
const parts = path.split(".");
|
|
115
|
+
let nodes = [body];
|
|
116
|
+
for (const part of parts) {
|
|
117
|
+
const next = [];
|
|
118
|
+
const isArray = part.endsWith("[]");
|
|
119
|
+
const key = isArray ? part.slice(0, -2) : part;
|
|
120
|
+
for (const node of nodes) {
|
|
121
|
+
if (node == null || typeof node !== "object")
|
|
122
|
+
continue;
|
|
123
|
+
const v = node[key];
|
|
124
|
+
if (v === undefined)
|
|
125
|
+
continue;
|
|
126
|
+
if (isArray && Array.isArray(v))
|
|
127
|
+
next.push(...v);
|
|
128
|
+
else
|
|
129
|
+
next.push(v);
|
|
130
|
+
}
|
|
131
|
+
nodes = next;
|
|
132
|
+
}
|
|
133
|
+
return nodes;
|
|
134
|
+
}
|
|
135
|
+
async function runEnumChecks(supabase, env, body, checks) {
|
|
136
|
+
for (const check of checks) {
|
|
137
|
+
const values = readPathValues(body, check.field).filter((v) => v != null);
|
|
138
|
+
if (values.length === 0)
|
|
139
|
+
continue;
|
|
140
|
+
const allowed = await fetchAllowedValues(supabase, env, check.listName);
|
|
141
|
+
if (allowed.length === 0)
|
|
142
|
+
continue; // list not cached yet → skip
|
|
143
|
+
const bad = values.find((v) => !allowed.includes(String(v)));
|
|
144
|
+
if (bad !== undefined) {
|
|
145
|
+
return {
|
|
146
|
+
ok: false,
|
|
147
|
+
error: `Veld "${check.field}" heeft ongeldige waarde "${bad}" voor lijst "${check.listName}"`,
|
|
148
|
+
allowed,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { ok: true };
|
|
153
|
+
}
|
|
154
|
+
function computeIdempotencyKey(args) {
|
|
155
|
+
const h = createHash("sha256");
|
|
156
|
+
h.update(args.module);
|
|
157
|
+
h.update("|");
|
|
158
|
+
h.update(args.action);
|
|
159
|
+
h.update("|");
|
|
160
|
+
h.update(args.referenceId ?? "");
|
|
161
|
+
h.update("|");
|
|
162
|
+
h.update(JSON.stringify(args.body ?? null));
|
|
163
|
+
return h.digest("hex");
|
|
164
|
+
}
|
|
165
|
+
export async function florydayWrite(supabase, env, args, callerUserId) {
|
|
166
|
+
const idempotencyKey = computeIdempotencyKey(args);
|
|
167
|
+
// 1) Local pre-flight enum validation (saves a Floriday roundtrip on mismatches).
|
|
168
|
+
if (args.validateEnums && args.validateEnums.length > 0) {
|
|
169
|
+
const v = await runEnumChecks(supabase, env, args.body, args.validateEnums);
|
|
170
|
+
if (!v.ok) {
|
|
171
|
+
const ins = await supabase.from("floriday_write_log")
|
|
172
|
+
.insert({
|
|
173
|
+
module: args.module,
|
|
174
|
+
action: args.action,
|
|
175
|
+
resource: args.path,
|
|
176
|
+
floriday_id: args.referenceId ?? null,
|
|
177
|
+
idempotency_key: idempotencyKey,
|
|
178
|
+
request: { body: args.body ?? null, env },
|
|
179
|
+
response: { allowed: v.allowed ?? null },
|
|
180
|
+
http_status: 0,
|
|
181
|
+
ok: false,
|
|
182
|
+
error: v.error,
|
|
183
|
+
user_id: callerUserId ?? null,
|
|
184
|
+
})
|
|
185
|
+
.select("id")
|
|
186
|
+
.maybeSingle();
|
|
187
|
+
return { ok: false, status: 0, error: v.error, attemptId: ins.data?.id };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const conn = await pickActiveConnection(supabase, env);
|
|
191
|
+
const res = await florydayFetch(supabase, conn, {
|
|
192
|
+
method: args.method,
|
|
193
|
+
path: args.path,
|
|
194
|
+
body: args.body,
|
|
195
|
+
});
|
|
196
|
+
const ins = await supabase.from("floriday_write_log")
|
|
197
|
+
.insert({
|
|
198
|
+
connection_id: conn.id,
|
|
199
|
+
module: args.module,
|
|
200
|
+
action: args.action,
|
|
201
|
+
resource: args.path,
|
|
202
|
+
floriday_id: args.referenceId ?? null,
|
|
203
|
+
idempotency_key: idempotencyKey,
|
|
204
|
+
request: { body: args.body ?? null, env, method: args.method },
|
|
205
|
+
response: res.rawText ? { body: res.rawText.slice(0, 32000) } : null,
|
|
206
|
+
http_status: res.status,
|
|
207
|
+
ok: res.ok,
|
|
208
|
+
error: res.ok ? null : res.rawText.slice(0, 500),
|
|
209
|
+
user_id: callerUserId ?? null,
|
|
210
|
+
})
|
|
211
|
+
.select("id")
|
|
212
|
+
.maybeSingle();
|
|
213
|
+
if (!res.ok) {
|
|
214
|
+
return {
|
|
215
|
+
ok: false,
|
|
216
|
+
status: res.status,
|
|
217
|
+
error: res.rawText ? res.rawText.slice(0, 400) : `HTTP ${res.status}`,
|
|
218
|
+
attemptId: ins.data?.id,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
return { ok: true, status: res.status, data: res.data, attemptId: ins.data?.id };
|
|
222
|
+
}
|