@friggframework/devtools 2.0.0-next.3 → 2.0.0-next.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +36 -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 +24 -4
- 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/AWS-DISCOVERY-TROUBLESHOOTING.md +245 -0
- package/infrastructure/AWS-IAM-CREDENTIAL-NEEDS.md +596 -0
- package/infrastructure/DEPLOYMENT-INSTRUCTIONS.md +268 -0
- package/infrastructure/GENERATE-IAM-DOCS.md +253 -0
- package/infrastructure/IAM-POLICY-TEMPLATES.md +176 -0
- package/infrastructure/README-TESTING.md +332 -0
- package/infrastructure/README.md +421 -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 +568 -0
- package/infrastructure/aws-discovery.test.js +373 -0
- package/infrastructure/build-time-discovery.js +206 -0
- package/infrastructure/build-time-discovery.test.js +375 -0
- package/infrastructure/create-frigg-infrastructure.js +3 -5
- package/infrastructure/frigg-deployment-iam-stack.yaml +379 -0
- package/infrastructure/iam-generator.js +687 -0
- package/infrastructure/iam-generator.test.js +169 -0
- package/infrastructure/iam-policy-basic.json +212 -0
- package/infrastructure/iam-policy-full.json +282 -0
- package/infrastructure/integration.test.js +383 -0
- package/infrastructure/run-discovery.js +110 -0
- package/infrastructure/serverless-template.js +923 -113
- package/infrastructure/serverless-template.test.js +541 -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 +16 -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,413 @@
|
|
|
1
|
+
import { CloudWatchClient, GetMetricStatisticsCommand, ListMetricsCommand, PutMetricDataCommand } from '@aws-sdk/client-cloudwatch'
|
|
2
|
+
import { LambdaClient, ListFunctionsCommand, GetFunctionCommand } from '@aws-sdk/client-lambda'
|
|
3
|
+
import { APIGatewayClient, GetRestApisCommand, GetResourcesCommand } from '@aws-sdk/client-api-gateway'
|
|
4
|
+
import { SQSClient, GetQueueAttributesCommand, ListQueuesCommand } from '@aws-sdk/client-sqs'
|
|
5
|
+
import { EventEmitter } from 'events'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* AWS Monitoring Service for Frigg Production Instances
|
|
9
|
+
* Provides real-time metrics collection and monitoring for AWS resources
|
|
10
|
+
*/
|
|
11
|
+
export class AWSMonitoringService extends EventEmitter {
|
|
12
|
+
constructor(config = {}) {
|
|
13
|
+
super()
|
|
14
|
+
this.region = config.region || process.env.AWS_REGION || 'us-east-1'
|
|
15
|
+
this.stage = config.stage || process.env.STAGE || 'production'
|
|
16
|
+
this.serviceName = config.serviceName || process.env.SERVICE_NAME || 'frigg'
|
|
17
|
+
|
|
18
|
+
// Initialize AWS clients
|
|
19
|
+
this.cloudWatchClient = new CloudWatchClient({ region: this.region })
|
|
20
|
+
this.lambdaClient = new LambdaClient({ region: this.region })
|
|
21
|
+
this.apiGatewayClient = new APIGatewayClient({ region: this.region })
|
|
22
|
+
this.sqsClient = new SQSClient({ region: this.region })
|
|
23
|
+
|
|
24
|
+
// Metrics collection interval (default 60 seconds)
|
|
25
|
+
this.collectionInterval = config.collectionInterval || 60000
|
|
26
|
+
this.metricsCache = new Map()
|
|
27
|
+
this.isMonitoring = false
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Start monitoring AWS resources
|
|
32
|
+
*/
|
|
33
|
+
async startMonitoring() {
|
|
34
|
+
if (this.isMonitoring) {
|
|
35
|
+
console.log('Monitoring already started')
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.isMonitoring = true
|
|
40
|
+
console.log(`Starting AWS monitoring for ${this.serviceName}-${this.stage}`)
|
|
41
|
+
|
|
42
|
+
// Initial collection
|
|
43
|
+
await this.collectAllMetrics()
|
|
44
|
+
|
|
45
|
+
// Set up periodic collection
|
|
46
|
+
this.monitoringInterval = setInterval(async () => {
|
|
47
|
+
try {
|
|
48
|
+
await this.collectAllMetrics()
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('Error collecting metrics:', error)
|
|
51
|
+
this.emit('error', { type: 'collection_error', error: error.message })
|
|
52
|
+
}
|
|
53
|
+
}, this.collectionInterval)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Stop monitoring
|
|
58
|
+
*/
|
|
59
|
+
stopMonitoring() {
|
|
60
|
+
if (this.monitoringInterval) {
|
|
61
|
+
clearInterval(this.monitoringInterval)
|
|
62
|
+
this.monitoringInterval = null
|
|
63
|
+
}
|
|
64
|
+
this.isMonitoring = false
|
|
65
|
+
console.log('Monitoring stopped')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Collect all metrics from various AWS services
|
|
70
|
+
*/
|
|
71
|
+
async collectAllMetrics() {
|
|
72
|
+
const startTime = Date.now()
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const [lambdaMetrics, apiGatewayMetrics, sqsMetrics] = await Promise.all([
|
|
76
|
+
this.collectLambdaMetrics(),
|
|
77
|
+
this.collectAPIGatewayMetrics(),
|
|
78
|
+
this.collectSQSMetrics()
|
|
79
|
+
])
|
|
80
|
+
|
|
81
|
+
const allMetrics = {
|
|
82
|
+
timestamp: new Date().toISOString(),
|
|
83
|
+
region: this.region,
|
|
84
|
+
stage: this.stage,
|
|
85
|
+
serviceName: this.serviceName,
|
|
86
|
+
lambda: lambdaMetrics,
|
|
87
|
+
apiGateway: apiGatewayMetrics,
|
|
88
|
+
sqs: sqsMetrics,
|
|
89
|
+
collectionDuration: Date.now() - startTime
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Update cache
|
|
93
|
+
this.metricsCache.set('latest', allMetrics)
|
|
94
|
+
|
|
95
|
+
// Emit metrics for real-time updates
|
|
96
|
+
this.emit('metrics', allMetrics)
|
|
97
|
+
|
|
98
|
+
return allMetrics
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error('Error collecting metrics:', error)
|
|
101
|
+
throw error
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Collect Lambda function metrics
|
|
107
|
+
*/
|
|
108
|
+
async collectLambdaMetrics() {
|
|
109
|
+
try {
|
|
110
|
+
// List all functions for this service
|
|
111
|
+
const listCommand = new ListFunctionsCommand({})
|
|
112
|
+
const { Functions } = await this.lambdaClient.send(listCommand)
|
|
113
|
+
|
|
114
|
+
// Filter functions by service name and stage
|
|
115
|
+
const serviceFunctions = Functions.filter(fn =>
|
|
116
|
+
fn.FunctionName.includes(this.serviceName) &&
|
|
117
|
+
fn.FunctionName.includes(this.stage)
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
// Collect metrics for each function
|
|
121
|
+
const functionMetrics = await Promise.all(
|
|
122
|
+
serviceFunctions.map(async (fn) => {
|
|
123
|
+
const metrics = await this.getLambdaMetrics(fn.FunctionName)
|
|
124
|
+
return {
|
|
125
|
+
functionName: fn.FunctionName,
|
|
126
|
+
runtime: fn.Runtime,
|
|
127
|
+
memorySize: fn.MemorySize,
|
|
128
|
+
timeout: fn.Timeout,
|
|
129
|
+
lastModified: fn.LastModified,
|
|
130
|
+
metrics
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
totalFunctions: functionMetrics.length,
|
|
137
|
+
functions: functionMetrics
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error('Error collecting Lambda metrics:', error)
|
|
141
|
+
return { error: error.message }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get CloudWatch metrics for a specific Lambda function
|
|
147
|
+
*/
|
|
148
|
+
async getLambdaMetrics(functionName) {
|
|
149
|
+
const endTime = new Date()
|
|
150
|
+
const startTime = new Date(endTime.getTime() - 3600000) // Last hour
|
|
151
|
+
|
|
152
|
+
const metricQueries = [
|
|
153
|
+
{ metricName: 'Invocations', stat: 'Sum' },
|
|
154
|
+
{ metricName: 'Errors', stat: 'Sum' },
|
|
155
|
+
{ metricName: 'Duration', stat: 'Average' },
|
|
156
|
+
{ metricName: 'Throttles', stat: 'Sum' },
|
|
157
|
+
{ metricName: 'ConcurrentExecutions', stat: 'Average' }
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
const metrics = {}
|
|
161
|
+
|
|
162
|
+
for (const query of metricQueries) {
|
|
163
|
+
try {
|
|
164
|
+
const command = new GetMetricStatisticsCommand({
|
|
165
|
+
Namespace: 'AWS/Lambda',
|
|
166
|
+
MetricName: query.metricName,
|
|
167
|
+
Dimensions: [
|
|
168
|
+
{
|
|
169
|
+
Name: 'FunctionName',
|
|
170
|
+
Value: functionName
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
StartTime: startTime,
|
|
174
|
+
EndTime: endTime,
|
|
175
|
+
Period: 300, // 5 minutes
|
|
176
|
+
Statistics: [query.stat]
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
const { Datapoints } = await this.cloudWatchClient.send(command)
|
|
180
|
+
|
|
181
|
+
// Get the most recent datapoint
|
|
182
|
+
const latestDatapoint = Datapoints.sort((a, b) =>
|
|
183
|
+
new Date(b.Timestamp) - new Date(a.Timestamp)
|
|
184
|
+
)[0]
|
|
185
|
+
|
|
186
|
+
metrics[query.metricName.toLowerCase()] = latestDatapoint
|
|
187
|
+
? latestDatapoint[query.stat]
|
|
188
|
+
: 0
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.error(`Error getting ${query.metricName} for ${functionName}:`, error)
|
|
191
|
+
metrics[query.metricName.toLowerCase()] = null
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return metrics
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Collect API Gateway metrics
|
|
200
|
+
*/
|
|
201
|
+
async collectAPIGatewayMetrics() {
|
|
202
|
+
try {
|
|
203
|
+
// Get REST APIs
|
|
204
|
+
const { items } = await this.apiGatewayClient.send(new GetRestApisCommand({}))
|
|
205
|
+
|
|
206
|
+
// Filter APIs by service name
|
|
207
|
+
const serviceApis = items.filter(api =>
|
|
208
|
+
api.name.includes(this.serviceName) &&
|
|
209
|
+
api.name.includes(this.stage)
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
// Collect metrics for each API
|
|
213
|
+
const apiMetrics = await Promise.all(
|
|
214
|
+
serviceApis.map(async (api) => {
|
|
215
|
+
const metrics = await this.getAPIGatewayMetrics(api.name)
|
|
216
|
+
return {
|
|
217
|
+
apiId: api.id,
|
|
218
|
+
apiName: api.name,
|
|
219
|
+
description: api.description,
|
|
220
|
+
createdDate: api.createdDate,
|
|
221
|
+
metrics
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
totalApis: apiMetrics.length,
|
|
228
|
+
apis: apiMetrics
|
|
229
|
+
}
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error('Error collecting API Gateway metrics:', error)
|
|
232
|
+
return { error: error.message }
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Get CloudWatch metrics for API Gateway
|
|
238
|
+
*/
|
|
239
|
+
async getAPIGatewayMetrics(apiName) {
|
|
240
|
+
const endTime = new Date()
|
|
241
|
+
const startTime = new Date(endTime.getTime() - 3600000) // Last hour
|
|
242
|
+
|
|
243
|
+
const metricQueries = [
|
|
244
|
+
{ metricName: 'Count', stat: 'Sum' },
|
|
245
|
+
{ metricName: '4XXError', stat: 'Sum' },
|
|
246
|
+
{ metricName: '5XXError', stat: 'Sum' },
|
|
247
|
+
{ metricName: 'Latency', stat: 'Average' },
|
|
248
|
+
{ metricName: 'IntegrationLatency', stat: 'Average' }
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
const metrics = {}
|
|
252
|
+
|
|
253
|
+
for (const query of metricQueries) {
|
|
254
|
+
try {
|
|
255
|
+
const command = new GetMetricStatisticsCommand({
|
|
256
|
+
Namespace: 'AWS/ApiGateway',
|
|
257
|
+
MetricName: query.metricName,
|
|
258
|
+
Dimensions: [
|
|
259
|
+
{
|
|
260
|
+
Name: 'ApiName',
|
|
261
|
+
Value: apiName
|
|
262
|
+
}
|
|
263
|
+
],
|
|
264
|
+
StartTime: startTime,
|
|
265
|
+
EndTime: endTime,
|
|
266
|
+
Period: 300, // 5 minutes
|
|
267
|
+
Statistics: [query.stat]
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
const { Datapoints } = await this.cloudWatchClient.send(command)
|
|
271
|
+
|
|
272
|
+
// Get the most recent datapoint
|
|
273
|
+
const latestDatapoint = Datapoints.sort((a, b) =>
|
|
274
|
+
new Date(b.Timestamp) - new Date(a.Timestamp)
|
|
275
|
+
)[0]
|
|
276
|
+
|
|
277
|
+
metrics[query.metricName.toLowerCase()] = latestDatapoint
|
|
278
|
+
? latestDatapoint[query.stat]
|
|
279
|
+
: 0
|
|
280
|
+
} catch (error) {
|
|
281
|
+
console.error(`Error getting ${query.metricName} for ${apiName}:`, error)
|
|
282
|
+
metrics[query.metricName.toLowerCase()] = null
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Calculate error rate
|
|
287
|
+
if (metrics.count > 0) {
|
|
288
|
+
metrics.errorRate = ((metrics['4xxerror'] + metrics['5xxerror']) / metrics.count) * 100
|
|
289
|
+
} else {
|
|
290
|
+
metrics.errorRate = 0
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return metrics
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Collect SQS queue metrics
|
|
298
|
+
*/
|
|
299
|
+
async collectSQSMetrics() {
|
|
300
|
+
try {
|
|
301
|
+
// List all queues
|
|
302
|
+
const { QueueUrls } = await this.sqsClient.send(new ListQueuesCommand({}))
|
|
303
|
+
|
|
304
|
+
// Filter queues by service name
|
|
305
|
+
const serviceQueues = QueueUrls.filter(url =>
|
|
306
|
+
url.includes(this.serviceName) &&
|
|
307
|
+
url.includes(this.stage)
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
// Get attributes for each queue
|
|
311
|
+
const queueMetrics = await Promise.all(
|
|
312
|
+
serviceQueues.map(async (queueUrl) => {
|
|
313
|
+
const queueName = queueUrl.split('/').pop()
|
|
314
|
+
|
|
315
|
+
try {
|
|
316
|
+
const { Attributes } = await this.sqsClient.send(new GetQueueAttributesCommand({
|
|
317
|
+
QueueUrl: queueUrl,
|
|
318
|
+
AttributeNames: ['All']
|
|
319
|
+
}))
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
queueName,
|
|
323
|
+
queueUrl,
|
|
324
|
+
messagesAvailable: parseInt(Attributes.ApproximateNumberOfMessages || 0),
|
|
325
|
+
messagesInFlight: parseInt(Attributes.ApproximateNumberOfMessagesNotVisible || 0),
|
|
326
|
+
messagesDelayed: parseInt(Attributes.ApproximateNumberOfMessagesDelayed || 0),
|
|
327
|
+
createdTimestamp: Attributes.CreatedTimestamp,
|
|
328
|
+
lastModifiedTimestamp: Attributes.LastModifiedTimestamp,
|
|
329
|
+
visibilityTimeout: parseInt(Attributes.VisibilityTimeout || 0),
|
|
330
|
+
messageRetentionPeriod: parseInt(Attributes.MessageRetentionPeriod || 0)
|
|
331
|
+
}
|
|
332
|
+
} catch (error) {
|
|
333
|
+
console.error(`Error getting attributes for queue ${queueName}:`, error)
|
|
334
|
+
return {
|
|
335
|
+
queueName,
|
|
336
|
+
queueUrl,
|
|
337
|
+
error: error.message
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
})
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
totalQueues: queueMetrics.length,
|
|
345
|
+
queues: queueMetrics
|
|
346
|
+
}
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.error('Error collecting SQS metrics:', error)
|
|
349
|
+
return { error: error.message }
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Get current cached metrics
|
|
355
|
+
*/
|
|
356
|
+
getLatestMetrics() {
|
|
357
|
+
return this.metricsCache.get('latest') || null
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Get historical metrics (last N collections)
|
|
362
|
+
*/
|
|
363
|
+
getHistoricalMetrics(limit = 10) {
|
|
364
|
+
// This would typically query from a time-series database
|
|
365
|
+
// For now, we'll just return the latest metrics
|
|
366
|
+
const latest = this.getLatestMetrics()
|
|
367
|
+
return latest ? [latest] : []
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Custom metric publishing for application-specific metrics
|
|
372
|
+
*/
|
|
373
|
+
async publishCustomMetric(metricName, value, unit = 'Count', dimensions = []) {
|
|
374
|
+
try {
|
|
375
|
+
const command = new PutMetricDataCommand({
|
|
376
|
+
Namespace: `Frigg/${this.serviceName}`,
|
|
377
|
+
MetricData: [
|
|
378
|
+
{
|
|
379
|
+
MetricName: metricName,
|
|
380
|
+
Value: value,
|
|
381
|
+
Unit: unit,
|
|
382
|
+
Timestamp: new Date(),
|
|
383
|
+
Dimensions: [
|
|
384
|
+
{
|
|
385
|
+
Name: 'Stage',
|
|
386
|
+
Value: this.stage
|
|
387
|
+
},
|
|
388
|
+
...dimensions
|
|
389
|
+
]
|
|
390
|
+
}
|
|
391
|
+
]
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
await this.cloudWatchClient.send(command)
|
|
395
|
+
console.log(`Published custom metric: ${metricName} = ${value}`)
|
|
396
|
+
} catch (error) {
|
|
397
|
+
console.error('Error publishing custom metric:', error)
|
|
398
|
+
throw error
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Create singleton instance
|
|
404
|
+
let monitoringService = null
|
|
405
|
+
|
|
406
|
+
export function getMonitoringService(config = {}) {
|
|
407
|
+
if (!monitoringService) {
|
|
408
|
+
monitoringService = new AWSMonitoringService(config)
|
|
409
|
+
}
|
|
410
|
+
return monitoringService
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export default AWSMonitoringService
|