omgkit 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/SKILL_STANDARDS.md +743 -0
  3. package/plugin/skills/databases/mongodb/SKILL.md +797 -28
  4. package/plugin/skills/databases/prisma/SKILL.md +776 -30
  5. package/plugin/skills/databases/redis/SKILL.md +885 -25
  6. package/plugin/skills/devops/aws/SKILL.md +686 -28
  7. package/plugin/skills/devops/github-actions/SKILL.md +684 -29
  8. package/plugin/skills/devops/kubernetes/SKILL.md +621 -24
  9. package/plugin/skills/frameworks/django/SKILL.md +920 -20
  10. package/plugin/skills/frameworks/express/SKILL.md +1361 -35
  11. package/plugin/skills/frameworks/fastapi/SKILL.md +1260 -33
  12. package/plugin/skills/frameworks/laravel/SKILL.md +1244 -31
  13. package/plugin/skills/frameworks/nestjs/SKILL.md +1005 -26
  14. package/plugin/skills/frameworks/rails/SKILL.md +594 -28
  15. package/plugin/skills/frameworks/spring/SKILL.md +528 -35
  16. package/plugin/skills/frameworks/vue/SKILL.md +1296 -27
  17. package/plugin/skills/frontend/accessibility/SKILL.md +1108 -34
  18. package/plugin/skills/frontend/frontend-design/SKILL.md +1304 -26
  19. package/plugin/skills/frontend/responsive/SKILL.md +847 -21
  20. package/plugin/skills/frontend/shadcn-ui/SKILL.md +976 -38
  21. package/plugin/skills/frontend/tailwindcss/SKILL.md +831 -35
  22. package/plugin/skills/frontend/threejs/SKILL.md +1298 -29
  23. package/plugin/skills/languages/javascript/SKILL.md +935 -31
  24. package/plugin/skills/methodology/brainstorming/SKILL.md +597 -23
  25. package/plugin/skills/methodology/defense-in-depth/SKILL.md +832 -34
  26. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +665 -31
  27. package/plugin/skills/methodology/executing-plans/SKILL.md +556 -24
  28. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +595 -25
  29. package/plugin/skills/methodology/problem-solving/SKILL.md +429 -61
  30. package/plugin/skills/methodology/receiving-code-review/SKILL.md +536 -24
  31. package/plugin/skills/methodology/requesting-code-review/SKILL.md +632 -21
  32. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +641 -30
  33. package/plugin/skills/methodology/sequential-thinking/SKILL.md +262 -3
  34. package/plugin/skills/methodology/systematic-debugging/SKILL.md +571 -32
  35. package/plugin/skills/methodology/test-driven-development/SKILL.md +779 -24
  36. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +691 -29
  37. package/plugin/skills/methodology/token-optimization/SKILL.md +598 -29
  38. package/plugin/skills/methodology/verification-before-completion/SKILL.md +543 -22
  39. package/plugin/skills/methodology/writing-plans/SKILL.md +590 -18
  40. package/plugin/skills/omega/omega-architecture/SKILL.md +838 -39
  41. package/plugin/skills/omega/omega-coding/SKILL.md +636 -39
  42. package/plugin/skills/omega/omega-sprint/SKILL.md +855 -48
  43. package/plugin/skills/omega/omega-testing/SKILL.md +940 -41
  44. package/plugin/skills/omega/omega-thinking/SKILL.md +703 -50
  45. package/plugin/skills/security/better-auth/SKILL.md +1065 -28
  46. package/plugin/skills/security/oauth/SKILL.md +968 -31
  47. package/plugin/skills/security/owasp/SKILL.md +894 -33
  48. package/plugin/skills/testing/playwright/SKILL.md +764 -38
  49. package/plugin/skills/testing/pytest/SKILL.md +873 -36
  50. package/plugin/skills/testing/vitest/SKILL.md +980 -35
@@ -1,51 +1,709 @@
1
1
  ---
2
2
  name: aws
