@axiom-lattice/gateway 2.1.28 → 2.1.30

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.
@@ -0,0 +1,432 @@
1
+ import { FastifyRequest, FastifyReply } from "fastify";
2
+ import {
3
+ getStoreLattice,
4
+ sqlDatabaseManager,
5
+ } from "@axiom-lattice/core";
6
+ import type {
7
+ DatabaseConfigStore,
8
+ DatabaseConfigEntry,
9
+ CreateDatabaseConfigRequest,
10
+ UpdateDatabaseConfigRequest,
11
+ DatabaseConfig,
12
+ } from "@axiom-lattice/protocols";
13
+ import { randomUUID } from "crypto";
14
+
15
+ /**
16
+ * Database Config Controller
17
+ * Handles database configuration CRUD operations
18
+ */
19
+
20
+ /**
21
+ * Get tenant ID from request headers
22
+ */
23
+ function getTenantId(request: FastifyRequest): string {
24
+ return (request.headers["x-tenant-id"] as string) || "default";
25
+ }
26
+
27
+ /**
28
+ * Database config list response
29
+ */
30
+ interface DatabaseConfigListResponse {
31
+ success: boolean;
32
+ message: string;
33
+ data: {
34
+ records: DatabaseConfigEntry[];
35
+ total: number;
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Database config response
41
+ */
42
+ interface DatabaseConfigResponse {
43
+ success: boolean;
44
+ message: string;
45
+ data?: DatabaseConfigEntry;
46
+ }
47
+
48
+ /**
49
+ * Test connection response
50
+ */
51
+ interface TestConnectionResponse {
52
+ success: boolean;
53
+ message: string;
54
+ data?: {
55
+ connected: boolean;
56
+ latency?: number;
57
+ error?: string;
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Get all database configs for a tenant
63
+ */
64
+ export async function getDatabaseConfigList(
65
+ request: FastifyRequest,
66
+ reply: FastifyReply
67
+ ): Promise<DatabaseConfigListResponse> {
68
+ const tenantId = getTenantId(request);
69
+
70
+ try {
71
+ const storeLattice = getStoreLattice("default", "database");
72
+ const store: DatabaseConfigStore = storeLattice.store;
73
+
74
+ const configs = await store.getAllConfigs(tenantId);
75
+
76
+ console.log('Backend: getAllConfigs returned:', configs);
77
+ if (configs.length > 0) {
78
+ console.log('Backend: First config key:', configs[0].key);
79
+ }
80
+
81
+ return {
82
+ success: true,
83
+ message: "Database configurations retrieved successfully",
84
+ data: {
85
+ records: configs,
86
+ total: configs.length,
87
+ },
88
+ };
89
+ } catch (error) {
90
+ console.error("Failed to get database configs:", error);
91
+ return {
92
+ success: false,
93
+ message: "Failed to retrieve database configurations",
94
+ data: {
95
+ records: [],
96
+ total: 0,
97
+ },
98
+ };
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Get database config by key
104
+ */
105
+ export async function getDatabaseConfig(
106
+ request: FastifyRequest,
107
+ reply: FastifyReply
108
+ ): Promise<DatabaseConfigResponse> {
109
+ const tenantId = getTenantId(request);
110
+ const { key } = request.params as { key: string };
111
+
112
+ try {
113
+ const storeLattice = getStoreLattice("default", "database");
114
+ const store: DatabaseConfigStore = storeLattice.store;
115
+
116
+ const config = await store.getConfigByKey(tenantId, key);
117
+
118
+ if (!config) {
119
+ return {
120
+ success: false,
121
+ message: "Database configuration not found",
122
+ };
123
+ }
124
+
125
+ return {
126
+ success: true,
127
+ message: "Database configuration retrieved successfully",
128
+ data: config,
129
+ };
130
+ } catch (error) {
131
+ console.error("Failed to get database config:", error);
132
+ return {
133
+ success: false,
134
+ message: "Failed to retrieve database configuration",
135
+ };
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Create new database config
141
+ */
142
+ export async function createDatabaseConfig(
143
+ request: FastifyRequest,
144
+ reply: FastifyReply
145
+ ): Promise<DatabaseConfigResponse> {
146
+ const tenantId = getTenantId(request);
147
+ const body = request.body as CreateDatabaseConfigRequest & { id?: string };
148
+
149
+ try {
150
+ const storeLattice = getStoreLattice("default", "database");
151
+ const store: DatabaseConfigStore = storeLattice.store;
152
+
153
+ // Check if key already exists
154
+ const existing = await store.getConfigByKey(tenantId, body.key);
155
+ if (existing) {
156
+ reply.code(409);
157
+ return {
158
+ success: false,
159
+ message: "Database configuration with this key already exists",
160
+ };
161
+ }
162
+
163
+ const id = body.id || randomUUID();
164
+ const config = await store.createConfig(tenantId, id, body);
165
+
166
+ // Auto-register to SqlDatabaseManager
167
+ try {
168
+ sqlDatabaseManager.registerDatabase(config.key, config.config);
169
+ } catch (error) {
170
+ console.warn("Failed to auto-register database:", error);
171
+ }
172
+
173
+ reply.code(201);
174
+ return {
175
+ success: true,
176
+ message: "Database configuration created successfully",
177
+ data: config,
178
+ };
179
+ } catch (error) {
180
+ console.error("Failed to create database config:", error);
181
+ return {
182
+ success: false,
183
+ message: "Failed to create database configuration",
184
+ };
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Update database config
190
+ */
191
+ export async function updateDatabaseConfig(
192
+ request: FastifyRequest,
193
+ reply: FastifyReply
194
+ ): Promise<DatabaseConfigResponse> {
195
+ const tenantId = getTenantId(request);
196
+ const { key } = request.params as { key: string };
197
+ const updates = request.body as Partial<UpdateDatabaseConfigRequest>;
198
+
199
+ try {
200
+ const storeLattice = getStoreLattice("default", "database");
201
+ const store: DatabaseConfigStore = storeLattice.store;
202
+
203
+ const existing = await store.getConfigByKey(tenantId, key);
204
+ if (!existing) {
205
+ reply.code(404);
206
+ return {
207
+ success: false,
208
+ message: "Database configuration not found",
209
+ };
210
+ }
211
+
212
+ const updated = await store.updateConfig(tenantId, existing.id, updates);
213
+
214
+ if (!updated) {
215
+ return {
216
+ success: false,
217
+ message: "Failed to update database configuration",
218
+ };
219
+ }
220
+
221
+ // Re-register to SqlDatabaseManager if config changed
222
+ if (updates.config) {
223
+ try {
224
+ sqlDatabaseManager.registerDatabase(updated.key, updated.config);
225
+ } catch (error) {
226
+ console.warn("Failed to re-register database:", error);
227
+ }
228
+ }
229
+
230
+ return {
231
+ success: true,
232
+ message: "Database configuration updated successfully",
233
+ data: updated,
234
+ };
235
+ } catch (error) {
236
+ console.error("Failed to update database config:", error);
237
+ return {
238
+ success: false,
239
+ message: "Failed to update database configuration",
240
+ };
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Delete database config by key or id
246
+ */
247
+ export async function deleteDatabaseConfig(
248
+ request: FastifyRequest,
249
+ reply: FastifyReply
250
+ ): Promise<DatabaseConfigResponse> {
251
+ const tenantId = getTenantId(request);
252
+ const { keyOrId } = request.params as { keyOrId: string };
253
+
254
+ try {
255
+ const storeLattice = getStoreLattice("default", "database");
256
+ const store: DatabaseConfigStore = storeLattice.store;
257
+
258
+ console.log('Delete request - keyOrId:', keyOrId);
259
+
260
+ // Try to find by key first, then by id
261
+ let config = await store.getConfigByKey(tenantId, keyOrId);
262
+ let configKey = keyOrId;
263
+
264
+ if (!config) {
265
+ // Try to find by id
266
+ config = await store.getConfigById(tenantId, keyOrId);
267
+ if (config) {
268
+ configKey = config.key;
269
+ }
270
+ }
271
+
272
+ if (!config) {
273
+ reply.code(404);
274
+ return {
275
+ success: false,
276
+ message: "Database configuration not found",
277
+ };
278
+ }
279
+
280
+ console.log('Found config to delete:', { id: config.id, key: config.key });
281
+
282
+ const deleted = await store.deleteConfig(tenantId, config.id);
283
+
284
+ if (!deleted) {
285
+ return {
286
+ success: false,
287
+ message: "Failed to delete database configuration",
288
+ };
289
+ }
290
+
291
+ // Remove from SqlDatabaseManager
292
+ try {
293
+ if (sqlDatabaseManager.hasDatabase(configKey)) {
294
+ await sqlDatabaseManager.removeDatabase(configKey);
295
+ }
296
+ } catch (error) {
297
+ console.warn("Failed to remove from SqlDatabaseManager:", error);
298
+ }
299
+
300
+ return {
301
+ success: true,
302
+ message: "Database configuration deleted successfully",
303
+ };
304
+ } catch (error) {
305
+ console.error("Failed to delete database config:", error);
306
+ return {
307
+ success: false,
308
+ message: "Failed to delete database configuration",
309
+ };
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Test database connection
315
+ */
316
+ export async function testDatabaseConnection(
317
+ request: FastifyRequest,
318
+ reply: FastifyReply
319
+ ): Promise<TestConnectionResponse> {
320
+ const tenantId = getTenantId(request);
321
+ const { key } = request.params as { key: string };
322
+
323
+ try {
324
+ const storeLattice = getStoreLattice("default", "database");
325
+ const store: DatabaseConfigStore = storeLattice.store;
326
+
327
+ const config = await store.getConfigByKey(tenantId, key);
328
+ if (!config) {
329
+ reply.code(404);
330
+ return {
331
+ success: false,
332
+ message: "Database configuration not found",
333
+ };
334
+ }
335
+
336
+ // Register temporarily for testing
337
+ const testKey = `__test_${key}_${Date.now()}`;
338
+ sqlDatabaseManager.registerDatabase(testKey, config.config);
339
+
340
+ const startTime = Date.now();
341
+ const db = sqlDatabaseManager.getDatabase(testKey);
342
+
343
+ try {
344
+ // Try to connect and list tables
345
+ await db.connect();
346
+ await db.listTables();
347
+ const latency = Date.now() - startTime;
348
+
349
+ // Disconnect gracefully with delay to ensure all operations complete
350
+ await new Promise(resolve => setTimeout(resolve, 100));
351
+ await db.disconnect();
352
+
353
+ // Cleanup
354
+ await sqlDatabaseManager.removeDatabase(testKey);
355
+
356
+ return {
357
+ success: true,
358
+ message: "Connection test successful",
359
+ data: {
360
+ connected: true,
361
+ latency,
362
+ },
363
+ };
364
+ } catch (error) {
365
+ // Cleanup on error
366
+ try {
367
+ await db.disconnect();
368
+ await sqlDatabaseManager.removeDatabase(testKey);
369
+ } catch {}
370
+
371
+ return {
372
+ success: true,
373
+ message: "Connection test failed",
374
+ data: {
375
+ connected: false,
376
+ error: error instanceof Error ? error.message : "Unknown error",
377
+ },
378
+ };
379
+ }
380
+ } catch (error) {
381
+ console.error("Failed to test database connection:", error);
382
+ return {
383
+ success: false,
384
+ message: "Failed to test database connection",
385
+ data: {
386
+ connected: false,
387
+ error: error instanceof Error ? error.message : "Unknown error",
388
+ },
389
+ };
390
+ }
391
+ }
392
+
393
+ /**
394
+ * Register database config routes
395
+ */
396
+ export function registerDatabaseConfigRoutes(app: any): void {
397
+ // Get all configs
398
+ app.get(
399
+ "/api/database-configs",
400
+ getDatabaseConfigList
401
+ );
402
+
403
+ // Get config by key
404
+ app.get(
405
+ "/api/database-configs/:key",
406
+ getDatabaseConfig
407
+ );
408
+
409
+ // Create config
410
+ app.post(
411
+ "/api/database-configs",
412
+ createDatabaseConfig
413
+ );
414
+
415
+ // Update config
416
+ app.put(
417
+ "/api/database-configs/:key",
418
+ updateDatabaseConfig
419
+ );
420
+
421
+ // Delete config by key or id
422
+ app.delete(
423
+ "/api/database-configs/:keyOrId",
424
+ deleteDatabaseConfig
425
+ );
426
+
427
+ // Test connection
428
+ app.post(
429
+ "/api/database-configs/:key/test",
430
+ testDatabaseConnection
431
+ );
432
+ }