@friggframework/devtools 2.0.0-next.4 → 2.0.0-next.41
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 +54 -0
- package/frigg-cli/deploy-command/index.js +175 -0
- 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 +332 -0
- package/frigg-cli/generate-command/terraform-generator.js +555 -0
- package/frigg-cli/generate-iam-command.js +115 -0
- package/frigg-cli/index.js +47 -1
- package/frigg-cli/index.test.js +1 -4
- package/frigg-cli/init-command/backend-first-handler.js +756 -0
- package/frigg-cli/init-command/index.js +93 -0
- package/frigg-cli/init-command/template-handler.js +143 -0
- package/frigg-cli/install-command/index.js +1 -4
- package/frigg-cli/package.json +51 -0
- package/frigg-cli/start-command/index.js +30 -4
- package/frigg-cli/start-command/start-command.test.js +155 -0
- package/frigg-cli/test/init-command.test.js +180 -0
- package/frigg-cli/test/npm-registry.test.js +319 -0
- package/frigg-cli/ui-command/index.js +154 -0
- package/frigg-cli/utils/app-resolver.js +319 -0
- package/frigg-cli/utils/backend-path.js +16 -17
- package/frigg-cli/utils/npm-registry.js +167 -0
- package/frigg-cli/utils/process-manager.js +199 -0
- package/frigg-cli/utils/repo-detection.js +405 -0
- package/infrastructure/DEPLOYMENT-INSTRUCTIONS.md +268 -0
- package/infrastructure/GENERATE-IAM-DOCS.md +278 -0
- package/infrastructure/IAM-POLICY-TEMPLATES.md +176 -0
- package/infrastructure/README.md +443 -0
- package/infrastructure/WEBSOCKET-CONFIGURATION.md +105 -0
- package/infrastructure/__tests__/fixtures/mock-aws-resources.js +391 -0
- package/infrastructure/__tests__/helpers/test-utils.js +277 -0
- package/infrastructure/aws-discovery.js +1176 -0
- package/infrastructure/aws-discovery.test.js +1220 -0
- package/infrastructure/build-time-discovery.js +206 -0
- package/infrastructure/build-time-discovery.test.js +378 -0
- package/infrastructure/create-frigg-infrastructure.js +3 -5
- package/infrastructure/env-validator.js +77 -0
- package/infrastructure/frigg-deployment-iam-stack.yaml +401 -0
- package/infrastructure/iam-generator.js +836 -0
- package/infrastructure/iam-generator.test.js +172 -0
- package/infrastructure/iam-policy-basic.json +218 -0
- package/infrastructure/iam-policy-full.json +288 -0
- package/infrastructure/integration.test.js +383 -0
- package/infrastructure/run-discovery.js +110 -0
- package/infrastructure/serverless-template.js +1493 -138
- package/infrastructure/serverless-template.test.js +1804 -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/docs/phase2-integration-guide.md +320 -0
- package/management-ui/index.html +13 -0
- package/management-ui/package-lock.json +16517 -0
- package/management-ui/package.json +76 -0
- package/management-ui/packages/devtools/frigg-cli/ui-command/index.js +302 -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 +876 -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 +1029 -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 +873 -0
- package/management-ui/server/middleware/errorHandler.js +93 -0
- package/management-ui/server/middleware/security.js +32 -0
- package/management-ui/server/processManager.js +296 -0
- package/management-ui/server/server.js +346 -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 +109 -0
- package/management-ui/src/assets/FriggLogo.svg +1 -0
- package/management-ui/src/components/AppRouter.jsx +65 -0
- package/management-ui/src/components/Button.jsx +70 -0
- package/management-ui/src/components/Card.jsx +97 -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 +481 -0
- package/management-ui/src/components/IntegrationCardEnhanced.jsx +770 -0
- package/management-ui/src/components/IntegrationExplorer.jsx +379 -0
- package/management-ui/src/components/IntegrationStatus.jsx +336 -0
- package/management-ui/src/components/Layout.jsx +716 -0
- package/management-ui/src/components/LoadingSpinner.jsx +113 -0
- package/management-ui/src/components/RepositoryPicker.jsx +248 -0
- package/management-ui/src/components/SessionMonitor.jsx +350 -0
- package/management-ui/src/components/StatusBadge.jsx +208 -0
- package/management-ui/src/components/UserContextSwitcher.jsx +212 -0
- package/management-ui/src/components/UserSimulation.jsx +327 -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 +601 -0
- package/management-ui/src/hooks/useSocket.jsx +58 -0
- package/management-ui/src/index.css +193 -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 +633 -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 +669 -0
- package/management-ui/src/pages/IntegrationDiscovery.jsx +567 -0
- package/management-ui/src/pages/IntegrationTest.jsx +742 -0
- package/management-ui/src/pages/Integrations.jsx +253 -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 +20 -9
- package/infrastructure/app-handler-helpers.js +0 -57
- package/infrastructure/backend-utils.js +0 -90
- package/infrastructure/routers/auth.js +0 -26
- package/infrastructure/routers/integration-defined-routers.js +0 -37
- package/infrastructure/routers/middleware/loadUser.js +0 -15
- package/infrastructure/routers/middleware/requireLoggedInUser.js +0 -12
- package/infrastructure/routers/user.js +0 -41
- package/infrastructure/routers/websocket.js +0 -55
- package/infrastructure/workers/integration-defined-workers.js +0 -24
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import { createStandardResponse, createErrorResponse, ERROR_CODES, asyncHandler } from '../utils/response.js'
|
|
3
|
+
|
|
4
|
+
const router = express.Router()
|
|
5
|
+
|
|
6
|
+
// In-memory log storage (in production, this would be a persistent store)
|
|
7
|
+
let applicationLogs = []
|
|
8
|
+
const MAX_LOG_ENTRIES = 10000
|
|
9
|
+
|
|
10
|
+
// Log levels
|
|
11
|
+
const LOG_LEVELS = {
|
|
12
|
+
ERROR: 'error',
|
|
13
|
+
WARN: 'warn',
|
|
14
|
+
INFO: 'info',
|
|
15
|
+
DEBUG: 'debug'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Add a log entry
|
|
20
|
+
*/
|
|
21
|
+
function addLogEntry(level, message, component = 'system', metadata = {}) {
|
|
22
|
+
const logEntry = {
|
|
23
|
+
id: Date.now().toString() + Math.random().toString(36).substr(2, 9),
|
|
24
|
+
level,
|
|
25
|
+
message,
|
|
26
|
+
component,
|
|
27
|
+
metadata,
|
|
28
|
+
timestamp: new Date().toISOString()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
applicationLogs.push(logEntry)
|
|
32
|
+
|
|
33
|
+
// Keep only the most recent entries
|
|
34
|
+
if (applicationLogs.length > MAX_LOG_ENTRIES) {
|
|
35
|
+
applicationLogs = applicationLogs.slice(-MAX_LOG_ENTRIES)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return logEntry
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get application logs
|
|
43
|
+
*/
|
|
44
|
+
router.get('/', asyncHandler(async (req, res) => {
|
|
45
|
+
const {
|
|
46
|
+
limit = 100,
|
|
47
|
+
level,
|
|
48
|
+
component,
|
|
49
|
+
since,
|
|
50
|
+
search
|
|
51
|
+
} = req.query
|
|
52
|
+
|
|
53
|
+
let filteredLogs = [...applicationLogs]
|
|
54
|
+
|
|
55
|
+
// Filter by level
|
|
56
|
+
if (level && Object.values(LOG_LEVELS).includes(level)) {
|
|
57
|
+
filteredLogs = filteredLogs.filter(log => log.level === level)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Filter by component
|
|
61
|
+
if (component) {
|
|
62
|
+
filteredLogs = filteredLogs.filter(log =>
|
|
63
|
+
log.component.toLowerCase().includes(component.toLowerCase())
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Filter by timestamp
|
|
68
|
+
if (since) {
|
|
69
|
+
const sinceDate = new Date(since)
|
|
70
|
+
if (!isNaN(sinceDate.getTime())) {
|
|
71
|
+
filteredLogs = filteredLogs.filter(log =>
|
|
72
|
+
new Date(log.timestamp) >= sinceDate
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Search in message content
|
|
78
|
+
if (search) {
|
|
79
|
+
const searchTerm = search.toLowerCase()
|
|
80
|
+
filteredLogs = filteredLogs.filter(log =>
|
|
81
|
+
log.message.toLowerCase().includes(searchTerm) ||
|
|
82
|
+
log.component.toLowerCase().includes(searchTerm) ||
|
|
83
|
+
JSON.stringify(log.metadata).toLowerCase().includes(searchTerm)
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Sort by timestamp (newest first)
|
|
88
|
+
filteredLogs.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
|
|
89
|
+
|
|
90
|
+
// Limit results
|
|
91
|
+
const limitInt = parseInt(limit)
|
|
92
|
+
if (limitInt > 0) {
|
|
93
|
+
filteredLogs = filteredLogs.slice(0, limitInt)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
res.json(createStandardResponse({
|
|
97
|
+
logs: filteredLogs,
|
|
98
|
+
total: applicationLogs.length,
|
|
99
|
+
filtered: filteredLogs.length,
|
|
100
|
+
filters: {
|
|
101
|
+
level,
|
|
102
|
+
component,
|
|
103
|
+
since,
|
|
104
|
+
search,
|
|
105
|
+
limit: limitInt
|
|
106
|
+
}
|
|
107
|
+
}))
|
|
108
|
+
}))
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Add a new log entry
|
|
112
|
+
*/
|
|
113
|
+
router.post('/', asyncHandler(async (req, res) => {
|
|
114
|
+
const { level, message, component = 'api', metadata = {} } = req.body
|
|
115
|
+
|
|
116
|
+
if (!level || !message) {
|
|
117
|
+
return res.status(400).json(
|
|
118
|
+
createErrorResponse(ERROR_CODES.INVALID_REQUEST, 'Level and message are required')
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!Object.values(LOG_LEVELS).includes(level)) {
|
|
123
|
+
return res.status(400).json(
|
|
124
|
+
createErrorResponse(ERROR_CODES.INVALID_REQUEST, `Invalid log level. Must be one of: ${Object.values(LOG_LEVELS).join(', ')}`)
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const logEntry = addLogEntry(level, message, component, metadata)
|
|
129
|
+
|
|
130
|
+
// Broadcast new log entry via WebSocket
|
|
131
|
+
const io = req.app.get('io')
|
|
132
|
+
if (io) {
|
|
133
|
+
io.emit('logs:new', logEntry)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
res.status(201).json(createStandardResponse(logEntry, 'Log entry created'))
|
|
137
|
+
}))
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Clear all logs
|
|
141
|
+
*/
|
|
142
|
+
router.delete('/', asyncHandler(async (req, res) => {
|
|
143
|
+
const previousCount = applicationLogs.length
|
|
144
|
+
applicationLogs = []
|
|
145
|
+
|
|
146
|
+
// Broadcast logs cleared event via WebSocket
|
|
147
|
+
const io = req.app.get('io')
|
|
148
|
+
if (io) {
|
|
149
|
+
io.emit('logs:cleared', {
|
|
150
|
+
clearedCount: previousCount,
|
|
151
|
+
timestamp: new Date().toISOString()
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
res.json(createStandardResponse({
|
|
156
|
+
message: 'All logs cleared',
|
|
157
|
+
clearedCount: previousCount
|
|
158
|
+
}))
|
|
159
|
+
}))
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get log statistics
|
|
163
|
+
*/
|
|
164
|
+
router.get('/stats', asyncHandler(async (req, res) => {
|
|
165
|
+
const stats = {
|
|
166
|
+
total: applicationLogs.length,
|
|
167
|
+
byLevel: {},
|
|
168
|
+
byComponent: {},
|
|
169
|
+
oldest: null,
|
|
170
|
+
newest: null
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Count by level
|
|
174
|
+
Object.values(LOG_LEVELS).forEach(level => {
|
|
175
|
+
stats.byLevel[level] = applicationLogs.filter(log => log.level === level).length
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
// Count by component
|
|
179
|
+
const components = [...new Set(applicationLogs.map(log => log.component))]
|
|
180
|
+
components.forEach(component => {
|
|
181
|
+
stats.byComponent[component] = applicationLogs.filter(log => log.component === component).length
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
// Get oldest and newest timestamps
|
|
185
|
+
if (applicationLogs.length > 0) {
|
|
186
|
+
const sortedByTime = [...applicationLogs].sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp))
|
|
187
|
+
stats.oldest = sortedByTime[0].timestamp
|
|
188
|
+
stats.newest = sortedByTime[sortedByTime.length - 1].timestamp
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
res.json(createStandardResponse(stats))
|
|
192
|
+
}))
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Export logs (for backup/analysis)
|
|
196
|
+
*/
|
|
197
|
+
router.get('/export', asyncHandler(async (req, res) => {
|
|
198
|
+
const { format = 'json', level, component, since } = req.query
|
|
199
|
+
|
|
200
|
+
let logsToExport = [...applicationLogs]
|
|
201
|
+
|
|
202
|
+
// Apply filters
|
|
203
|
+
if (level) {
|
|
204
|
+
logsToExport = logsToExport.filter(log => log.level === level)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (component) {
|
|
208
|
+
logsToExport = logsToExport.filter(log => log.component === component)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (since) {
|
|
212
|
+
const sinceDate = new Date(since)
|
|
213
|
+
if (!isNaN(sinceDate.getTime())) {
|
|
214
|
+
logsToExport = logsToExport.filter(log => new Date(log.timestamp) >= sinceDate)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Sort by timestamp
|
|
219
|
+
logsToExport.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp))
|
|
220
|
+
|
|
221
|
+
if (format === 'csv') {
|
|
222
|
+
// Export as CSV
|
|
223
|
+
const csvHeader = 'timestamp,level,component,message,metadata\n'
|
|
224
|
+
const csvRows = logsToExport.map(log => {
|
|
225
|
+
const escapedMessage = `"${log.message.replace(/"/g, '""')}"`
|
|
226
|
+
const escapedMetadata = `"${JSON.stringify(log.metadata).replace(/"/g, '""')}"`
|
|
227
|
+
return `${log.timestamp},${log.level},${log.component},${escapedMessage},${escapedMetadata}`
|
|
228
|
+
}).join('\n')
|
|
229
|
+
|
|
230
|
+
res.setHeader('Content-Type', 'text/csv')
|
|
231
|
+
res.setHeader('Content-Disposition', `attachment; filename=frigg-logs-${new Date().toISOString().split('T')[0]}.csv`)
|
|
232
|
+
res.send(csvHeader + csvRows)
|
|
233
|
+
} else {
|
|
234
|
+
// Export as JSON
|
|
235
|
+
res.setHeader('Content-Type', 'application/json')
|
|
236
|
+
res.setHeader('Content-Disposition', `attachment; filename=frigg-logs-${new Date().toISOString().split('T')[0]}.json`)
|
|
237
|
+
res.json({
|
|
238
|
+
exportedAt: new Date().toISOString(),
|
|
239
|
+
totalLogs: logsToExport.length,
|
|
240
|
+
filters: { level, component, since },
|
|
241
|
+
logs: logsToExport
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
}))
|
|
245
|
+
|
|
246
|
+
// Export the addLogEntry function for use by other modules
|
|
247
|
+
export { addLogEntry, LOG_LEVELS }
|
|
248
|
+
export default router
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import { getMonitoringService } from '../services/aws-monitor.js'
|
|
3
|
+
import { wsHandler } from '../websocket/handler.js'
|
|
4
|
+
import { asyncHandler } from '../middleware/errorHandler.js'
|
|
5
|
+
|
|
6
|
+
const router = express.Router()
|
|
7
|
+
|
|
8
|
+
// Initialize monitoring service
|
|
9
|
+
let monitoringService = null
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Initialize monitoring with configuration
|
|
13
|
+
*/
|
|
14
|
+
router.post('/init', asyncHandler(async (req, res) => {
|
|
15
|
+
const { region, stage, serviceName, collectionInterval } = req.body
|
|
16
|
+
|
|
17
|
+
// Create or reconfigure monitoring service
|
|
18
|
+
monitoringService = getMonitoringService({
|
|
19
|
+
region: region || process.env.AWS_REGION,
|
|
20
|
+
stage: stage || process.env.STAGE,
|
|
21
|
+
serviceName: serviceName || process.env.SERVICE_NAME,
|
|
22
|
+
collectionInterval: collectionInterval || 60000
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
// Set up event listeners for real-time updates
|
|
26
|
+
monitoringService.removeAllListeners() // Clear any existing listeners
|
|
27
|
+
|
|
28
|
+
monitoringService.on('metrics', (metrics) => {
|
|
29
|
+
// Broadcast metrics to all subscribed WebSocket clients
|
|
30
|
+
wsHandler.broadcast('monitoring:metrics', metrics)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
monitoringService.on('error', (error) => {
|
|
34
|
+
// Broadcast errors to WebSocket clients
|
|
35
|
+
wsHandler.broadcast('monitoring:error', error)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
res.json({
|
|
39
|
+
success: true,
|
|
40
|
+
message: 'Monitoring service initialized',
|
|
41
|
+
config: {
|
|
42
|
+
region: monitoringService.region,
|
|
43
|
+
stage: monitoringService.stage,
|
|
44
|
+
serviceName: monitoringService.serviceName,
|
|
45
|
+
collectionInterval: monitoringService.collectionInterval
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
}))
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Start monitoring
|
|
52
|
+
*/
|
|
53
|
+
router.post('/start', asyncHandler(async (req, res) => {
|
|
54
|
+
if (!monitoringService) {
|
|
55
|
+
return res.status(400).json({
|
|
56
|
+
success: false,
|
|
57
|
+
error: 'Monitoring service not initialized. Call /init first.'
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
await monitoringService.startMonitoring()
|
|
62
|
+
|
|
63
|
+
res.json({
|
|
64
|
+
success: true,
|
|
65
|
+
message: 'Monitoring started',
|
|
66
|
+
isMonitoring: monitoringService.isMonitoring
|
|
67
|
+
})
|
|
68
|
+
}))
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Stop monitoring
|
|
72
|
+
*/
|
|
73
|
+
router.post('/stop', asyncHandler(async (req, res) => {
|
|
74
|
+
if (!monitoringService) {
|
|
75
|
+
return res.status(400).json({
|
|
76
|
+
success: false,
|
|
77
|
+
error: 'Monitoring service not initialized'
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
monitoringService.stopMonitoring()
|
|
82
|
+
|
|
83
|
+
res.json({
|
|
84
|
+
success: true,
|
|
85
|
+
message: 'Monitoring stopped',
|
|
86
|
+
isMonitoring: monitoringService.isMonitoring
|
|
87
|
+
})
|
|
88
|
+
}))
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get monitoring status
|
|
92
|
+
*/
|
|
93
|
+
router.get('/status', asyncHandler(async (req, res) => {
|
|
94
|
+
if (!monitoringService) {
|
|
95
|
+
return res.json({
|
|
96
|
+
initialized: false,
|
|
97
|
+
isMonitoring: false
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
res.json({
|
|
102
|
+
initialized: true,
|
|
103
|
+
isMonitoring: monitoringService.isMonitoring,
|
|
104
|
+
config: {
|
|
105
|
+
region: monitoringService.region,
|
|
106
|
+
stage: monitoringService.stage,
|
|
107
|
+
serviceName: monitoringService.serviceName,
|
|
108
|
+
collectionInterval: monitoringService.collectionInterval
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
}))
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get latest metrics
|
|
115
|
+
*/
|
|
116
|
+
router.get('/metrics/latest', asyncHandler(async (req, res) => {
|
|
117
|
+
if (!monitoringService) {
|
|
118
|
+
return res.status(400).json({
|
|
119
|
+
success: false,
|
|
120
|
+
error: 'Monitoring service not initialized'
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const metrics = monitoringService.getLatestMetrics()
|
|
125
|
+
|
|
126
|
+
if (!metrics) {
|
|
127
|
+
return res.json({
|
|
128
|
+
success: true,
|
|
129
|
+
message: 'No metrics available yet',
|
|
130
|
+
data: null
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
res.json({
|
|
135
|
+
success: true,
|
|
136
|
+
data: metrics
|
|
137
|
+
})
|
|
138
|
+
}))
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Force collect metrics now
|
|
142
|
+
*/
|
|
143
|
+
router.post('/metrics/collect', asyncHandler(async (req, res) => {
|
|
144
|
+
if (!monitoringService) {
|
|
145
|
+
return res.status(400).json({
|
|
146
|
+
success: false,
|
|
147
|
+
error: 'Monitoring service not initialized'
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const metrics = await monitoringService.collectAllMetrics()
|
|
152
|
+
|
|
153
|
+
res.json({
|
|
154
|
+
success: true,
|
|
155
|
+
message: 'Metrics collected',
|
|
156
|
+
data: metrics
|
|
157
|
+
})
|
|
158
|
+
}))
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get historical metrics
|
|
162
|
+
*/
|
|
163
|
+
router.get('/metrics/history', asyncHandler(async (req, res) => {
|
|
164
|
+
if (!monitoringService) {
|
|
165
|
+
return res.status(400).json({
|
|
166
|
+
success: false,
|
|
167
|
+
error: 'Monitoring service not initialized'
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const { limit = 10 } = req.query
|
|
172
|
+
const history = monitoringService.getHistoricalMetrics(parseInt(limit))
|
|
173
|
+
|
|
174
|
+
res.json({
|
|
175
|
+
success: true,
|
|
176
|
+
data: history
|
|
177
|
+
})
|
|
178
|
+
}))
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Publish custom metric
|
|
182
|
+
*/
|
|
183
|
+
router.post('/metrics/custom', asyncHandler(async (req, res) => {
|
|
184
|
+
if (!monitoringService) {
|
|
185
|
+
return res.status(400).json({
|
|
186
|
+
success: false,
|
|
187
|
+
error: 'Monitoring service not initialized'
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const { metricName, value, unit, dimensions } = req.body
|
|
192
|
+
|
|
193
|
+
if (!metricName || value === undefined) {
|
|
194
|
+
return res.status(400).json({
|
|
195
|
+
success: false,
|
|
196
|
+
error: 'metricName and value are required'
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
await monitoringService.publishCustomMetric(
|
|
201
|
+
metricName,
|
|
202
|
+
value,
|
|
203
|
+
unit,
|
|
204
|
+
dimensions
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
res.json({
|
|
208
|
+
success: true,
|
|
209
|
+
message: 'Custom metric published',
|
|
210
|
+
metric: {
|
|
211
|
+
name: metricName,
|
|
212
|
+
value,
|
|
213
|
+
unit: unit || 'Count'
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
}))
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Get Lambda function details
|
|
220
|
+
*/
|
|
221
|
+
router.get('/lambda/:functionName', asyncHandler(async (req, res) => {
|
|
222
|
+
if (!monitoringService) {
|
|
223
|
+
return res.status(400).json({
|
|
224
|
+
success: false,
|
|
225
|
+
error: 'Monitoring service not initialized'
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const { functionName } = req.params
|
|
230
|
+
const metrics = await monitoringService.getLambdaMetrics(functionName)
|
|
231
|
+
|
|
232
|
+
res.json({
|
|
233
|
+
success: true,
|
|
234
|
+
data: {
|
|
235
|
+
functionName,
|
|
236
|
+
metrics
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
}))
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Get API Gateway details
|
|
243
|
+
*/
|
|
244
|
+
router.get('/apigateway/:apiName', asyncHandler(async (req, res) => {
|
|
245
|
+
if (!monitoringService) {
|
|
246
|
+
return res.status(400).json({
|
|
247
|
+
success: false,
|
|
248
|
+
error: 'Monitoring service not initialized'
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const { apiName } = req.params
|
|
253
|
+
const metrics = await monitoringService.getAPIGatewayMetrics(apiName)
|
|
254
|
+
|
|
255
|
+
res.json({
|
|
256
|
+
success: true,
|
|
257
|
+
data: {
|
|
258
|
+
apiName,
|
|
259
|
+
metrics
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
}))
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Subscribe to real-time metrics via WebSocket
|
|
266
|
+
* This is just documentation - actual subscription happens via WebSocket
|
|
267
|
+
*/
|
|
268
|
+
router.get('/subscribe', (req, res) => {
|
|
269
|
+
res.json({
|
|
270
|
+
success: true,
|
|
271
|
+
message: 'To subscribe to real-time metrics, connect via WebSocket and subscribe to the "monitoring:metrics" topic',
|
|
272
|
+
websocketUrl: `ws://localhost:${process.env.PORT || 3002}`,
|
|
273
|
+
example: {
|
|
274
|
+
type: 'subscribe',
|
|
275
|
+
data: {
|
|
276
|
+
topics: ['monitoring:metrics', 'monitoring:error']
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
})
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
export default router
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { exec } from 'child_process'
|
|
2
|
+
import { promisify } from 'util'
|
|
3
|
+
|
|
4
|
+
const execAsync = promisify(exec)
|
|
5
|
+
|
|
6
|
+
export default async function(req, res) {
|
|
7
|
+
if (req.method !== 'POST') {
|
|
8
|
+
return res.status(405).json({ error: 'Method not allowed' })
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { path } = req.body
|
|
12
|
+
|
|
13
|
+
if (!path) {
|
|
14
|
+
return res.status(400).json({ error: 'Path is required' })
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
// Try to open in VS Code first
|
|
19
|
+
await execAsync(`code "${path}"`)
|
|
20
|
+
res.json({ success: true, method: 'vscode' })
|
|
21
|
+
} catch {
|
|
22
|
+
try {
|
|
23
|
+
// Fallback to open command (macOS/Linux)
|
|
24
|
+
const command = process.platform === 'darwin' ? 'open' : 'xdg-open'
|
|
25
|
+
await execAsync(`${command} "${path}"`)
|
|
26
|
+
res.json({ success: true, method: 'system' })
|
|
27
|
+
} catch (error) {
|
|
28
|
+
res.status(500).json({ error: 'Failed to open in IDE', details: error.message })
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|