@open-mercato/core 0.6.6-develop.6205.1.109e4b6a84 → 0.6.6-develop.6227.1.2695efff30
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/currencies/backend/currencies/[id]/page.js +33 -8
- package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +63 -27
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +35 -14
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js +104 -41
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js +57 -8
- package/dist/modules/customer_accounts/api/admin/roles/[id]/acl.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +5 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +30 -14
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +29 -19
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +166 -64
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
- package/dist/modules/customers/components/AddressFormatSettings.js +31 -16
- package/dist/modules/customers/components/AddressFormatSettings.js.map +2 -2
- package/dist/modules/customers/components/DictionarySettings.js +57 -31
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/PipelineSettings.js +156 -76
- package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js +10 -4
- package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +18 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
- package/dist/modules/customers/components/calendar/MonthGrid.js +5 -2
- package/dist/modules/customers/components/calendar/MonthGrid.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +6 -0
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/RelatedToField.js +2 -0
- package/dist/modules/customers/components/calendar/editor/RelatedToField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js +4 -1
- package/dist/modules/customers/components/calendar/editor/ScheduleSection.js.map +2 -2
- package/dist/modules/customers/lib/calendar/labels.js +33 -0
- package/dist/modules/customers/lib/calendar/labels.js.map +7 -0
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +32 -11
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/[id]/route.js +59 -0
- package/dist/modules/data_sync/api/mappings/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/mappings/route.js +44 -0
- package/dist/modules/data_sync/api/mappings/route.js.map +2 -2
- package/dist/modules/data_sync/api/run.js +31 -0
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +31 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/retry.js +31 -0
- package/dist/modules/data_sync/api/runs/[id]/retry.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +59 -3
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/route.js +33 -4
- package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +54 -2
- package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
- package/dist/modules/directory/backend/directory/branding/page.js +15 -9
- package/dist/modules/directory/backend/directory/branding/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js +28 -9
- package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js +29 -13
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/entities/api/definitions.batch.js +15 -0
- package/dist/modules/entities/api/definitions.batch.js.map +2 -2
- package/dist/modules/entities/api/definitions.js +26 -0
- package/dist/modules/entities/api/definitions.js.map +2 -2
- package/dist/modules/entities/api/definitions.mutation-guard.js +57 -0
- package/dist/modules/entities/api/definitions.mutation-guard.js.map +7 -0
- package/dist/modules/entities/api/definitions.restore.js +15 -0
- package/dist/modules/entities/api/definitions.restore.js.map +2 -2
- package/dist/modules/entities/api/entities.js +31 -3
- package/dist/modules/entities/api/entities.js.map +2 -2
- package/dist/modules/entities/api/records.js +18 -0
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +28 -10
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +38 -1
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/emails/[id]/route.js +30 -0
- package/dist/modules/inbox_ops/api/emails/[id]/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/accept-all/route.js +30 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/accept-all/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js +31 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/categorize/route.js +31 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/categorize/route.js.map +2 -2
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js +68 -39
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +14 -4
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
- package/dist/modules/payment_gateways/api/cancel/route.js +37 -0
- package/dist/modules/payment_gateways/api/cancel/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/capture/route.js +37 -0
- package/dist/modules/payment_gateways/api/capture/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/guards.js +31 -0
- package/dist/modules/payment_gateways/api/guards.js.map +7 -0
- package/dist/modules/payment_gateways/api/refund/route.js +37 -0
- package/dist/modules/payment_gateways/api/refund/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/sessions/route.js +37 -0
- package/dist/modules/payment_gateways/api/sessions/route.js.map +2 -2
- package/dist/modules/planner/api/availability-date-specific.js +11 -1
- package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +20 -3
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/planner/commands/availability-date-specific.js +16 -0
- package/dist/modules/planner/commands/availability-date-specific.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +109 -40
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/query_index/api/purge.js +35 -3
- package/dist/modules/query_index/api/purge.js.map +2 -2
- package/dist/modules/query_index/api/reindex.js +41 -3
- package/dist/modules/query_index/api/reindex.js.map +2 -2
- package/dist/modules/query_index/components/QueryIndexesTable.js +57 -24
- package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
- package/dist/modules/shipping_carriers/api/shipments/route.js +31 -0
- package/dist/modules/shipping_carriers/api/shipments/route.js.map +2 -2
- package/dist/modules/shipping_carriers/api/tracking/refresh/route.js +55 -0
- package/dist/modules/shipping_carriers/api/tracking/refresh/route.js.map +7 -0
- package/dist/modules/shipping_carriers/data/validators.js +6 -1
- package/dist/modules/shipping_carriers/data/validators.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js +28 -8
- package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipping-service.js +37 -7
- package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
- package/dist/modules/shipping_carriers/workers/status-poller.js +1 -1
- package/dist/modules/shipping_carriers/workers/status-poller.js.map +2 -2
- package/dist/modules/translations/components/TranslationManager.js +49 -20
- package/dist/modules/translations/components/TranslationManager.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/currencies/backend/currencies/[id]/page.tsx +40 -10
- package/src/modules/currencies/backend/currencies/page.tsx +68 -29
- package/src/modules/currencies/backend/exchange-rates/page.tsx +40 -15
- package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +110 -41
- package/src/modules/customer_accounts/api/admin/roles/[id]/acl.ts +69 -7
- package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +16 -8
- package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +32 -14
- package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +32 -20
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +171 -64
- package/src/modules/customers/components/AddressFormatSettings.tsx +39 -19
- package/src/modules/customers/components/DictionarySettings.tsx +63 -29
- package/src/modules/customers/components/PipelineSettings.tsx +165 -80
- package/src/modules/customers/components/calendar/AgendaList.tsx +13 -8
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +16 -2
- package/src/modules/customers/components/calendar/MonthGrid.tsx +5 -2
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +6 -0
- package/src/modules/customers/components/calendar/editor/RelatedToField.tsx +2 -0
- package/src/modules/customers/components/calendar/editor/ScheduleSection.tsx +7 -0
- package/src/modules/customers/i18n/de.json +11 -7
- package/src/modules/customers/i18n/en.json +6 -2
- package/src/modules/customers/i18n/es.json +11 -7
- package/src/modules/customers/i18n/pl.json +11 -7
- package/src/modules/customers/lib/calendar/labels.ts +42 -0
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +39 -11
- package/src/modules/data_sync/api/mappings/[id]/route.ts +63 -0
- package/src/modules/data_sync/api/mappings/route.ts +48 -0
- package/src/modules/data_sync/api/run.ts +33 -0
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +34 -0
- package/src/modules/data_sync/api/runs/[id]/retry.ts +33 -0
- package/src/modules/data_sync/api/schedules/[id]/route.ts +64 -2
- package/src/modules/data_sync/api/schedules/route.ts +36 -4
- package/src/modules/directory/api/organization-branding/route.ts +61 -0
- package/src/modules/directory/backend/directory/branding/page.tsx +16 -10
- package/src/modules/directory/backend/directory/organizations/page.tsx +35 -8
- package/src/modules/directory/backend/directory/tenants/page.tsx +37 -13
- package/src/modules/entities/api/definitions.batch.ts +17 -0
- package/src/modules/entities/api/definitions.mutation-guard.ts +80 -0
- package/src/modules/entities/api/definitions.restore.ts +17 -0
- package/src/modules/entities/api/definitions.ts +30 -0
- package/src/modules/entities/api/entities.ts +35 -3
- package/src/modules/entities/api/records.ts +20 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +33 -10
- package/src/modules/feature_toggles/api/overrides/route.ts +44 -1
- package/src/modules/inbox_ops/api/emails/[id]/route.ts +32 -0
- package/src/modules/inbox_ops/api/proposals/[id]/accept-all/route.ts +32 -0
- package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +33 -0
- package/src/modules/inbox_ops/api/proposals/[id]/categorize/route.ts +33 -0
- package/src/modules/messages/components/message-detail/hooks/useMessageDetailsActions.ts +80 -42
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +21 -4
- package/src/modules/payment_gateways/api/cancel/route.ts +39 -0
- package/src/modules/payment_gateways/api/capture/route.ts +39 -0
- package/src/modules/payment_gateways/api/guards.ts +59 -0
- package/src/modules/payment_gateways/api/refund/route.ts +39 -0
- package/src/modules/payment_gateways/api/sessions/route.ts +40 -0
- package/src/modules/planner/api/availability-date-specific.ts +10 -0
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +26 -5
- package/src/modules/planner/commands/availability-date-specific.ts +24 -0
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +122 -41
- package/src/modules/query_index/api/purge.ts +37 -3
- package/src/modules/query_index/api/reindex.ts +43 -3
- package/src/modules/query_index/components/QueryIndexesTable.tsx +66 -24
- package/src/modules/shipping_carriers/api/shipments/route.ts +31 -0
- package/src/modules/shipping_carriers/api/tracking/refresh/route.ts +53 -0
- package/src/modules/shipping_carriers/data/validators.ts +5 -0
- package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.ts +29 -8
- package/src/modules/shipping_carriers/lib/shipping-service.ts +43 -7
- package/src/modules/shipping_carriers/workers/status-poller.ts +1 -1
- package/src/modules/translations/components/TranslationManager.tsx +65 -21
|
@@ -3,6 +3,10 @@ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
|
3
3
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
4
|
import { queryIndexTag, queryIndexErrorSchema, queryIndexOkSchema, queryIndexPurgeRequestSchema } from "./openapi.js";
|
|
5
5
|
import { recordIndexerLog } from "@open-mercato/shared/lib/indexers/status-log";
|
|
6
|
+
import {
|
|
7
|
+
runCrudMutationGuardAfterSuccess,
|
|
8
|
+
validateCrudMutationGuard
|
|
9
|
+
} from "@open-mercato/shared/lib/crud/mutation-guard";
|
|
6
10
|
const metadata = {
|
|
7
11
|
POST: { requireAuth: true, requireFeatures: ["query_index.purge"] }
|
|
8
12
|
};
|
|
@@ -12,13 +16,28 @@ async function POST(req) {
|
|
|
12
16
|
const body = await req.json().catch(() => ({}));
|
|
13
17
|
const entityType = String(body?.entityType || "");
|
|
14
18
|
if (!entityType) return NextResponse.json({ error: "Missing entityType" }, { status: 400 });
|
|
15
|
-
const
|
|
19
|
+
const container = await createRequestContainer();
|
|
16
20
|
let em = null;
|
|
17
21
|
try {
|
|
18
|
-
em = resolve("em");
|
|
22
|
+
em = container.resolve("em");
|
|
19
23
|
} catch {
|
|
20
24
|
}
|
|
21
|
-
const bus = resolve("eventBus");
|
|
25
|
+
const bus = container.resolve("eventBus");
|
|
26
|
+
const guardUserId = typeof auth.sub === "string" ? auth.sub : "";
|
|
27
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
28
|
+
tenantId: auth.tenantId,
|
|
29
|
+
organizationId: auth.orgId,
|
|
30
|
+
userId: guardUserId,
|
|
31
|
+
resourceKind: "query_index",
|
|
32
|
+
resourceId: entityType,
|
|
33
|
+
operation: "custom",
|
|
34
|
+
requestMethod: req.method,
|
|
35
|
+
requestHeaders: req.headers,
|
|
36
|
+
mutationPayload: { entityType }
|
|
37
|
+
});
|
|
38
|
+
if (guardResult && !guardResult.ok) {
|
|
39
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
40
|
+
}
|
|
22
41
|
await recordIndexerLog(
|
|
23
42
|
{ em: em ?? void 0 },
|
|
24
43
|
{
|
|
@@ -47,6 +66,19 @@ async function POST(req) {
|
|
|
47
66
|
organizationId: auth.orgId ?? null
|
|
48
67
|
}
|
|
49
68
|
).catch(() => void 0);
|
|
69
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
70
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
71
|
+
tenantId: auth.tenantId,
|
|
72
|
+
organizationId: auth.orgId,
|
|
73
|
+
userId: guardUserId,
|
|
74
|
+
resourceKind: "query_index",
|
|
75
|
+
resourceId: entityType,
|
|
76
|
+
operation: "custom",
|
|
77
|
+
requestMethod: req.method,
|
|
78
|
+
requestHeaders: req.headers,
|
|
79
|
+
metadata: guardResult.metadata ?? null
|
|
80
|
+
});
|
|
81
|
+
}
|
|
50
82
|
} catch (error) {
|
|
51
83
|
await recordIndexerLog(
|
|
52
84
|
{ em: em ?? void 0 },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/query_index/api/purge.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { queryIndexTag, queryIndexErrorSchema, queryIndexOkSchema, queryIndexPurgeRequestSchema } from './openapi'\nimport { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['query_index.purge'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const body = await req.json().catch(() => ({})) as any\n const entityType = String(body?.entityType || '')\n if (!entityType) return NextResponse.json({ error: 'Missing entityType' }, { status: 400 })\n\n const
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAEvC,SAAS,eAAe,uBAAuB,oBAAoB,oCAAoC;AACvG,SAAS,wBAAwB;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { queryIndexTag, queryIndexErrorSchema, queryIndexOkSchema, queryIndexPurgeRequestSchema } from './openapi'\nimport { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['query_index.purge'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const body = await req.json().catch(() => ({})) as any\n const entityType = String(body?.entityType || '')\n if (!entityType) return NextResponse.json({ error: 'Missing entityType' }, { status: 400 })\n\n const container = await createRequestContainer()\n let em: any | null = null\n try {\n em = container.resolve('em')\n } catch {}\n const bus = container.resolve('eventBus') as any\n\n const guardUserId = typeof auth.sub === 'string' ? auth.sub : ''\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: guardUserId,\n resourceKind: 'query_index',\n resourceId: entityType,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { entityType },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'api:query_index.purge',\n message: `Purge requested for ${entityType}`,\n entityType,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n },\n ).catch(() => undefined)\n try {\n await bus.emitEvent(\n 'query_index.purge',\n { entityType, organizationId: auth.orgId, tenantId: auth.tenantId },\n { persistent: true, deliverInline: false },\n )\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'api:query_index.purge',\n message: `Purge queued for ${entityType}`,\n entityType,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n },\n ).catch(() => undefined)\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: guardUserId,\n resourceKind: 'query_index',\n resourceId: entityType,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n } catch (error) {\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'api:query_index.purge',\n level: 'warn',\n message: `Failed to queue purge for ${entityType}`,\n entityType,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n details: { error: error instanceof Error ? error.message : String(error) },\n },\n ).catch(() => undefined)\n throw error\n }\n return NextResponse.json({ ok: true })\n}\n\nconst queryIndexPurgeDoc: OpenApiMethodDoc = {\n summary: 'Purge query index records',\n description: 'Queues a purge job to remove indexed records for an entity type within the active scope.',\n tags: [queryIndexTag],\n requestBody: {\n contentType: 'application/json',\n schema: queryIndexPurgeRequestSchema,\n description: 'Entity identifier whose index entries should be removed.',\n },\n responses: [\n { status: 200, description: 'Purge job accepted.', schema: queryIndexOkSchema },\n ],\n errors: [\n { status: 400, description: 'Missing entity type', schema: queryIndexErrorSchema },\n { status: 401, description: 'Authentication required', schema: queryIndexErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: queryIndexTag,\n summary: 'Queue a query index purge',\n methods: {\n POST: queryIndexPurgeDoc,\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,6 +4,10 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
4
4
|
import { queryIndexTag, queryIndexErrorSchema, queryIndexOkSchema, queryIndexReindexRequestSchema } from "./openapi.js";
|
|
5
5
|
import { recordIndexerLog } from "@open-mercato/shared/lib/indexers/status-log";
|
|
6
6
|
import { isValidEntityIdShape } from "@open-mercato/shared/lib/query/engine";
|
|
7
|
+
import {
|
|
8
|
+
runCrudMutationGuardAfterSuccess,
|
|
9
|
+
validateCrudMutationGuard
|
|
10
|
+
} from "@open-mercato/shared/lib/crud/mutation-guard";
|
|
7
11
|
const metadata = {
|
|
8
12
|
POST: { requireAuth: true, requireFeatures: ["query_index.reindex"] }
|
|
9
13
|
};
|
|
@@ -25,15 +29,36 @@ async function POST(req) {
|
|
|
25
29
|
if (partitionIndex !== void 0 && partitionIndex >= partitionCount) {
|
|
26
30
|
return NextResponse.json({ error: "partitionIndex must be < partitionCount" }, { status: 400 });
|
|
27
31
|
}
|
|
28
|
-
const
|
|
32
|
+
const container = await createRequestContainer();
|
|
29
33
|
let em = null;
|
|
30
34
|
try {
|
|
31
|
-
em = resolve("em");
|
|
35
|
+
em = container.resolve("em");
|
|
32
36
|
} catch {
|
|
33
37
|
}
|
|
34
|
-
const bus = resolve("eventBus");
|
|
38
|
+
const bus = container.resolve("eventBus");
|
|
35
39
|
const partitions = partitionIndex !== void 0 ? [partitionIndex] : Array.from({ length: partitionCount }, (_, idx) => idx);
|
|
36
40
|
const firstPartition = partitions[0] ?? 0;
|
|
41
|
+
const guardUserId = typeof auth.sub === "string" ? auth.sub : "";
|
|
42
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
43
|
+
tenantId: auth.tenantId,
|
|
44
|
+
organizationId: auth.orgId,
|
|
45
|
+
userId: guardUserId,
|
|
46
|
+
resourceKind: "query_index",
|
|
47
|
+
resourceId: entityType,
|
|
48
|
+
operation: "custom",
|
|
49
|
+
requestMethod: req.method,
|
|
50
|
+
requestHeaders: req.headers,
|
|
51
|
+
mutationPayload: {
|
|
52
|
+
entityType,
|
|
53
|
+
force,
|
|
54
|
+
batchSize: batchSize ?? null,
|
|
55
|
+
partitionCount,
|
|
56
|
+
partitionIndex: partitionIndex ?? null
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
if (guardResult && !guardResult.ok) {
|
|
60
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
61
|
+
}
|
|
37
62
|
await recordIndexerLog(
|
|
38
63
|
{ em: em ?? void 0 },
|
|
39
64
|
{
|
|
@@ -95,6 +120,19 @@ async function POST(req) {
|
|
|
95
120
|
}
|
|
96
121
|
}
|
|
97
122
|
).catch(() => void 0);
|
|
123
|
+
if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {
|
|
124
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
125
|
+
tenantId: auth.tenantId,
|
|
126
|
+
organizationId: auth.orgId,
|
|
127
|
+
userId: guardUserId,
|
|
128
|
+
resourceKind: "query_index",
|
|
129
|
+
resourceId: entityType,
|
|
130
|
+
operation: "custom",
|
|
131
|
+
requestMethod: req.method,
|
|
132
|
+
requestHeaders: req.headers,
|
|
133
|
+
metadata: guardResult.metadata ?? null
|
|
134
|
+
});
|
|
135
|
+
}
|
|
98
136
|
} catch (error) {
|
|
99
137
|
await recordIndexerLog(
|
|
100
138
|
{ em: em ?? void 0 },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/query_index/api/reindex.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { queryIndexTag, queryIndexErrorSchema, queryIndexOkSchema, queryIndexReindexRequestSchema } from './openapi'\nimport { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'\nimport { isValidEntityIdShape } from '@open-mercato/shared/lib/query/engine'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['query_index.reindex'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const body = await req.json().catch(() => ({})) as any\n const entityType = String(body?.entityType || '')\n if (!entityType) return NextResponse.json({ error: 'Missing entityType' }, { status: 400 })\n if (!isValidEntityIdShape(entityType)) {\n return NextResponse.json({ error: 'Invalid entityType' }, { status: 400 })\n }\n const force = Boolean(body?.force)\n const batchSize = Number.isFinite(body?.batchSize) ? Math.max(1, Math.trunc(body.batchSize)) : undefined\n const partitionCountInput = Number(body?.partitionCount)\n const partitionCount = Number.isFinite(partitionCountInput)\n ? Math.max(1, Math.trunc(partitionCountInput))\n : 1\n const partitionIndexInput = Number(body?.partitionIndex)\n const partitionIndex = Number.isFinite(partitionIndexInput) ? Math.max(0, Math.trunc(partitionIndexInput)) : undefined\n if (partitionIndex !== undefined && partitionIndex >= partitionCount) {\n return NextResponse.json({ error: 'partitionIndex must be < partitionCount' }, { status: 400 })\n }\n\n const
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AAEvC,SAAS,eAAe,uBAAuB,oBAAoB,sCAAsC;AACzG,SAAS,wBAAwB;AACjC,SAAS,4BAA4B;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { queryIndexTag, queryIndexErrorSchema, queryIndexOkSchema, queryIndexReindexRequestSchema } from './openapi'\nimport { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'\nimport { isValidEntityIdShape } from '@open-mercato/shared/lib/query/engine'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['query_index.reindex'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const body = await req.json().catch(() => ({})) as any\n const entityType = String(body?.entityType || '')\n if (!entityType) return NextResponse.json({ error: 'Missing entityType' }, { status: 400 })\n if (!isValidEntityIdShape(entityType)) {\n return NextResponse.json({ error: 'Invalid entityType' }, { status: 400 })\n }\n const force = Boolean(body?.force)\n const batchSize = Number.isFinite(body?.batchSize) ? Math.max(1, Math.trunc(body.batchSize)) : undefined\n const partitionCountInput = Number(body?.partitionCount)\n const partitionCount = Number.isFinite(partitionCountInput)\n ? Math.max(1, Math.trunc(partitionCountInput))\n : 1\n const partitionIndexInput = Number(body?.partitionIndex)\n const partitionIndex = Number.isFinite(partitionIndexInput) ? Math.max(0, Math.trunc(partitionIndexInput)) : undefined\n if (partitionIndex !== undefined && partitionIndex >= partitionCount) {\n return NextResponse.json({ error: 'partitionIndex must be < partitionCount' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n let em: any | null = null\n try {\n em = container.resolve('em')\n } catch {}\n const bus = container.resolve('eventBus') as any\n const partitions = partitionIndex !== undefined\n ? [partitionIndex]\n : Array.from({ length: partitionCount }, (_, idx) => idx)\n const firstPartition = partitions[0] ?? 0\n\n const guardUserId = typeof auth.sub === 'string' ? auth.sub : ''\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: guardUserId,\n resourceKind: 'query_index',\n resourceId: entityType,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: {\n entityType,\n force,\n batchSize: batchSize ?? null,\n partitionCount,\n partitionIndex: partitionIndex ?? null,\n },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'api:query_index.reindex',\n message: `Reindex requested for ${entityType}`,\n entityType,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n details: {\n force,\n batchSize: batchSize ?? null,\n partitionCount,\n partitionIndex: partitionIndex ?? null,\n },\n },\n ).catch(() => undefined)\n try {\n await Promise.all(\n partitions.map((part) => {\n const payload: Record<string, unknown> = {\n entityType,\n force,\n batchSize,\n partitionCount,\n partitionIndex: part,\n resetCoverage: part === firstPartition,\n }\n if (auth.tenantId !== undefined) {\n payload.tenantId = auth.tenantId ?? null\n }\n if (auth.orgId !== undefined) {\n payload.organizationId = auth.orgId ?? null\n }\n if (typeof auth.sub === 'string' && auth.sub.length > 0) {\n payload.requestedByUserId = auth.sub\n }\n return bus.emitEvent(\n 'query_index.reindex',\n payload,\n { persistent: true, deliverInline: false },\n )\n }),\n )\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'api:query_index.reindex',\n message: `Reindex queued for ${entityType}`,\n entityType,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n details: {\n force,\n batchSize: batchSize ?? null,\n partitionCount,\n partitionIndex: partitionIndex ?? null,\n },\n },\n ).catch(() => undefined)\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: guardUserId,\n resourceKind: 'query_index',\n resourceId: entityType,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n } catch (error) {\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'api:query_index.reindex',\n level: 'warn',\n message: `Failed to queue reindex for ${entityType}`,\n entityType,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n details: {\n error: error instanceof Error ? error.message : String(error),\n },\n },\n ).catch(() => undefined)\n throw error\n }\n return NextResponse.json({ ok: true })\n}\n\nconst queryIndexReindexDoc: OpenApiMethodDoc = {\n summary: 'Trigger query index rebuild',\n description: 'Queues a reindex job for the specified entity type within the current tenant scope.',\n tags: [queryIndexTag],\n requestBody: {\n contentType: 'application/json',\n schema: queryIndexReindexRequestSchema,\n description: 'Entity identifier and optional force flag.',\n },\n responses: [\n { status: 200, description: 'Reindex job accepted.', schema: queryIndexOkSchema },\n ],\n errors: [\n { status: 400, description: 'Missing entity type', schema: queryIndexErrorSchema },\n { status: 401, description: 'Authentication required', schema: queryIndexErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: queryIndexTag,\n summary: 'Queue a query index rebuild',\n methods: {\n POST: queryIndexReindexDoc,\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -10,6 +10,8 @@ import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
|
10
10
|
import { useOrganizationScopeVersion } from "@open-mercato/shared/lib/frontend/useOrganizationScope";
|
|
11
11
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
12
12
|
import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
|
|
13
|
+
import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
|
|
14
|
+
const MUTATION_CONTEXT_ID = "query_index.status.list:actions";
|
|
13
15
|
function formatCount(value) {
|
|
14
16
|
if (value == null) return "\u2014";
|
|
15
17
|
return value.toLocaleString();
|
|
@@ -139,6 +141,10 @@ function QueryIndexesTable() {
|
|
|
139
141
|
const t = useT();
|
|
140
142
|
const { confirm, ConfirmDialogElement } = useConfirmDialog();
|
|
141
143
|
const columns = React.useMemo(() => createColumns(t), [t]);
|
|
144
|
+
const { runMutation, retryLastMutation } = useGuardedMutation({
|
|
145
|
+
contextId: MUTATION_CONTEXT_ID,
|
|
146
|
+
blockedMessage: t("ui.forms.flash.saveBlocked", "Save blocked by validation")
|
|
147
|
+
});
|
|
142
148
|
const { data, isLoading } = useQuery({
|
|
143
149
|
queryKey: ["query-index-status", scopeVersion, refreshSeq],
|
|
144
150
|
queryFn: async () => {
|
|
@@ -165,11 +171,20 @@ function QueryIndexesTable() {
|
|
|
165
171
|
const actionLabel = action === "purge" ? t("query_index.table.actions.purge") : t("query_index.table.actions.reindex");
|
|
166
172
|
const errorMessage = t("query_index.table.errors.actionFailed", { action: actionLabel });
|
|
167
173
|
try {
|
|
168
|
-
await
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
await runMutation({
|
|
175
|
+
operation: () => apiCallOrThrow(`/api/query_index/${action}`, {
|
|
176
|
+
method: "POST",
|
|
177
|
+
headers: { "content-type": "application/json" },
|
|
178
|
+
body: JSON.stringify(body)
|
|
179
|
+
}, { errorMessage }),
|
|
180
|
+
context: {
|
|
181
|
+
formId: MUTATION_CONTEXT_ID,
|
|
182
|
+
resourceKind: "query_index",
|
|
183
|
+
resourceId: entityId,
|
|
184
|
+
retryLastMutation
|
|
185
|
+
},
|
|
186
|
+
mutationPayload: { action, entityType: entityId, force: Boolean(opts?.force) }
|
|
187
|
+
});
|
|
173
188
|
} catch (err) {
|
|
174
189
|
const message = err instanceof Error && err.message ? err.message : errorMessage;
|
|
175
190
|
console.warn("[query_index] index action failed", message);
|
|
@@ -177,7 +192,7 @@ function QueryIndexesTable() {
|
|
|
177
192
|
}
|
|
178
193
|
qc.invalidateQueries({ queryKey: ["query-index-status"] });
|
|
179
194
|
},
|
|
180
|
-
[qc, t]
|
|
195
|
+
[qc, t, runMutation, retryLastMutation]
|
|
181
196
|
);
|
|
182
197
|
const triggerVector = React.useCallback(
|
|
183
198
|
async (action, entityId) => {
|
|
@@ -191,14 +206,23 @@ function QueryIndexesTable() {
|
|
|
191
206
|
const actionLabel = action === "purge" ? t("query_index.table.actions.vectorPurge") : t("query_index.table.actions.vectorReindex");
|
|
192
207
|
const errorMessage = t("query_index.table.errors.actionFailed", { action: actionLabel });
|
|
193
208
|
try {
|
|
194
|
-
await
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
209
|
+
await runMutation({
|
|
210
|
+
operation: () => apiCallOrThrow("/api/search/embeddings/reindex", {
|
|
211
|
+
method: "POST",
|
|
212
|
+
headers: { "content-type": "application/json" },
|
|
213
|
+
body: JSON.stringify({
|
|
214
|
+
entityId,
|
|
215
|
+
purgeFirst: action === "purge"
|
|
216
|
+
})
|
|
217
|
+
}, { errorMessage }),
|
|
218
|
+
context: {
|
|
219
|
+
formId: MUTATION_CONTEXT_ID,
|
|
220
|
+
resourceKind: "query_index.vector",
|
|
221
|
+
resourceId: entityId,
|
|
222
|
+
retryLastMutation
|
|
223
|
+
},
|
|
224
|
+
mutationPayload: { action, entityId, purgeFirst: action === "purge" }
|
|
225
|
+
});
|
|
202
226
|
} catch (err) {
|
|
203
227
|
const message = err instanceof Error && err.message ? err.message : errorMessage;
|
|
204
228
|
console.warn("[query_index] vector action failed", message);
|
|
@@ -206,7 +230,7 @@ function QueryIndexesTable() {
|
|
|
206
230
|
}
|
|
207
231
|
qc.invalidateQueries({ queryKey: ["query-index-status"] });
|
|
208
232
|
},
|
|
209
|
-
[confirm, qc, t]
|
|
233
|
+
[confirm, qc, t, runMutation, retryLastMutation]
|
|
210
234
|
);
|
|
211
235
|
const triggerFulltext = React.useCallback(
|
|
212
236
|
async (action, entityId) => {
|
|
@@ -220,14 +244,23 @@ function QueryIndexesTable() {
|
|
|
220
244
|
const actionLabel = action === "purge" ? t("query_index.table.actions.fulltextPurge") : t("query_index.table.actions.fulltextReindex");
|
|
221
245
|
const errorMessage = t("query_index.table.errors.actionFailed", { action: actionLabel });
|
|
222
246
|
try {
|
|
223
|
-
await
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
247
|
+
await runMutation({
|
|
248
|
+
operation: () => apiCallOrThrow("/api/search/reindex", {
|
|
249
|
+
method: "POST",
|
|
250
|
+
headers: { "content-type": "application/json" },
|
|
251
|
+
body: JSON.stringify({
|
|
252
|
+
action: action === "purge" ? "clear" : "reindex",
|
|
253
|
+
entityId
|
|
254
|
+
})
|
|
255
|
+
}, { errorMessage }),
|
|
256
|
+
context: {
|
|
257
|
+
formId: MUTATION_CONTEXT_ID,
|
|
258
|
+
resourceKind: "query_index.fulltext",
|
|
259
|
+
resourceId: entityId,
|
|
260
|
+
retryLastMutation
|
|
261
|
+
},
|
|
262
|
+
mutationPayload: { action, entityId }
|
|
263
|
+
});
|
|
231
264
|
} catch (err) {
|
|
232
265
|
const message = err instanceof Error && err.message ? err.message : errorMessage;
|
|
233
266
|
console.warn("[query_index] fulltext action failed", message);
|
|
@@ -235,7 +268,7 @@ function QueryIndexesTable() {
|
|
|
235
268
|
}
|
|
236
269
|
qc.invalidateQueries({ queryKey: ["query-index-status"] });
|
|
237
270
|
},
|
|
238
|
-
[confirm, qc, t]
|
|
271
|
+
[confirm, qc, t, runMutation, retryLastMutation]
|
|
239
272
|
);
|
|
240
273
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
241
274
|
/* @__PURE__ */ jsx(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/query_index/components/QueryIndexesTable.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\n\ntype Translator = (key: string, params?: Record<string, string | number>) => string\n\ntype PartitionStatus = {\n partitionIndex: number | null\n partitionCount: number | null\n status: 'reindexing' | 'purging' | 'stalled' | 'completed'\n processedCount?: number | null\n totalCount?: number | null\n heartbeatAt?: string | null\n startedAt?: string | null\n finishedAt?: string | null\n}\n\ntype JobStatus = {\n status: 'idle' | 'reindexing' | 'purging' | 'stalled'\n startedAt?: string | null\n finishedAt?: string | null\n heartbeatAt?: string | null\n processedCount?: number | null\n totalCount?: number | null\n partitions?: PartitionStatus[]\n scope?: {\n status?: 'reindexing' | 'purging' | 'stalled' | 'completed' | null\n processedCount?: number | null\n totalCount?: number | null\n } | null\n}\n\ntype Row = {\n entityId: string\n label: string\n baseCount: number | null\n indexCount: number | null\n vectorCount: number | null\n vectorEnabled: boolean\n fulltextCount: number | null\n fulltextEnabled: boolean\n ok: boolean\n job?: JobStatus\n}\n\ntype Resp = { items: Row[] }\n\nfunction formatCount(value: number | null): string {\n if (value == null) return '\u2014'\n return value.toLocaleString()\n}\n\nfunction formatNumeric(value: number | null | undefined): string | null {\n if (value == null) return null\n return Number(value).toLocaleString()\n}\n\nfunction formatProgressLabel(\n processed: number | null | undefined,\n total: number | null | undefined,\n t: Translator,\n): string | null {\n const processedText = formatNumeric(processed)\n if (!processedText) return null\n const totalText = formatNumeric(total)\n if (totalText) return t('query_index.table.status.progress', { processed: processedText, total: totalText })\n return t('query_index.table.status.progressSingle', { processed: processedText })\n}\n\nfunction translateJobStatus(t: Translator, status: JobStatus['status'] | undefined, ok: boolean): string {\n if (!status || status === 'idle') {\n return ok ? t('query_index.table.status.in_sync') : t('query_index.table.status.out_of_sync')\n }\n if (status === 'reindexing') return t('query_index.table.status.reindexing')\n if (status === 'purging') return t('query_index.table.status.purging')\n if (status === 'stalled') return t('query_index.table.status.stalled')\n return ok ? t('query_index.table.status.in_sync') : t('query_index.table.status.out_of_sync')\n}\n\nfunction translateScopeStatus(\n t: Translator,\n status: PartitionStatus['status'] | JobStatus['status'] | undefined | null,\n): string {\n if (status === 'reindexing') return t('query_index.table.status.scope.reindexing')\n if (status === 'purging') return t('query_index.table.status.scope.purging')\n if (status === 'stalled') return t('query_index.table.status.scope.stalled')\n return t('query_index.table.status.scope.completed')\n}\n\n\nfunction createColumns(t: Translator): ColumnDef<Row>[] {\n return [\n { id: 'entityId', header: () => t('query_index.table.columns.entity'), accessorKey: 'entityId', meta: { priority: 1 } },\n { id: 'label', header: () => t('query_index.table.columns.label'), accessorKey: 'label', meta: { priority: 2 } },\n {\n id: 'baseCount',\n header: () => t('query_index.table.columns.records'),\n accessorFn: (row) => row.baseCount ?? 0,\n cell: ({ row }) => <span>{formatCount(row.original.baseCount)}</span>,\n meta: { priority: 2 },\n },\n {\n id: 'indexCount',\n header: () => t('query_index.table.columns.indexed'),\n accessorFn: (row) => row.indexCount ?? 0,\n cell: ({ row }) => <span>{formatCount(row.original.indexCount)}</span>,\n meta: { priority: 2 },\n },\n {\n id: 'vectorCount',\n header: () => t('query_index.table.columns.vector'),\n accessorFn: (row) => (row.vectorEnabled ? row.vectorCount ?? 0 : -1),\n cell: ({ row }) => {\n const record = row.original\n if (!record.vectorEnabled) return <span>\u2014</span>\n const ok = record.vectorCount != null && record.baseCount != null && record.vectorCount === record.baseCount\n const display = formatCount(record.vectorCount)\n const className = ok ? 'text-green-600' : 'text-orange-600'\n return <span className={className}>{display}</span>\n },\n meta: { priority: 2 },\n },\n {\n id: 'fulltextCount',\n header: () => t('query_index.table.columns.fulltext'),\n accessorFn: (row) => (row.fulltextEnabled ? row.fulltextCount ?? 0 : -1),\n cell: ({ row }) => {\n const record = row.original\n if (!record.fulltextEnabled) return <span>\u2014</span>\n const ok = record.fulltextCount != null && record.baseCount != null && record.fulltextCount === record.baseCount\n const display = formatCount(record.fulltextCount)\n const className = ok ? 'text-green-600' : 'text-orange-600'\n return <span className={className}>{display}</span>\n },\n meta: { priority: 2 },\n },\n {\n id: 'status',\n header: () => t('query_index.table.columns.status'),\n cell: ({ row }) => {\n const record = row.original\n const job = record.job\n const partitions = job?.partitions ?? []\n const ok = record.ok && (!job || job.status === 'idle')\n const statusText = translateJobStatus(t, job?.status, ok)\n const jobProgress = job ? formatProgressLabel(job.processedCount ?? null, job.totalCount ?? null, t) : null\n const label = jobProgress\n ? t('query_index.table.status.withProgress', { status: statusText, progress: jobProgress })\n : statusText\n const className = job\n ? job.status === 'stalled'\n ? 'text-red-600'\n : job.status === 'reindexing' || job.status === 'purging'\n ? 'text-orange-600'\n : ok\n ? 'text-green-600'\n : 'text-muted-foreground'\n : ok\n ? 'text-green-600'\n : 'text-muted-foreground'\n\n const lines: string[] = []\n\n if (job?.scope && partitions.length <= 1) {\n const scopeStatus = translateScopeStatus(t, job.scope.status ?? null)\n const scopeProgress = formatProgressLabel(job.scope.processedCount ?? null, job.scope.totalCount ?? null, t)\n const scopeLabel = t('query_index.table.status.scopeLabel')\n lines.push(`${scopeLabel}: ${scopeStatus}${scopeProgress ? ` (${scopeProgress})` : ''}`)\n }\n\n if (partitions.length > 1) {\n for (const part of partitions) {\n const partitionLabel =\n part.partitionIndex != null\n ? t('query_index.table.status.partitionLabel', { index: Number(part.partitionIndex) + 1 })\n : t('query_index.table.status.scopeLabel')\n const partitionStatus = translateScopeStatus(t, part.status)\n const partitionProgress = formatProgressLabel(part.processedCount ?? null, part.totalCount ?? null, t)\n lines.push(`${partitionLabel}: ${partitionStatus}${partitionProgress ? ` (${partitionProgress})` : ''}`)\n }\n }\n\n if (record.vectorEnabled) {\n const vectorLabel = t('query_index.table.status.vectorLabel')\n const vectorCount = formatCount(record.vectorCount)\n const vectorTotal = record.baseCount != null ? formatCount(record.baseCount) : null\n const vectorValue = vectorTotal\n ? t('query_index.table.status.vectorValue', { count: vectorCount, total: vectorTotal })\n : vectorCount\n lines.push(`${vectorLabel}: ${vectorValue}`)\n }\n\n return (\n <div className=\"space-y-1\">\n <span className={className}>{label}</span>\n {lines.length > 0 && (\n <div className=\"text-xs text-muted-foreground\">\n {lines.map((line, idx) => (\n <div key={idx}>{line}</div>\n ))}\n </div>\n )}\n </div>\n )\n },\n meta: { priority: 1 },\n },\n ]\n}\n\nexport default function QueryIndexesTable() {\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'entityId', desc: false }])\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const qc = useQueryClient()\n const scopeVersion = useOrganizationScopeVersion()\n const [refreshSeq, setRefreshSeq] = React.useState(0)\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const columns = React.useMemo(() => createColumns(t), [t])\n\n const { data, isLoading } = useQuery<Resp>({\n queryKey: ['query-index-status', scopeVersion, refreshSeq],\n queryFn: async () => {\n const baseUrl = '/api/query_index/status'\n const url = refreshSeq > 0 ? `${baseUrl}?refresh=${refreshSeq}` : baseUrl\n return readApiResultOrThrow<Resp>(\n url,\n undefined,\n { errorMessage: t('query_index.table.errors.loadFailed') },\n )\n },\n refetchInterval: 4000,\n })\n\n const rowsAll = data?.items || []\n const rows = React.useMemo(() => {\n if (!search) return rowsAll\n const q = search.toLowerCase()\n return rowsAll.filter((r) => r.entityId.toLowerCase().includes(q) || r.label.toLowerCase().includes(q))\n }, [rowsAll, search])\n\n const trigger = React.useCallback(\n async (action: 'reindex' | 'purge', entityId: string, opts?: { force?: boolean }) => {\n const body: Record<string, unknown> = { entityType: entityId }\n if (opts?.force) body.force = true\n const actionLabel =\n action === 'purge' ? t('query_index.table.actions.purge') : t('query_index.table.actions.reindex')\n const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })\n try {\n await apiCallOrThrow(`/api/query_index/${action}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n }, { errorMessage })\n } catch (err) {\n // Expected operational failures (e.g. a 503 when a search backend is not\n // configured) \u2014 surface a flash toast, not an alert or an error-level\n // stack dump that reads like an unhandled exception.\n const message = err instanceof Error && err.message ? err.message : errorMessage\n console.warn('[query_index] index action failed', message)\n flash(message, 'error')\n }\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n },\n [qc, t],\n )\n\n const triggerVector = React.useCallback(\n async (action: 'reindex' | 'purge', entityId: string) => {\n if (action === 'purge') {\n const confirmed = await confirm({\n title: t('query_index.table.confirm.vectorPurge'),\n variant: 'destructive',\n })\n if (!confirmed) return\n }\n\n const actionLabel = action === 'purge'\n ? t('query_index.table.actions.vectorPurge')\n : t('query_index.table.actions.vectorReindex')\n const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })\n try {\n await apiCallOrThrow('/api/search/embeddings/reindex', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n entityId,\n purgeFirst: action === 'purge',\n }),\n }, { errorMessage })\n } catch (err) {\n const message = err instanceof Error && err.message ? err.message : errorMessage\n console.warn('[query_index] vector action failed', message)\n flash(message, 'error')\n }\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n },\n [confirm, qc, t],\n )\n\n const triggerFulltext = React.useCallback(\n async (action: 'reindex' | 'purge', entityId: string) => {\n if (action === 'purge') {\n const confirmed = await confirm({\n title: t('query_index.table.confirm.fulltextPurge'),\n variant: 'destructive',\n })\n if (!confirmed) return\n }\n\n const actionLabel = action === 'purge'\n ? t('query_index.table.actions.fulltextPurge')\n : t('query_index.table.actions.fulltextReindex')\n const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })\n try {\n await apiCallOrThrow('/api/search/reindex', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n action: action === 'purge' ? 'clear' : 'reindex',\n entityId,\n }),\n }, { errorMessage })\n } catch (err) {\n const message = err instanceof Error && err.message ? err.message : errorMessage\n console.warn('[query_index] fulltext action failed', message)\n flash(message, 'error')\n }\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n },\n [confirm, qc, t],\n )\n\n return (\n <>\n <DataTable\n title={t('query_index.nav.queryIndexes')}\n actions={(\n <>\n <Button\n variant=\"outline\"\n onClick={() => {\n setRefreshSeq((v) => v + 1)\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n }}\n >\n {t('query_index.table.refresh')}\n </Button>\n </>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n searchPlaceholder={t('query_index.table.searchPlaceholder')}\n onSearchChange={(value) => {\n setSearch(value)\n setPage(1)\n }}\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n perspective={{ tableId: 'query_index.status.list' }}\n rowActions={(row) => {\n const items: Array<{ id: string; label: string; onSelect: () => void; destructive?: boolean }> = [\n { id: 'reindex', label: t('query_index.table.actions.reindex'), onSelect: () => void trigger('reindex', row.entityId) },\n {\n id: 'reindex-force',\n label: t('query_index.table.actions.reindexForce'),\n onSelect: () => void trigger('reindex', row.entityId, { force: true }),\n },\n {\n id: 'purge',\n label: t('query_index.table.actions.purge'),\n destructive: true,\n onSelect: () => void trigger('purge', row.entityId),\n },\n ]\n\n if (row.vectorEnabled) {\n items.push(\n {\n id: 'vector-reindex',\n label: t('query_index.table.actions.vectorReindex'),\n onSelect: () => void triggerVector('reindex', row.entityId),\n },\n {\n id: 'vector-purge',\n label: t('query_index.table.actions.vectorPurge'),\n destructive: true,\n onSelect: () => void triggerVector('purge', row.entityId),\n },\n )\n }\n\n if (row.fulltextEnabled) {\n items.push(\n {\n id: 'fulltext-reindex',\n label: t('query_index.table.actions.fulltextReindex'),\n onSelect: () => void triggerFulltext('reindex', row.entityId),\n },\n {\n id: 'fulltext-purge',\n label: t('query_index.table.actions.fulltextPurge'),\n destructive: true,\n onSelect: () => void triggerFulltext('purge', row.entityId),\n },\n )\n }\n\n return <RowActions items={items} />\n }}\n pagination={{ page, pageSize: 50, total: rows.length, totalPages: 1, onPageChange: setPage }}\n isLoading={isLoading}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\n\nconst MUTATION_CONTEXT_ID = 'query_index.status.list:actions'\n\ntype Translator = (key: string, params?: Record<string, string | number>) => string\n\ntype PartitionStatus = {\n partitionIndex: number | null\n partitionCount: number | null\n status: 'reindexing' | 'purging' | 'stalled' | 'completed'\n processedCount?: number | null\n totalCount?: number | null\n heartbeatAt?: string | null\n startedAt?: string | null\n finishedAt?: string | null\n}\n\ntype JobStatus = {\n status: 'idle' | 'reindexing' | 'purging' | 'stalled'\n startedAt?: string | null\n finishedAt?: string | null\n heartbeatAt?: string | null\n processedCount?: number | null\n totalCount?: number | null\n partitions?: PartitionStatus[]\n scope?: {\n status?: 'reindexing' | 'purging' | 'stalled' | 'completed' | null\n processedCount?: number | null\n totalCount?: number | null\n } | null\n}\n\ntype Row = {\n entityId: string\n label: string\n baseCount: number | null\n indexCount: number | null\n vectorCount: number | null\n vectorEnabled: boolean\n fulltextCount: number | null\n fulltextEnabled: boolean\n ok: boolean\n job?: JobStatus\n}\n\ntype Resp = { items: Row[] }\n\nfunction formatCount(value: number | null): string {\n if (value == null) return '\u2014'\n return value.toLocaleString()\n}\n\nfunction formatNumeric(value: number | null | undefined): string | null {\n if (value == null) return null\n return Number(value).toLocaleString()\n}\n\nfunction formatProgressLabel(\n processed: number | null | undefined,\n total: number | null | undefined,\n t: Translator,\n): string | null {\n const processedText = formatNumeric(processed)\n if (!processedText) return null\n const totalText = formatNumeric(total)\n if (totalText) return t('query_index.table.status.progress', { processed: processedText, total: totalText })\n return t('query_index.table.status.progressSingle', { processed: processedText })\n}\n\nfunction translateJobStatus(t: Translator, status: JobStatus['status'] | undefined, ok: boolean): string {\n if (!status || status === 'idle') {\n return ok ? t('query_index.table.status.in_sync') : t('query_index.table.status.out_of_sync')\n }\n if (status === 'reindexing') return t('query_index.table.status.reindexing')\n if (status === 'purging') return t('query_index.table.status.purging')\n if (status === 'stalled') return t('query_index.table.status.stalled')\n return ok ? t('query_index.table.status.in_sync') : t('query_index.table.status.out_of_sync')\n}\n\nfunction translateScopeStatus(\n t: Translator,\n status: PartitionStatus['status'] | JobStatus['status'] | undefined | null,\n): string {\n if (status === 'reindexing') return t('query_index.table.status.scope.reindexing')\n if (status === 'purging') return t('query_index.table.status.scope.purging')\n if (status === 'stalled') return t('query_index.table.status.scope.stalled')\n return t('query_index.table.status.scope.completed')\n}\n\n\nfunction createColumns(t: Translator): ColumnDef<Row>[] {\n return [\n { id: 'entityId', header: () => t('query_index.table.columns.entity'), accessorKey: 'entityId', meta: { priority: 1 } },\n { id: 'label', header: () => t('query_index.table.columns.label'), accessorKey: 'label', meta: { priority: 2 } },\n {\n id: 'baseCount',\n header: () => t('query_index.table.columns.records'),\n accessorFn: (row) => row.baseCount ?? 0,\n cell: ({ row }) => <span>{formatCount(row.original.baseCount)}</span>,\n meta: { priority: 2 },\n },\n {\n id: 'indexCount',\n header: () => t('query_index.table.columns.indexed'),\n accessorFn: (row) => row.indexCount ?? 0,\n cell: ({ row }) => <span>{formatCount(row.original.indexCount)}</span>,\n meta: { priority: 2 },\n },\n {\n id: 'vectorCount',\n header: () => t('query_index.table.columns.vector'),\n accessorFn: (row) => (row.vectorEnabled ? row.vectorCount ?? 0 : -1),\n cell: ({ row }) => {\n const record = row.original\n if (!record.vectorEnabled) return <span>\u2014</span>\n const ok = record.vectorCount != null && record.baseCount != null && record.vectorCount === record.baseCount\n const display = formatCount(record.vectorCount)\n const className = ok ? 'text-green-600' : 'text-orange-600'\n return <span className={className}>{display}</span>\n },\n meta: { priority: 2 },\n },\n {\n id: 'fulltextCount',\n header: () => t('query_index.table.columns.fulltext'),\n accessorFn: (row) => (row.fulltextEnabled ? row.fulltextCount ?? 0 : -1),\n cell: ({ row }) => {\n const record = row.original\n if (!record.fulltextEnabled) return <span>\u2014</span>\n const ok = record.fulltextCount != null && record.baseCount != null && record.fulltextCount === record.baseCount\n const display = formatCount(record.fulltextCount)\n const className = ok ? 'text-green-600' : 'text-orange-600'\n return <span className={className}>{display}</span>\n },\n meta: { priority: 2 },\n },\n {\n id: 'status',\n header: () => t('query_index.table.columns.status'),\n cell: ({ row }) => {\n const record = row.original\n const job = record.job\n const partitions = job?.partitions ?? []\n const ok = record.ok && (!job || job.status === 'idle')\n const statusText = translateJobStatus(t, job?.status, ok)\n const jobProgress = job ? formatProgressLabel(job.processedCount ?? null, job.totalCount ?? null, t) : null\n const label = jobProgress\n ? t('query_index.table.status.withProgress', { status: statusText, progress: jobProgress })\n : statusText\n const className = job\n ? job.status === 'stalled'\n ? 'text-red-600'\n : job.status === 'reindexing' || job.status === 'purging'\n ? 'text-orange-600'\n : ok\n ? 'text-green-600'\n : 'text-muted-foreground'\n : ok\n ? 'text-green-600'\n : 'text-muted-foreground'\n\n const lines: string[] = []\n\n if (job?.scope && partitions.length <= 1) {\n const scopeStatus = translateScopeStatus(t, job.scope.status ?? null)\n const scopeProgress = formatProgressLabel(job.scope.processedCount ?? null, job.scope.totalCount ?? null, t)\n const scopeLabel = t('query_index.table.status.scopeLabel')\n lines.push(`${scopeLabel}: ${scopeStatus}${scopeProgress ? ` (${scopeProgress})` : ''}`)\n }\n\n if (partitions.length > 1) {\n for (const part of partitions) {\n const partitionLabel =\n part.partitionIndex != null\n ? t('query_index.table.status.partitionLabel', { index: Number(part.partitionIndex) + 1 })\n : t('query_index.table.status.scopeLabel')\n const partitionStatus = translateScopeStatus(t, part.status)\n const partitionProgress = formatProgressLabel(part.processedCount ?? null, part.totalCount ?? null, t)\n lines.push(`${partitionLabel}: ${partitionStatus}${partitionProgress ? ` (${partitionProgress})` : ''}`)\n }\n }\n\n if (record.vectorEnabled) {\n const vectorLabel = t('query_index.table.status.vectorLabel')\n const vectorCount = formatCount(record.vectorCount)\n const vectorTotal = record.baseCount != null ? formatCount(record.baseCount) : null\n const vectorValue = vectorTotal\n ? t('query_index.table.status.vectorValue', { count: vectorCount, total: vectorTotal })\n : vectorCount\n lines.push(`${vectorLabel}: ${vectorValue}`)\n }\n\n return (\n <div className=\"space-y-1\">\n <span className={className}>{label}</span>\n {lines.length > 0 && (\n <div className=\"text-xs text-muted-foreground\">\n {lines.map((line, idx) => (\n <div key={idx}>{line}</div>\n ))}\n </div>\n )}\n </div>\n )\n },\n meta: { priority: 1 },\n },\n ]\n}\n\nexport default function QueryIndexesTable() {\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'entityId', desc: false }])\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const qc = useQueryClient()\n const scopeVersion = useOrganizationScopeVersion()\n const [refreshSeq, setRefreshSeq] = React.useState(0)\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const columns = React.useMemo(() => createColumns(t), [t])\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: MUTATION_CONTEXT_ID,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const { data, isLoading } = useQuery<Resp>({\n queryKey: ['query-index-status', scopeVersion, refreshSeq],\n queryFn: async () => {\n const baseUrl = '/api/query_index/status'\n const url = refreshSeq > 0 ? `${baseUrl}?refresh=${refreshSeq}` : baseUrl\n return readApiResultOrThrow<Resp>(\n url,\n undefined,\n { errorMessage: t('query_index.table.errors.loadFailed') },\n )\n },\n refetchInterval: 4000,\n })\n\n const rowsAll = data?.items || []\n const rows = React.useMemo(() => {\n if (!search) return rowsAll\n const q = search.toLowerCase()\n return rowsAll.filter((r) => r.entityId.toLowerCase().includes(q) || r.label.toLowerCase().includes(q))\n }, [rowsAll, search])\n\n const trigger = React.useCallback(\n async (action: 'reindex' | 'purge', entityId: string, opts?: { force?: boolean }) => {\n const body: Record<string, unknown> = { entityType: entityId }\n if (opts?.force) body.force = true\n const actionLabel =\n action === 'purge' ? t('query_index.table.actions.purge') : t('query_index.table.actions.reindex')\n const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })\n try {\n await runMutation({\n operation: () =>\n apiCallOrThrow(`/api/query_index/${action}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n }, { errorMessage }),\n context: {\n formId: MUTATION_CONTEXT_ID,\n resourceKind: 'query_index',\n resourceId: entityId,\n retryLastMutation,\n },\n mutationPayload: { action, entityType: entityId, force: Boolean(opts?.force) },\n })\n } catch (err) {\n // Expected operational failures (e.g. a 503 when a search backend is not\n // configured) \u2014 surface a flash toast, not an alert or an error-level\n // stack dump that reads like an unhandled exception.\n const message = err instanceof Error && err.message ? err.message : errorMessage\n console.warn('[query_index] index action failed', message)\n flash(message, 'error')\n }\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n },\n [qc, t, runMutation, retryLastMutation],\n )\n\n const triggerVector = React.useCallback(\n async (action: 'reindex' | 'purge', entityId: string) => {\n if (action === 'purge') {\n const confirmed = await confirm({\n title: t('query_index.table.confirm.vectorPurge'),\n variant: 'destructive',\n })\n if (!confirmed) return\n }\n\n const actionLabel = action === 'purge'\n ? t('query_index.table.actions.vectorPurge')\n : t('query_index.table.actions.vectorReindex')\n const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })\n try {\n await runMutation({\n operation: () =>\n apiCallOrThrow('/api/search/embeddings/reindex', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n entityId,\n purgeFirst: action === 'purge',\n }),\n }, { errorMessage }),\n context: {\n formId: MUTATION_CONTEXT_ID,\n resourceKind: 'query_index.vector',\n resourceId: entityId,\n retryLastMutation,\n },\n mutationPayload: { action, entityId, purgeFirst: action === 'purge' },\n })\n } catch (err) {\n const message = err instanceof Error && err.message ? err.message : errorMessage\n console.warn('[query_index] vector action failed', message)\n flash(message, 'error')\n }\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n },\n [confirm, qc, t, runMutation, retryLastMutation],\n )\n\n const triggerFulltext = React.useCallback(\n async (action: 'reindex' | 'purge', entityId: string) => {\n if (action === 'purge') {\n const confirmed = await confirm({\n title: t('query_index.table.confirm.fulltextPurge'),\n variant: 'destructive',\n })\n if (!confirmed) return\n }\n\n const actionLabel = action === 'purge'\n ? t('query_index.table.actions.fulltextPurge')\n : t('query_index.table.actions.fulltextReindex')\n const errorMessage = t('query_index.table.errors.actionFailed', { action: actionLabel })\n try {\n await runMutation({\n operation: () =>\n apiCallOrThrow('/api/search/reindex', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n action: action === 'purge' ? 'clear' : 'reindex',\n entityId,\n }),\n }, { errorMessage }),\n context: {\n formId: MUTATION_CONTEXT_ID,\n resourceKind: 'query_index.fulltext',\n resourceId: entityId,\n retryLastMutation,\n },\n mutationPayload: { action, entityId },\n })\n } catch (err) {\n const message = err instanceof Error && err.message ? err.message : errorMessage\n console.warn('[query_index] fulltext action failed', message)\n flash(message, 'error')\n }\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n },\n [confirm, qc, t, runMutation, retryLastMutation],\n )\n\n return (\n <>\n <DataTable\n title={t('query_index.nav.queryIndexes')}\n actions={(\n <>\n <Button\n variant=\"outline\"\n onClick={() => {\n setRefreshSeq((v) => v + 1)\n qc.invalidateQueries({ queryKey: ['query-index-status'] })\n }}\n >\n {t('query_index.table.refresh')}\n </Button>\n </>\n )}\n columns={columns}\n data={rows}\n searchValue={search}\n searchPlaceholder={t('query_index.table.searchPlaceholder')}\n onSearchChange={(value) => {\n setSearch(value)\n setPage(1)\n }}\n sortable\n sorting={sorting}\n onSortingChange={setSorting}\n perspective={{ tableId: 'query_index.status.list' }}\n rowActions={(row) => {\n const items: Array<{ id: string; label: string; onSelect: () => void; destructive?: boolean }> = [\n { id: 'reindex', label: t('query_index.table.actions.reindex'), onSelect: () => void trigger('reindex', row.entityId) },\n {\n id: 'reindex-force',\n label: t('query_index.table.actions.reindexForce'),\n onSelect: () => void trigger('reindex', row.entityId, { force: true }),\n },\n {\n id: 'purge',\n label: t('query_index.table.actions.purge'),\n destructive: true,\n onSelect: () => void trigger('purge', row.entityId),\n },\n ]\n\n if (row.vectorEnabled) {\n items.push(\n {\n id: 'vector-reindex',\n label: t('query_index.table.actions.vectorReindex'),\n onSelect: () => void triggerVector('reindex', row.entityId),\n },\n {\n id: 'vector-purge',\n label: t('query_index.table.actions.vectorPurge'),\n destructive: true,\n onSelect: () => void triggerVector('purge', row.entityId),\n },\n )\n }\n\n if (row.fulltextEnabled) {\n items.push(\n {\n id: 'fulltext-reindex',\n label: t('query_index.table.actions.fulltextReindex'),\n onSelect: () => void triggerFulltext('reindex', row.entityId),\n },\n {\n id: 'fulltext-purge',\n label: t('query_index.table.actions.fulltextPurge'),\n destructive: true,\n onSelect: () => void triggerFulltext('purge', row.entityId),\n },\n )\n }\n\n return <RowActions items={items} />\n }}\n pagination={{ page, pageSize: 50, total: rows.length, totalPages: 1, onPageChange: setPage }}\n isLoading={isLoading}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,6 +2,10 @@ import { NextResponse } from "next/server";
|
|
|
2
2
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
3
3
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
4
4
|
import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
|
|
5
|
+
import {
|
|
6
|
+
runCrudMutationGuardAfterSuccess,
|
|
7
|
+
validateCrudMutationGuard
|
|
8
|
+
} from "@open-mercato/shared/lib/crud/mutation-guard";
|
|
5
9
|
import { createShipmentSchema } from "../../data/validators.js";
|
|
6
10
|
import { shippingCarriersTag } from "../openapi.js";
|
|
7
11
|
const metadata = {
|
|
@@ -20,12 +24,39 @@ async function POST(req) {
|
|
|
20
24
|
}
|
|
21
25
|
const container = await createRequestContainer();
|
|
22
26
|
const service = container.resolve("shippingCarrierService");
|
|
27
|
+
const guardResult = await validateCrudMutationGuard(container, {
|
|
28
|
+
tenantId: auth.tenantId,
|
|
29
|
+
organizationId: auth.orgId,
|
|
30
|
+
userId: auth.sub ?? "",
|
|
31
|
+
resourceKind: "shipping_carriers.shipment",
|
|
32
|
+
resourceId: parsed.data.orderId,
|
|
33
|
+
operation: "create",
|
|
34
|
+
requestMethod: req.method,
|
|
35
|
+
requestHeaders: req.headers,
|
|
36
|
+
mutationPayload: parsed.data
|
|
37
|
+
});
|
|
38
|
+
if (guardResult && !guardResult.ok) {
|
|
39
|
+
return NextResponse.json(guardResult.body, { status: guardResult.status });
|
|
40
|
+
}
|
|
23
41
|
try {
|
|
24
42
|
const shipment = await service.createShipment({
|
|
25
43
|
...parsed.data,
|
|
26
44
|
organizationId: auth.orgId,
|
|
27
45
|
tenantId: auth.tenantId
|
|
28
46
|
});
|
|
47
|
+
if (guardResult?.shouldRunAfterSuccess) {
|
|
48
|
+
await runCrudMutationGuardAfterSuccess(container, {
|
|
49
|
+
tenantId: auth.tenantId,
|
|
50
|
+
organizationId: auth.orgId,
|
|
51
|
+
userId: auth.sub ?? "",
|
|
52
|
+
resourceKind: "shipping_carriers.shipment",
|
|
53
|
+
resourceId: shipment.id,
|
|
54
|
+
operation: "create",
|
|
55
|
+
requestMethod: req.method,
|
|
56
|
+
requestHeaders: req.headers,
|
|
57
|
+
metadata: guardResult.metadata ?? null
|
|
58
|
+
});
|
|
59
|
+
}
|
|
29
60
|
return NextResponse.json({
|
|
30
61
|
shipmentId: shipment.id,
|
|
31
62
|
carrierShipmentId: shipment.carrierShipmentId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/shipping_carriers/api/shipments/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport type { ShippingCarrierService } from '../../lib/shipping-service'\nimport { createShipmentSchema } from '../../data/validators'\nimport { shippingCarriersTag } from '../openapi'\n\nexport const metadata = {\n path: '/shipping-carriers/shipments',\n POST: { requireAuth: true, requireFeatures: ['shipping_carriers.manage'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const payload = await readJsonSafe<unknown>(req)\n const parsed = createShipmentSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })\n }\n const container = await createRequestContainer()\n const service = container.resolve('shippingCarrierService') as ShippingCarrierService\n try {\n const shipment = await service.createShipment({\n ...parsed.data,\n organizationId: auth.orgId as string,\n tenantId: auth.tenantId,\n })\n return NextResponse.json({\n shipmentId: shipment.id,\n carrierShipmentId: shipment.carrierShipmentId,\n trackingNumber: shipment.trackingNumber,\n status: shipment.unifiedStatus,\n labelUrl: shipment.labelUrl,\n }, { status: 201 })\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Failed to create shipment'\n return NextResponse.json({ error: message }, { status: 502 })\n }\n}\n\nexport const openApi = {\n tags: [shippingCarriersTag],\n summary: 'Create shipment',\n methods: {\n POST: {\n summary: 'Create shipment',\n tags: [shippingCarriersTag],\n responses: [\n { status: 201, description: 'Shipment created' },\n { status: 422, description: 'Validation failed' },\n { status: 502, description: 'Provider upstream error' },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport type { ShippingCarrierService } from '../../lib/shipping-service'\nimport { createShipmentSchema } from '../../data/validators'\nimport { shippingCarriersTag } from '../openapi'\n\nexport const metadata = {\n path: '/shipping-carriers/shipments',\n POST: { requireAuth: true, requireFeatures: ['shipping_carriers.manage'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const payload = await readJsonSafe<unknown>(req)\n const parsed = createShipmentSchema.safeParse(payload)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Invalid payload', details: parsed.error.flatten() }, { status: 422 })\n }\n const container = await createRequestContainer()\n const service = container.resolve('shippingCarrierService') as ShippingCarrierService\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: auth.sub ?? '',\n resourceKind: 'shipping_carriers.shipment',\n resourceId: parsed.data.orderId,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed.data as unknown as Record<string, unknown>,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n try {\n const shipment = await service.createShipment({\n ...parsed.data,\n organizationId: auth.orgId as string,\n tenantId: auth.tenantId,\n })\n if (guardResult?.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: auth.sub ?? '',\n resourceKind: 'shipping_carriers.shipment',\n resourceId: shipment.id,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n return NextResponse.json({\n shipmentId: shipment.id,\n carrierShipmentId: shipment.carrierShipmentId,\n trackingNumber: shipment.trackingNumber,\n status: shipment.unifiedStatus,\n labelUrl: shipment.labelUrl,\n }, { status: 201 })\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Failed to create shipment'\n return NextResponse.json({ error: message }, { status: 502 })\n }\n}\n\nexport const openApi = {\n tags: [shippingCarriersTag],\n summary: 'Create shipment',\n methods: {\n POST: {\n summary: 'Create shipment',\n tags: [shippingCarriersTag],\n responses: [\n { status: 201, description: 'Shipment created' },\n { status: 422, description: 'Validation failed' },\n { status: 502, description: 'Provider upstream error' },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,SAAS,4BAA4B;AACrC,SAAS,2BAA2B;AAE7B,MAAM,WAAW;AAAA,EACtB,MAAM;AAAA,EACN,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,0BAA0B,EAAE;AAC3E;AAEA,eAAsB,KAAK,KAAc;AACvC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM,YAAY,CAAC,KAAK,OAAO;AAClC,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACrE;AACA,QAAM,UAAU,MAAM,aAAsB,GAAG;AAC/C,QAAM,SAAS,qBAAqB,UAAU,OAAO;AACrD,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO,aAAa,KAAK,EAAE,OAAO,mBAAmB,SAAS,OAAO,MAAM,QAAQ,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzG;AACA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,UAAU,UAAU,QAAQ,wBAAwB;AAC1D,QAAM,cAAc,MAAM,0BAA0B,WAAW;AAAA,IAC7D,UAAU,KAAK;AAAA,IACf,gBAAgB,KAAK;AAAA,IACrB,QAAQ,KAAK,OAAO;AAAA,IACpB,cAAc;AAAA,IACd,YAAY,OAAO,KAAK;AAAA,IACxB,WAAW;AAAA,IACX,eAAe,IAAI;AAAA,IACnB,gBAAgB,IAAI;AAAA,IACpB,iBAAiB,OAAO;AAAA,EAC1B,CAAC;AACD,MAAI,eAAe,CAAC,YAAY,IAAI;AAClC,WAAO,aAAa,KAAK,YAAY,MAAM,EAAE,QAAQ,YAAY,OAAO,CAAC;AAAA,EAC3E;AACA,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,eAAe;AAAA,MAC5C,GAAG,OAAO;AAAA,MACV,gBAAgB,KAAK;AAAA,MACrB,UAAU,KAAK;AAAA,IACjB,CAAC;AACD,QAAI,aAAa,uBAAuB;AACtC,YAAM,iCAAiC,WAAW;AAAA,QAChD,UAAU,KAAK;AAAA,QACf,gBAAgB,KAAK;AAAA,QACrB,QAAQ,KAAK,OAAO;AAAA,QACpB,cAAc;AAAA,QACd,YAAY,SAAS;AAAA,QACrB,WAAW;AAAA,QACX,eAAe,IAAI;AAAA,QACnB,gBAAgB,IAAI;AAAA,QACpB,UAAU,YAAY,YAAY;AAAA,MACpC,CAAC;AAAA,IACH;AACA,WAAO,aAAa,KAAK;AAAA,MACvB,YAAY,SAAS;AAAA,MACrB,mBAAmB,SAAS;AAAA,MAC5B,gBAAgB,SAAS;AAAA,MACzB,QAAQ,SAAS;AAAA,MACjB,UAAU,SAAS;AAAA,IACrB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACpB,SAAS,OAAgB;AACvB,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,WAAO,aAAa,KAAK,EAAE,OAAO,QAAQ,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC9D;AACF;AAEO,MAAM,UAAU;AAAA,EACrB,MAAM,CAAC,mBAAmB;AAAA,EAC1B,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,MAAM,CAAC,mBAAmB;AAAA,MAC1B,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,mBAAmB;AAAA,QAC/C,EAAE,QAAQ,KAAK,aAAa,oBAAoB;AAAA,QAChD,EAAE,QAAQ,KAAK,aAAa,0BAA0B;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|