@friggframework/devtools 2.0.0--canary.398.7664c46.0 → 2.0.0--canary.400.bed3308.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.
- package/frigg-cli/.eslintrc.js +141 -0
- package/frigg-cli/__tests__/jest.config.js +102 -0
- package/frigg-cli/__tests__/unit/commands/build.test.js +483 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +418 -0
- package/frigg-cli/__tests__/unit/commands/ui.test.js +592 -0
- package/frigg-cli/__tests__/utils/command-tester.js +170 -0
- package/frigg-cli/__tests__/utils/mock-factory.js +270 -0
- package/frigg-cli/__tests__/utils/test-fixtures.js +463 -0
- package/frigg-cli/__tests__/utils/test-setup.js +286 -0
- package/frigg-cli/build-command/index.js +15 -2
- package/frigg-cli/deploy-command/index.js +15 -2
- package/frigg-cli/generate-command/__tests__/generate-command.test.js +312 -0
- package/frigg-cli/generate-command/azure-generator.js +43 -0
- package/frigg-cli/generate-command/gcp-generator.js +47 -0
- package/frigg-cli/generate-command/index.js +350 -0
- package/frigg-cli/generate-command/terraform-generator.js +555 -0
- package/frigg-cli/index.js +66 -4
- package/frigg-cli/install-command/index.js +15 -2
- package/frigg-cli/package.json +75 -0
- package/frigg-cli/start-command/index.js +17 -2
- package/frigg-cli/ui-command/index.js +167 -0
- package/frigg-cli/utils/app-resolver.js +319 -0
- package/frigg-cli/utils/backend-path.js +38 -0
- package/frigg-cli/utils/process-manager.js +199 -0
- package/frigg-cli/utils/repo-detection.js +405 -0
- package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +43 -19
- package/infrastructure/IAM-POLICY-TEMPLATES.md +1 -1
- package/infrastructure/frigg-deployment-iam-stack.yaml +16 -2
- package/infrastructure/iam-generator.js +129 -6
- package/infrastructure/iam-policy-basic.json +29 -5
- package/infrastructure/iam-policy-full.json +28 -5
- package/infrastructure/serverless-template.js +209 -3
- package/infrastructure/serverless-template.test.js +12 -0
- package/management-ui/.eslintrc.js +22 -0
- package/management-ui/README.md +203 -0
- package/management-ui/components.json +21 -0
- package/management-ui/{dist/index.html → index.html} +1 -2
- package/management-ui/merge-conflict-cleaner.py +371 -0
- package/management-ui/package-lock.json +10997 -0
- package/management-ui/package.json +76 -0
- package/management-ui/postcss.config.js +6 -0
- package/management-ui/server/api/backend.js +256 -0
- package/management-ui/server/api/cli.js +315 -0
- package/management-ui/server/api/codegen.js +663 -0
- package/management-ui/server/api/connections.js +857 -0
- package/management-ui/server/api/discovery.js +185 -0
- package/management-ui/server/api/environment/index.js +1 -0
- package/management-ui/server/api/environment/router.js +378 -0
- package/management-ui/server/api/environment.js +328 -0
- package/management-ui/server/api/integrations.js +479 -0
- package/management-ui/server/api/logs.js +248 -0
- package/management-ui/server/api/monitoring.js +282 -0
- package/management-ui/server/api/open-ide.js +31 -0
- package/management-ui/server/api/project.js +553 -0
- package/management-ui/server/api/users/sessions.js +371 -0
- package/management-ui/server/api/users/simulation.js +254 -0
- package/management-ui/server/api/users.js +362 -0
- package/management-ui/server/api-contract.md +275 -0
- package/management-ui/server/index.js +428 -0
- package/management-ui/server/middleware/errorHandler.js +70 -0
- package/management-ui/server/middleware/security.js +32 -0
- package/management-ui/server/processManager.js +296 -0
- package/management-ui/server/server.js +188 -0
- package/management-ui/server/services/aws-monitor.js +413 -0
- package/management-ui/server/services/npm-registry.js +347 -0
- package/management-ui/server/services/template-engine.js +538 -0
- package/management-ui/server/utils/cliIntegration.js +220 -0
- package/management-ui/server/utils/environment/auditLogger.js +471 -0
- package/management-ui/server/utils/environment/awsParameterStore.js +264 -0
- package/management-ui/server/utils/environment/encryption.js +278 -0
- package/management-ui/server/utils/environment/envFileManager.js +286 -0
- package/management-ui/server/utils/import-commonjs.js +28 -0
- package/management-ui/server/utils/response.js +83 -0
- package/management-ui/server/websocket/handler.js +325 -0
- package/management-ui/src/App.jsx +51 -0
- package/management-ui/src/components/AppRouter.jsx +65 -0
- package/management-ui/src/components/Button.jsx +2 -0
- package/management-ui/src/components/Card.jsx +9 -0
- package/management-ui/src/components/EnvironmentCompare.jsx +400 -0
- package/management-ui/src/components/EnvironmentEditor.jsx +372 -0
- package/management-ui/src/components/EnvironmentImportExport.jsx +469 -0
- package/management-ui/src/components/EnvironmentSchema.jsx +491 -0
- package/management-ui/src/components/EnvironmentSecurity.jsx +463 -0
- package/management-ui/src/components/ErrorBoundary.jsx +73 -0
- package/management-ui/src/components/IntegrationCard.jsx +199 -0
- package/management-ui/src/components/IntegrationCardEnhanced.jsx +490 -0
- package/management-ui/src/components/IntegrationExplorer.jsx +379 -0
- package/management-ui/src/components/IntegrationStatus.jsx +235 -0
- package/management-ui/src/components/Layout.jsx +250 -0
- package/management-ui/src/components/LoadingSpinner.jsx +45 -0
- package/management-ui/src/components/RepositoryPicker.jsx +248 -0
- package/management-ui/src/components/SessionMonitor.jsx +255 -0
- package/management-ui/src/components/StatusBadge.jsx +70 -0
- package/management-ui/src/components/UserContextSwitcher.jsx +154 -0
- package/management-ui/src/components/UserSimulation.jsx +299 -0
- package/management-ui/src/components/Welcome.jsx +434 -0
- package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +637 -0
- package/management-ui/src/components/codegen/APIModuleSelector.jsx +227 -0
- package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +247 -0
- package/management-ui/src/components/codegen/CodePreviewEditor.jsx +316 -0
- package/management-ui/src/components/codegen/DynamicModuleForm.jsx +271 -0
- package/management-ui/src/components/codegen/FormBuilder.jsx +737 -0
- package/management-ui/src/components/codegen/IntegrationGenerator.jsx +855 -0
- package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +797 -0
- package/management-ui/src/components/codegen/SchemaBuilder.jsx +303 -0
- package/management-ui/src/components/codegen/TemplateSelector.jsx +586 -0
- package/management-ui/src/components/codegen/index.js +10 -0
- package/management-ui/src/components/connections/ConnectionConfigForm.jsx +362 -0
- package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +182 -0
- package/management-ui/src/components/connections/ConnectionTester.jsx +200 -0
- package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +292 -0
- package/management-ui/src/components/connections/OAuthFlow.jsx +204 -0
- package/management-ui/src/components/connections/index.js +5 -0
- package/management-ui/src/components/index.js +21 -0
- package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +222 -0
- package/management-ui/src/components/monitoring/LambdaMetrics.jsx +169 -0
- package/management-ui/src/components/monitoring/MetricsChart.jsx +197 -0
- package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +393 -0
- package/management-ui/src/components/monitoring/SQSMetrics.jsx +246 -0
- package/management-ui/src/components/monitoring/index.js +6 -0
- package/management-ui/src/components/monitoring/monitoring.css +218 -0
- package/management-ui/src/components/theme-provider.jsx +52 -0
- package/management-ui/src/components/theme-toggle.jsx +39 -0
- package/management-ui/src/components/ui/badge.tsx +36 -0
- package/management-ui/src/components/ui/button.test.jsx +56 -0
- package/management-ui/src/components/ui/button.tsx +57 -0
- package/management-ui/src/components/ui/card.tsx +76 -0
- package/management-ui/src/components/ui/dropdown-menu.tsx +199 -0
- package/management-ui/src/components/ui/select.tsx +157 -0
- package/management-ui/src/components/ui/skeleton.jsx +15 -0
- package/management-ui/src/hooks/useFrigg.jsx +387 -0
- package/management-ui/src/hooks/useSocket.jsx +58 -0
- package/management-ui/src/index.css +194 -0
- package/management-ui/src/lib/utils.ts +6 -0
- package/management-ui/src/main.jsx +10 -0
- package/management-ui/src/pages/CodeGeneration.jsx +14 -0
- package/management-ui/src/pages/Connections.jsx +252 -0
- package/management-ui/src/pages/ConnectionsEnhanced.jsx +427 -0
- package/management-ui/src/pages/Dashboard.jsx +311 -0
- package/management-ui/src/pages/Environment.jsx +314 -0
- package/management-ui/src/pages/IntegrationConfigure.jsx +544 -0
- package/management-ui/src/pages/IntegrationDiscovery.jsx +479 -0
- package/management-ui/src/pages/IntegrationTest.jsx +494 -0
- package/management-ui/src/pages/Integrations.jsx +254 -0
- package/management-ui/src/pages/Monitoring.jsx +17 -0
- package/management-ui/src/pages/Simulation.jsx +155 -0
- package/management-ui/src/pages/Users.jsx +492 -0
- package/management-ui/src/services/api.js +41 -0
- package/management-ui/src/services/apiModuleService.js +193 -0
- package/management-ui/src/services/websocket-handlers.js +120 -0
- package/management-ui/src/test/api/project.test.js +273 -0
- package/management-ui/src/test/components/Welcome.test.jsx +378 -0
- package/management-ui/src/test/mocks/server.js +178 -0
- package/management-ui/src/test/setup.js +61 -0
- package/management-ui/src/test/utils/test-utils.jsx +134 -0
- package/management-ui/src/utils/repository.js +98 -0
- package/management-ui/src/utils/repository.test.js +118 -0
- package/management-ui/src/workflows/phase2-integration-workflows.js +884 -0
- package/management-ui/tailwind.config.js +63 -0
- package/management-ui/tsconfig.json +37 -0
- package/management-ui/tsconfig.node.json +10 -0
- package/management-ui/vite.config.js +26 -0
- package/management-ui/vitest.config.js +38 -0
- package/package.json +5 -5
- package/management-ui/dist/assets/index-CbM64Oba.js +0 -1221
- package/management-ui/dist/assets/index-CkvseXTC.css +0 -1
- /package/management-ui/{dist/assets/FriggLogo-B7Xx8ZW1.svg → src/assets/FriggLogo.svg} +0 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import { exec } from 'child_process'
|
|
3
|
+
import { promisify } from 'util'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import fs from 'fs-extra'
|
|
6
|
+
import fetch from 'node-fetch'
|
|
7
|
+
import { createStandardResponse, createErrorResponse, ERROR_CODES, asyncHandler } from '../utils/response.js'
|
|
8
|
+
import { importCommonJS } from '../utils/import-commonjs.js'
|
|
9
|
+
import { wsHandler } from '../websocket/handler.js'
|
|
10
|
+
|
|
11
|
+
const router = express.Router();
|
|
12
|
+
const execAsync = promisify(exec);
|
|
13
|
+
|
|
14
|
+
async function getAvailableIntegrations() {
|
|
15
|
+
try {
|
|
16
|
+
// Search NPM registry for @friggframework/api-module-* packages
|
|
17
|
+
const searchUrl = 'https://registry.npmjs.org/-/v1/search?text=@friggframework%20api-module&size=100';
|
|
18
|
+
|
|
19
|
+
const response = await fetch(searchUrl);
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
throw new Error(`NPM search failed: ${response.statusText}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const data = await response.json();
|
|
25
|
+
|
|
26
|
+
// Filter and format integration packages
|
|
27
|
+
const integrations = data.objects
|
|
28
|
+
.filter(pkg => pkg.package.name.includes('@friggframework/api-module-'))
|
|
29
|
+
.map(pkg => ({
|
|
30
|
+
name: pkg.package.name,
|
|
31
|
+
version: pkg.package.version,
|
|
32
|
+
description: pkg.package.description || 'No description available',
|
|
33
|
+
category: detectCategory(pkg.package.name, pkg.package.description || '', pkg.package.keywords || []),
|
|
34
|
+
installed: false,
|
|
35
|
+
tags: pkg.package.keywords || [],
|
|
36
|
+
npmUrl: `https://www.npmjs.com/package/${pkg.package.name}`
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
console.log(`Found ${integrations.length} available integrations from NPM`);
|
|
40
|
+
return integrations;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error('Error fetching integrations from NPM:', error);
|
|
43
|
+
// Fallback to basic list if NPM search fails
|
|
44
|
+
return [
|
|
45
|
+
{
|
|
46
|
+
name: '@friggframework/api-module-hubspot',
|
|
47
|
+
version: 'latest',
|
|
48
|
+
description: 'HubSpot CRM integration for Frigg',
|
|
49
|
+
category: 'CRM',
|
|
50
|
+
installed: false
|
|
51
|
+
}
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Helper to detect integration category
|
|
57
|
+
function detectCategory(name, description, keywords) {
|
|
58
|
+
const text = `${name} ${description} ${keywords.join(' ')}`.toLowerCase();
|
|
59
|
+
|
|
60
|
+
const categoryPatterns = {
|
|
61
|
+
'CRM': ['crm', 'customer', 'salesforce', 'hubspot', 'pipedrive'],
|
|
62
|
+
'Communication': ['email', 'sms', 'chat', 'slack', 'discord', 'teams'],
|
|
63
|
+
'E-commerce': ['ecommerce', 'shop', 'store', 'payment', 'stripe', 'paypal'],
|
|
64
|
+
'Marketing': ['marketing', 'campaign', 'mailchimp', 'activecampaign'],
|
|
65
|
+
'Productivity': ['task', 'project', 'asana', 'trello', 'notion', 'jira'],
|
|
66
|
+
'Analytics': ['analytics', 'tracking', 'google', 'mixpanel', 'segment'],
|
|
67
|
+
'Support': ['support', 'helpdesk', 'ticket', 'zendesk', 'intercom'],
|
|
68
|
+
'Finance': ['accounting', 'invoice', 'quickbooks', 'xero', 'billing'],
|
|
69
|
+
'Developer Tools': ['github', 'gitlab', 'bitbucket', 'api', 'webhook'],
|
|
70
|
+
'Social Media': ['social', 'facebook', 'twitter', 'instagram', 'linkedin']
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
for (const [category, patterns] of Object.entries(categoryPatterns)) {
|
|
74
|
+
for (const pattern of patterns) {
|
|
75
|
+
if (text.includes(pattern)) {
|
|
76
|
+
return category;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return 'Other';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Helper to get actual integrations from backend.js appDefinition
|
|
85
|
+
async function getInstalledIntegrations() {
|
|
86
|
+
try {
|
|
87
|
+
// Try multiple possible backend locations
|
|
88
|
+
const possiblePaths = [
|
|
89
|
+
path.join(process.cwd(), '../../../backend'),
|
|
90
|
+
path.join(process.cwd(), '../../backend'),
|
|
91
|
+
path.join(process.cwd(), '../backend'),
|
|
92
|
+
path.join(process.cwd(), 'backend'),
|
|
93
|
+
// Also check template backend
|
|
94
|
+
path.join(process.cwd(), '../frigg-cli/templates/backend')
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
for (const backendPath of possiblePaths) {
|
|
98
|
+
const backendJsPath = path.join(backendPath, 'backend.js');
|
|
99
|
+
const indexJsPath = path.join(backendPath, 'index.js');
|
|
100
|
+
|
|
101
|
+
// Try both backend.js and index.js
|
|
102
|
+
const targetFile = await fs.pathExists(backendJsPath) ? backendJsPath :
|
|
103
|
+
await fs.pathExists(indexJsPath) ? indexJsPath : null;
|
|
104
|
+
|
|
105
|
+
if (targetFile) {
|
|
106
|
+
console.log(`Found backend file at: ${targetFile}`);
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
// Dynamically import the backend file to get the actual appDefinition
|
|
110
|
+
// Use importCommonJS helper to handle both ESM and CommonJS modules
|
|
111
|
+
const backendModule = await importCommonJS(targetFile);
|
|
112
|
+
|
|
113
|
+
// Extract appDefinition - could be default export, named export, or variable
|
|
114
|
+
const appDefinition = backendModule.default?.appDefinition ||
|
|
115
|
+
backendModule.appDefinition ||
|
|
116
|
+
backendModule.default ||
|
|
117
|
+
backendModule;
|
|
118
|
+
|
|
119
|
+
if (appDefinition && appDefinition.integrations && Array.isArray(appDefinition.integrations)) {
|
|
120
|
+
console.log(`Found ${appDefinition.integrations.length} integrations in appDefinition`);
|
|
121
|
+
|
|
122
|
+
const integrations = appDefinition.integrations.map((IntegrationClass, index) => {
|
|
123
|
+
try {
|
|
124
|
+
// Get integration metadata from static properties
|
|
125
|
+
const config = IntegrationClass.Config || {};
|
|
126
|
+
const options = IntegrationClass.Options || {};
|
|
127
|
+
const modules = IntegrationClass.modules || {};
|
|
128
|
+
const display = options.display || {};
|
|
129
|
+
|
|
130
|
+
// Extract service name from class name
|
|
131
|
+
const className = IntegrationClass.name || `Integration${index}`;
|
|
132
|
+
const serviceName = className.replace(/Integration$/, '');
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
name: config.name || serviceName.toLowerCase(),
|
|
136
|
+
displayName: display.name || serviceName,
|
|
137
|
+
description: display.description || `${serviceName} integration`,
|
|
138
|
+
category: display.category || detectCategory(serviceName.toLowerCase(), display.description || '', []),
|
|
139
|
+
version: config.version || '1.0.0',
|
|
140
|
+
installed: true,
|
|
141
|
+
status: 'active',
|
|
142
|
+
type: 'integration',
|
|
143
|
+
className: className,
|
|
144
|
+
|
|
145
|
+
// Integration configuration details
|
|
146
|
+
events: config.events || [],
|
|
147
|
+
supportedVersions: config.supportedVersions || [],
|
|
148
|
+
hasUserConfig: options.hasUserConfig || false,
|
|
149
|
+
|
|
150
|
+
// Display properties
|
|
151
|
+
icon: display.icon,
|
|
152
|
+
detailsUrl: display.detailsUrl,
|
|
153
|
+
|
|
154
|
+
// API Modules information
|
|
155
|
+
apiModules: Object.keys(modules).map(key => ({
|
|
156
|
+
name: key,
|
|
157
|
+
module: modules[key]?.name || key,
|
|
158
|
+
description: `API module for ${key}`
|
|
159
|
+
})),
|
|
160
|
+
|
|
161
|
+
// Constructor details
|
|
162
|
+
constructor: {
|
|
163
|
+
name: className,
|
|
164
|
+
hasConfig: !!config,
|
|
165
|
+
hasOptions: !!options,
|
|
166
|
+
hasModules: Object.keys(modules).length > 0
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
} catch (classError) {
|
|
170
|
+
console.error(`Error processing integration class ${IntegrationClass.name}:`, classError);
|
|
171
|
+
return {
|
|
172
|
+
name: `unknown-${index}`,
|
|
173
|
+
displayName: `Unknown Integration ${index}`,
|
|
174
|
+
description: 'Error processing integration',
|
|
175
|
+
category: 'Other',
|
|
176
|
+
installed: true,
|
|
177
|
+
status: 'error',
|
|
178
|
+
type: 'integration',
|
|
179
|
+
error: classError.message
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
console.log(`Successfully processed ${integrations.length} integrations:`,
|
|
185
|
+
integrations.map(i => `${i.displayName} (${i.name})`));
|
|
186
|
+
return integrations;
|
|
187
|
+
} else {
|
|
188
|
+
console.log('No integrations array found in appDefinition');
|
|
189
|
+
}
|
|
190
|
+
} catch (importError) {
|
|
191
|
+
console.error(`Error importing ${targetFile}:`, importError);
|
|
192
|
+
// Fall back to file parsing if dynamic import fails
|
|
193
|
+
return await parseBackendFile(targetFile);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log('No backend file found in any expected location');
|
|
199
|
+
return [];
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error('Error reading installed integrations:', error);
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Fallback function to parse backend file if dynamic import fails
|
|
207
|
+
async function parseBackendFile(filePath) {
|
|
208
|
+
try {
|
|
209
|
+
const backendContent = await fs.readFile(filePath, 'utf8');
|
|
210
|
+
const integrations = [];
|
|
211
|
+
|
|
212
|
+
// Look for integration imports and uses
|
|
213
|
+
const importMatches = backendContent.match(/import.*Integration.*from.*@friggframework/g) || [];
|
|
214
|
+
const classMatches = backendContent.match(/class.*Integration/g) || [];
|
|
215
|
+
const allMatches = [...importMatches, ...classMatches];
|
|
216
|
+
|
|
217
|
+
for (const match of allMatches) {
|
|
218
|
+
const nameMatch = match.match(/(\w+Integration)/);
|
|
219
|
+
if (nameMatch) {
|
|
220
|
+
const integrationName = nameMatch[1];
|
|
221
|
+
const serviceName = integrationName.replace('Integration', '');
|
|
222
|
+
|
|
223
|
+
// Check if this integration is in the integrations array
|
|
224
|
+
if (backendContent.includes(integrationName)) {
|
|
225
|
+
integrations.push({
|
|
226
|
+
name: serviceName.toLowerCase(),
|
|
227
|
+
displayName: serviceName,
|
|
228
|
+
description: `${serviceName} integration`,
|
|
229
|
+
category: detectCategory(serviceName.toLowerCase(), '', []),
|
|
230
|
+
installed: true,
|
|
231
|
+
status: 'active',
|
|
232
|
+
type: 'integration',
|
|
233
|
+
className: integrationName,
|
|
234
|
+
constructor: {
|
|
235
|
+
name: integrationName,
|
|
236
|
+
hasConfig: true,
|
|
237
|
+
hasOptions: true,
|
|
238
|
+
hasModules: true
|
|
239
|
+
},
|
|
240
|
+
note: 'Parsed from file (dynamic loading failed)'
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return integrations;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error('Error parsing backend file:', error);
|
|
249
|
+
return [];
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// List all integrations
|
|
254
|
+
router.get('/', async (req, res) => {
|
|
255
|
+
try {
|
|
256
|
+
const [availableApiModules, installedIntegrations] = await Promise.all([
|
|
257
|
+
getAvailableIntegrations(),
|
|
258
|
+
getInstalledIntegrations()
|
|
259
|
+
]);
|
|
260
|
+
|
|
261
|
+
// Format available API modules (not yet integrations)
|
|
262
|
+
const formattedAvailable = availableApiModules.map(apiModule => ({
|
|
263
|
+
...apiModule,
|
|
264
|
+
displayName: apiModule.name.replace('@friggframework/api-module-', '').replace(/-/g, ' '),
|
|
265
|
+
installed: false,
|
|
266
|
+
status: 'available',
|
|
267
|
+
type: 'api-module' // These are just API modules, not full integrations
|
|
268
|
+
}));
|
|
269
|
+
|
|
270
|
+
// Actual integrations already properly formatted from appDefinition
|
|
271
|
+
const formattedIntegrations = installedIntegrations.map(integration => ({
|
|
272
|
+
...integration,
|
|
273
|
+
installed: true,
|
|
274
|
+
status: integration.status || 'active'
|
|
275
|
+
}));
|
|
276
|
+
|
|
277
|
+
res.json({
|
|
278
|
+
// Main integrations array contains actual integrations from appDefinition
|
|
279
|
+
integrations: formattedIntegrations,
|
|
280
|
+
|
|
281
|
+
// Available API modules that could become integrations
|
|
282
|
+
availableApiModules: formattedAvailable,
|
|
283
|
+
|
|
284
|
+
// Summary counts
|
|
285
|
+
total: formattedIntegrations.length + formattedAvailable.length,
|
|
286
|
+
activeIntegrations: formattedIntegrations.length,
|
|
287
|
+
availableModules: formattedAvailable.length,
|
|
288
|
+
|
|
289
|
+
// Metadata about the response
|
|
290
|
+
source: 'appDefinition',
|
|
291
|
+
message: formattedIntegrations.length > 0
|
|
292
|
+
? `Found ${formattedIntegrations.length} active integrations from backend appDefinition`
|
|
293
|
+
: 'No integrations found in backend appDefinition'
|
|
294
|
+
});
|
|
295
|
+
} catch (error) {
|
|
296
|
+
res.status(500).json({
|
|
297
|
+
error: error.message,
|
|
298
|
+
details: 'Failed to fetch integrations'
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Install an integration
|
|
304
|
+
router.post('/install', async (req, res) => {
|
|
305
|
+
const { packageName } = req.body;
|
|
306
|
+
|
|
307
|
+
if (!packageName) {
|
|
308
|
+
return res.status(400).json({
|
|
309
|
+
error: 'Package name is required'
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
// Broadcast installation start
|
|
315
|
+
wsHandler.broadcast('integration-install', {
|
|
316
|
+
status: 'installing',
|
|
317
|
+
packageName,
|
|
318
|
+
message: `Installing ${packageName}...`
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Run frigg install command
|
|
322
|
+
const { stdout, stderr } = await execAsync(
|
|
323
|
+
`npx frigg install ${packageName}`,
|
|
324
|
+
{ cwd: path.join(process.cwd(), '../../../backend') }
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
// Broadcast success
|
|
328
|
+
wsHandler.broadcast('integration-install', {
|
|
329
|
+
status: 'installed',
|
|
330
|
+
packageName,
|
|
331
|
+
message: `Successfully installed ${packageName}`,
|
|
332
|
+
output: stdout
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
res.json({
|
|
336
|
+
status: 'success',
|
|
337
|
+
message: `Integration ${packageName} installed successfully`,
|
|
338
|
+
output: stdout
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
} catch (error) {
|
|
342
|
+
// Broadcast error
|
|
343
|
+
wsHandler.broadcast('integration-install', {
|
|
344
|
+
status: 'error',
|
|
345
|
+
packageName,
|
|
346
|
+
message: `Failed to install ${packageName}`,
|
|
347
|
+
error: error.message
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
res.status(500).json({
|
|
351
|
+
error: error.message,
|
|
352
|
+
details: 'Failed to install integration',
|
|
353
|
+
stderr: error.stderr
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// Configure an integration
|
|
359
|
+
router.post('/:integrationName/configure', async (req, res) => {
|
|
360
|
+
const { integrationName } = req.params;
|
|
361
|
+
const { config } = req.body;
|
|
362
|
+
|
|
363
|
+
try {
|
|
364
|
+
// This would typically update the integration configuration
|
|
365
|
+
// For now, we'll store it in a config file
|
|
366
|
+
const configPath = path.join(
|
|
367
|
+
process.cwd(),
|
|
368
|
+
'../../../backend',
|
|
369
|
+
'config',
|
|
370
|
+
'integrations',
|
|
371
|
+
`${integrationName}.json`
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
await fs.ensureDir(path.dirname(configPath));
|
|
375
|
+
await fs.writeJson(configPath, config, { spaces: 2 });
|
|
376
|
+
|
|
377
|
+
res.json({
|
|
378
|
+
status: 'success',
|
|
379
|
+
message: `Configuration saved for ${integrationName}`,
|
|
380
|
+
config
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
} catch (error) {
|
|
384
|
+
res.status(500).json({
|
|
385
|
+
error: error.message,
|
|
386
|
+
details: 'Failed to configure integration'
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// Get integration configuration
|
|
392
|
+
router.get('/:integrationName/config', async (req, res) => {
|
|
393
|
+
const { integrationName } = req.params;
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
const configPath = path.join(
|
|
397
|
+
process.cwd(),
|
|
398
|
+
'../../../backend',
|
|
399
|
+
'config',
|
|
400
|
+
'integrations',
|
|
401
|
+
`${integrationName}.json`
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
if (await fs.pathExists(configPath)) {
|
|
405
|
+
const config = await fs.readJson(configPath);
|
|
406
|
+
res.json({ config });
|
|
407
|
+
} else {
|
|
408
|
+
res.json({ config: {} });
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
} catch (error) {
|
|
412
|
+
res.status(500).json({
|
|
413
|
+
error: error.message,
|
|
414
|
+
details: 'Failed to read integration configuration'
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
// Remove an integration
|
|
420
|
+
router.delete('/:integrationName', async (req, res) => {
|
|
421
|
+
const { integrationName } = req.params;
|
|
422
|
+
|
|
423
|
+
try {
|
|
424
|
+
// Broadcast removal start
|
|
425
|
+
wsHandler.broadcast('integration-remove', {
|
|
426
|
+
status: 'removing',
|
|
427
|
+
packageName: integrationName,
|
|
428
|
+
message: `Removing ${integrationName}...`
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// Remove the package
|
|
432
|
+
const { stdout, stderr } = await execAsync(
|
|
433
|
+
`npm uninstall ${integrationName}`,
|
|
434
|
+
{ cwd: path.join(process.cwd(), '../../../backend') }
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
// Remove config if exists
|
|
438
|
+
const configPath = path.join(
|
|
439
|
+
process.cwd(),
|
|
440
|
+
'../../../backend',
|
|
441
|
+
'config',
|
|
442
|
+
'integrations',
|
|
443
|
+
`${integrationName}.json`
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
if (await fs.pathExists(configPath)) {
|
|
447
|
+
await fs.remove(configPath);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Broadcast success
|
|
451
|
+
wsHandler.broadcast('integration-remove', {
|
|
452
|
+
status: 'removed',
|
|
453
|
+
packageName: integrationName,
|
|
454
|
+
message: `Successfully removed ${integrationName}`
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
res.json({
|
|
458
|
+
status: 'success',
|
|
459
|
+
message: `Integration ${integrationName} removed successfully`
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
} catch (error) {
|
|
463
|
+
// Broadcast error
|
|
464
|
+
wsHandler.broadcast('integration-remove', {
|
|
465
|
+
status: 'error',
|
|
466
|
+
packageName: integrationName,
|
|
467
|
+
message: `Failed to remove ${integrationName}`,
|
|
468
|
+
error: error.message
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
res.status(500).json({
|
|
472
|
+
error: error.message,
|
|
473
|
+
details: 'Failed to remove integration'
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
export { getInstalledIntegrations }
|
|
479
|
+
export default router
|