mirror of
https://github.com/Sea-Haven-Industries/seahaven-ap.git
synced 2026-10-01 20:13:22 +00:00
229 lines
6 KiB
HCL
229 lines
6 KiB
HCL
resource "aws_ecr_repository" "api" {
|
|
name = local.project
|
|
image_tag_mutability = "MUTABLE"
|
|
force_delete = true
|
|
|
|
image_scanning_configuration {
|
|
scan_on_push = true
|
|
}
|
|
|
|
encryption_configuration {
|
|
encryption_type = "AES256"
|
|
}
|
|
}
|
|
|
|
resource "aws_ecr_lifecycle_policy" "api" {
|
|
repository = aws_ecr_repository.api.name
|
|
|
|
policy = jsonencode({
|
|
rules = [
|
|
{
|
|
rulePriority = 1
|
|
description = "Expire untagged images. SHA tags stay so registered task revisions can roll back."
|
|
selection = {
|
|
tagStatus = "untagged"
|
|
countType = "sinceImagePushed"
|
|
countUnit = "days"
|
|
countNumber = 14
|
|
}
|
|
action = {
|
|
type = "expire"
|
|
}
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
resource "aws_security_group" "alb" {
|
|
name = "${local.project}-alb"
|
|
description = "ALB for seahaven-ap (CloudFront origin only)"
|
|
vpc_id = local.vpc_id
|
|
|
|
ingress {
|
|
description = "HTTP from CloudFront origin-facing prefix list"
|
|
from_port = 80
|
|
to_port = 80
|
|
protocol = "tcp"
|
|
prefix_list_ids = [data.aws_ec2_managed_prefix_list.cloudfront_origin.id]
|
|
}
|
|
|
|
egress {
|
|
from_port = 0
|
|
to_port = 0
|
|
protocol = "-1"
|
|
cidr_blocks = ["0.0.0.0/0"]
|
|
}
|
|
}
|
|
|
|
resource "aws_security_group" "api" {
|
|
name = "${local.project}-api"
|
|
description = "Fargate tasks for seahaven-ap"
|
|
vpc_id = local.vpc_id
|
|
|
|
ingress {
|
|
description = "From ALB"
|
|
from_port = 8080
|
|
to_port = 8080
|
|
protocol = "tcp"
|
|
security_groups = [aws_security_group.alb.id]
|
|
}
|
|
|
|
egress {
|
|
from_port = 0
|
|
to_port = 0
|
|
protocol = "-1"
|
|
cidr_blocks = ["0.0.0.0/0"]
|
|
}
|
|
}
|
|
|
|
resource "aws_lb" "api" {
|
|
name = local.project
|
|
load_balancer_type = "application"
|
|
idle_timeout = 120
|
|
security_groups = [aws_security_group.alb.id]
|
|
subnets = local.public_subnet_ids
|
|
drop_invalid_header_fields = true
|
|
}
|
|
|
|
resource "aws_lb_target_group" "api" {
|
|
name = "${local.project}-api"
|
|
port = 8080
|
|
protocol = "HTTP"
|
|
vpc_id = local.vpc_id
|
|
target_type = "ip"
|
|
|
|
health_check {
|
|
enabled = true
|
|
path = "/api/health"
|
|
matcher = "200"
|
|
interval = 30
|
|
timeout = 5
|
|
healthy_threshold = 2
|
|
unhealthy_threshold = 3
|
|
}
|
|
}
|
|
|
|
resource "aws_lb_listener" "http" {
|
|
load_balancer_arn = aws_lb.api.arn
|
|
port = 80
|
|
protocol = "HTTP"
|
|
|
|
default_action {
|
|
type = "forward"
|
|
target_group_arn = aws_lb_target_group.api.arn
|
|
}
|
|
}
|
|
|
|
resource "aws_ecs_cluster" "api" {
|
|
name = local.project
|
|
|
|
setting {
|
|
name = "containerInsights"
|
|
value = "disabled"
|
|
}
|
|
}
|
|
|
|
locals {
|
|
api_container_name = "api"
|
|
bootstrap_command = [
|
|
"node",
|
|
"-e",
|
|
"require('http').createServer((q,s)=>{const p=(q.url||'').split('?')[0];const ok=q.method==='GET'&&p==='/api/health';const b=Buffer.from(ok?JSON.stringify({stage:'bootstrap',sha:'bootstrap'}):'');s.writeHead(ok?200:503,ok?{'content-type':'application/json','content-length':b.length}:{});s.end(b)}).listen(8080)",
|
|
]
|
|
|
|
database_url = "postgresql://seahaven:${urlencode(random_password.db.result)}@${aws_rds_cluster.api.endpoint}:5432/seahaven_ap"
|
|
|
|
api_environment_map = {
|
|
NODE_ENV = "production"
|
|
STAGE = var.environment
|
|
API_PORT = "8080"
|
|
DATABASE_DRIVER = "postgres"
|
|
DATABASE_URL = local.database_url
|
|
AWS_REGION = var.aws_region
|
|
COGNITO_ISSUER = local.cognito_issuer
|
|
COGNITO_AUDIENCE = local.cognito_client_id
|
|
COGNITO_DOMAIN = local.cognito_hosted_domain
|
|
APP_ORIGIN = local.app_origin
|
|
ORIGIN_VERIFY_SECRET = random_password.origin_verify.result
|
|
DOCUMENTS_BUCKET = aws_s3_bucket.documents.id
|
|
}
|
|
|
|
api_environment = concat(
|
|
[for name, value in local.api_environment_map : { name = name, value = value }],
|
|
[{ name = "GIT_SHA", value = "bootstrap" }],
|
|
)
|
|
}
|
|
|
|
resource "aws_ecs_task_definition" "api" {
|
|
family = local.project
|
|
requires_compatibilities = ["FARGATE"]
|
|
network_mode = "awsvpc"
|
|
cpu = "512"
|
|
memory = "1024"
|
|
execution_role_arn = aws_iam_role.ecs_execution.arn
|
|
task_role_arn = aws_iam_role.ecs_task.arn
|
|
|
|
runtime_platform {
|
|
operating_system_family = "LINUX"
|
|
cpu_architecture = "X86_64"
|
|
}
|
|
|
|
container_definitions = jsonencode([
|
|
{
|
|
name = local.api_container_name
|
|
image = "public.ecr.aws/docker/library/node:24-alpine"
|
|
essential = true
|
|
command = local.bootstrap_command
|
|
portMappings = [
|
|
{
|
|
containerPort = 8080
|
|
protocol = "tcp"
|
|
}
|
|
]
|
|
environment = local.api_environment
|
|
stopTimeout = 60
|
|
logConfiguration = {
|
|
logDriver = "awslogs"
|
|
options = {
|
|
"awslogs-group" = aws_cloudwatch_log_group.api.name
|
|
"awslogs-region" = var.aws_region
|
|
"awslogs-stream-prefix" = "ecs"
|
|
}
|
|
}
|
|
}
|
|
])
|
|
|
|
lifecycle {
|
|
ignore_changes = [container_definitions]
|
|
}
|
|
}
|
|
|
|
resource "aws_ecs_service" "api" {
|
|
name = local.project
|
|
cluster = aws_ecs_cluster.api.id
|
|
task_definition = aws_ecs_task_definition.api.arn
|
|
desired_count = 1
|
|
launch_type = "FARGATE"
|
|
|
|
network_configuration {
|
|
subnets = local.public_subnet_ids
|
|
security_groups = [aws_security_group.api.id]
|
|
assign_public_ip = true
|
|
}
|
|
|
|
load_balancer {
|
|
target_group_arn = aws_lb_target_group.api.arn
|
|
container_name = local.api_container_name
|
|
container_port = 8080
|
|
}
|
|
|
|
health_check_grace_period_seconds = 60
|
|
deployment_minimum_healthy_percent = 0
|
|
deployment_maximum_percent = 200
|
|
|
|
lifecycle {
|
|
ignore_changes = [task_definition, desired_count]
|
|
}
|
|
|
|
depends_on = [aws_lb_listener.http]
|
|
}
|