diff --git a/README.md b/README.md index 268f96d..2a311dd 100644 --- a/README.md +++ b/README.md @@ -36,17 +36,18 @@ The [`master`](https://github.com/codequest-eu/terraform-single-page-app/tree/ma ## Inputs -| Name | Description | Type | Default | Required | -| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | :----: | :----------------: | :------: | -| bucket | Kebab-cased bucket name override | string | `""` | no | -| certificate\_arn | ACM certificate ARN to use instead of the default cloudfront certificate | string | `""` | no | -| cloudfront\_price\_class | CloudFront price class, which specifies where the distribution should be replicated, one of: PriceClass_100, PriceClass_200, PriceClass_All | string | `"PriceClass_100"` | no | -| domains | List of domains which will serve the application. If empty, will use the default cloudfront domain | list | `` | no | -| environment | Kebab-cased name of the environment, eg. production, staging, development, preview. Will be included in resource names | string | n/a | yes | -| project | Kebab-cased name of the project. Will be included in resource names | string | n/a | yes | -| static\_cors\_max\_age\_seconds | How long can CORS OPTIONS request responses be cached | string | `"3600"` | no | -| static\_path | Base path for static assets | string | `"/static"` | no | -| tags | Additional tags to add to each resource that supports them | map | `` | no | +| Name | Description | Type | Default | Required | +| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | :----: | :------------------: | :------: | +| bucket | Kebab-cased bucket name override | string | `""` | no | +| certificate\_arn | ACM certificate ARN to use instead of the default cloudfront certificate | string | `""` | no | +| cloudfront\_price\_class | CloudFront price class, which specifies where the distribution should be replicated, one of: PriceClass_100, PriceClass_200, PriceClass_All | string | `"PriceClass_100"` | no | +| domains | List of domains which will serve the application. If empty, will use the default cloudfront domain | list | `` | no | +| environment | Kebab-cased name of the environment, eg. production, staging, development, preview. Will be included in resource names | string | n/a | yes | +| project | Kebab-cased name of the project. Will be included in resource names | string | n/a | yes | +| pull\_request\_path\_re | Regular expression which extracts the base directory of a PR as it's first match group | string | `"^/(PR-\\d+)($|/)"` | no | +| static\_cors\_max\_age\_seconds | How long can CORS OPTIONS request responses be cached | string | `"3600"` | no | +| static\_path | Base path for static assets | string | `"/static"` | no | +| tags | Additional tags to add to each resource that supports them | map | `` | no | ## Outputs diff --git a/example/main.tf b/example/main.tf index ff26edd..e0a1ca3 100644 --- a/example/main.tf +++ b/example/main.tf @@ -26,7 +26,7 @@ module "basic" { resource "aws_s3_bucket_object" "basic_index" { bucket = "${module.basic.bucket_name}" - key = "index.html" + key = "PR-1/index.html" content = "

Hello world

" content_type = "text/html" cache_control = "no-cache no-store" diff --git a/main.tf b/main.tf index b58e828..4514327 100644 --- a/main.tf +++ b/main.tf @@ -1,3 +1,7 @@ +provider "aws" { + alias = "middleware" +} + resource "aws_s3_bucket" "assets" { bucket = "${var.bucket != "" ? var.bucket : "${local.name_prefix}-assets"}" acl = "private" @@ -94,13 +98,12 @@ resource "aws_cloudfront_distribution" "assets" { forward = "none" } } - } - custom_error_response { - error_code = 404 - error_caching_min_ttl = 0 - response_code = 200 - response_page_path = "/index.html" + lambda_function_association { + event_type = "origin-request" + lambda_arn = "${module.pull_request_router.arn}" + include_body = false + } } restrictions { @@ -118,3 +121,34 @@ resource "aws_cloudfront_distribution" "assets" { tags = "${local.tags}" } + +module "middleware_common" { + source = "./middleware_common" + + name_prefix = "${local.name_prefix}" + + providers = { + aws = "aws.middleware" + } +} + +data "template_file" "pull_request_router" { + template = "${file("${path.module}/templates/pull-request-router.js")}" + + vars { + path_re = "${var.pull_request_path_re}" + } +} + +module "pull_request_router" { + source = "./middleware" + + name = "${local.name_prefix}-pull-request-router" + code = "${data.template_file.pull_request_router.rendered}" + role_arn = "${module.middleware_common.role_arn}" + tags = "${local.tags}" + + providers = { + aws = "aws.middleware" + } +} diff --git a/templates/pull-request-router.js b/templates/pull-request-router.js new file mode 100644 index 0000000..9af2d0b --- /dev/null +++ b/templates/pull-request-router.js @@ -0,0 +1,19 @@ +const path = require("path") + +const r = String.raw +const pathRe = new RegExp(r`${path_re}`) + +exports.handler = (evt, ctx, cb) => { + const { request } = evt.Records[0].cf + console.log("<-", request.uri) + + const match = pathRe.exec(request.uri) + const extension = path.extname(request.uri) + + if (match && !extension) { + request.uri = `/$${match[1]}/index.html` + } + + console.log("->", request.uri) + cb(null, request) +} diff --git a/variables.tf b/variables.tf index 300a3fa..7207795 100644 --- a/variables.tf +++ b/variables.tf @@ -46,3 +46,8 @@ variable "bucket" { description = "Kebab-cased bucket name override" default = "" } + +variable "pull_request_path_re" { + description = "Regular expression which extracts the base directory of a PR as it's first match group" + default = "^/(PR-\\d+)($|/)" +}