@open-mercato/core 0.6.6-develop.6345.1.b236ea3d4b → 0.6.6-develop.6351.1.c140d703c9
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/entities/api/definitions.batch.js +68 -21
- package/dist/modules/entities/api/definitions.batch.js.map +2 -2
- package/dist/modules/entities/api/definitions.js +57 -20
- package/dist/modules/entities/api/definitions.js.map +2 -2
- package/dist/modules/entities/api/definitions.restore.js +5 -3
- package/dist/modules/entities/api/definitions.restore.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/page.js +21 -10
- package/dist/modules/entities/backend/entities/user/[entityId]/page.js.map +2 -2
- package/dist/modules/entities/lib/definition-scope.js +102 -0
- package/dist/modules/entities/lib/definition-scope.js.map +7 -0
- package/package.json +7 -7
- package/src/modules/entities/api/definitions.batch.ts +69 -20
- package/src/modules/entities/api/definitions.restore.ts +5 -3
- package/src/modules/entities/api/definitions.ts +65 -21
- package/src/modules/entities/backend/entities/user/[entityId]/page.tsx +30 -13
- package/src/modules/entities/lib/definition-scope.ts +172 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -10,6 +10,14 @@ import {
|
|
|
10
10
|
beginEntitiesMutationGuard,
|
|
11
11
|
FIELD_DEFINITION_RESOURCE_KIND
|
|
12
12
|
} from "./definitions.mutation-guard.js";
|
|
13
|
+
import {
|
|
14
|
+
createExactDefinitionWhere,
|
|
15
|
+
createScopedDefinitionTombstone,
|
|
16
|
+
createVisibleDefinitionWhere,
|
|
17
|
+
markDefinitionTombstoned,
|
|
18
|
+
resolveDefinitionMutationScope,
|
|
19
|
+
selectVisibleDefinitionWinner
|
|
20
|
+
} from "../lib/definition-scope.js";
|
|
13
21
|
const metadata = {
|
|
14
22
|
POST: { requireAuth: true, requireFeatures: ["entities.definitions.manage"] }
|
|
15
23
|
};
|
|
@@ -49,6 +57,7 @@ async function POST(req) {
|
|
|
49
57
|
if (!parsed.success) return NextResponse.json({ error: "Validation failed", details: parsed.error.flatten() }, { status: 400 });
|
|
50
58
|
const { entityId, definitions, fieldsets, singleFieldsetPerRecord } = parsed.data;
|
|
51
59
|
const container = await createRequestContainer();
|
|
60
|
+
const scope = await resolveDefinitionMutationScope({ auth, container, request: req });
|
|
52
61
|
const { resolve } = container;
|
|
53
62
|
const em = resolve("em");
|
|
54
63
|
let cache;
|
|
@@ -72,26 +81,31 @@ async function POST(req) {
|
|
|
72
81
|
const keys = definitions.map((d) => d.key);
|
|
73
82
|
if (keys.length > 0) {
|
|
74
83
|
const existingDefs = await em.find(CustomFieldDef, {
|
|
75
|
-
entityId,
|
|
76
|
-
key: { $in: keys },
|
|
77
|
-
organizationId: auth.orgId ?? null,
|
|
78
|
-
tenantId: auth.tenantId ?? null
|
|
84
|
+
...createExactDefinitionWhere(entityId, { $in: keys }, scope)
|
|
79
85
|
});
|
|
80
86
|
for (const existing of existingDefs) defByKey.set(existing.key, existing);
|
|
81
87
|
}
|
|
82
|
-
|
|
83
|
-
|
|
88
|
+
const inheritedByKey = /* @__PURE__ */ new Map();
|
|
89
|
+
const inactiveKeys = definitions.filter((d) => d.isActive === false).map((d) => d.key);
|
|
90
|
+
if (inactiveKeys.length > 0) {
|
|
91
|
+
const visibleDefs = await em.find(CustomFieldDef, createVisibleDefinitionWhere(
|
|
84
92
|
entityId,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
{ $in: inactiveKeys },
|
|
94
|
+
scope,
|
|
95
|
+
{ deletedAt: null, isActive: true }
|
|
96
|
+
));
|
|
97
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
98
|
+
for (const visible of visibleDefs) {
|
|
99
|
+
if (!grouped.has(visible.key)) grouped.set(visible.key, []);
|
|
100
|
+
grouped.get(visible.key).push(visible);
|
|
93
101
|
}
|
|
94
|
-
|
|
102
|
+
for (const [key, group] of grouped) {
|
|
103
|
+
inheritedByKey.set(key, selectVisibleDefinitionWinner(group));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const [idx, d] of definitions.entries()) {
|
|
107
|
+
const where = createExactDefinitionWhere(entityId, d.key, scope);
|
|
108
|
+
let def = defByKey.get(d.key);
|
|
95
109
|
const inCfg = d.configJson ?? {};
|
|
96
110
|
const cfg = { ...inCfg };
|
|
97
111
|
if (cfg.label == null || String(cfg.label).trim() === "") cfg.label = d.key;
|
|
@@ -99,15 +113,48 @@ async function POST(req) {
|
|
|
99
113
|
if (cfg.listVisible === void 0) cfg.listVisible = true;
|
|
100
114
|
if (d.kind === "multiline" && (cfg.editor == null || String(cfg.editor).trim() === "")) cfg.editor = "markdown";
|
|
101
115
|
cfg.priority = idx;
|
|
116
|
+
if (d.isActive === false) {
|
|
117
|
+
const now = /* @__PURE__ */ new Date();
|
|
118
|
+
const inherited = inheritedByKey.get(d.key);
|
|
119
|
+
if (!def) {
|
|
120
|
+
def = createScopedDefinitionTombstone(
|
|
121
|
+
em,
|
|
122
|
+
{
|
|
123
|
+
entityId,
|
|
124
|
+
key: d.key,
|
|
125
|
+
kind: d.kind,
|
|
126
|
+
configJson: inherited?.configJson ?? cfg
|
|
127
|
+
},
|
|
128
|
+
scope,
|
|
129
|
+
now
|
|
130
|
+
);
|
|
131
|
+
defByKey.set(d.key, def);
|
|
132
|
+
} else {
|
|
133
|
+
markDefinitionTombstoned(def, now);
|
|
134
|
+
}
|
|
135
|
+
def.kind = d.kind;
|
|
136
|
+
def.configJson = cfg;
|
|
137
|
+
def.isActive = false;
|
|
138
|
+
def.deletedAt = def.deletedAt ?? now;
|
|
139
|
+
def.updatedAt = now;
|
|
140
|
+
em.persist(def);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (!def) {
|
|
144
|
+
def = em.create(CustomFieldDef, { ...where, createdAt: /* @__PURE__ */ new Date() });
|
|
145
|
+
defByKey.set(d.key, def);
|
|
146
|
+
}
|
|
147
|
+
def.kind = d.kind;
|
|
102
148
|
def.configJson = cfg;
|
|
103
|
-
def.isActive =
|
|
149
|
+
def.isActive = true;
|
|
150
|
+
def.deletedAt = null;
|
|
104
151
|
def.updatedAt = /* @__PURE__ */ new Date();
|
|
105
152
|
em.persist(def);
|
|
106
153
|
}
|
|
107
154
|
if (fieldsets !== void 0 || singleFieldsetPerRecord !== void 0) {
|
|
108
|
-
const
|
|
109
|
-
let cfg = await em.findOne(CustomFieldEntityConfig,
|
|
110
|
-
if (!cfg) cfg = em.create(CustomFieldEntityConfig, { ...
|
|
155
|
+
const entityConfigScope = { entityId, organizationId: scope.organizationId, tenantId: scope.tenantId };
|
|
156
|
+
let cfg = await em.findOne(CustomFieldEntityConfig, entityConfigScope);
|
|
157
|
+
if (!cfg) cfg = em.create(CustomFieldEntityConfig, { ...entityConfigScope, createdAt: /* @__PURE__ */ new Date() });
|
|
111
158
|
const existing = normalizeEntityFieldsetConfig(cfg.configJson ?? {});
|
|
112
159
|
const patch = mergeEntityFieldsetConfig(existing, {
|
|
113
160
|
fieldsets: fieldsets !== void 0 ? cloneFieldsets(fieldsets) ?? [] : void 0,
|
|
@@ -132,8 +179,8 @@ async function POST(req) {
|
|
|
132
179
|
}
|
|
133
180
|
await guard.runAfterSuccess();
|
|
134
181
|
await invalidateDefinitionsCache(cache, {
|
|
135
|
-
tenantId:
|
|
136
|
-
organizationId:
|
|
182
|
+
tenantId: scope.tenantId,
|
|
183
|
+
organizationId: scope.organizationId,
|
|
137
184
|
entityIds: [entityId]
|
|
138
185
|
});
|
|
139
186
|
return NextResponse.json({ ok: true });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/entities/api/definitions.batch.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\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, CustomFieldEntityConfig } from '@open-mercato/core/modules/entities/data/entities'\nimport { customFieldEntityConfigSchema, upsertCustomFieldDefSchema } from '@open-mercato/core/modules/entities/data/validators'\nimport { z } from 'zod'\nimport { invalidateDefinitionsCache } from './definitions.cache'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { mergeEntityFieldsetConfig, normalizeEntityFieldsetConfig } from '../lib/fieldsets'\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\nconst MAX_DEFINITIONS_PER_BATCH = 1000\n\nconst batchSchema = z\n .object({\n entityId: z.string().regex(/^[a-z0-9_]+:[a-z0-9_]+$/),\n definitions: z\n .array(\n upsertCustomFieldDefSchema\n .omit({ entityId: true })\n .extend({\n configJson: z.any().optional(),\n })\n )\n .max(MAX_DEFINITIONS_PER_BATCH),\n })\n .extend(customFieldEntityConfigSchema.shape)\n\ntype IncomingFieldset = z.infer<typeof customFieldEntityConfigSchema>['fieldsets']\n\nfunction cloneFieldsets(fieldsets?: IncomingFieldset): IncomingFieldset {\n if (!Array.isArray(fieldsets)) return undefined\n return fieldsets.map((fieldset) => ({\n code: fieldset.code,\n label: fieldset.label,\n icon: fieldset.icon,\n description: fieldset.description,\n groups: Array.isArray(fieldset.groups)\n ? fieldset.groups.map((group) => ({\n code: group.code,\n title: group.title,\n hint: group.hint,\n }))\n : undefined,\n }))\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\n const parsed = batchSchema.safeParse(body)\n if (!parsed.success) return NextResponse.json({ error: 'Validation failed', details: parsed.error.flatten() }, { status: 400 })\n const { entityId, definitions, fieldsets, singleFieldsetPerRecord } = parsed.data\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 guard = await beginEntitiesMutationGuard({\n container,\n auth,\n req,\n resourceKind: FIELD_DEFINITION_RESOURCE_KIND,\n resourceId: entityId,\n operation: 'custom',\n mutationPayload: { entityId, definitionCount: definitions.length },\n })\n if (guard.blockedResponse) return guard.blockedResponse\n\n await em.begin()\n try {\n // Prefetch every existing definition for this entity in a single query, then index\n // by key so the per-definition loop resolves create/update without round trips.\n const defByKey = new Map<string, any>()\n const keys = definitions.map((d) => d.key)\n if (keys.length > 0) {\n const existingDefs = await em.find(CustomFieldDef, {\n entityId
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAE7B,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,gBAAgB,+BAA+B;AACxD,SAAS,+BAA+B,kCAAkC;AAC1E,SAAS,SAAS;AAClB,SAAS,kCAAkC;AAE3C,SAAS,2BAA2B,qCAAqC;AACzE;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEA,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,6BAA6B,EAAE;AAC9E;AAEA,MAAM,4BAA4B;AAElC,MAAM,cAAc,EACjB,OAAO;AAAA,EACN,UAAU,EAAE,OAAO,EAAE,MAAM,yBAAyB;AAAA,EACpD,aAAa,EACV;AAAA,IACC,2BACG,KAAK,EAAE,UAAU,KAAK,CAAC,EACvB,OAAO;AAAA,MACN,YAAY,EAAE,IAAI,EAAE,SAAS;AAAA,IAC/B,CAAC;AAAA,EACL,EACC,IAAI,yBAAyB;AAClC,CAAC,EACA,OAAO,8BAA8B,KAAK;AAI7C,SAAS,eAAe,WAAgD;AACtE,MAAI,CAAC,MAAM,QAAQ,SAAS,EAAG,QAAO;AACtC,SAAO,UAAU,IAAI,CAAC,cAAc;AAAA,IAClC,MAAM,SAAS;AAAA,IACf,OAAO,SAAS;AAAA,IAChB,MAAM,SAAS;AAAA,IACf,aAAa,SAAS;AAAA,IACtB,QAAQ,MAAM,QAAQ,SAAS,MAAM,IACjC,SAAS,OAAO,IAAI,CAAC,WAAW;AAAA,MAC9B,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,MACb,MAAM,MAAM;AAAA,IACd,EAAE,IACF;AAAA,EACN,EAAE;AACJ;AAEA,eAAsB,KAAK,KAAc;AACvC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,QAAQ,CAAC,KAAK,MAAO,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAC7F,MAAI;AACJ,MAAI;AAAE,WAAO,MAAM,IAAI,KAAK;AAAA,EAAE,QAAQ;AAAE,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAAE;AAE7G,QAAM,SAAS,YAAY,UAAU,IAAI;AACzC,MAAI,CAAC,OAAO,QAAS,QAAO,aAAa,KAAK,EAAE,OAAO,qBAAqB,SAAS,OAAO,MAAM,QAAQ,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAC9H,QAAM,EAAE,UAAU,aAAa,WAAW,wBAAwB,IAAI,OAAO;AAE7E,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,EAAE,QAAQ,IAAI;AACpB,QAAM,KAAK,QAAQ,IAAI;AACvB,MAAI;AACJ,MAAI;AACF,YAAQ,QAAQ,OAAO;AAAA,EACzB,QAAQ;AAAA,EAAC;AAET,QAAM,QAAQ,MAAM,2BAA2B;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,iBAAiB,EAAE,UAAU,iBAAiB,YAAY,OAAO;AAAA,EACnE,CAAC;AACD,MAAI,MAAM,gBAAiB,QAAO,MAAM;AAExC,QAAM,GAAG,MAAM;AACf,MAAI;AAGF,UAAM,WAAW,oBAAI,IAAiB;AACtC,UAAM,OAAO,YAAY,IAAI,CAAC,MAAM,EAAE,GAAG;AACzC,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,eAAe,MAAM,GAAG,KAAK,gBAAgB;AAAA,QACjD
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\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, CustomFieldEntityConfig } from '@open-mercato/core/modules/entities/data/entities'\nimport { customFieldEntityConfigSchema, upsertCustomFieldDefSchema } from '@open-mercato/core/modules/entities/data/validators'\nimport { z } from 'zod'\nimport { invalidateDefinitionsCache } from './definitions.cache'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { mergeEntityFieldsetConfig, normalizeEntityFieldsetConfig } from '../lib/fieldsets'\nimport {\n beginEntitiesMutationGuard,\n FIELD_DEFINITION_RESOURCE_KIND,\n} from './definitions.mutation-guard'\nimport {\n createExactDefinitionWhere,\n createScopedDefinitionTombstone,\n createVisibleDefinitionWhere,\n markDefinitionTombstoned,\n resolveDefinitionMutationScope,\n selectVisibleDefinitionWinner,\n} from '../lib/definition-scope'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['entities.definitions.manage'] },\n}\n\nconst MAX_DEFINITIONS_PER_BATCH = 1000\n\nconst batchSchema = z\n .object({\n entityId: z.string().regex(/^[a-z0-9_]+:[a-z0-9_]+$/),\n definitions: z\n .array(\n upsertCustomFieldDefSchema\n .omit({ entityId: true })\n .extend({\n configJson: z.any().optional(),\n })\n )\n .max(MAX_DEFINITIONS_PER_BATCH),\n })\n .extend(customFieldEntityConfigSchema.shape)\n\ntype IncomingFieldset = z.infer<typeof customFieldEntityConfigSchema>['fieldsets']\n\nfunction cloneFieldsets(fieldsets?: IncomingFieldset): IncomingFieldset {\n if (!Array.isArray(fieldsets)) return undefined\n return fieldsets.map((fieldset) => ({\n code: fieldset.code,\n label: fieldset.label,\n icon: fieldset.icon,\n description: fieldset.description,\n groups: Array.isArray(fieldset.groups)\n ? fieldset.groups.map((group) => ({\n code: group.code,\n title: group.title,\n hint: group.hint,\n }))\n : undefined,\n }))\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\n const parsed = batchSchema.safeParse(body)\n if (!parsed.success) return NextResponse.json({ error: 'Validation failed', details: parsed.error.flatten() }, { status: 400 })\n const { entityId, definitions, fieldsets, singleFieldsetPerRecord } = parsed.data\n\n const container = await createRequestContainer()\n const scope = await resolveDefinitionMutationScope({ auth, container, request: req })\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 guard = await beginEntitiesMutationGuard({\n container,\n auth,\n req,\n resourceKind: FIELD_DEFINITION_RESOURCE_KIND,\n resourceId: entityId,\n operation: 'custom',\n mutationPayload: { entityId, definitionCount: definitions.length },\n })\n if (guard.blockedResponse) return guard.blockedResponse\n\n await em.begin()\n try {\n // Prefetch every existing definition for this entity in a single query, then index\n // by key so the per-definition loop resolves create/update without round trips.\n const defByKey = new Map<string, any>()\n const keys = definitions.map((d) => d.key)\n if (keys.length > 0) {\n const existingDefs = await em.find(CustomFieldDef, {\n ...createExactDefinitionWhere(entityId, { $in: keys }, scope),\n })\n for (const existing of existingDefs) defByKey.set(existing.key, existing)\n }\n\n const inheritedByKey = new Map<string, any>()\n const inactiveKeys = definitions.filter((d) => d.isActive === false).map((d) => d.key)\n if (inactiveKeys.length > 0) {\n const visibleDefs = await em.find(CustomFieldDef, createVisibleDefinitionWhere(\n entityId,\n { $in: inactiveKeys },\n scope,\n { deletedAt: null, isActive: true },\n ))\n const grouped = new Map<string, any[]>()\n for (const visible of visibleDefs) {\n if (!grouped.has(visible.key)) grouped.set(visible.key, [])\n grouped.get(visible.key)!.push(visible)\n }\n for (const [key, group] of grouped) {\n inheritedByKey.set(key, selectVisibleDefinitionWinner(group))\n }\n }\n\n for (const [idx, d] of definitions.entries()) {\n const where: any = createExactDefinitionWhere(entityId, d.key, scope)\n let def = defByKey.get(d.key)\n\n const inCfg = (d as any).configJson ?? {}\n const cfg: Record<string, any> = { ...inCfg }\n if (cfg.label == null || String(cfg.label).trim() === '') cfg.label = d.key\n if (cfg.formEditable === undefined) cfg.formEditable = true\n if (cfg.listVisible === undefined) cfg.listVisible = true\n if (d.kind === 'multiline' && (cfg.editor == null || String(cfg.editor).trim() === '')) cfg.editor = 'markdown'\n cfg.priority = idx\n\n if (d.isActive === false) {\n const now = new Date()\n const inherited = inheritedByKey.get(d.key)\n if (!def) {\n def = createScopedDefinitionTombstone(\n em,\n {\n entityId,\n key: d.key,\n kind: d.kind,\n configJson: inherited?.configJson ?? cfg,\n },\n scope,\n now,\n )\n defByKey.set(d.key, def)\n } else {\n markDefinitionTombstoned(def, now)\n }\n def.kind = d.kind\n def.configJson = cfg\n def.isActive = false\n def.deletedAt = def.deletedAt ?? now\n def.updatedAt = now\n em.persist(def)\n continue\n }\n\n if (!def) {\n def = em.create(CustomFieldDef, { ...where, createdAt: new Date() })\n defByKey.set(d.key, def)\n }\n def.kind = d.kind\n def.configJson = cfg\n def.isActive = true\n def.deletedAt = null\n def.updatedAt = new Date()\n em.persist(def)\n }\n if (fieldsets !== undefined || singleFieldsetPerRecord !== undefined) {\n const entityConfigScope: any = { entityId, organizationId: scope.organizationId, tenantId: scope.tenantId }\n let cfg = await em.findOne(CustomFieldEntityConfig, entityConfigScope)\n if (!cfg) cfg = em.create(CustomFieldEntityConfig, { ...entityConfigScope, createdAt: new Date() })\n const existing = normalizeEntityFieldsetConfig(cfg.configJson ?? {})\n const patch = mergeEntityFieldsetConfig(existing, {\n fieldsets: fieldsets !== undefined ? cloneFieldsets(fieldsets) ?? [] : undefined,\n singleFieldsetPerRecord,\n })\n cfg.configJson = {\n fieldsets: patch.fieldsets,\n singleFieldsetPerRecord: patch.singleFieldsetPerRecord,\n }\n cfg.updatedAt = new Date()\n cfg.isActive = true\n em.persist(cfg)\n }\n await em.flush()\n await em.commit()\n } catch (e) {\n try { await em.rollback() } catch {}\n return NextResponse.json({ error: 'Failed to save definitions batch' }, { status: 500 })\n }\n\n await guard.runAfterSuccess()\n\n await invalidateDefinitionsCache(cache, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n entityIds: [entityId],\n })\n\n return NextResponse.json({ ok: true })\n}\n\nconst batchResponseSchema = z.object({\n ok: z.literal(true),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Entities',\n summary: 'Batch upsert custom field definitions',\n methods: {\n POST: {\n summary: 'Save multiple custom field definitions',\n description: 'Creates or updates multiple definitions for a single entity in one transaction.',\n requestBody: {\n contentType: 'application/json',\n schema: batchSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Definitions saved',\n schema: batchResponseSchema,\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 status: 500,\n description: 'Unexpected failure',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAE7B,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,gBAAgB,+BAA+B;AACxD,SAAS,+BAA+B,kCAAkC;AAC1E,SAAS,SAAS;AAClB,SAAS,kCAAkC;AAE3C,SAAS,2BAA2B,qCAAqC;AACzE;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,6BAA6B,EAAE;AAC9E;AAEA,MAAM,4BAA4B;AAElC,MAAM,cAAc,EACjB,OAAO;AAAA,EACN,UAAU,EAAE,OAAO,EAAE,MAAM,yBAAyB;AAAA,EACpD,aAAa,EACV;AAAA,IACC,2BACG,KAAK,EAAE,UAAU,KAAK,CAAC,EACvB,OAAO;AAAA,MACN,YAAY,EAAE,IAAI,EAAE,SAAS;AAAA,IAC/B,CAAC;AAAA,EACL,EACC,IAAI,yBAAyB;AAClC,CAAC,EACA,OAAO,8BAA8B,KAAK;AAI7C,SAAS,eAAe,WAAgD;AACtE,MAAI,CAAC,MAAM,QAAQ,SAAS,EAAG,QAAO;AACtC,SAAO,UAAU,IAAI,CAAC,cAAc;AAAA,IAClC,MAAM,SAAS;AAAA,IACf,OAAO,SAAS;AAAA,IAChB,MAAM,SAAS;AAAA,IACf,aAAa,SAAS;AAAA,IACtB,QAAQ,MAAM,QAAQ,SAAS,MAAM,IACjC,SAAS,OAAO,IAAI,CAAC,WAAW;AAAA,MAC9B,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,MACb,MAAM,MAAM;AAAA,IACd,EAAE,IACF;AAAA,EACN,EAAE;AACJ;AAEA,eAAsB,KAAK,KAAc;AACvC,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,QAAQ,CAAC,KAAK,MAAO,QAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAC7F,MAAI;AACJ,MAAI;AAAE,WAAO,MAAM,IAAI,KAAK;AAAA,EAAE,QAAQ;AAAE,WAAO,aAAa,KAAK,EAAE,OAAO,eAAe,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAAE;AAE7G,QAAM,SAAS,YAAY,UAAU,IAAI;AACzC,MAAI,CAAC,OAAO,QAAS,QAAO,aAAa,KAAK,EAAE,OAAO,qBAAqB,SAAS,OAAO,MAAM,QAAQ,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAC9H,QAAM,EAAE,UAAU,aAAa,WAAW,wBAAwB,IAAI,OAAO;AAE7E,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,QAAQ,MAAM,+BAA+B,EAAE,MAAM,WAAW,SAAS,IAAI,CAAC;AACpF,QAAM,EAAE,QAAQ,IAAI;AACpB,QAAM,KAAK,QAAQ,IAAI;AACvB,MAAI;AACJ,MAAI;AACF,YAAQ,QAAQ,OAAO;AAAA,EACzB,QAAQ;AAAA,EAAC;AAET,QAAM,QAAQ,MAAM,2BAA2B;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,iBAAiB,EAAE,UAAU,iBAAiB,YAAY,OAAO;AAAA,EACnE,CAAC;AACD,MAAI,MAAM,gBAAiB,QAAO,MAAM;AAExC,QAAM,GAAG,MAAM;AACf,MAAI;AAGF,UAAM,WAAW,oBAAI,IAAiB;AACtC,UAAM,OAAO,YAAY,IAAI,CAAC,MAAM,EAAE,GAAG;AACzC,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,eAAe,MAAM,GAAG,KAAK,gBAAgB;AAAA,QACjD,GAAG,2BAA2B,UAAU,EAAE,KAAK,KAAK,GAAG,KAAK;AAAA,MAC9D,CAAC;AACD,iBAAW,YAAY,aAAc,UAAS,IAAI,SAAS,KAAK,QAAQ;AAAA,IAC1E;AAEA,UAAM,iBAAiB,oBAAI,IAAiB;AAC5C,UAAM,eAAe,YAAY,OAAO,CAAC,MAAM,EAAE,aAAa,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG;AACrF,QAAI,aAAa,SAAS,GAAG;AAC3B,YAAM,cAAc,MAAM,GAAG,KAAK,gBAAgB;AAAA,QAChD;AAAA,QACA,EAAE,KAAK,aAAa;AAAA,QACpB;AAAA,QACA,EAAE,WAAW,MAAM,UAAU,KAAK;AAAA,MACpC,CAAC;AACD,YAAM,UAAU,oBAAI,IAAmB;AACvC,iBAAW,WAAW,aAAa;AACjC,YAAI,CAAC,QAAQ,IAAI,QAAQ,GAAG,EAAG,SAAQ,IAAI,QAAQ,KAAK,CAAC,CAAC;AAC1D,gBAAQ,IAAI,QAAQ,GAAG,EAAG,KAAK,OAAO;AAAA,MACxC;AACA,iBAAW,CAAC,KAAK,KAAK,KAAK,SAAS;AAClC,uBAAe,IAAI,KAAK,8BAA8B,KAAK,CAAC;AAAA,MAC9D;AAAA,IACF;AAEA,eAAW,CAAC,KAAK,CAAC,KAAK,YAAY,QAAQ,GAAG;AAC5C,YAAM,QAAa,2BAA2B,UAAU,EAAE,KAAK,KAAK;AACpE,UAAI,MAAM,SAAS,IAAI,EAAE,GAAG;AAE5B,YAAM,QAAS,EAAU,cAAc,CAAC;AACxC,YAAM,MAA2B,EAAE,GAAG,MAAM;AAC5C,UAAI,IAAI,SAAS,QAAQ,OAAO,IAAI,KAAK,EAAE,KAAK,MAAM,GAAI,KAAI,QAAQ,EAAE;AACxE,UAAI,IAAI,iBAAiB,OAAW,KAAI,eAAe;AACvD,UAAI,IAAI,gBAAgB,OAAW,KAAI,cAAc;AACrD,UAAI,EAAE,SAAS,gBAAgB,IAAI,UAAU,QAAQ,OAAO,IAAI,MAAM,EAAE,KAAK,MAAM,IAAK,KAAI,SAAS;AACrG,UAAI,WAAW;AAEf,UAAI,EAAE,aAAa,OAAO;AACxB,cAAM,MAAM,oBAAI,KAAK;AACrB,cAAM,YAAY,eAAe,IAAI,EAAE,GAAG;AAC1C,YAAI,CAAC,KAAK;AACR,gBAAM;AAAA,YACJ;AAAA,YACA;AAAA,cACE;AAAA,cACA,KAAK,EAAE;AAAA,cACP,MAAM,EAAE;AAAA,cACR,YAAY,WAAW,cAAc;AAAA,YACvC;AAAA,YACA;AAAA,YACA;AAAA,UACF;AACA,mBAAS,IAAI,EAAE,KAAK,GAAG;AAAA,QACzB,OAAO;AACL,mCAAyB,KAAK,GAAG;AAAA,QACnC;AACA,YAAI,OAAO,EAAE;AACb,YAAI,aAAa;AACjB,YAAI,WAAW;AACf,YAAI,YAAY,IAAI,aAAa;AACjC,YAAI,YAAY;AAChB,WAAG,QAAQ,GAAG;AACd;AAAA,MACF;AAEA,UAAI,CAAC,KAAK;AACR,cAAM,GAAG,OAAO,gBAAgB,EAAE,GAAG,OAAO,WAAW,oBAAI,KAAK,EAAE,CAAC;AACnE,iBAAS,IAAI,EAAE,KAAK,GAAG;AAAA,MACzB;AACA,UAAI,OAAO,EAAE;AACb,UAAI,aAAa;AACjB,UAAI,WAAW;AACf,UAAI,YAAY;AAChB,UAAI,YAAY,oBAAI,KAAK;AACzB,SAAG,QAAQ,GAAG;AAAA,IAChB;AACA,QAAI,cAAc,UAAa,4BAA4B,QAAW;AACpE,YAAM,oBAAyB,EAAE,UAAU,gBAAgB,MAAM,gBAAgB,UAAU,MAAM,SAAS;AAC1G,UAAI,MAAM,MAAM,GAAG,QAAQ,yBAAyB,iBAAiB;AACrE,UAAI,CAAC,IAAK,OAAM,GAAG,OAAO,yBAAyB,EAAE,GAAG,mBAAmB,WAAW,oBAAI,KAAK,EAAE,CAAC;AAClG,YAAM,WAAW,8BAA8B,IAAI,cAAc,CAAC,CAAC;AACnE,YAAM,QAAQ,0BAA0B,UAAU;AAAA,QAChD,WAAW,cAAc,SAAY,eAAe,SAAS,KAAK,CAAC,IAAI;AAAA,QACvE;AAAA,MACF,CAAC;AACD,UAAI,aAAa;AAAA,QACf,WAAW,MAAM;AAAA,QACjB,yBAAyB,MAAM;AAAA,MACjC;AACA,UAAI,YAAY,oBAAI,KAAK;AACzB,UAAI,WAAW;AACf,SAAG,QAAQ,GAAG;AAAA,IAChB;AACA,UAAM,GAAG,MAAM;AACf,UAAM,GAAG,OAAO;AAAA,EAClB,SAAS,GAAG;AACV,QAAI;AAAE,YAAM,GAAG,SAAS;AAAA,IAAE,QAAQ;AAAA,IAAC;AACnC,WAAO,aAAa,KAAK,EAAE,OAAO,mCAAmC,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzF;AAEA,QAAM,MAAM,gBAAgB;AAE5B,QAAM,2BAA2B,OAAO;AAAA,IACtC,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,IACtB,WAAW,CAAC,QAAQ;AAAA,EACtB,CAAC;AAED,SAAO,aAAa,KAAK,EAAE,IAAI,KAAK,CAAC;AACvC;AAEA,MAAM,sBAAsB,EAAE,OAAO;AAAA,EACnC,IAAI,EAAE,QAAQ,IAAI;AACpB,CAAC;AAEM,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa;AAAA,QACX,aAAa;AAAA,QACb,QAAQ;AAAA,MACV;AAAA,MACA,WAAW;AAAA,QACT;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ;AAAA,QACV;AAAA,QACA;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO;AAAA,YACf,OAAO,EAAE,OAAO;AAAA,YAChB,SAAS,EAAE,IAAI,EAAE,SAAS;AAAA,UAC5B,CAAC;AAAA,QACH;AAAA,QACA;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAAA,QACxC;AAAA,QACA;AAAA,UACE,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAAA,QACxC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -20,6 +20,15 @@ import {
|
|
|
20
20
|
beginEntitiesMutationGuard,
|
|
21
21
|
FIELD_DEFINITION_RESOURCE_KIND
|
|
22
22
|
} from "./definitions.mutation-guard.js";
|
|
23
|
+
import {
|
|
24
|
+
createExactDefinitionWhere,
|
|
25
|
+
createScopedDefinitionTombstone,
|
|
26
|
+
createVisibleDefinitionWhere,
|
|
27
|
+
markDefinitionTombstoned,
|
|
28
|
+
resolveDefinitionScopeFromOrganizationScope,
|
|
29
|
+
resolveDefinitionMutationScope,
|
|
30
|
+
selectVisibleDefinitionWinner
|
|
31
|
+
} from "../lib/definition-scope.js";
|
|
23
32
|
async function validateDefaultValueByKind(value, kind, cfg, em, tenantContext) {
|
|
24
33
|
switch (kind) {
|
|
25
34
|
case "boolean":
|
|
@@ -158,6 +167,13 @@ function normalizeFieldGroup(raw) {
|
|
|
158
167
|
if (typeof entry.hint === "string" && entry.hint.trim()) group.hint = entry.hint.trim();
|
|
159
168
|
return group;
|
|
160
169
|
}
|
|
170
|
+
function definitionMatchesReadScope(definition, scope) {
|
|
171
|
+
const definitionTenantId = definition.tenantId ?? null;
|
|
172
|
+
const definitionOrganizationId = definition.organizationId ?? null;
|
|
173
|
+
const tenantMatches = definitionTenantId === null || definitionTenantId === scope.tenantId;
|
|
174
|
+
const organizationMatches = definitionOrganizationId === null || scope.organizationId !== null && definitionOrganizationId === scope.organizationId;
|
|
175
|
+
return tenantMatches && organizationMatches;
|
|
176
|
+
}
|
|
161
177
|
async function GET(req) {
|
|
162
178
|
const url = new URL(req.url);
|
|
163
179
|
const requestedEntityIds = parseEntityIds(url);
|
|
@@ -174,11 +190,12 @@ async function GET(req) {
|
|
|
174
190
|
}
|
|
175
191
|
const container = await createRequestContainer();
|
|
176
192
|
const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
|
|
177
|
-
const
|
|
193
|
+
const definitionScope = resolveDefinitionScopeFromOrganizationScope(auth, scope);
|
|
194
|
+
const tenantId = definitionScope.tenantId;
|
|
178
195
|
if (!tenantId) {
|
|
179
196
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
180
197
|
}
|
|
181
|
-
const organizationId =
|
|
198
|
+
const organizationId = definitionScope.organizationId;
|
|
182
199
|
const { resolve } = container;
|
|
183
200
|
const em = resolve("em");
|
|
184
201
|
let cache;
|
|
@@ -235,21 +252,27 @@ async function GET(req) {
|
|
|
235
252
|
organizationId,
|
|
236
253
|
mode: "public"
|
|
237
254
|
});
|
|
255
|
+
const tenantCandidates = [{ tenantId }, { tenantId: null }];
|
|
256
|
+
const organizationCandidates = [{ organizationId: null }];
|
|
257
|
+
if (organizationId) organizationCandidates.unshift({ organizationId });
|
|
258
|
+
const readScope = { tenantId, organizationId };
|
|
238
259
|
const whereActive = {
|
|
239
260
|
entityId: { $in: entityIds },
|
|
240
261
|
deletedAt: null,
|
|
241
262
|
$and: [
|
|
242
|
-
{ $or:
|
|
263
|
+
{ $or: tenantCandidates },
|
|
264
|
+
{ $or: organizationCandidates }
|
|
243
265
|
]
|
|
244
266
|
};
|
|
245
|
-
const defs = await em.find(CustomFieldDef, whereActive);
|
|
246
|
-
const tombstones = await em.find(CustomFieldDef, {
|
|
267
|
+
const defs = (await em.find(CustomFieldDef, whereActive)).filter((definition) => definitionMatchesReadScope(definition, readScope));
|
|
268
|
+
const tombstones = (await em.find(CustomFieldDef, {
|
|
247
269
|
entityId: { $in: entityIds },
|
|
248
270
|
deletedAt: { $ne: null },
|
|
249
271
|
$and: [
|
|
250
|
-
{ $or:
|
|
272
|
+
{ $or: tenantCandidates },
|
|
273
|
+
{ $or: organizationCandidates }
|
|
251
274
|
]
|
|
252
|
-
});
|
|
275
|
+
})).filter((definition) => definitionMatchesReadScope(definition, readScope));
|
|
253
276
|
const tombstonedByEntity = /* @__PURE__ */ new Map();
|
|
254
277
|
for (const entry of tombstones) {
|
|
255
278
|
const eid = String(entry.entityId);
|
|
@@ -407,6 +430,7 @@ async function POST(req) {
|
|
|
407
430
|
}
|
|
408
431
|
}
|
|
409
432
|
const container = await createRequestContainer();
|
|
433
|
+
const scope = await resolveDefinitionMutationScope({ auth, container, request: req });
|
|
410
434
|
const { resolve } = container;
|
|
411
435
|
const em = resolve("em");
|
|
412
436
|
let cache;
|
|
@@ -414,7 +438,7 @@ async function POST(req) {
|
|
|
414
438
|
cache = resolve("cache");
|
|
415
439
|
} catch {
|
|
416
440
|
}
|
|
417
|
-
const where =
|
|
441
|
+
const where = createExactDefinitionWhere(input.entityId, input.key, scope);
|
|
418
442
|
let def = await em.findOne(CustomFieldDef, where);
|
|
419
443
|
const guard = await beginEntitiesMutationGuard({
|
|
420
444
|
container,
|
|
@@ -451,7 +475,7 @@ async function POST(req) {
|
|
|
451
475
|
input.kind,
|
|
452
476
|
cfg,
|
|
453
477
|
em,
|
|
454
|
-
{ tenantId:
|
|
478
|
+
{ tenantId: scope.tenantId, organizationId: scope.organizationId }
|
|
455
479
|
);
|
|
456
480
|
if (validationError) {
|
|
457
481
|
return NextResponse.json({ error: validationError }, { status: 400 });
|
|
@@ -460,13 +484,14 @@ async function POST(req) {
|
|
|
460
484
|
}
|
|
461
485
|
def.configJson = cfg;
|
|
462
486
|
def.isActive = input.isActive ?? true;
|
|
487
|
+
def.deletedAt = def.isActive === false ? def.deletedAt ?? /* @__PURE__ */ new Date() : null;
|
|
463
488
|
def.updatedAt = /* @__PURE__ */ new Date();
|
|
464
489
|
em.persist(def);
|
|
465
490
|
await em.flush();
|
|
466
491
|
await guard.runAfterSuccess();
|
|
467
492
|
await invalidateDefinitionsCache(cache, {
|
|
468
|
-
tenantId:
|
|
469
|
-
organizationId:
|
|
493
|
+
tenantId: scope.tenantId,
|
|
494
|
+
organizationId: scope.organizationId,
|
|
470
495
|
entityIds: [input.entityId]
|
|
471
496
|
});
|
|
472
497
|
return NextResponse.json({ ok: true, item: { id: def.id, key: def.key, kind: def.kind, configJson: def.configJson, isActive: def.isActive } });
|
|
@@ -483,6 +508,7 @@ async function DELETE(req) {
|
|
|
483
508
|
const { entityId, key } = body || {};
|
|
484
509
|
if (!entityId || !key) return NextResponse.json({ error: "entityId and key are required" }, { status: 400 });
|
|
485
510
|
const container = await createRequestContainer();
|
|
511
|
+
const scope = await resolveDefinitionMutationScope({ auth, container, request: req });
|
|
486
512
|
const { resolve } = container;
|
|
487
513
|
const em = resolve("em");
|
|
488
514
|
let cache;
|
|
@@ -490,28 +516,39 @@ async function DELETE(req) {
|
|
|
490
516
|
cache = resolve("cache");
|
|
491
517
|
} catch {
|
|
492
518
|
}
|
|
493
|
-
const where =
|
|
494
|
-
|
|
495
|
-
|
|
519
|
+
const where = createExactDefinitionWhere(entityId, key, scope);
|
|
520
|
+
let def = await em.findOne(CustomFieldDef, where);
|
|
521
|
+
let inherited = null;
|
|
522
|
+
if (!def) {
|
|
523
|
+
inherited = selectVisibleDefinitionWinner(await em.find(CustomFieldDef, createVisibleDefinitionWhere(
|
|
524
|
+
entityId,
|
|
525
|
+
key,
|
|
526
|
+
scope,
|
|
527
|
+
{ deletedAt: null, isActive: true }
|
|
528
|
+
)));
|
|
529
|
+
if (!inherited) return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
530
|
+
}
|
|
496
531
|
const guard = await beginEntitiesMutationGuard({
|
|
497
532
|
container,
|
|
498
533
|
auth,
|
|
499
534
|
req,
|
|
500
535
|
resourceKind: FIELD_DEFINITION_RESOURCE_KIND,
|
|
501
|
-
resourceId: def
|
|
536
|
+
resourceId: def?.id ?? inherited?.id ?? `${entityId}:${key}`,
|
|
502
537
|
operation: "delete",
|
|
503
538
|
mutationPayload: { entityId, key }
|
|
504
539
|
});
|
|
505
540
|
if (guard.blockedResponse) return guard.blockedResponse;
|
|
506
|
-
def
|
|
507
|
-
|
|
508
|
-
|
|
541
|
+
if (!def) {
|
|
542
|
+
def = createScopedDefinitionTombstone(em, inherited, scope);
|
|
543
|
+
} else {
|
|
544
|
+
markDefinitionTombstoned(def);
|
|
545
|
+
}
|
|
509
546
|
em.persist(def);
|
|
510
547
|
await em.flush();
|
|
511
548
|
await guard.runAfterSuccess();
|
|
512
549
|
await invalidateDefinitionsCache(cache, {
|
|
513
|
-
tenantId:
|
|
514
|
-
organizationId:
|
|
550
|
+
tenantId: scope.tenantId,
|
|
551
|
+
organizationId: scope.organizationId,
|
|
515
552
|
entityIds: [entityId]
|
|
516
553
|
});
|
|
517
554
|
return NextResponse.json({ ok: true });
|