- Create CloudFront Function to rewrite directory requests to index.html - Fix admin login page routing issue (/upadaj -> /upadaj/index.html) - Attach function to CloudFront distribution default cache behavior - Enables proper routing for all admin pages without .html extension
199 lines
5.7 KiB
HCL
199 lines
5.7 KiB
HCL
# S3 bucket for static website hosting
|
|
resource "aws_s3_bucket" "frontend" {
|
|
bucket = "${var.app_name}-frontend"
|
|
|
|
tags = {
|
|
Name = "${var.app_name}-frontend"
|
|
Environment = var.environment
|
|
}
|
|
}
|
|
|
|
# S3 bucket website configuration
|
|
resource "aws_s3_bucket_website_configuration" "frontend" {
|
|
bucket = aws_s3_bucket.frontend.id
|
|
|
|
index_document {
|
|
suffix = "index.html"
|
|
}
|
|
|
|
error_document {
|
|
key = "404.html"
|
|
}
|
|
}
|
|
|
|
# S3 bucket public access block (we'll use CloudFront OAC instead)
|
|
resource "aws_s3_bucket_public_access_block" "frontend" {
|
|
bucket = aws_s3_bucket.frontend.id
|
|
|
|
block_public_acls = true
|
|
block_public_policy = true
|
|
ignore_public_acls = true
|
|
restrict_public_buckets = true
|
|
}
|
|
|
|
# CloudFront Origin Access Control
|
|
resource "aws_cloudfront_origin_access_control" "frontend" {
|
|
name = "${var.app_name}-frontend-oac"
|
|
description = "OAC for ${var.app_name} frontend"
|
|
origin_access_control_origin_type = "s3"
|
|
signing_behavior = "always"
|
|
signing_protocol = "sigv4"
|
|
}
|
|
|
|
# CloudFront Function for directory index rewrites
|
|
resource "aws_cloudfront_function" "index_rewrite" {
|
|
name = "${var.app_name}-index-rewrite"
|
|
runtime = "cloudfront-js-1.0"
|
|
comment = "Rewrite directory requests to index.html"
|
|
publish = true
|
|
code = file("${path.module}/cloudfront-function.js")
|
|
}
|
|
|
|
# CloudFront distribution
|
|
resource "aws_cloudfront_distribution" "frontend" {
|
|
enabled = true
|
|
is_ipv6_enabled = true
|
|
comment = "${var.app_name} frontend"
|
|
default_root_object = "index.html"
|
|
price_class = "PriceClass_100" # US, Canada, Europe only (cheapest)
|
|
|
|
# No aliases - Cloudflare will proxy to CloudFront's default domain
|
|
# aliases = var.domain_name != "" ? [var.domain_name, "www.${var.domain_name}"] : []
|
|
|
|
origin {
|
|
domain_name = aws_s3_bucket.frontend.bucket_regional_domain_name
|
|
origin_id = "S3-${aws_s3_bucket.frontend.id}"
|
|
origin_access_control_id = aws_cloudfront_origin_access_control.frontend.id
|
|
}
|
|
|
|
default_cache_behavior {
|
|
allowed_methods = ["GET", "HEAD", "OPTIONS"]
|
|
cached_methods = ["GET", "HEAD"]
|
|
target_origin_id = "S3-${aws_s3_bucket.frontend.id}"
|
|
|
|
forwarded_values {
|
|
query_string = false
|
|
cookies {
|
|
forward = "none"
|
|
}
|
|
}
|
|
|
|
viewer_protocol_policy = "redirect-to-https"
|
|
min_ttl = 0
|
|
default_ttl = 3600 # 1 hour
|
|
max_ttl = 86400 # 24 hours
|
|
compress = true
|
|
|
|
function_association {
|
|
event_type = "viewer-request"
|
|
function_arn = aws_cloudfront_function.index_rewrite.arn
|
|
}
|
|
}
|
|
|
|
# Custom error responses for SPA routing
|
|
custom_error_response {
|
|
error_code = 404
|
|
response_code = 200
|
|
response_page_path = "/index.html"
|
|
error_caching_min_ttl = 300
|
|
}
|
|
|
|
custom_error_response {
|
|
error_code = 403
|
|
response_code = 200
|
|
response_page_path = "/index.html"
|
|
error_caching_min_ttl = 300
|
|
}
|
|
|
|
restrictions {
|
|
geo_restriction {
|
|
restriction_type = "none"
|
|
}
|
|
}
|
|
|
|
# Use default CloudFront certificate (we'll handle SSL via Cloudflare)
|
|
viewer_certificate {
|
|
cloudfront_default_certificate = true
|
|
# If you want CloudFront SSL, add ACM certificate here
|
|
# acm_certificate_arn = aws_acm_certificate.cert.arn
|
|
# ssl_support_method = "sni-only"
|
|
# minimum_protocol_version = "TLSv1.2_2021"
|
|
}
|
|
|
|
tags = {
|
|
Name = "${var.app_name}-frontend"
|
|
Environment = var.environment
|
|
}
|
|
}
|
|
|
|
# S3 bucket policy to allow CloudFront OAC access
|
|
resource "aws_s3_bucket_policy" "frontend" {
|
|
bucket = aws_s3_bucket.frontend.id
|
|
|
|
policy = jsonencode({
|
|
Version = "2012-10-17"
|
|
Statement = [
|
|
{
|
|
Sid = "AllowCloudFrontServicePrincipal"
|
|
Effect = "Allow"
|
|
Principal = {
|
|
Service = "cloudfront.amazonaws.com"
|
|
}
|
|
Action = "s3:GetObject"
|
|
Resource = "${aws_s3_bucket.frontend.arn}/*"
|
|
Condition = {
|
|
StringEquals = {
|
|
"AWS:SourceArn" = aws_cloudfront_distribution.frontend.arn
|
|
}
|
|
}
|
|
}
|
|
]
|
|
})
|
|
}
|
|
|
|
# Cloudflare DNS records for frontend
|
|
resource "cloudflare_record" "frontend_root" {
|
|
count = var.domain_name != "" && var.cloudflare_api_token != "" ? 1 : 0
|
|
zone_id = data.cloudflare_zone.domain[0].id
|
|
name = "@"
|
|
type = "CNAME"
|
|
value = aws_cloudfront_distribution.frontend.domain_name
|
|
ttl = 1
|
|
proxied = true # Enable Cloudflare proxy for SSL and caching
|
|
comment = "CloudFront distribution for frontend"
|
|
}
|
|
|
|
resource "cloudflare_record" "frontend_www" {
|
|
count = var.domain_name != "" && var.cloudflare_api_token != "" ? 1 : 0
|
|
zone_id = data.cloudflare_zone.domain[0].id
|
|
name = "www"
|
|
type = "CNAME"
|
|
value = aws_cloudfront_distribution.frontend.domain_name
|
|
ttl = 1
|
|
proxied = true # Enable Cloudflare proxy for SSL and caching
|
|
comment = "CloudFront distribution for frontend (www)"
|
|
}
|
|
|
|
# Cloudflare Transform Rule to rewrite Host header for CloudFront
|
|
resource "cloudflare_ruleset" "frontend_host_header_rewrite" {
|
|
count = var.domain_name != "" && var.cloudflare_api_token != "" ? 1 : 0
|
|
zone_id = data.cloudflare_zone.domain[0].id
|
|
name = "Rewrite Host header for CloudFront"
|
|
kind = "zone"
|
|
phase = "http_request_late_transform"
|
|
|
|
rules {
|
|
action = "rewrite"
|
|
expression = "(http.host eq \"${var.domain_name}\" or http.host eq \"www.${var.domain_name}\")"
|
|
description = "Rewrite Host header to CloudFront domain"
|
|
|
|
action_parameters {
|
|
headers {
|
|
name = "Host"
|
|
operation = "set"
|
|
value = aws_cloudfront_distribution.frontend.domain_name
|
|
}
|
|
}
|
|
}
|
|
}
|