claude-agent-framework 1.0.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 (111) hide show
  1. package/README.md +128 -0
  2. package/bin/claude-framework +3 -0
  3. package/framework/agents/design-lead.md +240 -0
  4. package/framework/agents/product-owner.md +179 -0
  5. package/framework/agents/tech-lead.md +226 -0
  6. package/framework/commands/ayuda.md +127 -0
  7. package/framework/commands/a/303/261adir.md +98 -0
  8. package/framework/commands/backup.md +397 -0
  9. package/framework/commands/cambiar.md +110 -0
  10. package/framework/commands/cloud.md +457 -0
  11. package/framework/commands/code.md +142 -0
  12. package/framework/commands/debug.md +334 -0
  13. package/framework/commands/deploy.md +383 -0
  14. package/framework/commands/deshacer.md +120 -0
  15. package/framework/commands/estado.md +218 -0
  16. package/framework/commands/explica.md +227 -0
  17. package/framework/commands/feature.md +120 -0
  18. package/framework/commands/git.md +427 -0
  19. package/framework/commands/historial.md +202 -0
  20. package/framework/commands/learn.md +408 -0
  21. package/framework/commands/movil.md +245 -0
  22. package/framework/commands/nuevo.md +118 -0
  23. package/framework/commands/plan.md +134 -0
  24. package/framework/commands/prd.md +113 -0
  25. package/framework/commands/probar.md +148 -0
  26. package/framework/commands/revisar.md +208 -0
  27. package/framework/commands/seeds.md +230 -0
  28. package/framework/commands/seguridad.md +226 -0
  29. package/framework/commands/tasks.md +157 -0
  30. package/framework/skills/architecture/algorithms.md +970 -0
  31. package/framework/skills/architecture/clean-code.md +1080 -0
  32. package/framework/skills/architecture/design-patterns.md +1984 -0
  33. package/framework/skills/architecture/functional-programming.md +972 -0
  34. package/framework/skills/architecture/solid.md +991 -0
  35. package/framework/skills/cloud/cloud-aws.md +848 -0
  36. package/framework/skills/cloud/cloud-azure.md +931 -0
  37. package/framework/skills/cloud/cloud-gcp.md +848 -0
  38. package/framework/skills/cloud/message-queues.md +1229 -0
  39. package/framework/skills/core/accessibility.md +401 -0
  40. package/framework/skills/core/api.md +474 -0
  41. package/framework/skills/core/authentication.md +306 -0
  42. package/framework/skills/core/authorization.md +388 -0
  43. package/framework/skills/core/background-jobs.md +341 -0
  44. package/framework/skills/core/caching.md +473 -0
  45. package/framework/skills/core/code-review.md +341 -0
  46. package/framework/skills/core/controllers.md +290 -0
  47. package/framework/skills/core/cua.md +285 -0
  48. package/framework/skills/core/documentation.md +472 -0
  49. package/framework/skills/core/file-uploads.md +351 -0
  50. package/framework/skills/core/hotwire-native.md +296 -0
  51. package/framework/skills/core/hotwire.md +278 -0
  52. package/framework/skills/core/i18n.md +334 -0
  53. package/framework/skills/core/imports-exports.md +750 -0
  54. package/framework/skills/core/infrastructure.md +337 -0
  55. package/framework/skills/core/models.md +228 -0
  56. package/framework/skills/core/notifications.md +672 -0
  57. package/framework/skills/core/payments.md +581 -0
  58. package/framework/skills/core/performance.md +361 -0
  59. package/framework/skills/core/rails-scaffold.md +131 -0
  60. package/framework/skills/core/search.md +518 -0
  61. package/framework/skills/core/security.md +565 -0
  62. package/framework/skills/core/seeds.md +307 -0
  63. package/framework/skills/core/seo.md +542 -0
  64. package/framework/skills/core/testing.md +393 -0
  65. package/framework/skills/core/views.md +260 -0
  66. package/framework/skills/core/websockets.md +564 -0
  67. package/framework/skills/data/advanced-sql.md +1204 -0
  68. package/framework/skills/data/nosql.md +1141 -0
  69. package/framework/skills/devops/containers-advanced.md +1237 -0
  70. package/framework/skills/devops/debugging.md +834 -0
  71. package/framework/skills/devops/git-workflow.md +752 -0
  72. package/framework/skills/devops/networking.md +932 -0
  73. package/framework/skills/devops/shell-scripting.md +1132 -0
  74. package/framework/sub-agents/architecture-patterns-agent.md +1450 -0
  75. package/framework/sub-agents/cloud-agent.md +677 -0
  76. package/framework/sub-agents/data.md +504 -0
  77. package/framework/sub-agents/debugging-agent.md +554 -0
  78. package/framework/sub-agents/devops.md +483 -0
  79. package/framework/sub-agents/docs.md +176 -0
  80. package/framework/sub-agents/frontend-dev.md +349 -0
  81. package/framework/sub-agents/git-workflow-agent.md +697 -0
  82. package/framework/sub-agents/integrations.md +630 -0
  83. package/framework/sub-agents/native-dev.md +434 -0
  84. package/framework/sub-agents/qa.md +138 -0
  85. package/framework/sub-agents/rails-dev.md +375 -0
  86. package/framework/sub-agents/security.md +526 -0
  87. package/framework/sub-agents/ui.md +437 -0
  88. package/framework/sub-agents/ux.md +284 -0
  89. package/framework/templates/api-spec.md +500 -0
  90. package/framework/templates/component-spec.md +248 -0
  91. package/framework/templates/feature.json +13 -0
  92. package/framework/templates/model-spec.md +318 -0
  93. package/framework/templates/prd-template.md +80 -0
  94. package/framework/templates/task-plan.md +122 -0
  95. package/framework/templates/task-user-story.md +52 -0
  96. package/framework/templates/technical-spec.md +260 -0
  97. package/framework/templates/user-story.md +95 -0
  98. package/package.json +42 -0
  99. package/project-templates/CLAUDE.md +42 -0
  100. package/project-templates/contexts/architecture.md +25 -0
  101. package/project-templates/contexts/conventions.md +46 -0
  102. package/project-templates/contexts/design-system.md +47 -0
  103. package/project-templates/contexts/requirements.md +38 -0
  104. package/project-templates/contexts/stack.md +30 -0
  105. package/project-templates/history/active/models.md +11 -0
  106. package/project-templates/history/changelog.md +15 -0
  107. package/project-templates/workspace/.gitkeep +0 -0
  108. package/src/cli.js +52 -0
  109. package/src/init.js +104 -0
  110. package/src/status.js +75 -0
  111. package/src/update.js +88 -0
