@friggframework/devtools 2.0.0--canary.517.35ee143.0 → 2.0.0--canary.524.06156322a.0

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.
Files changed (147) hide show
  1. package/infrastructure/domains/shared/types/app-definition.js +0 -21
  2. package/infrastructure/domains/shared/utilities/base-definition-factory.js +15 -1
  3. package/infrastructure/infrastructure-composer.js +0 -2
  4. package/package.json +15 -7
  5. package/.eslintrc.json +0 -3
  6. package/CHANGELOG.md +0 -132
  7. package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
  8. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
  9. package/infrastructure/domains/admin-scripts/index.js +0 -5
  10. package/layers/prisma/.build-complete +0 -3
  11. package/layers/prisma/nodejs/package.json +0 -8
  12. package/management-ui/.eslintrc.js +0 -22
  13. package/management-ui/components.json +0 -21
  14. package/management-ui/docs/phase2-integration-guide.md +0 -320
  15. package/management-ui/index.html +0 -13
  16. package/management-ui/package.json +0 -76
  17. package/management-ui/packages/devtools/frigg-cli/ui-command/index.js +0 -302
  18. package/management-ui/postcss.config.js +0 -6
  19. package/management-ui/server/api/backend.js +0 -256
  20. package/management-ui/server/api/cli.js +0 -315
  21. package/management-ui/server/api/codegen.js +0 -663
  22. package/management-ui/server/api/connections.js +0 -857
  23. package/management-ui/server/api/discovery.js +0 -185
  24. package/management-ui/server/api/environment/index.js +0 -1
  25. package/management-ui/server/api/environment/router.js +0 -378
  26. package/management-ui/server/api/environment.js +0 -328
  27. package/management-ui/server/api/integrations.js +0 -876
  28. package/management-ui/server/api/logs.js +0 -248
  29. package/management-ui/server/api/monitoring.js +0 -282
  30. package/management-ui/server/api/open-ide.js +0 -31
  31. package/management-ui/server/api/project.js +0 -1029
  32. package/management-ui/server/api/users/sessions.js +0 -371
  33. package/management-ui/server/api/users/simulation.js +0 -254
  34. package/management-ui/server/api/users.js +0 -362
  35. package/management-ui/server/api-contract.md +0 -275
  36. package/management-ui/server/index.js +0 -873
  37. package/management-ui/server/middleware/errorHandler.js +0 -93
  38. package/management-ui/server/middleware/security.js +0 -32
  39. package/management-ui/server/processManager.js +0 -296
  40. package/management-ui/server/server.js +0 -346
  41. package/management-ui/server/services/aws-monitor.js +0 -413
  42. package/management-ui/server/services/npm-registry.js +0 -347
  43. package/management-ui/server/services/template-engine.js +0 -538
  44. package/management-ui/server/utils/cliIntegration.js +0 -220
  45. package/management-ui/server/utils/environment/auditLogger.js +0 -471
  46. package/management-ui/server/utils/environment/awsParameterStore.js +0 -275
  47. package/management-ui/server/utils/environment/encryption.js +0 -278
  48. package/management-ui/server/utils/environment/envFileManager.js +0 -286
  49. package/management-ui/server/utils/import-commonjs.js +0 -28
  50. package/management-ui/server/utils/response.js +0 -83
  51. package/management-ui/server/websocket/handler.js +0 -325
  52. package/management-ui/src/App.jsx +0 -25
  53. package/management-ui/src/assets/FriggLogo.svg +0 -1
  54. package/management-ui/src/components/AppRouter.jsx +0 -65
  55. package/management-ui/src/components/Button.jsx +0 -70
  56. package/management-ui/src/components/Card.jsx +0 -97
  57. package/management-ui/src/components/EnvironmentCompare.jsx +0 -400
  58. package/management-ui/src/components/EnvironmentEditor.jsx +0 -372
  59. package/management-ui/src/components/EnvironmentImportExport.jsx +0 -469
  60. package/management-ui/src/components/EnvironmentSchema.jsx +0 -491
  61. package/management-ui/src/components/EnvironmentSecurity.jsx +0 -463
  62. package/management-ui/src/components/ErrorBoundary.jsx +0 -73
  63. package/management-ui/src/components/IntegrationCard.jsx +0 -481
  64. package/management-ui/src/components/IntegrationCardEnhanced.jsx +0 -770
  65. package/management-ui/src/components/IntegrationExplorer.jsx +0 -379
  66. package/management-ui/src/components/IntegrationStatus.jsx +0 -336
  67. package/management-ui/src/components/Layout.jsx +0 -716
  68. package/management-ui/src/components/LoadingSpinner.jsx +0 -113
  69. package/management-ui/src/components/RepositoryPicker.jsx +0 -248
  70. package/management-ui/src/components/SessionMonitor.jsx +0 -350
  71. package/management-ui/src/components/StatusBadge.jsx +0 -208
  72. package/management-ui/src/components/UserContextSwitcher.jsx +0 -212
  73. package/management-ui/src/components/UserSimulation.jsx +0 -327
  74. package/management-ui/src/components/Welcome.jsx +0 -434
  75. package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +0 -637
  76. package/management-ui/src/components/codegen/APIModuleSelector.jsx +0 -227
  77. package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +0 -247
  78. package/management-ui/src/components/codegen/CodePreviewEditor.jsx +0 -316
  79. package/management-ui/src/components/codegen/DynamicModuleForm.jsx +0 -271
  80. package/management-ui/src/components/codegen/FormBuilder.jsx +0 -737
  81. package/management-ui/src/components/codegen/IntegrationGenerator.jsx +0 -855
  82. package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +0 -797
  83. package/management-ui/src/components/codegen/SchemaBuilder.jsx +0 -303
  84. package/management-ui/src/components/codegen/TemplateSelector.jsx +0 -586
  85. package/management-ui/src/components/codegen/index.js +0 -10
  86. package/management-ui/src/components/connections/ConnectionConfigForm.jsx +0 -362
  87. package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +0 -182
  88. package/management-ui/src/components/connections/ConnectionTester.jsx +0 -200
  89. package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +0 -292
  90. package/management-ui/src/components/connections/OAuthFlow.jsx +0 -204
  91. package/management-ui/src/components/connections/index.js +0 -5
  92. package/management-ui/src/components/index.js +0 -21
  93. package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +0 -222
  94. package/management-ui/src/components/monitoring/LambdaMetrics.jsx +0 -169
  95. package/management-ui/src/components/monitoring/MetricsChart.jsx +0 -197
  96. package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +0 -393
  97. package/management-ui/src/components/monitoring/SQSMetrics.jsx +0 -246
  98. package/management-ui/src/components/monitoring/index.js +0 -6
  99. package/management-ui/src/components/monitoring/monitoring.css +0 -218
  100. package/management-ui/src/components/theme-provider.jsx +0 -52
  101. package/management-ui/src/components/theme-toggle.jsx +0 -39
  102. package/management-ui/src/components/ui/badge.tsx +0 -36
  103. package/management-ui/src/components/ui/button.test.jsx +0 -56
  104. package/management-ui/src/components/ui/button.tsx +0 -57
  105. package/management-ui/src/components/ui/card.tsx +0 -76
  106. package/management-ui/src/components/ui/dropdown-menu.tsx +0 -199
  107. package/management-ui/src/components/ui/select.tsx +0 -157
  108. package/management-ui/src/components/ui/skeleton.jsx +0 -15
  109. package/management-ui/src/hooks/useFrigg.jsx +0 -387
  110. package/management-ui/src/hooks/useSocket.jsx +0 -58
  111. package/management-ui/src/index.css +0 -193
  112. package/management-ui/src/lib/utils.ts +0 -6
  113. package/management-ui/src/main.jsx +0 -10
  114. package/management-ui/src/pages/CodeGeneration.jsx +0 -14
  115. package/management-ui/src/pages/Connections.jsx +0 -252
  116. package/management-ui/src/pages/ConnectionsEnhanced.jsx +0 -633
  117. package/management-ui/src/pages/Dashboard.jsx +0 -311
  118. package/management-ui/src/pages/Environment.jsx +0 -314
  119. package/management-ui/src/pages/IntegrationConfigure.jsx +0 -669
  120. package/management-ui/src/pages/IntegrationDiscovery.jsx +0 -567
  121. package/management-ui/src/pages/IntegrationTest.jsx +0 -742
  122. package/management-ui/src/pages/Integrations.jsx +0 -253
  123. package/management-ui/src/pages/Monitoring.jsx +0 -17
  124. package/management-ui/src/pages/Simulation.jsx +0 -155
  125. package/management-ui/src/pages/Users.jsx +0 -492
  126. package/management-ui/src/services/api.js +0 -41
  127. package/management-ui/src/services/apiModuleService.js +0 -193
  128. package/management-ui/src/services/websocket-handlers.js +0 -120
  129. package/management-ui/src/test/api/project.test.js +0 -273
  130. package/management-ui/src/test/components/Welcome.test.jsx +0 -378
  131. package/management-ui/src/test/mocks/server.js +0 -178
  132. package/management-ui/src/test/setup.js +0 -61
  133. package/management-ui/src/test/utils/test-utils.jsx +0 -134
  134. package/management-ui/src/utils/repository.js +0 -98
  135. package/management-ui/src/utils/repository.test.js +0 -118
  136. package/management-ui/src/workflows/phase2-integration-workflows.js +0 -884
  137. package/management-ui/tailwind.config.js +0 -63
  138. package/management-ui/tsconfig.json +0 -37
  139. package/management-ui/tsconfig.node.json +0 -10
  140. package/management-ui/vite.config.js +0 -26
  141. package/management-ui/vitest.config.js +0 -38
  142. package/test/auther-definition-method-tester.js +0 -45
  143. package/test/index.js +0 -9
  144. package/test/integration-validator.js +0 -2
  145. package/test/mock-api-readme.md +0 -102
  146. package/test/mock-api.js +0 -284
  147. package/test/mock-integration.js +0 -78