3
- description: AWS services. Use for Lambda, S3, RDS, ECS, and other AWS services.
3
+ description: AWS cloud services with Lambda, S3, DynamoDB, ECS, and infrastructure as code patterns
4
+ category: devops
5
+ triggers:
6
+ - aws
7
+ - amazon web services
8
+ - lambda
9
+ - s3
10
+ - dynamodb
11
+ - ecs
12
+ - cloudformation
13
+ - cdk
4
14
  ---
5
15
 
6
- # AWS Skill
16
+ # AWS
7
17
 
8
- ## Common Services
18
+ Enterprise-grade **AWS cloud development** following industry best practices. This skill covers Lambda functions, S3 storage, DynamoDB, ECS containers, API Gateway, infrastructure as code with CDK, and production-ready patterns used by top engineering teams.
19
+
20
+ ## Purpose
21
+
22
+ Build scalable cloud applications on AWS:
23
+
24
+ - Deploy serverless functions with Lambda
25
+ - Store and retrieve data with S3 and DynamoDB
26
+ - Run containerized applications with ECS
27
+ - Build APIs with API Gateway
28
+ - Implement infrastructure as code with CDK
29
+ - Monitor applications with CloudWatch
30
+ - Secure resources with IAM
31
+
32
+ ## Features
33
+
34
+ ### 1. Lambda Functions
9
35
 
10
- ### Lambda
11
36
  ```typescript
12
- export const handler = async (event: APIGatewayEvent) => {
13
- const body = JSON.parse(event.body || '{}');
37
+ // src/handlers/user.handler.ts
38
+ import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
39
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
40
+ import { DynamoDBDocumentClient, GetCommand, PutCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';
41
+
42
+ const client = new DynamoDBClient({});
43
+ const docClient = DynamoDBDocumentClient.from(client);
44
+
45
+ const TABLE_NAME = process.env.USERS_TABLE!;
14
46
 
15
- return {
16
- statusCode: 200,
17
- body: JSON.stringify({ message: 'Success' }),
18
- };
47
+ interface User {
48
+ id: string;
49
+ email: string;
50
+ name: string;
51
+ createdAt: string;
52
+ }
53
+
54
+ // Response helpers
55
+ const response = (statusCode: number, body: unknown): APIGatewayProxyResult => ({
56
+ statusCode,
57
+ headers: {
58
+ 'Content-Type': 'application/json',
59
+ 'Access-Control-Allow-Origin': '*',
60
+ 'Access-Control-Allow-Credentials': true,
61
+ },
62
+ body: JSON.stringify(body),
63
+ });
64
+
65
+ const success = (data: unknown) => response(200, { data });
66
+ const created = (data: unknown) => response(201, { data });
67
+ const notFound = (message: string) => response(404, { error: { code: 'NOT_FOUND', message } });
68
+ const badRequest = (message: string) => response(400, { error: { code: 'BAD_REQUEST', message } });
69
+ const serverError = (error: Error) => response(500, { error: { code: 'SERVER_ERROR', message: error.message } });
70
+
71
+ // GET /users/{id}
72
+ export const getUser = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
73
+ try {
74
+ const userId = event.pathParameters?.id;
75
+ if (!userId) return badRequest('User ID is required');
76
+
77
+ const result = await docClient.send(new GetCommand({
78
+ TableName: TABLE_NAME,
79
+ Key: { id: userId },
80
+ }));
81
+
82
+ if (!result.Item) {
83
+ return notFound(`User ${userId} not found`);
84
+ }
85
+
86
+ return success(result.Item);
87
+ } catch (error) {
88
+ console.error('Error getting user:', error);
89
+ return serverError(error as Error);
90
+ }
91
+ };
92
+
93
+ // POST /users
94
+ export const createUser = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
95
+ try {
96
+ if (!event.body) return badRequest('Request body is required');
97
+
98
+ const body = JSON.parse(event.body);
99
+ const { email, name } = body;
100
+
101
+ if (!email || !name) {
102
+ return badRequest('Email and name are required');
103
+ }
104
+
105
+ const user: User = {
106
+ id: crypto.randomUUID(),
107
+ email: email.toLowerCase(),
108
+ name,
109
+ createdAt: new Date().toISOString(),
110
+ };
111
+
112
+ await docClient.send(new PutCommand({
113
+ TableName: TABLE_NAME,
114
+ Item: user,
115
+ ConditionExpression: 'attribute_not_exists(id)',
116
+ }));
117
+
118
+ return created(user);
119
+ } catch (error) {
120
+ console.error('Error creating user:', error);
121
+ return serverError(error as Error);
122
+ }
123
+ };
124
+
125
+ // GET /users
126
+ export const listUsers = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
127
+ try {
128
+ const limit = parseInt(event.queryStringParameters?.limit || '20');
129
+ const lastKey = event.queryStringParameters?.cursor;
130
+
131
+ const params: any = {
132
+ TableName: TABLE_NAME,
133
+ Limit: limit,
134
+ };
135
+
136
+ if (lastKey) {
137
+ params.ExclusiveStartKey = JSON.parse(Buffer.from(lastKey, 'base64').toString());
138
+ }
139
+
140
+ const result = await docClient.send(new QueryCommand(params));
141
+
142
+ const cursor = result.LastEvaluatedKey
143
+ ? Buffer.from(JSON.stringify(result.LastEvaluatedKey)).toString('base64')
144
+ : null;
145
+
146
+ return success({
147
+ items: result.Items,
148
+ cursor,
149
+ hasMore: !!result.LastEvaluatedKey,
150
+ });
151
+ } catch (error) {
152
+ console.error('Error listing users:', error);
153
+ return serverError(error as Error);
154
+ }
19
155
  };
20
156
  ```
