@axiom-lattice/gateway 2.1.111 → 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 +13 -13
- package/CHANGELOG.md +20 -0
- package/dist/index.js +2118 -319
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1948 -145
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -6
- package/src/controllers/__tests__/workflow-tracking.test.ts +113 -0
- package/src/controllers/connections.ts +7 -0
- package/src/controllers/eval.ts +44 -4
- package/src/controllers/export-import.ts +166 -0
- package/src/controllers/run.ts +30 -4
- package/src/controllers/workflow-tracking.ts +105 -11
- 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 +18 -8
- package/src/routes/index.ts +42 -1
- package/src/services/ExportImportService.ts +296 -0
- package/src/services/__tests__/ExportImportService.test.ts +130 -0
- package/src/services/eval-runner.ts +63 -55
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ExportableEntityDefinition, ExportableEntity, ImportPreviewResult } from '@axiom-lattice/core';
|
|
2
|
+
import { getStoreLattice } from '@axiom-lattice/core';
|
|
3
|
+
import type { AssistantStore } from '@axiom-lattice/protocols';
|
|
4
|
+
|
|
5
|
+
function getStore(): AssistantStore {
|
|
6
|
+
return getStoreLattice('default', 'assistant').store as unknown as AssistantStore;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const agentRegistration: ExportableEntityDefinition = {
|
|
10
|
+
entityType: 'agent',
|
|
11
|
+
label: 'Agent(智能体)',
|
|
12
|
+
category: 'core',
|
|
13
|
+
dependsOn: ['skill'],
|
|
14
|
+
cascadeParents: [],
|
|
15
|
+
|
|
16
|
+
async listForExport(tenantId: string): Promise<ExportableEntity[]> {
|
|
17
|
+
const store = getStore();
|
|
18
|
+
const agents = await store.getAllAssistants(tenantId);
|
|
19
|
+
return agents.map((a) => ({
|
|
20
|
+
_exportId: '',
|
|
21
|
+
data: {
|
|
22
|
+
id: a.id,
|
|
23
|
+
name: a.name,
|
|
24
|
+
description: a.description,
|
|
25
|
+
graphDefinition: a.graphDefinition,
|
|
26
|
+
},
|
|
27
|
+
}));
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
async previewImport(
|
|
31
|
+
tenantId: string,
|
|
32
|
+
entities: ExportableEntity[],
|
|
33
|
+
): Promise<ImportPreviewResult> {
|
|
34
|
+
const store = getStore();
|
|
35
|
+
const existing = await store.getAllAssistants(tenantId);
|
|
36
|
+
const existingIds = new Set(existing.map((a) => a.id));
|
|
37
|
+
|
|
38
|
+
const conflicts: ImportPreviewResult['conflicts'] = [];
|
|
39
|
+
const insertions: ImportPreviewResult['insertions'] = [];
|
|
40
|
+
|
|
41
|
+
for (const entity of entities) {
|
|
42
|
+
const id = entity.data.id as string;
|
|
43
|
+
if (existingIds.has(id)) {
|
|
44
|
+
conflicts.push({
|
|
45
|
+
_exportId: entity._exportId,
|
|
46
|
+
entityType: 'agent',
|
|
47
|
+
conflictType: 'id_exists',
|
|
48
|
+
existingId: id,
|
|
49
|
+
existingName: (entity.data.name as string) || id,
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
insertions.push({
|
|
53
|
+
_exportId: entity._exportId,
|
|
54
|
+
entityType: 'agent',
|
|
55
|
+
name: (entity.data.name as string) || id,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { conflicts, insertions, errors: [] };
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
async applyImport(tenantId, entity, resolution) {
|
|
64
|
+
const store = getStore();
|
|
65
|
+
const data = entity.data as Record<string, unknown>;
|
|
66
|
+
const id = resolution.action === 'rename' && resolution.newId ? resolution.newId : data.id as string;
|
|
67
|
+
|
|
68
|
+
if (resolution.action === 'overwrite') {
|
|
69
|
+
try { await store.deleteAssistant(tenantId, id); } catch { /* may not exist */ }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await store.createAssistant(tenantId, id, {
|
|
73
|
+
name: data.name as string,
|
|
74
|
+
description: (data.description as string) || '',
|
|
75
|
+
graphDefinition: data.graphDefinition as Record<string, unknown>,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return { newId: id };
|
|
79
|
+
},
|
|
80
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { ExportableEntityDefinition, ExportableEntity, ImportPreviewResult } from '@axiom-lattice/core';
|
|
2
|
+
import { getStoreLattice } from '@axiom-lattice/core';
|
|
3
|
+
import type { BindingRegistry } from '@axiom-lattice/protocols';
|
|
4
|
+
|
|
5
|
+
function getStore(): BindingRegistry {
|
|
6
|
+
return getStoreLattice('default', 'channelBinding').store as unknown as BindingRegistry;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const bindingRegistration: ExportableEntityDefinition = {
|
|
10
|
+
entityType: 'binding',
|
|
11
|
+
label: 'Binding(渠道绑定)',
|
|
12
|
+
category: 'core',
|
|
13
|
+
dependsOn: ['agent', 'channel_installation'],
|
|
14
|
+
cascadeParents: [],
|
|
15
|
+
referenceFields: { agentId: 'agent', channelInstallationId: 'channel_installation' },
|
|
16
|
+
|
|
17
|
+
async listForExport(tenantId: string): Promise<ExportableEntity[]> {
|
|
18
|
+
const store = getStore();
|
|
19
|
+
const bindings = await store.list({ tenantId, limit: 10000, offset: 0 });
|
|
20
|
+
return bindings.map((b) => ({
|
|
21
|
+
_exportId: '',
|
|
22
|
+
data: {
|
|
23
|
+
channel: b.channel,
|
|
24
|
+
channelInstallationId: b.channelInstallationId,
|
|
25
|
+
senderId: b.senderId,
|
|
26
|
+
agentId: b.agentId,
|
|
27
|
+
threadId: b.threadId,
|
|
28
|
+
workspaceId: b.workspaceId,
|
|
29
|
+
projectId: b.projectId,
|
|
30
|
+
threadMode: b.threadMode,
|
|
31
|
+
senderDisplayName: b.senderDisplayName,
|
|
32
|
+
senderMetadata: b.senderMetadata,
|
|
33
|
+
enabled: b.enabled,
|
|
34
|
+
},
|
|
35
|
+
}));
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
async previewImport(
|
|
39
|
+
tenantId: string,
|
|
40
|
+
entities: ExportableEntity[],
|
|
41
|
+
): Promise<ImportPreviewResult> {
|
|
42
|
+
const store = getStore();
|
|
43
|
+
const existing = await store.list({ tenantId, limit: 10000, offset: 0 });
|
|
44
|
+
|
|
45
|
+
const existingKeys = new Set(
|
|
46
|
+
existing.map((b) => `${b.channel}:${b.channelInstallationId}:${b.senderId}`),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const conflicts: ImportPreviewResult['conflicts'] = [];
|
|
50
|
+
const insertions: ImportPreviewResult['insertions'] = [];
|
|
51
|
+
|
|
52
|
+
for (const entity of entities) {
|
|
53
|
+
const d = entity.data as Record<string, unknown>;
|
|
54
|
+
const key = `${d.channel}:${d.channelInstallationId}:${d.senderId}`;
|
|
55
|
+
if (existingKeys.has(key)) {
|
|
56
|
+
conflicts.push({
|
|
57
|
+
_exportId: entity._exportId,
|
|
58
|
+
entityType: 'binding',
|
|
59
|
+
conflictType: 'unique_constraint',
|
|
60
|
+
existingName: `${d.channel}:${d.senderId}`,
|
|
61
|
+
field: 'channel+channelInstallationId+senderId',
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
insertions.push({
|
|
65
|
+
_exportId: entity._exportId,
|
|
66
|
+
entityType: 'binding',
|
|
67
|
+
name: `${d.channel}:${d.senderId}`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return { conflicts, insertions, errors: [] };
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
async applyImport(tenantId, entity, resolution) {
|
|
76
|
+
if (resolution.action === 'skip') return {};
|
|
77
|
+
|
|
78
|
+
const store = getStore();
|
|
79
|
+
const d = entity.data as Record<string, unknown>;
|
|
80
|
+
|
|
81
|
+
if (resolution.action === 'overwrite') {
|
|
82
|
+
const existing = await store.list({ tenantId, limit: 10000, offset: 0 });
|
|
83
|
+
const match = existing.find(
|
|
84
|
+
(b) => b.channel === d.channel && b.channelInstallationId === d.channelInstallationId && b.senderId === d.senderId,
|
|
85
|
+
);
|
|
86
|
+
if (match) {
|
|
87
|
+
await store.delete(match.id);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await store.create({
|
|
92
|
+
channel: d.channel as string,
|
|
93
|
+
channelInstallationId: d.channelInstallationId as string,
|
|
94
|
+
tenantId,
|
|
95
|
+
senderId: d.senderId as string,
|
|
96
|
+
agentId: d.agentId as string,
|
|
97
|
+
threadMode: (d.threadMode as 'fixed' | 'per_conversation') || 'fixed',
|
|
98
|
+
senderDisplayName: d.senderDisplayName as string | undefined,
|
|
99
|
+
senderMetadata: d.senderMetadata as Record<string, unknown> | undefined,
|
|
100
|
+
workspaceId: d.workspaceId as string | undefined,
|
|
101
|
+
projectId: d.projectId as string | undefined,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return { newId: undefined };
|
|
105
|
+
},
|
|
106
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { ExportableEntityDefinition, ExportableEntity, ImportPreviewResult } from '@axiom-lattice/core';
|
|
2
|
+
import { getStoreLattice } from '@axiom-lattice/core';
|
|
3
|
+
import type { ChannelInstallationStore } from '@axiom-lattice/protocols';
|
|
4
|
+
|
|
5
|
+
function getStore(): ChannelInstallationStore {
|
|
6
|
+
return getStoreLattice('default', 'channelInstallation').store as unknown as ChannelInstallationStore;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const channelInstallationRegistration: ExportableEntityDefinition = {
|
|
10
|
+
entityType: 'channel_installation',
|
|
11
|
+
label: 'Channel Installation(渠道安装)',
|
|
12
|
+
category: 'core',
|
|
13
|
+
dependsOn: ['agent'],
|
|
14
|
+
cascadeParents: [],
|
|
15
|
+
referenceFields: { fallbackAgentId: 'agent' },
|
|
16
|
+
|
|
17
|
+
async listForExport(tenantId: string): Promise<ExportableEntity[]> {
|
|
18
|
+
const store = getStore();
|
|
19
|
+
const installations = await store.getInstallationsByTenant(tenantId);
|
|
20
|
+
return installations.map((i) => ({
|
|
21
|
+
_exportId: '',
|
|
22
|
+
data: {
|
|
23
|
+
id: i.id,
|
|
24
|
+
channel: i.channel,
|
|
25
|
+
name: i.name,
|
|
26
|
+
config: i.config,
|
|
27
|
+
enabled: i.enabled,
|
|
28
|
+
fallbackAgentId: i.fallbackAgentId,
|
|
29
|
+
rejectWhenNoBinding: i.rejectWhenNoBinding,
|
|
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.getInstallationsByTenant(tenantId);
|
|
40
|
+
const existingIds = new Set(existing.map((i) => i.id));
|
|
41
|
+
|
|
42
|
+
const conflicts: ImportPreviewResult['conflicts'] = [];
|
|
43
|
+
const insertions: ImportPreviewResult['insertions'] = [];
|
|
44
|
+
|
|
45
|
+
for (const entity of entities) {
|
|
46
|
+
const d = entity.data as Record<string, unknown>;
|
|
47
|
+
const id = d.id as string;
|
|
48
|
+
if (existingIds.has(id)) {
|
|
49
|
+
conflicts.push({
|
|
50
|
+
_exportId: entity._exportId,
|
|
51
|
+
entityType: 'channel_installation',
|
|
52
|
+
conflictType: 'id_exists',
|
|
53
|
+
existingId: id,
|
|
54
|
+
existingName: (d.name as string) || id,
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
insertions.push({
|
|
58
|
+
_exportId: entity._exportId,
|
|
59
|
+
entityType: 'channel_installation',
|
|
60
|
+
name: (d.name as string) || id,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { conflicts, insertions, errors: [] };
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
async applyImport(tenantId, entity, resolution) {
|
|
69
|
+
const store = getStore();
|
|
70
|
+
const data = entity.data as Record<string, unknown>;
|
|
71
|
+
const id = resolution.action === 'rename' && resolution.newId ? resolution.newId : data.id as string;
|
|
72
|
+
|
|
73
|
+
if (resolution.action === 'overwrite') {
|
|
74
|
+
try { await store.deleteInstallation(tenantId, id); } catch { /* may not exist */ }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
await store.createInstallation(tenantId, id, {
|
|
78
|
+
channel: data.channel as 'lark' | 'email' | 'slack' | 'wechat',
|
|
79
|
+
name: data.name as string | undefined,
|
|
80
|
+
config: data.config as any,
|
|
81
|
+
enabled: data.enabled as boolean | undefined,
|
|
82
|
+
fallbackAgentId: data.fallbackAgentId as string | undefined,
|
|
83
|
+
rejectWhenNoBinding: data.rejectWhenNoBinding as boolean | undefined,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return { newId: id };
|
|
87
|
+
},
|
|
88
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { ExportableEntityDefinition, ExportableEntity, ImportPreviewResult } from '@axiom-lattice/core';
|
|
2
|
+
import { getStoreLattice } from '@axiom-lattice/core';
|
|
3
|
+
import type { CollectionStore } from '@axiom-lattice/protocols';
|
|
4
|
+
|
|
5
|
+
function getStore(): CollectionStore {
|
|
6
|
+
return getStoreLattice('default', 'collection').store as unknown as CollectionStore;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const collectionRegistration: ExportableEntityDefinition = {
|
|
10
|
+
entityType: 'collection',
|
|
11
|
+
label: 'Collection(知识库)',
|
|
12
|
+
category: 'core',
|
|
13
|
+
dependsOn: [],
|
|
14
|
+
cascadeParents: [],
|
|
15
|
+
|
|
16
|
+
async listForExport(tenantId: string): Promise<ExportableEntity[]> {
|
|
17
|
+
const store = getStore();
|
|
18
|
+
const collections = await store.getAllCollections(tenantId);
|
|
19
|
+
return collections.map((c) => ({
|
|
20
|
+
_exportId: '',
|
|
21
|
+
data: {
|
|
22
|
+
id: c.id,
|
|
23
|
+
name: c.name,
|
|
24
|
+
label: c.label,
|
|
25
|
+
embeddingKey: c.embeddingKey,
|
|
26
|
+
schema: c.schema,
|
|
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.getAllCollections(tenantId);
|
|
37
|
+
const existingNames = new Set(existing.map((c) => c.name));
|
|
38
|
+
|
|
39
|
+
const conflicts: ImportPreviewResult['conflicts'] = [];
|
|
40
|
+
const insertions: ImportPreviewResult['insertions'] = [];
|
|
41
|
+
|
|
42
|
+
for (const entity of entities) {
|
|
43
|
+
const d = entity.data as Record<string, unknown>;
|
|
44
|
+
const name = d.name as string;
|
|
45
|
+
if (existingNames.has(name)) {
|
|
46
|
+
conflicts.push({
|
|
47
|
+
_exportId: entity._exportId,
|
|
48
|
+
entityType: 'collection',
|
|
49
|
+
conflictType: 'unique_constraint',
|
|
50
|
+
existingName: name,
|
|
51
|
+
field: 'name',
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
insertions.push({
|
|
55
|
+
_exportId: entity._exportId,
|
|
56
|
+
entityType: 'collection',
|
|
57
|
+
name: name,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { conflicts, insertions, errors: [] };
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
async applyImport(tenantId, entity, resolution) {
|
|
66
|
+
if (resolution.action === 'skip') return {};
|
|
67
|
+
|
|
68
|
+
const store = getStore();
|
|
69
|
+
const d = entity.data as Record<string, unknown>;
|
|
70
|
+
const name = d.name as string;
|
|
71
|
+
|
|
72
|
+
if (resolution.action === 'overwrite') {
|
|
73
|
+
try { await store.deleteCollection(tenantId, name); } catch { /* may not exist */ }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await store.createCollection(tenantId, {
|
|
77
|
+
name,
|
|
78
|
+
label: d.label as string,
|
|
79
|
+
embeddingKey: d.embeddingKey as string,
|
|
80
|
+
schema: d.schema as any,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return { newId: undefined };
|
|
84
|
+
},
|
|
85
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { ExportableEntityDefinition, ExportableEntity, ImportPreviewResult } from '@axiom-lattice/core';
|
|
2
|
+
import { getStoreLattice } from '@axiom-lattice/core';
|
|
3
|
+
import type { ConnectionEntry, ConnectionStore } from '@axiom-lattice/protocols';
|
|
4
|
+
|
|
5
|
+
function getStore(): ConnectionStore {
|
|
6
|
+
return getStoreLattice('default', 'connection').store as unknown as ConnectionStore;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async function listAllConnections(tenantId: string): Promise<ConnectionEntry[]> {
|
|
10
|
+
const store = getStore();
|
|
11
|
+
if (typeof store.listByTenant !== 'function') {
|
|
12
|
+
throw new Error('ConnectionStore.listByTenant is not implemented. SQL bypass removed — upgrade your store adapter.');
|
|
13
|
+
}
|
|
14
|
+
return store.listByTenant(tenantId);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const connectionRegistration: ExportableEntityDefinition = {
|
|
18
|
+
entityType: 'connection',
|
|
19
|
+
label: 'Connection(连接)',
|
|
20
|
+
category: 'core',
|
|
21
|
+
dependsOn: [],
|
|
22
|
+
cascadeParents: [],
|
|
23
|
+
|
|
24
|
+
async listForExport(tenantId: string): Promise<ExportableEntity[]> {
|
|
25
|
+
const connections = await listAllConnections(tenantId);
|
|
26
|
+
return connections.map((c) => ({
|
|
27
|
+
_exportId: '',
|
|
28
|
+
data: {
|
|
29
|
+
id: c.id,
|
|
30
|
+
type: c.type,
|
|
31
|
+
key: c.key,
|
|
32
|
+
name: c.name,
|
|
33
|
+
description: c.description,
|
|
34
|
+
config: c.config,
|
|
35
|
+
},
|
|
36
|
+
}));
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
async previewImport(
|
|
40
|
+
tenantId: string,
|
|
41
|
+
entities: ExportableEntity[],
|
|
42
|
+
): Promise<ImportPreviewResult> {
|
|
43
|
+
const existing = await listAllConnections(tenantId);
|
|
44
|
+
const existingCompositeKeys = new Set(
|
|
45
|
+
existing.map((c) => `${c.type}:${c.key}`),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const conflicts: ImportPreviewResult['conflicts'] = [];
|
|
49
|
+
const insertions: ImportPreviewResult['insertions'] = [];
|
|
50
|
+
|
|
51
|
+
for (const entity of entities) {
|
|
52
|
+
const d = entity.data as Record<string, unknown>;
|
|
53
|
+
const compositeKey = `${d.type}:${d.key}`;
|
|
54
|
+
if (existingCompositeKeys.has(compositeKey)) {
|
|
55
|
+
conflicts.push({
|
|
56
|
+
_exportId: entity._exportId,
|
|
57
|
+
entityType: 'connection',
|
|
58
|
+
conflictType: 'unique_constraint',
|
|
59
|
+
existingName: `${d.type}:${d.key}`,
|
|
60
|
+
field: 'type+key',
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
insertions.push({
|
|
64
|
+
_exportId: entity._exportId,
|
|
65
|
+
entityType: 'connection',
|
|
66
|
+
name: `${d.type}:${d.key}`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { conflicts, insertions, errors: [] };
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
async applyImport(tenantId, entity, resolution) {
|
|
75
|
+
if (resolution.action === 'skip') return {};
|
|
76
|
+
|
|
77
|
+
const store = getStore();
|
|
78
|
+
const d = entity.data as Record<string, unknown>;
|
|
79
|
+
const type = d.type as string;
|
|
80
|
+
const key = d.key as string;
|
|
81
|
+
|
|
82
|
+
if (resolution.action === 'overwrite') {
|
|
83
|
+
try { await store.delete(tenantId, type, key); } catch { /* may not exist */ }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
await store.create({
|
|
87
|
+
tenantId,
|
|
88
|
+
type,
|
|
89
|
+
key,
|
|
90
|
+
name: d.name as string,
|
|
91
|
+
description: d.description as string | undefined,
|
|
92
|
+
config: d.config as Record<string, unknown>,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return { newId: undefined };
|
|
96
|
+
},
|
|
97
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ExportableEntityDefinition, ExportableEntity, ImportPreviewResult } from '@axiom-lattice/core';
|
|
2
|
+
import { getStoreLattice } from '@axiom-lattice/core';
|
|
3
|
+
import type { DatabaseConfigStore } from '@axiom-lattice/protocols';
|
|
4
|
+
|
|
5
|
+
function getStore(): DatabaseConfigStore {
|
|
6
|
+
return getStoreLattice('default', 'database').store as unknown as DatabaseConfigStore;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const databaseConfigRegistration: ExportableEntityDefinition = {
|
|
10
|
+
entityType: 'database_config',
|
|
11
|
+
label: 'Database 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: 'database_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: 'database_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: 'database_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
|
+
};
|