@mytechtoday/augment-extensions 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +33 -1
- package/README.md +3 -3
- package/augment-extensions/domain-rules/software-architecture/README.md +143 -0
- package/augment-extensions/domain-rules/software-architecture/examples/banking-layered.md +961 -0
- package/augment-extensions/domain-rules/software-architecture/examples/ecommerce-microservices.md +990 -0
- package/augment-extensions/domain-rules/software-architecture/examples/iot-eventdriven.md +882 -0
- package/augment-extensions/domain-rules/software-architecture/examples/monolith-to-microservices-migration.md +703 -0
- package/augment-extensions/domain-rules/software-architecture/examples/serverless-imageprocessing.md +957 -0
- package/augment-extensions/domain-rules/software-architecture/examples/trading-eventdriven.md +747 -0
- package/augment-extensions/domain-rules/software-architecture/module.json +119 -0
- package/augment-extensions/domain-rules/software-architecture/rules/challenges-solutions.md +763 -0
- package/augment-extensions/domain-rules/software-architecture/rules/definitions-terminology.md +409 -0
- package/augment-extensions/domain-rules/software-architecture/rules/design-principles.md +684 -0
- package/augment-extensions/domain-rules/software-architecture/rules/evaluation-testing.md +1381 -0
- package/augment-extensions/domain-rules/software-architecture/rules/event-driven-architecture.md +616 -0
- package/augment-extensions/domain-rules/software-architecture/rules/fundamentals.md +306 -0
- package/augment-extensions/domain-rules/software-architecture/rules/industry-architectures.md +554 -0
- package/augment-extensions/domain-rules/software-architecture/rules/layered-architecture.md +776 -0
- package/augment-extensions/domain-rules/software-architecture/rules/microservices-architecture.md +503 -0
- package/augment-extensions/domain-rules/software-architecture/rules/modeling-documentation.md +1199 -0
- package/augment-extensions/domain-rules/software-architecture/rules/monolithic-architecture.md +351 -0
- package/augment-extensions/domain-rules/software-architecture/rules/principles.md +556 -0
- package/augment-extensions/domain-rules/software-architecture/rules/quality-attributes.md +797 -0
- package/augment-extensions/domain-rules/software-architecture/rules/scalability-performance.md +1345 -0
- package/augment-extensions/domain-rules/software-architecture/rules/security-architecture.md +1039 -0
- package/augment-extensions/domain-rules/software-architecture/rules/serverless-architecture.md +711 -0
- package/augment-extensions/domain-rules/software-architecture/rules/skills-development.md +568 -0
- package/augment-extensions/domain-rules/software-architecture/rules/tools-methodologies.md +961 -0
- package/augment-extensions/visual-design/CHANGELOG.md +132 -0
- package/augment-extensions/visual-design/README.md +255 -0
- package/augment-extensions/visual-design/__tests__/README.md +119 -0
- package/augment-extensions/visual-design/__tests__/style-selector.test.ts +172 -0
- package/augment-extensions/visual-design/__tests__/vendor-styles.test.ts +214 -0
- package/augment-extensions/visual-design/domains/other/ai-prompt-helper.ts +157 -0
- package/augment-extensions/visual-design/domains/other/dotnet-application.ts +156 -0
- package/augment-extensions/visual-design/domains/other/linux-platform.ts +156 -0
- package/augment-extensions/visual-design/domains/other/mobile-application.ts +157 -0
- package/augment-extensions/visual-design/domains/other/motion-picture.ts +156 -0
- package/augment-extensions/visual-design/domains/other/os-application.ts +156 -0
- package/augment-extensions/visual-design/domains/other/print-campaigns.ts +158 -0
- package/augment-extensions/visual-design/domains/other/web-app.ts +157 -0
- package/augment-extensions/visual-design/domains/other/website.ts +161 -0
- package/augment-extensions/visual-design/domains/other/windows-platform.ts +156 -0
- package/augment-extensions/visual-design/domains/web-page-styles/amazon-cloudscape.ts +506 -0
- package/augment-extensions/visual-design/domains/web-page-styles/google-modern.ts +615 -0
- package/augment-extensions/visual-design/domains/web-page-styles/microsoft-fluent.ts +531 -0
- package/augment-extensions/visual-design/examples/README.md +97 -0
- package/augment-extensions/visual-design/examples/ai-prompt-generation.md +233 -0
- package/augment-extensions/visual-design/examples/basic-usage.md +216 -0
- package/augment-extensions/visual-design/examples/domain-workflows.md +257 -0
- package/augment-extensions/visual-design/examples/vendor-comparison.md +247 -0
- package/augment-extensions/visual-design/module.json +78 -0
- package/augment-extensions/visual-design/style-selector.ts +177 -0
- package/augment-extensions/visual-design/types.ts +302 -0
- package/augment-extensions/visual-design/visual-design-core.ts +469 -0
- package/augment-extensions/workflows/adr-support/README.md +227 -0
- package/augment-extensions/workflows/adr-support/__tests__/adr-validator.test.ts +203 -0
- package/augment-extensions/workflows/adr-support/adr-validator.ts +162 -0
- package/augment-extensions/workflows/adr-support/examples/complete-lifecycle-example.md +449 -0
- package/augment-extensions/workflows/adr-support/examples/integration-example.md +580 -0
- package/augment-extensions/workflows/adr-support/examples/superseding-example.md +436 -0
- package/augment-extensions/workflows/adr-support/module.json +112 -0
- package/augment-extensions/workflows/adr-support/rules/adr-creation.md +372 -0
- package/augment-extensions/workflows/adr-support/rules/beads-integration.md +443 -0
- package/augment-extensions/workflows/adr-support/rules/conflict-detection.md +486 -0
- package/augment-extensions/workflows/adr-support/rules/decision-detection.md +362 -0
- package/augment-extensions/workflows/adr-support/rules/lifecycle-management.md +427 -0
- package/augment-extensions/workflows/adr-support/rules/openspec-integration.md +465 -0
- package/augment-extensions/workflows/adr-support/rules/template-selection.md +405 -0
- package/augment-extensions/workflows/adr-support/rules/validation-rules.md +543 -0
- package/augment-extensions/workflows/adr-support/schemas/adr-config.json +191 -0
- package/augment-extensions/workflows/adr-support/schemas/adr-metadata.json +172 -0
- package/augment-extensions/workflows/adr-support/templates/business-case.md +235 -0
- package/augment-extensions/workflows/adr-support/templates/madr-elaborate.md +197 -0
- package/augment-extensions/workflows/adr-support/templates/madr-simple.md +68 -0
- package/augment-extensions/workflows/adr-support/templates/nygard.md +84 -0
- package/augment-extensions/workflows/beads/rules/workflow.md +1 -1
- package/cli/dist/utils/__tests__/adr-validator.example.d.ts +6 -0
- package/cli/dist/utils/__tests__/adr-validator.example.d.ts.map +1 -0
- package/cli/dist/utils/__tests__/adr-validator.example.js +148 -0
- package/cli/dist/utils/__tests__/adr-validator.example.js.map +1 -0
- package/cli/dist/utils/adr-validator.d.ts +65 -0
- package/cli/dist/utils/adr-validator.d.ts.map +1 -0
- package/cli/dist/utils/adr-validator.js +203 -0
- package/cli/dist/utils/adr-validator.js.map +1 -0
- package/modules.md +40 -3
- package/package.json +1 -1
package/augment-extensions/domain-rules/software-architecture/examples/serverless-imageprocessing.md
ADDED
|
@@ -0,0 +1,957 @@
|
|
|
1
|
+
# Serverless Image Processing Pipeline Example
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document provides a comprehensive example of a serverless image processing pipeline built with AWS Lambda, S3, and event-driven architecture, focusing on scalability, cost-efficiency, and automatic scaling.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## System Context
|
|
10
|
+
|
|
11
|
+
### Business Requirements
|
|
12
|
+
|
|
13
|
+
**Functional Requirements**
|
|
14
|
+
- Image upload and storage
|
|
15
|
+
- Automatic thumbnail generation (multiple sizes)
|
|
16
|
+
- Image optimization and compression
|
|
17
|
+
- Metadata extraction (EXIF, dimensions, format)
|
|
18
|
+
- Image format conversion (JPEG, PNG, WebP)
|
|
19
|
+
- Watermark application
|
|
20
|
+
- Face detection and tagging
|
|
21
|
+
- Content moderation (NSFW detection)
|
|
22
|
+
- Image search and retrieval
|
|
23
|
+
|
|
24
|
+
**Non-Functional Requirements**
|
|
25
|
+
- **Scalability**: Handle 1M+ images/day
|
|
26
|
+
- **Cost**: Pay-per-use, no idle costs
|
|
27
|
+
- **Latency**: < 5 seconds for thumbnail generation
|
|
28
|
+
- **Availability**: 99.9% uptime
|
|
29
|
+
- **Storage**: Unlimited image storage
|
|
30
|
+
- **Security**: Encrypted storage, signed URLs
|
|
31
|
+
|
|
32
|
+
### Use Cases
|
|
33
|
+
|
|
34
|
+
- **Social Media Platform**: User profile pictures, photo uploads
|
|
35
|
+
- **E-Commerce**: Product images, multiple sizes for responsive design
|
|
36
|
+
- **Content Management**: Media library, automatic optimization
|
|
37
|
+
- **Real Estate**: Property photos, virtual tours
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Architecture Overview
|
|
42
|
+
|
|
43
|
+
### High-Level Architecture
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
47
|
+
│ Serverless Image Processing Pipeline │
|
|
48
|
+
├─────────────────────────────────────────────────────────────┤
|
|
49
|
+
│ │
|
|
50
|
+
│ User Upload → API Gateway → Lambda (Upload) → S3 (Original) │
|
|
51
|
+
│ ↓ │
|
|
52
|
+
│ S3 Event Trigger │
|
|
53
|
+
│ ↓ │
|
|
54
|
+
│ Lambda (Process Image) │
|
|
55
|
+
│ ↓ │
|
|
56
|
+
│ ┌─────────────────────────┴─────┐ │
|
|
57
|
+
│ ↓ ↓ │
|
|
58
|
+
│ Lambda (Thumbnail) Lambda (Metadata) │
|
|
59
|
+
│ ↓ ↓ │
|
|
60
|
+
│ S3 (Thumbnails) DynamoDB │
|
|
61
|
+
│ │
|
|
62
|
+
└─────────────────────────────────────────────────────────────┘
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Event Flow
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
1. Upload Flow
|
|
69
|
+
User → API Gateway → Lambda → S3 → S3 Event → Lambda (Process)
|
|
70
|
+
|
|
71
|
+
2. Processing Flow
|
|
72
|
+
S3 Event → Lambda → [Thumbnail, Optimize, Metadata, Moderation]
|
|
73
|
+
|
|
74
|
+
3. Retrieval Flow
|
|
75
|
+
User → API Gateway → Lambda → S3 (Signed URL) → User
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Service Architecture
|
|
81
|
+
|
|
82
|
+
### Lambda Functions
|
|
83
|
+
|
|
84
|
+
**1. Upload Handler**
|
|
85
|
+
- Validate image file (type, size)
|
|
86
|
+
- Generate unique filename
|
|
87
|
+
- Upload to S3 original bucket
|
|
88
|
+
- Return upload confirmation
|
|
89
|
+
|
|
90
|
+
**2. Image Processor (Orchestrator)**
|
|
91
|
+
- Triggered by S3 event
|
|
92
|
+
- Invoke parallel processing functions
|
|
93
|
+
- Coordinate workflow
|
|
94
|
+
|
|
95
|
+
**3. Thumbnail Generator**
|
|
96
|
+
- Generate multiple thumbnail sizes (150x150, 300x300, 600x600)
|
|
97
|
+
- Maintain aspect ratio
|
|
98
|
+
- Upload to S3 thumbnails bucket
|
|
99
|
+
|
|
100
|
+
**4. Image Optimizer**
|
|
101
|
+
- Compress images (reduce file size)
|
|
102
|
+
- Convert to WebP format
|
|
103
|
+
- Optimize for web delivery
|
|
104
|
+
|
|
105
|
+
**5. Metadata Extractor**
|
|
106
|
+
- Extract EXIF data (camera, location, timestamp)
|
|
107
|
+
- Calculate image dimensions
|
|
108
|
+
- Store metadata in DynamoDB
|
|
109
|
+
|
|
110
|
+
**6. Content Moderator**
|
|
111
|
+
- Detect NSFW content (AWS Rekognition)
|
|
112
|
+
- Flag inappropriate images
|
|
113
|
+
- Send notification if flagged
|
|
114
|
+
|
|
115
|
+
**7. Face Detector**
|
|
116
|
+
- Detect faces in images
|
|
117
|
+
- Extract face bounding boxes
|
|
118
|
+
- Store face data for search
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Technology Stack
|
|
123
|
+
|
|
124
|
+
### AWS Services
|
|
125
|
+
- **Compute**: AWS Lambda (Node.js, Python)
|
|
126
|
+
- **Storage**: S3 (original, thumbnails, optimized)
|
|
127
|
+
- **Database**: DynamoDB (metadata, image index)
|
|
128
|
+
- **API**: API Gateway (REST API)
|
|
129
|
+
- **AI/ML**: AWS Rekognition (face detection, content moderation)
|
|
130
|
+
- **Messaging**: SNS (notifications), SQS (dead letter queue)
|
|
131
|
+
- **CDN**: CloudFront (image delivery)
|
|
132
|
+
- **Security**: IAM, KMS (encryption)
|
|
133
|
+
|
|
134
|
+
### Libraries
|
|
135
|
+
- **Image Processing**: Sharp (Node.js), Pillow (Python)
|
|
136
|
+
- **Format Conversion**: ImageMagick
|
|
137
|
+
- **Metadata**: ExifTool
|
|
138
|
+
|
|
139
|
+
### Infrastructure as Code
|
|
140
|
+
- **IaC**: AWS SAM (Serverless Application Model)
|
|
141
|
+
- **CI/CD**: AWS CodePipeline, CodeBuild
|
|
142
|
+
- **Monitoring**: CloudWatch, X-Ray
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Implementation Details
|
|
147
|
+
|
|
148
|
+
### 1. Upload Handler Lambda
|
|
149
|
+
|
|
150
|
+
**API Gateway Integration**
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 2. Thumbnail Generator Lambda
|
|
154
|
+
|
|
155
|
+
**S3 Event-Triggered Processing**
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
// thumbnail-generator/index.js
|
|
159
|
+
const AWS = require('aws-sdk');
|
|
160
|
+
const sharp = require('sharp');
|
|
161
|
+
const s3 = new AWS.S3();
|
|
162
|
+
|
|
163
|
+
const THUMBNAIL_SIZES = [
|
|
164
|
+
{ name: 'small', width: 150, height: 150 },
|
|
165
|
+
{ name: 'medium', width: 300, height: 300 },
|
|
166
|
+
{ name: 'large', width: 600, height: 600 }
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
exports.handler = async (event) => {
|
|
170
|
+
// Get S3 event details
|
|
171
|
+
const record = event.Records[0];
|
|
172
|
+
const bucket = record.s3.bucket.name;
|
|
173
|
+
const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
// Download original image from S3
|
|
177
|
+
const originalImage = await s3.getObject({
|
|
178
|
+
Bucket: bucket,
|
|
179
|
+
Key: key
|
|
180
|
+
}).promise();
|
|
181
|
+
|
|
182
|
+
// Generate thumbnails in parallel
|
|
183
|
+
const thumbnailPromises = THUMBNAIL_SIZES.map(async (size) => {
|
|
184
|
+
// Resize image using Sharp
|
|
185
|
+
const thumbnail = await sharp(originalImage.Body)
|
|
186
|
+
.resize(size.width, size.height, {
|
|
187
|
+
fit: 'inside',
|
|
188
|
+
withoutEnlargement: true
|
|
189
|
+
})
|
|
190
|
+
.jpeg({ quality: 80 })
|
|
191
|
+
.toBuffer();
|
|
192
|
+
|
|
193
|
+
// Upload thumbnail to S3
|
|
194
|
+
const thumbnailKey = key.replace('original/', `thumbnails/${size.name}/`);
|
|
195
|
+
|
|
196
|
+
await s3.putObject({
|
|
197
|
+
Bucket: process.env.THUMBNAIL_BUCKET,
|
|
198
|
+
Key: thumbnailKey,
|
|
199
|
+
Body: thumbnail,
|
|
200
|
+
ContentType: 'image/jpeg',
|
|
201
|
+
CacheControl: 'max-age=31536000' // 1 year
|
|
202
|
+
}).promise();
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
size: size.name,
|
|
206
|
+
key: thumbnailKey,
|
|
207
|
+
width: size.width,
|
|
208
|
+
height: size.height
|
|
209
|
+
};
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const thumbnails = await Promise.all(thumbnailPromises);
|
|
213
|
+
|
|
214
|
+
console.log('Thumbnails generated:', thumbnails);
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
statusCode: 200,
|
|
218
|
+
body: JSON.stringify({ thumbnails })
|
|
219
|
+
};
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error('Thumbnail generation error:', error);
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### 3. Metadata Extractor Lambda
|
|
228
|
+
|
|
229
|
+
**Extract and Store Image Metadata**
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
// metadata-extractor/index.js
|
|
233
|
+
const AWS = require('aws-sdk');
|
|
234
|
+
const sharp = require('sharp');
|
|
235
|
+
const ExifParser = require('exif-parser');
|
|
236
|
+
const s3 = new AWS.S3();
|
|
237
|
+
const dynamodb = new AWS.DynamoDB.DocumentClient();
|
|
238
|
+
|
|
239
|
+
exports.handler = async (event) => {
|
|
240
|
+
const record = event.Records[0];
|
|
241
|
+
const bucket = record.s3.bucket.name;
|
|
242
|
+
const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
// Download image
|
|
246
|
+
const image = await s3.getObject({
|
|
247
|
+
Bucket: bucket,
|
|
248
|
+
Key: key
|
|
249
|
+
}).promise();
|
|
250
|
+
|
|
251
|
+
// Extract image metadata using Sharp
|
|
252
|
+
const metadata = await sharp(image.Body).metadata();
|
|
253
|
+
|
|
254
|
+
// Extract EXIF data
|
|
255
|
+
let exifData = {};
|
|
256
|
+
try {
|
|
257
|
+
const parser = ExifParser.create(image.Body);
|
|
258
|
+
const result = parser.parse();
|
|
259
|
+
exifData = result.tags;
|
|
260
|
+
} catch (e) {
|
|
261
|
+
console.log('No EXIF data found');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Extract image ID from key
|
|
265
|
+
const imageId = key.split('/').pop().split('.')[0];
|
|
266
|
+
|
|
267
|
+
// Prepare metadata document
|
|
268
|
+
const metadataDoc = {
|
|
269
|
+
imageId,
|
|
270
|
+
key,
|
|
271
|
+
bucket,
|
|
272
|
+
format: metadata.format,
|
|
273
|
+
width: metadata.width,
|
|
274
|
+
height: metadata.height,
|
|
275
|
+
size: image.ContentLength,
|
|
276
|
+
hasAlpha: metadata.hasAlpha,
|
|
277
|
+
orientation: metadata.orientation,
|
|
278
|
+
exif: {
|
|
279
|
+
make: exifData.Make,
|
|
280
|
+
model: exifData.Model,
|
|
281
|
+
dateTime: exifData.DateTime,
|
|
282
|
+
gps: {
|
|
283
|
+
latitude: exifData.GPSLatitude,
|
|
284
|
+
longitude: exifData.GPSLongitude
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
createdAt: new Date().toISOString()
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// Store metadata in DynamoDB
|
|
291
|
+
await dynamodb.put({
|
|
292
|
+
TableName: process.env.METADATA_TABLE,
|
|
293
|
+
Item: metadataDoc
|
|
294
|
+
}).promise();
|
|
295
|
+
|
|
296
|
+
console.log('Metadata stored:', metadataDoc);
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
statusCode: 200,
|
|
300
|
+
body: JSON.stringify({ metadata: metadataDoc })
|
|
301
|
+
};
|
|
302
|
+
} catch (error) {
|
|
303
|
+
console.error('Metadata extraction error:', error);
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### 4. Image Optimizer Lambda
|
|
310
|
+
|
|
311
|
+
**Optimize and Convert Images**
|
|
312
|
+
|
|
313
|
+
```javascript
|
|
314
|
+
// image-optimizer/index.js
|
|
315
|
+
const AWS = require('aws-sdk');
|
|
316
|
+
const sharp = require('sharp');
|
|
317
|
+
const s3 = new AWS.S3();
|
|
318
|
+
|
|
319
|
+
exports.handler = async (event) => {
|
|
320
|
+
const record = event.Records[0];
|
|
321
|
+
const bucket = record.s3.bucket.name;
|
|
322
|
+
const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
// Download original image
|
|
326
|
+
const originalImage = await s3.getObject({
|
|
327
|
+
Bucket: bucket,
|
|
328
|
+
Key: key
|
|
329
|
+
}).promise();
|
|
330
|
+
|
|
331
|
+
// Optimize JPEG
|
|
332
|
+
const optimizedJpeg = await sharp(originalImage.Body)
|
|
333
|
+
.jpeg({
|
|
334
|
+
quality: 85,
|
|
335
|
+
progressive: true,
|
|
336
|
+
mozjpeg: true
|
|
337
|
+
})
|
|
338
|
+
.toBuffer();
|
|
339
|
+
|
|
340
|
+
// Convert to WebP (modern format, better compression)
|
|
341
|
+
const webpImage = await sharp(originalImage.Body)
|
|
342
|
+
.webp({
|
|
343
|
+
quality: 85,
|
|
344
|
+
effort: 6
|
|
345
|
+
})
|
|
346
|
+
.toBuffer();
|
|
347
|
+
|
|
348
|
+
// Upload optimized JPEG
|
|
349
|
+
const jpegKey = key.replace('original/', 'optimized/jpeg/');
|
|
350
|
+
await s3.putObject({
|
|
351
|
+
Bucket: process.env.OPTIMIZED_BUCKET,
|
|
352
|
+
Key: jpegKey,
|
|
353
|
+
Body: optimizedJpeg,
|
|
354
|
+
ContentType: 'image/jpeg',
|
|
355
|
+
CacheControl: 'max-age=31536000'
|
|
356
|
+
}).promise();
|
|
357
|
+
|
|
358
|
+
// Upload WebP version
|
|
359
|
+
const webpKey = key.replace('original/', 'optimized/webp/').replace(/\.(jpg|jpeg|png)$/, '.webp');
|
|
360
|
+
await s3.putObject({
|
|
361
|
+
Bucket: process.env.OPTIMIZED_BUCKET,
|
|
362
|
+
Key: webpKey,
|
|
363
|
+
Body: webpImage,
|
|
364
|
+
ContentType: 'image/webp',
|
|
365
|
+
CacheControl: 'max-age=31536000'
|
|
366
|
+
}).promise();
|
|
367
|
+
|
|
368
|
+
// Calculate compression ratio
|
|
369
|
+
const originalSize = originalImage.ContentLength;
|
|
370
|
+
const jpegSize = optimizedJpeg.length;
|
|
371
|
+
const webpSize = webpImage.length;
|
|
372
|
+
|
|
373
|
+
console.log('Optimization complete:', {
|
|
374
|
+
originalSize,
|
|
375
|
+
jpegSize,
|
|
376
|
+
webpSize,
|
|
377
|
+
jpegSavings: ((originalSize - jpegSize) / originalSize * 100).toFixed(2) + '%',
|
|
378
|
+
webpSavings: ((originalSize - webpSize) / originalSize * 100).toFixed(2) + '%'
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
return {
|
|
382
|
+
statusCode: 200,
|
|
383
|
+
body: JSON.stringify({
|
|
384
|
+
jpegKey,
|
|
385
|
+
webpKey,
|
|
386
|
+
originalSize,
|
|
387
|
+
jpegSize,
|
|
388
|
+
webpSize
|
|
389
|
+
})
|
|
390
|
+
};
|
|
391
|
+
} catch (error) {
|
|
392
|
+
console.error('Optimization error:', error);
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### 5. Content Moderator Lambda
|
|
399
|
+
|
|
400
|
+
**AI-Powered Content Moderation**
|
|
401
|
+
|
|
402
|
+
```javascript
|
|
403
|
+
// content-moderator/index.js
|
|
404
|
+
const AWS = require('aws-sdk');
|
|
405
|
+
const rekognition = new AWS.Rekognition();
|
|
406
|
+
const sns = new AWS.SNS();
|
|
407
|
+
const dynamodb = new AWS.DynamoDB.DocumentClient();
|
|
408
|
+
|
|
409
|
+
exports.handler = async (event) => {
|
|
410
|
+
const record = event.Records[0];
|
|
411
|
+
const bucket = record.s3.bucket.name;
|
|
412
|
+
const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));
|
|
413
|
+
|
|
414
|
+
try {
|
|
415
|
+
// Detect inappropriate content using AWS Rekognition
|
|
416
|
+
const moderationResult = await rekognition.detectModerationLabels({
|
|
417
|
+
Image: {
|
|
418
|
+
S3Object: {
|
|
419
|
+
Bucket: bucket,
|
|
420
|
+
Name: key
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
MinConfidence: 75
|
|
424
|
+
}).promise();
|
|
425
|
+
|
|
426
|
+
const imageId = key.split('/').pop().split('.')[0];
|
|
427
|
+
|
|
428
|
+
// Check for inappropriate content
|
|
429
|
+
const inappropriateLabels = moderationResult.ModerationLabels.filter(
|
|
430
|
+
label => label.Confidence > 80
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
const isFlagged = inappropriateLabels.length > 0;
|
|
434
|
+
|
|
435
|
+
// Update DynamoDB with moderation results
|
|
436
|
+
await dynamodb.update({
|
|
437
|
+
TableName: process.env.METADATA_TABLE,
|
|
438
|
+
Key: { imageId },
|
|
439
|
+
UpdateExpression: 'SET moderation = :moderation, isFlagged = :isFlagged',
|
|
440
|
+
ExpressionAttributeValues: {
|
|
441
|
+
':moderation': {
|
|
442
|
+
labels: inappropriateLabels,
|
|
443
|
+
checkedAt: new Date().toISOString()
|
|
444
|
+
},
|
|
445
|
+
':isFlagged': isFlagged
|
|
446
|
+
}
|
|
447
|
+
}).promise();
|
|
448
|
+
|
|
449
|
+
// Send notification if flagged
|
|
450
|
+
if (isFlagged) {
|
|
451
|
+
await sns.publish({
|
|
452
|
+
TopicArn: process.env.ALERT_TOPIC_ARN,
|
|
453
|
+
Subject: 'Inappropriate Content Detected',
|
|
454
|
+
Message: JSON.stringify({
|
|
455
|
+
imageId,
|
|
456
|
+
key,
|
|
457
|
+
labels: inappropriateLabels
|
|
458
|
+
})
|
|
459
|
+
}).promise();
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
console.log('Moderation complete:', {
|
|
463
|
+
imageId,
|
|
464
|
+
isFlagged,
|
|
465
|
+
labels: inappropriateLabels
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
return {
|
|
469
|
+
statusCode: 200,
|
|
470
|
+
body: JSON.stringify({
|
|
471
|
+
imageId,
|
|
472
|
+
isFlagged,
|
|
473
|
+
labels: inappropriateLabels
|
|
474
|
+
})
|
|
475
|
+
};
|
|
476
|
+
} catch (error) {
|
|
477
|
+
console.error('Moderation error:', error);
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## Infrastructure as Code (AWS SAM)
|
|
486
|
+
|
|
487
|
+
### SAM Template
|
|
488
|
+
|
|
489
|
+
```yaml
|
|
490
|
+
# template.yaml
|
|
491
|
+
AWSTemplateFormatVersion: '2010-09-09'
|
|
492
|
+
Transform: AWS::Serverless-2016-10-31
|
|
493
|
+
Description: Serverless Image Processing Pipeline
|
|
494
|
+
|
|
495
|
+
Globals:
|
|
496
|
+
Function:
|
|
497
|
+
Timeout: 30
|
|
498
|
+
MemorySize: 1024
|
|
499
|
+
Runtime: nodejs18.x
|
|
500
|
+
Environment:
|
|
501
|
+
Variables:
|
|
502
|
+
ORIGINAL_BUCKET: !Ref OriginalBucket
|
|
503
|
+
THUMBNAIL_BUCKET: !Ref ThumbnailBucket
|
|
504
|
+
OPTIMIZED_BUCKET: !Ref OptimizedBucket
|
|
505
|
+
METADATA_TABLE: !Ref MetadataTable
|
|
506
|
+
|
|
507
|
+
Resources:
|
|
508
|
+
# S3 Buckets
|
|
509
|
+
OriginalBucket:
|
|
510
|
+
Type: AWS::S3::Bucket
|
|
511
|
+
Properties:
|
|
512
|
+
BucketName: !Sub '${AWS::StackName}-original'
|
|
513
|
+
VersioningConfiguration:
|
|
514
|
+
Status: Enabled
|
|
515
|
+
LifecycleConfiguration:
|
|
516
|
+
Rules:
|
|
517
|
+
- Id: DeleteOldVersions
|
|
518
|
+
Status: Enabled
|
|
519
|
+
NoncurrentVersionExpirationInDays: 30
|
|
520
|
+
|
|
521
|
+
ThumbnailBucket:
|
|
522
|
+
Type: AWS::S3::Bucket
|
|
523
|
+
Properties:
|
|
524
|
+
BucketName: !Sub '${AWS::StackName}-thumbnails'
|
|
525
|
+
|
|
526
|
+
OptimizedBucket:
|
|
527
|
+
Type: AWS::S3::Bucket
|
|
528
|
+
Properties:
|
|
529
|
+
BucketName: !Sub '${AWS::StackName}-optimized'
|
|
530
|
+
|
|
531
|
+
# DynamoDB Table
|
|
532
|
+
MetadataTable:
|
|
533
|
+
Type: AWS::DynamoDB::Table
|
|
534
|
+
Properties:
|
|
535
|
+
TableName: !Sub '${AWS::StackName}-metadata'
|
|
536
|
+
BillingMode: PAY_PER_REQUEST
|
|
537
|
+
AttributeDefinitions:
|
|
538
|
+
- AttributeName: imageId
|
|
539
|
+
AttributeType: S
|
|
540
|
+
KeySchema:
|
|
541
|
+
- AttributeName: imageId
|
|
542
|
+
KeyType: HASH
|
|
543
|
+
|
|
544
|
+
# Lambda Functions
|
|
545
|
+
UploadFunction:
|
|
546
|
+
Type: AWS::Serverless::Function
|
|
547
|
+
Properties:
|
|
548
|
+
CodeUri: upload-handler/
|
|
549
|
+
Handler: index.handler
|
|
550
|
+
Events:
|
|
551
|
+
UploadApi:
|
|
552
|
+
Type: Api
|
|
553
|
+
Properties:
|
|
554
|
+
Path: /upload
|
|
555
|
+
Method: post
|
|
556
|
+
Policies:
|
|
557
|
+
- S3WritePolicy:
|
|
558
|
+
BucketName: !Ref OriginalBucket
|
|
559
|
+
|
|
560
|
+
ThumbnailFunction:
|
|
561
|
+
Type: AWS::Serverless::Function
|
|
562
|
+
Properties:
|
|
563
|
+
CodeUri: thumbnail-generator/
|
|
564
|
+
Handler: index.handler
|
|
565
|
+
MemorySize: 2048 # More memory for image processing
|
|
566
|
+
Timeout: 60
|
|
567
|
+
Events:
|
|
568
|
+
S3Event:
|
|
569
|
+
Type: S3
|
|
570
|
+
Properties:
|
|
571
|
+
Bucket: !Ref OriginalBucket
|
|
572
|
+
Events: s3:ObjectCreated:*
|
|
573
|
+
Filter:
|
|
574
|
+
S3Key:
|
|
575
|
+
Rules:
|
|
576
|
+
- Name: prefix
|
|
577
|
+
Value: original/
|
|
578
|
+
Policies:
|
|
579
|
+
- S3ReadPolicy:
|
|
580
|
+
BucketName: !Ref OriginalBucket
|
|
581
|
+
- S3WritePolicy:
|
|
582
|
+
BucketName: !Ref ThumbnailBucket
|
|
583
|
+
|
|
584
|
+
MetadataFunction:
|
|
585
|
+
Type: AWS::Serverless::Function
|
|
586
|
+
Properties:
|
|
587
|
+
CodeUri: metadata-extractor/
|
|
588
|
+
Handler: index.handler
|
|
589
|
+
Events:
|
|
590
|
+
S3Event:
|
|
591
|
+
Type: S3
|
|
592
|
+
Properties:
|
|
593
|
+
Bucket: !Ref OriginalBucket
|
|
594
|
+
Events: s3:ObjectCreated:*
|
|
595
|
+
Policies:
|
|
596
|
+
- S3ReadPolicy:
|
|
597
|
+
BucketName: !Ref OriginalBucket
|
|
598
|
+
- DynamoDBWritePolicy:
|
|
599
|
+
TableName: !Ref MetadataTable
|
|
600
|
+
|
|
601
|
+
OptimizerFunction:
|
|
602
|
+
Type: AWS::Serverless::Function
|
|
603
|
+
Properties:
|
|
604
|
+
CodeUri: image-optimizer/
|
|
605
|
+
Handler: index.handler
|
|
606
|
+
MemorySize: 2048
|
|
607
|
+
Timeout: 60
|
|
608
|
+
Events:
|
|
609
|
+
S3Event:
|
|
610
|
+
Type: S3
|
|
611
|
+
Properties:
|
|
612
|
+
Bucket: !Ref OriginalBucket
|
|
613
|
+
Events: s3:ObjectCreated:*
|
|
614
|
+
Policies:
|
|
615
|
+
- S3ReadPolicy:
|
|
616
|
+
BucketName: !Ref OriginalBucket
|
|
617
|
+
- S3WritePolicy:
|
|
618
|
+
BucketName: !Ref OptimizedBucket
|
|
619
|
+
|
|
620
|
+
ModeratorFunction:
|
|
621
|
+
Type: AWS::Serverless::Function
|
|
622
|
+
Properties:
|
|
623
|
+
CodeUri: content-moderator/
|
|
624
|
+
Handler: index.handler
|
|
625
|
+
Events:
|
|
626
|
+
S3Event:
|
|
627
|
+
Type: S3
|
|
628
|
+
Properties:
|
|
629
|
+
Bucket: !Ref OriginalBucket
|
|
630
|
+
Events: s3:ObjectCreated:*
|
|
631
|
+
Policies:
|
|
632
|
+
- S3ReadPolicy:
|
|
633
|
+
BucketName: !Ref OriginalBucket
|
|
634
|
+
- DynamoDBWritePolicy:
|
|
635
|
+
TableName: !Ref MetadataTable
|
|
636
|
+
- RekognitionDetectOnlyPolicy: {}
|
|
637
|
+
- SNSPublishMessagePolicy:
|
|
638
|
+
TopicName: !GetAtt AlertTopic.TopicName
|
|
639
|
+
|
|
640
|
+
# SNS Topic for Alerts
|
|
641
|
+
AlertTopic:
|
|
642
|
+
Type: AWS::SNS::Topic
|
|
643
|
+
Properties:
|
|
644
|
+
TopicName: !Sub '${AWS::StackName}-alerts'
|
|
645
|
+
|
|
646
|
+
Outputs:
|
|
647
|
+
UploadApiUrl:
|
|
648
|
+
Description: API Gateway endpoint URL for upload
|
|
649
|
+
Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/upload/'
|
|
650
|
+
|
|
651
|
+
OriginalBucketName:
|
|
652
|
+
Description: S3 bucket for original images
|
|
653
|
+
Value: !Ref OriginalBucket
|
|
654
|
+
|
|
655
|
+
MetadataTableName:
|
|
656
|
+
Description: DynamoDB table for metadata
|
|
657
|
+
Value: !Ref MetadataTable
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
## Cost Optimization
|
|
663
|
+
|
|
664
|
+
### Lambda Cost Optimization
|
|
665
|
+
|
|
666
|
+
**1. Right-Size Memory**
|
|
667
|
+
```javascript
|
|
668
|
+
// Use AWS Lambda Power Tuning to find optimal memory
|
|
669
|
+
// Higher memory = faster execution = lower cost (sometimes)
|
|
670
|
+
// Example: 1024MB @ 500ms vs 512MB @ 1000ms (same cost, faster response)
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
**2. Reduce Cold Starts**
|
|
674
|
+
```javascript
|
|
675
|
+
// Keep functions warm with scheduled pings (if needed)
|
|
676
|
+
// Use provisioned concurrency for critical functions
|
|
677
|
+
// Minimize dependencies (smaller deployment package)
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
**3. Batch Processing**
|
|
681
|
+
```javascript
|
|
682
|
+
// Process multiple images in one invocation (if possible)
|
|
683
|
+
// Use S3 Batch Operations for bulk processing
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
### S3 Cost Optimization
|
|
687
|
+
|
|
688
|
+
**1. Lifecycle Policies**
|
|
689
|
+
```yaml
|
|
690
|
+
# Move old images to cheaper storage classes
|
|
691
|
+
LifecycleConfiguration:
|
|
692
|
+
Rules:
|
|
693
|
+
- Id: MoveToIA
|
|
694
|
+
Status: Enabled
|
|
695
|
+
Transitions:
|
|
696
|
+
- Days: 30
|
|
697
|
+
StorageClass: STANDARD_IA # Infrequent Access
|
|
698
|
+
- Days: 90
|
|
699
|
+
StorageClass: GLACIER # Archive
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
**2. Intelligent Tiering**
|
|
703
|
+
- Automatically moves objects between access tiers
|
|
704
|
+
- Optimizes costs based on access patterns
|
|
705
|
+
|
|
706
|
+
### Cost Metrics
|
|
707
|
+
|
|
708
|
+
**Monthly Cost Estimate (1M images/month)**
|
|
709
|
+
|
|
710
|
+
- **Lambda**: $50 (1M invocations × 5 functions × 1s avg × $0.0000166667/GB-second)
|
|
711
|
+
- **S3 Storage**: $100 (500GB × $0.023/GB)
|
|
712
|
+
- **S3 Requests**: $5 (5M PUT/GET requests)
|
|
713
|
+
- **DynamoDB**: $25 (1M writes, 5M reads)
|
|
714
|
+
- **Rekognition**: $100 (1M images × $0.001/image)
|
|
715
|
+
- **Data Transfer**: $20 (200GB out)
|
|
716
|
+
|
|
717
|
+
**Total**: ~$300/month for 1M images
|
|
718
|
+
|
|
719
|
+
**Comparison to EC2**:
|
|
720
|
+
- EC2 (t3.large 24/7): $60/month + EBS + ALB = ~$100/month
|
|
721
|
+
- But: No auto-scaling, manual management, idle costs
|
|
722
|
+
|
|
723
|
+
---
|
|
724
|
+
|
|
725
|
+
## Monitoring and Observability
|
|
726
|
+
|
|
727
|
+
### CloudWatch Metrics
|
|
728
|
+
|
|
729
|
+
**Lambda Metrics**
|
|
730
|
+
```javascript
|
|
731
|
+
// Custom metrics
|
|
732
|
+
const AWS = require('aws-sdk');
|
|
733
|
+
const cloudwatch = new AWS.CloudWatch();
|
|
734
|
+
|
|
735
|
+
async function publishMetric(metricName, value, unit = 'Count') {
|
|
736
|
+
await cloudwatch.putMetricData({
|
|
737
|
+
Namespace: 'ImageProcessing',
|
|
738
|
+
MetricData: [{
|
|
739
|
+
MetricName: metricName,
|
|
740
|
+
Value: value,
|
|
741
|
+
Unit: unit,
|
|
742
|
+
Timestamp: new Date()
|
|
743
|
+
}]
|
|
744
|
+
}).promise();
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// Usage
|
|
748
|
+
await publishMetric('ThumbnailsGenerated', 3);
|
|
749
|
+
await publishMetric('ImageSizeReduction', 45, 'Percent');
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
**Key Metrics to Monitor**
|
|
753
|
+
- Lambda invocations, duration, errors, throttles
|
|
754
|
+
- S3 bucket size, request count
|
|
755
|
+
- DynamoDB read/write capacity
|
|
756
|
+
- API Gateway latency, 4xx/5xx errors
|
|
757
|
+
|
|
758
|
+
### X-Ray Tracing
|
|
759
|
+
|
|
760
|
+
**Enable Distributed Tracing**
|
|
761
|
+
```javascript
|
|
762
|
+
const AWSXRay = require('aws-xray-sdk-core');
|
|
763
|
+
const AWS = AWSXRay.captureAWS(require('aws-sdk'));
|
|
764
|
+
|
|
765
|
+
// Trace subsegments
|
|
766
|
+
exports.handler = async (event) => {
|
|
767
|
+
const segment = AWSXRay.getSegment();
|
|
768
|
+
|
|
769
|
+
const subsegment = segment.addNewSubsegment('ImageProcessing');
|
|
770
|
+
try {
|
|
771
|
+
// Process image
|
|
772
|
+
await processImage(event);
|
|
773
|
+
subsegment.close();
|
|
774
|
+
} catch (error) {
|
|
775
|
+
subsegment.addError(error);
|
|
776
|
+
subsegment.close();
|
|
777
|
+
throw error;
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
### Alarms
|
|
783
|
+
|
|
784
|
+
```yaml
|
|
785
|
+
# CloudWatch Alarms
|
|
786
|
+
Alarms:
|
|
787
|
+
HighErrorRate:
|
|
788
|
+
Type: AWS::CloudWatch::Alarm
|
|
789
|
+
Properties:
|
|
790
|
+
AlarmName: ImageProcessing-HighErrorRate
|
|
791
|
+
MetricName: Errors
|
|
792
|
+
Namespace: AWS/Lambda
|
|
793
|
+
Statistic: Sum
|
|
794
|
+
Period: 300
|
|
795
|
+
EvaluationPeriods: 1
|
|
796
|
+
Threshold: 10
|
|
797
|
+
ComparisonOperator: GreaterThanThreshold
|
|
798
|
+
AlarmActions:
|
|
799
|
+
- !Ref AlertTopic
|
|
800
|
+
|
|
801
|
+
HighLatency:
|
|
802
|
+
Type: AWS::CloudWatch::Alarm
|
|
803
|
+
Properties:
|
|
804
|
+
AlarmName: ImageProcessing-HighLatency
|
|
805
|
+
MetricName: Duration
|
|
806
|
+
Namespace: AWS/Lambda
|
|
807
|
+
Statistic: Average
|
|
808
|
+
Period: 300
|
|
809
|
+
EvaluationPeriods: 2
|
|
810
|
+
Threshold: 5000 # 5 seconds
|
|
811
|
+
ComparisonOperator: GreaterThanThreshold
|
|
812
|
+
AlarmActions:
|
|
813
|
+
- !Ref AlertTopic
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
---
|
|
817
|
+
|
|
818
|
+
## Security Best Practices
|
|
819
|
+
|
|
820
|
+
### 1. Least Privilege IAM Policies
|
|
821
|
+
|
|
822
|
+
```yaml
|
|
823
|
+
# Lambda execution role with minimal permissions
|
|
824
|
+
LambdaExecutionRole:
|
|
825
|
+
Type: AWS::IAM::Role
|
|
826
|
+
Properties:
|
|
827
|
+
AssumeRolePolicyDocument:
|
|
828
|
+
Version: '2012-10-17'
|
|
829
|
+
Statement:
|
|
830
|
+
- Effect: Allow
|
|
831
|
+
Principal:
|
|
832
|
+
Service: lambda.amazonaws.com
|
|
833
|
+
Action: sts:AssumeRole
|
|
834
|
+
ManagedPolicyArns:
|
|
835
|
+
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
|
|
836
|
+
Policies:
|
|
837
|
+
- PolicyName: S3Access
|
|
838
|
+
PolicyDocument:
|
|
839
|
+
Version: '2012-10-17'
|
|
840
|
+
Statement:
|
|
841
|
+
- Effect: Allow
|
|
842
|
+
Action:
|
|
843
|
+
- s3:GetObject
|
|
844
|
+
Resource: !Sub '${OriginalBucket}/*'
|
|
845
|
+
- Effect: Allow
|
|
846
|
+
Action:
|
|
847
|
+
- s3:PutObject
|
|
848
|
+
Resource: !Sub '${ThumbnailBucket}/*'
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
### 2. Encryption
|
|
852
|
+
|
|
853
|
+
**S3 Encryption**
|
|
854
|
+
```yaml
|
|
855
|
+
OriginalBucket:
|
|
856
|
+
Type: AWS::S3::Bucket
|
|
857
|
+
Properties:
|
|
858
|
+
BucketEncryption:
|
|
859
|
+
ServerSideEncryptionConfiguration:
|
|
860
|
+
- ServerSideEncryptionByDefault:
|
|
861
|
+
SSEAlgorithm: AES256 # or aws:kms for KMS
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
**DynamoDB Encryption**
|
|
865
|
+
```yaml
|
|
866
|
+
MetadataTable:
|
|
867
|
+
Type: AWS::DynamoDB::Table
|
|
868
|
+
Properties:
|
|
869
|
+
SSESpecification:
|
|
870
|
+
SSEEnabled: true
|
|
871
|
+
SSEType: KMS
|
|
872
|
+
KMSMasterKeyId: !Ref KMSKey
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
### 3. API Security
|
|
876
|
+
|
|
877
|
+
**API Gateway with API Key**
|
|
878
|
+
```yaml
|
|
879
|
+
UploadApi:
|
|
880
|
+
Type: AWS::Serverless::Api
|
|
881
|
+
Properties:
|
|
882
|
+
StageName: Prod
|
|
883
|
+
Auth:
|
|
884
|
+
ApiKeyRequired: true
|
|
885
|
+
UsagePlan:
|
|
886
|
+
CreateUsagePlan: PER_API
|
|
887
|
+
Quota:
|
|
888
|
+
Limit: 10000
|
|
889
|
+
Period: DAY
|
|
890
|
+
Throttle:
|
|
891
|
+
BurstLimit: 100
|
|
892
|
+
RateLimit: 50
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
---
|
|
896
|
+
|
|
897
|
+
## Key Takeaways
|
|
898
|
+
|
|
899
|
+
### Architecture Decisions
|
|
900
|
+
|
|
901
|
+
1. **Serverless Architecture**: No server management, automatic scaling, pay-per-use
|
|
902
|
+
2. **Event-Driven Processing**: S3 events trigger Lambda functions automatically
|
|
903
|
+
3. **Parallel Processing**: Multiple Lambda functions process images concurrently
|
|
904
|
+
4. **Managed Services**: S3, DynamoDB, Rekognition reduce operational overhead
|
|
905
|
+
5. **Infrastructure as Code**: AWS SAM for reproducible deployments
|
|
906
|
+
|
|
907
|
+
### Trade-offs
|
|
908
|
+
|
|
909
|
+
**Benefits**
|
|
910
|
+
- ✅ Zero server management
|
|
911
|
+
- ✅ Automatic scaling (0 to millions)
|
|
912
|
+
- ✅ Pay only for actual usage
|
|
913
|
+
- ✅ Built-in high availability
|
|
914
|
+
- ✅ Fast time-to-market
|
|
915
|
+
- ✅ Reduced operational costs
|
|
916
|
+
|
|
917
|
+
**Challenges**
|
|
918
|
+
- ❌ Cold start latency (100-500ms)
|
|
919
|
+
- ❌ Execution time limits (15 min max)
|
|
920
|
+
- ❌ Vendor lock-in (AWS-specific)
|
|
921
|
+
- ❌ Debugging complexity (distributed system)
|
|
922
|
+
- ❌ Limited control over infrastructure
|
|
923
|
+
|
|
924
|
+
### Performance Metrics
|
|
925
|
+
|
|
926
|
+
**Before Optimization**
|
|
927
|
+
- Processing time: 10 seconds/image
|
|
928
|
+
- Cost: $500/month (1M images)
|
|
929
|
+
- Cold start: 2 seconds
|
|
930
|
+
|
|
931
|
+
**After Optimization**
|
|
932
|
+
- Processing time: 3 seconds/image
|
|
933
|
+
- Cost: $300/month (1M images)
|
|
934
|
+
- Cold start: 500ms (optimized package size)
|
|
935
|
+
|
|
936
|
+
### Lessons Learned
|
|
937
|
+
|
|
938
|
+
1. **Optimize Lambda memory**: Higher memory = faster execution (sometimes cheaper)
|
|
939
|
+
2. **Use S3 lifecycle policies**: Reduce storage costs for old images
|
|
940
|
+
3. **Implement dead letter queues**: Catch failed processing
|
|
941
|
+
4. **Monitor cold starts**: Use provisioned concurrency if needed
|
|
942
|
+
5. **Batch operations**: Process multiple items when possible
|
|
943
|
+
6. **Use CloudFront**: Cache images at edge for faster delivery
|
|
944
|
+
|
|
945
|
+
---
|
|
946
|
+
|
|
947
|
+
## References
|
|
948
|
+
|
|
949
|
+
- **AWS Lambda**: Serverless compute service
|
|
950
|
+
- **AWS SAM**: Serverless Application Model
|
|
951
|
+
- **Sharp**: High-performance Node.js image processing
|
|
952
|
+
- **AWS Rekognition**: AI-powered image analysis
|
|
953
|
+
- **AWS X-Ray**: Distributed tracing
|
|
954
|
+
|
|
955
|
+
---
|
|
956
|
+
|
|
957
|
+
**Total Lines**: 957
|