@@ -0,0 +1,848 @@
1
+ # Skill: Amazon Web Services (AWS) para Rails
2
+
3
+ ## Purpose
4
+
5
+ Configurar, desplegar y gestionar aplicaciones Rails en Amazon Web Services, aprovechando los servicios cloud para escalabilidad, rendimiento y seguridad.
6
+
7
+ ## EC2 (Elastic Compute Cloud)
8
+
9
+ ### Configurar instancia para Rails
10
+
11
+ ```bash
12
+ # Conectar a la instancia
13
+ ssh -i "mi-key.pem" ec2-user@ec2-xx-xx-xx-xx.compute-1.amazonaws.com
14
+
15
+ # Instalar dependencias (Amazon Linux 2023)
16
+ sudo dnf update -y
17
+ sudo dnf install -y git gcc make openssl-devel readline-devel zlib-devel
18
+
19
+ # Instalar rbenv y Ruby
20
+ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
21
+ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
22
+ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
23
+ source ~/.bashrc
24
+
25
+ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
26
+ rbenv install 3.3.0
27
+ rbenv global 3.3.0
28
+
29
+ # Instalar Rails
30
+ gem install bundler rails
31
+ ```
32
+
33
+ ### Security Groups
34
+
35
+ ```ruby
36
+ # Terraform o CloudFormation para Security Groups
37
+ # security_group.tf
38
+
39
+ resource "aws_security_group" "rails_app" {
40
+ name = "rails-app-sg"
41
+ description = "Security group for Rails application"
42
+ vpc_id = aws_vpc.main.id
43
+
44
+ # SSH
45
+ ingress {
46
+ from_port = 22
47
+ to_port = 22
48
+ protocol = "tcp"
49
+ cidr_blocks = ["TU_IP/32"] # Solo tu IP
50
+ }
51
+
52
+ # HTTP
53
+ ingress {
54
+ from_port = 80
55
+ to_port = 80
56
+ protocol = "tcp"
57
+ cidr_blocks = ["0.0.0.0/0"]
58
+ }
59
+
60
+ # HTTPS
61
+ ingress {
62
+ from_port = 443
63
+ to_port = 443
64
+ protocol = "tcp"
65
+ cidr_blocks = ["0.0.0.0/0"]
66
+ }
67
+
68
+ # Rails en desarrollo
69
+ ingress {
70
+ from_port = 3000
71
+ to_port = 3000
72
+ protocol = "tcp"
73
+ cidr_blocks = ["0.0.0.0/0"]
74
+ }
75
+
76
+ egress {
77
+ from_port = 0
78
+ to_port = 0
79
+ protocol = "-1"
80
+ cidr_blocks = ["0.0.0.0/0"]
81
+ }
82
+ }
83
+ ```
84
+
85
+ ### AMI personalizada para Rails
86
+
87
+ ```bash
88
+ # Después de configurar la instancia, crear AMI
89
+ aws ec2 create-image \
90
+ --instance-id i-1234567890abcdef0 \
91
+ --name "Rails-App-AMI-$(date +%Y%m%d)" \
92
+ --description "Rails app with Ruby 3.3, dependencies"
93
+ ```
94
+
95
+ ## S3 (Simple Storage Service)
96
+
97
+ ### Configurar Active Storage con S3
98
+
99
+ ```ruby
100
+ # Gemfile
101
+ gem "aws-sdk-s3", require: false
102
+
103
+ # config/storage.yml
104
+ amazon:
105
+ service: S3
106
+ access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
107
+ secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
108
+ region: us-east-1
109
+ bucket: <%= ENV.fetch("S3_BUCKET") { "mi-app-#{Rails.env}" } %>
110
+
111
+ # Para uploads públicos
112
+ amazon_public:
113
+ service: S3
114
+ access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
115
+ secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
116
+ region: us-east-1
117
+ bucket: mi-app-public
118
+ public: true
119
+
120
+ # config/environments/production.rb
121
+ config.active_storage.service = :amazon
122
+ ```
123
+
124
+ ### Bucket Policy para Active Storage
125
+
126
+ ```json
127
+ {
128
+ "Version": "2012-10-17",
129
+ "Statement": [
130
+ {
131
+ "Sid": "AllowRailsApp",
132
+ "Effect": "Allow",
133
+ "Principal": {
134
+ "AWS": "arn:aws:iam::123456789012:user/rails-app-user"
135
+ },
136
+ "Action": [
137
+ "s3:PutObject",
138
+ "s3:GetObject",
139
+ "s3:DeleteObject",
140
+ "s3:ListBucket"
141
+ ],
142
+ "Resource": [
143
+ "arn:aws:s3:::mi-app-production",
144
+ "arn:aws:s3:::mi-app-production/*"
145
+ ]
146
+ }
147
+ ]
148
+ }
149
+ ```
150
+
151
+ ### Presigned URLs
152
+
153
+ ```ruby
154
+ # app/services/s3_presigner.rb
155
+ class S3Presigner
156
+ def initialize
157
+ @client = Aws::S3::Client.new(
158
+ region: ENV["AWS_REGION"],
159
+ credentials: Aws::Credentials.new(
160
+ Rails.application.credentials.dig(:aws, :access_key_id),
161
+ Rails.application.credentials.dig(:aws, :secret_access_key)
162
+ )
163
+ )
164
+ @signer = Aws::S3::Presigner.new(client: @client)
165
+ end
166
+
167
+ # URL para subir archivo
168
+ def presigned_put(key, content_type:, expires_in: 900)
169
+ @signer.presigned_url(:put_object,
170
+ bucket: ENV["S3_BUCKET"],
171
+ key: key,
172
+ content_type: content_type,
173
+ expires_in: expires_in
174
+ )
175
+ end
176
+
177
+ # URL para descargar archivo (privado)
178
+ def presigned_get(key, expires_in: 3600, filename: nil)
179
+ options = {
180
+ bucket: ENV["S3_BUCKET"],
181
+ key: key,
182
+ expires_in: expires_in
183
+ }
184
+
185
+ if filename
186
+ options[:response_content_disposition] = "attachment; filename=\"#{filename}\""
187
+ end
188
+
189
+ @signer.presigned_url(:get_object, options)
190
+ end
191
+ end
192
+
193
+ # Uso en controller
194
+ class UploadsController < ApplicationController
195
+ def presigned_url
196
+ presigner = S3Presigner.new
197
+ key = "uploads/#{SecureRandom.uuid}/#{params[:filename]}"
198
+
199
+ render json: {
200
+ url: presigner.presigned_put(key, content_type: params[:content_type]),
201
+ key: key
202
+ }
203
+ end
204
+ end
205
+ ```
206
+
207
+ ### Configurar CORS en S3
208
+
209
+ ```json
210
+ [
211
+ {
212
+ "AllowedHeaders": ["*"],
213
+ "AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
214
+ "AllowedOrigins": ["https://miapp.com", "http://localhost:3000"],
215
+ "ExposeHeaders": ["ETag"],
216
+ "MaxAgeSeconds": 3000
217
+ }
218
+ ]
219
+ ```
220
+
221
+ ## RDS (Relational Database Service)
222
+
223
+ ### Configurar Rails con RDS PostgreSQL
224
+
225
+ ```yaml
226
+ # config/database.yml
227
+ production:
228
+ adapter: postgresql
229
+ encoding: unicode
230
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
231
+ database: <%= ENV["RDS_DB_NAME"] %>
232
+ username: <%= ENV["RDS_USERNAME"] %>
233
+ password: <%= ENV["RDS_PASSWORD"] %>
234
+ host: <%= ENV["RDS_HOSTNAME"] %>
235
+ port: <%= ENV["RDS_PORT"] || 5432 %>
236
+
237
+ # Configuraciones de conexión
238
+ connect_timeout: 5
239
+ checkout_timeout: 5
240
+ reaping_frequency: 10
241
+ dead_connection_timeout: 5
242
+
243
+ # SSL en producción
244
+ sslmode: require
245
+ ```
246
+
247
+ ### Read Replicas
248
+
249
+ ```ruby
250
+ # config/database.yml con read replica
251
+ production:
252
+ primary:
253
+ adapter: postgresql
254
+ database: <%= ENV["RDS_DB_NAME"] %>
255
+ username: <%= ENV["RDS_USERNAME"] %>
256
+ password: <%= ENV["RDS_PASSWORD"] %>
257
+ host: <%= ENV["RDS_PRIMARY_HOST"] %>
258
+
259
+ primary_replica:
260
+ adapter: postgresql
261
+ database: <%= ENV["RDS_DB_NAME"] %>
262
+ username: <%= ENV["RDS_USERNAME"] %>
263
+ password: <%= ENV["RDS_PASSWORD"] %>
264
+ host: <%= ENV["RDS_REPLICA_HOST"] %>
265
+ replica: true
266
+
267
+ # app/models/application_record.rb
268
+ class ApplicationRecord < ActiveRecord::Base
269
+ primary_abstract_class
270
+
271
+ # Usar replica para lecturas pesadas
272
+ connects_to database: { writing: :primary, reading: :primary_replica }
273
+ end
274
+
275
+ # Uso en controladores
276
+ class ReportsController < ApplicationController
277
+ def index
278
+ ActiveRecord::Base.connected_to(role: :reading) do
279
+ @reports = Report.complex_query.to_a
280
+ end
281
+ end
282
+ end
283
+ ```
284
+
285
+ ### Parameter Groups
286
+
287
+ ```ruby
288
+ # Configuración recomendada para Rails (via AWS CLI o Console)
289
+ # Crear parameter group personalizado
290
+
291
+ # Parámetros importantes:
292
+ # - shared_buffers: 25% de la memoria
293
+ # - effective_cache_size: 75% de la memoria
294
+ # - work_mem: 64MB
295
+ # - maintenance_work_mem: 512MB
296
+ # - checkpoint_completion_target: 0.9
297
+ # - wal_buffers: 16MB
298
+ # - random_page_cost: 1.1 (para SSD)
299
+ # - log_statement: 'all' (desarrollo)
300
+ # - log_min_duration_statement: 1000 (queries > 1s)
301
+ ```
302
+
303
+ ## Lambda
304
+
305
+ ### Rails con Lambda via Jets o Lamby
306
+
307
+ ```ruby
308
+ # Gemfile
309
+ gem "lamby"
310
+
311
+ # config/lamby.rb
312
+ Lamby.config do |config|
313
+ config.rack_app = Rails.application
314
+ end
315
+
316
+ # handler.rb
317
+ require_relative "config/boot"
318
+ require "lamby"
319
+ require_relative "config/application"
320
+ require_relative "config/environment"
321
+
322
+ $app = Rack::Builder.new { run Rails.application }.to_app
323
+
324
+ def handler(event:, context:)
325
+ Lamby.handler($app, event, context)
326
+ end
327
+ ```
328
+
329
+ ### API Gateway + Lambda para Webhooks
330
+
331
+ ```ruby
332
+ # Función Lambda para procesar webhooks
333
+ # handler.rb
334
+ require "json"
335
+ require "aws-sdk-sqs"
336
+
337
+ def handler(event:, context:)
338
+ body = JSON.parse(event["body"])
339
+
340
+ # Enviar a SQS para procesamiento async
341
+ sqs = Aws::SQS::Client.new
342
+ sqs.send_message(
343
+ queue_url: ENV["WEBHOOK_QUEUE_URL"],
344
+ message_body: body.to_json
345
+ )
346
+
347
+ {
348
+ statusCode: 200,
349
+ body: JSON.generate({ received: true })
350
+ }
351
+ rescue JSON::ParserError
352
+ {
353
+ statusCode: 400,
354
+ body: JSON.generate({ error: "Invalid JSON" })
355
+ }
356
+ end
357
+ ```
358
+
359
+ ## ECS/Fargate
360
+
361
+ ### Dockerfile para Rails
362
+
363
+ ```dockerfile
364
+ # Dockerfile
365
+ FROM ruby:3.3.0-slim
366
+
367
+ # Instalar dependencias
368
+ RUN apt-get update -qq && \
369
+ apt-get install -y build-essential libpq-dev nodejs npm && \
370
+ npm install -g yarn && \
371
+ rm -rf /var/lib/apt/lists/*
372
+
373
+ WORKDIR /app
374
+
375
+ # Instalar gems
376
+ COPY Gemfile Gemfile.lock ./
377
+ RUN bundle config set --local deployment 'true' && \
378
+ bundle config set --local without 'development test' && \
379
+ bundle install
380
+
381
+ # Copiar aplicación
382
+ COPY . .
383
+
384
+ # Precompilar assets
385
+ RUN SECRET_KEY_BASE=dummy bundle exec rails assets:precompile
386
+
387
+ # Puerto
388
+ EXPOSE 3000
389
+
390
+ # Comando
391
+ CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
392
+ ```
393
+
394
+ ### Task Definition
395
+
396
+ ```json
397
+ {
398
+ "family": "rails-app",
399
+ "networkMode": "awsvpc",
400
+ "requiresCompatibilities": ["FARGATE"],
401
+ "cpu": "512",
402
+ "memory": "1024",
403
+ "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
404
+ "taskRoleArn": "arn:aws:iam::123456789012:role/ecsTaskRole",
405
+ "containerDefinitions": [
406
+ {
407
+ "name": "rails-app",
408
+ "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/rails-app:latest",
409
+ "portMappings": [
410
+ {
411
+ "containerPort": 3000,
412
+ "protocol": "tcp"
413
+ }
414
+ ],
415
+ "environment": [
416
+ { "name": "RAILS_ENV", "value": "production" },
417
+ { "name": "RAILS_LOG_TO_STDOUT", "value": "true" }
418
+ ],
419
+ "secrets": [
420
+ {
421
+ "name": "DATABASE_URL",
422
+ "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:rails-app/database-url"
423
+ },
424
+ {
425
+ "name": "SECRET_KEY_BASE",
426
+ "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:rails-app/secret-key-base"
427
+ }
428
+ ],
429
+ "logConfiguration": {
430
+ "logDriver": "awslogs",
431
+ "options": {
432
+ "awslogs-group": "/ecs/rails-app",
433
+ "awslogs-region": "us-east-1",
434
+ "awslogs-stream-prefix": "ecs"
435
+ }
436
+ }
437
+ }
438
+ ]
439
+ }
440
+ ```
441
+
442
+ ## CloudFront (CDN)
443
+
444
+ ### Configurar con Rails Assets
445
+
446
+ ```ruby
447
+ # config/environments/production.rb
448
+ config.asset_host = ENV["CLOUDFRONT_DOMAIN"]
449
+ # Ejemplo: "https://d1234567890.cloudfront.net"
450
+
451
+ # Para Active Storage con CloudFront
452
+ config.active_storage.resolve_model_to_route = :rails_storage_proxy
453
+ ```
454
+
455
+ ### Invalidar cache después de deploy
456
+
457
+ ```ruby
458
+ # lib/tasks/cloudfront.rake
459
+ namespace :cloudfront do
460
+ desc "Invalidar cache de CloudFront"
461
+ task invalidate: :environment do
462
+ require "aws-sdk-cloudfront"
463
+
464
+ client = Aws::CloudFront::Client.new(region: "us-east-1")
465
+
466
+ client.create_invalidation(
467
+ distribution_id: ENV["CLOUDFRONT_DISTRIBUTION_ID"],
468
+ invalidation_batch: {
469
+ paths: {
470
+ quantity: 1,
471
+ items: ["/assets/*"]
472
+ },
473
+ caller_reference: "deploy-#{Time.current.to_i}"
474
+ }
475
+ )
476
+
477
+ puts "Invalidation created"
478
+ end
479
+ end
480
+ ```
481
+
482
+ ## Route53
483
+
484
+ ### Configurar DNS para la app
485
+
486
+ ```ruby
487
+ # Ejemplo con Terraform
488
+ resource "aws_route53_record" "app" {
489
+ zone_id = aws_route53_zone.main.zone_id
490
+ name = "app.midominio.com"
491
+ type = "A"
492
+
493
+ alias {
494
+ name = aws_lb.main.dns_name
495
+ zone_id = aws_lb.main.zone_id
496
+ evaluate_target_health = true
497
+ }
498
+ }
499
+
500
+ # Health check
501
+ resource "aws_route53_health_check" "app" {
502
+ fqdn = "app.midominio.com"
503
+ port = 443
504
+ type = "HTTPS"
505
+ resource_path = "/up"
506
+ failure_threshold = "3"
507
+ request_interval = "30"
508
+ }
509
+ ```
510
+
511
+ ## IAM (Identity and Access Management)
512
+
513
+ ### Política mínima para Rails App
514
+
515
+ ```json
516
+ {
517
+ "Version": "2012-10-17",
518
+ "Statement": [
519
+ {
520
+ "Sid": "S3Access",
521
+ "Effect": "Allow",
522
+ "Action": [
523
+ "s3:PutObject",
524
+ "s3:GetObject",
525
+ "s3:DeleteObject"
526
+ ],
527
+ "Resource": "arn:aws:s3:::mi-app-production/*"
528
+ },
529
+ {
530
+ "Sid": "S3ListBucket",
531
+ "Effect": "Allow",
532
+ "Action": "s3:ListBucket",
533
+ "Resource": "arn:aws:s3:::mi-app-production"
534
+ },
535
+ {
536
+ "Sid": "SESAccess",
537
+ "Effect": "Allow",
538
+ "Action": [
539
+ "ses:SendEmail",
540
+ "ses:SendRawEmail"
541
+ ],
542
+ "Resource": "*",
543
+ "Condition": {
544
+ "StringEquals": {
545
+ "ses:FromAddress": "noreply@midominio.com"
546
+ }
547
+ }
548
+ },
549
+ {
550
+ "Sid": "SecretsManagerAccess",
551
+ "Effect": "Allow",
552
+ "Action": [
553
+ "secretsmanager:GetSecretValue"
554
+ ],
555
+ "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:rails-app/*"
556
+ },
557
+ {
558
+ "Sid": "SQSAccess",
559
+ "Effect": "Allow",
560
+ "Action": [
561
+ "sqs:SendMessage",
562
+ "sqs:ReceiveMessage",
563
+ "sqs:DeleteMessage",
564
+ "sqs:GetQueueAttributes"
565
+ ],
566
+ "Resource": "arn:aws:sqs:us-east-1:123456789012:rails-app-*"
567
+ }
568
+ ]
569
+ }
570
+ ```
571
+
572
+ ## ElastiCache (Redis)
573
+
574
+ ### Configurar con Rails
575
+
576
+ ```ruby
577
+ # Gemfile
578
+ gem "redis"
579
+ gem "hiredis"
580
+
581
+ # config/initializers/redis.rb
582
+ REDIS = Redis.new(
583
+ url: ENV.fetch("ELASTICACHE_URL") { "redis://localhost:6379/0" },
584
+ driver: :hiredis
585
+ )
586
+
587
+ # config/environments/production.rb
588
+ # Cache store
589
+ config.cache_store = :redis_cache_store, {
590
+ url: ENV["ELASTICACHE_URL"],
591
+ pool_size: ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i,
592
+ pool_timeout: 5
593
+ }
594
+
595
+ # Session store
596
+ config.session_store :redis_store,
597
+ servers: [ENV["ELASTICACHE_URL"]],
598
+ expire_after: 1.day,
599
+ key: "_myapp_session"
600
+
601
+ # Action Cable
602
+ config.action_cable.adapter = :redis
603
+ config.action_cable.url = ENV["ELASTICACHE_URL"]
604
+ ```
605
+
606
+ ## SES (Simple Email Service)
607
+
608
+ ### Configurar Action Mailer
609
+
610
+ ```ruby
611
+ # config/environments/production.rb
612
+ config.action_mailer.delivery_method = :ses
613
+
614
+ # Opción 1: Con gem aws-sdk-ses
615
+ config.action_mailer.ses_settings = {
616
+ region: "us-east-1",
617
+ credentials: Aws::Credentials.new(
618
+ Rails.application.credentials.dig(:aws, :access_key_id),
619
+ Rails.application.credentials.dig(:aws, :secret_access_key)
620
+ )
621
+ }
622
+
623
+ # Opción 2: Con SMTP
624
+ config.action_mailer.smtp_settings = {
625
+ address: "email-smtp.us-east-1.amazonaws.com",
626
+ port: 587,
627
+ user_name: ENV["SES_SMTP_USERNAME"],
628
+ password: ENV["SES_SMTP_PASSWORD"],
629
+ authentication: :login,
630
+ enable_starttls_auto: true
631
+ }
632
+ ```
633
+
634
+ ## Secrets Manager / Parameter Store
635
+
636
+ ### Cargar secrets al iniciar Rails
637
+
638
+ ```ruby
639
+ # config/initializers/aws_secrets.rb
640
+ if Rails.env.production?
641
+ require "aws-sdk-secretsmanager"
642
+
643
+ client = Aws::SecretsManager::Client.new(region: "us-east-1")
644
+
645
+ begin
646
+ secret = client.get_secret_value(secret_id: "rails-app/#{Rails.env}")
647
+ secrets = JSON.parse(secret.secret_string)
648
+
649
+ # Setear variables de entorno
650
+ secrets.each do |key, value|
651
+ ENV[key] = value unless ENV[key].present?
652
+ end
653
+ rescue Aws::SecretsManager::Errors::ServiceError => e
654
+ Rails.logger.error "Error loading secrets: #{e.message}"
655
+ end
656
+ end
657
+ ```
658
+
659
+ ### Rotar secrets automáticamente
660
+
661
+ ```ruby
662
+ # Lambda function para rotación
663
+ def handler(event:, context:)
664
+ require "aws-sdk-secretsmanager"
665
+ require "securerandom"
666
+
667
+ client = Aws::SecretsManager::Client.new
668
+ secret_id = event["SecretId"]
669
+ step = event["Step"]
670
+
671
+ case step
672
+ when "createSecret"
673
+ # Generar nuevo secret
674
+ new_secret = SecureRandom.hex(64)
675
+ client.put_secret_value(
676
+ secret_id: secret_id,
677
+ client_request_token: event["ClientRequestToken"],
678
+ secret_string: new_secret,
679
+ version_stages: ["AWSPENDING"]
680
+ )
681
+ when "setSecret"
682
+ # Aplicar nuevo secret (actualizar en la app)
683
+ when "testSecret"
684
+ # Verificar que funciona
685
+ when "finishSecret"
686
+ # Marcar como current
687
+ client.update_secret_version_stage(
688
+ secret_id: secret_id,
689
+ version_stage: "AWSCURRENT",
690
+ move_to_version_id: event["ClientRequestToken"],
691
+ remove_from_version_id: get_current_version(client, secret_id)
692
+ )
693
+ end
694
+ end
695
+ ```
696
+
697
+ ## CloudWatch
698
+
699
+ ### Configurar logging
700
+
701
+ ```ruby
702
+ # config/environments/production.rb
703
+ if ENV["AWS_EXECUTION_ENV"].present? || ENV["ECS_CONTAINER_METADATA_URI"].present?
704
+ config.logger = ActiveSupport::Logger.new(STDOUT)
705
+ config.logger.formatter = proc do |severity, datetime, progname, msg|
706
+ {
707
+ timestamp: datetime.iso8601,
708
+ level: severity,
709
+ message: msg,
710
+ progname: progname
711
+ }.to_json + "\n"
712
+ end
713
+ end
714
+ ```
715
+
716
+ ### Custom Metrics
717
+
718
+ ```ruby
719
+ # app/services/cloudwatch_metrics.rb
720
+ class CloudwatchMetrics
721
+ def initialize
722
+ @client = Aws::CloudWatch::Client.new(region: "us-east-1")
723
+ @namespace = "RailsApp/#{Rails.env}"
724
+ end
725
+
726
+ def record(metric_name, value, unit: "Count", dimensions: {})
727
+ @client.put_metric_data(
728
+ namespace: @namespace,
729
+ metric_data: [
730
+ {
731
+ metric_name: metric_name,
732
+ value: value,
733
+ unit: unit,
734
+ timestamp: Time.current,
735
+ dimensions: dimensions.map { |k, v| { name: k.to_s, value: v.to_s } }
736
+ }
737
+ ]
738
+ )
739
+ end
740
+
741
+ def record_request_time(duration_ms, controller:, action:)
742
+ record(
743
+ "RequestDuration",
744
+ duration_ms,
745
+ unit: "Milliseconds",
746
+ dimensions: { Controller: controller, Action: action }
747
+ )
748
+ end
749
+ end
750
+
751
+ # Uso en ApplicationController
752
+ class ApplicationController < ActionController::Base
753
+ around_action :record_metrics
754
+
755
+ private
756
+
757
+ def record_metrics
758
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
759
+ yield
760
+ ensure
761
+ duration = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000).round
762
+ CloudwatchMetrics.new.record_request_time(
763
+ duration,
764
+ controller: controller_name,
765
+ action: action_name
766
+ )
767
+ end
768
+ end
769
+ ```
770
+
771
+ ### Alarms
772
+
773
+ ```ruby
774
+ # Crear alarma para errores 5xx
775
+ resource "aws_cloudwatch_metric_alarm" "high_5xx_errors" {
776
+ alarm_name = "rails-app-high-5xx-errors"
777
+ comparison_operator = "GreaterThanThreshold"
778
+ evaluation_periods = "2"
779
+ metric_name = "HTTPCode_Target_5XX_Count"
780
+ namespace = "AWS/ApplicationELB"
781
+ period = "300"
782
+ statistic = "Sum"
783
+ threshold = "10"
784
+ alarm_description = "Alerta cuando hay mas de 10 errores 5xx en 5 minutos"
785
+
786
+ dimensions = {
787
+ LoadBalancer = aws_lb.main.arn_suffix
788
+ TargetGroup = aws_lb_target_group.rails.arn_suffix
789
+ }
790
+
791
+ alarm_actions = [aws_sns_topic.alerts.arn]
792
+ ok_actions = [aws_sns_topic.alerts.arn]
793
+ }
794
+ ```
795
+
796
+ ## Costos y Optimización
797
+
798
+ ### Instancias Reservadas vs Spot
799
+
800
+ ```ruby
801
+ # Para workloads predecibles: Reserved Instances
802
+ # Ahorro: 30-60% vs On-Demand
803
+
804
+ # Para workers/jobs: Spot Instances
805
+ # Ahorro: hasta 90%
806
+
807
+ # Estrategia mixta para ECS:
808
+ # - Web servers: On-Demand o Reserved
809
+ # - Background workers: Spot con fallback a On-Demand
810
+ ```
811
+
812
+ ### Calculadora de costos típicos
813
+
814
+ ```markdown
815
+ ## Estimación mensual para app Rails pequeña-mediana
816
+
817
+ | Servicio | Configuración | Costo/mes |
818
+ |----------|--------------|-----------|
819
+ | EC2/ECS | t3.medium (2 instancias) | ~$60 |
820
+ | RDS | db.t3.small PostgreSQL | ~$25 |
821
+ | ElastiCache | cache.t3.micro | ~$12 |
822
+ | S3 | 50GB + transferencia | ~$5 |
823
+ | CloudFront | 100GB transferencia | ~$10 |
824
+ | Route53 | 1 zona + queries | ~$1 |
825
+ | SES | 10,000 emails | ~$1 |
826
+ | **Total** | | **~$114/mes** |
827
+
828
+ ## Optimizaciones:
829
+ - Usar Savings Plans (hasta 72% ahorro)
830
+ - Auto Scaling para reducir en horas bajas
831
+ - S3 Intelligent-Tiering para archivos
832
+ - Reserved Capacity para RDS
833
+ ```
834
+
835
+ ## Checklist de Deployment
836
+
837
+ - [ ] VPC con subnets públicas y privadas
838
+ - [ ] Security Groups configurados (mínimo privilegio)
839
+ - [ ] RDS en subnet privada con backups automáticos
840
+ - [ ] S3 bucket con versionado y lifecycle policies
841
+ - [ ] IAM roles con políticas mínimas
842
+ - [ ] Secrets en Secrets Manager o Parameter Store
843
+ - [ ] CloudWatch logs y métricas configuradas
844
+ - [ ] Alarmas para métricas críticas
845
+ - [ ] CloudFront para assets estáticos
846
+ - [ ] Route53 con health checks
847
+ - [ ] SSL/TLS con ACM (Certificate Manager)
848
+ - [ ] Backups automáticos probados