local-serverless-stack 0.0.1
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/CHANGELOG.md +104 -0
- package/LICENSE +21 -0
- package/README.md +653 -0
- package/bin/cli.js +204 -0
- package/dist/server/dev/dynamo-proxy.d.ts +3 -0
- package/dist/server/dev/dynamo-proxy.d.ts.map +1 -0
- package/dist/server/dev/dynamo-proxy.js +35 -0
- package/dist/server/dev/dynamo-proxy.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +67 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/routes/resources.d.ts +3 -0
- package/dist/server/routes/resources.d.ts.map +1 -0
- package/dist/server/routes/resources.js +16 -0
- package/dist/server/routes/resources.js.map +1 -0
- package/dist/server/routes/services.d.ts +5 -0
- package/dist/server/routes/services.d.ts.map +1 -0
- package/dist/server/routes/services.js +217 -0
- package/dist/server/routes/services.js.map +1 -0
- package/dist/server/services/cache-manager.d.ts +21 -0
- package/dist/server/services/cache-manager.d.ts.map +1 -0
- package/dist/server/services/cache-manager.js +70 -0
- package/dist/server/services/cache-manager.js.map +1 -0
- package/dist/server/services/cloudformation-parser.d.ts +88 -0
- package/dist/server/services/cloudformation-parser.d.ts.map +1 -0
- package/dist/server/services/cloudformation-parser.js +112 -0
- package/dist/server/services/cloudformation-parser.js.map +1 -0
- package/dist/server/services/localstack-manager.d.ts +23 -0
- package/dist/server/services/localstack-manager.d.ts.map +1 -0
- package/dist/server/services/localstack-manager.js +142 -0
- package/dist/server/services/localstack-manager.js.map +1 -0
- package/dist/server/services/process-manager.d.ts +41 -0
- package/dist/server/services/process-manager.d.ts.map +1 -0
- package/dist/server/services/process-manager.js +119 -0
- package/dist/server/services/process-manager.js.map +1 -0
- package/dist/server/services/resource-provisioner.d.ts +37 -0
- package/dist/server/services/resource-provisioner.d.ts.map +1 -0
- package/dist/server/services/resource-provisioner.js +539 -0
- package/dist/server/services/resource-provisioner.js.map +1 -0
- package/dist/ui/assets/index-7WT0rkfU.css +1 -0
- package/dist/ui/assets/index-M0lXeUj-.js +19 -0
- package/dist/ui/index.html +16 -0
- package/package.json +89 -0
- package/packages/serverless-plugin/README.md +95 -0
- package/packages/serverless-plugin/dist/index.d.ts +2 -0
- package/packages/serverless-plugin/dist/index.d.ts.map +1 -0
- package/packages/serverless-plugin/dist/index.js +88 -0
- package/packages/serverless-plugin/dist/index.js.map +1 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Resource } from './cloudformation-parser.js';
|
|
2
|
+
export declare class ResourceProvisioner {
|
|
3
|
+
private dynamoClient;
|
|
4
|
+
private sqsClient;
|
|
5
|
+
private snsClient;
|
|
6
|
+
private lambdaClient;
|
|
7
|
+
private provisionedResources;
|
|
8
|
+
private serviceMetadata;
|
|
9
|
+
constructor();
|
|
10
|
+
provisionResources(serviceName: string, resources: Resource[], metadata?: {
|
|
11
|
+
invokePort?: number;
|
|
12
|
+
invokeUrl?: string;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
private createDynamoDBTable;
|
|
15
|
+
private createSQSQueue;
|
|
16
|
+
private createSNSTopic;
|
|
17
|
+
private generateProxyLambdaCode;
|
|
18
|
+
private createEventSourceMapping;
|
|
19
|
+
private ensureLambdaProxyExists;
|
|
20
|
+
private resolveLambdaFunctionName;
|
|
21
|
+
private convertCamelCaseToKebab;
|
|
22
|
+
private resolveEventSourceArn;
|
|
23
|
+
listAllResources(): Promise<{
|
|
24
|
+
tables: string[];
|
|
25
|
+
queues: string[];
|
|
26
|
+
topics: string[];
|
|
27
|
+
}>;
|
|
28
|
+
private listDynamoDBTables;
|
|
29
|
+
private listSQSQueues;
|
|
30
|
+
private listSNSTopics;
|
|
31
|
+
cleanupResources(serviceName: string): Promise<void>;
|
|
32
|
+
private deleteDynamoDBTable;
|
|
33
|
+
private deleteSQSQueue;
|
|
34
|
+
private deleteSNSTopic;
|
|
35
|
+
private deleteEventSourceMapping;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=resource-provisioner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-provisioner.d.ts","sourceRoot":"","sources":["../../../src/server/services/resource-provisioner.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,QAAQ,EAKT,MAAM,4BAA4B,CAAC;AAGpC,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,oBAAoB,CAAkC;IAC9D,OAAO,CAAC,eAAe,CAAgE;;IAUjF,kBAAkB,CACtB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,QAAQ,EAAE,EACrB,QAAQ,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GACrD,OAAO,CAAC,IAAI,CAAC;YAwDF,mBAAmB;YAyDnB,cAAc;YAiCd,cAAc;IAiB5B,OAAO,CAAC,uBAAuB;YAoEjB,wBAAwB;YA0CxB,uBAAuB;IAiDrC,OAAO,CAAC,yBAAyB;IAoBjC,OAAO,CAAC,uBAAuB;YAOjB,qBAAqB;IAqE7B,gBAAgB,IAAI,OAAO,CAAC;QAChC,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;YAUY,kBAAkB;YASlB,aAAa;YASb,aAAa;IAYrB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAyC5C,mBAAmB;YAYnB,cAAc;YAiBd,cAAc;YAkBd,wBAAwB;CA6BvC"}
|
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
import { DynamoDBClient, CreateTableCommand, ListTablesCommand, DeleteTableCommand } from '@aws-sdk/client-dynamodb';
|
|
2
|
+
import { SQSClient, CreateQueueCommand, ListQueuesCommand, GetQueueAttributesCommand, DeleteQueueCommand, GetQueueUrlCommand } from '@aws-sdk/client-sqs';
|
|
3
|
+
import { SNSClient, CreateTopicCommand, ListTopicsCommand, DeleteTopicCommand } from '@aws-sdk/client-sns';
|
|
4
|
+
import { LambdaClient, CreateFunctionCommand, GetFunctionCommand, DeleteFunctionCommand } from '@aws-sdk/client-lambda';
|
|
5
|
+
import { CreateEventSourceMappingCommand, ListEventSourceMappingsCommand, DeleteEventSourceMappingCommand } from '@aws-sdk/client-lambda';
|
|
6
|
+
import { LocalStackManager } from './localstack-manager.js';
|
|
7
|
+
import AdmZip from 'adm-zip';
|
|
8
|
+
export class ResourceProvisioner {
|
|
9
|
+
dynamoClient;
|
|
10
|
+
sqsClient;
|
|
11
|
+
snsClient;
|
|
12
|
+
lambdaClient;
|
|
13
|
+
provisionedResources = new Map();
|
|
14
|
+
serviceMetadata = new Map();
|
|
15
|
+
constructor() {
|
|
16
|
+
const config = LocalStackManager.getInstance().getConfig();
|
|
17
|
+
this.dynamoClient = new DynamoDBClient(config);
|
|
18
|
+
this.sqsClient = new SQSClient(config);
|
|
19
|
+
this.snsClient = new SNSClient(config);
|
|
20
|
+
this.lambdaClient = new LambdaClient(config);
|
|
21
|
+
}
|
|
22
|
+
async provisionResources(serviceName, resources, metadata) {
|
|
23
|
+
const provisioned = new Set();
|
|
24
|
+
// Store service metadata for Lambda proxy creation
|
|
25
|
+
if (metadata?.invokePort) {
|
|
26
|
+
this.serviceMetadata.set(serviceName, {
|
|
27
|
+
invokePort: metadata.invokePort,
|
|
28
|
+
invokeUrl: metadata.invokeUrl || `http://host.docker.internal:${metadata.invokePort}`,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// First pass: Create infrastructure resources (DynamoDB, SQS, SNS)
|
|
32
|
+
// Lambda functions are handled by Serverless Offline, not LocalStack
|
|
33
|
+
for (const resource of resources) {
|
|
34
|
+
try {
|
|
35
|
+
switch (resource.type) {
|
|
36
|
+
case 'dynamodb':
|
|
37
|
+
await this.createDynamoDBTable(resource);
|
|
38
|
+
provisioned.add(`dynamodb:${resource.name}`);
|
|
39
|
+
break;
|
|
40
|
+
case 'sqs':
|
|
41
|
+
await this.createSQSQueue(resource);
|
|
42
|
+
provisioned.add(`sqs:${resource.name}`);
|
|
43
|
+
break;
|
|
44
|
+
case 'sns':
|
|
45
|
+
await this.createSNSTopic(resource);
|
|
46
|
+
provisioned.add(`sns:${resource.name}`);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (!error.message?.includes('already exists')) {
|
|
52
|
+
const resourceName = 'name' in resource ? resource.name : resource.functionName;
|
|
53
|
+
console.error(`Failed to provision ${resource.type}:${resourceName}:`, error.message);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Second pass: Create event source mappings (which will create Lambda proxies if needed)
|
|
58
|
+
const eventSources = resources.filter(r => r.type === 'event-source');
|
|
59
|
+
for (const eventSource of eventSources) {
|
|
60
|
+
try {
|
|
61
|
+
await this.createEventSourceMapping(serviceName, eventSource);
|
|
62
|
+
provisioned.add(`event-source:${eventSource.functionName}`);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
66
|
+
const errorName = error instanceof Error && 'name' in error ? error.name : '';
|
|
67
|
+
if (!errorMessage.includes('already exists') && errorName !== 'ResourceConflictException') {
|
|
68
|
+
console.error(`Failed to provision event-source for ${eventSource.functionName}:`, errorMessage);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
this.provisionedResources.set(serviceName, provisioned);
|
|
73
|
+
console.log(`✅ Provisioned ${provisioned.size} resources for ${serviceName}`);
|
|
74
|
+
}
|
|
75
|
+
async createDynamoDBTable(resource) {
|
|
76
|
+
try {
|
|
77
|
+
// Ensure attribute definitions include all attributes referenced by KeySchema and GSIs/LSIs
|
|
78
|
+
const attributeDefinitions = [...(resource.attributeDefinitions || [])];
|
|
79
|
+
const defined = new Set(attributeDefinitions.map(a => a.AttributeName));
|
|
80
|
+
const required = new Set();
|
|
81
|
+
resource.keySchema.forEach(k => required.add(k.AttributeName));
|
|
82
|
+
// Look for GSI/LSI keys inside the resource
|
|
83
|
+
const gsiList = resource.globalSecondaryIndexes || [];
|
|
84
|
+
const lsiList = resource.localSecondaryIndexes || [];
|
|
85
|
+
const collectIndexKeys = (indexes) => {
|
|
86
|
+
if (!indexes)
|
|
87
|
+
return;
|
|
88
|
+
indexes.forEach(idx => {
|
|
89
|
+
(idx.KeySchema || []).forEach(k => required.add(k.AttributeName));
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
collectIndexKeys(gsiList);
|
|
93
|
+
collectIndexKeys(lsiList);
|
|
94
|
+
for (const attr of required) {
|
|
95
|
+
if (!defined.has(attr)) {
|
|
96
|
+
attributeDefinitions.push({ AttributeName: attr, AttributeType: 'S' });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
await this.dynamoClient.send(new CreateTableCommand({
|
|
100
|
+
TableName: resource.name,
|
|
101
|
+
KeySchema: resource.keySchema,
|
|
102
|
+
AttributeDefinitions: attributeDefinitions,
|
|
103
|
+
BillingMode: resource.billingMode,
|
|
104
|
+
GlobalSecondaryIndexes: (resource.globalSecondaryIndexes || []).length
|
|
105
|
+
? resource.globalSecondaryIndexes
|
|
106
|
+
: undefined,
|
|
107
|
+
LocalSecondaryIndexes: (resource.localSecondaryIndexes || []).length
|
|
108
|
+
? resource.localSecondaryIndexes
|
|
109
|
+
: undefined,
|
|
110
|
+
StreamSpecification: resource.streamEnabled
|
|
111
|
+
? {
|
|
112
|
+
StreamEnabled: true,
|
|
113
|
+
StreamViewType: 'NEW_AND_OLD_IMAGES',
|
|
114
|
+
}
|
|
115
|
+
: undefined,
|
|
116
|
+
}));
|
|
117
|
+
console.log(` ✓ Created DynamoDB table: ${resource.name}`);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
const errorName = error instanceof Error && 'name' in error ? error.name : '';
|
|
121
|
+
if (errorName === 'ResourceInUseException') {
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async createSQSQueue(resource) {
|
|
129
|
+
try {
|
|
130
|
+
const attributes = {};
|
|
131
|
+
if (resource.visibilityTimeout) {
|
|
132
|
+
attributes.VisibilityTimeout = resource.visibilityTimeout.toString();
|
|
133
|
+
}
|
|
134
|
+
if (resource.messageRetentionPeriod) {
|
|
135
|
+
attributes.MessageRetentionPeriod = resource.messageRetentionPeriod.toString();
|
|
136
|
+
}
|
|
137
|
+
if (resource.fifoQueue) {
|
|
138
|
+
attributes.FifoQueue = 'true';
|
|
139
|
+
if (resource.contentBasedDeduplication) {
|
|
140
|
+
attributes.ContentBasedDeduplication = 'true';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
await this.sqsClient.send(new CreateQueueCommand({
|
|
144
|
+
QueueName: resource.name,
|
|
145
|
+
Attributes: Object.keys(attributes).length > 0 ? attributes : undefined,
|
|
146
|
+
}));
|
|
147
|
+
console.log(` ✓ Created SQS queue: ${resource.name}`);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (error.name === 'QueueAlreadyExists') {
|
|
151
|
+
console.log(` ⚠ SQS queue already exists: ${resource.name}`);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async createSNSTopic(resource) {
|
|
159
|
+
try {
|
|
160
|
+
await this.snsClient.send(new CreateTopicCommand({
|
|
161
|
+
Name: resource.name,
|
|
162
|
+
}));
|
|
163
|
+
console.log(` ✓ Created SNS topic: ${resource.name}`);
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
if (error.message?.includes('already exists')) {
|
|
167
|
+
console.log(` ⚠ SNS topic already exists: ${resource.name}`);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
generateProxyLambdaCode(functionName, invokeUrl) {
|
|
175
|
+
return `
|
|
176
|
+
export const handler = async (event, context) => {
|
|
177
|
+
const http = await import('http');
|
|
178
|
+
const url = await import('url');
|
|
179
|
+
|
|
180
|
+
const invokeUrl = process.env.INVOKE_URL || '${invokeUrl}';
|
|
181
|
+
const parsedUrl = url.parse(invokeUrl);
|
|
182
|
+
|
|
183
|
+
// Transform event based on source
|
|
184
|
+
// LocalStack sends events through different sources (SQS, DynamoDB, SNS, etc.)
|
|
185
|
+
// We need to pass them correctly to the Serverless Offline handler
|
|
186
|
+
let transformedEvent = event;
|
|
187
|
+
|
|
188
|
+
// If the event already has Records (SQS/DynamoDB/SNS event), use it as is
|
|
189
|
+
// Otherwise wrap it in a Records array
|
|
190
|
+
if (!event.Records) {
|
|
191
|
+
transformedEvent = {
|
|
192
|
+
Records: [event]
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const payload = JSON.stringify(transformedEvent);
|
|
197
|
+
|
|
198
|
+
return new Promise((resolve, reject) => {
|
|
199
|
+
const options = {
|
|
200
|
+
hostname: parsedUrl.hostname,
|
|
201
|
+
port: parsedUrl.port,
|
|
202
|
+
path: '/2015-03-31/functions/${functionName}/invocations',
|
|
203
|
+
method: 'POST',
|
|
204
|
+
headers: {
|
|
205
|
+
'Content-Type': 'application/json',
|
|
206
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
207
|
+
'X-Amz-Invocation-Type': 'RequestResponse'
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const req = http.request(options, (res) => {
|
|
212
|
+
let data = '';
|
|
213
|
+
res.on('data', chunk => data += chunk);
|
|
214
|
+
res.on('end', () => {
|
|
215
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
216
|
+
try {
|
|
217
|
+
// Try to parse as JSON
|
|
218
|
+
const response = JSON.parse(data);
|
|
219
|
+
resolve(response);
|
|
220
|
+
} catch (e) {
|
|
221
|
+
// If not JSON, return as string
|
|
222
|
+
resolve(data);
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
reject(new Error(\`Lambda invoke failed: \${res.statusCode} \${data}\`));
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
req.on('error', (err) => {
|
|
231
|
+
console.error('Request error:', err);
|
|
232
|
+
reject(err);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
req.write(payload);
|
|
236
|
+
req.end();
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
`;
|
|
240
|
+
}
|
|
241
|
+
async createEventSourceMapping(serviceName, mapping) {
|
|
242
|
+
try {
|
|
243
|
+
// Convert CloudFormation function name to Serverless naming convention
|
|
244
|
+
// CloudFormation: ConsumerAppsQueueLambdaFunction -> Serverless: payment-reminder-api-consumerAppsQueue
|
|
245
|
+
const actualFunctionName = this.resolveLambdaFunctionName(serviceName, mapping.functionName);
|
|
246
|
+
// Resolve queue ARN from queue name
|
|
247
|
+
const eventSourceArn = await this.resolveEventSourceArn(mapping.eventSourceArn);
|
|
248
|
+
// Create Lambda proxy function if it doesn't exist
|
|
249
|
+
await this.ensureLambdaProxyExists(serviceName, actualFunctionName);
|
|
250
|
+
// Check if mapping already exists
|
|
251
|
+
const existingMappings = await this.lambdaClient.send(new ListEventSourceMappingsCommand({
|
|
252
|
+
FunctionName: actualFunctionName,
|
|
253
|
+
}));
|
|
254
|
+
if (existingMappings.EventSourceMappings?.some(m => m.EventSourceArn?.includes(mapping.eventSourceArn))) {
|
|
255
|
+
console.log(` ⚠ Event source mapping already exists: ${actualFunctionName} <- ${mapping.eventSourceArn}`);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
await this.lambdaClient.send(new CreateEventSourceMappingCommand({
|
|
259
|
+
FunctionName: actualFunctionName,
|
|
260
|
+
EventSourceArn: eventSourceArn,
|
|
261
|
+
BatchSize: mapping.batchSize || 10,
|
|
262
|
+
Enabled: mapping.enabled,
|
|
263
|
+
}));
|
|
264
|
+
console.log(` ✓ Created event source mapping: ${actualFunctionName} <- ${mapping.eventSourceArn}`);
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
if (error.name === 'ResourceConflictException') {
|
|
268
|
+
console.log(` ⚠ Event source mapping already exists: ${mapping.functionName}`);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async ensureLambdaProxyExists(serviceName, functionName) {
|
|
276
|
+
try {
|
|
277
|
+
// Check if function already exists
|
|
278
|
+
await this.lambdaClient.send(new GetFunctionCommand({
|
|
279
|
+
FunctionName: functionName,
|
|
280
|
+
}));
|
|
281
|
+
// Function exists, no need to create
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
if (error.name !== 'ResourceNotFoundException') {
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Function doesn't exist, create a Lambda proxy
|
|
290
|
+
const metadata = this.serviceMetadata.get(serviceName);
|
|
291
|
+
if (!metadata) {
|
|
292
|
+
throw new Error(`Cannot create Lambda proxy for ${functionName}: no invoke port configured for ${serviceName}`);
|
|
293
|
+
}
|
|
294
|
+
const proxyCode = this.generateProxyLambdaCode(functionName, metadata.invokeUrl);
|
|
295
|
+
const zip = new AdmZip();
|
|
296
|
+
zip.addFile('index.mjs', Buffer.from(proxyCode, 'utf-8'));
|
|
297
|
+
const zipBuffer = zip.toBuffer();
|
|
298
|
+
await this.lambdaClient.send(new CreateFunctionCommand({
|
|
299
|
+
FunctionName: functionName,
|
|
300
|
+
Runtime: 'nodejs20.x',
|
|
301
|
+
Role: 'arn:aws:iam::000000000000:role/lambda-role',
|
|
302
|
+
Handler: 'index.handler',
|
|
303
|
+
Code: {
|
|
304
|
+
ZipFile: zipBuffer,
|
|
305
|
+
},
|
|
306
|
+
Environment: {
|
|
307
|
+
Variables: {
|
|
308
|
+
INVOKE_URL: metadata.invokeUrl,
|
|
309
|
+
FUNCTION_NAME: functionName,
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
MemorySize: 256,
|
|
313
|
+
Timeout: 60,
|
|
314
|
+
}));
|
|
315
|
+
console.log(` ✓ Created Lambda proxy: ${functionName} -> ${metadata.invokeUrl}`);
|
|
316
|
+
}
|
|
317
|
+
resolveLambdaFunctionName(serviceName, cfnName) {
|
|
318
|
+
// CloudFormation names like "ConsumerAppsQueueLambdaFunction" are converted to Serverless naming
|
|
319
|
+
// by removing the "LambdaFunction" suffix, converting to camelCase, then to the serverless pattern
|
|
320
|
+
// Example: ConsumerAppsQueueLambdaFunction -> consumerAppsQueue -> payment-reminder-api-consumerAppsQueue
|
|
321
|
+
// Remove "LambdaFunction" suffix if present
|
|
322
|
+
let funcName = cfnName;
|
|
323
|
+
if (funcName.endsWith('LambdaFunction')) {
|
|
324
|
+
funcName = funcName.slice(0, -14); // Remove "LambdaFunction"
|
|
325
|
+
}
|
|
326
|
+
// Convert to camelCase (first letter lowercase)
|
|
327
|
+
funcName = funcName.charAt(0).toLowerCase() + funcName.slice(1);
|
|
328
|
+
// Serverless names follow pattern: {service}-{stage}-{functionName}
|
|
329
|
+
// We need to extract service and stage from the created Lambda names
|
|
330
|
+
// For now, assume standard naming: payment-reminder-api-{functionName}
|
|
331
|
+
return `${serviceName}-api-${funcName}`;
|
|
332
|
+
}
|
|
333
|
+
convertCamelCaseToKebab(camelCase) {
|
|
334
|
+
return camelCase
|
|
335
|
+
.replace(/([A-Z])/g, '-$1')
|
|
336
|
+
.toLowerCase()
|
|
337
|
+
.replace(/^-/, '');
|
|
338
|
+
}
|
|
339
|
+
async resolveEventSourceArn(arnRef) {
|
|
340
|
+
// If it's already a full ARN, return it
|
|
341
|
+
if (arnRef.startsWith('arn:aws:')) {
|
|
342
|
+
return arnRef;
|
|
343
|
+
}
|
|
344
|
+
// Extract resource name from Fn::GetAtt reference (e.g., "paymentReminderApps::Arn")
|
|
345
|
+
let resourceName = arnRef.split('::')[0];
|
|
346
|
+
// Convert camelCase resource names to kebab-case (serverless convention)
|
|
347
|
+
// e.g., "paymentReminderApps" -> "payment-reminder-apps"
|
|
348
|
+
resourceName = this.convertCamelCaseToKebab(resourceName);
|
|
349
|
+
// Try to resolve as SQS queue
|
|
350
|
+
try {
|
|
351
|
+
const queues = await this.sqsClient.send(new ListQueuesCommand({}));
|
|
352
|
+
const queueUrl = queues.QueueUrls?.find(url => url.includes(resourceName));
|
|
353
|
+
if (queueUrl) {
|
|
354
|
+
const attributes = await this.sqsClient.send(new GetQueueAttributesCommand({
|
|
355
|
+
QueueUrl: queueUrl,
|
|
356
|
+
AttributeNames: ['QueueArn'],
|
|
357
|
+
}));
|
|
358
|
+
const queueArn = attributes.Attributes?.QueueArn;
|
|
359
|
+
if (queueArn) {
|
|
360
|
+
return queueArn;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
// Continue to try other resource types
|
|
366
|
+
}
|
|
367
|
+
// Try to resolve as DynamoDB stream
|
|
368
|
+
try {
|
|
369
|
+
const tables = await this.dynamoClient.send(new ListTablesCommand({}));
|
|
370
|
+
const tableMatch = tables.TableNames?.find(name => this.convertCamelCaseToKebab(name) === resourceName);
|
|
371
|
+
if (tableMatch) {
|
|
372
|
+
// Return DynamoDB Streams ARN (format: arn:aws:dynamodb:region:account:table/name/stream/type)
|
|
373
|
+
// For now, we'll construct a generic stream ARN
|
|
374
|
+
return `arn:aws:dynamodb:us-east-1:000000000000:table/${tableMatch}/stream/NEW_AND_OLD_IMAGES`;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
// Continue to try other resource types
|
|
379
|
+
}
|
|
380
|
+
// Try to resolve as SNS topic
|
|
381
|
+
try {
|
|
382
|
+
const topics = await this.snsClient.send(new ListTopicsCommand({}));
|
|
383
|
+
const topicMatch = topics.Topics?.find(topic => {
|
|
384
|
+
const topicName = topic.TopicArn?.split(':').pop() || '';
|
|
385
|
+
return this.convertCamelCaseToKebab(topicName) === resourceName;
|
|
386
|
+
});
|
|
387
|
+
if (topicMatch?.TopicArn) {
|
|
388
|
+
return topicMatch.TopicArn;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
// Continue
|
|
393
|
+
}
|
|
394
|
+
throw new Error(`Event source not found: ${arnRef} (resolved as: ${resourceName})`);
|
|
395
|
+
}
|
|
396
|
+
async listAllResources() {
|
|
397
|
+
const [tables, queues, topics] = await Promise.all([
|
|
398
|
+
this.listDynamoDBTables(),
|
|
399
|
+
this.listSQSQueues(),
|
|
400
|
+
this.listSNSTopics(),
|
|
401
|
+
]);
|
|
402
|
+
return { tables, queues, topics };
|
|
403
|
+
}
|
|
404
|
+
async listDynamoDBTables() {
|
|
405
|
+
try {
|
|
406
|
+
const response = await this.dynamoClient.send(new ListTablesCommand({}));
|
|
407
|
+
return response.TableNames || [];
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
return [];
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
async listSQSQueues() {
|
|
414
|
+
try {
|
|
415
|
+
const response = await this.sqsClient.send(new ListQueuesCommand({}));
|
|
416
|
+
return response.QueueUrls?.map(url => url.split('/').pop()) || [];
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
return [];
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
async listSNSTopics() {
|
|
423
|
+
try {
|
|
424
|
+
const response = await this.snsClient.send(new ListTopicsCommand({}));
|
|
425
|
+
return response.Topics?.map(t => {
|
|
426
|
+
const arn = t.TopicArn?.split(':').pop();
|
|
427
|
+
return arn || '';
|
|
428
|
+
}).filter(arn => arn !== '') || [];
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
return [];
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
async cleanupResources(serviceName) {
|
|
435
|
+
const resources = this.provisionedResources.get(serviceName);
|
|
436
|
+
if (!resources) {
|
|
437
|
+
console.log(`No resources found for service ${serviceName}`);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
let cleaned = 0;
|
|
441
|
+
for (const resource of resources) {
|
|
442
|
+
try {
|
|
443
|
+
const [type, name] = resource.split(':');
|
|
444
|
+
switch (type) {
|
|
445
|
+
case 'dynamodb':
|
|
446
|
+
await this.deleteDynamoDBTable(name);
|
|
447
|
+
cleaned++;
|
|
448
|
+
break;
|
|
449
|
+
case 'sqs':
|
|
450
|
+
await this.deleteSQSQueue(name);
|
|
451
|
+
cleaned++;
|
|
452
|
+
break;
|
|
453
|
+
case 'sns':
|
|
454
|
+
await this.deleteSNSTopic(name);
|
|
455
|
+
cleaned++;
|
|
456
|
+
break;
|
|
457
|
+
case 'event-source':
|
|
458
|
+
await this.deleteEventSourceMapping(serviceName, name);
|
|
459
|
+
cleaned++;
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
465
|
+
console.error(`Failed to cleanup ${resource}:`, errorMessage);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
this.provisionedResources.delete(serviceName);
|
|
469
|
+
this.serviceMetadata.delete(serviceName);
|
|
470
|
+
console.log(`✅ Cleaned up ${cleaned} resources for ${serviceName}`);
|
|
471
|
+
}
|
|
472
|
+
async deleteDynamoDBTable(tableName) {
|
|
473
|
+
try {
|
|
474
|
+
await this.dynamoClient.send(new DeleteTableCommand({ TableName: tableName }));
|
|
475
|
+
console.log(`Deleted DynamoDB table: ${tableName}`);
|
|
476
|
+
}
|
|
477
|
+
catch (error) {
|
|
478
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
479
|
+
if (!errorMessage.includes('ResourceNotFoundException')) {
|
|
480
|
+
throw error;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async deleteSQSQueue(queueName) {
|
|
485
|
+
try {
|
|
486
|
+
const urlResponse = await this.sqsClient.send(new GetQueueUrlCommand({ QueueName: queueName }));
|
|
487
|
+
if (urlResponse.QueueUrl) {
|
|
488
|
+
await this.sqsClient.send(new DeleteQueueCommand({ QueueUrl: urlResponse.QueueUrl }));
|
|
489
|
+
console.log(`Deleted SQS queue: ${queueName}`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
catch (error) {
|
|
493
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
494
|
+
if (!errorMessage.includes('NonExistentQueue')) {
|
|
495
|
+
throw error;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
async deleteSNSTopic(topicName) {
|
|
500
|
+
try {
|
|
501
|
+
// Find the topic ARN from list
|
|
502
|
+
const response = await this.snsClient.send(new ListTopicsCommand({}));
|
|
503
|
+
const topic = response.Topics?.find(t => t.TopicArn?.endsWith(`:${topicName}`));
|
|
504
|
+
if (topic?.TopicArn) {
|
|
505
|
+
await this.snsClient.send(new DeleteTopicCommand({ TopicArn: topic.TopicArn }));
|
|
506
|
+
console.log(`Deleted SNS topic: ${topicName}`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
catch (error) {
|
|
510
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
511
|
+
if (!errorMessage.includes('NotFound')) {
|
|
512
|
+
throw error;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
async deleteEventSourceMapping(serviceName, functionName) {
|
|
517
|
+
try {
|
|
518
|
+
// List all event source mappings
|
|
519
|
+
const response = await this.lambdaClient.send(new ListEventSourceMappingsCommand({ FunctionName: `${serviceName}-${functionName}-proxy` }));
|
|
520
|
+
// Delete each mapping
|
|
521
|
+
for (const mapping of response.EventSourceMappings || []) {
|
|
522
|
+
if (mapping.UUID) {
|
|
523
|
+
await this.lambdaClient.send(new DeleteEventSourceMappingCommand({ UUID: mapping.UUID }));
|
|
524
|
+
console.log(`Deleted event source mapping: ${mapping.UUID}`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
// Delete the Lambda proxy function
|
|
528
|
+
await this.lambdaClient.send(new DeleteFunctionCommand({ FunctionName: `${serviceName}-${functionName}-proxy` }));
|
|
529
|
+
console.log(`Deleted Lambda proxy: ${serviceName}-${functionName}-proxy`);
|
|
530
|
+
}
|
|
531
|
+
catch (error) {
|
|
532
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
533
|
+
if (!errorMessage.includes('ResourceNotFoundException')) {
|
|
534
|
+
throw error;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
//# sourceMappingURL=resource-provisioner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-provisioner.js","sourceRoot":"","sources":["../../../src/server/services/resource-provisioner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.modal[data-v-af35b7d4]{position:fixed;top:0;right:0;bottom:0;left:0;background:#0000008c;display:flex;align-items:center;justify-content:center;z-index:50}.modal-content[data-v-af35b7d4]{background:var(--card);border:1px solid var(--border);border-radius:.5rem;width:720px;max-height:80vh;display:flex;flex-direction:column;box-shadow:0 10px 30px #0000004d}.modal-header[data-v-af35b7d4]{display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid var(--border)}.logs[data-v-af35b7d4]{flex:1;padding:1rem;margin:0;background:#0b0c10;color:#d1d5db;font-family:Menlo,Consolas,Monaco,monospace;font-size:.85rem;overflow:auto;min-height:300px;white-space:pre-wrap}.actions[data-v-af35b7d4]{display:flex;gap:.5rem;flex-wrap:wrap}*{margin:0;padding:0;box-sizing:border-box}:root{--primary: #3b82f6;--primary-hover: #2563eb;--success: #10b981;--warning: #f59e0b;--danger: #ef4444;--bg: #0f172a;--bg-card: #1e293b;--bg-hover: #334155;--text: #f1f5f9;--text-muted: #94a3b8;--border: #334155}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif;background:var(--bg);color:var(--text);line-height:1.6}#app{min-height:100vh}.header{background:var(--bg-card);border-bottom:1px solid var(--border);padding:1rem 2rem}.header h1{font-size:1.5rem;font-weight:600}.header-subtitle{color:var(--text-muted);font-size:.875rem}.container{max-width:1400px;margin:0 auto;padding:2rem}.card{background:var(--bg-card);border:1px solid var(--border);border-radius:.5rem;padding:1.5rem;margin-bottom:1.5rem}.card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;padding-bottom:1rem;border-bottom:1px solid var(--border)}.card-title{font-size:1.25rem;font-weight:600}.btn{padding:.5rem 1rem;border:none;border-radius:.375rem;font-size:.875rem;font-weight:500;cursor:pointer;transition:all .2s}.btn-primary{background:var(--primary);color:#fff}.btn-primary:hover{background:var(--primary-hover)}.btn-success{background:var(--success);color:#fff}.btn-danger{background:var(--danger);color:#fff}.btn-sm{padding:.375rem .75rem;font-size:.8125rem}.table{width:100%;border-collapse:collapse}.table th,.table td{padding:.75rem;text-align:left;border-bottom:1px solid var(--border)}.table th{font-weight:600;color:var(--text-muted);font-size:.875rem;text-transform:uppercase;letter-spacing:.05em}.table tr:hover{background:var(--bg-hover)}.badge{display:inline-block;padding:.25rem .625rem;border-radius:9999px;font-size:.75rem;font-weight:500}.badge-success{background:#10b98133;color:var(--success)}.badge-warning{background:#f59e0b33;color:var(--warning)}.badge-danger{background:#ef444433;color:var(--danger)}.grid{display:grid;gap:1.5rem}.grid-cols-3{grid-template-columns:repeat(3,1fr)}.stat-card{background:var(--bg-card);border:1px solid var(--border);border-radius:.5rem;padding:1.5rem}.stat-label{color:var(--text-muted);font-size:.875rem;margin-bottom:.5rem}.stat-value{font-size:2rem;font-weight:600}.empty-state{text-align:center;padding:3rem 1rem;color:var(--text-muted)}.loading{text-align:center;padding:2rem;color:var(--text-muted)}.actions{display:flex;gap:.5rem}
|