@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/entities/api/definitions.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomEntity, CustomFieldDef } from '@open-mercato/core/modules/entities/data/entities'\nimport { upsertCustomFieldDefSchema, fieldsetCodeRegex } from '@open-mercato/core/modules/entities/data/validators'\nimport {\n createDefinitionsCacheKey,\n createDefinitionsCacheTags,\n invalidateDefinitionsCache,\n ENTITY_DEFINITIONS_CACHE_TTL_MS,\n} from './definitions.cache'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { filterSelectableSystemEntityIds, isSystemEntitySelectable } from '@open-mercato/shared/lib/entities/system-entities'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { loadEntityFieldsetConfigs, CustomFieldsetDefinition } from '../lib/fieldsets'\nimport { installCustomEntitiesFromModules } from '../lib/install-from-ce'\nimport { normalizeCustomFieldOptions } from '@open-mercato/shared/modules/entities/options'\nimport { CURRENCY_OPTIONS_URL } from '@open-mercato/shared/modules/entities/kinds'\nimport type { EntityManager } from '@mikro-orm/postgresql'\n\n/**\n * Validate defaultValue against the field kind. Returns an error message string\n * if invalid, or null if valid. For dictionary and currency kinds, queries the\n * database to verify the token exists.\n */\nasync function validateDefaultValueByKind(\n value: unknown,\n kind: string,\n cfg: Record<string, unknown>,\n em: EntityManager,\n tenantContext: { tenantId: string | null; organizationId: string | null },\n): Promise<string | null> {\n switch (kind) {\n case 'boolean':\n if (typeof value !== 'boolean') return 'defaultValue for boolean fields must be true or false'\n return null\n case 'integer':\n if (typeof value !== 'number' || !Number.isInteger(value))\n return 'defaultValue for integer fields must be a whole number'\n return null\n case 'float':\n if (typeof value !== 'number' || !isFinite(value))\n return 'defaultValue for float fields must be a finite number'\n return null\n case 'text':\n case 'multiline':\n case 'date':\n case 'datetime':\n if (typeof value !== 'string')\n return `defaultValue for ${kind} fields must be a string`\n return null\n case 'dictionary': {\n if (typeof value !== 'string')\n return 'defaultValue for dictionary fields must be a string'\n const dictionaryId = typeof cfg.dictionaryId === 'string' ? cfg.dictionaryId.trim() : ''\n if (dictionaryId) {\n try {\n const { DictionaryEntry } = await import('@open-mercato/core/modules/dictionaries/data/entities')\n const entry = await em.findOne(DictionaryEntry, {\n dictionary: dictionaryId,\n value: value,\n ...(tenantContext.organizationId ? { organizationId: tenantContext.organizationId } : {}),\n ...(tenantContext.tenantId ? { tenantId: tenantContext.tenantId } : {}),\n })\n if (!entry) return `defaultValue \"${value}\" does not match any entry in the configured dictionary`\n } catch (err) {\n // If the dictionaries module is not available, skip entry validation\n console.debug('[entities.definitions] dictionary validation skipped \u2014 module not available', err)\n }\n }\n return null\n }\n case 'currency': {\n if (typeof value !== 'string')\n return 'defaultValue for currency fields must be a string'\n try {\n const { Currency } = await import('@open-mercato/core/modules/currencies/data/entities')\n const where: Record<string, string> = { code: value as string }\n if (tenantContext.tenantId) where.tenantId = tenantContext.tenantId\n if (tenantContext.organizationId) where.organizationId = tenantContext.organizationId\n const currency = await em.findOne(Currency, where)\n if (!currency) return `defaultValue \"${value}\" does not match any available currency`\n } catch (err) {\n // If the currencies module is not available, skip currency validation\n console.debug('[entities.definitions] currency validation skipped \u2014 module not available', err)\n }\n return null\n }\n case 'select': {\n if (typeof value !== 'string' && typeof value !== 'number')\n return 'defaultValue for select fields must be a string or number'\n const options = normalizeCustomFieldOptions(\n Array.isArray(cfg.options) ? cfg.options : [],\n )\n if (options.length > 0) {\n const match = options.some(\n (o) => o.value === value || String(o.value) === String(value),\n )\n if (!match) return 'defaultValue does not match any of the configured options'\n }\n return null\n }\n case 'relation':\n case 'attachment':\n return `defaultValue is not supported for ${kind} fields`\n default:\n return null\n }\n}\n\n/**\n * Normalize defaultValue to the canonical storage shape for the given kind.\n */\nfunction normalizeDefaultValueByKind(value: unknown, kind: string): unknown {\n switch (kind) {\n case 'boolean':\n return value === true\n case 'integer':\n return Math.round(Number(value))\n case 'float':\n return Number(value)\n default:\n return value\n }\n}\n\nexport const metadata = {\n // Reading definitions is needed by record forms; keep it auth-protected but accessible to all authenticated users\n GET: { requireAuth: true },\n // Mutations remain admin-only\n POST: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n}\n\nfunction parseEntityIds(url: URL): string[] {\n const direct = url.searchParams.getAll('entityId').filter((id) => typeof id === 'string' && id.trim().length > 0)\n const combined = url.searchParams.get('entityIds')\n if (combined) {\n combined\n .split(',')\n .map((id) => id.trim())\n .filter((id) => id.length > 0)\n .forEach((id) => direct.push(id))\n }\n const unique: string[] = []\n const seen = new Set<string>()\n for (const id of direct) {\n if (seen.has(id)) continue\n seen.add(id)\n unique.push(id)\n }\n return unique\n}\n\nasync function resolveEntityDefaultEditor(em: any, entityId: string, tenantId: string | null | undefined): Promise<string | undefined> {\n try {\n const ent = await em.findOne(CustomEntity, {\n entityId,\n $and: [\n { $or: [ { tenantId: tenantId ?? undefined as any }, { tenantId: null } ] },\n ],\n } as any)\n if (ent && typeof (ent as any).defaultEditor === 'string') return (ent as any).defaultEditor\n } catch {}\n return undefined\n}\n\nfunction normalizeFieldGroup(raw: unknown): { code: string; title?: string; hint?: string } | undefined {\n if (!raw) return undefined\n if (typeof raw === 'string') {\n const code = raw.trim()\n return code ? { code } : undefined\n }\n if (typeof raw !== 'object') return undefined\n const entry = raw as Record<string, unknown>\n const code = typeof entry.code === 'string' ? entry.code.trim() : ''\n if (!code) return undefined\n const group: { code: string; title?: string; hint?: string } = { code }\n if (typeof entry.title === 'string' && entry.title.trim()) group.title = entry.title.trim()\n if (typeof entry.hint === 'string' && entry.hint.trim()) group.hint = entry.hint.trim()\n return group\n}\n\nexport async function GET(req: Request) {\n const url = new URL(req.url)\n const requestedEntityIds = parseEntityIds(url)\n if (!requestedEntityIds.length) {\n return NextResponse.json({ error: 'entityId is required' }, { status: 400 })\n }\n const requestedFieldset = url.searchParams.get('fieldset')\n const fieldsetFilter = requestedFieldset && requestedFieldset.trim().length ? requestedFieldset.trim() : null\n\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const entityIds = filterSelectableSystemEntityIds(requestedEntityIds)\n if (!entityIds.length) {\n return NextResponse.json({ items: [] })\n }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope.tenantId ?? auth.tenantId ?? null\n if (!tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const organizationId = scope.selectedId ?? auth.orgId ?? null\n const { resolve } = container\n const em = resolve('em') as any\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n\n let canManageDefinitions = false\n if (typeof auth.sub === 'string' && auth.sub.length > 0) {\n try {\n const rbac = resolve('rbacService') as RbacService\n canManageDefinitions = await rbac.userHasAllFeatures(auth.sub, ['entities.definitions.manage'], {\n tenantId,\n organizationId,\n })\n } catch {\n // RBAC service unavailable \u2014 fall back to read-only (no definition sync)\n }\n }\n\n if (canManageDefinitions) {\n try {\n await installCustomEntitiesFromModules(em, cache, {\n tenantIds: [tenantId],\n entityIds,\n includeGlobal: true,\n createOnly: true,\n })\n } catch (err) {\n console.warn('[entities.definitions] Failed to synchronize module-backed definitions', {\n tenantId,\n entityIds,\n err,\n })\n }\n }\n\n let cacheKey: string | null = null\n if (cache && !fieldsetFilter) {\n cacheKey = createDefinitionsCacheKey({\n tenantId,\n organizationId,\n entityIds,\n })\n try {\n const cached = await cache.get(cacheKey)\n if (cached) {\n return NextResponse.json(cached)\n }\n } catch (err) {\n console.warn('[entities.definitions.cache] Failed to read cache', err)\n }\n }\n\n const fieldsetConfigs = await loadEntityFieldsetConfigs(em, {\n entityIds,\n tenantId,\n organizationId,\n mode: 'public',\n })\n\n // Tenant-only scoping: allow global (null) or exact tenant match; do not scope by organization here\n const whereActive = {\n entityId: { $in: entityIds as any },\n deletedAt: null,\n $and: [\n { $or: [ { tenantId: tenantId ?? undefined as any }, { tenantId: null } ] },\n ],\n } as any\n const defs = await em.find(CustomFieldDef, whereActive as any)\n const tombstones = await em.find(CustomFieldDef, {\n entityId: { $in: entityIds as any },\n deletedAt: { $ne: null } as any,\n $and: [\n { $or: [ { tenantId: tenantId ?? undefined as any }, { tenantId: null } ] },\n ],\n } as any)\n\n const tombstonedByEntity = new Map<string, Set<string>>()\n for (const entry of tombstones as any[]) {\n const eid = String(entry.entityId)\n if (!tombstonedByEntity.has(eid)) tombstonedByEntity.set(eid, new Set())\n tombstonedByEntity.get(eid)!.add(entry.key)\n }\n\n const scopeScore = (x: any) => (x.tenantId ? 2 : 0) + (x.organizationId ? 1 : 0)\n\n const definitionsByEntity = new Map<string, any[]>()\n for (const d of defs) {\n const eid = String(d.entityId)\n if (!definitionsByEntity.has(eid)) definitionsByEntity.set(eid, [])\n definitionsByEntity.get(eid)!.push(d)\n }\n\n const entityDefaultEditors = new Map<string, string | undefined>()\n for (const entityId of entityIds) {\n const editor = await resolveEntityDefaultEditor(em, entityId, tenantId ?? null)\n entityDefaultEditors.set(entityId, editor)\n }\n\n const items: any[] = []\n\n const entityOrder = new Map<string, number>()\n entityIds.forEach((id, idx) => entityOrder.set(id, idx))\n\n for (const entityId of entityIds) {\n const defsForEntity = definitionsByEntity.get(entityId) ?? []\n if (!defsForEntity.length) continue\n const tombstonedKeys = tombstonedByEntity.get(entityId) ?? new Set<string>()\n const byKey = new Map<string, any>()\n for (const d of defsForEntity) {\n const existing = byKey.get(d.key)\n if (!existing) { byKey.set(d.key, d); continue }\n const sNew = scopeScore(d)\n const sOld = scopeScore(existing)\n if (sNew > sOld) { byKey.set(d.key, d); continue }\n if (sNew < sOld) continue\n const tNew = (d.updatedAt instanceof Date) ? d.updatedAt.getTime() : new Date(d.updatedAt).getTime()\n const tOld = (existing.updatedAt instanceof Date) ? existing.updatedAt.getTime() : new Date(existing.updatedAt).getTime()\n if (tNew >= tOld) byKey.set(d.key, d)\n }\n\n const defaultEditor = entityDefaultEditors.get(entityId)\n const winning = Array.from(byKey.values()).filter((d: any) => (d.isActive !== false) && !tombstonedKeys.has(d.key))\n winning.sort((a: any, b: any) => ((a.configJson?.priority ?? 0) - (b.configJson?.priority ?? 0)))\n\n for (const d of winning) {\n const rawFieldset = typeof d.configJson?.fieldset === 'string' ? d.configJson.fieldset.trim() : ''\n const normalizedFieldset = rawFieldset.length > 0 ? rawFieldset : undefined\n const normalizedFieldsets = Array.isArray(d.configJson?.fieldsets)\n ? d.configJson.fieldsets\n .filter((entry: unknown): entry is string => typeof entry === 'string')\n .map((entry: string) => entry.trim())\n .filter((entry: string) => entry.length > 0)\n : []\n const effectiveFieldsets = normalizedFieldsets.length > 0\n ? normalizedFieldsets\n : (normalizedFieldset ? [normalizedFieldset] : [])\n if (fieldsetFilter && !effectiveFieldsets.includes(fieldsetFilter)) continue\n const groupInfo = normalizeFieldGroup(d.configJson?.group)\n const keyLower = String(d.key).toLowerCase()\n const candidateBase = {\n key: d.key,\n kind: d.kind,\n label: d.configJson?.label || d.key,\n description: d.configJson?.description || undefined,\n multi: Boolean(d.configJson?.multi),\n options: (() => {\n if (d.kind === 'currency') return undefined\n const normalizedOptions = normalizeCustomFieldOptions(d.configJson?.options)\n return normalizedOptions.length ? normalizedOptions : undefined\n })(),\n optionsUrl: (() => {\n if (d.kind === 'currency') return CURRENCY_OPTIONS_URL\n const dictionaryId = typeof d.configJson?.dictionaryId === 'string' ? d.configJson.dictionaryId : undefined\n if (dictionaryId) return `/api/dictionaries/${dictionaryId}/entries`\n return typeof d.configJson?.optionsUrl === 'string' ? d.configJson.optionsUrl : undefined\n })(),\n filterable: Boolean(d.configJson?.filterable),\n formEditable: d.configJson?.formEditable !== undefined ? Boolean(d.configJson.formEditable) : true,\n listVisible: d.configJson?.listVisible !== undefined ? Boolean(d.configJson.listVisible) : true,\n editor: typeof d.configJson?.editor === 'string'\n ? d.configJson.editor\n : (d.kind === 'multiline' ? defaultEditor : undefined),\n input: typeof d.configJson?.input === 'string' ? d.configJson.input : undefined,\n dictionaryId: typeof d.configJson?.dictionaryId === 'string' ? d.configJson.dictionaryId : undefined,\n dictionaryInlineCreate: d.configJson?.dictionaryInlineCreate !== undefined\n ? Boolean(d.configJson.dictionaryInlineCreate)\n : undefined,\n priority: typeof d.configJson?.priority === 'number' ? d.configJson.priority : 0,\n validation: Array.isArray(d.configJson?.validation) ? d.configJson.validation : undefined,\n defaultValue: d.configJson?.defaultValue !== undefined ? d.configJson.defaultValue : undefined,\n // attachments config passthrough\n maxAttachmentSizeMb: typeof d.configJson?.maxAttachmentSizeMb === 'number' ? d.configJson.maxAttachmentSizeMb : undefined,\n acceptExtensions: Array.isArray(d.configJson?.acceptExtensions) ? d.configJson.acceptExtensions : undefined,\n entityId,\n fieldset: normalizedFieldset ?? effectiveFieldsets[0],\n fieldsets: effectiveFieldsets.length > 0 ? effectiveFieldsets : undefined,\n group: groupInfo,\n } as any\n const metrics = computeDefinitionScore(d, candidateBase, entityOrder.get(entityId) ?? Number.MAX_SAFE_INTEGER)\n const candidate = { ...candidateBase, __score: metrics }\n const existing = (items as any[]).find((entry) => entry.key.toLowerCase() === keyLower)\n if (!existing) {\n items.push(candidate)\n continue\n }\n const existingScore = existing.__score as { base: number; penalty: number; entityIndex: number }\n const candidateScoreInfo = candidate.__score as { base: number; penalty: number; entityIndex: number }\n const better =\n candidateScoreInfo.base > existingScore.base ||\n (candidateScoreInfo.base === existingScore.base && candidateScoreInfo.penalty < existingScore.penalty) ||\n (candidateScoreInfo.base === existingScore.base && candidateScoreInfo.penalty === existingScore.penalty && candidateScoreInfo.entityIndex < existingScore.entityIndex)\n if (better) {\n const index = items.findIndex((entry) => entry.key.toLowerCase() === keyLower)\n if (index >= 0) items[index] = candidate\n }\n }\n }\n\n const sanitized = items.map((item: any) => {\n const { __score, ...rest } = item\n return rest\n })\n sanitized.sort((a: any, b: any) => ((a.priority ?? 0) - (b.priority ?? 0)))\n\n const fieldsetsByEntity: Record<string, CustomFieldsetDefinition[]> = {}\n const entitySettings: Record<string, { singleFieldsetPerRecord: boolean }> = {}\n for (const entityId of entityIds) {\n const cfg = fieldsetConfigs.get(entityId) ?? { fieldsets: [], singleFieldsetPerRecord: true }\n fieldsetsByEntity[entityId] = cfg.fieldsets\n entitySettings[entityId] = { singleFieldsetPerRecord: cfg.singleFieldsetPerRecord }\n }\n\n const responseBody = { items: sanitized, fieldsetsByEntity, entitySettings }\n\n if (cache && cacheKey && !fieldsetFilter) {\n const tags = createDefinitionsCacheTags({\n tenantId,\n organizationId,\n entityIds,\n })\n try {\n await cache.set(cacheKey, responseBody, {\n ttl: ENTITY_DEFINITIONS_CACHE_TTL_MS,\n tags,\n })\n } catch (err) {\n console.warn('[entities.definitions.cache] Failed to store cache entry', err)\n }\n }\n\n return NextResponse.json(responseBody)\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const parsed = upsertCustomFieldDefSchema.safeParse(body)\n if (!parsed.success) return NextResponse.json({ error: 'Validation failed', details: parsed.error.flatten() }, { status: 400 })\n const input = parsed.data\n\n if (!isSystemEntitySelectable(input.entityId)) {\n return NextResponse.json({ error: 'Custom fields are not supported for this entity' }, { status: 400 })\n }\n\n if (input.kind === 'dictionary') {\n const dictionaryId = input.configJson?.dictionaryId\n if (typeof dictionaryId !== 'string' || dictionaryId.trim().length === 0) {\n return NextResponse.json({ error: 'dictionaryId is required for dictionary custom fields' }, { status: 400 })\n }\n }\n\n const container = await createRequestContainer()\n const { resolve } = container\n const em = resolve('em') as any\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n\n const where: any = { entityId: input.entityId, key: input.key, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n let def = await em.findOne(CustomFieldDef, where)\n if (!def) def = em.create(CustomFieldDef, { ...where, createdAt: new Date() })\n def.kind = input.kind\n const inCfg = (input as any).configJson ?? {}\n const cfg: Record<string, any> = { ...inCfg }\n if (cfg.label == null || String(cfg.label).trim() === '') cfg.label = input.key\n if (cfg.formEditable === undefined) cfg.formEditable = true\n if (cfg.listVisible === undefined) cfg.listVisible = true\n if (input.kind === 'dictionary') {\n const dictionaryId = typeof cfg.dictionaryId === 'string' ? cfg.dictionaryId.trim() : ''\n cfg.dictionaryId = dictionaryId\n cfg.dictionaryInlineCreate = cfg.dictionaryInlineCreate !== false\n }\n if (input.kind === 'currency') {\n cfg.optionsUrl = CURRENCY_OPTIONS_URL\n if (Array.isArray(cfg.options)) delete cfg.options\n }\n if (input.kind === 'multiline' && (cfg.editor == null || String(cfg.editor).trim() === '')) {\n cfg.editor = 'markdown'\n }\n // Validate and normalize defaultValue by kind before persisting\n if (cfg.defaultValue !== undefined && cfg.defaultValue !== null) {\n const validationError = await validateDefaultValueByKind(\n cfg.defaultValue, input.kind, cfg, em,\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n )\n if (validationError) {\n return NextResponse.json({ error: validationError }, { status: 400 })\n }\n cfg.defaultValue = normalizeDefaultValueByKind(cfg.defaultValue, input.kind)\n }\n def.configJson = cfg\n def.isActive = input.isActive ?? true\n def.updatedAt = new Date()\n em.persist(def)\n await em.flush()\n await invalidateDefinitionsCache(cache, {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n entityIds: [input.entityId],\n })\n // Changing field definitions may impact forms but not sidebar items; no nav cache touch\n return NextResponse.json({ ok: true, item: { id: def.id, key: def.key, kind: def.kind, configJson: def.configJson, isActive: def.isActive } })\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const { entityId, key } = body || {}\n if (!entityId || !key) return NextResponse.json({ error: 'entityId and key are required' }, { status: 400 })\n\n const container = await createRequestContainer()\n const { resolve } = container\n const em = resolve('em') as any\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n const where: any = { entityId, key, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n const def = await em.findOne(CustomFieldDef, where)\n if (!def) return NextResponse.json({ error: 'Not found' }, { status: 404 })\n def.isActive = false\n def.updatedAt = new Date()\n def.deletedAt = def.deletedAt ?? new Date()\n em.persist(def)\n await em.flush()\n await invalidateDefinitionsCache(cache, {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n entityIds: [entityId],\n })\n // Changing field definitions may impact forms but not sidebar items; no nav cache touch\n return NextResponse.json({ ok: true })\n}\n\nconst definitionsQuerySchema = z\n .object({\n entityId: z.union([z.string(), z.array(z.string())]).optional(),\n entityIds: z.string().optional(),\n fieldset: z.string().regex(fieldsetCodeRegex).optional(),\n })\n .refine(\n (value) => {\n if (value.entityId && typeof value.entityId === 'string' && value.entityId.trim().length > 0) return true\n if (Array.isArray(value.entityId) && value.entityId.length > 0) return true\n return typeof value.entityIds === 'string' && value.entityIds.trim().length > 0\n },\n { message: 'Provide at least one entityId or an entityIds list.' }\n )\n\nconst customFieldOptionValueSchema = z.union([z.string(), z.number()])\n\nconst customFieldDefinitionSchema = z.object({\n key: z.string(),\n kind: z.string(),\n label: z.string(),\n description: z.string().optional(),\n multi: z.boolean().optional(),\n options: z.array(customFieldOptionValueSchema).optional(),\n optionsUrl: z.string().optional(),\n filterable: z.boolean().optional(),\n formEditable: z.boolean().optional(),\n listVisible: z.boolean().optional(),\n editor: z.string().optional(),\n input: z.string().optional(),\n dictionaryId: z.string().optional(),\n dictionaryInlineCreate: z.boolean().optional(),\n priority: z.number().optional(),\n validation: z.array(z.any()).optional(),\n defaultValue: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),\n maxAttachmentSizeMb: z.number().optional(),\n acceptExtensions: z.array(z.any()).optional(),\n entityId: z.string(),\n fieldset: z.string().optional(),\n group: z\n .object({\n code: z.string(),\n title: z.string().optional(),\n hint: z.string().optional(),\n })\n .optional(),\n})\n\nconst customFieldsetGroupResponseSchema = z.object({\n code: z.string(),\n title: z.string().optional(),\n hint: z.string().optional(),\n})\n\nconst entityFieldsetResponseSchema = z.object({\n code: z.string(),\n label: z.string(),\n icon: z.string().optional(),\n description: z.string().optional(),\n groups: z.array(customFieldsetGroupResponseSchema).optional(),\n})\n\nconst definitionsResponseSchema = z.object({\n items: z.array(customFieldDefinitionSchema),\n fieldsetsByEntity: z.record(z.string(), z.array(entityFieldsetResponseSchema)).optional(),\n entitySettings: z\n .record(\n z.string(),\n z.object({\n singleFieldsetPerRecord: z.boolean().optional(),\n })\n )\n .optional(),\n})\n\nconst upsertDefinitionResponseSchema = z.object({\n ok: z.literal(true),\n item: z.object({\n id: z.string().uuid(),\n key: z.string(),\n kind: z.string(),\n configJson: z.record(z.string(), z.any()),\n isActive: z.boolean().optional(),\n }),\n})\n\nconst deleteDefinitionRequestSchema = z.object({\n entityId: z.string(),\n key: z.string(),\n})\n\nconst deleteDefinitionResponseSchema = z.object({\n ok: z.literal(true),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Entities',\n summary: 'Manage custom field definitions',\n methods: {\n GET: {\n summary: 'List active custom field definitions',\n description: 'Returns active custom field definitions for the supplied entity ids, respecting tenant scope and tombstones.',\n query: definitionsQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Definition list',\n schema: definitionsResponseSchema,\n },\n {\n status: 400,\n description: 'Missing entity id',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n POST: {\n summary: 'Upsert custom field definition',\n description: 'Creates or updates a custom field definition for the current tenant/org scope.',\n requestBody: {\n contentType: 'application/json',\n schema: upsertCustomFieldDefSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Definition saved',\n schema: upsertDefinitionResponseSchema,\n },\n {\n status: 400,\n description: 'Validation failed',\n schema: z.object({\n error: z.string(),\n details: z.any().optional(),\n }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n DELETE: {\n summary: 'Soft delete custom field definition',\n description: 'Marks the specified definition inactive and tombstones it for the current scope.',\n requestBody: {\n contentType: 'application/json',\n schema: deleteDefinitionRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Definition deleted',\n schema: deleteDefinitionResponseSchema,\n },\n {\n status: 400,\n description: 'Missing entity id or key',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 404,\n description: 'Definition not found',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\nconst computeDefinitionScore = (def: any, cfg: Record<string, any>, entityIndex: number) => {\n const listVisibleScore = cfg.listVisible === false ? 0 : 1\n const formEditableScore = cfg.formEditable === false ? 0 : 1\n const filterableScore = cfg.filterable ? 1 : 0\n const kindScore = (() => {\n switch (def.kind) {\n case 'dictionary':\n return 8\n case 'relation':\n return 6\n case 'select':\n case 'currency':\n return 4\n case 'multiline':\n return 3\n case 'boolean':\n case 'integer':\n case 'float':\n return 2\n default:\n return 1\n }\n })()\n const optionsBonus = Array.isArray(cfg.options) && cfg.options.length ? 2 : 0\n const dictionaryBonus = typeof cfg.dictionaryId === 'string' && cfg.dictionaryId.trim().length ? 5 : 0\n const base = (listVisibleScore * 16) + (formEditableScore * 8) + (filterableScore * 4) + kindScore + optionsBonus + dictionaryBonus\n const penalty = typeof cfg.priority === 'number' ? cfg.priority : 0\n return { base, penalty, entityIndex }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { CustomEntity, CustomFieldDef } from '@open-mercato/core/modules/entities/data/entities'\nimport { upsertCustomFieldDefSchema, fieldsetCodeRegex } from '@open-mercato/core/modules/entities/data/validators'\nimport {\n createDefinitionsCacheKey,\n createDefinitionsCacheTags,\n invalidateDefinitionsCache,\n ENTITY_DEFINITIONS_CACHE_TTL_MS,\n} from './definitions.cache'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { filterSelectableSystemEntityIds, isSystemEntitySelectable } from '@open-mercato/shared/lib/entities/system-entities'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { loadEntityFieldsetConfigs, CustomFieldsetDefinition } from '../lib/fieldsets'\nimport { installCustomEntitiesFromModules } from '../lib/install-from-ce'\nimport { normalizeCustomFieldOptions } from '@open-mercato/shared/modules/entities/options'\nimport { CURRENCY_OPTIONS_URL } from '@open-mercato/shared/modules/entities/kinds'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n beginEntitiesMutationGuard,\n FIELD_DEFINITION_RESOURCE_KIND,\n} from './definitions.mutation-guard'\n\n/**\n * Validate defaultValue against the field kind. Returns an error message string\n * if invalid, or null if valid. For dictionary and currency kinds, queries the\n * database to verify the token exists.\n */\nasync function validateDefaultValueByKind(\n value: unknown,\n kind: string,\n cfg: Record<string, unknown>,\n em: EntityManager,\n tenantContext: { tenantId: string | null; organizationId: string | null },\n): Promise<string | null> {\n switch (kind) {\n case 'boolean':\n if (typeof value !== 'boolean') return 'defaultValue for boolean fields must be true or false'\n return null\n case 'integer':\n if (typeof value !== 'number' || !Number.isInteger(value))\n return 'defaultValue for integer fields must be a whole number'\n return null\n case 'float':\n if (typeof value !== 'number' || !isFinite(value))\n return 'defaultValue for float fields must be a finite number'\n return null\n case 'text':\n case 'multiline':\n case 'date':\n case 'datetime':\n if (typeof value !== 'string')\n return `defaultValue for ${kind} fields must be a string`\n return null\n case 'dictionary': {\n if (typeof value !== 'string')\n return 'defaultValue for dictionary fields must be a string'\n const dictionaryId = typeof cfg.dictionaryId === 'string' ? cfg.dictionaryId.trim() : ''\n if (dictionaryId) {\n try {\n const { DictionaryEntry } = await import('@open-mercato/core/modules/dictionaries/data/entities')\n const entry = await em.findOne(DictionaryEntry, {\n dictionary: dictionaryId,\n value: value,\n ...(tenantContext.organizationId ? { organizationId: tenantContext.organizationId } : {}),\n ...(tenantContext.tenantId ? { tenantId: tenantContext.tenantId } : {}),\n })\n if (!entry) return `defaultValue \"${value}\" does not match any entry in the configured dictionary`\n } catch (err) {\n // If the dictionaries module is not available, skip entry validation\n console.debug('[entities.definitions] dictionary validation skipped \u2014 module not available', err)\n }\n }\n return null\n }\n case 'currency': {\n if (typeof value !== 'string')\n return 'defaultValue for currency fields must be a string'\n try {\n const { Currency } = await import('@open-mercato/core/modules/currencies/data/entities')\n const where: Record<string, string> = { code: value as string }\n if (tenantContext.tenantId) where.tenantId = tenantContext.tenantId\n if (tenantContext.organizationId) where.organizationId = tenantContext.organizationId\n const currency = await em.findOne(Currency, where)\n if (!currency) return `defaultValue \"${value}\" does not match any available currency`\n } catch (err) {\n // If the currencies module is not available, skip currency validation\n console.debug('[entities.definitions] currency validation skipped \u2014 module not available', err)\n }\n return null\n }\n case 'select': {\n if (typeof value !== 'string' && typeof value !== 'number')\n return 'defaultValue for select fields must be a string or number'\n const options = normalizeCustomFieldOptions(\n Array.isArray(cfg.options) ? cfg.options : [],\n )\n if (options.length > 0) {\n const match = options.some(\n (o) => o.value === value || String(o.value) === String(value),\n )\n if (!match) return 'defaultValue does not match any of the configured options'\n }\n return null\n }\n case 'relation':\n case 'attachment':\n return `defaultValue is not supported for ${kind} fields`\n default:\n return null\n }\n}\n\n/**\n * Normalize defaultValue to the canonical storage shape for the given kind.\n */\nfunction normalizeDefaultValueByKind(value: unknown, kind: string): unknown {\n switch (kind) {\n case 'boolean':\n return value === true\n case 'integer':\n return Math.round(Number(value))\n case 'float':\n return Number(value)\n default:\n return value\n }\n}\n\nexport const metadata = {\n // Reading definitions is needed by record forms; keep it auth-protected but accessible to all authenticated users\n GET: { requireAuth: true },\n // Mutations remain admin-only\n POST: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n}\n\nfunction parseEntityIds(url: URL): string[] {\n const direct = url.searchParams.getAll('entityId').filter((id) => typeof id === 'string' && id.trim().length > 0)\n const combined = url.searchParams.get('entityIds')\n if (combined) {\n combined\n .split(',')\n .map((id) => id.trim())\n .filter((id) => id.length > 0)\n .forEach((id) => direct.push(id))\n }\n const unique: string[] = []\n const seen = new Set<string>()\n for (const id of direct) {\n if (seen.has(id)) continue\n seen.add(id)\n unique.push(id)\n }\n return unique\n}\n\nasync function resolveEntityDefaultEditor(em: any, entityId: string, tenantId: string | null | undefined): Promise<string | undefined> {\n try {\n const ent = await em.findOne(CustomEntity, {\n entityId,\n $and: [\n { $or: [ { tenantId: tenantId ?? undefined as any }, { tenantId: null } ] },\n ],\n } as any)\n if (ent && typeof (ent as any).defaultEditor === 'string') return (ent as any).defaultEditor\n } catch {}\n return undefined\n}\n\nfunction normalizeFieldGroup(raw: unknown): { code: string; title?: string; hint?: string } | undefined {\n if (!raw) return undefined\n if (typeof raw === 'string') {\n const code = raw.trim()\n return code ? { code } : undefined\n }\n if (typeof raw !== 'object') return undefined\n const entry = raw as Record<string, unknown>\n const code = typeof entry.code === 'string' ? entry.code.trim() : ''\n if (!code) return undefined\n const group: { code: string; title?: string; hint?: string } = { code }\n if (typeof entry.title === 'string' && entry.title.trim()) group.title = entry.title.trim()\n if (typeof entry.hint === 'string' && entry.hint.trim()) group.hint = entry.hint.trim()\n return group\n}\n\nexport async function GET(req: Request) {\n const url = new URL(req.url)\n const requestedEntityIds = parseEntityIds(url)\n if (!requestedEntityIds.length) {\n return NextResponse.json({ error: 'entityId is required' }, { status: 400 })\n }\n const requestedFieldset = url.searchParams.get('fieldset')\n const fieldsetFilter = requestedFieldset && requestedFieldset.trim().length ? requestedFieldset.trim() : null\n\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const entityIds = filterSelectableSystemEntityIds(requestedEntityIds)\n if (!entityIds.length) {\n return NextResponse.json({ items: [] })\n }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope.tenantId ?? auth.tenantId ?? null\n if (!tenantId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n const organizationId = scope.selectedId ?? auth.orgId ?? null\n const { resolve } = container\n const em = resolve('em') as any\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n\n let canManageDefinitions = false\n if (typeof auth.sub === 'string' && auth.sub.length > 0) {\n try {\n const rbac = resolve('rbacService') as RbacService\n canManageDefinitions = await rbac.userHasAllFeatures(auth.sub, ['entities.definitions.manage'], {\n tenantId,\n organizationId,\n })\n } catch {\n // RBAC service unavailable \u2014 fall back to read-only (no definition sync)\n }\n }\n\n if (canManageDefinitions) {\n try {\n await installCustomEntitiesFromModules(em, cache, {\n tenantIds: [tenantId],\n entityIds,\n includeGlobal: true,\n createOnly: true,\n })\n } catch (err) {\n console.warn('[entities.definitions] Failed to synchronize module-backed definitions', {\n tenantId,\n entityIds,\n err,\n })\n }\n }\n\n let cacheKey: string | null = null\n if (cache && !fieldsetFilter) {\n cacheKey = createDefinitionsCacheKey({\n tenantId,\n organizationId,\n entityIds,\n })\n try {\n const cached = await cache.get(cacheKey)\n if (cached) {\n return NextResponse.json(cached)\n }\n } catch (err) {\n console.warn('[entities.definitions.cache] Failed to read cache', err)\n }\n }\n\n const fieldsetConfigs = await loadEntityFieldsetConfigs(em, {\n entityIds,\n tenantId,\n organizationId,\n mode: 'public',\n })\n\n // Tenant-only scoping: allow global (null) or exact tenant match; do not scope by organization here\n const whereActive = {\n entityId: { $in: entityIds as any },\n deletedAt: null,\n $and: [\n { $or: [ { tenantId: tenantId ?? undefined as any }, { tenantId: null } ] },\n ],\n } as any\n const defs = await em.find(CustomFieldDef, whereActive as any)\n const tombstones = await em.find(CustomFieldDef, {\n entityId: { $in: entityIds as any },\n deletedAt: { $ne: null } as any,\n $and: [\n { $or: [ { tenantId: tenantId ?? undefined as any }, { tenantId: null } ] },\n ],\n } as any)\n\n const tombstonedByEntity = new Map<string, Set<string>>()\n for (const entry of tombstones as any[]) {\n const eid = String(entry.entityId)\n if (!tombstonedByEntity.has(eid)) tombstonedByEntity.set(eid, new Set())\n tombstonedByEntity.get(eid)!.add(entry.key)\n }\n\n const scopeScore = (x: any) => (x.tenantId ? 2 : 0) + (x.organizationId ? 1 : 0)\n\n const definitionsByEntity = new Map<string, any[]>()\n for (const d of defs) {\n const eid = String(d.entityId)\n if (!definitionsByEntity.has(eid)) definitionsByEntity.set(eid, [])\n definitionsByEntity.get(eid)!.push(d)\n }\n\n const entityDefaultEditors = new Map<string, string | undefined>()\n for (const entityId of entityIds) {\n const editor = await resolveEntityDefaultEditor(em, entityId, tenantId ?? null)\n entityDefaultEditors.set(entityId, editor)\n }\n\n const items: any[] = []\n\n const entityOrder = new Map<string, number>()\n entityIds.forEach((id, idx) => entityOrder.set(id, idx))\n\n for (const entityId of entityIds) {\n const defsForEntity = definitionsByEntity.get(entityId) ?? []\n if (!defsForEntity.length) continue\n const tombstonedKeys = tombstonedByEntity.get(entityId) ?? new Set<string>()\n const byKey = new Map<string, any>()\n for (const d of defsForEntity) {\n const existing = byKey.get(d.key)\n if (!existing) { byKey.set(d.key, d); continue }\n const sNew = scopeScore(d)\n const sOld = scopeScore(existing)\n if (sNew > sOld) { byKey.set(d.key, d); continue }\n if (sNew < sOld) continue\n const tNew = (d.updatedAt instanceof Date) ? d.updatedAt.getTime() : new Date(d.updatedAt).getTime()\n const tOld = (existing.updatedAt instanceof Date) ? existing.updatedAt.getTime() : new Date(existing.updatedAt).getTime()\n if (tNew >= tOld) byKey.set(d.key, d)\n }\n\n const defaultEditor = entityDefaultEditors.get(entityId)\n const winning = Array.from(byKey.values()).filter((d: any) => (d.isActive !== false) && !tombstonedKeys.has(d.key))\n winning.sort((a: any, b: any) => ((a.configJson?.priority ?? 0) - (b.configJson?.priority ?? 0)))\n\n for (const d of winning) {\n const rawFieldset = typeof d.configJson?.fieldset === 'string' ? d.configJson.fieldset.trim() : ''\n const normalizedFieldset = rawFieldset.length > 0 ? rawFieldset : undefined\n const normalizedFieldsets = Array.isArray(d.configJson?.fieldsets)\n ? d.configJson.fieldsets\n .filter((entry: unknown): entry is string => typeof entry === 'string')\n .map((entry: string) => entry.trim())\n .filter((entry: string) => entry.length > 0)\n : []\n const effectiveFieldsets = normalizedFieldsets.length > 0\n ? normalizedFieldsets\n : (normalizedFieldset ? [normalizedFieldset] : [])\n if (fieldsetFilter && !effectiveFieldsets.includes(fieldsetFilter)) continue\n const groupInfo = normalizeFieldGroup(d.configJson?.group)\n const keyLower = String(d.key).toLowerCase()\n const candidateBase = {\n key: d.key,\n kind: d.kind,\n label: d.configJson?.label || d.key,\n description: d.configJson?.description || undefined,\n multi: Boolean(d.configJson?.multi),\n options: (() => {\n if (d.kind === 'currency') return undefined\n const normalizedOptions = normalizeCustomFieldOptions(d.configJson?.options)\n return normalizedOptions.length ? normalizedOptions : undefined\n })(),\n optionsUrl: (() => {\n if (d.kind === 'currency') return CURRENCY_OPTIONS_URL\n const dictionaryId = typeof d.configJson?.dictionaryId === 'string' ? d.configJson.dictionaryId : undefined\n if (dictionaryId) return `/api/dictionaries/${dictionaryId}/entries`\n return typeof d.configJson?.optionsUrl === 'string' ? d.configJson.optionsUrl : undefined\n })(),\n filterable: Boolean(d.configJson?.filterable),\n formEditable: d.configJson?.formEditable !== undefined ? Boolean(d.configJson.formEditable) : true,\n listVisible: d.configJson?.listVisible !== undefined ? Boolean(d.configJson.listVisible) : true,\n editor: typeof d.configJson?.editor === 'string'\n ? d.configJson.editor\n : (d.kind === 'multiline' ? defaultEditor : undefined),\n input: typeof d.configJson?.input === 'string' ? d.configJson.input : undefined,\n dictionaryId: typeof d.configJson?.dictionaryId === 'string' ? d.configJson.dictionaryId : undefined,\n dictionaryInlineCreate: d.configJson?.dictionaryInlineCreate !== undefined\n ? Boolean(d.configJson.dictionaryInlineCreate)\n : undefined,\n priority: typeof d.configJson?.priority === 'number' ? d.configJson.priority : 0,\n validation: Array.isArray(d.configJson?.validation) ? d.configJson.validation : undefined,\n defaultValue: d.configJson?.defaultValue !== undefined ? d.configJson.defaultValue : undefined,\n // attachments config passthrough\n maxAttachmentSizeMb: typeof d.configJson?.maxAttachmentSizeMb === 'number' ? d.configJson.maxAttachmentSizeMb : undefined,\n acceptExtensions: Array.isArray(d.configJson?.acceptExtensions) ? d.configJson.acceptExtensions : undefined,\n entityId,\n fieldset: normalizedFieldset ?? effectiveFieldsets[0],\n fieldsets: effectiveFieldsets.length > 0 ? effectiveFieldsets : undefined,\n group: groupInfo,\n } as any\n const metrics = computeDefinitionScore(d, candidateBase, entityOrder.get(entityId) ?? Number.MAX_SAFE_INTEGER)\n const candidate = { ...candidateBase, __score: metrics }\n const existing = (items as any[]).find((entry) => entry.key.toLowerCase() === keyLower)\n if (!existing) {\n items.push(candidate)\n continue\n }\n const existingScore = existing.__score as { base: number; penalty: number; entityIndex: number }\n const candidateScoreInfo = candidate.__score as { base: number; penalty: number; entityIndex: number }\n const better =\n candidateScoreInfo.base > existingScore.base ||\n (candidateScoreInfo.base === existingScore.base && candidateScoreInfo.penalty < existingScore.penalty) ||\n (candidateScoreInfo.base === existingScore.base && candidateScoreInfo.penalty === existingScore.penalty && candidateScoreInfo.entityIndex < existingScore.entityIndex)\n if (better) {\n const index = items.findIndex((entry) => entry.key.toLowerCase() === keyLower)\n if (index >= 0) items[index] = candidate\n }\n }\n }\n\n const sanitized = items.map((item: any) => {\n const { __score, ...rest } = item\n return rest\n })\n sanitized.sort((a: any, b: any) => ((a.priority ?? 0) - (b.priority ?? 0)))\n\n const fieldsetsByEntity: Record<string, CustomFieldsetDefinition[]> = {}\n const entitySettings: Record<string, { singleFieldsetPerRecord: boolean }> = {}\n for (const entityId of entityIds) {\n const cfg = fieldsetConfigs.get(entityId) ?? { fieldsets: [], singleFieldsetPerRecord: true }\n fieldsetsByEntity[entityId] = cfg.fieldsets\n entitySettings[entityId] = { singleFieldsetPerRecord: cfg.singleFieldsetPerRecord }\n }\n\n const responseBody = { items: sanitized, fieldsetsByEntity, entitySettings }\n\n if (cache && cacheKey && !fieldsetFilter) {\n const tags = createDefinitionsCacheTags({\n tenantId,\n organizationId,\n entityIds,\n })\n try {\n await cache.set(cacheKey, responseBody, {\n ttl: ENTITY_DEFINITIONS_CACHE_TTL_MS,\n tags,\n })\n } catch (err) {\n console.warn('[entities.definitions.cache] Failed to store cache entry', err)\n }\n }\n\n return NextResponse.json(responseBody)\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const parsed = upsertCustomFieldDefSchema.safeParse(body)\n if (!parsed.success) return NextResponse.json({ error: 'Validation failed', details: parsed.error.flatten() }, { status: 400 })\n const input = parsed.data\n\n if (!isSystemEntitySelectable(input.entityId)) {\n return NextResponse.json({ error: 'Custom fields are not supported for this entity' }, { status: 400 })\n }\n\n if (input.kind === 'dictionary') {\n const dictionaryId = input.configJson?.dictionaryId\n if (typeof dictionaryId !== 'string' || dictionaryId.trim().length === 0) {\n return NextResponse.json({ error: 'dictionaryId is required for dictionary custom fields' }, { status: 400 })\n }\n }\n\n const container = await createRequestContainer()\n const { resolve } = container\n const em = resolve('em') as any\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n\n const where: any = { entityId: input.entityId, key: input.key, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n let def = await em.findOne(CustomFieldDef, where)\n\n const guard = await beginEntitiesMutationGuard({\n container,\n auth,\n req,\n resourceKind: FIELD_DEFINITION_RESOURCE_KIND,\n resourceId: def ? def.id : `${input.entityId}:${input.key}`,\n operation: def ? 'update' : 'create',\n mutationPayload: input as unknown as Record<string, unknown>,\n })\n if (guard.blockedResponse) return guard.blockedResponse\n\n if (!def) def = em.create(CustomFieldDef, { ...where, createdAt: new Date() })\n def.kind = input.kind\n const inCfg = (input as any).configJson ?? {}\n const cfg: Record<string, any> = { ...inCfg }\n if (cfg.label == null || String(cfg.label).trim() === '') cfg.label = input.key\n if (cfg.formEditable === undefined) cfg.formEditable = true\n if (cfg.listVisible === undefined) cfg.listVisible = true\n if (input.kind === 'dictionary') {\n const dictionaryId = typeof cfg.dictionaryId === 'string' ? cfg.dictionaryId.trim() : ''\n cfg.dictionaryId = dictionaryId\n cfg.dictionaryInlineCreate = cfg.dictionaryInlineCreate !== false\n }\n if (input.kind === 'currency') {\n cfg.optionsUrl = CURRENCY_OPTIONS_URL\n if (Array.isArray(cfg.options)) delete cfg.options\n }\n if (input.kind === 'multiline' && (cfg.editor == null || String(cfg.editor).trim() === '')) {\n cfg.editor = 'markdown'\n }\n // Validate and normalize defaultValue by kind before persisting\n if (cfg.defaultValue !== undefined && cfg.defaultValue !== null) {\n const validationError = await validateDefaultValueByKind(\n cfg.defaultValue, input.kind, cfg, em,\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n )\n if (validationError) {\n return NextResponse.json({ error: validationError }, { status: 400 })\n }\n cfg.defaultValue = normalizeDefaultValueByKind(cfg.defaultValue, input.kind)\n }\n def.configJson = cfg\n def.isActive = input.isActive ?? true\n def.updatedAt = new Date()\n em.persist(def)\n await em.flush()\n await guard.runAfterSuccess()\n await invalidateDefinitionsCache(cache, {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n entityIds: [input.entityId],\n })\n // Changing field definitions may impact forms but not sidebar items; no nav cache touch\n return NextResponse.json({ ok: true, item: { id: def.id, key: def.key, kind: def.kind, configJson: def.configJson, isActive: def.isActive } })\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const { entityId, key } = body || {}\n if (!entityId || !key) return NextResponse.json({ error: 'entityId and key are required' }, { status: 400 })\n\n const container = await createRequestContainer()\n const { resolve } = container\n const em = resolve('em') as any\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n const where: any = { entityId, key, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n const def = await em.findOne(CustomFieldDef, where)\n if (!def) return NextResponse.json({ error: 'Not found' }, { status: 404 })\n\n const guard = await beginEntitiesMutationGuard({\n container,\n auth,\n req,\n resourceKind: FIELD_DEFINITION_RESOURCE_KIND,\n resourceId: def.id,\n operation: 'delete',\n mutationPayload: { entityId, key },\n })\n if (guard.blockedResponse) return guard.blockedResponse\n\n def.isActive = false\n def.updatedAt = new Date()\n def.deletedAt = def.deletedAt ?? new Date()\n em.persist(def)\n await em.flush()\n await guard.runAfterSuccess()\n await invalidateDefinitionsCache(cache, {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n entityIds: [entityId],\n })\n // Changing field definitions may impact forms but not sidebar items; no nav cache touch\n return NextResponse.json({ ok: true })\n}\n\nconst definitionsQuerySchema = z\n .object({\n entityId: z.union([z.string(), z.array(z.string())]).optional(),\n entityIds: z.string().optional(),\n fieldset: z.string().regex(fieldsetCodeRegex).optional(),\n })\n .refine(\n (value) => {\n if (value.entityId && typeof value.entityId === 'string' && value.entityId.trim().length > 0) return true\n if (Array.isArray(value.entityId) && value.entityId.length > 0) return true\n return typeof value.entityIds === 'string' && value.entityIds.trim().length > 0\n },\n { message: 'Provide at least one entityId or an entityIds list.' }\n )\n\nconst customFieldOptionValueSchema = z.union([z.string(), z.number()])\n\nconst customFieldDefinitionSchema = z.object({\n key: z.string(),\n kind: z.string(),\n label: z.string(),\n description: z.string().optional(),\n multi: z.boolean().optional(),\n options: z.array(customFieldOptionValueSchema).optional(),\n optionsUrl: z.string().optional(),\n filterable: z.boolean().optional(),\n formEditable: z.boolean().optional(),\n listVisible: z.boolean().optional(),\n editor: z.string().optional(),\n input: z.string().optional(),\n dictionaryId: z.string().optional(),\n dictionaryInlineCreate: z.boolean().optional(),\n priority: z.number().optional(),\n validation: z.array(z.any()).optional(),\n defaultValue: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),\n maxAttachmentSizeMb: z.number().optional(),\n acceptExtensions: z.array(z.any()).optional(),\n entityId: z.string(),\n fieldset: z.string().optional(),\n group: z\n .object({\n code: z.string(),\n title: z.string().optional(),\n hint: z.string().optional(),\n })\n .optional(),\n})\n\nconst customFieldsetGroupResponseSchema = z.object({\n code: z.string(),\n title: z.string().optional(),\n hint: z.string().optional(),\n})\n\nconst entityFieldsetResponseSchema = z.object({\n code: z.string(),\n label: z.string(),\n icon: z.string().optional(),\n description: z.string().optional(),\n groups: z.array(customFieldsetGroupResponseSchema).optional(),\n})\n\nconst definitionsResponseSchema = z.object({\n items: z.array(customFieldDefinitionSchema),\n fieldsetsByEntity: z.record(z.string(), z.array(entityFieldsetResponseSchema)).optional(),\n entitySettings: z\n .record(\n z.string(),\n z.object({\n singleFieldsetPerRecord: z.boolean().optional(),\n })\n )\n .optional(),\n})\n\nconst upsertDefinitionResponseSchema = z.object({\n ok: z.literal(true),\n item: z.object({\n id: z.string().uuid(),\n key: z.string(),\n kind: z.string(),\n configJson: z.record(z.string(), z.any()),\n isActive: z.boolean().optional(),\n }),\n})\n\nconst deleteDefinitionRequestSchema = z.object({\n entityId: z.string(),\n key: z.string(),\n})\n\nconst deleteDefinitionResponseSchema = z.object({\n ok: z.literal(true),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Entities',\n summary: 'Manage custom field definitions',\n methods: {\n GET: {\n summary: 'List active custom field definitions',\n description: 'Returns active custom field definitions for the supplied entity ids, respecting tenant scope and tombstones.',\n query: definitionsQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Definition list',\n schema: definitionsResponseSchema,\n },\n {\n status: 400,\n description: 'Missing entity id',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n POST: {\n summary: 'Upsert custom field definition',\n description: 'Creates or updates a custom field definition for the current tenant/org scope.',\n requestBody: {\n contentType: 'application/json',\n schema: upsertCustomFieldDefSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Definition saved',\n schema: upsertDefinitionResponseSchema,\n },\n {\n status: 400,\n description: 'Validation failed',\n schema: z.object({\n error: z.string(),\n details: z.any().optional(),\n }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n DELETE: {\n summary: 'Soft delete custom field definition',\n description: 'Marks the specified definition inactive and tombstones it for the current scope.',\n requestBody: {\n contentType: 'application/json',\n schema: deleteDefinitionRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Definition deleted',\n schema: deleteDefinitionResponseSchema,\n },\n {\n status: 400,\n description: 'Missing entity id or key',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 404,\n description: 'Definition not found',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\nconst computeDefinitionScore = (def: any, cfg: Record<string, any>, entityIndex: number) => {\n const listVisibleScore = cfg.listVisible === false ? 0 : 1\n const formEditableScore = cfg.formEditable === false ? 0 : 1\n const filterableScore = cfg.filterable ? 1 : 0\n const kindScore = (() => {\n switch (def.kind) {\n case 'dictionary':\n return 8\n case 'relation':\n return 6\n case 'select':\n case 'currency':\n return 4\n case 'multiline':\n return 3\n case 'boolean':\n case 'integer':\n case 'float':\n return 2\n default:\n return 1\n }\n })()\n const optionsBonus = Array.isArray(cfg.options) && cfg.options.length ? 2 : 0\n const dictionaryBonus = typeof cfg.dictionaryId === 'string' && cfg.dictionaryId.trim().length ? 5 : 0\n const base = (listVisibleScore * 16) + (formEditableScore * 8) + (filterableScore * 4) + kindScore + optionsBonus + dictionaryBonus\n const penalty = typeof cfg.priority === 'number' ? cfg.priority : 0\n return { base, penalty, entityIndex }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["code"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import {
|
|
3
|
+
runCrudMutationGuardAfterSuccess,
|
|
4
|
+
validateCrudMutationGuard
|
|
5
|
+
} from "@open-mercato/shared/lib/crud/mutation-guard";
|
|
6
|
+
const ENTITY_DEFINITION_RESOURCE_KIND = "entities.entity";
|
|
7
|
+
const FIELD_DEFINITION_RESOURCE_KIND = "entities.field_definition";
|
|
8
|
+
function resolveActorUserId(auth) {
|
|
9
|
+
return auth.userId ?? auth.sub;
|
|
10
|
+
}
|
|
11
|
+
async function beginEntitiesMutationGuard(ctx) {
|
|
12
|
+
const tenantId = ctx.auth.tenantId;
|
|
13
|
+
const userId = resolveActorUserId(ctx.auth);
|
|
14
|
+
if (!tenantId) {
|
|
15
|
+
return { blockedResponse: null, runAfterSuccess: async () => void 0 };
|
|
16
|
+
}
|
|
17
|
+
const guardResult = await validateCrudMutationGuard(ctx.container, {
|
|
18
|
+
tenantId,
|
|
19
|
+
organizationId: ctx.auth.orgId ?? null,
|
|
20
|
+
userId,
|
|
21
|
+
resourceKind: ctx.resourceKind,
|
|
22
|
+
resourceId: ctx.resourceId,
|
|
23
|
+
operation: ctx.operation,
|
|
24
|
+
requestMethod: ctx.req.method,
|
|
25
|
+
requestHeaders: ctx.req.headers,
|
|
26
|
+
mutationPayload: ctx.mutationPayload ?? null
|
|
27
|
+
});
|
|
28
|
+
if (guardResult && !guardResult.ok) {
|
|
29
|
+
return {
|
|
30
|
+
blockedResponse: NextResponse.json(guardResult.body, { status: guardResult.status }),
|
|
31
|
+
runAfterSuccess: async () => void 0
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
blockedResponse: null,
|
|
36
|
+
runAfterSuccess: async () => {
|
|
37
|
+
if (!guardResult?.ok || !guardResult.shouldRunAfterSuccess) return;
|
|
38
|
+
await runCrudMutationGuardAfterSuccess(ctx.container, {
|
|
39
|
+
tenantId,
|
|
40
|
+
organizationId: ctx.auth.orgId ?? null,
|
|
41
|
+
userId,
|
|
42
|
+
resourceKind: ctx.resourceKind,
|
|
43
|
+
resourceId: ctx.resourceId,
|
|
44
|
+
operation: ctx.operation,
|
|
45
|
+
requestMethod: ctx.req.method,
|
|
46
|
+
requestHeaders: ctx.req.headers,
|
|
47
|
+
metadata: guardResult.metadata ?? null
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
ENTITY_DEFINITION_RESOURCE_KIND,
|
|
54
|
+
FIELD_DEFINITION_RESOURCE_KIND,
|
|
55
|
+
beginEntitiesMutationGuard
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=definitions.mutation-guard.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/entities/api/definitions.mutation-guard.ts"],
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport type { AwilixContainer } from 'awilix'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\n\nexport const ENTITY_DEFINITION_RESOURCE_KIND = 'entities.entity'\nexport const FIELD_DEFINITION_RESOURCE_KIND = 'entities.field_definition'\n\ntype AuthenticatedContext = NonNullable<AuthContext>\n\ntype GuardOperation = 'create' | 'update' | 'delete' | 'custom'\n\ntype EntitiesMutationGuardContext = {\n container: AwilixContainer\n auth: AuthenticatedContext\n req: Request\n resourceKind: string\n resourceId: string\n operation: GuardOperation\n mutationPayload?: Record<string, unknown> | null\n}\n\ntype EntitiesMutationGuardHandle = {\n blockedResponse: NextResponse | null\n runAfterSuccess: () => Promise<void>\n}\n\nfunction resolveActorUserId(auth: AuthenticatedContext): string {\n return auth.userId ?? auth.sub\n}\n\nexport async function beginEntitiesMutationGuard(\n ctx: EntitiesMutationGuardContext,\n): Promise<EntitiesMutationGuardHandle> {\n const tenantId = ctx.auth.tenantId\n const userId = resolveActorUserId(ctx.auth)\n if (!tenantId) {\n return { blockedResponse: null, runAfterSuccess: async () => undefined }\n }\n\n const guardResult = await validateCrudMutationGuard(ctx.container, {\n tenantId,\n organizationId: ctx.auth.orgId ?? null,\n userId,\n resourceKind: ctx.resourceKind,\n resourceId: ctx.resourceId,\n operation: ctx.operation,\n requestMethod: ctx.req.method,\n requestHeaders: ctx.req.headers,\n mutationPayload: ctx.mutationPayload ?? null,\n })\n\n if (guardResult && !guardResult.ok) {\n return {\n blockedResponse: NextResponse.json(guardResult.body, { status: guardResult.status }),\n runAfterSuccess: async () => undefined,\n }\n }\n\n return {\n blockedResponse: null,\n runAfterSuccess: async () => {\n if (!guardResult?.ok || !guardResult.shouldRunAfterSuccess) return\n await runCrudMutationGuardAfterSuccess(ctx.container, {\n tenantId,\n organizationId: ctx.auth.orgId ?? null,\n userId,\n resourceKind: ctx.resourceKind,\n resourceId: ctx.resourceId,\n operation: ctx.operation,\n requestMethod: ctx.req.method,\n requestHeaders: ctx.req.headers,\n metadata: guardResult.metadata ?? null,\n })\n },\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAG7B;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEA,MAAM,kCAAkC;AACxC,MAAM,iCAAiC;AAqB9C,SAAS,mBAAmB,MAAoC;AAC9D,SAAO,KAAK,UAAU,KAAK;AAC7B;AAEA,eAAsB,2BACpB,KACsC;AACtC,QAAM,WAAW,IAAI,KAAK;AAC1B,QAAM,SAAS,mBAAmB,IAAI,IAAI;AAC1C,MAAI,CAAC,UAAU;AACb,WAAO,EAAE,iBAAiB,MAAM,iBAAiB,YAAY,OAAU;AAAA,EACzE;AAEA,QAAM,cAAc,MAAM,0BAA0B,IAAI,WAAW;AAAA,IACjE;AAAA,IACA,gBAAgB,IAAI,KAAK,SAAS;AAAA,IAClC;AAAA,IACA,cAAc,IAAI;AAAA,IAClB,YAAY,IAAI;AAAA,IAChB,WAAW,IAAI;AAAA,IACf,eAAe,IAAI,IAAI;AAAA,IACvB,gBAAgB,IAAI,IAAI;AAAA,IACxB,iBAAiB,IAAI,mBAAmB;AAAA,EAC1C,CAAC;AAED,MAAI,eAAe,CAAC,YAAY,IAAI;AAClC,WAAO;AAAA,MACL,iBAAiB,aAAa,KAAK,YAAY,MAAM,EAAE,QAAQ,YAAY,OAAO,CAAC;AAAA,MACnF,iBAAiB,YAAY;AAAA,IAC/B;AAAA,EACF;AAEA,SAAO;AAAA,IACL,iBAAiB;AAAA,IACjB,iBAAiB,YAAY;AAC3B,UAAI,CAAC,aAAa,MAAM,CAAC,YAAY,sBAAuB;AAC5D,YAAM,iCAAiC,IAAI,WAAW;AAAA,QACpD;AAAA,QACA,gBAAgB,IAAI,KAAK,SAAS;AAAA,QAClC;AAAA,QACA,cAAc,IAAI;AAAA,QAClB,YAAY,IAAI;AAAA,QAChB,WAAW,IAAI;AAAA,QACf,eAAe,IAAI,IAAI;AAAA,QACvB,gBAAgB,IAAI,IAAI;AAAA,QACxB,UAAU,YAAY,YAAY;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -4,6 +4,10 @@ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
|
4
4
|
import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
|
|
5
5
|
import { CustomFieldDef } from "@open-mercato/core/modules/entities/data/entities";
|
|
6
6
|
import { invalidateDefinitionsCache } from "./definitions.cache.js";
|
|
7
|
+
import {
|
|
8
|
+
beginEntitiesMutationGuard,
|
|
9
|
+
FIELD_DEFINITION_RESOURCE_KIND
|
|
10
|
+
} from "./definitions.mutation-guard.js";
|
|
7
11
|
const metadata = {
|
|
8
12
|
POST: { requireAuth: true, requireFeatures: ["entities.definitions.manage"] }
|
|
9
13
|
};
|
|
@@ -29,11 +33,22 @@ async function POST(req) {
|
|
|
29
33
|
const where = { entityId, key, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null };
|
|
30
34
|
const def = await em.findOne(CustomFieldDef, where);
|
|
31
35
|
if (!def) return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
36
|
+
const guard = await beginEntitiesMutationGuard({
|
|
37
|
+
container,
|
|
38
|
+
auth,
|
|
39
|
+
req,
|
|
40
|
+
resourceKind: FIELD_DEFINITION_RESOURCE_KIND,
|
|
41
|
+
resourceId: def.id,
|
|
42
|
+
operation: "custom",
|
|
43
|
+
mutationPayload: { entityId, key }
|
|
44
|
+
});
|
|
45
|
+
if (guard.blockedResponse) return guard.blockedResponse;
|
|
32
46
|
def.deletedAt = null;
|
|
33
47
|
def.isActive = true;
|
|
34
48
|
def.updatedAt = /* @__PURE__ */ new Date();
|
|
35
49
|
em.persist(def);
|
|
36
50
|
await em.flush();
|
|
51
|
+
await guard.runAfterSuccess();
|
|
37
52
|
await invalidateDefinitionsCache(cache, {
|
|
38
53
|
tenantId: auth.tenantId ?? null,
|
|
39
54
|
organizationId: auth.orgId ?? null,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/entities/api/definitions.restore.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { CustomFieldDef } from '@open-mercato/core/modules/entities/data/entities'\nimport { invalidateDefinitionsCache } from './definitions.cache'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const { entityId, key } = body || {}\n if (!entityId || !key) return NextResponse.json({ error: 'entityId and key are required' }, { status: 400 })\n\n const container = await createRequestContainer()\n const { resolve } = container\n const em = resolve('em') as any\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n\n const where: any = { entityId, key, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n const def = await em.findOne(CustomFieldDef, where)\n if (!def) return NextResponse.json({ error: 'Not found' }, { status: 404 })\n ;(def as any).deletedAt = null\n ;(def as any).isActive = true\n ;(def as any).updatedAt = new Date()\n em.persist(def)\n await em.flush()\n await invalidateDefinitionsCache(cache, {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n entityIds: [entityId],\n })\n return NextResponse.json({ ok: true })\n}\n\nconst restoreDefinitionRequestSchema = z.object({\n entityId: z.string(),\n key: z.string(),\n})\n\nconst restoreDefinitionResponseSchema = z.object({\n ok: z.literal(true),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Entities',\n summary: 'Restore soft-deleted custom field definitions',\n methods: {\n POST: {\n summary: 'Restore definition',\n description: 'Reactivates a previously soft-deleted definition within the current tenant/org scope.',\n requestBody: {\n contentType: 'application/json',\n schema: restoreDefinitionRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Definition restored',\n schema: restoreDefinitionResponseSchema,\n },\n {\n status: 400,\n description: 'Missing entity id or key',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 404,\n description: 'Definition not found',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAElB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,sBAAsB;AAC/B,SAAS,kCAAkC;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { CacheStrategy } from '@open-mercato/cache'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { CustomFieldDef } from '@open-mercato/core/modules/entities/data/entities'\nimport { invalidateDefinitionsCache } from './definitions.cache'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n beginEntitiesMutationGuard,\n FIELD_DEFINITION_RESOURCE_KIND,\n} from './definitions.mutation-guard'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const { entityId, key } = body || {}\n if (!entityId || !key) return NextResponse.json({ error: 'entityId and key are required' }, { status: 400 })\n\n const container = await createRequestContainer()\n const { resolve } = container\n const em = resolve('em') as any\n let cache: CacheStrategy | undefined\n try {\n cache = resolve('cache') as CacheStrategy\n } catch {}\n\n const where: any = { entityId, key, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n const def = await em.findOne(CustomFieldDef, where)\n if (!def) return NextResponse.json({ error: 'Not found' }, { status: 404 })\n\n const guard = await beginEntitiesMutationGuard({\n container,\n auth,\n req,\n resourceKind: FIELD_DEFINITION_RESOURCE_KIND,\n resourceId: def.id,\n operation: 'custom',\n mutationPayload: { entityId, key },\n })\n if (guard.blockedResponse) return guard.blockedResponse\n\n ;(def as any).deletedAt = null\n ;(def as any).isActive = true\n ;(def as any).updatedAt = new Date()\n em.persist(def)\n await em.flush()\n await guard.runAfterSuccess()\n await invalidateDefinitionsCache(cache, {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n entityIds: [entityId],\n })\n return NextResponse.json({ ok: true })\n}\n\nconst restoreDefinitionRequestSchema = z.object({\n entityId: z.string(),\n key: z.string(),\n})\n\nconst restoreDefinitionResponseSchema = z.object({\n ok: z.literal(true),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Entities',\n summary: 'Restore soft-deleted custom field definitions',\n methods: {\n POST: {\n summary: 'Restore definition',\n description: 'Reactivates a previously soft-deleted definition within the current tenant/org scope.',\n requestBody: {\n contentType: 'application/json',\n schema: restoreDefinitionRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Definition restored',\n schema: restoreDefinitionResponseSchema,\n },\n {\n status: 400,\n description: 'Missing entity id or key',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 404,\n description: 'Definition not found',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,7 +9,11 @@ import { isSystemEntitySelectable } from "@open-mercato/shared/lib/entities/syst
|
|
|
9
9
|
import { SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, isOrmBackedSystemEntityId } from "@open-mercato/shared/lib/data/engine";
|
|
10
10
|
import { enforceCommandOptimisticLock } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
|
|
11
11
|
import { isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
12
|
-
|
|
12
|
+
import {
|
|
13
|
+
beginEntitiesMutationGuard,
|
|
14
|
+
ENTITY_DEFINITION_RESOURCE_KIND
|
|
15
|
+
} from "./definitions.mutation-guard.js";
|
|
16
|
+
const CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND = ENTITY_DEFINITION_RESOURCE_KIND;
|
|
13
17
|
const metadata = {
|
|
14
18
|
GET: { requireAuth: true },
|
|
15
19
|
POST: { requireAuth: true, requireFeatures: ["entities.definitions.manage"] },
|
|
@@ -93,7 +97,8 @@ async function POST(req) {
|
|
|
93
97
|
return NextResponse.json({ error: "Validation failed", details: parsed.error.flatten() }, { status: 400 });
|
|
94
98
|
}
|
|
95
99
|
const input = parsed.data;
|
|
96
|
-
const
|
|
100
|
+
const container = await createRequestContainer();
|
|
101
|
+
const { resolve } = container;
|
|
97
102
|
const em = resolve("em");
|
|
98
103
|
if (isOrmBackedSystemEntityId(em, input.entityId)) {
|
|
99
104
|
return NextResponse.json(
|
|
@@ -118,6 +123,16 @@ async function POST(req) {
|
|
|
118
123
|
throw lockError;
|
|
119
124
|
}
|
|
120
125
|
}
|
|
126
|
+
const guard = await beginEntitiesMutationGuard({
|
|
127
|
+
container,
|
|
128
|
+
auth,
|
|
129
|
+
req,
|
|
130
|
+
resourceKind: ENTITY_DEFINITION_RESOURCE_KIND,
|
|
131
|
+
resourceId: ent ? ent.id : input.entityId,
|
|
132
|
+
operation: ent ? "update" : "create",
|
|
133
|
+
mutationPayload: input
|
|
134
|
+
});
|
|
135
|
+
if (guard.blockedResponse) return guard.blockedResponse;
|
|
121
136
|
if (!ent) ent = em.create(CustomEntity, { ...where, createdAt: /* @__PURE__ */ new Date() });
|
|
122
137
|
ent.label = input.label;
|
|
123
138
|
ent.description = input.description ?? null;
|
|
@@ -128,6 +143,7 @@ async function POST(req) {
|
|
|
128
143
|
ent.updatedAt = /* @__PURE__ */ new Date();
|
|
129
144
|
em.persist(ent);
|
|
130
145
|
await em.flush();
|
|
146
|
+
await guard.runAfterSuccess();
|
|
131
147
|
try {
|
|
132
148
|
const cache = (await createRequestContainer()).resolve("cache");
|
|
133
149
|
if (cache) {
|
|
@@ -148,16 +164,28 @@ async function DELETE(req) {
|
|
|
148
164
|
}
|
|
149
165
|
const entityId = body?.entityId;
|
|
150
166
|
if (!entityId) return NextResponse.json({ error: "entityId is required" }, { status: 400 });
|
|
151
|
-
const
|
|
167
|
+
const container = await createRequestContainer();
|
|
168
|
+
const { resolve } = container;
|
|
152
169
|
const em = resolve("em");
|
|
153
170
|
const where = { entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null };
|
|
154
171
|
const ent = await em.findOne(CustomEntity, where);
|
|
155
172
|
if (!ent) return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
173
|
+
const guard = await beginEntitiesMutationGuard({
|
|
174
|
+
container,
|
|
175
|
+
auth,
|
|
176
|
+
req,
|
|
177
|
+
resourceKind: ENTITY_DEFINITION_RESOURCE_KIND,
|
|
178
|
+
resourceId: ent.id,
|
|
179
|
+
operation: "delete",
|
|
180
|
+
mutationPayload: { entityId }
|
|
181
|
+
});
|
|
182
|
+
if (guard.blockedResponse) return guard.blockedResponse;
|
|
156
183
|
ent.isActive = false;
|
|
157
184
|
ent.updatedAt = /* @__PURE__ */ new Date();
|
|
158
185
|
ent.deletedAt = ent.deletedAt ?? /* @__PURE__ */ new Date();
|
|
159
186
|
em.persist(ent);
|
|
160
187
|
await em.flush();
|
|
188
|
+
await guard.runAfterSuccess();
|
|
161
189
|
try {
|
|
162
190
|
const cache = (await createRequestContainer()).resolve("cache");
|
|
163
191
|
if (cache) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/entities/api/entities.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { CustomEntity, CustomFieldDef } from '@open-mercato/core/modules/entities/data/entities'\nimport { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { upsertCustomEntitySchema } from '@open-mercato/core/modules/entities/data/validators'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { isSystemEntitySelectable } from '@open-mercato/shared/lib/entities/system-entities'\nimport { SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, isOrmBackedSystemEntityId } from '@open-mercato/shared/lib/data/engine'\nimport { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\n\nconst CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND = 'entities.entity'\n\nexport const metadata = {\n GET: { requireAuth: true },\n POST: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || (!auth.orgId && !auth.isSuperAdmin)) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n // Generated entities from code\n const AllEntities = getEntityIds()\n const generated: { entityId: string; source: 'code'; label: string }[] = []\n for (const modId of Object.keys(AllEntities)) {\n const entities = (AllEntities as any)[modId] as Record<string, string>\n for (const k of Object.keys(entities)) {\n const id = entities[k]\n if (!isSystemEntitySelectable(id)) continue\n generated.push({ entityId: id, source: 'code', label: id })\n }\n }\n\n // Custom user-defined entities (global/org/tenant scoped)\n const where: any = { isActive: true }\n where.$and = [\n { $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] },\n { $or: [ { tenantId: auth.tenantId ?? undefined as any }, { tenantId: null } ] },\n ]\n const customs = await em.find(CustomEntity as any, where as any, { orderBy: { entityId: 'asc' } as any })\n // Resolve overlay precedence: prefer organization/tenant-specific over global\n const customByEntityId = new Map<string, any>()\n for (const c of customs as any[]) {\n const specificity = (c.organizationId ? 2 : 0) + (c.tenantId ? 1 : 0)\n const prev = customByEntityId.get(c.entityId)\n const prevSpec = prev ? ((prev.organizationId ? 2 : 0) + (prev.tenantId ? 1 : 0)) : -1\n if (!prev || specificity > prevSpec) customByEntityId.set(c.entityId, c)\n }\n\n const custom = Array.from(customByEntityId.values())\n .filter((c) => isSystemEntitySelectable(c.entityId))\n .map((c) => ({\n entityId: c.entityId,\n source: 'custom' as const,\n label: c.label,\n description: c.description ?? undefined,\n labelField: (c as any).labelField ?? undefined,\n defaultEditor: (c as any).defaultEditor ?? undefined,\n showInSidebar: (c as any).showInSidebar ?? false,\n updatedAt: c.updatedAt instanceof Date ? c.updatedAt.toISOString() : (c.updatedAt ?? undefined),\n }))\n\n const byId = new Map<string, any>()\n for (const g of generated) byId.set(g.entityId, g)\n for (const cu of custom) {\n const existing = byId.get(cu.entityId)\n byId.set(cu.entityId, { ...existing, ...cu, source: existing?.source ?? cu.source })\n }\n\n // Count field definitions scoped to current tenant/org (same scoping as custom entities)\n const defsWhere: any = { isActive: true }\n defsWhere.$and = [\n //{ $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] }, // the entities and custom fields are defined per tenant\n { tenantId: auth.tenantId ?? undefined as any },\n ]\n const defs = await em.find(CustomFieldDef as any, defsWhere as any)\n // Count distinct field names (keys) per entityId\n const keySets = new Map<string, Set<string>>()\n for (const d of defs as any[]) {\n const eid = String(d.entityId)\n const k = String(d.key)\n if (!isSystemEntitySelectable(eid)) continue\n const set = keySets.get(eid) || new Set<string>()\n set.add(k)\n keySets.set(eid, set)\n }\n const counts: Record<string, number> = {}\n for (const [eid, set] of keySets.entries()) counts[eid] = set.size\n\n const items = Array.from(byId.values()).map((it: any) => ({ ...it, count: counts[it.entityId] || 0 }))\n return NextResponse.json({ items })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const parsed = upsertCustomEntitySchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Validation failed', details: parsed.error.flatten() }, { status: 400 })\n }\n const input = parsed.data\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n // A registration for a module-declared, table-backed system entity would flip\n // query-engine classification to doc storage for the whole entity type (#2939's\n // failure mode via another door) \u2014 refuse to create one.\n if (isOrmBackedSystemEntityId(em, input.entityId)) {\n return NextResponse.json(\n { error: 'System entities cannot be registered as custom entities', code: SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, entityId: input.entityId },\n { status: 400 },\n )\n }\n\n const where: any = { entityId: input.entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n let ent = await em.findOne(CustomEntity, where)\n if (ent) {\n try {\n enforceCommandOptimisticLock({\n resourceKind: CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND,\n resourceId: ent.id,\n current: ent.updatedAt ?? null,\n request: req,\n })\n } catch (lockError) {\n if (isCrudHttpError(lockError)) {\n return NextResponse.json(lockError.body, { status: lockError.status })\n }\n throw lockError\n }\n }\n if (!ent) ent = em.create(CustomEntity, { ...where, createdAt: new Date() })\n ent.label = input.label\n ent.description = input.description ?? null\n ent.isActive = input.isActive ?? true\n ent.labelField = input.labelField ?? ent.labelField ?? null\n ent.defaultEditor = input.defaultEditor ?? ent.defaultEditor ?? null\n ent.showInSidebar = input.showInSidebar ?? ent.showInSidebar ?? false\n ent.updatedAt = new Date()\n em.persist(ent)\n await em.flush()\n // Invalidate sidebar/nav cache for tenant scope (also when tenantId is null)\n try {\n const cache = (await createRequestContainer()).resolve('cache') as any\n if (cache) {\n await cache.deleteByTags([`nav:entities:${auth.tenantId || 'null'}`])\n }\n } catch {}\n return NextResponse.json({ ok: true, item: { id: ent.id, entityId: ent.entityId, label: ent.label, description: ent.description ?? undefined } })\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const entityId = body?.entityId\n if (!entityId) return NextResponse.json({ error: 'entityId is required' }, { status: 400 })\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n const where: any = { entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n const ent = await em.findOne(CustomEntity, where)\n if (!ent) return NextResponse.json({ error: 'Not found' }, { status: 404 })\n ent.isActive = false\n ent.updatedAt = new Date()\n ent.deletedAt = ent.deletedAt ?? new Date()\n em.persist(ent)\n await em.flush()\n // Invalidate sidebar/nav cache for tenant scope (also when tenantId is null)\n try {\n const cache = (await createRequestContainer()).resolve('cache') as any\n if (cache) {\n await cache.deleteByTags([`nav:entities:${auth.tenantId || 'null'}`])\n }\n } catch {}\n return NextResponse.json({ ok: true })\n}\n\nconst entitySummarySchema = z.object({\n entityId: z.string(),\n source: z.enum(['code', 'custom']),\n label: z.string(),\n description: z.string().optional(),\n labelField: z.string().optional(),\n defaultEditor: z.string().optional(),\n showInSidebar: z.boolean().optional(),\n updatedAt: z.string().optional(),\n count: z.number(),\n})\n\nconst entityListResponseSchema = z.object({\n items: z.array(entitySummarySchema),\n})\n\nconst deleteEntityRequestSchema = z.object({\n entityId: z.string(),\n})\n\nconst upsertCustomEntityResponseSchema = z.object({\n ok: z.literal(true),\n item: z.object({\n id: z.string().uuid(),\n entityId: z.string(),\n label: z.string(),\n description: z.string().optional(),\n }),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Entities',\n summary: 'Manage custom entities',\n methods: {\n GET: {\n summary: 'List available entities',\n description: 'Returns generated and custom entities scoped to the caller with field counts per entity.',\n responses: [\n {\n status: 200,\n description: 'List of entities',\n schema: entityListResponseSchema,\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n POST: {\n summary: 'Upsert custom entity',\n description: 'Creates or updates a tenant/org scoped custom entity definition.',\n requestBody: {\n contentType: 'application/json',\n schema: upsertCustomEntitySchema,\n },\n responses: [\n {\n status: 200,\n description: 'Entity saved',\n schema: upsertCustomEntityResponseSchema,\n },\n {\n status: 400,\n description: 'Validation error',\n schema: z.object({\n error: z.string(),\n details: z.any().optional(),\n }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n DELETE: {\n summary: 'Soft delete custom entity',\n description: 'Marks the specified custom entity inactive within the current scope.',\n requestBody: {\n contentType: 'application/json',\n schema: deleteEntityRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Entity deleted',\n schema: z.object({ ok: z.boolean() }),\n },\n {\n status: 400,\n description: 'Missing entity id',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 404,\n description: 'Entity not found in scope',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,cAAc,sBAAsB;AAC7C,SAAS,oBAAoB;AAC7B,SAAS,gCAAgC;AAEzC,SAAS,gCAAgC;AACzC,SAAS,oCAAoC,iCAAiC;AAC9E,SAAS,oCAAoC;AAC7C,SAAS,uBAAuB;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { CustomEntity, CustomFieldDef } from '@open-mercato/core/modules/entities/data/entities'\nimport { getEntityIds } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { upsertCustomEntitySchema } from '@open-mercato/core/modules/entities/data/validators'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { isSystemEntitySelectable } from '@open-mercato/shared/lib/entities/system-entities'\nimport { SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, isOrmBackedSystemEntityId } from '@open-mercato/shared/lib/data/engine'\nimport { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n beginEntitiesMutationGuard,\n ENTITY_DEFINITION_RESOURCE_KIND,\n} from './definitions.mutation-guard'\n\nconst CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND = ENTITY_DEFINITION_RESOURCE_KIND\n\nexport const metadata = {\n GET: { requireAuth: true },\n POST: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || (!auth.orgId && !auth.isSuperAdmin)) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n // Generated entities from code\n const AllEntities = getEntityIds()\n const generated: { entityId: string; source: 'code'; label: string }[] = []\n for (const modId of Object.keys(AllEntities)) {\n const entities = (AllEntities as any)[modId] as Record<string, string>\n for (const k of Object.keys(entities)) {\n const id = entities[k]\n if (!isSystemEntitySelectable(id)) continue\n generated.push({ entityId: id, source: 'code', label: id })\n }\n }\n\n // Custom user-defined entities (global/org/tenant scoped)\n const where: any = { isActive: true }\n where.$and = [\n { $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] },\n { $or: [ { tenantId: auth.tenantId ?? undefined as any }, { tenantId: null } ] },\n ]\n const customs = await em.find(CustomEntity as any, where as any, { orderBy: { entityId: 'asc' } as any })\n // Resolve overlay precedence: prefer organization/tenant-specific over global\n const customByEntityId = new Map<string, any>()\n for (const c of customs as any[]) {\n const specificity = (c.organizationId ? 2 : 0) + (c.tenantId ? 1 : 0)\n const prev = customByEntityId.get(c.entityId)\n const prevSpec = prev ? ((prev.organizationId ? 2 : 0) + (prev.tenantId ? 1 : 0)) : -1\n if (!prev || specificity > prevSpec) customByEntityId.set(c.entityId, c)\n }\n\n const custom = Array.from(customByEntityId.values())\n .filter((c) => isSystemEntitySelectable(c.entityId))\n .map((c) => ({\n entityId: c.entityId,\n source: 'custom' as const,\n label: c.label,\n description: c.description ?? undefined,\n labelField: (c as any).labelField ?? undefined,\n defaultEditor: (c as any).defaultEditor ?? undefined,\n showInSidebar: (c as any).showInSidebar ?? false,\n updatedAt: c.updatedAt instanceof Date ? c.updatedAt.toISOString() : (c.updatedAt ?? undefined),\n }))\n\n const byId = new Map<string, any>()\n for (const g of generated) byId.set(g.entityId, g)\n for (const cu of custom) {\n const existing = byId.get(cu.entityId)\n byId.set(cu.entityId, { ...existing, ...cu, source: existing?.source ?? cu.source })\n }\n\n // Count field definitions scoped to current tenant/org (same scoping as custom entities)\n const defsWhere: any = { isActive: true }\n defsWhere.$and = [\n //{ $or: [ { organizationId: auth.orgId ?? undefined as any }, { organizationId: null } ] }, // the entities and custom fields are defined per tenant\n { tenantId: auth.tenantId ?? undefined as any },\n ]\n const defs = await em.find(CustomFieldDef as any, defsWhere as any)\n // Count distinct field names (keys) per entityId\n const keySets = new Map<string, Set<string>>()\n for (const d of defs as any[]) {\n const eid = String(d.entityId)\n const k = String(d.key)\n if (!isSystemEntitySelectable(eid)) continue\n const set = keySets.get(eid) || new Set<string>()\n set.add(k)\n keySets.set(eid, set)\n }\n const counts: Record<string, number> = {}\n for (const [eid, set] of keySets.entries()) counts[eid] = set.size\n\n const items = Array.from(byId.values()).map((it: any) => ({ ...it, count: counts[it.entityId] || 0 }))\n return NextResponse.json({ items })\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const parsed = upsertCustomEntitySchema.safeParse(body)\n if (!parsed.success) {\n return NextResponse.json({ error: 'Validation failed', details: parsed.error.flatten() }, { status: 400 })\n }\n const input = parsed.data\n\n const container = await createRequestContainer()\n const { resolve } = container\n const em = resolve('em') as any\n\n // A registration for a module-declared, table-backed system entity would flip\n // query-engine classification to doc storage for the whole entity type (#2939's\n // failure mode via another door) \u2014 refuse to create one.\n if (isOrmBackedSystemEntityId(em, input.entityId)) {\n return NextResponse.json(\n { error: 'System entities cannot be registered as custom entities', code: SYSTEM_ENTITY_RECORDS_BLOCKED_CODE, entityId: input.entityId },\n { status: 400 },\n )\n }\n\n const where: any = { entityId: input.entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n let ent = await em.findOne(CustomEntity, where)\n if (ent) {\n try {\n enforceCommandOptimisticLock({\n resourceKind: CUSTOM_ENTITY_DEFINITION_RESOURCE_KIND,\n resourceId: ent.id,\n current: ent.updatedAt ?? null,\n request: req,\n })\n } catch (lockError) {\n if (isCrudHttpError(lockError)) {\n return NextResponse.json(lockError.body, { status: lockError.status })\n }\n throw lockError\n }\n }\n\n const guard = await beginEntitiesMutationGuard({\n container,\n auth,\n req,\n resourceKind: ENTITY_DEFINITION_RESOURCE_KIND,\n resourceId: ent ? ent.id : input.entityId,\n operation: ent ? 'update' : 'create',\n mutationPayload: input as unknown as Record<string, unknown>,\n })\n if (guard.blockedResponse) return guard.blockedResponse\n\n if (!ent) ent = em.create(CustomEntity, { ...where, createdAt: new Date() })\n ent.label = input.label\n ent.description = input.description ?? null\n ent.isActive = input.isActive ?? true\n ent.labelField = input.labelField ?? ent.labelField ?? null\n ent.defaultEditor = input.defaultEditor ?? ent.defaultEditor ?? null\n ent.showInSidebar = input.showInSidebar ?? ent.showInSidebar ?? false\n ent.updatedAt = new Date()\n em.persist(ent)\n await em.flush()\n await guard.runAfterSuccess()\n // Invalidate sidebar/nav cache for tenant scope (also when tenantId is null)\n try {\n const cache = (await createRequestContainer()).resolve('cache') as any\n if (cache) {\n await cache.deleteByTags([`nav:entities:${auth.tenantId || 'null'}`])\n }\n } catch {}\n return NextResponse.json({ ok: true, item: { id: ent.id, entityId: ent.entityId, label: ent.label, description: ent.description ?? undefined } })\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n let body: any\n try { body = await req.json() } catch { return NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) }\n const entityId = body?.entityId\n if (!entityId) return NextResponse.json({ error: 'entityId is required' }, { status: 400 })\n\n const container = await createRequestContainer()\n const { resolve } = container\n const em = resolve('em') as any\n\n const where: any = { entityId, organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n const ent = await em.findOne(CustomEntity, where)\n if (!ent) return NextResponse.json({ error: 'Not found' }, { status: 404 })\n\n const guard = await beginEntitiesMutationGuard({\n container,\n auth,\n req,\n resourceKind: ENTITY_DEFINITION_RESOURCE_KIND,\n resourceId: ent.id,\n operation: 'delete',\n mutationPayload: { entityId },\n })\n if (guard.blockedResponse) return guard.blockedResponse\n\n ent.isActive = false\n ent.updatedAt = new Date()\n ent.deletedAt = ent.deletedAt ?? new Date()\n em.persist(ent)\n await em.flush()\n await guard.runAfterSuccess()\n // Invalidate sidebar/nav cache for tenant scope (also when tenantId is null)\n try {\n const cache = (await createRequestContainer()).resolve('cache') as any\n if (cache) {\n await cache.deleteByTags([`nav:entities:${auth.tenantId || 'null'}`])\n }\n } catch {}\n return NextResponse.json({ ok: true })\n}\n\nconst entitySummarySchema = z.object({\n entityId: z.string(),\n source: z.enum(['code', 'custom']),\n label: z.string(),\n description: z.string().optional(),\n labelField: z.string().optional(),\n defaultEditor: z.string().optional(),\n showInSidebar: z.boolean().optional(),\n updatedAt: z.string().optional(),\n count: z.number(),\n})\n\nconst entityListResponseSchema = z.object({\n items: z.array(entitySummarySchema),\n})\n\nconst deleteEntityRequestSchema = z.object({\n entityId: z.string(),\n})\n\nconst upsertCustomEntityResponseSchema = z.object({\n ok: z.literal(true),\n item: z.object({\n id: z.string().uuid(),\n entityId: z.string(),\n label: z.string(),\n description: z.string().optional(),\n }),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Entities',\n summary: 'Manage custom entities',\n methods: {\n GET: {\n summary: 'List available entities',\n description: 'Returns generated and custom entities scoped to the caller with field counts per entity.',\n responses: [\n {\n status: 200,\n description: 'List of entities',\n schema: entityListResponseSchema,\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n POST: {\n summary: 'Upsert custom entity',\n description: 'Creates or updates a tenant/org scoped custom entity definition.',\n requestBody: {\n contentType: 'application/json',\n schema: upsertCustomEntitySchema,\n },\n responses: [\n {\n status: 200,\n description: 'Entity saved',\n schema: upsertCustomEntityResponseSchema,\n },\n {\n status: 400,\n description: 'Validation error',\n schema: z.object({\n error: z.string(),\n details: z.any().optional(),\n }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n DELETE: {\n summary: 'Soft delete custom entity',\n description: 'Marks the specified custom entity inactive within the current scope.',\n requestBody: {\n contentType: 'application/json',\n schema: deleteEntityRequestSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Entity deleted',\n schema: z.object({ ok: z.boolean() }),\n },\n {\n status: 400,\n description: 'Missing entity id',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 401,\n description: 'Missing authentication',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 404,\n description: 'Entity not found in scope',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -468,6 +468,24 @@ async function DELETE(req) {
|
|
|
468
468
|
if (entityKind === "system") return systemEntityRecordsRejection(entityId);
|
|
469
469
|
const isCustomEntity = entityKind === "custom";
|
|
470
470
|
await assertEntityAclForRequest({ auth, entityId, action: "manage", isCustomEntity, rbac });
|
|
471
|
+
try {
|
|
472
|
+
const currentUpdatedAt = await readCustomEntityRecordUpdatedAt(em, {
|
|
473
|
+
entityType: entityId,
|
|
474
|
+
entityId: recordId,
|
|
475
|
+
organizationId: targetOrgId
|
|
476
|
+
});
|
|
477
|
+
enforceCommandOptimisticLock({
|
|
478
|
+
resourceKind: CUSTOM_ENTITY_RECORD_RESOURCE_KIND,
|
|
479
|
+
resourceId: recordId,
|
|
480
|
+
current: currentUpdatedAt,
|
|
481
|
+
request: req
|
|
482
|
+
});
|
|
483
|
+
} catch (lockError) {
|
|
484
|
+
if (isCrudHttpError(lockError)) {
|
|
485
|
+
return NextResponse.json(lockError.body, { status: lockError.status });
|
|
486
|
+
}
|
|
487
|
+
throw lockError;
|
|
488
|
+
}
|
|
471
489
|
await de.deleteCustomEntityRecord({ entityId, recordId, organizationId: targetOrgId, tenantId: auth.tenantId, soft: true });
|
|
472
490
|
return NextResponse.json({ ok: true });
|
|
473
491
|
} catch (e) {
|