@axiom-lattice/gateway 3.0.2 → 3.0.3
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/dist/index.js +31 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +31 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -6
- package/.eslintrc.json +0 -22
- package/.turbo/turbo-build.log +0 -46
- package/AGENTS.md +0 -112
- package/CHANGELOG.md +0 -1190
- package/LOGGER_CONFIG.md +0 -135
- package/RESUME_STREAM_CONTENT_BASED.md +0 -326
- package/RESUME_STREAM_README.md +0 -388
- package/jest.config.js +0 -72
- package/public/sdk/README.md +0 -695
- package/public/sdk/dashboard-engine-skill.md +0 -1122
- package/public/sdk/dashboard-single-file-spec.md +0 -357
- package/public/sdk/data-query-sdk-skill.md +0 -307
- package/public/sdk/data-query-sdk.d.ts +0 -252
- package/public/sdk/data-query-sdk.js +0 -970
- package/public/sdk/occupancy-dashboard.html +0 -363
- package/public/sdk/test-dashboard.html +0 -690
- package/scripts/seed-lark-installation.ts +0 -54
- package/src/__tests__/__mocks__/e2b.ts +0 -1
- package/src/__tests__/a2a.test.ts +0 -428
- package/src/__tests__/channel-installations.test.ts +0 -199
- package/src/__tests__/data-query.test.ts +0 -77
- package/src/__tests__/sandbox-provider-registration.test.ts +0 -74
- package/src/__tests__/setup.ts +0 -5
- package/src/__tests__/workspace.test.ts +0 -400
- package/src/channels/__tests__/routes.test.ts +0 -50
- package/src/channels/lark/LarkChannelAdapter.ts +0 -217
- package/src/channels/lark/README.md +0 -187
- package/src/channels/lark/__tests__/controller.test.ts +0 -136
- package/src/channels/lark/__tests__/mapping-service.test.ts +0 -118
- package/src/channels/lark/__tests__/parser.test.ts +0 -72
- package/src/channels/lark/__tests__/sender.test.ts +0 -37
- package/src/channels/lark/__tests__/verification.test.ts +0 -157
- package/src/channels/lark/controller.ts +0 -48
- package/src/channels/lark/mapping-service.ts +0 -138
- package/src/channels/lark/parser.ts +0 -68
- package/src/channels/lark/routes.ts +0 -22
- package/src/channels/lark/sender.ts +0 -58
- package/src/channels/lark/types.ts +0 -20
- package/src/channels/lark/verification.ts +0 -32
- package/src/channels/registry.ts +0 -20
- package/src/channels/routes.ts +0 -30
- package/src/channels/wechat/WechatChannelAdapter.ts +0 -294
- package/src/channels/wechat/__tests__/context-store.test.ts +0 -148
- package/src/channels/wechat/context-store.ts +0 -54
- package/src/channels/wechat/controller.ts +0 -59
- package/src/channels/wechat/routes.ts +0 -7
- package/src/channels/wechat/types.ts +0 -62
- package/src/channels/wechat/wechat-client.ts +0 -162
- package/src/config/local-a2a-templates.ts +0 -59
- package/src/config.ts +0 -124
- package/src/controllers/__tests__/assistant-local-a2a.test.ts +0 -147
- package/src/controllers/__tests__/local-a2a-templates.test.ts +0 -112
- package/src/controllers/__tests__/mime.test.ts +0 -74
- package/src/controllers/__tests__/resources.test.ts +0 -122
- package/src/controllers/__tests__/run.test.ts +0 -115
- package/src/controllers/__tests__/tasks.test.ts +0 -215
- package/src/controllers/__tests__/workflow-tracking.test.ts +0 -113
- package/src/controllers/agent_task.ts +0 -72
- package/src/controllers/assistant.ts +0 -490
- package/src/controllers/auth.ts +0 -524
- package/src/controllers/channel-bindings.ts +0 -135
- package/src/controllers/channel-installations.ts +0 -409
- package/src/controllers/collections.ts +0 -176
- package/src/controllers/config.ts +0 -126
- package/src/controllers/connections.ts +0 -181
- package/src/controllers/data-query.ts +0 -236
- package/src/controllers/database-configs.ts +0 -440
- package/src/controllers/embeddings.ts +0 -22
- package/src/controllers/entries.ts +0 -167
- package/src/controllers/eval.ts +0 -513
- package/src/controllers/export-import.ts +0 -166
- package/src/controllers/health.ts +0 -36
- package/src/controllers/local-a2a-templates.ts +0 -18
- package/src/controllers/local-a2a.ts +0 -149
- package/src/controllers/mcp-configs.ts +0 -673
- package/src/controllers/memory.ts +0 -257
- package/src/controllers/menu-items.ts +0 -114
- package/src/controllers/metrics-configs.ts +0 -1061
- package/src/controllers/middleware-types.ts +0 -15
- package/src/controllers/models.ts +0 -199
- package/src/controllers/personal-assistant.ts +0 -191
- package/src/controllers/resources.ts +0 -477
- package/src/controllers/run.ts +0 -338
- package/src/controllers/sandbox.ts +0 -255
- package/src/controllers/schedules.ts +0 -336
- package/src/controllers/skills.ts +0 -256
- package/src/controllers/tasks.ts +0 -213
- package/src/controllers/tenants.ts +0 -121
- package/src/controllers/thread_status.ts +0 -66
- package/src/controllers/threads.ts +0 -260
- package/src/controllers/tools.ts +0 -139
- package/src/controllers/users.ts +0 -135
- package/src/controllers/workflow-tracking.ts +0 -693
- package/src/controllers/workspace.ts +0 -879
- package/src/export_registrations/a2a-api-key.registration.ts +0 -89
- package/src/export_registrations/agent.registration.ts +0 -80
- package/src/export_registrations/binding.registration.ts +0 -106
- package/src/export_registrations/channel-installation.registration.ts +0 -88
- package/src/export_registrations/collection.registration.ts +0 -85
- package/src/export_registrations/connection.registration.ts +0 -97
- package/src/export_registrations/database-config.registration.ts +0 -94
- package/src/export_registrations/eval.registration.ts +0 -334
- package/src/export_registrations/index.ts +0 -31
- package/src/export_registrations/mcp-config.registration.ts +0 -97
- package/src/export_registrations/menu.registration.ts +0 -91
- package/src/export_registrations/metrics-config.registration.ts +0 -94
- package/src/export_registrations/skill.registration.ts +0 -88
- package/src/export_registrations/task.registration.ts +0 -99
- package/src/index.ts +0 -497
- package/src/logger/Logger.ts +0 -186
- package/src/router/MessageContext.ts +0 -14
- package/src/router/MessageRouter.ts +0 -490
- package/src/router/middlewares/auditLogger.ts +0 -34
- package/src/router/middlewares/deduplication.ts +0 -38
- package/src/router/middlewares/index.ts +0 -3
- package/src/router/middlewares/rateLimit.ts +0 -46
- package/src/routes/__tests__/a2a-test-connection.test.ts +0 -177
- package/src/routes/a2a-bridge.ts +0 -266
- package/src/routes/a2a-test-connection.ts +0 -126
- package/src/routes/a2a.ts +0 -851
- package/src/routes/channel-bindings.ts +0 -11
- package/src/routes/channel-installations.ts +0 -33
- package/src/routes/index.ts +0 -640
- package/src/routes/menu-items.ts +0 -10
- package/src/routes/resource-routes.ts +0 -35
- package/src/routes/stt.ts +0 -102
- package/src/schemas/data-query.ts +0 -69
- package/src/schemas/index.ts +0 -852
- package/src/services/ExportImportService.ts +0 -296
- package/src/services/__tests__/ExportImportService.test.ts +0 -130
- package/src/services/__tests__/eval-runner.test.ts +0 -131
- package/src/services/__tests__/local-a2a.test.ts +0 -124
- package/src/services/agent_task_consumer.ts +0 -348
- package/src/services/collection_service.ts +0 -258
- package/src/services/eval-runner.ts +0 -283
- package/src/services/local-a2a.ts +0 -354
- package/src/services/queue_service.ts +0 -104
- package/src/services/sandbox_service.ts +0 -223
- package/src/services/skill_service.ts +0 -97
- package/src/services/supabase.ts +0 -60
- package/src/swagger.ts +0 -77
- package/src/types/index.ts +0 -118
- package/src/types/koa.d.ts +0 -7
- package/src/utils/__tests__/local-a2a-runtime.test.ts +0 -77
- package/src/utils/__tests__/path-containment.test.ts +0 -35
- package/src/utils/a2a-url.ts +0 -20
- package/src/utils/local-a2a-runtime.ts +0 -107
- package/src/utils/mime.ts +0 -44
- package/src/utils/path-containment.ts +0 -38
- package/tsconfig.json +0 -26
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
import { FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
-
import { randomUUID } from "crypto";
|
|
3
|
-
import {
|
|
4
|
-
ChannelInstallationStore,
|
|
5
|
-
ChannelInstallation,
|
|
6
|
-
CreateChannelInstallationRequest,
|
|
7
|
-
UpdateChannelInstallationRequest,
|
|
8
|
-
ChannelInstallationType,
|
|
9
|
-
} from "@axiom-lattice/protocols";
|
|
10
|
-
import type { ChannelAdapterRegistry } from "../channels/registry";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Controller-scoped deps for channel lifecycle management.
|
|
14
|
-
* Set by the gateway during route registration so that
|
|
15
|
-
* enable/disable/deletion can start/stop live connections.
|
|
16
|
-
*/
|
|
17
|
-
let _adapterRegistry: ChannelAdapterRegistry | undefined;
|
|
18
|
-
let _router: { dispatch(msg: unknown): Promise<unknown> } | undefined;
|
|
19
|
-
|
|
20
|
-
export function setChannelControllerDeps(deps: {
|
|
21
|
-
adapterRegistry: ChannelAdapterRegistry;
|
|
22
|
-
router: { dispatch(msg: unknown): Promise<unknown> };
|
|
23
|
-
}): void {
|
|
24
|
-
_adapterRegistry = deps.adapterRegistry;
|
|
25
|
-
_router = deps.router;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Channel Installation Controller
|
|
30
|
-
* Handles channel installation CRUD operations for Lark/Feishu and future channels
|
|
31
|
-
* All operations are scoped to a tenant ID
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Get tenant ID from authenticated user context
|
|
36
|
-
* Falls back to request headers for backward compatibility
|
|
37
|
-
*/
|
|
38
|
-
function getTenantId(request: FastifyRequest): string {
|
|
39
|
-
// First try to get from authenticated user context
|
|
40
|
-
const userTenantId = (request as any).user?.tenantId;
|
|
41
|
-
if (userTenantId) {
|
|
42
|
-
return userTenantId;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Fallback to request headers for backward compatibility
|
|
46
|
-
return (request.headers["x-tenant-id"] as string) || "default";
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Channel installation list response
|
|
51
|
-
*/
|
|
52
|
-
interface ChannelInstallationListResponse {
|
|
53
|
-
success: boolean;
|
|
54
|
-
message: string;
|
|
55
|
-
data: {
|
|
56
|
-
records: ChannelInstallation[];
|
|
57
|
-
total: number;
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Channel installation response
|
|
63
|
-
*/
|
|
64
|
-
interface ChannelInstallationResponse {
|
|
65
|
-
success: boolean;
|
|
66
|
-
message: string;
|
|
67
|
-
data?: ChannelInstallation;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Get channel installation store
|
|
72
|
-
*/
|
|
73
|
-
async function getInstallationStore(): Promise<ChannelInstallationStore> {
|
|
74
|
-
// Use registered store from StoreLatticeManager (supports both PG and in-memory)
|
|
75
|
-
const { getStoreLattice } = await import("@axiom-lattice/core");
|
|
76
|
-
const store = getStoreLattice("default", "channelInstallation").store as ChannelInstallationStore | undefined;
|
|
77
|
-
if (store) return store;
|
|
78
|
-
|
|
79
|
-
// Fallback: create PG store directly (legacy path)
|
|
80
|
-
const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
|
|
81
|
-
const databaseUrl = process.env.DATABASE_URL;
|
|
82
|
-
if (!databaseUrl) {
|
|
83
|
-
throw new Error("DATABASE_URL is required for channel installation store");
|
|
84
|
-
}
|
|
85
|
-
return new PostgreSQLChannelInstallationStore({
|
|
86
|
-
poolConfig: databaseUrl,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Get all channel installations for a tenant
|
|
92
|
-
*/
|
|
93
|
-
export async function getChannelInstallationList(
|
|
94
|
-
request: FastifyRequest<{
|
|
95
|
-
Querystring: { channel?: ChannelInstallationType };
|
|
96
|
-
}>,
|
|
97
|
-
reply: FastifyReply,
|
|
98
|
-
): Promise<ChannelInstallationListResponse> {
|
|
99
|
-
const tenantId = getTenantId(request);
|
|
100
|
-
const { channel } = request.query;
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
const store = await getInstallationStore();
|
|
104
|
-
const installations = await store.getInstallationsByTenant(tenantId, channel);
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
success: true,
|
|
108
|
-
message: "Channel installations retrieved successfully",
|
|
109
|
-
data: {
|
|
110
|
-
records: installations,
|
|
111
|
-
total: installations.length,
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
} catch (error) {
|
|
115
|
-
console.error("Failed to get channel installations:", error);
|
|
116
|
-
return {
|
|
117
|
-
success: false,
|
|
118
|
-
message: "Failed to retrieve channel installations",
|
|
119
|
-
data: {
|
|
120
|
-
records: [],
|
|
121
|
-
total: 0,
|
|
122
|
-
},
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Get a single channel installation by ID
|
|
129
|
-
*/
|
|
130
|
-
export async function getChannelInstallation(
|
|
131
|
-
request: FastifyRequest<{
|
|
132
|
-
Params: { installationId: string };
|
|
133
|
-
}>,
|
|
134
|
-
reply: FastifyReply,
|
|
135
|
-
): Promise<ChannelInstallationResponse> {
|
|
136
|
-
const tenantId = getTenantId(request);
|
|
137
|
-
const { installationId } = request.params;
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
const store = await getInstallationStore();
|
|
141
|
-
const installation = await store.getInstallationById(installationId);
|
|
142
|
-
|
|
143
|
-
if (!installation) {
|
|
144
|
-
reply.code(404);
|
|
145
|
-
return {
|
|
146
|
-
success: false,
|
|
147
|
-
message: "Channel installation not found",
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Verify tenant access
|
|
152
|
-
if (installation.tenantId !== tenantId) {
|
|
153
|
-
reply.code(403);
|
|
154
|
-
return {
|
|
155
|
-
success: false,
|
|
156
|
-
message: "Access denied",
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
success: true,
|
|
162
|
-
message: "Channel installation retrieved successfully",
|
|
163
|
-
data: installation,
|
|
164
|
-
};
|
|
165
|
-
} catch (error) {
|
|
166
|
-
console.error("Failed to get channel installation:", error);
|
|
167
|
-
return {
|
|
168
|
-
success: false,
|
|
169
|
-
message: "Failed to retrieve channel installation",
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Create new channel installation
|
|
176
|
-
*/
|
|
177
|
-
export async function createChannelInstallation(
|
|
178
|
-
request: FastifyRequest<{
|
|
179
|
-
Body: CreateChannelInstallationRequest & { id?: string };
|
|
180
|
-
}>,
|
|
181
|
-
reply: FastifyReply,
|
|
182
|
-
): Promise<ChannelInstallationResponse> {
|
|
183
|
-
const tenantId = getTenantId(request);
|
|
184
|
-
const body = request.body;
|
|
185
|
-
|
|
186
|
-
try {
|
|
187
|
-
// Validate required fields
|
|
188
|
-
if (!body.channel) {
|
|
189
|
-
reply.code(400);
|
|
190
|
-
return {
|
|
191
|
-
success: false,
|
|
192
|
-
message: "Channel type is required",
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (!body.config) {
|
|
197
|
-
reply.code(400);
|
|
198
|
-
return {
|
|
199
|
-
success: false,
|
|
200
|
-
message: "Configuration is required",
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Validate Lark-specific config
|
|
205
|
-
if (body.channel === "lark") {
|
|
206
|
-
const larkConfig = body.config;
|
|
207
|
-
if (!larkConfig.appId || !larkConfig.appSecret) {
|
|
208
|
-
reply.code(400);
|
|
209
|
-
return {
|
|
210
|
-
success: false,
|
|
211
|
-
message: "appId and appSecret are required for Lark installations",
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
if (!larkConfig.assistantId) {
|
|
215
|
-
reply.code(400);
|
|
216
|
-
return {
|
|
217
|
-
success: false,
|
|
218
|
-
message: "assistantId is required for Lark installations",
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const store = await getInstallationStore();
|
|
224
|
-
const installationId = body.id || randomUUID();
|
|
225
|
-
|
|
226
|
-
const installation = await store.createInstallation(
|
|
227
|
-
tenantId,
|
|
228
|
-
installationId,
|
|
229
|
-
body,
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
// Start live connection for channels that need it (e.g. WeChat polling, Lark WS)
|
|
233
|
-
if (body.enabled !== false && _adapterRegistry) {
|
|
234
|
-
const adapter = _adapterRegistry.get(body.channel);
|
|
235
|
-
if (adapter?.connect) {
|
|
236
|
-
try {
|
|
237
|
-
await adapter.connect(installation, { router: _router });
|
|
238
|
-
} catch (err) {
|
|
239
|
-
console.error("Failed to start channel connection after creation:", err);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
reply.code(201);
|
|
245
|
-
return {
|
|
246
|
-
success: true,
|
|
247
|
-
message: "Channel installation created successfully",
|
|
248
|
-
data: installation,
|
|
249
|
-
};
|
|
250
|
-
} catch (error: any) {
|
|
251
|
-
console.error("Failed to create channel installation:", error);
|
|
252
|
-
|
|
253
|
-
// Check for duplicate key error
|
|
254
|
-
if (error.message?.includes("duplicate") || error.code === "23505") {
|
|
255
|
-
reply.code(409);
|
|
256
|
-
return {
|
|
257
|
-
success: false,
|
|
258
|
-
message: "Channel installation with this ID already exists",
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return {
|
|
263
|
-
success: false,
|
|
264
|
-
message: "Failed to create channel installation",
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Update channel installation
|
|
271
|
-
*/
|
|
272
|
-
export async function updateChannelInstallation(
|
|
273
|
-
request: FastifyRequest<{
|
|
274
|
-
Params: { installationId: string };
|
|
275
|
-
Body: UpdateChannelInstallationRequest;
|
|
276
|
-
}>,
|
|
277
|
-
reply: FastifyReply,
|
|
278
|
-
): Promise<ChannelInstallationResponse> {
|
|
279
|
-
const tenantId = getTenantId(request);
|
|
280
|
-
const { installationId } = request.params;
|
|
281
|
-
const body = request.body;
|
|
282
|
-
|
|
283
|
-
try {
|
|
284
|
-
const store = await getInstallationStore();
|
|
285
|
-
|
|
286
|
-
// Verify installation exists and belongs to tenant
|
|
287
|
-
const existing = await store.getInstallationById(installationId);
|
|
288
|
-
if (!existing) {
|
|
289
|
-
reply.code(404);
|
|
290
|
-
return {
|
|
291
|
-
success: false,
|
|
292
|
-
message: "Channel installation not found",
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (existing.tenantId !== tenantId) {
|
|
297
|
-
reply.code(403);
|
|
298
|
-
return {
|
|
299
|
-
success: false,
|
|
300
|
-
message: "Access denied",
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const installation = await store.updateInstallation(
|
|
305
|
-
tenantId,
|
|
306
|
-
installationId,
|
|
307
|
-
body,
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
if (!installation) {
|
|
311
|
-
reply.code(404);
|
|
312
|
-
return {
|
|
313
|
-
success: false,
|
|
314
|
-
message: "Channel installation not found",
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Handle enable/disable — start/stop live connections
|
|
319
|
-
if (body.enabled !== undefined && body.enabled !== existing.enabled) {
|
|
320
|
-
const adapter = _adapterRegistry?.get(existing.channel);
|
|
321
|
-
if (body.enabled && adapter?.connect) {
|
|
322
|
-
// Re-connect: pass router deps so the adapter can dispatch messages
|
|
323
|
-
await adapter.connect(installation, { router: _router });
|
|
324
|
-
} else if (!body.enabled && adapter?.disconnect) {
|
|
325
|
-
await adapter.disconnect(installationId);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
return {
|
|
330
|
-
success: true,
|
|
331
|
-
message: "Channel installation updated successfully",
|
|
332
|
-
data: installation,
|
|
333
|
-
};
|
|
334
|
-
} catch (error) {
|
|
335
|
-
console.error("Failed to update channel installation:", error);
|
|
336
|
-
return {
|
|
337
|
-
success: false,
|
|
338
|
-
message: "Failed to update channel installation",
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Delete channel installation
|
|
345
|
-
*/
|
|
346
|
-
export async function deleteChannelInstallation(
|
|
347
|
-
request: FastifyRequest<{
|
|
348
|
-
Params: { installationId: string };
|
|
349
|
-
}>,
|
|
350
|
-
reply: FastifyReply,
|
|
351
|
-
): Promise<{ success: boolean; message: string }> {
|
|
352
|
-
const tenantId = getTenantId(request);
|
|
353
|
-
const { installationId } = request.params;
|
|
354
|
-
|
|
355
|
-
try {
|
|
356
|
-
const store = await getInstallationStore();
|
|
357
|
-
|
|
358
|
-
// Verify installation exists and belongs to tenant
|
|
359
|
-
const existing = await store.getInstallationById(installationId);
|
|
360
|
-
if (!existing) {
|
|
361
|
-
reply.code(404);
|
|
362
|
-
return {
|
|
363
|
-
success: false,
|
|
364
|
-
message: "Channel installation not found",
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (existing.tenantId !== tenantId) {
|
|
369
|
-
reply.code(403);
|
|
370
|
-
return {
|
|
371
|
-
success: false,
|
|
372
|
-
message: "Access denied",
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// Disconnect live connection before removing from DB
|
|
377
|
-
if (_adapterRegistry) {
|
|
378
|
-
const adapter = _adapterRegistry.get(existing.channel);
|
|
379
|
-
if (adapter?.disconnect) {
|
|
380
|
-
try {
|
|
381
|
-
await adapter.disconnect(installationId);
|
|
382
|
-
} catch (err) {
|
|
383
|
-
console.error("Failed to disconnect channel adapter:", err);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
const deleted = await store.deleteInstallation(tenantId, installationId);
|
|
389
|
-
|
|
390
|
-
if (!deleted) {
|
|
391
|
-
reply.code(404);
|
|
392
|
-
return {
|
|
393
|
-
success: false,
|
|
394
|
-
message: "Channel installation not found",
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return {
|
|
399
|
-
success: true,
|
|
400
|
-
message: "Channel installation deleted successfully",
|
|
401
|
-
};
|
|
402
|
-
} catch (error) {
|
|
403
|
-
console.error("Failed to delete channel installation:", error);
|
|
404
|
-
return {
|
|
405
|
-
success: false,
|
|
406
|
-
message: "Failed to delete channel installation",
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
}
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import { FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
-
import { CollectionService } from "../services/collection_service";
|
|
3
|
-
import type {
|
|
4
|
-
Collection,
|
|
5
|
-
CreateCollectionRequest,
|
|
6
|
-
UpdateCollectionRequest,
|
|
7
|
-
} from "@axiom-lattice/protocols";
|
|
8
|
-
|
|
9
|
-
const collectionService = new CollectionService();
|
|
10
|
-
|
|
11
|
-
function serializeCollection(collection: Collection): any {
|
|
12
|
-
return {
|
|
13
|
-
id: collection.id,
|
|
14
|
-
name: collection.name,
|
|
15
|
-
label: collection.label,
|
|
16
|
-
embeddingKey: collection.embeddingKey,
|
|
17
|
-
schema: collection.schema,
|
|
18
|
-
entryCount: (collection as any)._entryCount ?? 0,
|
|
19
|
-
createdAt:
|
|
20
|
-
collection.createdAt instanceof Date
|
|
21
|
-
? collection.createdAt.toISOString()
|
|
22
|
-
: new Date().toISOString(),
|
|
23
|
-
updatedAt:
|
|
24
|
-
collection.updatedAt instanceof Date
|
|
25
|
-
? collection.updatedAt.toISOString()
|
|
26
|
-
: new Date().toISOString(),
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export async function getCollectionList(
|
|
31
|
-
request: FastifyRequest,
|
|
32
|
-
reply: FastifyReply
|
|
33
|
-
): Promise<any> {
|
|
34
|
-
try {
|
|
35
|
-
const collections = await collectionService.getAllCollections(request);
|
|
36
|
-
return {
|
|
37
|
-
success: true,
|
|
38
|
-
message: "Successfully retrieved collection list",
|
|
39
|
-
data: {
|
|
40
|
-
records: collections.map(serializeCollection),
|
|
41
|
-
total: collections.length,
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
} catch (error: any) {
|
|
45
|
-
return reply.status(500).send({
|
|
46
|
-
success: false,
|
|
47
|
-
message: `Failed to retrieve collections: ${error.message}`,
|
|
48
|
-
data: { records: [], total: 0 },
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export async function getCollection(
|
|
54
|
-
request: FastifyRequest<{ Params: { name: string } }>,
|
|
55
|
-
reply: FastifyReply
|
|
56
|
-
): Promise<any> {
|
|
57
|
-
try {
|
|
58
|
-
const { name } = request.params;
|
|
59
|
-
const collection = await collectionService.getCollectionByName(request, name);
|
|
60
|
-
|
|
61
|
-
if (!collection) {
|
|
62
|
-
return reply
|
|
63
|
-
.status(404)
|
|
64
|
-
.send({ success: false, message: "Collection not found" });
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
success: true,
|
|
69
|
-
message: "Successfully retrieved collection",
|
|
70
|
-
data: serializeCollection(collection),
|
|
71
|
-
};
|
|
72
|
-
} catch (error: any) {
|
|
73
|
-
return reply.status(500).send({
|
|
74
|
-
success: false,
|
|
75
|
-
message: `Failed to retrieve collection: ${error.message}`,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export async function createCollection(
|
|
81
|
-
request: FastifyRequest<{ Body: CreateCollectionRequest }>,
|
|
82
|
-
reply: FastifyReply
|
|
83
|
-
): Promise<any> {
|
|
84
|
-
try {
|
|
85
|
-
const data = request.body;
|
|
86
|
-
if (!data.name) {
|
|
87
|
-
return reply
|
|
88
|
-
.status(400)
|
|
89
|
-
.send({ success: false, message: "name is required" });
|
|
90
|
-
}
|
|
91
|
-
if (!data.label) {
|
|
92
|
-
return reply
|
|
93
|
-
.status(400)
|
|
94
|
-
.send({ success: false, message: "label is required" });
|
|
95
|
-
}
|
|
96
|
-
if (!data.embeddingKey) {
|
|
97
|
-
return reply
|
|
98
|
-
.status(400)
|
|
99
|
-
.send({ success: false, message: "embeddingKey is required" });
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const collection = await collectionService.createCollection(request, data);
|
|
103
|
-
return reply.status(201).send({
|
|
104
|
-
success: true,
|
|
105
|
-
message: "Successfully created collection",
|
|
106
|
-
data: serializeCollection(collection),
|
|
107
|
-
});
|
|
108
|
-
} catch (error: any) {
|
|
109
|
-
if (error.message?.includes("already exists")) {
|
|
110
|
-
return reply.status(409).send({ success: false, message: error.message });
|
|
111
|
-
}
|
|
112
|
-
return reply.status(500).send({
|
|
113
|
-
success: false,
|
|
114
|
-
message: `Failed to create collection: ${error.message}`,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export async function updateCollection(
|
|
120
|
-
request: FastifyRequest<{
|
|
121
|
-
Params: { name: string };
|
|
122
|
-
Body: UpdateCollectionRequest;
|
|
123
|
-
}>,
|
|
124
|
-
reply: FastifyReply
|
|
125
|
-
): Promise<any> {
|
|
126
|
-
try {
|
|
127
|
-
const { name } = request.params;
|
|
128
|
-
const updates = request.body;
|
|
129
|
-
|
|
130
|
-
const collection = await collectionService.updateCollection(
|
|
131
|
-
request,
|
|
132
|
-
name,
|
|
133
|
-
updates
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
if (!collection) {
|
|
137
|
-
return reply
|
|
138
|
-
.status(404)
|
|
139
|
-
.send({ success: false, message: "Collection not found" });
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
success: true,
|
|
144
|
-
message: "Successfully updated collection",
|
|
145
|
-
data: serializeCollection(collection),
|
|
146
|
-
};
|
|
147
|
-
} catch (error: any) {
|
|
148
|
-
return reply.status(500).send({
|
|
149
|
-
success: false,
|
|
150
|
-
message: `Failed to update collection: ${error.message}`,
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export async function deleteCollection(
|
|
156
|
-
request: FastifyRequest<{ Params: { name: string } }>,
|
|
157
|
-
reply: FastifyReply
|
|
158
|
-
): Promise<any> {
|
|
159
|
-
try {
|
|
160
|
-
const { name } = request.params;
|
|
161
|
-
const result = await collectionService.deleteCollection(request, name);
|
|
162
|
-
|
|
163
|
-
if (!result) {
|
|
164
|
-
return reply
|
|
165
|
-
.status(404)
|
|
166
|
-
.send({ success: false, message: "Collection not found" });
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return { success: true, message: "Successfully deleted collection" };
|
|
170
|
-
} catch (error: any) {
|
|
171
|
-
return reply.status(500).send({
|
|
172
|
-
success: false,
|
|
173
|
-
message: `Failed to delete collection: ${error.message}`,
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
}
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { FastifyRequest, FastifyReply } from "fastify";
|
|
2
|
-
import { configService } from "../config";
|
|
3
|
-
import {
|
|
4
|
-
setQueueServiceType,
|
|
5
|
-
QueueServiceType,
|
|
6
|
-
} from "../services/queue_service";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Configuration Controller
|
|
10
|
-
* Handles configuration updates from frontend
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
interface UpdateConfigRequest {
|
|
14
|
-
Body: {
|
|
15
|
-
config: Record<string, any>;
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Update gateway configuration
|
|
21
|
-
* Accepts JSON config and loads it into environment variables
|
|
22
|
-
*/
|
|
23
|
-
export async function updateConfig(
|
|
24
|
-
request: FastifyRequest<UpdateConfigRequest>,
|
|
25
|
-
reply: FastifyReply
|
|
26
|
-
) {
|
|
27
|
-
try {
|
|
28
|
-
const { config: jsonConfig } = request.body;
|
|
29
|
-
|
|
30
|
-
if (!jsonConfig || typeof jsonConfig !== "object") {
|
|
31
|
-
return reply.status(400).send({
|
|
32
|
-
success: false,
|
|
33
|
-
error: "Invalid configuration: config must be an object",
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Update configuration service
|
|
38
|
-
configService.updateConfig(jsonConfig);
|
|
39
|
-
|
|
40
|
-
const warnings: string[] = [];
|
|
41
|
-
const requiresRestart: string[] = [];
|
|
42
|
-
|
|
43
|
-
// Check if port is being changed (requires restart)
|
|
44
|
-
if (jsonConfig.port !== undefined) {
|
|
45
|
-
requiresRestart.push("PORT");
|
|
46
|
-
warnings.push("Port change requires server restart to take effect");
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// If queue service type is being updated, reconfigure the queue service
|
|
50
|
-
if (jsonConfig.queueServiceType) {
|
|
51
|
-
setQueueServiceType(jsonConfig.queueServiceType as QueueServiceType);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// If Redis configuration is being updated and queue service is Redis, reconfigure
|
|
55
|
-
if (
|
|
56
|
-
(jsonConfig.redisUrl || jsonConfig.redisPassword) &&
|
|
57
|
-
(process.env.QUEUE_SERVICE_TYPE === "redis" ||
|
|
58
|
-
jsonConfig.queueServiceType === "redis")
|
|
59
|
-
) {
|
|
60
|
-
// Reconfigure queue service to pick up new Redis settings
|
|
61
|
-
const currentType =
|
|
62
|
-
(jsonConfig.queueServiceType as QueueServiceType) ||
|
|
63
|
-
(process.env.QUEUE_SERVICE_TYPE as QueueServiceType) ||
|
|
64
|
-
"memory";
|
|
65
|
-
if (currentType === "redis") {
|
|
66
|
-
setQueueServiceType("redis");
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Get updated config (without sensitive data)
|
|
71
|
-
const updatedConfig = configService.getConfig();
|
|
72
|
-
const safeConfig = {
|
|
73
|
-
...updatedConfig,
|
|
74
|
-
redisPassword: updatedConfig.redisPassword
|
|
75
|
-
? "***"
|
|
76
|
-
: updatedConfig.redisPassword,
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
return reply.send({
|
|
80
|
-
success: true,
|
|
81
|
-
message: "Configuration updated successfully",
|
|
82
|
-
data: safeConfig,
|
|
83
|
-
warnings: warnings.length > 0 ? warnings : undefined,
|
|
84
|
-
requiresRestart: requiresRestart.length > 0 ? requiresRestart : undefined,
|
|
85
|
-
});
|
|
86
|
-
} catch (error: any) {
|
|
87
|
-
console.error("Failed to update configuration", {
|
|
88
|
-
error: error.message,
|
|
89
|
-
stack: error.stack,
|
|
90
|
-
});
|
|
91
|
-
return reply.status(500).send({
|
|
92
|
-
success: false,
|
|
93
|
-
error: error.message || "Failed to update configuration",
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Get current gateway configuration
|
|
100
|
-
* Returns current configuration (with sensitive data masked)
|
|
101
|
-
*/
|
|
102
|
-
export async function getConfig(request: FastifyRequest, reply: FastifyReply) {
|
|
103
|
-
try {
|
|
104
|
-
const currentConfig = configService.getConfig();
|
|
105
|
-
const safeConfig = {
|
|
106
|
-
...currentConfig,
|
|
107
|
-
redisPassword: currentConfig.redisPassword
|
|
108
|
-
? "***"
|
|
109
|
-
: currentConfig.redisPassword,
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
return reply.send({
|
|
113
|
-
success: true,
|
|
114
|
-
data: safeConfig,
|
|
115
|
-
});
|
|
116
|
-
} catch (error: any) {
|
|
117
|
-
console.error("Failed to get configuration", {
|
|
118
|
-
error: error.message,
|
|
119
|
-
stack: error.stack,
|
|
120
|
-
});
|
|
121
|
-
return reply.status(500).send({
|
|
122
|
-
success: false,
|
|
123
|
-
error: error.message || "Failed to get configuration",
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|