#!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import { ConditionCheckLambdaStack } from "../lib/condition-check-lambda-stack"; import { ImageProcessorLambdaStack } from "../lib/image-processor-lambda-stack"; import { VpcStack } from "../lib/vpc-stack"; import { CertificateStack } from "../lib/certificate-stack"; import { SecretsStack } from "../lib/secrets-stack"; import { EcrStack } from "../lib/ecr-stack"; import { RdsStack } from "../lib/rds-stack"; import { EcsServiceStack } from "../lib/ecs-service-stack"; const app = new cdk.App(); // Get environment from context or default to dev const environment = app.node.tryGetContext("env") || "dev"; // Get context variables for deployment configuration const allowedIp = app.node.tryGetContext("allowedIp"); // e.g., "1.2.3.4/32" const domainName = app.node.tryGetContext("domainName") || "village-share.com"; // Environment-specific configurations const envConfig: Record< string, { databaseUrl: string; frontendUrl: string; sesFromEmail: string; emailEnabled: boolean; natGateways: number; subdomain: string; dbInstanceType: "micro" | "small" | "medium"; multiAz: boolean; } > = { dev: { databaseUrl: process.env.DATABASE_URL || "postgresql://user:password@localhost:5432/rentall_dev", frontendUrl: `https://dev.${domainName}`, sesFromEmail: `noreply@${domainName}`, emailEnabled: false, // Disable emails in dev natGateways: 1, subdomain: "dev", dbInstanceType: "micro", multiAz: false, }, staging: { databaseUrl: process.env.DATABASE_URL || "postgresql://user:password@localhost:5432/rentall_staging", frontendUrl: `https://staging.${domainName}`, sesFromEmail: `noreply@${domainName}`, emailEnabled: true, natGateways: 1, subdomain: "staging", dbInstanceType: "micro", multiAz: false, }, prod: { databaseUrl: process.env.DATABASE_URL || "postgresql://user:password@localhost:5432/rentall_prod", frontendUrl: `https://${domainName}`, sesFromEmail: `noreply@${domainName}`, emailEnabled: true, natGateways: 2, // Multi-AZ NAT gateways for high availability subdomain: "", // No subdomain for prod dbInstanceType: "small", multiAz: true, }, }; const config = envConfig[environment]; if (!config) { throw new Error(`Unknown environment: ${environment}`); } const envProps = { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION || "us-east-1", }, }; // Common tags for all stacks const commonTags = { Environment: environment, Project: "village-share", ManagedBy: "cdk", }; // ============================================================================ // Certificate Stack (Shared across environments) // Deploy this once and validate DNS before deploying other stacks // ============================================================================ const certificateStack = new CertificateStack(app, "CertificateStack", { domainName, ...envProps, description: `ACM wildcard certificate for ${domainName}`, tags: { ...commonTags, Service: "certificate", }, }); // ============================================================================ // VPC Stack // ============================================================================ const vpcStack = new VpcStack(app, `VpcStack-${environment}`, { environment, natGateways: config.natGateways, maxAzs: 2, ...envProps, description: `VPC infrastructure with private subnets and VPC endpoints (${environment})`, tags: { ...commonTags, Service: "networking", }, }); // ============================================================================ // Secrets Stack // ============================================================================ const secretsStack = new SecretsStack(app, `SecretsStack-${environment}`, { environment, ...envProps, description: `Secrets Manager secrets for database and application (${environment})`, tags: { ...commonTags, Service: "secrets", }, }); // ============================================================================ // ECR Stack // ============================================================================ const ecrStack = new EcrStack(app, `EcrStack-${environment}`, { environment, ...envProps, description: `ECR repositories for Docker images (${environment})`, tags: { ...commonTags, Service: "ecr", }, }); // ============================================================================ // RDS Stack // ============================================================================ const rdsStack = new RdsStack(app, `RdsStack-${environment}`, { environment, vpc: vpcStack.vpc, databaseSecret: secretsStack.databaseSecret, databaseName: "rentall", multiAz: config.multiAz, ...envProps, description: `RDS PostgreSQL database (${environment})`, tags: { ...commonTags, Service: "database", }, }); // RDS depends on VPC and Secrets rdsStack.addDependency(vpcStack); rdsStack.addDependency(secretsStack); // ============================================================================ // ECS Service Stack // ============================================================================ const fullDomainName = config.subdomain ? `${config.subdomain}.${domainName}` : domainName; const ecsServiceStack = new EcsServiceStack( app, `EcsServiceStack-${environment}`, { environment, vpc: vpcStack.vpc, certificate: certificateStack.certificate, backendRepository: ecrStack.backendRepository, frontendRepository: ecrStack.frontendRepository, databaseSecret: secretsStack.databaseSecret, appSecret: secretsStack.appSecret, databaseSecurityGroup: rdsStack.databaseSecurityGroup, dbEndpoint: rdsStack.dbEndpoint, dbPort: rdsStack.dbPort, dbName: "rentall", domainName: fullDomainName, allowedIp: environment === "dev" ? allowedIp : undefined, // Only restrict in dev frontendUrl: config.frontendUrl, ...envProps, description: `ECS Fargate services with ALB (${environment})`, tags: { ...commonTags, Service: "ecs", }, } ); // ECS depends on VPC, Certificate, ECR, Secrets, and RDS ecsServiceStack.addDependency(vpcStack); ecsServiceStack.addDependency(certificateStack); ecsServiceStack.addDependency(ecrStack); ecsServiceStack.addDependency(secretsStack); ecsServiceStack.addDependency(rdsStack); // ============================================================================ // Lambda Stacks (existing) // ============================================================================ const conditionCheckStack = new ConditionCheckLambdaStack( app, `ConditionCheckLambdaStack-${environment}`, { environment, databaseUrl: config.databaseUrl, frontendUrl: config.frontendUrl, sesFromEmail: config.sesFromEmail, emailEnabled: config.emailEnabled, vpc: vpcStack.vpc, lambdaSecurityGroup: vpcStack.lambdaSecurityGroup, ...envProps, description: `Condition Check Reminder Lambda infrastructure (${environment})`, tags: { ...commonTags, Service: "condition-check-reminder", }, } ); conditionCheckStack.addDependency(vpcStack); const imageProcessorStack = new ImageProcessorLambdaStack( app, `ImageProcessorLambdaStack-${environment}`, { environment, databaseUrl: config.databaseUrl, frontendUrl: config.frontendUrl, vpc: vpcStack.vpc, lambdaSecurityGroup: vpcStack.lambdaSecurityGroup, ...envProps, description: `Image Processor Lambda infrastructure (${environment})`, tags: { ...commonTags, Service: "image-processor", }, } ); imageProcessorStack.addDependency(vpcStack);