21
157
 
22
- ### S3
158
+ ### 2. S3 Operations
159
+
23
160
  ```typescript
24
- import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
161
+ // src/services/s3.service.ts
162
+ import {
163
+ S3Client,
164
+ PutObjectCommand,
165
+ GetObjectCommand,
166
+ DeleteObjectCommand,
167
+ ListObjectsV2Command,
168
+ CopyObjectCommand,
169
+ } from '@aws-sdk/client-s3';
170
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
171
+ import { Readable } from 'stream';
25
172
 
26
- const s3 = new S3Client({ region: 'us-east-1' });
173
+ const s3Client = new S3Client({ region: process.env.AWS_REGION });
174
+ const BUCKET = process.env.S3_BUCKET!;
27
175
 
28
- await s3.send(new PutObjectCommand({
29
- Bucket: 'my-bucket',
30
- Key: 'file.txt',
31
- Body: 'content',
32
- }));
176
+ export class S3Service {
177
+ // Upload file
178
+ async upload(key: string, body: Buffer | Readable, contentType: string): Promise<string> {
179
+ await s3Client.send(new PutObjectCommand({
180
+ Bucket: BUCKET,
181
+ Key: key,
182
+ Body: body,
183
+ ContentType: contentType,
184
+ }));
185
+
186
+ return `s3://${BUCKET}/${key}`;
187
+ }
188
+
189
+ // Upload with metadata
190
+ async uploadWithMetadata(
191
+ key: string,
192
+ body: Buffer,
193
+ options: {
194
+ contentType: string;
195
+ metadata?: Record<string, string>;
196
+ tags?: Record<string, string>;
197
+ }
198
+ ): Promise<string> {
199
+ await s3Client.send(new PutObjectCommand({
200
+ Bucket: BUCKET,
201
+ Key: key,
202
+ Body: body,
203
+ ContentType: options.contentType,
204
+ Metadata: options.metadata,
205
+ Tagging: options.tags
206
+ ? Object.entries(options.tags).map(([k, v]) => `${k}=${v}`).join('&')
207
+ : undefined,
208
+ }));
209
+
210
+ return `https://${BUCKET}.s3.amazonaws.com/${key}`;
211
+ }
212
+
213
+ // Get file
214
+ async get(key: string): Promise<Buffer> {
215
+ const response = await s3Client.send(new GetObjectCommand({
216
+ Bucket: BUCKET,
217
+ Key: key,
218
+ }));
219
+
220
+ const chunks: Buffer[] = [];
221
+ for await (const chunk of response.Body as Readable) {
222
+ chunks.push(chunk);
223
+ }
224
+ return Buffer.concat(chunks);
225
+ }
226
+
227
+ // Generate presigned upload URL
228
+ async getUploadUrl(key: string, contentType: string, expiresIn = 3600): Promise<string> {
229
+ const command = new PutObjectCommand({
230
+ Bucket: BUCKET,
231
+ Key: key,
232
+ ContentType: contentType,
233
+ });
234
+
235
+ return getSignedUrl(s3Client, command, { expiresIn });
236
+ }
237
+
238
+ // Generate presigned download URL
239
+ async getDownloadUrl(key: string, expiresIn = 3600): Promise<string> {
240
+ const command = new GetObjectCommand({
241
+ Bucket: BUCKET,
242
+ Key: key,
243
+ });
244
+
245
+ return getSignedUrl(s3Client, command, { expiresIn });
246
+ }
247
+
248
+ // List objects with prefix
249
+ async list(prefix: string, maxKeys = 1000): Promise<string[]> {
250
+ const response = await s3Client.send(new ListObjectsV2Command({
251
+ Bucket: BUCKET,
252
+ Prefix: prefix,
253
+ MaxKeys: maxKeys,
254
+ }));
255
+
256
+ return response.Contents?.map(obj => obj.Key!) || [];
257
+ }
258
+
259
+ // Delete file
260
+ async delete(key: string): Promise<void> {
261
+ await s3Client.send(new DeleteObjectCommand({
262
+ Bucket: BUCKET,
263
+ Key: key,
264
+ }));
265
+ }
266
+
267
+ // Copy file
268
+ async copy(sourceKey: string, destKey: string): Promise<void> {
269
+ await s3Client.send(new CopyObjectCommand({
270
+ Bucket: BUCKET,
271
+ CopySource: `${BUCKET}/${sourceKey}`,
272
+ Key: destKey,
273
+ }));
274
+ }
275
+ }
33
276
  ```
34
277
 
35
- ### DynamoDB
278
+ ### 3. DynamoDB Patterns
279
+
36
280
  ```typescript
