Skip to content

Commit

Permalink
switch to alb; bring back frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
BEW111 committed Aug 18, 2024
1 parent b50d815 commit 138d24c
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 49 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ The boilerplate uses [SendGrid](https://sendgrid.com) to send emails to users in

- Create a SendGrid Account
- Register a [Sender Identity](https://docs.sendgrid.com/for-developers/sending-email/sender-identity) (Single Sender recommended for most)
- Create an [API Key](https://docs.sendgrid.com/ui/account-and-settings/api-keys#creating-an-api-key
- Create an [API Key](https://docs.sendgrid.com/ui/account-and-settings/api-keys#creating-an-api-key)

### Environment Variables

Expand Down
11 changes: 10 additions & 1 deletion client/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@ module.exports = {
'@typescript-eslint/no-unused-vars': ['warn'],
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
'prettier/prettier': ['error', { endOfLine: 'auto' }],
'import/extensions': [{ tsx: 'always' }, { ts: 'always' }],
'import/extensions': [
2,
'always',
{
js: 'never',
jsx: 'never',
ts: 'always',
tsx: 'always',
},
],
},
env: {
browser: true,
Expand Down
11 changes: 8 additions & 3 deletions client/src/util/api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,16 @@ async function resolve(promise: Promise<any>) {
/**
* To UPDATE DURING DEPLOYMENT USING ENVIRONMENT VARIABLES
*/
const BACKENDURL = process.env.PUBLIC_URL
? process.env.PUBLIC_URL
const BACKEND_URL = process.env.BACKEND_URL
? process.env.BACKEND_URL
: 'http://localhost:4000';

const URLPREFIX = `${BACKENDURL}/api`;
console.log(process.env.BACKEND_URL);
console.log(BACKEND_URL);

// const BACKEND_URL = 'api.hackboilerplate.com';

const URLPREFIX = `${BACKEND_URL}/api`;

/**
* A function which makes a GET request to the server when given a url and returns the response data after it is resolved by the {@link resolve} function.
Expand Down
2 changes: 1 addition & 1 deletion client/src/util/redux/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';

import { AppDispatch, RootState } from './store';
import { AppDispatch, RootState } from './store.ts';

/**
* A hook that returns the redux store's dispatch function. This is used to dispatch actions to the redux store. This is a typed version of the `useDispatch` hook from react-redux.
Expand Down
Empty file added infrastructure/client/main.tf
Empty file.
240 changes: 198 additions & 42 deletions infrastructure/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ resource "aws_ecs_cluster" "cluster" {
}
}

# TODO: this only needs to be created once; should we keep it in here or create
# it manually?
# resource "aws_cloudwatch_log_group" "ecs_app_family_log_group" {
# name = "/ecs/app-family"
# // retention_in_days = 90
Expand All @@ -34,10 +36,39 @@ resource "aws_ecs_task_definition" "app" {
family = "app-family"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "256"
memory = "1024"
cpu = "512"
memory = "2048"
execution_role_arn = data.aws_iam_role.ecs_task_execution_role.arn
container_definitions = jsonencode([
{
name = "frontend"
image = local.client_image_tag
cpu = 256
memory = 1024
essential = true
portMappings = [
{
containerPort = 3000
hostPort = 3000
protocol = "tcp"
}
],
environment = [
{
name = "REACT_APP_API_URL"
value = "http://${aws_lb.app.dns_name}/api"
}
],
// TODO: add env vars
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = "/ecs/app-family"
awslogs-region = var.region
awslogs-stream-prefix = "frontend"
}
}
},
{
name = "backend"
image = local.server_image_tag
Expand Down Expand Up @@ -70,78 +101,189 @@ resource "aws_ecs_task_definition" "app" {
}

// VPC configuration
// TODO: decide if we should use a different VPC
data "aws_vpc" "default" {
default = true
}

data "aws_security_group" "default" {
vpc_id = data.aws_vpc.default.id
name = "default"
}

data "aws_subnets" "default" {
filter {
name = "vpc-id"
values = [data.aws_vpc.default.id]
}
}

// Elastic IPs for NLB
resource "aws_eip" "nlb_eip_1" {
vpc = true
}
resource "aws_acm_certificate" "cert" {
domain_name = "hackboilerplate.com"
validation_method = "DNS"

resource "aws_eip" "nlb_eip_2" {
vpc = true
lifecycle {
create_before_destroy = true
}
}

// Load balancer configuration
resource "aws_lb" "nlb" {
name = "${var.cluster_name}-nlb"
resource "aws_lb" "app" {
name = "${var.cluster_name}-alb"
internal = false
load_balancer_type = "network"
load_balancer_type = "application"
security_groups = [data.aws_security_group.default.id]
subnets = data.aws_subnets.default.ids

subnet_mapping {
subnet_id = data.aws_subnets.default.ids[0]
allocation_id = aws_eip.nlb_eip_1.id
}
enable_deletion_protection = false
}

subnet_mapping {
subnet_id = data.aws_subnets.default.ids[1]
allocation_id = aws_eip.nlb_eip_2.id
}
resource "aws_lb_target_group" "frontend_tg" {
name = "${var.cluster_name}-frontend-tg"
port = 3000
protocol = "HTTP"
vpc_id = data.aws_vpc.default.id
target_type = "ip"

enable_deletion_protection = false
health_check {
path = "/"
healthy_threshold = 2
unhealthy_threshold = 10
timeout = 60
interval = 300
matcher = "200,301,302"
}
}

resource "aws_lb_target_group" "app_tg" {
name = "${var.cluster_name}-tg"
resource "aws_lb_target_group" "backend_tg" {
name = "${var.cluster_name}-backend-tg"
port = 4000
protocol = "TCP"
protocol = "HTTP"
vpc_id = data.aws_vpc.default.id
target_type = "ip"

health_check {
interval = 30
protocol = "TCP"
healthy_threshold = 3
unhealthy_threshold = 3
timeout = 10
path = "/" # TODO: add a health check endpoint
healthy_threshold = 2
unhealthy_threshold = 10
timeout = 60
interval = 300
matcher = "200"
}
}

# resource "aws_lb_listener" "app_listener_https" {
# load_balancer_arn = aws_lb.app.arn
# port = "443"
# protocol = "HTTPS"
# ssl_policy = "ELBSecurityPolicy-2016-08"
# certificate_arn = aws_acm_certificate.cert.arn

# default_action {
# type = "fixed-response"
# fixed_response {
# content_type = "text/plain"
# message_body = "Not Found"
# status_code = "404"
# }
# }
# }


resource "aws_lb_listener" "app_listener" {
load_balancer_arn = aws_lb.nlb.arn
port = 80
protocol = "TCP"
load_balancer_arn = aws_lb.app.arn
port = "80"
protocol = "HTTP"

# default_action {
# type = "redirect"
# redirect {
# port = "443"
# protocol = "HTTPS"
# status_code = "HTTP_301"
# }
# }

default_action {
type = "fixed-response"
fixed_response {
content_type = "text/plain"
message_body = "Not Found"
status_code = "404"
}
}
}

resource "aws_lb_listener_rule" "frontend" {
listener_arn = aws_lb_listener.app_listener.arn
priority = 100

action {
type = "forward"
target_group_arn = aws_lb_target_group.app_tg.arn
target_group_arn = aws_lb_target_group.frontend_tg.arn
}

condition {
host_header {
values = ["hackboilerplate.com"]
}
}
}

resource "aws_lb_listener_rule" "backend" {
listener_arn = aws_lb_listener.app_listener.arn
priority = 200

action {
type = "forward"
target_group_arn = aws_lb_target_group.backend_tg.arn
}

condition {
path_pattern {
values = ["/api/*"]
}
}
}

# resource "aws_lb_listener_rule" "frontend_https" {
# listener_arn = aws_lb_listener.app_listener_https.arn
# priority = 100

# action {
# type = "forward"
# target_group_arn = aws_lb_target_group.frontend_tg.arn
# }

# condition {
# host_header {
# values = ["hackboilerplate.com"]
# }
# }
# }

# resource "aws_lb_listener_rule" "backend_https" {
# listener_arn = aws_lb_listener.app_listener_https.arn
# priority = 200

# action {
# type = "forward"
# target_group_arn = aws_lb_target_group.backend_tg.arn
# }

# condition {
# path_pattern {
# values = ["/api/*"]
# }
# }
# }

// ECS service
resource "aws_ecs_service" "app_service" {
name = "app-service"
cluster = aws_ecs_cluster.cluster.id
task_definition = aws_ecs_task_definition.app.arn
desired_count = 1 # Example count
desired_count = 1
launch_type = "FARGATE"

network_configuration {
Expand All @@ -150,7 +292,13 @@ resource "aws_ecs_service" "app_service" {
}

load_balancer {
target_group_arn = aws_lb_target_group.app_tg.arn
target_group_arn = aws_lb_target_group.frontend_tg.arn
container_name = "frontend"
container_port = 3000
}

load_balancer {
target_group_arn = aws_lb_target_group.backend_tg.arn
container_name = "backend"
container_port = 4000
}
Expand All @@ -176,21 +324,29 @@ resource "aws_iam_role_policy_attachment" "cloudwatch_logs_policy_attachment" {
policy_arn = data.aws_iam_policy.cloudwatch_logs_policy.arn
}

// NLB IP routing
// ALB IP routing
resource "aws_route53_zone" "main" {
name = var.hosted_zone_name
}

resource "aws_route53_record" "backend" {
# resource "aws_route53_record" "backend" {
# zone_id = aws_route53_zone.main.zone_id
# name = "api.${var.hosted_zone_name}"
# type = "A"
# ttl = "300"
# records = [aws_eip.nlb_eip_1.public_ip, aws_eip.nlb_eip_2.public_ip]
# }

resource "aws_route53_record" "main" {
zone_id = aws_route53_zone.main.zone_id
name = "api.${var.hosted_zone_name}"
name = "hackboilerplate.com"
type = "A"
ttl = "300"
records = [aws_eip.nlb_eip_1.public_ip, aws_eip.nlb_eip_2.public_ip]
}

data "aws_route53_zone" "main" {
name = var.hosted_zone_name
alias {
name = aws_lb.app.dns_name
zone_id = aws_lb.app.zone_id
evaluate_target_health = true
}
}

# So that the ECS role can execute tasks
Expand Down
Loading

0 comments on commit 138d24c

Please sign in to comment.