@axiom-lattice/gateway 2.1.112 → 2.1.113
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 +11 -11
- package/CHANGELOG.md +9 -0
- package/dist/index.js +1954 -273
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1764 -83
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/controllers/export-import.ts +166 -0
- package/src/controllers/run.ts +30 -4
- package/src/export_registrations/a2a-api-key.registration.ts +89 -0
- package/src/export_registrations/agent.registration.ts +80 -0
- package/src/export_registrations/binding.registration.ts +106 -0
- package/src/export_registrations/channel-installation.registration.ts +88 -0
- package/src/export_registrations/collection.registration.ts +85 -0
- package/src/export_registrations/connection.registration.ts +97 -0
- package/src/export_registrations/database-config.registration.ts +94 -0
- package/src/export_registrations/eval.registration.ts +334 -0
- package/src/export_registrations/index.ts +31 -0
- package/src/export_registrations/mcp-config.registration.ts +97 -0
- package/src/export_registrations/menu.registration.ts +91 -0
- package/src/export_registrations/metrics-config.registration.ts +94 -0
- package/src/export_registrations/skill.registration.ts +88 -0
- package/src/export_registrations/task.registration.ts +99 -0
- package/src/index.ts +4 -0
- package/src/routes/index.ts +34 -0
- package/src/services/ExportImportService.ts +296 -0
- package/src/services/__tests__/ExportImportService.test.ts +130 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { ExportableEntityDefinition, ExportableEntity, ImportPreviewResult } from '@axiom-lattice/core';
|
|
2
|
+
import { getStoreLattice } from '@axiom-lattice/core';
|
|
3
|
+
import type { MenuRegistry } from '@axiom-lattice/protocols';
|
|
4
|
+
|
|
5
|
+
function getStore(): MenuRegistry {
|
|
6
|
+
return getStoreLattice('default', 'menu').store as unknown as MenuRegistry;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const menuRegistration: ExportableEntityDefinition = {
|
|
10
|
+
entityType: 'menu',
|
|
11
|
+
label: 'Menu(菜单)',
|
|
12
|
+
category: 'core',
|
|
13
|
+
dependsOn: ['agent'],
|
|
14
|
+
cascadeParents: [],
|
|
15
|
+
|
|
16
|
+
async listForExport(tenantId: string): Promise<ExportableEntity[]> {
|
|
17
|
+
const store = getStore();
|
|
18
|
+
const menus = await store.list({ tenantId });
|
|
19
|
+
return menus.map((m) => ({
|
|
20
|
+
_exportId: '',
|
|
21
|
+
data: {
|
|
22
|
+
id: m.id,
|
|
23
|
+
menuTarget: m.menuTarget,
|
|
24
|
+
group: m.group,
|
|
25
|
+
name: m.name,
|
|
26
|
+
icon: m.icon,
|
|
27
|
+
sortOrder: m.sortOrder,
|
|
28
|
+
contentType: m.contentType,
|
|
29
|
+
contentConfig: m.contentConfig,
|
|
30
|
+
enabled: m.enabled,
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
async previewImport(
|
|
36
|
+
tenantId: string,
|
|
37
|
+
entities: ExportableEntity[],
|
|
38
|
+
): Promise<ImportPreviewResult> {
|
|
39
|
+
const store = getStore();
|
|
40
|
+
const existing = await store.list({ tenantId });
|
|
41
|
+
const existingIds = new Set(existing.map((m) => m.id));
|
|
42
|
+
|
|
43
|
+
const conflicts: ImportPreviewResult['conflicts'] = [];
|
|
44
|
+
const insertions: ImportPreviewResult['insertions'] = [];
|
|
45
|
+
|
|
46
|
+
for (const entity of entities) {
|
|
47
|
+
const d = entity.data as Record<string, unknown>;
|
|
48
|
+
const id = d.id as string;
|
|
49
|
+
if (existingIds.has(id)) {
|
|
50
|
+
conflicts.push({
|
|
51
|
+
_exportId: entity._exportId,
|
|
52
|
+
entityType: 'menu',
|
|
53
|
+
conflictType: 'id_exists',
|
|
54
|
+
existingId: id,
|
|
55
|
+
existingName: (d.name as string) || id,
|
|
56
|
+
});
|
|
57
|
+
} else {
|
|
58
|
+
insertions.push({
|
|
59
|
+
_exportId: entity._exportId,
|
|
60
|
+
entityType: 'menu',
|
|
61
|
+
name: (d.name as string) || id,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { conflicts, insertions, errors: [] };
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
async applyImport(tenantId, entity, resolution) {
|
|
70
|
+
const store = getStore();
|
|
71
|
+
const d = entity.data as Record<string, unknown>;
|
|
72
|
+
const id = resolution.action === 'rename' && resolution.newId ? resolution.newId : d.id as string;
|
|
73
|
+
|
|
74
|
+
if (resolution.action === 'overwrite') {
|
|
75
|
+
try { await store.delete(id); } catch { /* may not exist */ }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
await store.create({
|
|
79
|
+
tenantId,
|
|
80
|
+
menuTarget: d.menuTarget as 'sidebar' | 'workspace',
|
|
81
|
+
group: d.group as string | undefined,
|
|
82
|
+
name: d.name as string,
|
|
83
|
+
icon: d.icon as string | undefined,
|
|
84
|
+
sortOrder: d.sortOrder as number | undefined,
|
|
85
|
+
contentType: d.contentType as 'agent' | 'html' | 'custom',
|
|
86
|
+
contentConfig: d.contentConfig as any,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return { newId: undefined };
|
|
90
|
+
},
|
|
91
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ExportableEntityDefinition, ExportableEntity, ImportPreviewResult } from '@axiom-lattice/core';
|
|
2
|
+
import { getStoreLattice } from '@axiom-lattice/core';
|
|
3
|
+
import type { MetricsServerConfigStore } from '@axiom-lattice/protocols';
|
|
4
|
+
|
|
5
|
+
function getStore(): MetricsServerConfigStore {
|
|
6
|
+
return getStoreLattice('default', 'metrics').store as unknown as MetricsServerConfigStore;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const metricsConfigRegistration: ExportableEntityDefinition = {
|
|
10
|
+
entityType: 'metrics_config',
|
|
11
|
+
label: 'Metrics Config(指标配置)',
|
|
12
|
+
category: 'core',
|
|
13
|
+
dependsOn: [],
|
|
14
|
+
cascadeParents: [],
|
|
15
|
+
|
|
16
|
+
async listForExport(tenantId: string): Promise<ExportableEntity[]> {
|
|
17
|
+
const store = getStore();
|
|
18
|
+
const configs = await store.getAllConfigs(tenantId);
|
|
19
|
+
return configs.map((c) => ({
|
|
20
|
+
_exportId: '',
|
|
21
|
+
data: {
|
|
22
|
+
id: c.id,
|
|
23
|
+
key: c.key,
|
|
24
|
+
name: c.name,
|
|
25
|
+
description: c.description,
|
|
26
|
+
config: c.config,
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
async previewImport(
|
|
32
|
+
tenantId: string,
|
|
33
|
+
entities: ExportableEntity[],
|
|
34
|
+
): Promise<ImportPreviewResult> {
|
|
35
|
+
const store = getStore();
|
|
36
|
+
const existing = await store.getAllConfigs(tenantId);
|
|
37
|
+
const existingIds = new Set(existing.map((c) => c.id));
|
|
38
|
+
const existingKeys = new Set(existing.map((c) => c.key));
|
|
39
|
+
|
|
40
|
+
const conflicts: ImportPreviewResult['conflicts'] = [];
|
|
41
|
+
const insertions: ImportPreviewResult['insertions'] = [];
|
|
42
|
+
|
|
43
|
+
for (const entity of entities) {
|
|
44
|
+
const d = entity.data as Record<string, unknown>;
|
|
45
|
+
const id = d.id as string;
|
|
46
|
+
const key = d.key as string;
|
|
47
|
+
|
|
48
|
+
if (existingIds.has(id)) {
|
|
49
|
+
conflicts.push({
|
|
50
|
+
_exportId: entity._exportId,
|
|
51
|
+
entityType: 'metrics_config',
|
|
52
|
+
conflictType: 'id_exists',
|
|
53
|
+
existingId: id,
|
|
54
|
+
existingName: (d.name as string) || key,
|
|
55
|
+
});
|
|
56
|
+
} else if (key && existingKeys.has(key)) {
|
|
57
|
+
conflicts.push({
|
|
58
|
+
_exportId: entity._exportId,
|
|
59
|
+
entityType: 'metrics_config',
|
|
60
|
+
conflictType: 'unique_constraint',
|
|
61
|
+
existingName: (d.name as string) || key,
|
|
62
|
+
field: 'key',
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
insertions.push({
|
|
66
|
+
_exportId: entity._exportId,
|
|
67
|
+
entityType: 'metrics_config',
|
|
68
|
+
name: (d.name as string) || key,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { conflicts, insertions, errors: [] };
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
async applyImport(tenantId, entity, resolution) {
|
|
77
|
+
const store = getStore();
|
|
78
|
+
const data = entity.data as Record<string, unknown>;
|
|
79
|
+
const id = resolution.action === 'rename' && resolution.newId ? resolution.newId : data.id as string;
|
|
80
|
+
|
|
81
|
+
if (resolution.action === 'overwrite') {
|
|
82
|
+
try { await store.deleteConfig(tenantId, id); } catch { /* may not exist */ }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
await store.createConfig(tenantId, id, {
|
|
86
|
+
key: data.key as string,
|
|
87
|
+
name: data.name as string | undefined,
|
|
88
|
+
description: data.description as string | undefined,
|
|
89
|
+
config: data.config as any,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return { newId: id };
|
|
93
|
+
},
|
|
94
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { ExportableEntityDefinition, ExportableEntity, ImportPreviewResult } from '@axiom-lattice/core';
|
|
2
|
+
import { getStoreLattice } from '@axiom-lattice/core';
|
|
3
|
+
import type { SkillStore } from '@axiom-lattice/protocols';
|
|
4
|
+
|
|
5
|
+
function getStore(): SkillStore {
|
|
6
|
+
return getStoreLattice('default', 'skill').store as unknown as SkillStore;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const skillRegistration: ExportableEntityDefinition = {
|
|
10
|
+
entityType: 'skill',
|
|
11
|
+
label: 'Skill(技能)',
|
|
12
|
+
category: 'core',
|
|
13
|
+
dependsOn: [],
|
|
14
|
+
cascadeParents: [],
|
|
15
|
+
|
|
16
|
+
async listForExport(tenantId: string): Promise<ExportableEntity[]> {
|
|
17
|
+
const store = getStore();
|
|
18
|
+
const skills = await store.getAllSkills(tenantId);
|
|
19
|
+
return skills.map((s) => ({
|
|
20
|
+
_exportId: '',
|
|
21
|
+
data: {
|
|
22
|
+
id: s.id,
|
|
23
|
+
name: s.name,
|
|
24
|
+
description: s.description,
|
|
25
|
+
license: s.license,
|
|
26
|
+
compatibility: s.compatibility,
|
|
27
|
+
metadata: s.metadata,
|
|
28
|
+
content: s.content,
|
|
29
|
+
subSkills: s.subSkills,
|
|
30
|
+
},
|
|
31
|
+
}));
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
async previewImport(
|
|
35
|
+
tenantId: string,
|
|
36
|
+
entities: ExportableEntity[],
|
|
37
|
+
): Promise<ImportPreviewResult> {
|
|
38
|
+
const store = getStore();
|
|
39
|
+
const existing = await store.getAllSkills(tenantId);
|
|
40
|
+
const existingIds = new Set(existing.map((s) => s.id));
|
|
41
|
+
|
|
42
|
+
const conflicts: ImportPreviewResult['conflicts'] = [];
|
|
43
|
+
const insertions: ImportPreviewResult['insertions'] = [];
|
|
44
|
+
|
|
45
|
+
for (const entity of entities) {
|
|
46
|
+
const id = entity.data.id as string;
|
|
47
|
+
if (existingIds.has(id)) {
|
|
48
|
+
conflicts.push({
|
|
49
|
+
_exportId: entity._exportId,
|
|
50
|
+
entityType: 'skill',
|
|
51
|
+
conflictType: 'id_exists',
|
|
52
|
+
existingId: id,
|
|
53
|
+
existingName: (entity.data.name as string) || id,
|
|
54
|
+
});
|
|
55
|
+
} else {
|
|
56
|
+
insertions.push({
|
|
57
|
+
_exportId: entity._exportId,
|
|
58
|
+
entityType: 'skill',
|
|
59
|
+
name: (entity.data.name as string) || id,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { conflicts, insertions, errors: [] };
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
async applyImport(tenantId, entity, resolution) {
|
|
68
|
+
const store = getStore();
|
|
69
|
+
const data = entity.data as Record<string, unknown>;
|
|
70
|
+
const id = resolution.action === 'rename' && resolution.newId ? resolution.newId : data.id as string;
|
|
71
|
+
|
|
72
|
+
if (resolution.action === 'overwrite') {
|
|
73
|
+
try { await store.deleteSkill(tenantId, id); } catch { /* may not exist */ }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await store.createSkill(tenantId, id, {
|
|
77
|
+
name: data.name as string,
|
|
78
|
+
description: data.description as string,
|
|
79
|
+
license: data.license as string | undefined,
|
|
80
|
+
compatibility: data.compatibility as string | undefined,
|
|
81
|
+
metadata: data.metadata as Record<string, string> | undefined,
|
|
82
|
+
content: data.content as string | undefined,
|
|
83
|
+
subSkills: data.subSkills as string[] | undefined,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return { newId: id };
|
|
87
|
+
},
|
|
88
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { ExportableEntityDefinition, ExportableEntity, ImportPreviewResult } from '@axiom-lattice/core';
|
|
2
|
+
import { getStoreLattice } from '@axiom-lattice/core';
|
|
3
|
+
import type { TaskStore } from '@axiom-lattice/protocols';
|
|
4
|
+
|
|
5
|
+
function getStore(): TaskStore {
|
|
6
|
+
return getStoreLattice('default', 'task').store as unknown as TaskStore;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const taskRegistration: ExportableEntityDefinition = {
|
|
10
|
+
entityType: 'task',
|
|
11
|
+
label: 'Task(任务)',
|
|
12
|
+
category: 'core',
|
|
13
|
+
dependsOn: ['agent'],
|
|
14
|
+
cascadeParents: [],
|
|
15
|
+
referenceFields: { ownerId: 'agent', parentId: 'task' },
|
|
16
|
+
|
|
17
|
+
async listForExport(tenantId: string): Promise<ExportableEntity[]> {
|
|
18
|
+
const store = getStore();
|
|
19
|
+
const tasks = await store.list({ tenantId, limit: 10000, offset: 0 });
|
|
20
|
+
return tasks.map((t) => ({
|
|
21
|
+
_exportId: '',
|
|
22
|
+
data: {
|
|
23
|
+
id: t.id,
|
|
24
|
+
title: t.title,
|
|
25
|
+
description: t.description,
|
|
26
|
+
status: t.status,
|
|
27
|
+
priority: t.priority,
|
|
28
|
+
dueDate: t.dueDate,
|
|
29
|
+
ownerType: t.ownerType,
|
|
30
|
+
ownerId: t.ownerId,
|
|
31
|
+
metadata: t.metadata,
|
|
32
|
+
parentId: t.parentId,
|
|
33
|
+
sourceId: t.sourceId,
|
|
34
|
+
context: t.context,
|
|
35
|
+
},
|
|
36
|
+
}));
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
async previewImport(
|
|
40
|
+
tenantId: string,
|
|
41
|
+
entities: ExportableEntity[],
|
|
42
|
+
): Promise<ImportPreviewResult> {
|
|
43
|
+
const store = getStore();
|
|
44
|
+
const existing = await store.list({ tenantId, limit: 10000, offset: 0 });
|
|
45
|
+
const existingIds = new Set(existing.map((t) => t.id));
|
|
46
|
+
|
|
47
|
+
const conflicts: ImportPreviewResult['conflicts'] = [];
|
|
48
|
+
const insertions: ImportPreviewResult['insertions'] = [];
|
|
49
|
+
|
|
50
|
+
for (const entity of entities) {
|
|
51
|
+
const d = entity.data as Record<string, unknown>;
|
|
52
|
+
const id = d.id as string;
|
|
53
|
+
if (existingIds.has(id)) {
|
|
54
|
+
conflicts.push({
|
|
55
|
+
_exportId: entity._exportId,
|
|
56
|
+
entityType: 'task',
|
|
57
|
+
conflictType: 'id_exists',
|
|
58
|
+
existingId: id,
|
|
59
|
+
existingName: (d.title as string) || id,
|
|
60
|
+
});
|
|
61
|
+
} else {
|
|
62
|
+
insertions.push({
|
|
63
|
+
_exportId: entity._exportId,
|
|
64
|
+
entityType: 'task',
|
|
65
|
+
name: (d.title as string) || id,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { conflicts, insertions, errors: [] };
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
async applyImport(tenantId, entity, resolution) {
|
|
74
|
+
const store = getStore();
|
|
75
|
+
const data = entity.data as Record<string, unknown>;
|
|
76
|
+
const id = resolution.action === 'rename' && resolution.newId ? resolution.newId : data.id as string;
|
|
77
|
+
|
|
78
|
+
if (resolution.action === 'overwrite') {
|
|
79
|
+
try { await store.delete(tenantId, id); } catch { /* may not exist */ }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
await store.create({
|
|
83
|
+
tenantId,
|
|
84
|
+
ownerType: (data.ownerType as 'user' | 'agent') || 'user',
|
|
85
|
+
ownerId: data.ownerId as string || '',
|
|
86
|
+
title: data.title as string,
|
|
87
|
+
description: data.description as string | undefined,
|
|
88
|
+
status: data.status as 'pending' | 'in_progress' | 'completed' | 'cancelled' | undefined,
|
|
89
|
+
priority: data.priority as 'low' | 'medium' | 'high' | undefined,
|
|
90
|
+
dueDate: data.dueDate as string | undefined,
|
|
91
|
+
metadata: data.metadata as Record<string, unknown> | undefined,
|
|
92
|
+
parentId: data.parentId as string | undefined,
|
|
93
|
+
sourceId: data.sourceId as string | undefined,
|
|
94
|
+
context: data.context as Record<string, unknown> | undefined,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return { newId: id };
|
|
98
|
+
},
|
|
99
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import path from "path";
|
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
import { registerLatticeRoutes } from "./routes";
|
|
10
10
|
import { registerResourceRoutes } from "./routes/resource-routes";
|
|
11
|
+
import { registerAllBuiltinEntities } from "./export_registrations";
|
|
11
12
|
import type { ChannelInstallationStore, BindingRegistry } from "@axiom-lattice/protocols";
|
|
12
13
|
import { MessageRouter } from "./router/MessageRouter";
|
|
13
14
|
import { ChannelAdapterRegistry } from "./channels/registry";
|
|
@@ -399,6 +400,9 @@ const start = async (config?: LatticeGatewayConfig) => {
|
|
|
399
400
|
// Menu store optional — custom menus won't work without it
|
|
400
401
|
}
|
|
401
402
|
|
|
403
|
+
// Register export/import entity types before routes
|
|
404
|
+
registerAllBuiltinEntities();
|
|
405
|
+
|
|
402
406
|
// Register all routes (channel routes only active if channelDeps is set)
|
|
403
407
|
// Wire controller deps so enable/disable/delete can manage live connections
|
|
404
408
|
if (channelDeps?.router) {
|
package/src/routes/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import * as dataQueryController from "../controllers/data-query";
|
|
|
19
19
|
import * as workflowTrackingController from "../controllers/workflow-tracking";
|
|
20
20
|
import * as paController from "../controllers/personal-assistant";
|
|
21
21
|
import * as tasksController from "../controllers/tasks";
|
|
22
|
+
import * as exportImportController from "../controllers/export-import";
|
|
22
23
|
import { middlewareTypesRoutes } from "../controllers/middleware-types";
|
|
23
24
|
import {
|
|
24
25
|
createRunSchema,
|
|
@@ -575,4 +576,37 @@ export const registerLatticeRoutes = (app: FastifyInstance, channelDeps?: { rout
|
|
|
575
576
|
"/api/assistants/:assistant_id/threads/:thread_id/pending-messages/:message_id",
|
|
576
577
|
removePendingMessageHandler
|
|
577
578
|
);
|
|
579
|
+
|
|
580
|
+
// Export/Import endpoints
|
|
581
|
+
app.get("/api/tenants/exportable-types", exportImportController.getExportableTypes);
|
|
582
|
+
|
|
583
|
+
app.post<{ Params: { tenantId: string }; Body: { entityTypes: string[]; entityIds?: Record<string, string[]> } }>(
|
|
584
|
+
"/api/tenants/:tenantId/export",
|
|
585
|
+
exportImportController.exportConfig,
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
app.post<{ Params: { tenantId: string }; Body: { entityTypes: string[]; entityIds?: Record<string, string[]> } }>(
|
|
589
|
+
"/api/tenants/:tenantId/export/confirm",
|
|
590
|
+
exportImportController.exportConfigConfirm,
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
app.post<{ Params: { tenantId: string }; Body: { entityTypes: string[] } }>(
|
|
594
|
+
"/api/tenants/:tenantId/export/entities",
|
|
595
|
+
exportImportController.previewEntities,
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
app.get<{ Params: { tenantId: string; jobId: string } }>(
|
|
599
|
+
"/api/tenants/:tenantId/export/:jobId/download",
|
|
600
|
+
exportImportController.downloadExport,
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
app.post<{ Params: { tenantId: string }; Body: { bundle: any } }>(
|
|
604
|
+
"/api/tenants/:tenantId/import/preview",
|
|
605
|
+
exportImportController.importPreview,
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
app.post<{ Params: { tenantId: string }; Body: { bundle: any; resolutions: Record<string, any> } }>(
|
|
609
|
+
"/api/tenants/:tenantId/import/apply",
|
|
610
|
+
exportImportController.importApply,
|
|
611
|
+
);
|
|
578
612
|
};
|