Files
rentall-app/infrastructure/cdk/bin/app.ts
2026-01-21 14:18:07 -05:00

255 lines
7.6 KiB
JavaScript

#!/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);