37
- import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';
281
+ // src/services/dynamodb.service.ts
282
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
283
+ import {
284
+ DynamoDBDocumentClient,
285
+ GetCommand,
286
+ PutCommand,
287
+ UpdateCommand,
288
+ DeleteCommand,
289
+ QueryCommand,
290
+ BatchWriteCommand,
291
+ TransactWriteCommand,
292
+ } from '@aws-sdk/lib-dynamodb';
38
293
 
39
294
  const client = new DynamoDBClient({});
295
+ const docClient = DynamoDBDocumentClient.from(client, {
296
+ marshallOptions: { removeUndefinedValues: true },
297
+ });
298
+
299
+ export class DynamoDBService<T extends { pk: string; sk: string }> {
300
+ constructor(private tableName: string) {}
301
+
302
+ async get(pk: string, sk: string): Promise<T | null> {
303
+ const result = await docClient.send(new GetCommand({
304
+ TableName: this.tableName,
305
+ Key: { pk, sk },
306
+ }));
307
+ return (result.Item as T) || null;
308
+ }
309
+
310
+ async put(item: T): Promise<T> {
311
+ await docClient.send(new PutCommand({
312
+ TableName: this.tableName,
313
+ Item: item,
314
+ }));
315
+ return item;
316
+ }
317
+
318
+ async update(pk: string, sk: string, updates: Partial<T>): Promise<T> {
319
+ const updateExpressions: string[] = [];
320
+ const expressionNames: Record<string, string> = {};
321
+ const expressionValues: Record<string, unknown> = {};
322
+
323
+ Object.entries(updates).forEach(([key, value], index) => {
324
+ if (key !== 'pk' && key !== 'sk') {
325
+ updateExpressions.push(`#attr${index} = :val${index}`);
326
+ expressionNames[`#attr${index}`] = key;
327
+ expressionValues[`:val${index}`] = value;
328
+ }
329
+ });
330
+
331
+ const result = await docClient.send(new UpdateCommand({
332
+ TableName: this.tableName,
333
+ Key: { pk, sk },
334
+ UpdateExpression: `SET ${updateExpressions.join(', ')}`,
335
+ ExpressionAttributeNames: expressionNames,
336
+ ExpressionAttributeValues: expressionValues,
337
+ ReturnValues: 'ALL_NEW',
338
+ }));
339
+
340
+ return result.Attributes as T;
341
+ }
342
+
343
+ async delete(pk: string, sk: string): Promise<void> {
344
+ await docClient.send(new DeleteCommand({
345
+ TableName: this.tableName,
346
+ Key: { pk, sk },
347
+ }));
348
+ }
349
+
350
+ async query(
351
+ pk: string,
352
+ options?: {
353
+ skPrefix?: string;
354
+ limit?: number;
355
+ scanForward?: boolean;
356
+ }
357
+ ): Promise<T[]> {
358
+ const params: any = {
359
+ TableName: this.tableName,
360
+ KeyConditionExpression: 'pk = :pk',
361
+ ExpressionAttributeValues: { ':pk': pk },
362
+ Limit: options?.limit,
363
+ ScanIndexForward: options?.scanForward ?? true,
364
+ };
365
+
366
+ if (options?.skPrefix) {
367
+ params.KeyConditionExpression += ' AND begins_with(sk, :skPrefix)';
368
+ params.ExpressionAttributeValues[':skPrefix'] = options.skPrefix;
369
+ }
370
+
371
+ const result = await docClient.send(new QueryCommand(params));
372
+ return (result.Items as T[]) || [];
373
+ }
374
+
375
+ async batchWrite(items: T[]): Promise<void> {
376
+ const chunks = this.chunkArray(items, 25);
377
+
378
+ for (const chunk of chunks) {
379
+ await docClient.send(new BatchWriteCommand({
380
+ RequestItems: {
381
+ [this.tableName]: chunk.map(item => ({
382
+ PutRequest: { Item: item },
383
+ })),
384
+ },
385
+ }));
386
+ }
387
+ }
388
+
389
+ async transactWrite(operations: Array<{ type: 'put' | 'delete'; item: T }>): Promise<void> {
390
+ await docClient.send(new TransactWriteCommand({
391
+ TransactItems: operations.map(op => {
392
+ if (op.type === 'put') {
393
+ return { Put: { TableName: this.tableName, Item: op.item } };
394
+ }
395
+ return { Delete: { TableName: this.tableName, Key: { pk: op.item.pk, sk: op.item.sk } } };
396
+ }),
397
+ }));
398
+ }
40
399
 
41
- await client.send(new PutItemCommand({
42
- TableName: 'users',
43
- Item: { id: { S: '123' }, email: { S: 'test@example.com' } },
44
- }));
400
+ private chunkArray<U>(array: U[], size: number): U[][] {
401
+ const chunks: U[][] = [];
402
+ for (let i = 0; i < array.length; i += size) {
403
+ chunks.push(array.slice(i, i + size));
404
+ }
405
+ return chunks;
406
+ }
407
+ }
408
+ ```
409
+
410
+ ### 4. CDK Infrastructure
411
+
412
+ ```typescript
413
+ // lib/app-stack.ts
414
+ import * as cdk from 'aws-cdk-lib';
415
+ import * as lambda from 'aws-cdk-lib/aws-lambda';
416
+ import * as apigateway from 'aws-cdk-lib/aws-apigateway';
417
+ import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
418
+ import * as s3 from 'aws-cdk-lib/aws-s3';
419
+ import * as iam from 'aws-cdk-lib/aws-iam';
420
+ import { Construct } from 'constructs';
421
+ import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
422
+
423
+ export class AppStack extends cdk.Stack {
424
+ constructor(scope: Construct, id: string, props?: cdk.StackProps) {
425
+ super(scope, id, props);
426
+
427
+ // DynamoDB Table
428
+ const usersTable = new dynamodb.Table(this, 'UsersTable', {
429
+ partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
430
+ sortKey: { name: 'sk', type: dynamodb.AttributeType.STRING },
431
+ billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
432
+ pointInTimeRecovery: true,
433
+ removalPolicy: cdk.RemovalPolicy.RETAIN,
434
+ });
435
+
436
+ usersTable.addGlobalSecondaryIndex({
437
+ indexName: 'GSI1',
438
+ partitionKey: { name: 'gsi1pk', type: dynamodb.AttributeType.STRING },
439
+ sortKey: { name: 'gsi1sk', type: dynamodb.AttributeType.STRING },
440
+ projectionType: dynamodb.ProjectionType.ALL,
441
+ });
442
+
443
+ // S3 Bucket
444
+ const bucket = new s3.Bucket(this, 'AssetsBucket', {
445
+ encryption: s3.BucketEncryption.S3_MANAGED,
446
+ blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
447
+ versioned: true,
448
+ lifecycleRules: [
449
+ {
450
+ expiration: cdk.Duration.days(365),
451
+ noncurrentVersionExpiration: cdk.Duration.days(30),
452
+ },
453
+ ],
454
+ });
455
+
456
+ // Lambda Functions
457
+ const getUserFn = new NodejsFunction(this, 'GetUserFunction', {
458
+ entry: 'src/handlers/user.handler.ts',
459
+ handler: 'getUser',
460
+ runtime: lambda.Runtime.NODEJS_20_X,
461
+ timeout: cdk.Duration.seconds(30),
462
+ memorySize: 256,
463
+ environment: {
464
+ USERS_TABLE: usersTable.tableName,
465
+ S3_BUCKET: bucket.bucketName,
466
+ },
467
+ tracing: lambda.Tracing.ACTIVE,
468
+ });
469
+
470
+ const createUserFn = new NodejsFunction(this, 'CreateUserFunction', {
471
+ entry: 'src/handlers/user.handler.ts',
472
+ handler: 'createUser',
473
+ runtime: lambda.Runtime.NODEJS_20_X,
474
+ timeout: cdk.Duration.seconds(30),
475
+ memorySize: 256,
476
+ environment: {
477
+ USERS_TABLE: usersTable.tableName,
478
+ },
479
+ });
480
+
481
+ // Grant permissions
482
+ usersTable.grantReadData(getUserFn);
483
+ usersTable.grantReadWriteData(createUserFn);
484
+ bucket.grantRead(getUserFn);
485
+
486
+ // API Gateway
487
+ const api = new apigateway.RestApi(this, 'UsersApi', {
488
+ restApiName: 'Users Service',
489
+ deployOptions: {
490
+ stageName: 'prod',
491
+ throttlingRateLimit: 1000,
492
+ throttlingBurstLimit: 500,
493
+ },
494
+ defaultCorsPreflightOptions: {
495
+ allowOrigins: apigateway.Cors.ALL_ORIGINS,
496
+ allowMethods: apigateway.Cors.ALL_METHODS,
497
+ },
498
+ });
499
+
500
+ const users = api.root.addResource('users');
501
+ users.addMethod('GET', new apigateway.LambdaIntegration(getUserFn));
502
+ users.addMethod('POST', new apigateway.LambdaIntegration(createUserFn));
503
+
504
+ const user = users.addResource('{id}');
505
+ user.addMethod('GET', new apigateway.LambdaIntegration(getUserFn));
506
+
507
+ // Outputs
508
+ new cdk.CfnOutput(this, 'ApiUrl', { value: api.url });
509
+ new cdk.CfnOutput(this, 'BucketName', { value: bucket.bucketName });
510
+ }
511
+ }
512
+ ```
513
+
514
+ ### 5. ECS Container Deployment
515
+
516
+ ```typescript
517
+ // lib/ecs-stack.ts
518
+ import * as cdk from 'aws-cdk-lib';
519
+ import * as ec2 from 'aws-cdk-lib/aws-ec2';
520
+ import * as ecs from 'aws-cdk-lib/aws-ecs';
521
+ import * as ecsPatterns from 'aws-cdk-lib/aws-ecs-patterns';
522
+ import * as ecr from 'aws-cdk-lib/aws-ecr';
523
+ import { Construct } from 'constructs';
524
+
525
+ export class EcsStack extends cdk.Stack {
526
+ constructor(scope: Construct, id: string, props?: cdk.StackProps) {
527
+ super(scope, id, props);
528
+
529
+ // VPC
530
+ const vpc = new ec2.Vpc(this, 'AppVpc', {
531
+ maxAzs: 2,
532
+ natGateways: 1,
533
+ });
534
+
535
+ // ECS Cluster
536
+ const cluster = new ecs.Cluster(this, 'AppCluster', {
537
+ vpc,
538
+ containerInsights: true,
539
+ });
540
+
541
+ // Fargate Service with ALB
542
+ const service = new ecsPatterns.ApplicationLoadBalancedFargateService(
543
+ this,
544
+ 'AppService',
545
+ {
546
+ cluster,
547
+ taskImageOptions: {
548
+ image: ecs.ContainerImage.fromRegistry('nginx:latest'),
549
+ containerPort: 80,
550
+ environment: {
551
+ NODE_ENV: 'production',
552
+ },
553
+ },
554
+ desiredCount: 2,
555
+ cpu: 256,
556
+ memoryLimitMiB: 512,
557
+ publicLoadBalancer: true,
558
+ healthCheckGracePeriod: cdk.Duration.seconds(60),
559
+ }
560
+ );
561
+
562
+ // Auto Scaling
563
+ const scaling = service.service.autoScaleTaskCount({
564
+ minCapacity: 2,
565
+ maxCapacity: 10,
566
+ });
567
+
568
+ scaling.scaleOnCpuUtilization('CpuScaling', {
569
+ targetUtilizationPercent: 70,
570
+ scaleInCooldown: cdk.Duration.seconds(60),
571
+ scaleOutCooldown: cdk.Duration.seconds(60),
572
+ });
573
+
574
+ scaling.scaleOnMemoryUtilization('MemoryScaling', {
575
+ targetUtilizationPercent: 80,
576
+ });
577
+ }
578
+ }
579
+ ```
580
+
581
+ ### 6. SQS and SNS Integration
582
+
583
+ ```typescript
584
+ // src/services/messaging.service.ts
585
+ import { SQSClient, SendMessageCommand, ReceiveMessageCommand, DeleteMessageCommand } from '@aws-sdk/client-sqs';
586
+ import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';
587
+
588
+ const sqsClient = new SQSClient({});
589
+ const snsClient = new SNSClient({});
590
+
591
+ export class MessagingService {
592
+ // Send message to SQS
593
+ async sendToQueue(queueUrl: string, message: unknown, delaySeconds = 0): Promise<string> {
594
+ const result = await sqsClient.send(new SendMessageCommand({
595
+ QueueUrl: queueUrl,
596
+ MessageBody: JSON.stringify(message),
597
+ DelaySeconds: delaySeconds,
598
+ }));
599
+ return result.MessageId!;
600
+ }
601
+
602
+ // Receive messages from SQS
603
+ async receiveFromQueue(queueUrl: string, maxMessages = 10): Promise<Array<{ id: string; body: unknown; receiptHandle: string }>> {
604
+ const result = await sqsClient.send(new ReceiveMessageCommand({
605
+ QueueUrl: queueUrl,
606
+ MaxNumberOfMessages: maxMessages,
607
+ WaitTimeSeconds: 20,
608
+ }));
609
+
610
+ return (result.Messages || []).map(msg => ({
611
+ id: msg.MessageId!,
612
+ body: JSON.parse(msg.Body!),
613
+ receiptHandle: msg.ReceiptHandle!,
614
+ }));
615
+ }
616
+
617
+ // Delete message from SQS
618
+ async deleteFromQueue(queueUrl: string, receiptHandle: string): Promise<void> {
619
+ await sqsClient.send(new DeleteMessageCommand({
620
+ QueueUrl: queueUrl,
621
+ ReceiptHandle: receiptHandle,
622
+ }));
623
+ }
624
+
625
+ // Publish to SNS
626
+ async publishToTopic(topicArn: string, message: unknown, subject?: string): Promise<string> {
627
+ const result = await snsClient.send(new PublishCommand({
628
+ TopicArn: topicArn,
629
+ Message: JSON.stringify(message),
630
+ Subject: subject,
631
+ }));
632
+ return result.MessageId!;
633
+ }
634
+ }
635
+ ```
636
+
637
+ ## Use Cases
638
+
639
+ ### File Upload with Presigned URL
640
+
641
+ ```typescript
642
+ // Lambda handler for file upload
643
+ export const getUploadUrl = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
644
+ const { filename, contentType } = JSON.parse(event.body || '{}');
645
+ const userId = event.requestContext.authorizer?.claims?.sub;
646
+
647
+ const key = `uploads/${userId}/${Date.now()}-${filename}`;
648
+ const s3Service = new S3Service();
649
+ const uploadUrl = await s3Service.getUploadUrl(key, contentType);
650
+
651
+ return success({ uploadUrl, key });
652
+ };
653
+ ```
654
+
655
+ ### Event-Driven Processing
656
+
657
+ ```typescript
658
+ // SQS Event Handler
659
+ import { SQSHandler, SQSEvent } from 'aws-lambda';
660
+
661
+ export const processQueue: SQSHandler = async (event: SQSEvent) => {
662
+ for (const record of event.Records) {
663
+ const message = JSON.parse(record.body);
664
+
665
+ try {
666
+ await processMessage(message);
667
+ } catch (error) {
668
+ console.error('Failed to process message:', error);
669
+ throw error; // Message returns to queue
670
+ }
671
+ }
672
+ };
45
673
  ```
46
674
 
47
675
  ## Best Practices
48
- - Use IAM roles
49
- - Enable encryption
50
- - Use VPC for security
51
- - Monitor with CloudWatch
676
+
677
+ ### Do's
678
+
679
+ - Use IAM roles instead of access keys
680
+ - Enable encryption at rest and in transit
681
+ - Use VPC for sensitive workloads
682
+ - Implement proper error handling and retries
683
+ - Use CloudWatch for monitoring and alerting
684
+ - Tag all resources for cost tracking
685
+ - Use infrastructure as code (CDK/CloudFormation)
686
+ - Implement least privilege access
687
+ - Use environment variables for configuration
688
+ - Enable X-Ray tracing for debugging
689
+
690
+ ### Don'ts
691
+
692
+ - Don't hardcode credentials in code
693
+ - Don't use root account for deployments
694
+ - Don't expose S3 buckets publicly
695
+ - Don't skip encryption for sensitive data
696
+ - Don't ignore CloudWatch alarms
697
+ - Don't over-provision resources
698
+ - Don't skip VPC for production workloads
699
+ - Don't use synchronous invocations for long tasks
700
+ - Don't ignore cost optimization
701
+ - Don't skip backup and disaster recovery
702
+
703
+ ## References
704
+
705
+ - [AWS Documentation](https://docs.aws.amazon.com/)
706
+ - [AWS CDK Documentation](https://docs.aws.amazon.com/cdk/)
707
+ - [AWS SDK for JavaScript](https://docs.aws.amazon.com/sdk-for-javascript/)
708
+ - [AWS Well-Architected Framework](https://aws.amazon.com/architecture/well-architected/)
709
+ - [AWS Best Practices](https://aws.amazon.com/architecture/best-practices/)