@flowselections/mailbox-orders 1.0.45 → 1.0.49
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/MailboxAuthBanner.d.ts +3 -0
- package/dist-lib/components/MailboxAuthBanner.d.ts.map +1 -1
- package/dist-lib/components/MailboxAuthBanner.js +8 -5
- package/dist-lib/components/MailboxProposalsPage.d.ts.map +1 -1
- package/dist-lib/components/MailboxProposalsPage.js +4 -3
- package/dist-lib/components/MailboxSkippedPage.d.ts.map +1 -1
- package/dist-lib/components/MailboxSkippedPage.js +5 -3
- package/dist-lib/components/MailboxStatusLine.d.ts +5 -0
- package/dist-lib/components/MailboxStatusLine.d.ts.map +1 -0
- package/dist-lib/components/MailboxStatusLine.js +23 -0
- package/dist-lib/integrations/supabase/types.d.ts +3364 -122
- package/dist-lib/integrations/supabase/types.d.ts.map +1 -1
- package/dist-lib/integrations/supabase/types.js +1 -0
- package/dist-lib/lib/mailbox-active.d.ts +26 -0
- package/dist-lib/lib/mailbox-active.d.ts.map +1 -0
- package/dist-lib/lib/mailbox-active.js +31 -0
- package/dist-lib/lib/mailbox-filter-settings.functions.d.ts.map +1 -1
- package/dist-lib/lib/mailbox-filter-settings.functions.js +3 -0
- package/dist-lib/lib/mailbox-folders.d.ts +25 -0
- package/dist-lib/lib/mailbox-folders.d.ts.map +1 -0
- package/dist-lib/lib/mailbox-folders.js +73 -0
- package/dist-lib/lib/mailbox-ingest.server.d.ts +5 -0
- package/dist-lib/lib/mailbox-ingest.server.d.ts.map +1 -1
- package/dist-lib/lib/mailbox-ingest.server.js +338 -109
- package/dist-lib/lib/mailbox.functions.d.ts +31 -34
- package/dist-lib/lib/mailbox.functions.d.ts.map +1 -1
- package/dist-lib/lib/mailbox.functions.js +133 -32
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { classifyVerkoopRefusal, existingOrderOrFilter, defaultNeedsSticker, checkLinePrices, zeroPriceFlag } from "./verkoop-order.js";
|
|
2
|
+
import { toActiveMailboxStatus } from "./mailbox-active.js";
|
|
2
3
|
// Server functions voor de Mailbox-bestellingen module
|
|
3
4
|
import { createServerFn } from "@tanstack/react-start";
|
|
4
5
|
import { requireSupabaseAuth } from "../integrations/supabase/auth-middleware.js";
|
|
@@ -7,38 +8,34 @@ import { z } from "zod";
|
|
|
7
8
|
import { emailBodyText, inlineCidImages, normalizeAddressList, normalizeAttachments, sanitizeEmailHtml, stripInvisible, } from "./mailbox-body.js";
|
|
8
9
|
import { EMPTY_LOAD_CARRIERS, isLoadCarrierAllowed, loadCarrierLabel, normalizeAllowedLoadCarriers, } from "./load-carriers.js";
|
|
9
10
|
// ─── Actieve mailbox (uit gedeelde e-mail-auth module) ──────────────────────
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
11
|
+
// De RPC email_auth_for_purpose vereist een ingelogde gebruiker (anders 42501).
|
|
12
|
+
// We sturen daarom de bearer-token van de gebruiker mee zodat auth.uid() in de
|
|
13
|
+
// RPC gevuld is. Bewust geen requireSupabaseAuth-middleware: we geven altijd
|
|
14
|
+
// nette JSON terug. Geen token of 42501 → { connected: null, error: "unauthorized" }.
|
|
13
15
|
export const getActiveOrdersMailbox = createServerFn({ method: "GET" })
|
|
14
16
|
.handler(async () => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
status: (row.status ?? null),
|
|
38
|
-
last_error: (row.last_error ?? null),
|
|
39
|
-
last_verified_at: (row.last_verified_at ?? null),
|
|
40
|
-
needs_reauth: !!row.needs_reauth,
|
|
41
|
-
};
|
|
17
|
+
try {
|
|
18
|
+
const url = process.env.SUPABASE_URL;
|
|
19
|
+
const key = process.env.SUPABASE_PUBLISHABLE_KEY;
|
|
20
|
+
if (!url || !key)
|
|
21
|
+
return { connected: null, error: "missing_env" };
|
|
22
|
+
const { getRequest } = await import("@tanstack/react-start/server");
|
|
23
|
+
const token = getRequest()?.headers?.get("authorization")?.replace(/^Bearer\s+/i, "") ?? "";
|
|
24
|
+
if (!token)
|
|
25
|
+
return { connected: null, error: "unauthorized" };
|
|
26
|
+
const sb = createClient(url, key, {
|
|
27
|
+
auth: { storage: undefined, persistSession: false, autoRefreshToken: false },
|
|
28
|
+
global: { headers: { Authorization: `Bearer ${token}` } },
|
|
29
|
+
});
|
|
30
|
+
const { data, error } = await sb.rpc("email_auth_for_purpose", {
|
|
31
|
+
_purpose: "orders",
|
|
32
|
+
_direction: "receive",
|
|
33
|
+
});
|
|
34
|
+
return toActiveMailboxStatus(data, error);
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
return { connected: null, error: String(e?.message ?? e) };
|
|
38
|
+
}
|
|
42
39
|
});
|
|
43
40
|
// ─── Ingest-triggers ────────────────────────────────────────────────────────
|
|
44
41
|
// De volledige ingest-pipeline (events → messages → stub-proposals) leeft in
|
|
@@ -143,6 +140,7 @@ export const listProposals = createServerFn({ method: "GET" })
|
|
|
143
140
|
.inputValidator((input) => z.object({ status: z.enum(["pending", "approved", "rejected"]).optional() }).parse(input ?? {}))
|
|
144
141
|
.handler(async ({ data }) => {
|
|
145
142
|
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
143
|
+
await ensureMailboxDefaults(supabaseAdmin);
|
|
146
144
|
// Alleen de kolommen die de lijstweergave toont. De mailtekst (body_text /
|
|
147
145
|
// body_html) en de bestelregels zijn bewust NIET opgenomen: die worden pas
|
|
148
146
|
// opgehaald zodra een voorstel geopend wordt (`getProposalDetail`).
|
|
@@ -151,7 +149,7 @@ export const listProposals = createServerFn({ method: "GET" })
|
|
|
151
149
|
.from("mailbox_order_proposals")
|
|
152
150
|
.select(sel("id, status, created_at, reviewed_at, matched_customer_id, ai_ok, customer_reference, " +
|
|
153
151
|
"readiness:parsed_payload->>readiness, duplicate:parsed_payload->duplicate_of, " +
|
|
154
|
-
"mailbox_messages(subject, from_email, from_name, received_at, connection_id, preview), " +
|
|
152
|
+
"mailbox_messages(subject, from_email, from_name, received_at, connection_id, account_email, preview), " +
|
|
155
153
|
"mailbox_proposal_field_values(field_key, value, needs_review)"))
|
|
156
154
|
.order("created_at", { ascending: false })
|
|
157
155
|
.limit(200);
|
|
@@ -205,7 +203,7 @@ export const getProposalDetail = createServerFn({ method: "GET" })
|
|
|
205
203
|
const sel = (s) => s;
|
|
206
204
|
const { data: row, error } = await supabaseAdmin
|
|
207
205
|
.from("mailbox_order_proposals")
|
|
208
|
-
.select(sel("*, mailbox_messages(subject, from_email, from_name, body_text, body_html, received_at, connection_id, to_recipients, cc_recipients, attachments, has_attachments), " +
|
|
206
|
+
.select(sel("*, mailbox_messages(subject, from_email, from_name, body_text, body_html, received_at, connection_id, account_email, to_recipients, cc_recipients, attachments, has_attachments), " +
|
|
209
207
|
"mailbox_proposal_lines(*), mailbox_proposal_field_values(*)"))
|
|
210
208
|
.eq("id", data.proposalId)
|
|
211
209
|
.maybeSingle();
|
|
@@ -1195,6 +1193,7 @@ export const listSkippedMessages = createServerFn({ method: "GET" })
|
|
|
1195
1193
|
.middleware([requireSupabaseAuth])
|
|
1196
1194
|
.handler(async () => {
|
|
1197
1195
|
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
1196
|
+
await ensureMailboxDefaults(supabaseAdmin);
|
|
1198
1197
|
const { data, error } = await supabaseAdmin
|
|
1199
1198
|
.from("mailbox_messages")
|
|
1200
1199
|
.select("id, from_email, from_name, subject, received_at, preview, filter_outcome, filter_reason, analysis_state, analysis_note, mailbox_order_proposals(id)")
|
|
@@ -1259,3 +1258,105 @@ export const getStickerDefaults = createServerFn({ method: "POST" })
|
|
|
1259
1258
|
defaults[id] = defaultNeedsSticker(list, data.customerId, id);
|
|
1260
1259
|
return { available: true, defaults };
|
|
1261
1260
|
});
|
|
1261
|
+
// ─── Standaardinstellingen + statusregel ───────────────────────────────────
|
|
1262
|
+
/** Maakt ontbrekende singleton-instellingen aan (overschrijft nooit iets). Faalt nooit hard. */
|
|
1263
|
+
export async function ensureMailboxDefaults(admin) {
|
|
1264
|
+
try {
|
|
1265
|
+
await admin.rpc("mailbox_ensure_defaults");
|
|
1266
|
+
}
|
|
1267
|
+
catch {
|
|
1268
|
+
/* functie ontbreekt nog in deze database */
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
export const ensureMailboxDefaultsFn = createServerFn({ method: "POST" })
|
|
1272
|
+
.middleware([requireSupabaseAuth])
|
|
1273
|
+
.handler(async () => {
|
|
1274
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
1275
|
+
await ensureMailboxDefaults(supabaseAdmin);
|
|
1276
|
+
return { ok: true };
|
|
1277
|
+
});
|
|
1278
|
+
export const getMailboxStatus = createServerFn({ method: "GET" })
|
|
1279
|
+
.middleware([requireSupabaseAuth])
|
|
1280
|
+
.handler(async () => {
|
|
1281
|
+
const { supabaseAdmin } = await import("../integrations/supabase/client.server.js");
|
|
1282
|
+
const admin = supabaseAdmin;
|
|
1283
|
+
await ensureMailboxDefaults(admin);
|
|
1284
|
+
const { data: active } = await admin.rpc("email_auth_for_purpose", { _purpose: "orders", _direction: "receive" });
|
|
1285
|
+
const activeRows = (Array.isArray(active) ? active : []);
|
|
1286
|
+
const activeIds = activeRows.map((r) => r.id).filter(Boolean);
|
|
1287
|
+
const [okRes, failRes] = await Promise.all([
|
|
1288
|
+
admin.from("mailbox_ingest_runs").select("started_at").eq("status", "ok").order("started_at", { ascending: false }).limit(1).maybeSingle(),
|
|
1289
|
+
admin.from("mailbox_ingest_runs").select("started_at, reason").eq("status", "fout").order("started_at", { ascending: false }).limit(1).maybeSingle(),
|
|
1290
|
+
]);
|
|
1291
|
+
const lastOkAt = okRes?.data?.started_at ?? null;
|
|
1292
|
+
const lastFailAt = failRes?.data?.started_at ?? null;
|
|
1293
|
+
const lastFailReason = failRes?.data?.reason ?? null;
|
|
1294
|
+
let pendingEvents = 0;
|
|
1295
|
+
if (activeIds.length > 0) {
|
|
1296
|
+
const { count } = await admin.from("email_auth_events").select("id", { count: "exact", head: true })
|
|
1297
|
+
.in("connection_id", activeIds).is("consumed_at", null);
|
|
1298
|
+
pendingEvents = count ?? 0;
|
|
1299
|
+
}
|
|
1300
|
+
const { count: waitingAi } = await admin.from("mailbox_messages").select("id", { count: "exact", head: true }).eq("analysis_state", "wacht");
|
|
1301
|
+
const since = new Date(Date.now() - 7 * 86400000).toISOString();
|
|
1302
|
+
const countIgnored = async (reason) => {
|
|
1303
|
+
const { count } = await admin.from("mailbox_ignored_events").select("id", { count: "exact", head: true })
|
|
1304
|
+
.eq("reason", reason).gte("created_at", since);
|
|
1305
|
+
return count ?? 0;
|
|
1306
|
+
};
|
|
1307
|
+
const [ignoredNotFollowed, ignoredUnknown] = await Promise.all([
|
|
1308
|
+
countIgnored("niet uit een gevolgde map"),
|
|
1309
|
+
countIgnored("map onbekend"),
|
|
1310
|
+
]);
|
|
1311
|
+
// Ontkoppelde adressen met onverwerkte events.
|
|
1312
|
+
// Events vóór de laatste ronde van die koppeling worden nog verwerkt; alleen de rest telt.
|
|
1313
|
+
let q = admin.from("email_auth_events").select("connection_id, received_at").is("consumed_at", null).limit(1000);
|
|
1314
|
+
if (activeIds.length > 0)
|
|
1315
|
+
q = q.not("connection_id", "in", `(${activeIds.join(",")})`);
|
|
1316
|
+
const { data: leftRows } = await q;
|
|
1317
|
+
const rowsLeft = (Array.isArray(leftRows) ? leftRows : []);
|
|
1318
|
+
const cutoffs = new Map();
|
|
1319
|
+
for (const id of new Set(rowsLeft.map((r) => r.connection_id).filter(Boolean))) {
|
|
1320
|
+
const { data: last } = await admin.from("mailbox_ingest_runs").select("started_at").eq("connection_id", id)
|
|
1321
|
+
.order("started_at", { ascending: false }).limit(1).maybeSingle();
|
|
1322
|
+
cutoffs.set(id, last?.started_at ?? null);
|
|
1323
|
+
}
|
|
1324
|
+
const leftCount = new Map();
|
|
1325
|
+
for (const r of rowsLeft) {
|
|
1326
|
+
if (!r.connection_id)
|
|
1327
|
+
continue;
|
|
1328
|
+
const cut = cutoffs.get(r.connection_id) ?? null;
|
|
1329
|
+
if (cut && r.received_at && r.received_at <= cut)
|
|
1330
|
+
continue;
|
|
1331
|
+
leftCount.set(r.connection_id, (leftCount.get(r.connection_id) ?? 0) + 1);
|
|
1332
|
+
}
|
|
1333
|
+
const disconnected = [];
|
|
1334
|
+
if (leftCount.size > 0) {
|
|
1335
|
+
const { data: cs } = await admin.from("email_auth_connections").select("id, account_email").in("id", [...leftCount.keys()]);
|
|
1336
|
+
const emailOf = new Map((Array.isArray(cs) ? cs : []).map((c) => [c.id, c.account_email ?? "onbekend adres"]));
|
|
1337
|
+
for (const [id, count] of leftCount)
|
|
1338
|
+
disconnected.push({ email: String(emailOf.get(id) ?? "onbekend adres"), count });
|
|
1339
|
+
}
|
|
1340
|
+
const { currentAiFingerprint } = await import("./mailbox-ai.server.js");
|
|
1341
|
+
const aiAgent = (await currentAiFingerprint(admin).catch(() => null)) !== null;
|
|
1342
|
+
const failNewer = !!lastFailAt && (!lastOkAt || lastFailAt > lastOkAt);
|
|
1343
|
+
let problem = null;
|
|
1344
|
+
if (failNewer)
|
|
1345
|
+
problem = `Er komen mails binnen, maar ze worden niet verwerkt: ${lastFailReason ?? "onbekende reden"}`;
|
|
1346
|
+
else if (pendingEvents > 0 && lastOkAt && Date.now() - Date.parse(lastOkAt) > 10 * 60000)
|
|
1347
|
+
problem = "Er komen mails binnen, maar ze worden niet verwerkt: de laatste geslaagde ronde is langer dan 10 minuten geleden.";
|
|
1348
|
+
else if (pendingEvents > 0 && !lastOkAt)
|
|
1349
|
+
problem = "Er komen mails binnen, maar ze worden niet verwerkt: er is nog geen enkele ronde geslaagd.";
|
|
1350
|
+
return {
|
|
1351
|
+
lastOkAt,
|
|
1352
|
+
lastFailAt,
|
|
1353
|
+
lastFailReason,
|
|
1354
|
+
problem,
|
|
1355
|
+
waiting: pendingEvents + (waitingAi ?? 0),
|
|
1356
|
+
aiAgent,
|
|
1357
|
+
addresses: activeRows.map((r) => String(r.account_email ?? "")).filter(Boolean),
|
|
1358
|
+
ignoredNotFollowed,
|
|
1359
|
+
ignoredUnknown,
|
|
1360
|
+
disconnected,
|
|
1361
|
+
};
|
|
1362
|
+
});
|