@@ -1,857 +0,0 @@
1
- import express from 'express'
2
- import path from 'path'
3
- import fs from 'fs-extra'
4
- import crypto from 'crypto'
5
- import axios from 'axios'
6
- import { createStandardResponse, createErrorResponse, ERROR_CODES, asyncHandler } from '../utils/response.js'
7
- import { wsHandler } from '../websocket/handler.js'
8
-
9
- const router = express.Router();
10
-
11
- // Helper to get connections data file path
12
- async function getConnectionsFilePath() {
13
- const dataDir = path.join(process.cwd(), '../../../backend/data');
14
- await fs.ensureDir(dataDir);
15
- return path.join(dataDir, 'connections.json');
16
- }
17
-
18
- // Helper to load connections
19
- async function loadConnections() {
20
- try {
21
- const filePath = await getConnectionsFilePath();
22
- if (await fs.pathExists(filePath)) {
23
- return await fs.readJson(filePath);
24
- }
25
- return { connections: [], entities: [] };
26
- } catch (error) {
27
- console.error('Error loading connections:', error);
28
- return { connections: [], entities: [] };
29
- }
30
- }
31
-
32
- // Helper to save connections
33
- async function saveConnections(data) {
34
- const filePath = await getConnectionsFilePath();
35
- await fs.writeJson(filePath, data, { spaces: 2 });
36
- }
37
-
38
- // Get all connections
39
- router.get('/', async (req, res) => {
40
- try {
41
- const { userId, integration, status } = req.query;
42
- const data = await loadConnections();
43
- let connections = data.connections || [];
44
-
45
- // Apply filters
46
- if (userId) {
47
- connections = connections.filter(c => c.userId === userId);
48
- }
49
-
50
- if (integration) {
51
- connections = connections.filter(c => c.integration === integration);
52
- }
53
-
54
- if (status) {
55
- connections = connections.filter(c => c.status === status);
56
- }
57
-
58
- res.json({
59
- connections,
60
- total: connections.length
61
- });
62
- } catch (error) {
63
- res.status(500).json({
64
- error: error.message,
65
- details: 'Failed to fetch connections'
66
- });
67
- }
68
- });
69
-
70
- // Get single connection
71
- router.get('/:id', async (req, res) => {
72
- const { id } = req.params;
73
-
74
- try {
75
- const data = await loadConnections();
76
- const connection = data.connections.find(c => c.id === id);
77
-
78
- if (!connection) {
79
- return res.status(404).json({
80
- error: 'Connection not found'
81
- });
82
- }
83
-
84
- res.json(connection);
85
- } catch (error) {
86
- res.status(500).json({
87
- error: error.message,
88
- details: 'Failed to fetch connection'
89
- });
90
- }
91
- });
92
-
93
- // Create new connection
94
- router.post('/', async (req, res) => {
95
- try {
96
- const { userId, integration, credentials, metadata } = req.body;
97
-
98
- if (!userId || !integration) {
99
- return res.status(400).json({
100
- error: 'userId and integration are required'
101
- });
102
- }
103
-
104
- const newConnection = {
105
- id: crypto.randomBytes(16).toString('hex'),
106
- userId,
107
- integration,
108
- status: 'active',
109
- credentials: credentials || {},
110
- metadata: metadata || {},
111
- createdAt: new Date().toISOString(),
112
- updatedAt: new Date().toISOString(),
113
- lastUsed: null
114
- };
115
-
116
- const data = await loadConnections();
117
-
118
- // Check if connection already exists
119
- const existingConnection = data.connections.find(c =>
120
- c.userId === userId && c.integration === integration
121
- );
122
-
123
- if (existingConnection) {
124
- return res.status(400).json({
125
- error: 'Connection already exists for this user and integration'
126
- });
127
- }
128
-
129
- data.connections.push(newConnection);
130
- await saveConnections(data);
131
-
132
- // Broadcast connection creation
133
- wsHandler.broadcast('connection-update', {
134
- action: 'created',
135
- connection: newConnection,
136
- timestamp: new Date().toISOString()
137
- });
138
-
139
- res.status(201).json(newConnection);
140
- } catch (error) {
141
- res.status(500).json({
142
- error: error.message,
143
- details: 'Failed to create connection'
144
- });
145
- }
146
- });
147
-
148
- // Update connection
149
- router.put('/:id', async (req, res) => {
150
- const { id } = req.params;
151
- const updates = req.body;
152
-
153
- try {
154
- const data = await loadConnections();
155
- const connectionIndex = data.connections.findIndex(c => c.id === id);
156
-
157
- if (connectionIndex === -1) {
158
- return res.status(404).json({
159
- error: 'Connection not found'
160
- });
161
- }
162
-
163
- // Update connection
164
- const updatedConnection = {
165
- ...data.connections[connectionIndex],
166
- ...updates,
167
- id, // Prevent ID from being changed
168
- updatedAt: new Date().toISOString()
169
- };
170
-
171
- data.connections[connectionIndex] = updatedConnection;
172
- await saveConnections(data);
173
-
174
- // Broadcast connection update
175
- wsHandler.broadcast('connection-update', {
176
- action: 'updated',
177
- connection: updatedConnection,
178
- timestamp: new Date().toISOString()
179
- });
180
-
181
- res.json(updatedConnection);
182
- } catch (error) {
183
- res.status(500).json({
184
- error: error.message,
185
- details: 'Failed to update connection'
186
- });
187
- }
188
- });
189
-
190
- // Delete connection
191
- router.delete('/:id', async (req, res) => {
192
- const { id } = req.params;
193
-
194
- try {
195
- const data = await loadConnections();
196
- const connectionIndex = data.connections.findIndex(c => c.id === id);
197
-
198
- if (connectionIndex === -1) {
199
- return res.status(404).json({
200
- error: 'Connection not found'
201
- });
202
- }
203
-
204
- const deletedConnection = data.connections[connectionIndex];
205
- data.connections.splice(connectionIndex, 1);
206
-
207
- // Also remove associated entities
208
- data.entities = data.entities.filter(e => e.connectionId !== id);
209
-
210
- await saveConnections(data);
211
-
212
- // Broadcast connection deletion
213
- wsHandler.broadcast('connection-update', {
214
- action: 'deleted',
215
- connectionId: id,
216
- timestamp: new Date().toISOString()
217
- });
218
-
219
- res.json({
220
- status: 'success',
221
- message: 'Connection deleted',
222
- connection: deletedConnection
223
- });
224
- } catch (error) {
225
- res.status(500).json({
226
- error: error.message,
227
- details: 'Failed to delete connection'
228
- });
229
- }
230
- });
231
-
232
- // Test connection with comprehensive checks
233
- router.post('/:id/test', async (req, res) => {
234
- const { id } = req.params;
235
- const { comprehensive = false } = req.body;
236
-
237
- try {
238
- const data = await loadConnections();
239
- const connection = data.connections.find(c => c.id === id);
240
-
241
- if (!connection) {
242
- return res.status(404).json({
243
- error: 'Connection not found'
244
- });
245
- }
246
-
247
- // Perform real connection test
248
- const results = {};
249
- const startTime = Date.now();
250
-
251
- // Test 1: Authentication validation
252
- try {
253
- const authStart = Date.now();
254
- // This would call the actual integration API
255
- // For now, simulate with a delay
256
- await new Promise(resolve => setTimeout(resolve, 100));
257
-
258
- results.auth = {
259
- success: true,
260
- message: 'Authentication valid',
261
- latency: Date.now() - authStart
262
- };
263
- } catch (error) {
264
- results.auth = {
265
- success: false,
266
- error: 'Authentication failed',
267
- message: error.message
268
- };
269
- }
270
-
271
- if (comprehensive && results.auth.success) {
272
- // Test 2: API connectivity
273
- try {
274
- const apiStart = Date.now();
275
- // Simulate API call
276
- await new Promise(resolve => setTimeout(resolve, 150));
277
-
278
- results.api = {
279
- success: true,
280
- message: 'API endpoint reachable',
281
- latency: Date.now() - apiStart
282
- };
283
- } catch (error) {
284
- results.api = {
285
- success: false,
286
- error: 'API connectivity failed',
287
- message: error.message
288
- };
289
- }
290
-
291
- // Test 3: Permissions check
292
- try {
293
- const permStart = Date.now();
294
- // Simulate permissions check
295
- await new Promise(resolve => setTimeout(resolve, 80));
296
-
297
- results.permissions = {
298
- success: true,
299
- message: 'All required permissions granted',
300
- latency: Date.now() - permStart
301
- };
302
- } catch (error) {
303
- results.permissions = {
304
- success: false,
305
- error: 'Insufficient permissions',
306
- message: error.message
307
- };
308
- }
309
-
310
- // Test 4: Sample data fetch
311
- try {
312
- const dataStart = Date.now();
313
- // Simulate data fetch
314
- await new Promise(resolve => setTimeout(resolve, 200));
315
-
316
- results.data = {
317
- success: true,
318
- message: 'Successfully fetched sample data',
319
- latency: Date.now() - dataStart
320
- };
321
- } catch (error) {
322
- results.data = {
323
- success: false,
324
- error: 'Failed to fetch data',
325
- message: error.message
326
- };
327
- }
328
- }
329
-
330
- // Calculate summary
331
- const totalLatency = Date.now() - startTime;
332
- const successfulTests = Object.values(results).filter(r => r.success).length;
333
- const totalTests = Object.keys(results).length;
334
- const avgLatency = Math.round(
335
- Object.values(results)
336
- .filter(r => r.latency)
337
- .reduce((sum, r) => sum + r.latency, 0) /
338
- Object.values(results).filter(r => r.latency).length
339
- );
340
-
341
- const summary = {
342
- success: successfulTests === totalTests,
343
- testsRun: totalTests,
344
- testsPassed: successfulTests,
345
- totalLatency,
346
- avgLatency,
347
- timestamp: new Date().toISOString(),
348
- canRefreshToken: connection.credentials?.refreshToken ? true : false
349
- };
350
-
351
- if (!summary.success) {
352
- summary.error = 'One or more tests failed';
353
- summary.suggestion = results.auth.success ?
354
- 'Check API permissions and connectivity' :
355
- 'Re-authenticate the connection';
356
- }
357
-
358
- // Update connection status and last tested
359
- connection.lastTested = new Date().toISOString();
360
- connection.status = summary.success ? 'active' : 'error';
361
- connection.lastTestResult = summary;
362
- await saveConnections(data);
363
-
364
- // Broadcast test result
365
- wsHandler.broadcast('connection-test', {
366
- connectionId: id,
367
- results,
368
- summary
369
- });
370
-
371
- res.json({ results, summary });
372
- } catch (error) {
373
- res.status(500).json({
374
- error: error.message,
375
- details: 'Failed to test connection'
376
- });
377
- }
378
- });
379
-
380
- // Get entities for a connection
381
- router.get('/:id/entities', async (req, res) => {
382
- const { id } = req.params;
383
-
384
- try {
385
- const data = await loadConnections();
386
- const entities = data.entities.filter(e => e.connectionId === id);
387
-
388
- res.json({
389
- entities,
390
- total: entities.length
391
- });
392
- } catch (error) {
393
- res.status(500).json({
394
- error: error.message,
395
- details: 'Failed to fetch entities'
396
- });
397
- }
398
- });
399
-
400
- // Create entity for a connection
401
- router.post('/:id/entities', async (req, res) => {
402
- const { id } = req.params;
403
- const { type, externalId, data: entityData } = req.body;
404
-
405
- try {
406
- const connectionsData = await loadConnections();
407
- const connection = connectionsData.connections.find(c => c.id === id);
408
-
409
- if (!connection) {
410
- return res.status(404).json({
411
- error: 'Connection not found'
412
- });
413
- }
414
-
415
- const newEntity = {
416
- id: crypto.randomBytes(16).toString('hex'),
417
- connectionId: id,
418
- type: type || 'generic',
419
- externalId: externalId || crypto.randomBytes(8).toString('hex'),
420
- data: entityData || {},
421
- createdAt: new Date().toISOString(),
422
- updatedAt: new Date().toISOString()
423
- };
424
-
425
- connectionsData.entities.push(newEntity);
426
- await saveConnections(connectionsData);
427
-
428
- // Broadcast entity creation
429
- wsHandler.broadcast('entity-update', {
430
- action: 'created',
431
- entity: newEntity,
432
- timestamp: new Date().toISOString()
433
- });
434
-
435
- res.status(201).json(newEntity);
436
- } catch (error) {
437
- res.status(500).json({
438
- error: error.message,
439
- details: 'Failed to create entity'
440
- });
441
- }
442
- });
443
-
444
- // Sync entities for a connection
445
- router.post('/:id/sync', async (req, res) => {
446
- const { id } = req.params;
447
-
448
- try {
449
- const data = await loadConnections();
450
- const connection = data.connections.find(c => c.id === id);
451
-
452
- if (!connection) {
453
- return res.status(404).json({
454
- error: 'Connection not found'
455
- });
456
- }
457
-
458
- // Simulate entity sync
459
- const syncResult = {
460
- connectionId: id,
461
- status: 'success',
462
- entitiesAdded: Math.floor(Math.random() * 10),
463
- entitiesUpdated: Math.floor(Math.random() * 5),
464
- entitiesRemoved: Math.floor(Math.random() * 2),
465
- duration: Math.floor(Math.random() * 3000) + 1000,
466
- timestamp: new Date().toISOString()
467
- };
468
-
469
- // Update connection last sync
470
- connection.lastSync = new Date().toISOString();
471
- await saveConnections(data);
472
-
473
- // Broadcast sync result
474
- wsHandler.broadcast('connection-sync', syncResult);
475
-
476
- res.json(syncResult);
477
- } catch (error) {
478
- res.status(500).json({
479
- error: error.message,
480
- details: 'Failed to sync entities'
481
- });
482
- }
483
- });
484
-
485
- // Get connection statistics
486
- router.get('/stats/summary', async (req, res) => {
487
- try {
488
- const data = await loadConnections();
489
- const connections = data.connections || [];
490
- const entities = data.entities || [];
491
-
492
- const stats = {
493
- totalConnections: connections.length,
494
- totalEntities: entities.length,
495
- byIntegration: {},
496
- byStatus: {},
497
- activeConnections: connections.filter(c => c.status === 'active').length,
498
- recentlyUsed: 0
499
- };
500
-
501
- const now = new Date();
502
- const hourAgo = new Date(now - 60 * 60 * 1000);
503
-
504
- connections.forEach(connection => {
505
- // Count by integration
506
- stats.byIntegration[connection.integration] =
507
- (stats.byIntegration[connection.integration] || 0) + 1;
508
-
509
- // Count by status
510
- stats.byStatus[connection.status] =
511
- (stats.byStatus[connection.status] || 0) + 1;
512
-
513
- // Count recently used
514
- if (connection.lastUsed && new Date(connection.lastUsed) > hourAgo) {
515
- stats.recentlyUsed++;
516
- }
517
- });
518
-
519
- // Count entities by type
520
- stats.entitiesByType = {};
521
- entities.forEach(entity => {
522
- stats.entitiesByType[entity.type] =
523
- (stats.entitiesByType[entity.type] || 0) + 1;
524
- });
525
-
526
- res.json(stats);
527
- } catch (error) {
528
- res.status(500).json({
529
- error: error.message,
530
- details: 'Failed to get connection statistics'
531
- });
532
- }
533
- });
534
-
535
- // OAuth initialization
536
- router.post('/oauth/init', async (req, res) => {
537
- const { integration, provider } = req.body;
538
-
539
- try {
540
- // Generate state for CSRF protection
541
- const state = crypto.randomBytes(32).toString('hex');
542
-
543
- // Generate PKCE code verifier and challenge
544
- const codeVerifier = crypto.randomBytes(32).toString('base64url');
545
- const codeChallenge = crypto
546
- .createHash('sha256')
547
- .update(codeVerifier)
548
- .digest('base64url');
549
-
550
- // Store OAuth session
551
- const oauthSessions = await loadOAuthSessions();
552
- oauthSessions[state] = {
553
- integration,
554
- provider,
555
- codeVerifier,
556
- status: 'pending',
557
- createdAt: new Date().toISOString()
558
- };
559
- await saveOAuthSessions(oauthSessions);
560
-
561
- // Build OAuth URL based on provider
562
- const redirectUri = `${process.env.APP_URL || 'http://localhost:3001'}/api/connections/oauth/callback`;
563
- let authUrl;
564
-
565
- switch (provider) {
566
- case 'slack':
567
- authUrl = `https://slack.com/oauth/v2/authorize?` +
568
- `client_id=${process.env.SLACK_CLIENT_ID}&` +
569
- `scope=channels:read,chat:write,users:read&` +
570
- `redirect_uri=${encodeURIComponent(redirectUri)}&` +
571
- `state=${state}`;
572
- break;
573
- case 'google':
574
- authUrl = `https://accounts.google.com/o/oauth2/v2/auth?` +
575
- `client_id=${process.env.GOOGLE_CLIENT_ID}&` +
576
- `response_type=code&` +
577
- `scope=${encodeURIComponent('https://www.googleapis.com/auth/userinfo.email')}&` +
578
- `redirect_uri=${encodeURIComponent(redirectUri)}&` +
579
- `state=${state}&` +
580
- `code_challenge=${codeChallenge}&` +
581
- `code_challenge_method=S256`;
582
- break;
583
- // Add more providers as needed
584
- default:
585
- throw new Error(`Unsupported OAuth provider: ${provider}`);
586
- }
587
-
588
- res.json({
589
- authUrl,
590
- state,
591
- codeVerifier
592
- });
593
- } catch (error) {
594
- res.status(500).json({
595
- error: error.message,
596
- details: 'Failed to initialize OAuth flow'
597
- });
598
- }
599
- });
600
-
601
- // OAuth callback
602
- router.get('/oauth/callback', async (req, res) => {
603
- const { code, state, error: oauthError } = req.query;
604
-
605
- try {
606
- const oauthSessions = await loadOAuthSessions();
607
- const session = oauthSessions[state];
608
-
609
- if (!session) {
610
- return res.status(400).send('Invalid OAuth state');
611
- }
612
-
613
- if (oauthError) {
614
- session.status = 'error';
615
- session.error = oauthError;
616
- await saveOAuthSessions(oauthSessions);
617
- return res.send('<script>window.close();</script>');
618
- }
619
-
620
- // Exchange code for tokens
621
- // This would be implemented based on the provider
622
- // For now, simulate success
623
- session.status = 'completed';
624
- session.tokens = {
625
- accessToken: crypto.randomBytes(32).toString('hex'),
626
- refreshToken: crypto.randomBytes(32).toString('hex'),
627
- expiresAt: new Date(Date.now() + 3600000).toISOString()
628
- };
629
-
630
- // Create the connection
631
- const newConnection = {
632
- id: crypto.randomBytes(16).toString('hex'),
633
- integration: session.integration,
634
- provider: session.provider,
635
- status: 'active',
636
- credentials: session.tokens,
637
- createdAt: new Date().toISOString(),
638
- updatedAt: new Date().toISOString()
639
- };
640
-
641
- const data = await loadConnections();
642
- data.connections.push(newConnection);
643
- await saveConnections(data);
644
-
645
- session.connectionId = newConnection.id;
646
- await saveOAuthSessions(oauthSessions);
647
-
648
- // Close the OAuth window
649
- res.send('<script>window.close();</script>');
650
- } catch (error) {
651
- console.error('OAuth callback error:', error);
652
- res.status(500).send('OAuth callback failed');
653
- }
654
- });
655
-
656
- // Check OAuth status
657
- router.get('/oauth/status/:state', async (req, res) => {
658
- const { state } = req.params;
659
-
660
- try {
661
- const oauthSessions = await loadOAuthSessions();
662
- const session = oauthSessions[state];
663
-
664
- if (!session) {
665
- return res.status(404).json({
666
- error: 'OAuth session not found'
667
- });
668
- }
669
-
670
- if (session.status === 'completed' && session.connectionId) {
671
- const data = await loadConnections();
672
- const connection = data.connections.find(c => c.id === session.connectionId);
673
-
674
- res.json({
675
- status: 'completed',
676
- connection
677
- });
678
-
679
- // Clean up session
680
- delete oauthSessions[state];
681
- await saveOAuthSessions(oauthSessions);
682
- } else {
683
- res.json({
684
- status: session.status,
685
- error: session.error
686
- });
687
- }
688
- } catch (error) {
689
- res.status(500).json({
690
- error: error.message,
691
- details: 'Failed to check OAuth status'
692
- });
693
- }
694
- });
695
-
696
- // Get connection health
697
- router.get('/:id/health', async (req, res) => {
698
- const { id } = req.params;
699
-
700
- try {
701
- const data = await loadConnections();
702
- const connection = data.connections.find(c => c.id === id);
703
-
704
- if (!connection) {
705
- return res.status(404).json({
706
- error: 'Connection not found'
707
- });
708
- }
709
-
710
- // Calculate health metrics
711
- const now = Date.now();
712
- const createdAt = new Date(connection.createdAt).getTime();
713
- const uptime = Math.floor((now - createdAt) / 1000);
714
-
715
- // Get API call stats (would be from actual logs)
716
- const apiCalls = {
717
- total: Math.floor(Math.random() * 1000) + 100,
718
- successful: 0,
719
- failed: 0
720
- };
721
- apiCalls.successful = Math.floor(apiCalls.total * 0.95);
722
- apiCalls.failed = apiCalls.total - apiCalls.successful;
723
-
724
- const health = {
725
- status: connection.status === 'active' ? 'healthy' : 'error',
726
- lastCheck: new Date().toISOString(),
727
- uptime,
728
- latency: connection.lastTestResult?.avgLatency || null,
729
- errorRate: (apiCalls.failed / apiCalls.total) * 100,
730
- apiCalls,
731
- recentEvents: [
732
- {
733
- type: 'sync_completed',
734
- timestamp: new Date(now - 300000).toISOString()
735
- },
736
- {
737
- type: 'api_call',
738
- timestamp: new Date(now - 60000).toISOString()
739
- }
740
- ]
741
- };
742
-
743
- // Broadcast health update
744
- wsHandler.broadcast(`connection-health-${id}`, health);
745
-
746
- res.json(health);
747
- } catch (error) {
748
- res.status(500).json({
749
- error: error.message,
750
- details: 'Failed to get connection health'
751
- });
752
- }
753
- });
754
-
755
- // Get entity relationships
756
- router.get('/:id/relationships', async (req, res) => {
757
- const { id } = req.params;
758
-
759
- try {
760
- const data = await loadConnections();
761
- const entities = data.entities.filter(e => e.connectionId === id);
762
-
763
- // Generate relationships based on entity data
764
- const relationships = [];
765
-
766
- // Example: Create relationships between entities
767
- entities.forEach((entity, index) => {
768
- if (index < entities.length - 1) {
769
- relationships.push({
770
- id: crypto.randomBytes(8).toString('hex'),
771
- fromId: entity.id,
772
- toId: entities[index + 1].id,
773
- type: 'related_to',
774
- createdAt: new Date().toISOString()
775
- });
776
- }
777
- });
778
-
779
- res.json({
780
- relationships,
781
- total: relationships.length
782
- });
783
- } catch (error) {
784
- res.status(500).json({
785
- error: error.message,
786
- details: 'Failed to fetch relationships'
787
- });
788
- }
789
- });
790
-
791
- // Update connection configuration
792
- router.put('/:id/config', async (req, res) => {
793
- const { id } = req.params;
794
- const config = req.body;
795
-
796
- try {
797
- const data = await loadConnections();
798
- const connectionIndex = data.connections.findIndex(c => c.id === id);
799
-
800
- if (connectionIndex === -1) {
801
- return res.status(404).json({
802
- error: 'Connection not found'
803
- });
804
- }
805
-
806
- // Update connection with new config
807
- data.connections[connectionIndex] = {
808
- ...data.connections[connectionIndex],
809
- ...config,
810
- id, // Prevent ID change
811
- updatedAt: new Date().toISOString()
812
- };
813
-
814
- await saveConnections(data);
815
-
816
- // Broadcast configuration update
817
- wsHandler.broadcast('connection-config-update', {
818
- connectionId: id,
819
- config,
820
- timestamp: new Date().toISOString()
821
- });
822
-
823
- res.json(data.connections[connectionIndex]);
824
- } catch (error) {
825
- res.status(500).json({
826
- error: error.message,
827
- details: 'Failed to update connection configuration'
828
- });
829
- }
830
- });
831
-
832
- // Helper functions for OAuth sessions
833
- async function getOAuthSessionsPath() {
834
- const dataDir = path.join(process.cwd(), '../../../backend/data');
835
- await fs.ensureDir(dataDir);
836
- return path.join(dataDir, 'oauth-sessions.json');
837
- }
838
-
839
- async function loadOAuthSessions() {
840
- try {
841
- const filePath = await getOAuthSessionsPath();
842
- if (await fs.pathExists(filePath)) {
843
- return await fs.readJson(filePath);
844
- }
845
- return {};
846
- } catch (error) {
847
- console.error('Error loading OAuth sessions:', error);
848
- return {};
849
- }
850
- }
851
-
852
- async function saveOAuthSessions(sessions) {
853
- const filePath = await getOAuthSessionsPath();
854
- await fs.writeJson(filePath, sessions, { spaces: 2 });
855
- }
856
-
857
- export default router