@friggframework/devtools 2.0.0--canary.400.bed3308.0 → 2.0.0--canary.404.e9d4980.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 (182) hide show
  1. package/frigg-cli/build-command/index.js +3 -18
  2. package/frigg-cli/deploy-command/index.js +3 -19
  3. package/frigg-cli/index.js +1 -73
  4. package/frigg-cli/install-command/index.js +2 -15
  5. package/frigg-cli/start-command/index.js +2 -17
  6. package/infrastructure/create-frigg-infrastructure.js +2 -2
  7. package/infrastructure/serverless-template.js +79 -529
  8. package/package.json +5 -9
  9. package/frigg-cli/.eslintrc.js +0 -141
  10. package/frigg-cli/__tests__/jest.config.js +0 -102
  11. package/frigg-cli/__tests__/unit/commands/build.test.js +0 -483
  12. package/frigg-cli/__tests__/unit/commands/install.test.js +0 -418
  13. package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -592
  14. package/frigg-cli/__tests__/utils/command-tester.js +0 -170
  15. package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
  16. package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
  17. package/frigg-cli/__tests__/utils/test-setup.js +0 -286
  18. package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -312
  19. package/frigg-cli/generate-command/azure-generator.js +0 -43
  20. package/frigg-cli/generate-command/gcp-generator.js +0 -47
  21. package/frigg-cli/generate-command/index.js +0 -350
  22. package/frigg-cli/generate-command/terraform-generator.js +0 -555
  23. package/frigg-cli/generate-iam-command.js +0 -115
  24. package/frigg-cli/package.json +0 -75
  25. package/frigg-cli/ui-command/index.js +0 -167
  26. package/frigg-cli/utils/app-resolver.js +0 -319
  27. package/frigg-cli/utils/backend-path.js +0 -38
  28. package/frigg-cli/utils/process-manager.js +0 -199
  29. package/frigg-cli/utils/repo-detection.js +0 -405
  30. package/infrastructure/AWS-DISCOVERY-TROUBLESHOOTING.md +0 -245
  31. package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +0 -620
  32. package/infrastructure/DEPLOYMENT-INSTRUCTIONS.md +0 -268
  33. package/infrastructure/GENERATE-IAM-DOCS.md +0 -253
  34. package/infrastructure/IAM-POLICY-TEMPLATES.md +0 -176
  35. package/infrastructure/README-TESTING.md +0 -332
  36. package/infrastructure/README.md +0 -421
  37. package/infrastructure/WEBSOCKET-CONFIGURATION.md +0 -105
  38. package/infrastructure/__tests__/fixtures/mock-aws-resources.js +0 -391
  39. package/infrastructure/__tests__/helpers/test-utils.js +0 -277
  40. package/infrastructure/aws-discovery.js +0 -568
  41. package/infrastructure/aws-discovery.test.js +0 -373
  42. package/infrastructure/build-time-discovery.js +0 -206
  43. package/infrastructure/build-time-discovery.test.js +0 -375
  44. package/infrastructure/frigg-deployment-iam-stack.yaml +0 -393
  45. package/infrastructure/iam-generator.js +0 -810
  46. package/infrastructure/iam-generator.test.js +0 -169
  47. package/infrastructure/iam-policy-basic.json +0 -236
  48. package/infrastructure/iam-policy-full.json +0 -305
  49. package/infrastructure/integration.test.js +0 -383
  50. package/infrastructure/run-discovery.js +0 -110
  51. package/infrastructure/serverless-template.test.js +0 -553
  52. package/management-ui/.eslintrc.js +0 -22
  53. package/management-ui/README.md +0 -203
  54. package/management-ui/components.json +0 -21
  55. package/management-ui/index.html +0 -13
  56. package/management-ui/merge-conflict-cleaner.py +0 -371
  57. package/management-ui/package-lock.json +0 -10997
  58. package/management-ui/package.json +0 -76
  59. package/management-ui/postcss.config.js +0 -6
  60. package/management-ui/server/api/backend.js +0 -256
  61. package/management-ui/server/api/cli.js +0 -315
  62. package/management-ui/server/api/codegen.js +0 -663
  63. package/management-ui/server/api/connections.js +0 -857
  64. package/management-ui/server/api/discovery.js +0 -185
  65. package/management-ui/server/api/environment/index.js +0 -1
  66. package/management-ui/server/api/environment/router.js +0 -378
  67. package/management-ui/server/api/environment.js +0 -328
  68. package/management-ui/server/api/integrations.js +0 -479
  69. package/management-ui/server/api/logs.js +0 -248
  70. package/management-ui/server/api/monitoring.js +0 -282
  71. package/management-ui/server/api/open-ide.js +0 -31
  72. package/management-ui/server/api/project.js +0 -553
  73. package/management-ui/server/api/users/sessions.js +0 -371
  74. package/management-ui/server/api/users/simulation.js +0 -254
  75. package/management-ui/server/api/users.js +0 -362
  76. package/management-ui/server/api-contract.md +0 -275
  77. package/management-ui/server/index.js +0 -428
  78. package/management-ui/server/middleware/errorHandler.js +0 -70
  79. package/management-ui/server/middleware/security.js +0 -32
  80. package/management-ui/server/processManager.js +0 -296
  81. package/management-ui/server/server.js +0 -188
  82. package/management-ui/server/services/aws-monitor.js +0 -413
  83. package/management-ui/server/services/npm-registry.js +0 -347
  84. package/management-ui/server/services/template-engine.js +0 -538
  85. package/management-ui/server/utils/cliIntegration.js +0 -220
  86. package/management-ui/server/utils/environment/auditLogger.js +0 -471
  87. package/management-ui/server/utils/environment/awsParameterStore.js +0 -264
  88. package/management-ui/server/utils/environment/encryption.js +0 -278
  89. package/management-ui/server/utils/environment/envFileManager.js +0 -286
  90. package/management-ui/server/utils/import-commonjs.js +0 -28
  91. package/management-ui/server/utils/response.js +0 -83
  92. package/management-ui/server/websocket/handler.js +0 -325
  93. package/management-ui/src/App.jsx +0 -51
  94. package/management-ui/src/assets/FriggLogo.svg +0 -1
  95. package/management-ui/src/components/AppRouter.jsx +0 -65
  96. package/management-ui/src/components/Button.jsx +0 -2
  97. package/management-ui/src/components/Card.jsx +0 -9
  98. package/management-ui/src/components/EnvironmentCompare.jsx +0 -400
  99. package/management-ui/src/components/EnvironmentEditor.jsx +0 -372
  100. package/management-ui/src/components/EnvironmentImportExport.jsx +0 -469
  101. package/management-ui/src/components/EnvironmentSchema.jsx +0 -491
  102. package/management-ui/src/components/EnvironmentSecurity.jsx +0 -463
  103. package/management-ui/src/components/ErrorBoundary.jsx +0 -73
  104. package/management-ui/src/components/IntegrationCard.jsx +0 -199
  105. package/management-ui/src/components/IntegrationCardEnhanced.jsx +0 -490
  106. package/management-ui/src/components/IntegrationExplorer.jsx +0 -379
  107. package/management-ui/src/components/IntegrationStatus.jsx +0 -235
  108. package/management-ui/src/components/Layout.jsx +0 -250
  109. package/management-ui/src/components/LoadingSpinner.jsx +0 -45
  110. package/management-ui/src/components/RepositoryPicker.jsx +0 -248
  111. package/management-ui/src/components/SessionMonitor.jsx +0 -255
  112. package/management-ui/src/components/StatusBadge.jsx +0 -70
  113. package/management-ui/src/components/UserContextSwitcher.jsx +0 -154
  114. package/management-ui/src/components/UserSimulation.jsx +0 -299
  115. package/management-ui/src/components/Welcome.jsx +0 -434
  116. package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +0 -637
  117. package/management-ui/src/components/codegen/APIModuleSelector.jsx +0 -227
  118. package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +0 -247
  119. package/management-ui/src/components/codegen/CodePreviewEditor.jsx +0 -316
  120. package/management-ui/src/components/codegen/DynamicModuleForm.jsx +0 -271
  121. package/management-ui/src/components/codegen/FormBuilder.jsx +0 -737
  122. package/management-ui/src/components/codegen/IntegrationGenerator.jsx +0 -855
  123. package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +0 -797
  124. package/management-ui/src/components/codegen/SchemaBuilder.jsx +0 -303
  125. package/management-ui/src/components/codegen/TemplateSelector.jsx +0 -586
  126. package/management-ui/src/components/codegen/index.js +0 -10
  127. package/management-ui/src/components/connections/ConnectionConfigForm.jsx +0 -362
  128. package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +0 -182
  129. package/management-ui/src/components/connections/ConnectionTester.jsx +0 -200
  130. package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +0 -292
  131. package/management-ui/src/components/connections/OAuthFlow.jsx +0 -204
  132. package/management-ui/src/components/connections/index.js +0 -5
  133. package/management-ui/src/components/index.js +0 -21
  134. package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +0 -222
  135. package/management-ui/src/components/monitoring/LambdaMetrics.jsx +0 -169
  136. package/management-ui/src/components/monitoring/MetricsChart.jsx +0 -197
  137. package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +0 -393
  138. package/management-ui/src/components/monitoring/SQSMetrics.jsx +0 -246
  139. package/management-ui/src/components/monitoring/index.js +0 -6
  140. package/management-ui/src/components/monitoring/monitoring.css +0 -218
  141. package/management-ui/src/components/theme-provider.jsx +0 -52
  142. package/management-ui/src/components/theme-toggle.jsx +0 -39
  143. package/management-ui/src/components/ui/badge.tsx +0 -36
  144. package/management-ui/src/components/ui/button.test.jsx +0 -56
  145. package/management-ui/src/components/ui/button.tsx +0 -57
  146. package/management-ui/src/components/ui/card.tsx +0 -76
  147. package/management-ui/src/components/ui/dropdown-menu.tsx +0 -199
  148. package/management-ui/src/components/ui/select.tsx +0 -157
  149. package/management-ui/src/components/ui/skeleton.jsx +0 -15
  150. package/management-ui/src/hooks/useFrigg.jsx +0 -387
  151. package/management-ui/src/hooks/useSocket.jsx +0 -58
  152. package/management-ui/src/index.css +0 -194
  153. package/management-ui/src/lib/utils.ts +0 -6
  154. package/management-ui/src/main.jsx +0 -10
  155. package/management-ui/src/pages/CodeGeneration.jsx +0 -14
  156. package/management-ui/src/pages/Connections.jsx +0 -252
  157. package/management-ui/src/pages/ConnectionsEnhanced.jsx +0 -427
  158. package/management-ui/src/pages/Dashboard.jsx +0 -311
  159. package/management-ui/src/pages/Environment.jsx +0 -314
  160. package/management-ui/src/pages/IntegrationConfigure.jsx +0 -544
  161. package/management-ui/src/pages/IntegrationDiscovery.jsx +0 -479
  162. package/management-ui/src/pages/IntegrationTest.jsx +0 -494
  163. package/management-ui/src/pages/Integrations.jsx +0 -254
  164. package/management-ui/src/pages/Monitoring.jsx +0 -17
  165. package/management-ui/src/pages/Simulation.jsx +0 -155
  166. package/management-ui/src/pages/Users.jsx +0 -492
  167. package/management-ui/src/services/api.js +0 -41
  168. package/management-ui/src/services/apiModuleService.js +0 -193
  169. package/management-ui/src/services/websocket-handlers.js +0 -120
  170. package/management-ui/src/test/api/project.test.js +0 -273
  171. package/management-ui/src/test/components/Welcome.test.jsx +0 -378
  172. package/management-ui/src/test/mocks/server.js +0 -178
  173. package/management-ui/src/test/setup.js +0 -61
  174. package/management-ui/src/test/utils/test-utils.jsx +0 -134
  175. package/management-ui/src/utils/repository.js +0 -98
  176. package/management-ui/src/utils/repository.test.js +0 -118
  177. package/management-ui/src/workflows/phase2-integration-workflows.js +0 -884
  178. package/management-ui/tailwind.config.js +0 -63
  179. package/management-ui/tsconfig.json +0 -37
  180. package/management-ui/tsconfig.node.json +0 -10
  181. package/management-ui/vite.config.js +0 -26
  182. package/management-ui/vitest.config.js +0 -38
@@ -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