@open-mercato/core 0.6.6 → 0.6.7-develop.6552.1.5208a19369
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 +2 -2
- package/dist/generated/entities/gateway_session_initialization/index.js +23 -0
- package/dist/generated/entities/gateway_session_initialization/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +1 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +12 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/api_keys/api/keys/route.js +30 -13
- package/dist/modules/api_keys/api/keys/route.js.map +2 -2
- package/dist/modules/api_keys/cli.js +2 -1
- package/dist/modules/api_keys/cli.js.map +2 -2
- package/dist/modules/attachments/api/file/[id]/route.js +9 -7
- package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +9 -7
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
- package/dist/modules/attachments/api/route.js +21 -17
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentContentPreview.js +6 -35
- package/dist/modules/attachments/components/AttachmentContentPreview.js.map +2 -2
- package/dist/modules/attachments/lib/reconcileOrganization.js +110 -0
- package/dist/modules/attachments/lib/reconcileOrganization.js.map +7 -0
- package/dist/modules/attachments/lib/requestScope.js +10 -0
- package/dist/modules/attachments/lib/requestScope.js.map +7 -0
- package/dist/modules/audit_logs/backend/audit-logs/page.js +14 -24
- package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
- package/dist/modules/auth/api/autologin.js +85 -0
- package/dist/modules/auth/api/autologin.js.map +7 -0
- package/dist/modules/auth/api/users/route.js +6 -2
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/cli.js +3 -2
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/auth/lib/autologin.js +19 -0
- package/dist/modules/auth/lib/autologin.js.map +7 -0
- package/dist/modules/configs/lib/upgrade-actions.js +22 -0
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js +1 -0
- package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/roles/[id].js +17 -3
- package/dist/modules/customer_accounts/api/admin/roles/[id].js.map +2 -2
- package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js +6 -1
- package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js.map +2 -2
- package/dist/modules/customer_accounts/api/portal/users-invite.js +6 -1
- package/dist/modules/customer_accounts/api/portal/users-invite.js.map +2 -2
- package/dist/modules/customer_accounts/services/customerInvitationService.js +1 -0
- package/dist/modules/customer_accounts/services/customerInvitationService.js.map +2 -2
- package/dist/modules/customer_accounts/services/customerRbacService.js +6 -1
- package/dist/modules/customer_accounts/services/customerRbacService.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection-table.js +0 -2
- package/dist/modules/customer_accounts/widgets/injection-table.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +7 -1
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyDetailTabs.js +37 -43
- package/dist/modules/customers/components/detail/CompanyDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/DealDetailTabs.js +23 -35
- package/dist/modules/customers/components/detail/DealDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/DetailTabsLayout.js +6 -21
- package/dist/modules/customers/components/detail/DetailTabsLayout.js.map +2 -2
- package/dist/modules/customers/components/detail/MobilePersonDetail.js +26 -32
- package/dist/modules/customers/components/detail/MobilePersonDetail.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonDetailTabs.js +29 -39
- package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
- package/dist/modules/customers/components/detail/useDealsAccess.js +16 -0
- package/dist/modules/customers/components/detail/useDealsAccess.js.map +7 -0
- package/dist/modules/dashboards/api/roles/widgets/route.js +22 -3
- package/dist/modules/dashboards/api/roles/widgets/route.js.map +2 -2
- package/dist/modules/dashboards/api/users/widgets/route.js +22 -3
- package/dist/modules/dashboards/api/users/widgets/route.js.map +2 -2
- package/dist/modules/dashboards/cli.js +4 -3
- package/dist/modules/dashboards/cli.js.map +2 -2
- package/dist/modules/dashboards/lib/widgetAssignmentScope.js +9 -1
- package/dist/modules/dashboards/lib/widgetAssignmentScope.js.map +2 -2
- package/dist/modules/dictionaries/commands/entries.js +7 -14
- package/dist/modules/dictionaries/commands/entries.js.map +2 -2
- package/dist/modules/dictionaries/commands/entry-operations.js +7 -16
- package/dist/modules/dictionaries/commands/entry-operations.js.map +2 -2
- package/dist/modules/dictionaries/commands/factory.js +9 -3
- package/dist/modules/dictionaries/commands/factory.js.map +2 -2
- package/dist/modules/dictionaries/fields/dictionary.js +56 -41
- package/dist/modules/dictionaries/fields/dictionary.js.map +2 -2
- package/dist/modules/directory/utils/organizationScope.js +10 -3
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/records.js +5 -4
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/system/[entityId]/page.meta.js +1 -0
- package/dist/modules/entities/backend/entities/system/[entityId]/page.meta.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/page.js +49 -49
- package/dist/modules/entities/backend/entities/user/[entityId]/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/page.meta.js +1 -0
- package/dist/modules/entities/backend/entities/user/[entityId]/page.meta.js.map +2 -2
- package/dist/modules/entities/cli.js +2 -1
- package/dist/modules/entities/cli.js.map +2 -2
- package/dist/modules/feature_toggles/api/check/boolean/route.js +0 -14
- package/dist/modules/feature_toggles/api/check/boolean/route.js.map +2 -2
- package/dist/modules/feature_toggles/defaults.json +8 -0
- package/dist/modules/integrations/backend/integrations/[id]/page.js +9 -80
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/payment_gateways/data/entities.js +45 -0
- package/dist/modules/payment_gateways/data/entities.js.map +2 -2
- package/dist/modules/payment_gateways/lib/gateway-service.js +173 -38
- package/dist/modules/payment_gateways/lib/gateway-service.js.map +3 -3
- package/dist/modules/payment_gateways/lib/session-idempotency.js +104 -0
- package/dist/modules/payment_gateways/lib/session-idempotency.js.map +7 -0
- package/dist/modules/payment_gateways/migrations/Migration20260709220938_payment_gateways.js +11 -0
- package/dist/modules/payment_gateways/migrations/Migration20260709220938_payment_gateways.js.map +7 -0
- package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js +16 -14
- package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +18 -30
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/query_index/api/status.js +2 -8
- package/dist/modules/query_index/api/status.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/[id]/page.js +24 -24
- package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/create/page.js +6 -0
- package/dist/modules/sales/backend/sales/channels/create/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/create/page.meta.js +2 -0
- package/dist/modules/sales/backend/sales/channels/create/page.meta.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js +6 -0
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.meta.js +2 -0
- package/dist/modules/sales/backend/sales/channels/offers/page.meta.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js +6 -0
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.meta.js +2 -0
- package/dist/modules/sales/backend/sales/channels/page.meta.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +6 -4
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +44 -0
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/returns.js +2 -27
- package/dist/modules/sales/commands/returns.js.map +2 -2
- package/dist/modules/sales/components/SalesChannelsDisabledNotice.js +15 -0
- package/dist/modules/sales/components/SalesChannelsDisabledNotice.js.map +7 -0
- package/dist/modules/sales/components/documents/AddressesSection.js +6 -2
- package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +1 -0
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +9 -0
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentForm.js +51 -3
- package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +10 -8
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentDialog.js +59 -38
- package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
- package/dist/modules/sales/components/useSalesChannelsEnabled.js +44 -0
- package/dist/modules/sales/components/useSalesChannelsEnabled.js.map +7 -0
- package/dist/modules/sales/lib/salesChannelsToggle.js +18 -0
- package/dist/modules/sales/lib/salesChannelsToggle.js.map +7 -0
- package/dist/modules/sales/lib/salesChannelsToggleId.js +5 -0
- package/dist/modules/sales/lib/salesChannelsToggleId.js.map +7 -0
- package/dist/modules/sales/lib/shipments/snapshots.js +27 -1
- package/dist/modules/sales/lib/shipments/snapshots.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +22 -25
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +12 -14
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.js +10 -29
- package/dist/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.js.map +2 -2
- package/dist/modules/sync_excel/lib/adapters/customers.js +19 -20
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
- package/dist/modules/sync_excel/lib/parser.js +185 -0
- package/dist/modules/sync_excel/lib/parser.js.map +2 -2
- package/dist/modules/sync_excel/lib/upload-storage.js +20 -1
- package/dist/modules/sync_excel/lib/upload-storage.js.map +2 -2
- package/generated/entities/gateway_session_initialization/index.ts +10 -0
- package/generated/entities.ids.generated.ts +1 -0
- package/generated/entity-fields-registry.ts +12 -0
- package/package.json +9 -8
- package/src/modules/api_keys/api/keys/route.ts +36 -13
- package/src/modules/api_keys/cli.ts +2 -3
- package/src/modules/attachments/api/file/[id]/route.ts +11 -7
- package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +11 -7
- package/src/modules/attachments/api/route.ts +26 -21
- package/src/modules/attachments/components/AttachmentContentPreview.tsx +9 -44
- package/src/modules/attachments/lib/reconcileOrganization.ts +188 -0
- package/src/modules/attachments/lib/requestScope.ts +27 -0
- package/src/modules/audit_logs/backend/audit-logs/page.tsx +12 -22
- package/src/modules/auth/api/autologin.ts +100 -0
- package/src/modules/auth/api/users/route.ts +11 -2
- package/src/modules/auth/cli.ts +3 -2
- package/src/modules/auth/lib/autologin.ts +36 -0
- package/src/modules/configs/i18n/de.json +5 -1
- package/src/modules/configs/i18n/en.json +5 -1
- package/src/modules/configs/i18n/es.json +5 -1
- package/src/modules/configs/i18n/pl.json +5 -1
- package/src/modules/configs/lib/upgrade-actions.ts +24 -0
- package/src/modules/customer_accounts/AGENTS.md +8 -6
- package/src/modules/customer_accounts/api/admin/roles/[id]/acl.ts +1 -0
- package/src/modules/customer_accounts/api/admin/roles/[id].ts +17 -3
- package/src/modules/customer_accounts/api/portal/users/[id]/roles.ts +6 -1
- package/src/modules/customer_accounts/api/portal/users-invite.ts +6 -1
- package/src/modules/customer_accounts/services/customerInvitationService.ts +1 -0
- package/src/modules/customer_accounts/services/customerRbacService.ts +9 -2
- package/src/modules/customer_accounts/widgets/injection-table.ts +0 -2
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +11 -1
- package/src/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.tsx +6 -2
- package/src/modules/customers/components/detail/CompanyDetailTabs.tsx +39 -55
- package/src/modules/customers/components/detail/DealDetailTabs.tsx +24 -49
- package/src/modules/customers/components/detail/DetailTabsLayout.tsx +13 -24
- package/src/modules/customers/components/detail/MobilePersonDetail.tsx +25 -31
- package/src/modules/customers/components/detail/PersonDetailTabs.tsx +29 -50
- package/src/modules/customers/components/detail/useDealsAccess.ts +19 -0
- package/src/modules/dashboards/api/roles/widgets/route.ts +22 -3
- package/src/modules/dashboards/api/users/widgets/route.ts +22 -3
- package/src/modules/dashboards/cli.ts +4 -9
- package/src/modules/dashboards/lib/widgetAssignmentScope.ts +18 -0
- package/src/modules/dictionaries/commands/entries.ts +7 -16
- package/src/modules/dictionaries/commands/entry-operations.ts +8 -18
- package/src/modules/dictionaries/commands/factory.ts +8 -2
- package/src/modules/dictionaries/fields/dictionary.tsx +55 -32
- package/src/modules/dictionaries/i18n/de.json +2 -0
- package/src/modules/dictionaries/i18n/en.json +2 -0
- package/src/modules/dictionaries/i18n/es.json +2 -0
- package/src/modules/dictionaries/i18n/pl.json +2 -0
- package/src/modules/directory/utils/organizationScope.ts +36 -2
- package/src/modules/entities/api/records.ts +5 -7
- package/src/modules/entities/backend/entities/system/[entityId]/page.meta.ts +1 -0
- package/src/modules/entities/backend/entities/user/[entityId]/page.meta.ts +1 -0
- package/src/modules/entities/backend/entities/user/[entityId]/page.tsx +51 -49
- package/src/modules/entities/cli.ts +2 -1
- package/src/modules/entities/i18n/de.json +34 -0
- package/src/modules/entities/i18n/en.json +34 -0
- package/src/modules/entities/i18n/es.json +34 -0
- package/src/modules/entities/i18n/pl.json +34 -0
- package/src/modules/feature_toggles/api/check/boolean/route.ts +0 -15
- package/src/modules/feature_toggles/defaults.json +8 -0
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +16 -59
- package/src/modules/payment_gateways/data/entities.ts +39 -0
- package/src/modules/payment_gateways/lib/gateway-service.ts +211 -40
- package/src/modules/payment_gateways/lib/session-idempotency.ts +142 -0
- package/src/modules/payment_gateways/migrations/.snapshot-open-mercato.json +236 -0
- package/src/modules/payment_gateways/migrations/Migration20260709220938_payment_gateways.ts +10 -0
- package/src/modules/planner/backend/planner/availability-rulesets/[id]/page.tsx +14 -20
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +16 -34
- package/src/modules/query_index/api/status.ts +2 -8
- package/src/modules/resources/backend/resources/resources/[id]/page.tsx +27 -37
- package/src/modules/sales/backend/sales/channels/create/page.meta.ts +3 -0
- package/src/modules/sales/backend/sales/channels/create/page.tsx +7 -0
- package/src/modules/sales/backend/sales/channels/offers/page.meta.ts +3 -0
- package/src/modules/sales/backend/sales/channels/offers/page.tsx +7 -0
- package/src/modules/sales/backend/sales/channels/page.meta.ts +4 -0
- package/src/modules/sales/backend/sales/channels/page.tsx +7 -0
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +7 -5
- package/src/modules/sales/commands/documents.ts +65 -0
- package/src/modules/sales/commands/returns.ts +2 -32
- package/src/modules/sales/components/SalesChannelsDisabledNotice.tsx +21 -0
- package/src/modules/sales/components/documents/AddressesSection.tsx +6 -2
- package/src/modules/sales/components/documents/ItemsSection.tsx +5 -0
- package/src/modules/sales/components/documents/LineItemDialog.tsx +10 -0
- package/src/modules/sales/components/documents/SalesDocumentForm.tsx +53 -3
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +10 -8
- package/src/modules/sales/components/documents/ShipmentDialog.tsx +65 -40
- package/src/modules/sales/components/useSalesChannelsEnabled.ts +59 -0
- package/src/modules/sales/i18n/de.json +5 -0
- package/src/modules/sales/i18n/en.json +5 -0
- package/src/modules/sales/i18n/es.json +5 -0
- package/src/modules/sales/i18n/pl.json +5 -0
- package/src/modules/sales/lib/salesChannelsToggle.ts +26 -0
- package/src/modules/sales/lib/salesChannelsToggleId.ts +1 -0
- package/src/modules/sales/lib/shipments/snapshots.ts +30 -0
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +25 -34
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +11 -17
- package/src/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.tsx +11 -28
- package/src/modules/sync_excel/lib/adapters/customers.ts +20 -22
- package/src/modules/sync_excel/lib/parser.ts +245 -0
- package/src/modules/sync_excel/lib/upload-storage.ts +23 -1
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { TextDecoder } from "util";
|
|
2
|
+
const CSV_DELIMITER_PROBE_ROWS = 5;
|
|
3
|
+
const CSV_DELIMITER_PROBE_MAX_CHARS = 64 * 1024;
|
|
1
4
|
function stripUtf8Bom(value) {
|
|
2
5
|
return value.charCodeAt(0) === 65279 ? value.slice(1) : value;
|
|
3
6
|
}
|
|
@@ -87,6 +90,186 @@ function rowsToObjects(headers, rows, maxRows) {
|
|
|
87
90
|
return record;
|
|
88
91
|
});
|
|
89
92
|
}
|
|
93
|
+
async function createCsvRowStream(input) {
|
|
94
|
+
const iterator = input[Symbol.asyncIterator]();
|
|
95
|
+
const decoder = new TextDecoder("utf-8");
|
|
96
|
+
const prefixTexts = [];
|
|
97
|
+
let probeText = "";
|
|
98
|
+
while (probeText.length < CSV_DELIMITER_PROBE_MAX_CHARS) {
|
|
99
|
+
const next = await iterator.next();
|
|
100
|
+
if (next.done) break;
|
|
101
|
+
const text = typeof next.value === "string" ? next.value : decoder.decode(next.value, { stream: true });
|
|
102
|
+
prefixTexts.push(text);
|
|
103
|
+
probeText += text;
|
|
104
|
+
if (parseCsvText(probeText, ",").length >= CSV_DELIMITER_PROBE_ROWS) {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const delimiter = detectCsvDelimiter(probeText);
|
|
109
|
+
async function* decodedChunks() {
|
|
110
|
+
for (const text of prefixTexts) {
|
|
111
|
+
yield text;
|
|
112
|
+
}
|
|
113
|
+
while (true) {
|
|
114
|
+
const next = await iterator.next();
|
|
115
|
+
if (next.done) break;
|
|
116
|
+
yield typeof next.value === "string" ? next.value : decoder.decode(next.value, { stream: true });
|
|
117
|
+
}
|
|
118
|
+
const finalChunk = decoder.decode();
|
|
119
|
+
if (finalChunk.length > 0) {
|
|
120
|
+
yield finalChunk;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
delimiter,
|
|
125
|
+
rows: parseCsvRowsFromTextChunks(decodedChunks(), delimiter)
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async function* parseCsvRowsFromTextChunks(chunks, delimiter) {
|
|
129
|
+
let currentRow = [];
|
|
130
|
+
let currentValue = "";
|
|
131
|
+
let inQuotes = false;
|
|
132
|
+
let isFirstChunk = true;
|
|
133
|
+
let previousWasCarriageReturn = false;
|
|
134
|
+
let pendingQuote = false;
|
|
135
|
+
let pendingQuoteWasInQuotes = false;
|
|
136
|
+
const completeRow = () => {
|
|
137
|
+
currentRow.push(currentValue);
|
|
138
|
+
const completedRow = currentRow;
|
|
139
|
+
currentRow = [];
|
|
140
|
+
currentValue = "";
|
|
141
|
+
return completedRow.some((value) => value.trim().length > 0) ? completedRow : null;
|
|
142
|
+
};
|
|
143
|
+
for await (const chunk of chunks) {
|
|
144
|
+
const text = isFirstChunk ? stripUtf8Bom(chunk) : chunk;
|
|
145
|
+
isFirstChunk = false;
|
|
146
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
147
|
+
const character = text[index];
|
|
148
|
+
if (pendingQuote) {
|
|
149
|
+
pendingQuote = false;
|
|
150
|
+
if (pendingQuoteWasInQuotes && character === '"') {
|
|
151
|
+
currentValue += '"';
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
inQuotes = !pendingQuoteWasInQuotes;
|
|
155
|
+
}
|
|
156
|
+
if (previousWasCarriageReturn) {
|
|
157
|
+
previousWasCarriageReturn = false;
|
|
158
|
+
if (character === "\n") {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (character === '"') {
|
|
163
|
+
if (index + 1 >= text.length) {
|
|
164
|
+
pendingQuote = true;
|
|
165
|
+
pendingQuoteWasInQuotes = inQuotes;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (inQuotes && text[index + 1] === '"') {
|
|
169
|
+
currentValue += '"';
|
|
170
|
+
index += 1;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
inQuotes = !inQuotes;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (!inQuotes && character === delimiter) {
|
|
177
|
+
currentRow.push(currentValue);
|
|
178
|
+
currentValue = "";
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (character === "\r") {
|
|
182
|
+
previousWasCarriageReturn = true;
|
|
183
|
+
if (inQuotes) {
|
|
184
|
+
currentValue += "\n";
|
|
185
|
+
} else {
|
|
186
|
+
const row = completeRow();
|
|
187
|
+
if (row) yield row;
|
|
188
|
+
}
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (character === "\n") {
|
|
192
|
+
if (inQuotes) {
|
|
193
|
+
currentValue += "\n";
|
|
194
|
+
} else {
|
|
195
|
+
const row = completeRow();
|
|
196
|
+
if (row) yield row;
|
|
197
|
+
}
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
currentValue += character;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (currentValue.length > 0 || currentRow.length > 0) {
|
|
204
|
+
const row = completeRow();
|
|
205
|
+
if (row) yield row;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async function parseCsvStreamMetadata(input) {
|
|
209
|
+
const stream = await createCsvRowStream(input);
|
|
210
|
+
let headers = [];
|
|
211
|
+
let hasHeaders = false;
|
|
212
|
+
let totalRows = 0;
|
|
213
|
+
for await (const row of stream.rows) {
|
|
214
|
+
if (!hasHeaders) {
|
|
215
|
+
headers = normalizeHeaders(row);
|
|
216
|
+
hasHeaders = true;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
totalRows += 1;
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
headers,
|
|
223
|
+
totalRows,
|
|
224
|
+
delimiter: stream.delimiter,
|
|
225
|
+
encoding: "utf-8"
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
async function* parseCsvDocumentBatches(input, options) {
|
|
229
|
+
const stream = await createCsvRowStream(input);
|
|
230
|
+
const batchSize = Math.max(1, Math.floor(options.batchSize));
|
|
231
|
+
const startOffset = Math.max(0, Math.floor(options.startOffset ?? 0));
|
|
232
|
+
let headers = null;
|
|
233
|
+
let rowOffset = 0;
|
|
234
|
+
let batchRows = [];
|
|
235
|
+
let batchStart = startOffset;
|
|
236
|
+
for await (const row of stream.rows) {
|
|
237
|
+
if (!headers) {
|
|
238
|
+
headers = normalizeHeaders(row);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (rowOffset < startOffset) {
|
|
242
|
+
rowOffset += 1;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (batchRows.length === 0) {
|
|
246
|
+
batchStart = rowOffset;
|
|
247
|
+
}
|
|
248
|
+
batchRows.push(row);
|
|
249
|
+
rowOffset += 1;
|
|
250
|
+
if (batchRows.length >= batchSize) {
|
|
251
|
+
yield {
|
|
252
|
+
headers,
|
|
253
|
+
rows: rowsToObjects(headers, batchRows, batchRows.length),
|
|
254
|
+
rowStart: batchStart,
|
|
255
|
+
nextOffset: rowOffset,
|
|
256
|
+
delimiter: stream.delimiter,
|
|
257
|
+
encoding: "utf-8"
|
|
258
|
+
};
|
|
259
|
+
batchRows = [];
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (headers && batchRows.length > 0) {
|
|
263
|
+
yield {
|
|
264
|
+
headers,
|
|
265
|
+
rows: rowsToObjects(headers, batchRows, batchRows.length),
|
|
266
|
+
rowStart: batchStart,
|
|
267
|
+
nextOffset: rowOffset,
|
|
268
|
+
delimiter: stream.delimiter,
|
|
269
|
+
encoding: "utf-8"
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
90
273
|
function parseCsvPreview(buffer, options = {}) {
|
|
91
274
|
const text = buffer.toString("utf-8");
|
|
92
275
|
const delimiter = detectCsvDelimiter(text);
|
|
@@ -119,7 +302,9 @@ function parseCsvDocument(buffer) {
|
|
|
119
302
|
export {
|
|
120
303
|
detectCsvDelimiter,
|
|
121
304
|
parseCsvDocument,
|
|
305
|
+
parseCsvDocumentBatches,
|
|
122
306
|
parseCsvPreview,
|
|
307
|
+
parseCsvStreamMetadata,
|
|
123
308
|
parseCsvText
|
|
124
309
|
};
|
|
125
310
|
//# sourceMappingURL=parser.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/sync_excel/lib/parser.ts"],
|
|
4
|
-
"sourcesContent": ["export type CsvPreviewRow = Record<string, string | null>\n\nexport type CsvPreview = {\n headers: string[]\n sampleRows: CsvPreviewRow[]\n totalRows: number\n delimiter: ',' | ';'\n encoding: 'utf-8'\n}\n\nexport type CsvDocument = {\n headers: string[]\n rows: CsvPreviewRow[]\n totalRows: number\n delimiter: ',' | ';'\n encoding: 'utf-8'\n}\n\ntype ParseCsvPreviewOptions = {\n maxRows?: number\n}\n\nfunction stripUtf8Bom(value: string): string {\n return value.charCodeAt(0) === 0xfeff ? value.slice(1) : value\n}\n\nfunction countFields(line: string, delimiter: ',' | ';'): number {\n let count = 1\n let inQuotes = false\n\n for (let index = 0; index < line.length; index += 1) {\n const character = line[index]\n if (character === '\"') {\n if (inQuotes && line[index + 1] === '\"') {\n index += 1\n continue\n }\n inQuotes = !inQuotes\n continue\n }\n if (!inQuotes && character === delimiter) {\n count += 1\n }\n }\n\n return count\n}\n\nexport function detectCsvDelimiter(text: string): ',' | ';' {\n const normalized = stripUtf8Bom(text).replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n')\n const lines = normalized\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .slice(0, 5)\n\n if (lines.length === 0) return ','\n\n const commaScore = lines.reduce((accumulator, line) => accumulator + countFields(line, ','), 0)\n const semicolonScore = lines.reduce((accumulator, line) => accumulator + countFields(line, ';'), 0)\n\n return semicolonScore > commaScore ? ';' : ','\n}\n\nexport function parseCsvText(text: string, delimiter: ',' | ';'): string[][] {\n const normalized = stripUtf8Bom(text).replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n')\n const rows: string[][] = []\n let currentRow: string[] = []\n let currentValue = ''\n let inQuotes = false\n\n for (let index = 0; index < normalized.length; index += 1) {\n const character = normalized[index]\n\n if (character === '\"') {\n if (inQuotes && normalized[index + 1] === '\"') {\n currentValue += '\"'\n index += 1\n continue\n }\n inQuotes = !inQuotes\n continue\n }\n\n if (!inQuotes && character === delimiter) {\n currentRow.push(currentValue)\n currentValue = ''\n continue\n }\n\n if (!inQuotes && character === '\\n') {\n currentRow.push(currentValue)\n const isMeaningfulRow = currentRow.some((value) => value.trim().length > 0)\n if (isMeaningfulRow) {\n rows.push(currentRow)\n }\n currentRow = []\n currentValue = ''\n continue\n }\n\n currentValue += character\n }\n\n if (currentValue.length > 0 || currentRow.length > 0) {\n currentRow.push(currentValue)\n const isMeaningfulRow = currentRow.some((value) => value.trim().length > 0)\n if (isMeaningfulRow) {\n rows.push(currentRow)\n }\n }\n\n return rows\n}\n\nfunction normalizeHeaders(headers: string[]): string[] {\n return headers.map((header, index) => {\n const trimmed = header.trim()\n return trimmed.length > 0 ? trimmed : `Column ${index + 1}`\n })\n}\n\nfunction rowsToObjects(headers: string[], rows: string[][], maxRows: number): CsvPreviewRow[] {\n return rows.slice(0, maxRows).map((row) => {\n const record: CsvPreviewRow = {}\n headers.forEach((header, index) => {\n const value = row[index]\n record[header] = typeof value === 'string' && value.length > 0 ? value : null\n })\n return record\n })\n}\n\nexport function parseCsvPreview(buffer: Buffer, options: ParseCsvPreviewOptions = {}): CsvPreview {\n const text = buffer.toString('utf-8')\n const delimiter = detectCsvDelimiter(text)\n const rows = parseCsvText(text, delimiter)\n const [rawHeaders = [], ...dataRows] = rows\n const headers = normalizeHeaders(rawHeaders)\n const maxRows = options.maxRows ?? 5\n\n return {\n headers,\n sampleRows: rowsToObjects(headers, dataRows, maxRows),\n totalRows: dataRows.length,\n delimiter,\n encoding: 'utf-8',\n }\n}\n\nexport function parseCsvDocument(buffer: Buffer): CsvDocument {\n const text = buffer.toString('utf-8')\n const delimiter = detectCsvDelimiter(text)\n const rows = parseCsvText(text, delimiter)\n const [rawHeaders = [], ...dataRows] = rows\n const headers = normalizeHeaders(rawHeaders)\n\n return {\n headers,\n rows: rowsToObjects(headers, dataRows, dataRows.length),\n totalRows: dataRows.length,\n delimiter,\n encoding: 'utf-8',\n }\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { TextDecoder } from 'util'\n\nexport type CsvPreviewRow = Record<string, string | null>\n\nexport type CsvPreview = {\n headers: string[]\n sampleRows: CsvPreviewRow[]\n totalRows: number\n delimiter: ',' | ';'\n encoding: 'utf-8'\n}\n\nexport type CsvDocument = {\n headers: string[]\n rows: CsvPreviewRow[]\n totalRows: number\n delimiter: ',' | ';'\n encoding: 'utf-8'\n}\n\nexport type CsvStreamMetadata = {\n headers: string[]\n totalRows: number\n delimiter: ',' | ';'\n encoding: 'utf-8'\n}\n\nexport type CsvDocumentBatch = {\n headers: string[]\n rows: CsvPreviewRow[]\n rowStart: number\n nextOffset: number\n delimiter: ',' | ';'\n encoding: 'utf-8'\n}\n\ntype ParseCsvPreviewOptions = {\n maxRows?: number\n}\n\ntype ParseCsvBatchOptions = {\n batchSize: number\n startOffset?: number\n}\n\ntype CsvInputChunk = Buffer | Uint8Array | string\n\nconst CSV_DELIMITER_PROBE_ROWS = 5\nconst CSV_DELIMITER_PROBE_MAX_CHARS = 64 * 1024\n\nfunction stripUtf8Bom(value: string): string {\n return value.charCodeAt(0) === 0xfeff ? value.slice(1) : value\n}\n\nfunction countFields(line: string, delimiter: ',' | ';'): number {\n let count = 1\n let inQuotes = false\n\n for (let index = 0; index < line.length; index += 1) {\n const character = line[index]\n if (character === '\"') {\n if (inQuotes && line[index + 1] === '\"') {\n index += 1\n continue\n }\n inQuotes = !inQuotes\n continue\n }\n if (!inQuotes && character === delimiter) {\n count += 1\n }\n }\n\n return count\n}\n\nexport function detectCsvDelimiter(text: string): ',' | ';' {\n const normalized = stripUtf8Bom(text).replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n')\n const lines = normalized\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .slice(0, 5)\n\n if (lines.length === 0) return ','\n\n const commaScore = lines.reduce((accumulator, line) => accumulator + countFields(line, ','), 0)\n const semicolonScore = lines.reduce((accumulator, line) => accumulator + countFields(line, ';'), 0)\n\n return semicolonScore > commaScore ? ';' : ','\n}\n\nexport function parseCsvText(text: string, delimiter: ',' | ';'): string[][] {\n const normalized = stripUtf8Bom(text).replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n')\n const rows: string[][] = []\n let currentRow: string[] = []\n let currentValue = ''\n let inQuotes = false\n\n for (let index = 0; index < normalized.length; index += 1) {\n const character = normalized[index]\n\n if (character === '\"') {\n if (inQuotes && normalized[index + 1] === '\"') {\n currentValue += '\"'\n index += 1\n continue\n }\n inQuotes = !inQuotes\n continue\n }\n\n if (!inQuotes && character === delimiter) {\n currentRow.push(currentValue)\n currentValue = ''\n continue\n }\n\n if (!inQuotes && character === '\\n') {\n currentRow.push(currentValue)\n const isMeaningfulRow = currentRow.some((value) => value.trim().length > 0)\n if (isMeaningfulRow) {\n rows.push(currentRow)\n }\n currentRow = []\n currentValue = ''\n continue\n }\n\n currentValue += character\n }\n\n if (currentValue.length > 0 || currentRow.length > 0) {\n currentRow.push(currentValue)\n const isMeaningfulRow = currentRow.some((value) => value.trim().length > 0)\n if (isMeaningfulRow) {\n rows.push(currentRow)\n }\n }\n\n return rows\n}\n\nfunction normalizeHeaders(headers: string[]): string[] {\n return headers.map((header, index) => {\n const trimmed = header.trim()\n return trimmed.length > 0 ? trimmed : `Column ${index + 1}`\n })\n}\n\nfunction rowsToObjects(headers: string[], rows: string[][], maxRows: number): CsvPreviewRow[] {\n return rows.slice(0, maxRows).map((row) => {\n const record: CsvPreviewRow = {}\n headers.forEach((header, index) => {\n const value = row[index]\n record[header] = typeof value === 'string' && value.length > 0 ? value : null\n })\n return record\n })\n}\n\nasync function createCsvRowStream(input: AsyncIterable<CsvInputChunk>): Promise<{\n delimiter: ',' | ';'\n rows: AsyncIterable<string[]>\n}> {\n const iterator = input[Symbol.asyncIterator]()\n const decoder = new TextDecoder('utf-8')\n const prefixTexts: string[] = []\n let probeText = ''\n\n while (probeText.length < CSV_DELIMITER_PROBE_MAX_CHARS) {\n const next = await iterator.next()\n if (next.done) break\n\n const text = typeof next.value === 'string' ? next.value : decoder.decode(next.value, { stream: true })\n prefixTexts.push(text)\n probeText += text\n\n if (parseCsvText(probeText, ',').length >= CSV_DELIMITER_PROBE_ROWS) {\n break\n }\n }\n\n const delimiter = detectCsvDelimiter(probeText)\n\n async function* decodedChunks(): AsyncIterable<string> {\n for (const text of prefixTexts) {\n yield text\n }\n while (true) {\n const next = await iterator.next()\n if (next.done) break\n yield typeof next.value === 'string' ? next.value : decoder.decode(next.value, { stream: true })\n }\n const finalChunk = decoder.decode()\n if (finalChunk.length > 0) {\n yield finalChunk\n }\n }\n\n return {\n delimiter,\n rows: parseCsvRowsFromTextChunks(decodedChunks(), delimiter),\n }\n}\n\nasync function* parseCsvRowsFromTextChunks(\n chunks: AsyncIterable<string>,\n delimiter: ',' | ';',\n): AsyncIterable<string[]> {\n let currentRow: string[] = []\n let currentValue = ''\n let inQuotes = false\n let isFirstChunk = true\n let previousWasCarriageReturn = false\n let pendingQuote = false\n let pendingQuoteWasInQuotes = false\n\n const completeRow = (): string[] | null => {\n currentRow.push(currentValue)\n const completedRow = currentRow\n currentRow = []\n currentValue = ''\n return completedRow.some((value) => value.trim().length > 0) ? completedRow : null\n }\n\n for await (const chunk of chunks) {\n const text = isFirstChunk ? stripUtf8Bom(chunk) : chunk\n isFirstChunk = false\n\n for (let index = 0; index < text.length; index += 1) {\n const character = text[index]\n\n if (pendingQuote) {\n pendingQuote = false\n if (pendingQuoteWasInQuotes && character === '\"') {\n currentValue += '\"'\n continue\n }\n inQuotes = !pendingQuoteWasInQuotes\n }\n\n if (previousWasCarriageReturn) {\n previousWasCarriageReturn = false\n if (character === '\\n') {\n continue\n }\n }\n\n if (character === '\"') {\n if (index + 1 >= text.length) {\n pendingQuote = true\n pendingQuoteWasInQuotes = inQuotes\n continue\n }\n if (inQuotes && text[index + 1] === '\"') {\n currentValue += '\"'\n index += 1\n continue\n }\n inQuotes = !inQuotes\n continue\n }\n\n if (!inQuotes && character === delimiter) {\n currentRow.push(currentValue)\n currentValue = ''\n continue\n }\n\n if (character === '\\r') {\n previousWasCarriageReturn = true\n if (inQuotes) {\n currentValue += '\\n'\n } else {\n const row = completeRow()\n if (row) yield row\n }\n continue\n }\n\n if (character === '\\n') {\n if (inQuotes) {\n currentValue += '\\n'\n } else {\n const row = completeRow()\n if (row) yield row\n }\n continue\n }\n\n currentValue += character\n }\n }\n\n if (currentValue.length > 0 || currentRow.length > 0) {\n const row = completeRow()\n if (row) yield row\n }\n}\n\nexport async function parseCsvStreamMetadata(input: AsyncIterable<CsvInputChunk>): Promise<CsvStreamMetadata> {\n const stream = await createCsvRowStream(input)\n let headers: string[] = []\n let hasHeaders = false\n let totalRows = 0\n\n for await (const row of stream.rows) {\n if (!hasHeaders) {\n headers = normalizeHeaders(row)\n hasHeaders = true\n continue\n }\n totalRows += 1\n }\n\n return {\n headers,\n totalRows,\n delimiter: stream.delimiter,\n encoding: 'utf-8',\n }\n}\n\nexport async function* parseCsvDocumentBatches(\n input: AsyncIterable<CsvInputChunk>,\n options: ParseCsvBatchOptions,\n): AsyncIterable<CsvDocumentBatch> {\n const stream = await createCsvRowStream(input)\n const batchSize = Math.max(1, Math.floor(options.batchSize))\n const startOffset = Math.max(0, Math.floor(options.startOffset ?? 0))\n let headers: string[] | null = null\n let rowOffset = 0\n let batchRows: string[][] = []\n let batchStart = startOffset\n\n for await (const row of stream.rows) {\n if (!headers) {\n headers = normalizeHeaders(row)\n continue\n }\n\n if (rowOffset < startOffset) {\n rowOffset += 1\n continue\n }\n\n if (batchRows.length === 0) {\n batchStart = rowOffset\n }\n batchRows.push(row)\n rowOffset += 1\n\n if (batchRows.length >= batchSize) {\n yield {\n headers,\n rows: rowsToObjects(headers, batchRows, batchRows.length),\n rowStart: batchStart,\n nextOffset: rowOffset,\n delimiter: stream.delimiter,\n encoding: 'utf-8',\n }\n batchRows = []\n }\n }\n\n if (headers && batchRows.length > 0) {\n yield {\n headers,\n rows: rowsToObjects(headers, batchRows, batchRows.length),\n rowStart: batchStart,\n nextOffset: rowOffset,\n delimiter: stream.delimiter,\n encoding: 'utf-8',\n }\n }\n}\n\nexport function parseCsvPreview(buffer: Buffer, options: ParseCsvPreviewOptions = {}): CsvPreview {\n const text = buffer.toString('utf-8')\n const delimiter = detectCsvDelimiter(text)\n const rows = parseCsvText(text, delimiter)\n const [rawHeaders = [], ...dataRows] = rows\n const headers = normalizeHeaders(rawHeaders)\n const maxRows = options.maxRows ?? 5\n\n return {\n headers,\n sampleRows: rowsToObjects(headers, dataRows, maxRows),\n totalRows: dataRows.length,\n delimiter,\n encoding: 'utf-8',\n }\n}\n\nexport function parseCsvDocument(buffer: Buffer): CsvDocument {\n const text = buffer.toString('utf-8')\n const delimiter = detectCsvDelimiter(text)\n const rows = parseCsvText(text, delimiter)\n const [rawHeaders = [], ...dataRows] = rows\n const headers = normalizeHeaders(rawHeaders)\n\n return {\n headers,\n rows: rowsToObjects(headers, dataRows, dataRows.length),\n totalRows: dataRows.length,\n delimiter,\n encoding: 'utf-8',\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { promises as fs } from "fs";
|
|
1
|
+
import { createReadStream, promises as fs } from "fs";
|
|
2
|
+
import { Readable } from "stream";
|
|
2
3
|
import { randomUUID } from "crypto";
|
|
3
4
|
import { Attachment, AttachmentPartition } from "../../attachments/data/entities.js";
|
|
4
5
|
import { buildAttachmentFileUrl } from "../../attachments/lib/imageUrls.js";
|
|
@@ -59,8 +60,26 @@ async function readSyncExcelUploadBuffer(attachment) {
|
|
|
59
60
|
throw error;
|
|
60
61
|
}
|
|
61
62
|
}
|
|
63
|
+
async function createSyncExcelUploadReadStream(attachment) {
|
|
64
|
+
const inlineCsvBase64 = attachment.storageMetadata?.inlineCsvBase64;
|
|
65
|
+
try {
|
|
66
|
+
const absolutePath = resolveAttachmentAbsolutePath(
|
|
67
|
+
attachment.partitionCode,
|
|
68
|
+
attachment.storagePath,
|
|
69
|
+
attachment.storageDriver
|
|
70
|
+
);
|
|
71
|
+
await fs.access(absolutePath);
|
|
72
|
+
return createReadStream(absolutePath);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (typeof inlineCsvBase64 === "string" && inlineCsvBase64.length > 0) {
|
|
75
|
+
return Readable.from([Buffer.from(inlineCsvBase64, "base64")]);
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
62
80
|
export {
|
|
63
81
|
createSyncExcelUploadAttachment,
|
|
82
|
+
createSyncExcelUploadReadStream,
|
|
64
83
|
readSyncExcelUploadBuffer
|
|
65
84
|
};
|
|
66
85
|
//# sourceMappingURL=upload-storage.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/sync_excel/lib/upload-storage.ts"],
|
|
4
|
-
"sourcesContent": ["import { promises as fs } from 'fs'\nimport { randomUUID } from 'crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { Attachment, AttachmentPartition } from '../../attachments/data/entities'\nimport { buildAttachmentFileUrl } from '../../attachments/lib/imageUrls'\nimport { ensureDefaultPartitions } from '../../attachments/lib/partitions'\nimport { resolveAttachmentAbsolutePath, storePartitionFile } from '../../attachments/lib/storage'\n\nconst SYNC_EXCEL_ATTACHMENT_ENTITY_ID = 'sync_excel:upload'\nconst SYNC_EXCEL_PARTITION_CODE = 'privateAttachments'\n\nexport async function createSyncExcelUploadAttachment(input: {\n em: EntityManager\n uploadId: string\n organizationId: string\n tenantId: string\n fileName: string\n mimeType: string\n buffer: Buffer\n}): Promise<Attachment> {\n await ensureDefaultPartitions(input.em)\n\n // Attachment partitions are global storage configuration, not tenant-scoped payload data.\n const partition = await input.em.findOne(AttachmentPartition, { code: SYNC_EXCEL_PARTITION_CODE })\n if (!partition) {\n throw new Error('Storage partition is not configured.')\n }\n\n const stored = await storePartitionFile({\n partitionCode: partition.code,\n orgId: input.organizationId,\n tenantId: input.tenantId,\n fileName: input.fileName,\n buffer: input.buffer,\n })\n\n const attachmentId = randomUUID()\n const attachment = input.em.create(Attachment, {\n id: attachmentId,\n entityId: SYNC_EXCEL_ATTACHMENT_ENTITY_ID,\n recordId: input.uploadId,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n partitionCode: partition.code,\n fileName: input.fileName,\n mimeType: input.mimeType,\n fileSize: input.buffer.length,\n storageDriver: partition.storageDriver || 'local',\n storagePath: stored.storagePath,\n url: buildAttachmentFileUrl(attachmentId),\n storageMetadata: {\n module: 'sync_excel',\n temporary: true,\n uploadId: input.uploadId,\n },\n })\n\n input.em.persist(attachment)\n await input.em.flush()\n\n return attachment\n}\n\nexport async function readSyncExcelUploadBuffer(\n attachment: Pick<Attachment, 'partitionCode' | 'storagePath' | 'storageDriver' | 'storageMetadata'>,\n): Promise<Buffer> {\n const inlineCsvBase64 = attachment.storageMetadata?.inlineCsvBase64\n const absolutePath = resolveAttachmentAbsolutePath(\n attachment.partitionCode,\n attachment.storagePath,\n attachment.storageDriver,\n )\n try {\n return await fs.readFile(absolutePath)\n } catch (error) {\n if (typeof inlineCsvBase64 === 'string' && inlineCsvBase64.length > 0) {\n return Buffer.from(inlineCsvBase64, 'base64')\n }\n throw error\n }\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,YAAY,UAAU;
|
|
4
|
+
"sourcesContent": ["import { createReadStream, promises as fs } from 'fs'\nimport { Readable } from 'stream'\nimport { randomUUID } from 'crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { Attachment, AttachmentPartition } from '../../attachments/data/entities'\nimport { buildAttachmentFileUrl } from '../../attachments/lib/imageUrls'\nimport { ensureDefaultPartitions } from '../../attachments/lib/partitions'\nimport { resolveAttachmentAbsolutePath, storePartitionFile } from '../../attachments/lib/storage'\n\nconst SYNC_EXCEL_ATTACHMENT_ENTITY_ID = 'sync_excel:upload'\nconst SYNC_EXCEL_PARTITION_CODE = 'privateAttachments'\n\nexport async function createSyncExcelUploadAttachment(input: {\n em: EntityManager\n uploadId: string\n organizationId: string\n tenantId: string\n fileName: string\n mimeType: string\n buffer: Buffer\n}): Promise<Attachment> {\n await ensureDefaultPartitions(input.em)\n\n // Attachment partitions are global storage configuration, not tenant-scoped payload data.\n const partition = await input.em.findOne(AttachmentPartition, { code: SYNC_EXCEL_PARTITION_CODE })\n if (!partition) {\n throw new Error('Storage partition is not configured.')\n }\n\n const stored = await storePartitionFile({\n partitionCode: partition.code,\n orgId: input.organizationId,\n tenantId: input.tenantId,\n fileName: input.fileName,\n buffer: input.buffer,\n })\n\n const attachmentId = randomUUID()\n const attachment = input.em.create(Attachment, {\n id: attachmentId,\n entityId: SYNC_EXCEL_ATTACHMENT_ENTITY_ID,\n recordId: input.uploadId,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n partitionCode: partition.code,\n fileName: input.fileName,\n mimeType: input.mimeType,\n fileSize: input.buffer.length,\n storageDriver: partition.storageDriver || 'local',\n storagePath: stored.storagePath,\n url: buildAttachmentFileUrl(attachmentId),\n storageMetadata: {\n module: 'sync_excel',\n temporary: true,\n uploadId: input.uploadId,\n },\n })\n\n input.em.persist(attachment)\n await input.em.flush()\n\n return attachment\n}\n\nexport async function readSyncExcelUploadBuffer(\n attachment: Pick<Attachment, 'partitionCode' | 'storagePath' | 'storageDriver' | 'storageMetadata'>,\n): Promise<Buffer> {\n const inlineCsvBase64 = attachment.storageMetadata?.inlineCsvBase64\n const absolutePath = resolveAttachmentAbsolutePath(\n attachment.partitionCode,\n attachment.storagePath,\n attachment.storageDriver,\n )\n try {\n return await fs.readFile(absolutePath)\n } catch (error) {\n if (typeof inlineCsvBase64 === 'string' && inlineCsvBase64.length > 0) {\n return Buffer.from(inlineCsvBase64, 'base64')\n }\n throw error\n }\n}\n\nexport async function createSyncExcelUploadReadStream(\n attachment: Pick<Attachment, 'partitionCode' | 'storagePath' | 'storageDriver' | 'storageMetadata'>,\n): Promise<Readable> {\n const inlineCsvBase64 = attachment.storageMetadata?.inlineCsvBase64\n\n try {\n const absolutePath = resolveAttachmentAbsolutePath(\n attachment.partitionCode,\n attachment.storagePath,\n attachment.storageDriver,\n )\n await fs.access(absolutePath)\n return createReadStream(absolutePath)\n } catch (error) {\n if (typeof inlineCsvBase64 === 'string' && inlineCsvBase64.length > 0) {\n return Readable.from([Buffer.from(inlineCsvBase64, 'base64')])\n }\n throw error\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,kBAAkB,YAAY,UAAU;AACjD,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAE3B,SAAS,YAAY,2BAA2B;AAChD,SAAS,8BAA8B;AACvC,SAAS,+BAA+B;AACxC,SAAS,+BAA+B,0BAA0B;AAElE,MAAM,kCAAkC;AACxC,MAAM,4BAA4B;AAElC,eAAsB,gCAAgC,OAQ9B;AACtB,QAAM,wBAAwB,MAAM,EAAE;AAGtC,QAAM,YAAY,MAAM,MAAM,GAAG,QAAQ,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjG,MAAI,CAAC,WAAW;AACd,UAAM,IAAI,MAAM,sCAAsC;AAAA,EACxD;AAEA,QAAM,SAAS,MAAM,mBAAmB;AAAA,IACtC,eAAe,UAAU;AAAA,IACzB,OAAO,MAAM;AAAA,IACb,UAAU,MAAM;AAAA,IAChB,UAAU,MAAM;AAAA,IAChB,QAAQ,MAAM;AAAA,EAChB,CAAC;AAED,QAAM,eAAe,WAAW;AAChC,QAAM,aAAa,MAAM,GAAG,OAAO,YAAY;AAAA,IAC7C,IAAI;AAAA,IACJ,UAAU;AAAA,IACV,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,IACtB,UAAU,MAAM;AAAA,IAChB,eAAe,UAAU;AAAA,IACzB,UAAU,MAAM;AAAA,IAChB,UAAU,MAAM;AAAA,IAChB,UAAU,MAAM,OAAO;AAAA,IACvB,eAAe,UAAU,iBAAiB;AAAA,IAC1C,aAAa,OAAO;AAAA,IACpB,KAAK,uBAAuB,YAAY;AAAA,IACxC,iBAAiB;AAAA,MACf,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,UAAU,MAAM;AAAA,IAClB;AAAA,EACF,CAAC;AAED,QAAM,GAAG,QAAQ,UAAU;AAC3B,QAAM,MAAM,GAAG,MAAM;AAErB,SAAO;AACT;AAEA,eAAsB,0BACpB,YACiB;AACjB,QAAM,kBAAkB,WAAW,iBAAiB;AACpD,QAAM,eAAe;AAAA,IACnB,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACA,MAAI;AACF,WAAO,MAAM,GAAG,SAAS,YAAY;AAAA,EACvC,SAAS,OAAO;AACd,QAAI,OAAO,oBAAoB,YAAY,gBAAgB,SAAS,GAAG;AACrE,aAAO,OAAO,KAAK,iBAAiB,QAAQ;AAAA,IAC9C;AACA,UAAM;AAAA,EACR;AACF;AAEA,eAAsB,gCACpB,YACmB;AACnB,QAAM,kBAAkB,WAAW,iBAAiB;AAEpD,MAAI;AACF,UAAM,eAAe;AAAA,MACnB,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AACA,UAAM,GAAG,OAAO,YAAY;AAC5B,WAAO,iBAAiB,YAAY;AAAA,EACtC,SAAS,OAAO;AACd,QAAI,OAAO,oBAAoB,YAAY,gBAAgB,SAAS,GAAG;AACrE,aAAO,SAAS,KAAK,CAAC,OAAO,KAAK,iBAAiB,QAAQ,CAAC,CAAC;AAAA,IAC/D;AACA,UAAM;AAAA,EACR;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const id = "id";
|
|
2
|
+
export const operation_key = "operation_key";
|
|
3
|
+
export const provider_key = "provider_key";
|
|
4
|
+
export const claim_token = "claim_token";
|
|
5
|
+
export const claimed_at = "claimed_at";
|
|
6
|
+
export const gateway_transaction_id = "gateway_transaction_id";
|
|
7
|
+
export const organization_id = "organization_id";
|
|
8
|
+
export const tenant_id = "tenant_id";
|
|
9
|
+
export const created_at = "created_at";
|
|
10
|
+
export const updated_at = "updated_at";
|
|
@@ -267,6 +267,7 @@ export const M = {
|
|
|
267
267
|
},
|
|
268
268
|
"payment_gateways": {
|
|
269
269
|
"gateway_transaction": "payment_gateways:gateway_transaction",
|
|
270
|
+
"gateway_session_initialization": "payment_gateways:gateway_session_initialization",
|
|
270
271
|
"webhook_processed_event": "payment_gateways:webhook_processed_event"
|
|
271
272
|
},
|
|
272
273
|
"shipping_carriers": {
|
|
@@ -1225,6 +1225,18 @@ export const entityFieldsRegistry: Record<string, Record<string, string>> = {
|
|
|
1225
1225
|
"updated_at": "updated_at",
|
|
1226
1226
|
"value": "value"
|
|
1227
1227
|
},
|
|
1228
|
+
"gateway_session_initialization": {
|
|
1229
|
+
"id": "id",
|
|
1230
|
+
"operation_key": "operation_key",
|
|
1231
|
+
"provider_key": "provider_key",
|
|
1232
|
+
"claim_token": "claim_token",
|
|
1233
|
+
"claimed_at": "claimed_at",
|
|
1234
|
+
"gateway_transaction_id": "gateway_transaction_id",
|
|
1235
|
+
"organization_id": "organization_id",
|
|
1236
|
+
"tenant_id": "tenant_id",
|
|
1237
|
+
"created_at": "created_at",
|
|
1238
|
+
"updated_at": "updated_at"
|
|
1239
|
+
},
|
|
1228
1240
|
"gateway_transaction": {
|
|
1229
1241
|
"id": "id",
|
|
1230
1242
|
"payment_id": "payment_id",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7-develop.6552.1.5208a19369",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -253,16 +253,16 @@
|
|
|
253
253
|
"zod": "^4.4.3"
|
|
254
254
|
},
|
|
255
255
|
"peerDependencies": {
|
|
256
|
-
"@open-mercato/ai-assistant": "0.6.
|
|
257
|
-
"@open-mercato/shared": "0.6.
|
|
258
|
-
"@open-mercato/ui": "0.6.
|
|
256
|
+
"@open-mercato/ai-assistant": "0.6.7-develop.6552.1.5208a19369",
|
|
257
|
+
"@open-mercato/shared": "0.6.7-develop.6552.1.5208a19369",
|
|
258
|
+
"@open-mercato/ui": "0.6.7-develop.6552.1.5208a19369",
|
|
259
259
|
"react": "^19.0.0",
|
|
260
260
|
"react-dom": "^19.0.0"
|
|
261
261
|
},
|
|
262
262
|
"devDependencies": {
|
|
263
|
-
"@open-mercato/ai-assistant": "0.6.
|
|
264
|
-
"@open-mercato/shared": "0.6.
|
|
265
|
-
"@open-mercato/ui": "0.6.
|
|
263
|
+
"@open-mercato/ai-assistant": "0.6.7-develop.6552.1.5208a19369",
|
|
264
|
+
"@open-mercato/shared": "0.6.7-develop.6552.1.5208a19369",
|
|
265
|
+
"@open-mercato/ui": "0.6.7-develop.6552.1.5208a19369",
|
|
266
266
|
"@testing-library/dom": "^10.4.1",
|
|
267
267
|
"@testing-library/jest-dom": "^6.9.1",
|
|
268
268
|
"@testing-library/react": "^16.3.1",
|
|
@@ -285,5 +285,6 @@
|
|
|
285
285
|
"type": "git",
|
|
286
286
|
"url": "https://github.com/open-mercato/open-mercato",
|
|
287
287
|
"directory": "packages/core"
|
|
288
|
-
}
|
|
288
|
+
},
|
|
289
|
+
"stableVersion": "0.6.6"
|
|
289
290
|
}
|
|
@@ -13,6 +13,8 @@ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
|
13
13
|
import { enforceTenantSelection, resolveIsSuperAdmin } from '@open-mercato/core/modules/auth/lib/tenantAccess'
|
|
14
14
|
import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
|
|
15
15
|
import { assertActorCanGrantRoles } from '@open-mercato/core/modules/auth/lib/grantChecks'
|
|
16
|
+
import { isOrganizationAccessAllowed } from '@open-mercato/shared/lib/auth/organizationAccess'
|
|
17
|
+
import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
16
18
|
|
|
17
19
|
type ApiKeyCrudCtx = CrudCtx & {
|
|
18
20
|
__apiKeySecret?: { secret: string; prefix: string }
|
|
@@ -297,10 +299,15 @@ const crud = makeCrudRoute<
|
|
|
297
299
|
|
|
298
300
|
const allowedIds = ctx.organizationScope?.allowedIds ?? null
|
|
299
301
|
const organizationId = input.organizationId ?? ctx.selectedOrganizationId ?? auth.orgId ?? null
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
302
|
+
const isSuperAdmin = await resolveIsSuperAdmin(scopedCtx)
|
|
303
|
+
if (
|
|
304
|
+
!isOrganizationAccessAllowed({
|
|
305
|
+
isSuperAdmin,
|
|
306
|
+
allowedOrganizationIds: allowedIds,
|
|
307
|
+
targetOrganizationId: organizationId,
|
|
308
|
+
})
|
|
309
|
+
) {
|
|
310
|
+
throw json({ error: translate('api_keys.errors.organizationOutOfScope', 'Organization out of scope') }, { status: 403 })
|
|
304
311
|
}
|
|
305
312
|
scopedCtx.__apiKeyOrganizationId = organizationId ?? null
|
|
306
313
|
|
|
@@ -322,18 +329,34 @@ const crud = makeCrudRoute<
|
|
|
322
329
|
const { translate } = await resolveTranslations()
|
|
323
330
|
if (!auth?.tenantId) throw json({ error: translate('api_keys.errors.tenantRequired', 'Tenant context required') }, { status: 400 })
|
|
324
331
|
const em = (ctx.container.resolve('em') as EntityManager)
|
|
325
|
-
const record = await em.findOne(ApiKey, { id, deletedAt: null })
|
|
326
|
-
if (!record) throw json({ error: translate('api_keys.errors.notFound', 'Not found') }, { status: 404 })
|
|
327
332
|
const scopedCtx = ctx as ApiKeyCrudCtx
|
|
328
333
|
const isSuperAdmin = await resolveIsSuperAdmin(scopedCtx)
|
|
329
|
-
if (!isSuperAdmin && record.tenantId && record.tenantId !== auth.tenantId) {
|
|
330
|
-
throw json({ error: translate('api_keys.errors.forbidden', 'Forbidden') }, { status: 403 })
|
|
331
|
-
}
|
|
332
334
|
const allowedIds = ctx.organizationScope?.allowedIds ?? null
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
335
|
+
const recordFilter: FilterQuery<ApiKey> = {
|
|
336
|
+
id,
|
|
337
|
+
tenantId: auth.tenantId,
|
|
338
|
+
deletedAt: null,
|
|
339
|
+
}
|
|
340
|
+
if (!isSuperAdmin && Array.isArray(allowedIds)) {
|
|
341
|
+
recordFilter.organizationId = { $in: allowedIds }
|
|
342
|
+
}
|
|
343
|
+
const record = await findOneWithDecryption(
|
|
344
|
+
em,
|
|
345
|
+
ApiKey,
|
|
346
|
+
recordFilter,
|
|
347
|
+
undefined,
|
|
348
|
+
{ tenantId: auth.tenantId, organizationId: null },
|
|
349
|
+
)
|
|
350
|
+
if (
|
|
351
|
+
!record ||
|
|
352
|
+
record.tenantId !== auth.tenantId ||
|
|
353
|
+
!isOrganizationAccessAllowed({
|
|
354
|
+
isSuperAdmin,
|
|
355
|
+
allowedOrganizationIds: allowedIds,
|
|
356
|
+
targetOrganizationId: record.organizationId ?? null,
|
|
357
|
+
})
|
|
358
|
+
) {
|
|
359
|
+
throw json({ error: translate('api_keys.errors.notFound', 'Not found') }, { status: 404 })
|
|
337
360
|
}
|
|
338
361
|
scopedCtx.__apiKeyOrganizationId = record.organizationId ?? null
|
|
339
362
|
},
|
|
@@ -3,6 +3,7 @@ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
|
3
3
|
import { createApiKey } from './services/apiKeyService'
|
|
4
4
|
import { Organization } from '@open-mercato/core/modules/directory/data/entities'
|
|
5
5
|
import { Role } from '@open-mercato/core/modules/auth/data/entities'
|
|
6
|
+
import { parseCommaSeparatedList } from '@open-mercato/shared/lib/string'
|
|
6
7
|
|
|
7
8
|
function parseArgs(rest: string[]): Record<string, string> {
|
|
8
9
|
const args: Record<string, string> = {}
|
|
@@ -47,9 +48,7 @@ const addApiKey: ModuleCli = {
|
|
|
47
48
|
return
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
const roleInputs = rolesCsv
|
|
51
|
-
? rolesCsv.split(',').map((value) => value.trim()).filter(Boolean)
|
|
52
|
-
: []
|
|
51
|
+
const roleInputs = parseCommaSeparatedList(rolesCsv)
|
|
53
52
|
const roleIds: string[] = []
|
|
54
53
|
if (roleInputs.length) {
|
|
55
54
|
for (const token of roleInputs) {
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
canRenderInlineAttachment,
|
|
16
16
|
} from "@open-mercato/core/modules/attachments/lib/security";
|
|
17
17
|
import { StorageDriverFactory } from '../../../lib/drivers';
|
|
18
|
+
import { resolveAttachmentOrganizationId } from '@open-mercato/core/modules/attachments/lib/requestScope';
|
|
18
19
|
|
|
19
20
|
export const metadata = {
|
|
20
21
|
GET: { requireAuth: false },
|
|
@@ -32,16 +33,19 @@ export async function GET(
|
|
|
32
33
|
);
|
|
33
34
|
}
|
|
34
35
|
const auth = await getAuthFromRequest(req);
|
|
35
|
-
const
|
|
36
|
-
const em = resolve("em") as EntityManager;
|
|
36
|
+
const container = await createRequestContainer();
|
|
37
|
+
const em = container.resolve("em") as EntityManager;
|
|
37
38
|
const storageDriverFactory =
|
|
38
|
-
(resolve("storageDriverFactory") as StorageDriverFactory | null) ??
|
|
39
|
+
(container.resolve("storageDriverFactory") as StorageDriverFactory | null) ??
|
|
39
40
|
new StorageDriverFactory(em);
|
|
40
41
|
|
|
42
|
+
const scopedAuth = auth
|
|
43
|
+
? { ...auth, orgId: await resolveAttachmentOrganizationId(container, auth, req) }
|
|
44
|
+
: auth;
|
|
41
45
|
const findFilter: Record<string, unknown> = { id };
|
|
42
|
-
if (
|
|
43
|
-
if (
|
|
44
|
-
if (
|
|
46
|
+
if (scopedAuth && !isSuperAdminAuth(scopedAuth)) {
|
|
47
|
+
if (scopedAuth.tenantId) findFilter.tenantId = scopedAuth.tenantId;
|
|
48
|
+
if (scopedAuth.orgId) findFilter.organizationId = scopedAuth.orgId;
|
|
45
49
|
}
|
|
46
50
|
const attachment = await em.findOne(Attachment, findFilter);
|
|
47
51
|
if (!attachment) {
|
|
@@ -60,7 +64,7 @@ export async function GET(
|
|
|
60
64
|
);
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
const access = checkAttachmentAccess(
|
|
67
|
+
const access = checkAttachmentAccess(scopedAuth, attachment, partition);
|
|
64
68
|
if (!access.ok) {
|
|
65
69
|
const message = access.status === 401 ? "Unauthorized" : "Forbidden";
|
|
66
70
|
return NextResponse.json({ error: message }, { status: access.status });
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
validateImageMagicBytes,
|
|
21
21
|
} from '@open-mercato/core/modules/attachments/lib/imageSafety'
|
|
22
22
|
import { StorageDriverFactory } from '../../../../lib/drivers'
|
|
23
|
+
import { resolveAttachmentOrganizationId } from '@open-mercato/core/modules/attachments/lib/requestScope'
|
|
23
24
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
24
25
|
|
|
25
26
|
const logger = createLogger('attachments').child({ component: 'image' })
|
|
@@ -51,15 +52,18 @@ export async function GET(
|
|
|
51
52
|
}
|
|
52
53
|
const { width, height, cropType } = parsedQuery.data
|
|
53
54
|
|
|
54
|
-
const
|
|
55
|
-
const em = resolve('em') as EntityManager
|
|
55
|
+
const container = await createRequestContainer()
|
|
56
|
+
const em = container.resolve('em') as EntityManager
|
|
56
57
|
const storageDriverFactory =
|
|
57
|
-
(resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
|
|
58
|
+
(container.resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
|
|
58
59
|
|
|
60
|
+
const scopedAuth = auth
|
|
61
|
+
? { ...auth, orgId: await resolveAttachmentOrganizationId(container, auth, req) }
|
|
62
|
+
: auth
|
|
59
63
|
const findFilter: Record<string, unknown> = { id }
|
|
60
|
-
if (
|
|
61
|
-
if (
|
|
62
|
-
if (
|
|
64
|
+
if (scopedAuth && !isSuperAdminAuth(scopedAuth)) {
|
|
65
|
+
if (scopedAuth.tenantId) findFilter.tenantId = scopedAuth.tenantId
|
|
66
|
+
if (scopedAuth.orgId) findFilter.organizationId = scopedAuth.orgId
|
|
63
67
|
}
|
|
64
68
|
const attachment = await em.findOne(Attachment, findFilter)
|
|
65
69
|
if (!attachment) {
|
|
@@ -72,7 +76,7 @@ export async function GET(
|
|
|
72
76
|
if (!partition) {
|
|
73
77
|
return NextResponse.json({ error: 'Partition misconfigured' }, { status: 500 })
|
|
74
78
|
}
|
|
75
|
-
const access = checkAttachmentAccess(
|
|
79
|
+
const access = checkAttachmentAccess(scopedAuth, attachment, partition)
|
|
76
80
|
if (!access.ok) {
|
|
77
81
|
const message = access.status === 401 ? 'Unauthorized' : 'Forbidden'
|
|
78
82
|
return NextResponse.json({ error: message }, { status: access.status })
|