@open-mercato/core 0.10.1-develop.7679.1.cb2c13f49e → 0.10.1-develop.7687.1.e1a6f9de51
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/.turbo/turbo-build.log +1 -1
- package/dist/modules/auth/api/users/route.js +1 -1
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/customer_accounts/api/magic-link/request.js +15 -1
- package/dist/modules/customer_accounts/api/magic-link/request.js.map +2 -2
- package/dist/modules/customer_accounts/api/password/reset-request.js +15 -1
- package/dist/modules/customer_accounts/api/password/reset-request.js.map +2 -2
- package/dist/modules/customer_accounts/emails/CustomerAuthLinkEmail.js +26 -0
- package/dist/modules/customer_accounts/emails/CustomerAuthLinkEmail.js.map +7 -0
- package/dist/modules/customer_accounts/lib/authLinkEmails.js +67 -0
- package/dist/modules/customer_accounts/lib/authLinkEmails.js.map +7 -0
- package/dist/modules/messages/commands/actions.js +8 -1
- package/dist/modules/messages/commands/actions.js.map +2 -2
- package/dist/modules/payment_gateways/data/entities.js +4 -0
- package/dist/modules/payment_gateways/data/entities.js.map +2 -2
- package/dist/modules/payment_gateways/lib/session-idempotency.js +29 -5
- package/dist/modules/payment_gateways/lib/session-idempotency.js.map +2 -2
- package/dist/modules/payment_gateways/migrations/Migration20261018180000_payment_gateways.js +19 -0
- package/dist/modules/payment_gateways/migrations/Migration20261018180000_payment_gateways.js.map +7 -0
- package/dist/modules/payment_gateways/workers/session-initialization-prune.js +3 -3
- package/dist/modules/payment_gateways/workers/session-initialization-prune.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/magic-link/page.js +59 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/magic-link/page.js.map +7 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/magic-link/page.meta.js +11 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/magic-link/page.meta.js.map +7 -0
- package/dist/modules/query_index/lib/engine.js +60 -13
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/auth/api/users/route.ts +1 -1
- package/src/modules/customer_accounts/api/magic-link/request.ts +16 -3
- package/src/modules/customer_accounts/api/password/reset-request.ts +16 -3
- package/src/modules/customer_accounts/emails/CustomerAuthLinkEmail.tsx +45 -0
- package/src/modules/customer_accounts/i18n/de.json +12 -0
- package/src/modules/customer_accounts/i18n/en.json +12 -0
- package/src/modules/customer_accounts/i18n/es.json +12 -0
- package/src/modules/customer_accounts/i18n/ko.json +12 -0
- package/src/modules/customer_accounts/i18n/pl.json +12 -0
- package/src/modules/customer_accounts/lib/authLinkEmails.ts +77 -0
- package/src/modules/messages/commands/actions.ts +8 -1
- package/src/modules/payment_gateways/data/entities.ts +4 -0
- package/src/modules/payment_gateways/lib/session-idempotency.ts +41 -5
- package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +14 -1
- package/src/modules/payment_gateways/migrations/Migration20261018180000_payment_gateways.ts +18 -0
- package/src/modules/payment_gateways/workers/session-initialization-prune.ts +3 -3
- package/src/modules/portal/frontend/[orgSlug]/portal/magic-link/page.meta.ts +9 -0
- package/src/modules/portal/frontend/[orgSlug]/portal/magic-link/page.tsx +71 -0
- package/src/modules/portal/i18n/de.json +7 -0
- package/src/modules/portal/i18n/en.json +7 -0
- package/src/modules/portal/i18n/es.json +7 -0
- package/src/modules/portal/i18n/ko.json +7 -0
- package/src/modules/portal/i18n/pl.json +7 -0
- package/src/modules/query_index/lib/engine.ts +70 -12
|
@@ -12,6 +12,13 @@ import { sql } from "kysely";
|
|
|
12
12
|
import { readCoverageSnapshot, refreshCoverageSnapshot } from "./coverage.js";
|
|
13
13
|
import { createProfiler, shouldEnableProfiler } from "@open-mercato/shared/lib/profiler";
|
|
14
14
|
import { decryptIndexDocCustomFields } from "@open-mercato/shared/lib/encryption/indexDoc";
|
|
15
|
+
import {
|
|
16
|
+
DECLINED_ENCRYPTED_SORT_LOG_MESSAGE,
|
|
17
|
+
DECRYPT_REFUSAL_LOG_MESSAGE,
|
|
18
|
+
DecryptRefusalTally,
|
|
19
|
+
resolveDecryptEnabled,
|
|
20
|
+
resolveDecryptScope
|
|
21
|
+
} from "@open-mercato/shared/lib/encryption/decryptScope";
|
|
15
22
|
import {
|
|
16
23
|
buildCustomFieldKindIndex,
|
|
17
24
|
createCustomFieldKindMapResolver,
|
|
@@ -37,7 +44,13 @@ import { tokenizeText } from "@open-mercato/shared/lib/search/tokenize";
|
|
|
37
44
|
import { buildContainmentPatterns } from "@open-mercato/shared/lib/search/containment";
|
|
38
45
|
import { runBeforeQueryPipeline, runAfterQueryPipeline } from "@open-mercato/shared/lib/query/query-extension-runner";
|
|
39
46
|
import { warnOnCiphertextLikeFallback } from "@open-mercato/shared/lib/query/ciphertext-search-warning";
|
|
40
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
matchEncryptedSortFields,
|
|
49
|
+
resolveEncryptedFieldNames,
|
|
50
|
+
resolveEncryptedSortFields,
|
|
51
|
+
resolveEncryptedSortMaxRows,
|
|
52
|
+
sortRowsInMemory
|
|
53
|
+
} from "@open-mercato/shared/lib/query/encrypted-sort";
|
|
41
54
|
import { resolveListCountCap } from "@open-mercato/shared/lib/query/count-cap";
|
|
42
55
|
import { resolveCfDefIndexOrgCandidates } from "@open-mercato/shared/lib/crud/custom-field-definition-index";
|
|
43
56
|
import { mapWithConcurrency } from "@open-mercato/shared/lib/query/bounded-decrypt";
|
|
@@ -584,7 +597,8 @@ class HybridQueryEngine {
|
|
|
584
597
|
return null;
|
|
585
598
|
};
|
|
586
599
|
const fallbackOrgId = opts.organizationId ?? (Array.isArray(opts.organizationIds) && opts.organizationIds.length === 1 ? opts.organizationIds[0] : null);
|
|
587
|
-
const
|
|
600
|
+
const decryptEnabled = resolveDecryptEnabled(opts);
|
|
601
|
+
const encSvc = decryptEnabled ? this.getEncryptionService() : null;
|
|
588
602
|
const resolvedSorts = [];
|
|
589
603
|
for (const sort of opts.sort || []) {
|
|
590
604
|
const field = String(sort.field);
|
|
@@ -595,14 +609,32 @@ class HybridQueryEngine {
|
|
|
595
609
|
if (baseField) resolvedSorts.push({ ...sort, field: baseField });
|
|
596
610
|
}
|
|
597
611
|
}
|
|
598
|
-
const
|
|
612
|
+
const plainSortFields = resolvedSorts.filter((sort) => !sort.field.startsWith("cf:")).map((sort) => sort.field);
|
|
613
|
+
const encryptedFieldNames = await resolveEncryptedFieldNames(
|
|
599
614
|
encSvc,
|
|
600
615
|
entity,
|
|
601
|
-
resolvedSorts.filter((sort) => !sort.field.startsWith("cf:")).map((sort) => sort.field),
|
|
602
616
|
opts.tenantId ?? null,
|
|
603
617
|
fallbackOrgId
|
|
604
618
|
);
|
|
619
|
+
const encryptedSortFields = matchEncryptedSortFields(encryptedFieldNames, plainSortFields);
|
|
605
620
|
const requiresPlaintextSort = encryptedSortFields.size > 0;
|
|
621
|
+
if (!decryptEnabled && plainSortFields.length > 0) {
|
|
622
|
+
const declinedSortFields = await resolveEncryptedSortFields(
|
|
623
|
+
this.getEncryptionService(),
|
|
624
|
+
entity,
|
|
625
|
+
plainSortFields,
|
|
626
|
+
opts.tenantId ?? null,
|
|
627
|
+
fallbackOrgId
|
|
628
|
+
);
|
|
629
|
+
if (declinedSortFields.size > 0) {
|
|
630
|
+
logger.warn(DECLINED_ENCRYPTED_SORT_LOG_MESSAGE, {
|
|
631
|
+
entity: String(entity),
|
|
632
|
+
sortFields: Array.from(declinedSortFields)
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
const requestedFields = opts.fields && opts.fields.length ? opts.fields.map(String) : null;
|
|
637
|
+
const injectsTenantIdForDecrypt = encSvc != null && hasTenantColumn && requestedFields != null && !requestedFields.includes("tenant_id") && !requestedFields.includes("tenantId") && (encryptedFieldNames === null || encryptedFieldNames.length > 0);
|
|
606
638
|
const cfSortKeys = Array.from(new Set(
|
|
607
639
|
resolvedSorts.filter((sort) => sort.field.startsWith("cf:")).map((sort) => sort.field.slice(3))
|
|
608
640
|
));
|
|
@@ -923,6 +955,7 @@ class HybridQueryEngine {
|
|
|
923
955
|
if (requiresPlaintextSort) {
|
|
924
956
|
for (const field of encryptedSortFields) selectFieldSet.add(field);
|
|
925
957
|
}
|
|
958
|
+
if (injectsTenantIdForDecrypt) selectFieldSet.add("tenant_id");
|
|
926
959
|
if (opts.includeCustomFields === true) {
|
|
927
960
|
const entityIds = Array.from(new Set(indexSources.map((src) => String(src.entityId))));
|
|
928
961
|
try {
|
|
@@ -1020,18 +1053,24 @@ class HybridQueryEngine {
|
|
|
1020
1053
|
}
|
|
1021
1054
|
return customFieldKinds;
|
|
1022
1055
|
};
|
|
1056
|
+
const refusalTally = new DecryptRefusalTally();
|
|
1023
1057
|
const decryptRow = async (item) => {
|
|
1024
1058
|
let next = item;
|
|
1059
|
+
if (!encSvc) return next;
|
|
1060
|
+
const decision = resolveDecryptScope({
|
|
1061
|
+
rowTenantId: next?.tenant_id ?? next?.tenantId ?? null,
|
|
1062
|
+
rowOrganizationId: next?.organization_id ?? next?.organizationId ?? null,
|
|
1063
|
+
callerTenantId: opts.tenantId ?? null,
|
|
1064
|
+
callerOrganizationId: fallbackOrgId ?? null
|
|
1065
|
+
});
|
|
1066
|
+
if (!decision.decrypt) {
|
|
1067
|
+
refusalTally.record(decision, next?.id == null ? null : String(next.id));
|
|
1068
|
+
return next;
|
|
1069
|
+
}
|
|
1025
1070
|
if (encSvc?.decryptEntityPayload) {
|
|
1026
1071
|
const decrypt = encSvc.decryptEntityPayload.bind(encSvc);
|
|
1027
1072
|
try {
|
|
1028
|
-
const decrypted = await decrypt(
|
|
1029
|
-
entity,
|
|
1030
|
-
next,
|
|
1031
|
-
next?.tenant_id ?? next?.tenantId ?? opts.tenantId ?? null,
|
|
1032
|
-
next?.organization_id ?? next?.organizationId ?? fallbackOrgId ?? null,
|
|
1033
|
-
{ em: this.em }
|
|
1034
|
-
);
|
|
1073
|
+
const decrypted = await decrypt(entity, next, decision.tenantId, decision.organizationId, { em: this.em });
|
|
1035
1074
|
next = { ...next, ...decrypted };
|
|
1036
1075
|
} catch (err) {
|
|
1037
1076
|
logger.error("Error decrypting entity payload", { err });
|
|
@@ -1040,8 +1079,8 @@ class HybridQueryEngine {
|
|
|
1040
1079
|
if (encSvc) {
|
|
1041
1080
|
try {
|
|
1042
1081
|
const cfScope = {
|
|
1043
|
-
tenantId:
|
|
1044
|
-
organizationId:
|
|
1082
|
+
tenantId: decision.tenantId,
|
|
1083
|
+
organizationId: decision.organizationId
|
|
1045
1084
|
};
|
|
1046
1085
|
const resolveKinds = await resolveCustomFieldKinds();
|
|
1047
1086
|
next = await decryptIndexDocCustomFields(
|
|
@@ -1056,6 +1095,10 @@ class HybridQueryEngine {
|
|
|
1056
1095
|
}
|
|
1057
1096
|
return next;
|
|
1058
1097
|
};
|
|
1098
|
+
const reportDecryptRefusals = () => {
|
|
1099
|
+
if (refusalTally.refused === 0) return;
|
|
1100
|
+
logger.warn(DECRYPT_REFUSAL_LOG_MESSAGE, refusalTally.toLogContext(String(entity)));
|
|
1101
|
+
};
|
|
1059
1102
|
let items;
|
|
1060
1103
|
let encryptedSortRowCapWarning;
|
|
1061
1104
|
if (requiresPlaintextSort) {
|
|
@@ -1136,6 +1179,10 @@ class HybridQueryEngine {
|
|
|
1136
1179
|
);
|
|
1137
1180
|
items = await mapWithConcurrency(itemsRaw, DECRYPT_CONCURRENCY, decryptRow);
|
|
1138
1181
|
}
|
|
1182
|
+
reportDecryptRefusals();
|
|
1183
|
+
if (injectsTenantIdForDecrypt) {
|
|
1184
|
+
for (const row of items) delete row.tenant_id;
|
|
1185
|
+
}
|
|
1139
1186
|
if (debugEnabled) this.debug("query:complete", { entity, total, items: items.length });
|
|
1140
1187
|
const typedItems = items;
|
|
1141
1188
|
let result = { items: typedItems, page, pageSize, total };
|