From 8d05943dee820f6a93b3675649cc6b6569157d15 Mon Sep 17 00:00:00 2001 From: Logan Date: Thu, 26 Mar 2026 12:24:31 -0500 Subject: [PATCH 01/17] setup docker compose file for prod deployment --- deployment/.env | 3 ++ deployment/.terraform.lock.hcl | 25 ------------- deployment/default_container.env | 9 +++++ deployment/docker-compose.yml | 36 +++++++++++++++++++ deployment/{ => infrastructure}/main.tf | 0 .../modules/finus/main.tf | 0 deployment/{ => infrastructure}/terraform.tf | 0 deployment/secrets.env | 5 +++ 8 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 deployment/.env delete mode 100644 deployment/.terraform.lock.hcl create mode 100644 deployment/default_container.env create mode 100644 deployment/docker-compose.yml rename deployment/{ => infrastructure}/main.tf (100%) rename deployment/{ => infrastructure}/modules/finus/main.tf (100%) rename deployment/{ => infrastructure}/terraform.tf (100%) create mode 100644 deployment/secrets.env diff --git a/deployment/.env b/deployment/.env new file mode 100644 index 0000000..c3ae092 --- /dev/null +++ b/deployment/.env @@ -0,0 +1,3 @@ +#THIS ENV FILE IS NOT GIT IGNORED +DOCKER_HUB_USERNAME=measureonecodetwice +RELEASE_VERSION=latest diff --git a/deployment/.terraform.lock.hcl b/deployment/.terraform.lock.hcl deleted file mode 100644 index 3bfa040..0000000 --- a/deployment/.terraform.lock.hcl +++ /dev/null @@ -1,25 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/hashicorp/aws" { - version = "6.37.0" - constraints = "~> 6.37.0" - hashes = [ - "h1:w3z/TApcKD3b/aMZoZZKSxOld4xw+gEtQ1ka6C1UN+4=", - "zh:0427fadb719ed5a32feb09f047539d2348e659056f3b8a8589d34d8f0a95be7a", - "zh:3891c670674aba2125a7ac6d4348cde43646b1b46ce6f829e6f4724091bc0dcd", - "zh:632cb24b7b5790b730b33bcbe9f1a7b75f2644fb52f9d6aaafb0249c9e7601d2", - "zh:6e96ed1f824c2efa9de5b7c22ab3715624ba34c28564a06e9a15e71bc3d3a30b", - "zh:7b8fd86907b659bc45f4a3f42c3c0ccc66925a74e265b01e9e66242c0b2cafef", - "zh:81f9a587deddef4dfcc2101c54ec28a3a554056837f68ebb920c83fe8327b16f", - "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", - "zh:a9a38a67cb98d690fec951ec3e133b6836279629db2ed3a0ebf97a5bea58674f", - "zh:b18f60d62e4bd4d466077e09c39259d1a85355f0f00b801fe8aedbc50193d357", - "zh:b7a51bc0faf60d17043b4df1d1b7bb55129eaa4bdeb65ff55f5b00b9b8fee9f7", - "zh:c28c42f91ca3a6b65b3fd3ed6e891fc0fc28d0cb5ab65dea65eda8eec5cea5f3", - "zh:d895ddc04280ed26b6ca64ca05b78caaa7b72c8e167af4093545efbc608d5482", - "zh:f4a56f5157009ef160fbd79105078fe675df479cb73c1b7e1fea2741403a0b67", - "zh:f547d6ca371b96fec97b972fc0c93bcfc23d58e34a9da215b94e9d2aa170fb77", - "zh:f7b0a3cd4adadd3f4b9609a54e651ed5eafa22c196ab229042fc1d0aa0ab8f3a", - ] -} diff --git a/deployment/default_container.env b/deployment/default_container.env new file mode 100644 index 0000000..6800694 --- /dev/null +++ b/deployment/default_container.env @@ -0,0 +1,9 @@ +API_GATEWAY_ADDR=http://api-gateway:8000 +AUTH_SERVICE_ADDR=http://auth:8000 +USER_SERVICE_ADDR=http://user:8000 +ANALYTICS_SERVICE_ADDR=http://analytics:8000 +MARKET_SERVICE_ADDR=http://market:8000 + +MYSQL_HOST=database +MYSQL_PORT=3306 +DB_NAME=finus diff --git a/deployment/docker-compose.yml b/deployment/docker-compose.yml new file mode 100644 index 0000000..bb14be4 --- /dev/null +++ b/deployment/docker-compose.yml @@ -0,0 +1,36 @@ +services: + api-gateway: + image: $DOCKER_HUB_USERNAME/finus-api-gateway:$RELEASE_VERSION + env_file: + - path: ./default_container.env + ports: + - "3000:8000" + depends_on: + - auth + - user + - analytics + - market + + auth: + image: $DOCKER_HUB_USERNAME/finus-auth:$RELEASE_VERSION + env_file: + - path: ./default_container.env + - path: ./secrets.env + + + user: + image: $DOCKER_HUB_USERNAME/finus-user:$RELEASE_VERSION + env_file: + - path: ./default_container.env + - path: ./secrets.env + + analytics: + image: $DOCKER_HUB_USERNAME/finus-analytics:$RELEASE_VERSION + env_file: + - path: ./default_container.env + - path: ./secrets.env + + market: + image: $DOCKER_HUB_USERNAME/finus-market:$RELEASE_VERSION + env_file: + - path: ./default_container.env diff --git a/deployment/main.tf b/deployment/infrastructure/main.tf similarity index 100% rename from deployment/main.tf rename to deployment/infrastructure/main.tf diff --git a/deployment/modules/finus/main.tf b/deployment/infrastructure/modules/finus/main.tf similarity index 100% rename from deployment/modules/finus/main.tf rename to deployment/infrastructure/modules/finus/main.tf diff --git a/deployment/terraform.tf b/deployment/infrastructure/terraform.tf similarity index 100% rename from deployment/terraform.tf rename to deployment/infrastructure/terraform.tf diff --git a/deployment/secrets.env b/deployment/secrets.env new file mode 100644 index 0000000..f083420 --- /dev/null +++ b/deployment/secrets.env @@ -0,0 +1,5 @@ +#THIS ENV FILE IS NOT GIT IGNORED. +#REPLACE IT WITH THE REAL SECRETS FILE IN PROD +MYSQL_USER=dummyuser +MYSQL_PASSWORD=dummypassword +JWT_SECRET=dummysecret From 908dbc1b6afb014fb8143090e8d01c5ee89c72ee Mon Sep 17 00:00:00 2001 From: Logan Date: Thu, 26 Mar 2026 12:39:56 -0500 Subject: [PATCH 02/17] add deployment compose file for webserver + re-arranged + workflow tags images with latest created script for EC2 env setup & webhook --- .github/workflows/docker_compose_publish.yml | 8 + deployment/{ => infrastructure}/.gitignore | 0 deployment/infrastructure/.terraform.lock.hcl | 25 + deployment/infrastructure/main.tf | 18 +- deployment/infrastructure/terraform.tfstate | 1376 +++++++++++++++++ .../infrastructure/terraform.tfstate.backup | 1363 ++++++++++++++++ deployment/scripts/install-deps.sh | 30 + deployment/scripts/start.sh | 3 + deployment/scripts/upgrade-images.sh | 3 + deployment/scripts/upgrade-on-new-image.js | 60 + deployment/{ => services}/.env | 0 .../{ => services}/default_container.env | 0 deployment/{ => services}/docker-compose.yml | 0 deployment/{ => services}/secrets.env | 0 deployment/webserver/.env | 3 + deployment/webserver/docker-compose.yml | 5 + 16 files changed, 2890 insertions(+), 4 deletions(-) rename deployment/{ => infrastructure}/.gitignore (100%) create mode 100644 deployment/infrastructure/.terraform.lock.hcl create mode 100644 deployment/infrastructure/terraform.tfstate create mode 100644 deployment/infrastructure/terraform.tfstate.backup create mode 100755 deployment/scripts/install-deps.sh create mode 100755 deployment/scripts/start.sh create mode 100644 deployment/scripts/upgrade-images.sh create mode 100644 deployment/scripts/upgrade-on-new-image.js rename deployment/{ => services}/.env (100%) rename deployment/{ => services}/default_container.env (100%) rename deployment/{ => services}/docker-compose.yml (100%) rename deployment/{ => services}/secrets.env (100%) create mode 100644 deployment/webserver/.env create mode 100644 deployment/webserver/docker-compose.yml diff --git a/.github/workflows/docker_compose_publish.yml b/.github/workflows/docker_compose_publish.yml index 7e43184..c2193a5 100644 --- a/.github/workflows/docker_compose_publish.yml +++ b/.github/workflows/docker_compose_publish.yml @@ -36,6 +36,14 @@ jobs: docker compose build echo "Pushing images..." docker compose push + + echo RELEASE_VERSION=latest > .env + echo DOCKER_HUB_USERNAME=$DOCKER_HUB_USERNAME >> .env + echo "Re-building with lastest tag..." + docker compose build + echo "Pushing images..." + docker compose push + env: DOCKER_HUB_USERNAME: ${{ inputs.DOCKER_HUB_USERNAME }} diff --git a/deployment/.gitignore b/deployment/infrastructure/.gitignore similarity index 100% rename from deployment/.gitignore rename to deployment/infrastructure/.gitignore diff --git a/deployment/infrastructure/.terraform.lock.hcl b/deployment/infrastructure/.terraform.lock.hcl new file mode 100644 index 0000000..3bfa040 --- /dev/null +++ b/deployment/infrastructure/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "6.37.0" + constraints = "~> 6.37.0" + hashes = [ + "h1:w3z/TApcKD3b/aMZoZZKSxOld4xw+gEtQ1ka6C1UN+4=", + "zh:0427fadb719ed5a32feb09f047539d2348e659056f3b8a8589d34d8f0a95be7a", + "zh:3891c670674aba2125a7ac6d4348cde43646b1b46ce6f829e6f4724091bc0dcd", + "zh:632cb24b7b5790b730b33bcbe9f1a7b75f2644fb52f9d6aaafb0249c9e7601d2", + "zh:6e96ed1f824c2efa9de5b7c22ab3715624ba34c28564a06e9a15e71bc3d3a30b", + "zh:7b8fd86907b659bc45f4a3f42c3c0ccc66925a74e265b01e9e66242c0b2cafef", + "zh:81f9a587deddef4dfcc2101c54ec28a3a554056837f68ebb920c83fe8327b16f", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:a9a38a67cb98d690fec951ec3e133b6836279629db2ed3a0ebf97a5bea58674f", + "zh:b18f60d62e4bd4d466077e09c39259d1a85355f0f00b801fe8aedbc50193d357", + "zh:b7a51bc0faf60d17043b4df1d1b7bb55129eaa4bdeb65ff55f5b00b9b8fee9f7", + "zh:c28c42f91ca3a6b65b3fd3ed6e891fc0fc28d0cb5ab65dea65eda8eec5cea5f3", + "zh:d895ddc04280ed26b6ca64ca05b78caaa7b72c8e167af4093545efbc608d5482", + "zh:f4a56f5157009ef160fbd79105078fe675df479cb73c1b7e1fea2741403a0b67", + "zh:f547d6ca371b96fec97b972fc0c93bcfc23d58e34a9da215b94e9d2aa170fb77", + "zh:f7b0a3cd4adadd3f4b9609a54e651ed5eafa22c196ab229042fc1d0aa0ab8f3a", + ] +} diff --git a/deployment/infrastructure/main.tf b/deployment/infrastructure/main.tf index 88f8ce6..b4b96b1 100644 --- a/deployment/infrastructure/main.tf +++ b/deployment/infrastructure/main.tf @@ -36,12 +36,22 @@ resource "aws_security_group" "default" { protocol = "tcp" } - egress { - description = "HTTP from anywhere" - from_port = "3000" - to_port = "3000" + ingress { + description = "Docker webhook" + cidr_blocks = ["0.0.0.0/0"] + from_port = "4444" + to_port = "4444" protocol = "tcp" } + + egress { + description = "All outbound traffic" + from_port = "0" + to_port = "0" + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + ipv6_cidr_blocks = ["::/0"] + } } resource "aws_route_table" "route_table" { diff --git a/deployment/infrastructure/terraform.tfstate b/deployment/infrastructure/terraform.tfstate new file mode 100644 index 0000000..ef3d053 --- /dev/null +++ b/deployment/infrastructure/terraform.tfstate @@ -0,0 +1,1376 @@ +{ + "version": 4, + "terraform_version": "1.14.7", + "serial": 112, + "lineage": "ae3e9505-c9b9-9084-580e-4ba9a7f0ba2f", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "aws_internet_gateway", + "name": "default", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:internet-gateway/igw-0a74c1fef454437c8", + "id": "igw-0a74c1fef454437c8", + "owner_id": "177559564758", + "region": "us-east-2", + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxMjAwMDAwMDAwMDAwLCJkZWxldGUiOjEyMDAwMDAwMDAwMDAsInVwZGF0ZSI6MTIwMDAwMDAwMDAwMH19", + "dependencies": [ + "aws_vpc.finus" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_route_table", + "name": "route_table", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:route-table/rtb-0f24beedbed7e02a3", + "id": "rtb-0f24beedbed7e02a3", + "owner_id": "177559564758", + "propagating_vgws": [], + "region": "us-east-2", + "route": [ + { + "carrier_gateway_id": "", + "cidr_block": "0.0.0.0/0", + "core_network_arn": "", + "destination_prefix_list_id": "", + "egress_only_gateway_id": "", + "gateway_id": "igw-0a74c1fef454437c8", + "ipv6_cidr_block": "", + "local_gateway_id": "", + "nat_gateway_id": "", + "network_interface_id": "", + "transit_gateway_id": "", + "vpc_endpoint_id": "", + "vpc_peering_connection_id": "" + } + ], + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "rtb-0f24beedbed7e02a3", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjEyMDAwMDAwMDAwMH19", + "dependencies": [ + "aws_internet_gateway.default", + "aws_vpc.finus" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_security_group", + "name": "default", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:security-group/sg-0c03eb59bd6270fcf", + "description": "Managed by Terraform", + "egress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "All outbound traffic", + "from_port": 0, + "ipv6_cidr_blocks": [ + "::/0" + ], + "prefix_list_ids": [], + "protocol": "-1", + "security_groups": [], + "self": false, + "to_port": 0 + } + ], + "id": "sg-0c03eb59bd6270fcf", + "ingress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "Docker webhook", + "from_port": 4444, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "tcp", + "security_groups": [], + "self": false, + "to_port": 4444 + }, + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "SSH from anywhere", + "from_port": 22, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "tcp", + "security_groups": [], + "self": false, + "to_port": 22 + } + ], + "name": "finus-default", + "name_prefix": "", + "owner_id": "177559564758", + "region": "us-east-2", + "revoke_rules_on_delete": false, + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "sg-0c03eb59bd6270fcf", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6OTAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIxIn0=", + "dependencies": [ + "aws_vpc.finus" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_subnet", + "name": "dev", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:subnet/subnet-09d5718b3434ec2be", + "assign_ipv6_address_on_creation": false, + "availability_zone": "us-east-2c", + "availability_zone_id": "use2-az3", + "cidr_block": "10.0.128.0/17", + "customer_owned_ipv4_pool": "", + "enable_dns64": false, + "enable_lni_at_device_index": 0, + "enable_resource_name_dns_a_record_on_launch": false, + "enable_resource_name_dns_aaaa_record_on_launch": false, + "id": "subnet-09d5718b3434ec2be", + "ipv4_ipam_pool_id": null, + "ipv4_netmask_length": null, + "ipv6_cidr_block": "", + "ipv6_cidr_block_association_id": "", + "ipv6_ipam_pool_id": null, + "ipv6_native": false, + "ipv6_netmask_length": null, + "map_customer_owned_ip_on_launch": false, + "map_public_ip_on_launch": false, + "outpost_arn": "", + "owner_id": "177559564758", + "private_dns_hostname_type_on_launch": "ip-name", + "region": "us-east-2", + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "subnet-09d5718b3434ec2be", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9", + "dependencies": [ + "aws_vpc.finus" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_subnet", + "name": "prod", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:subnet/subnet-05e5676ab26e11acc", + "assign_ipv6_address_on_creation": false, + "availability_zone": "us-east-2c", + "availability_zone_id": "use2-az3", + "cidr_block": "10.0.0.0/17", + "customer_owned_ipv4_pool": "", + "enable_dns64": false, + "enable_lni_at_device_index": 0, + "enable_resource_name_dns_a_record_on_launch": false, + "enable_resource_name_dns_aaaa_record_on_launch": false, + "id": "subnet-05e5676ab26e11acc", + "ipv4_ipam_pool_id": null, + "ipv4_netmask_length": null, + "ipv6_cidr_block": "", + "ipv6_cidr_block_association_id": "", + "ipv6_ipam_pool_id": null, + "ipv6_native": false, + "ipv6_netmask_length": null, + "map_customer_owned_ip_on_launch": false, + "map_public_ip_on_launch": false, + "outpost_arn": "", + "owner_id": "177559564758", + "private_dns_hostname_type_on_launch": "ip-name", + "region": "us-east-2", + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "subnet-05e5676ab26e11acc", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9", + "dependencies": [ + "aws_vpc.finus" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_vpc", + "name": "finus", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:vpc/vpc-0e13d5c774ed2b3c8", + "assign_generated_ipv6_cidr_block": false, + "cidr_block": "10.0.0.0/16", + "default_network_acl_id": "acl-0f7a72d6813ae088c", + "default_route_table_id": "rtb-09da1959a912933f3", + "default_security_group_id": "sg-06987cbe4f87d0594", + "dhcp_options_id": "dopt-01d5dfa449859477f", + "enable_dns_hostnames": false, + "enable_dns_support": true, + "enable_network_address_usage_metrics": false, + "id": "vpc-0e13d5c774ed2b3c8", + "instance_tenancy": "default", + "ipv4_ipam_pool_id": null, + "ipv4_netmask_length": null, + "ipv6_association_id": "", + "ipv6_cidr_block": "", + "ipv6_cidr_block_network_border_group": "", + "ipv6_ipam_pool_id": "", + "ipv6_netmask_length": 0, + "main_route_table_id": "rtb-09da1959a912933f3", + "owner_id": "177559564758", + "region": "us-east-2", + "tags": { + "Name": "Finus" + }, + "tags_all": { + "Name": "Finus" + } + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "vpc-0e13d5c774ed2b3c8", + "region": "us-east-2" + }, + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==" + } + ] + }, + { + "module": "module.dev", + "mode": "data", + "type": "aws_ami", + "name": "amazon_linux", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "allow_unsafe_filter": null, + "architecture": "x86_64", + "arn": "arn:aws:ec2:us-east-2::image/ami-0b0b78dcacbab728f", + "block_device_mappings": [ + { + "device_name": "/dev/xvda", + "ebs": { + "delete_on_termination": "true", + "encrypted": "false", + "iops": "3000", + "snapshot_id": "snap-0afc5230d5113aa86", + "throughput": "125", + "volume_initialization_rate": "0", + "volume_size": "8", + "volume_type": "gp3" + }, + "no_device": "", + "virtual_name": "" + } + ], + "boot_mode": "uefi-preferred", + "creation_date": "2026-03-03T18:01:43.000Z", + "deprecation_time": "2026-06-01T23:47:00.000Z", + "description": "Amazon Linux 2023 AMI 2023.10.20260302.1 x86_64 HVM kernel-6.1", + "ena_support": true, + "executable_users": null, + "filter": [ + { + "name": "image-id", + "values": [ + "ami-0b0b78dcacbab728f" + ] + } + ], + "hypervisor": "xen", + "id": "ami-0b0b78dcacbab728f", + "image_id": "ami-0b0b78dcacbab728f", + "image_location": "amazon/al2023-ami-2023.10.20260302.1-kernel-6.1-x86_64", + "image_owner_alias": "amazon", + "image_type": "machine", + "imds_support": "v2.0", + "include_deprecated": false, + "kernel_id": "", + "last_launched_time": "", + "most_recent": false, + "name": "al2023-ami-2023.10.20260302.1-kernel-6.1-x86_64", + "name_regex": null, + "owner_id": "137112412989", + "owners": null, + "platform": "", + "platform_details": "Linux/UNIX", + "product_codes": [], + "public": true, + "ramdisk_id": "", + "region": "us-east-2", + "root_device_name": "/dev/xvda", + "root_device_type": "ebs", + "root_snapshot_id": "snap-0afc5230d5113aa86", + "sriov_net_support": "simple", + "state": "available", + "state_reason": { + "code": "UNSET", + "message": "UNSET" + }, + "tags": {}, + "timeouts": null, + "tpm_support": "", + "uefi_data": null, + "usage_operation": "RunInstances", + "virtualization_type": "hvm" + }, + "sensitive_attributes": [], + "identity_schema_version": 0 + } + ] + }, + { + "module": "module.dev", + "mode": "managed", + "type": "aws_eip", + "name": "backend", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "address": null, + "allocation_id": "eipalloc-04f46a5b83e1a6af0", + "arn": "arn:aws:ec2:us-east-2:177559564758:elastic-ip/eipalloc-04f46a5b83e1a6af0", + "associate_with_private_ip": null, + "association_id": "eipassoc-0818aa83d85c2f91b", + "carrier_ip": "", + "customer_owned_ip": "", + "customer_owned_ipv4_pool": "", + "domain": "vpc", + "id": "eipalloc-04f46a5b83e1a6af0", + "instance": "i-008147727278f7080", + "ipam_pool_id": null, + "network_border_group": "us-east-2", + "network_interface": "eni-0f364cdcae030e0e6", + "private_dns": "ip-10-0-221-90.us-east-2.compute.internal", + "private_ip": "10.0.221.90", + "ptr_record": "", + "public_dns": "ec2-16-58-118-74.us-east-2.compute.amazonaws.com", + "public_ip": "16.58.118.74", + "public_ipv4_pool": "amazon", + "region": "us-east-2", + "tags": { + "Name": "finus-dev-backend" + }, + "tags_all": { + "Name": "finus-dev-backend" + }, + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.dev", + "aws_vpc.finus", + "module.dev.aws_instance.backend", + "module.dev.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.dev", + "mode": "managed", + "type": "aws_eip", + "name": "webserver", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "address": null, + "allocation_id": "eipalloc-0857b784b20a92dad", + "arn": "arn:aws:ec2:us-east-2:177559564758:elastic-ip/eipalloc-0857b784b20a92dad", + "associate_with_private_ip": null, + "association_id": "eipassoc-06e143e0fc9004868", + "carrier_ip": "", + "customer_owned_ip": "", + "customer_owned_ipv4_pool": "", + "domain": "vpc", + "id": "eipalloc-0857b784b20a92dad", + "instance": "i-020ba441d6c3c0258", + "ipam_pool_id": null, + "network_border_group": "us-east-2", + "network_interface": "eni-03283cda15cf431d3", + "private_dns": "ip-10-0-140-152.us-east-2.compute.internal", + "private_ip": "10.0.140.152", + "ptr_record": "", + "public_dns": "ec2-3-142-125-202.us-east-2.compute.amazonaws.com", + "public_ip": "3.142.125.202", + "public_ipv4_pool": "amazon", + "region": "us-east-2", + "tags": { + "Name": "finus-dev-webserver" + }, + "tags_all": { + "Name": "finus-dev-webserver" + }, + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.dev", + "aws_vpc.finus", + "module.dev.aws_instance.webserver", + "module.dev.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.dev", + "mode": "managed", + "type": "aws_instance", + "name": "backend", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 2, + "attributes": { + "ami": "ami-0b0b78dcacbab728f", + "arn": "arn:aws:ec2:us-east-2:177559564758:instance/i-008147727278f7080", + "associate_public_ip_address": true, + "availability_zone": "us-east-2c", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 1 + } + ], + "credit_specification": [ + { + "cpu_credits": "standard" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-008147727278f7080", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t2.nano", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "finus-dev", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 2, + "http_tokens": "required", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-0f364cdcae030e0e6" + } + ], + "primary_network_interface_id": "eni-0f364cdcae030e0e6", + "private_dns": "ip-10-0-221-90.us-east-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "10.0.221.90", + "public_dns": "", + "public_ip": "16.58.118.74", + "region": "us-east-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/xvda", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": {}, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-0c0b46050d4eb986e", + "volume_size": 8, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-09d5718b3434ec2be", + "tags": { + "Name": "finus-dev-backend" + }, + "tags_all": { + "Name": "finus-dev-backend" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0c03eb59bd6270fcf" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "i-008147727278f7080", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.dev", + "aws_vpc.finus", + "module.dev.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.dev", + "mode": "managed", + "type": "aws_instance", + "name": "webserver", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 2, + "attributes": { + "ami": "ami-0b0b78dcacbab728f", + "arn": "arn:aws:ec2:us-east-2:177559564758:instance/i-020ba441d6c3c0258", + "associate_public_ip_address": true, + "availability_zone": "us-east-2c", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 1 + } + ], + "credit_specification": [ + { + "cpu_credits": "standard" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-020ba441d6c3c0258", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t2.nano", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "finus-dev", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 2, + "http_tokens": "required", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-03283cda15cf431d3" + } + ], + "primary_network_interface_id": "eni-03283cda15cf431d3", + "private_dns": "ip-10-0-140-152.us-east-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "10.0.140.152", + "public_dns": "", + "public_ip": "3.142.125.202", + "region": "us-east-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/xvda", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": {}, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-09e9f25279b1c2ac1", + "volume_size": 8, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-09d5718b3434ec2be", + "tags": { + "Name": "finus-dev-webserver" + }, + "tags_all": { + "Name": "finus-dev-webserver" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0c03eb59bd6270fcf" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "i-020ba441d6c3c0258", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.dev", + "aws_vpc.finus", + "module.dev.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.dev", + "mode": "managed", + "type": "aws_route_table_association", + "name": "assoc", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "gateway_id": "", + "id": "rtbassoc-04712335fcc923e95", + "region": "us-east-2", + "route_table_id": "rtb-0f24beedbed7e02a3", + "subnet_id": "subnet-09d5718b3434ec2be", + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjEyMDAwMDAwMDAwMH19", + "dependencies": [ + "aws_internet_gateway.default", + "aws_route_table.route_table", + "aws_subnet.dev", + "aws_vpc.finus" + ] + } + ] + }, + { + "module": "module.prod", + "mode": "data", + "type": "aws_ami", + "name": "amazon_linux", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "allow_unsafe_filter": null, + "architecture": "x86_64", + "arn": "arn:aws:ec2:us-east-2::image/ami-0b0b78dcacbab728f", + "block_device_mappings": [ + { + "device_name": "/dev/xvda", + "ebs": { + "delete_on_termination": "true", + "encrypted": "false", + "iops": "3000", + "snapshot_id": "snap-0afc5230d5113aa86", + "throughput": "125", + "volume_initialization_rate": "0", + "volume_size": "8", + "volume_type": "gp3" + }, + "no_device": "", + "virtual_name": "" + } + ], + "boot_mode": "uefi-preferred", + "creation_date": "2026-03-03T18:01:43.000Z", + "deprecation_time": "2026-06-01T23:47:00.000Z", + "description": "Amazon Linux 2023 AMI 2023.10.20260302.1 x86_64 HVM kernel-6.1", + "ena_support": true, + "executable_users": null, + "filter": [ + { + "name": "image-id", + "values": [ + "ami-0b0b78dcacbab728f" + ] + } + ], + "hypervisor": "xen", + "id": "ami-0b0b78dcacbab728f", + "image_id": "ami-0b0b78dcacbab728f", + "image_location": "amazon/al2023-ami-2023.10.20260302.1-kernel-6.1-x86_64", + "image_owner_alias": "amazon", + "image_type": "machine", + "imds_support": "v2.0", + "include_deprecated": false, + "kernel_id": "", + "last_launched_time": "", + "most_recent": false, + "name": "al2023-ami-2023.10.20260302.1-kernel-6.1-x86_64", + "name_regex": null, + "owner_id": "137112412989", + "owners": null, + "platform": "", + "platform_details": "Linux/UNIX", + "product_codes": [], + "public": true, + "ramdisk_id": "", + "region": "us-east-2", + "root_device_name": "/dev/xvda", + "root_device_type": "ebs", + "root_snapshot_id": "snap-0afc5230d5113aa86", + "sriov_net_support": "simple", + "state": "available", + "state_reason": { + "code": "UNSET", + "message": "UNSET" + }, + "tags": {}, + "timeouts": null, + "tpm_support": "", + "uefi_data": null, + "usage_operation": "RunInstances", + "virtualization_type": "hvm" + }, + "sensitive_attributes": [], + "identity_schema_version": 0 + } + ] + }, + { + "module": "module.prod", + "mode": "managed", + "type": "aws_eip", + "name": "backend", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "address": null, + "allocation_id": "eipalloc-0262f07d6e23594c4", + "arn": "arn:aws:ec2:us-east-2:177559564758:elastic-ip/eipalloc-0262f07d6e23594c4", + "associate_with_private_ip": null, + "association_id": "eipassoc-0202a50414bdbee94", + "carrier_ip": "", + "customer_owned_ip": "", + "customer_owned_ipv4_pool": "", + "domain": "vpc", + "id": "eipalloc-0262f07d6e23594c4", + "instance": "i-0dd805bcaa0a0ade3", + "ipam_pool_id": null, + "network_border_group": "us-east-2", + "network_interface": "eni-0d9461ec08710f2d7", + "private_dns": "ip-10-0-50-231.us-east-2.compute.internal", + "private_ip": "10.0.50.231", + "ptr_record": "", + "public_dns": "ec2-3-150-23-72.us-east-2.compute.amazonaws.com", + "public_ip": "3.150.23.72", + "public_ipv4_pool": "amazon", + "region": "us-east-2", + "tags": { + "Name": "finus-prod-backend" + }, + "tags_all": { + "Name": "finus-prod-backend" + }, + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.prod", + "aws_vpc.finus", + "module.prod.aws_instance.backend", + "module.prod.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.prod", + "mode": "managed", + "type": "aws_eip", + "name": "webserver", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "address": null, + "allocation_id": "eipalloc-06fe359af4fbd5f9b", + "arn": "arn:aws:ec2:us-east-2:177559564758:elastic-ip/eipalloc-06fe359af4fbd5f9b", + "associate_with_private_ip": null, + "association_id": "eipassoc-0ce3ab9543feb5d7f", + "carrier_ip": "", + "customer_owned_ip": "", + "customer_owned_ipv4_pool": "", + "domain": "vpc", + "id": "eipalloc-06fe359af4fbd5f9b", + "instance": "i-0f897d2768cad0033", + "ipam_pool_id": null, + "network_border_group": "us-east-2", + "network_interface": "eni-0e2de615da26dd0d9", + "private_dns": "ip-10-0-57-91.us-east-2.compute.internal", + "private_ip": "10.0.57.91", + "ptr_record": "", + "public_dns": "ec2-18-190-215-135.us-east-2.compute.amazonaws.com", + "public_ip": "18.190.215.135", + "public_ipv4_pool": "amazon", + "region": "us-east-2", + "tags": { + "Name": "finus-prod-webserver" + }, + "tags_all": { + "Name": "finus-prod-webserver" + }, + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.prod", + "aws_vpc.finus", + "module.prod.aws_instance.webserver", + "module.prod.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.prod", + "mode": "managed", + "type": "aws_instance", + "name": "backend", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 2, + "attributes": { + "ami": "ami-0b0b78dcacbab728f", + "arn": "arn:aws:ec2:us-east-2:177559564758:instance/i-0dd805bcaa0a0ade3", + "associate_public_ip_address": true, + "availability_zone": "us-east-2c", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 1 + } + ], + "credit_specification": [ + { + "cpu_credits": "standard" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-0dd805bcaa0a0ade3", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t2.nano", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "finus-dev", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 2, + "http_tokens": "required", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-0d9461ec08710f2d7" + } + ], + "primary_network_interface_id": "eni-0d9461ec08710f2d7", + "private_dns": "ip-10-0-50-231.us-east-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "10.0.50.231", + "public_dns": "", + "public_ip": "3.150.23.72", + "region": "us-east-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/xvda", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": {}, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-0115ad31723e63439", + "volume_size": 8, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-05e5676ab26e11acc", + "tags": { + "Name": "finus-prod-backend" + }, + "tags_all": { + "Name": "finus-prod-backend" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0c03eb59bd6270fcf" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "i-0dd805bcaa0a0ade3", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.prod", + "aws_vpc.finus", + "module.prod.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.prod", + "mode": "managed", + "type": "aws_instance", + "name": "webserver", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 2, + "attributes": { + "ami": "ami-0b0b78dcacbab728f", + "arn": "arn:aws:ec2:us-east-2:177559564758:instance/i-0f897d2768cad0033", + "associate_public_ip_address": true, + "availability_zone": "us-east-2c", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 1 + } + ], + "credit_specification": [ + { + "cpu_credits": "standard" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-0f897d2768cad0033", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t2.nano", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "finus-dev", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 2, + "http_tokens": "required", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-0e2de615da26dd0d9" + } + ], + "primary_network_interface_id": "eni-0e2de615da26dd0d9", + "private_dns": "ip-10-0-57-91.us-east-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "10.0.57.91", + "public_dns": "", + "public_ip": "18.190.215.135", + "region": "us-east-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/xvda", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": {}, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-05d61c7cb18058c36", + "volume_size": 8, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-05e5676ab26e11acc", + "tags": { + "Name": "finus-prod-webserver" + }, + "tags_all": { + "Name": "finus-prod-webserver" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0c03eb59bd6270fcf" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "i-0f897d2768cad0033", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.prod", + "aws_vpc.finus", + "module.prod.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.prod", + "mode": "managed", + "type": "aws_route_table_association", + "name": "assoc", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "gateway_id": "", + "id": "rtbassoc-0b1e2801d2830d94c", + "region": "us-east-2", + "route_table_id": "rtb-0f24beedbed7e02a3", + "subnet_id": "subnet-05e5676ab26e11acc", + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjEyMDAwMDAwMDAwMH19", + "dependencies": [ + "aws_internet_gateway.default", + "aws_route_table.route_table", + "aws_subnet.prod", + "aws_vpc.finus" + ] + } + ] + } + ], + "check_results": null +} diff --git a/deployment/infrastructure/terraform.tfstate.backup b/deployment/infrastructure/terraform.tfstate.backup new file mode 100644 index 0000000..6d85dc9 --- /dev/null +++ b/deployment/infrastructure/terraform.tfstate.backup @@ -0,0 +1,1363 @@ +{ + "version": 4, + "terraform_version": "1.14.7", + "serial": 110, + "lineage": "ae3e9505-c9b9-9084-580e-4ba9a7f0ba2f", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "aws_internet_gateway", + "name": "default", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:internet-gateway/igw-0a74c1fef454437c8", + "id": "igw-0a74c1fef454437c8", + "owner_id": "177559564758", + "region": "us-east-2", + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjoxMjAwMDAwMDAwMDAwLCJkZWxldGUiOjEyMDAwMDAwMDAwMDAsInVwZGF0ZSI6MTIwMDAwMDAwMDAwMH19", + "dependencies": [ + "aws_vpc.finus" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_route_table", + "name": "route_table", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:route-table/rtb-0f24beedbed7e02a3", + "id": "rtb-0f24beedbed7e02a3", + "owner_id": "177559564758", + "propagating_vgws": [], + "region": "us-east-2", + "route": [ + { + "carrier_gateway_id": "", + "cidr_block": "0.0.0.0/0", + "core_network_arn": "", + "destination_prefix_list_id": "", + "egress_only_gateway_id": "", + "gateway_id": "igw-0a74c1fef454437c8", + "ipv6_cidr_block": "", + "local_gateway_id": "", + "nat_gateway_id": "", + "network_interface_id": "", + "transit_gateway_id": "", + "vpc_endpoint_id": "", + "vpc_peering_connection_id": "" + } + ], + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "rtb-0f24beedbed7e02a3", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjEyMDAwMDAwMDAwMH19", + "dependencies": [ + "aws_internet_gateway.default", + "aws_vpc.finus" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_security_group", + "name": "default", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:security-group/sg-0c03eb59bd6270fcf", + "description": "Managed by Terraform", + "egress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "All outbound traffic", + "from_port": 0, + "ipv6_cidr_blocks": [ + "::/0" + ], + "prefix_list_ids": [], + "protocol": "-1", + "security_groups": [], + "self": false, + "to_port": 0 + } + ], + "id": "sg-0c03eb59bd6270fcf", + "ingress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "SSH from anywhere", + "from_port": 22, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "tcp", + "security_groups": [], + "self": false, + "to_port": 22 + } + ], + "name": "finus-default", + "name_prefix": "", + "owner_id": "177559564758", + "region": "us-east-2", + "revoke_rules_on_delete": false, + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "sg-0c03eb59bd6270fcf", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6OTAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIxIn0=", + "dependencies": [ + "aws_vpc.finus" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_subnet", + "name": "dev", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:subnet/subnet-09d5718b3434ec2be", + "assign_ipv6_address_on_creation": false, + "availability_zone": "us-east-2c", + "availability_zone_id": "use2-az3", + "cidr_block": "10.0.128.0/17", + "customer_owned_ipv4_pool": "", + "enable_dns64": false, + "enable_lni_at_device_index": 0, + "enable_resource_name_dns_a_record_on_launch": false, + "enable_resource_name_dns_aaaa_record_on_launch": false, + "id": "subnet-09d5718b3434ec2be", + "ipv4_ipam_pool_id": null, + "ipv4_netmask_length": null, + "ipv6_cidr_block": "", + "ipv6_cidr_block_association_id": "", + "ipv6_ipam_pool_id": null, + "ipv6_native": false, + "ipv6_netmask_length": null, + "map_customer_owned_ip_on_launch": false, + "map_public_ip_on_launch": false, + "outpost_arn": "", + "owner_id": "177559564758", + "private_dns_hostname_type_on_launch": "ip-name", + "region": "us-east-2", + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "subnet-09d5718b3434ec2be", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9", + "dependencies": [ + "aws_vpc.finus" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_subnet", + "name": "prod", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:subnet/subnet-05e5676ab26e11acc", + "assign_ipv6_address_on_creation": false, + "availability_zone": "us-east-2c", + "availability_zone_id": "use2-az3", + "cidr_block": "10.0.0.0/17", + "customer_owned_ipv4_pool": "", + "enable_dns64": false, + "enable_lni_at_device_index": 0, + "enable_resource_name_dns_a_record_on_launch": false, + "enable_resource_name_dns_aaaa_record_on_launch": false, + "id": "subnet-05e5676ab26e11acc", + "ipv4_ipam_pool_id": null, + "ipv4_netmask_length": null, + "ipv6_cidr_block": "", + "ipv6_cidr_block_association_id": "", + "ipv6_ipam_pool_id": null, + "ipv6_native": false, + "ipv6_netmask_length": null, + "map_customer_owned_ip_on_launch": false, + "map_public_ip_on_launch": false, + "outpost_arn": "", + "owner_id": "177559564758", + "private_dns_hostname_type_on_launch": "ip-name", + "region": "us-east-2", + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "subnet-05e5676ab26e11acc", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMSJ9", + "dependencies": [ + "aws_vpc.finus" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_vpc", + "name": "finus", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:vpc/vpc-0e13d5c774ed2b3c8", + "assign_generated_ipv6_cidr_block": false, + "cidr_block": "10.0.0.0/16", + "default_network_acl_id": "acl-0f7a72d6813ae088c", + "default_route_table_id": "rtb-09da1959a912933f3", + "default_security_group_id": "sg-06987cbe4f87d0594", + "dhcp_options_id": "dopt-01d5dfa449859477f", + "enable_dns_hostnames": false, + "enable_dns_support": true, + "enable_network_address_usage_metrics": false, + "id": "vpc-0e13d5c774ed2b3c8", + "instance_tenancy": "default", + "ipv4_ipam_pool_id": null, + "ipv4_netmask_length": null, + "ipv6_association_id": "", + "ipv6_cidr_block": "", + "ipv6_cidr_block_network_border_group": "", + "ipv6_ipam_pool_id": "", + "ipv6_netmask_length": 0, + "main_route_table_id": "rtb-09da1959a912933f3", + "owner_id": "177559564758", + "region": "us-east-2", + "tags": { + "Name": "Finus" + }, + "tags_all": { + "Name": "Finus" + } + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "vpc-0e13d5c774ed2b3c8", + "region": "us-east-2" + }, + "private": "eyJzY2hlbWFfdmVyc2lvbiI6IjEifQ==" + } + ] + }, + { + "module": "module.dev", + "mode": "data", + "type": "aws_ami", + "name": "amazon_linux", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "allow_unsafe_filter": null, + "architecture": "x86_64", + "arn": "arn:aws:ec2:us-east-2::image/ami-0b0b78dcacbab728f", + "block_device_mappings": [ + { + "device_name": "/dev/xvda", + "ebs": { + "delete_on_termination": "true", + "encrypted": "false", + "iops": "3000", + "snapshot_id": "snap-0afc5230d5113aa86", + "throughput": "125", + "volume_initialization_rate": "0", + "volume_size": "8", + "volume_type": "gp3" + }, + "no_device": "", + "virtual_name": "" + } + ], + "boot_mode": "uefi-preferred", + "creation_date": "2026-03-03T18:01:43.000Z", + "deprecation_time": "2026-06-01T23:47:00.000Z", + "description": "Amazon Linux 2023 AMI 2023.10.20260302.1 x86_64 HVM kernel-6.1", + "ena_support": true, + "executable_users": null, + "filter": [ + { + "name": "image-id", + "values": [ + "ami-0b0b78dcacbab728f" + ] + } + ], + "hypervisor": "xen", + "id": "ami-0b0b78dcacbab728f", + "image_id": "ami-0b0b78dcacbab728f", + "image_location": "amazon/al2023-ami-2023.10.20260302.1-kernel-6.1-x86_64", + "image_owner_alias": "amazon", + "image_type": "machine", + "imds_support": "v2.0", + "include_deprecated": false, + "kernel_id": "", + "last_launched_time": "", + "most_recent": false, + "name": "al2023-ami-2023.10.20260302.1-kernel-6.1-x86_64", + "name_regex": null, + "owner_id": "137112412989", + "owners": null, + "platform": "", + "platform_details": "Linux/UNIX", + "product_codes": [], + "public": true, + "ramdisk_id": "", + "region": "us-east-2", + "root_device_name": "/dev/xvda", + "root_device_type": "ebs", + "root_snapshot_id": "snap-0afc5230d5113aa86", + "sriov_net_support": "simple", + "state": "available", + "state_reason": { + "code": "UNSET", + "message": "UNSET" + }, + "tags": {}, + "timeouts": null, + "tpm_support": "", + "uefi_data": null, + "usage_operation": "RunInstances", + "virtualization_type": "hvm" + }, + "sensitive_attributes": [], + "identity_schema_version": 0 + } + ] + }, + { + "module": "module.dev", + "mode": "managed", + "type": "aws_eip", + "name": "backend", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "address": null, + "allocation_id": "eipalloc-04f46a5b83e1a6af0", + "arn": "arn:aws:ec2:us-east-2:177559564758:elastic-ip/eipalloc-04f46a5b83e1a6af0", + "associate_with_private_ip": null, + "association_id": "eipassoc-0818aa83d85c2f91b", + "carrier_ip": "", + "customer_owned_ip": "", + "customer_owned_ipv4_pool": "", + "domain": "vpc", + "id": "eipalloc-04f46a5b83e1a6af0", + "instance": "i-008147727278f7080", + "ipam_pool_id": null, + "network_border_group": "us-east-2", + "network_interface": "eni-0f364cdcae030e0e6", + "private_dns": "ip-10-0-221-90.us-east-2.compute.internal", + "private_ip": "10.0.221.90", + "ptr_record": "", + "public_dns": "ec2-16-58-118-74.us-east-2.compute.amazonaws.com", + "public_ip": "16.58.118.74", + "public_ipv4_pool": "amazon", + "region": "us-east-2", + "tags": { + "Name": "finus-dev-backend" + }, + "tags_all": { + "Name": "finus-dev-backend" + }, + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.dev", + "aws_vpc.finus", + "module.dev.aws_instance.backend", + "module.dev.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.dev", + "mode": "managed", + "type": "aws_eip", + "name": "webserver", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "address": null, + "allocation_id": "eipalloc-0857b784b20a92dad", + "arn": "arn:aws:ec2:us-east-2:177559564758:elastic-ip/eipalloc-0857b784b20a92dad", + "associate_with_private_ip": null, + "association_id": "eipassoc-06e143e0fc9004868", + "carrier_ip": "", + "customer_owned_ip": "", + "customer_owned_ipv4_pool": "", + "domain": "vpc", + "id": "eipalloc-0857b784b20a92dad", + "instance": "i-020ba441d6c3c0258", + "ipam_pool_id": null, + "network_border_group": "us-east-2", + "network_interface": "eni-03283cda15cf431d3", + "private_dns": "ip-10-0-140-152.us-east-2.compute.internal", + "private_ip": "10.0.140.152", + "ptr_record": "", + "public_dns": "ec2-3-142-125-202.us-east-2.compute.amazonaws.com", + "public_ip": "3.142.125.202", + "public_ipv4_pool": "amazon", + "region": "us-east-2", + "tags": { + "Name": "finus-dev-webserver" + }, + "tags_all": { + "Name": "finus-dev-webserver" + }, + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.dev", + "aws_vpc.finus", + "module.dev.aws_instance.webserver", + "module.dev.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.dev", + "mode": "managed", + "type": "aws_instance", + "name": "backend", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 2, + "attributes": { + "ami": "ami-0b0b78dcacbab728f", + "arn": "arn:aws:ec2:us-east-2:177559564758:instance/i-008147727278f7080", + "associate_public_ip_address": true, + "availability_zone": "us-east-2c", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 1 + } + ], + "credit_specification": [ + { + "cpu_credits": "standard" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-008147727278f7080", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t2.nano", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "finus-dev", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 2, + "http_tokens": "required", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-0f364cdcae030e0e6" + } + ], + "primary_network_interface_id": "eni-0f364cdcae030e0e6", + "private_dns": "ip-10-0-221-90.us-east-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "10.0.221.90", + "public_dns": "", + "public_ip": "16.58.118.74", + "region": "us-east-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/xvda", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": {}, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-0c0b46050d4eb986e", + "volume_size": 8, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-09d5718b3434ec2be", + "tags": { + "Name": "finus-dev-backend" + }, + "tags_all": { + "Name": "finus-dev-backend" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0c03eb59bd6270fcf" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "i-008147727278f7080", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.dev", + "aws_vpc.finus", + "module.dev.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.dev", + "mode": "managed", + "type": "aws_instance", + "name": "webserver", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 2, + "attributes": { + "ami": "ami-0b0b78dcacbab728f", + "arn": "arn:aws:ec2:us-east-2:177559564758:instance/i-020ba441d6c3c0258", + "associate_public_ip_address": true, + "availability_zone": "us-east-2c", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 1 + } + ], + "credit_specification": [ + { + "cpu_credits": "standard" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-020ba441d6c3c0258", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t2.nano", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "finus-dev", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 2, + "http_tokens": "required", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-03283cda15cf431d3" + } + ], + "primary_network_interface_id": "eni-03283cda15cf431d3", + "private_dns": "ip-10-0-140-152.us-east-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "10.0.140.152", + "public_dns": "", + "public_ip": "3.142.125.202", + "region": "us-east-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/xvda", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": {}, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-09e9f25279b1c2ac1", + "volume_size": 8, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-09d5718b3434ec2be", + "tags": { + "Name": "finus-dev-webserver" + }, + "tags_all": { + "Name": "finus-dev-webserver" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0c03eb59bd6270fcf" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "i-020ba441d6c3c0258", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.dev", + "aws_vpc.finus", + "module.dev.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.dev", + "mode": "managed", + "type": "aws_route_table_association", + "name": "assoc", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "gateway_id": "", + "id": "rtbassoc-04712335fcc923e95", + "region": "us-east-2", + "route_table_id": "rtb-0f24beedbed7e02a3", + "subnet_id": "subnet-09d5718b3434ec2be", + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjEyMDAwMDAwMDAwMH19", + "dependencies": [ + "aws_internet_gateway.default", + "aws_route_table.route_table", + "aws_subnet.dev", + "aws_vpc.finus" + ] + } + ] + }, + { + "module": "module.prod", + "mode": "data", + "type": "aws_ami", + "name": "amazon_linux", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "allow_unsafe_filter": null, + "architecture": "x86_64", + "arn": "arn:aws:ec2:us-east-2::image/ami-0b0b78dcacbab728f", + "block_device_mappings": [ + { + "device_name": "/dev/xvda", + "ebs": { + "delete_on_termination": "true", + "encrypted": "false", + "iops": "3000", + "snapshot_id": "snap-0afc5230d5113aa86", + "throughput": "125", + "volume_initialization_rate": "0", + "volume_size": "8", + "volume_type": "gp3" + }, + "no_device": "", + "virtual_name": "" + } + ], + "boot_mode": "uefi-preferred", + "creation_date": "2026-03-03T18:01:43.000Z", + "deprecation_time": "2026-06-01T23:47:00.000Z", + "description": "Amazon Linux 2023 AMI 2023.10.20260302.1 x86_64 HVM kernel-6.1", + "ena_support": true, + "executable_users": null, + "filter": [ + { + "name": "image-id", + "values": [ + "ami-0b0b78dcacbab728f" + ] + } + ], + "hypervisor": "xen", + "id": "ami-0b0b78dcacbab728f", + "image_id": "ami-0b0b78dcacbab728f", + "image_location": "amazon/al2023-ami-2023.10.20260302.1-kernel-6.1-x86_64", + "image_owner_alias": "amazon", + "image_type": "machine", + "imds_support": "v2.0", + "include_deprecated": false, + "kernel_id": "", + "last_launched_time": "", + "most_recent": false, + "name": "al2023-ami-2023.10.20260302.1-kernel-6.1-x86_64", + "name_regex": null, + "owner_id": "137112412989", + "owners": null, + "platform": "", + "platform_details": "Linux/UNIX", + "product_codes": [], + "public": true, + "ramdisk_id": "", + "region": "us-east-2", + "root_device_name": "/dev/xvda", + "root_device_type": "ebs", + "root_snapshot_id": "snap-0afc5230d5113aa86", + "sriov_net_support": "simple", + "state": "available", + "state_reason": { + "code": "UNSET", + "message": "UNSET" + }, + "tags": {}, + "timeouts": null, + "tpm_support": "", + "uefi_data": null, + "usage_operation": "RunInstances", + "virtualization_type": "hvm" + }, + "sensitive_attributes": [], + "identity_schema_version": 0 + } + ] + }, + { + "module": "module.prod", + "mode": "managed", + "type": "aws_eip", + "name": "backend", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "address": null, + "allocation_id": "eipalloc-0262f07d6e23594c4", + "arn": "arn:aws:ec2:us-east-2:177559564758:elastic-ip/eipalloc-0262f07d6e23594c4", + "associate_with_private_ip": null, + "association_id": "eipassoc-0202a50414bdbee94", + "carrier_ip": "", + "customer_owned_ip": "", + "customer_owned_ipv4_pool": "", + "domain": "vpc", + "id": "eipalloc-0262f07d6e23594c4", + "instance": "i-0dd805bcaa0a0ade3", + "ipam_pool_id": null, + "network_border_group": "us-east-2", + "network_interface": "eni-0d9461ec08710f2d7", + "private_dns": "ip-10-0-50-231.us-east-2.compute.internal", + "private_ip": "10.0.50.231", + "ptr_record": "", + "public_dns": "ec2-3-150-23-72.us-east-2.compute.amazonaws.com", + "public_ip": "3.150.23.72", + "public_ipv4_pool": "amazon", + "region": "us-east-2", + "tags": { + "Name": "finus-prod-backend" + }, + "tags_all": { + "Name": "finus-prod-backend" + }, + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.prod", + "aws_vpc.finus", + "module.prod.aws_instance.backend", + "module.prod.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.prod", + "mode": "managed", + "type": "aws_eip", + "name": "webserver", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "address": null, + "allocation_id": "eipalloc-06fe359af4fbd5f9b", + "arn": "arn:aws:ec2:us-east-2:177559564758:elastic-ip/eipalloc-06fe359af4fbd5f9b", + "associate_with_private_ip": null, + "association_id": "eipassoc-0ce3ab9543feb5d7f", + "carrier_ip": "", + "customer_owned_ip": "", + "customer_owned_ipv4_pool": "", + "domain": "vpc", + "id": "eipalloc-06fe359af4fbd5f9b", + "instance": "i-0f897d2768cad0033", + "ipam_pool_id": null, + "network_border_group": "us-east-2", + "network_interface": "eni-0e2de615da26dd0d9", + "private_dns": "ip-10-0-57-91.us-east-2.compute.internal", + "private_ip": "10.0.57.91", + "ptr_record": "", + "public_dns": "ec2-18-190-215-135.us-east-2.compute.amazonaws.com", + "public_ip": "18.190.215.135", + "public_ipv4_pool": "amazon", + "region": "us-east-2", + "tags": { + "Name": "finus-prod-webserver" + }, + "tags_all": { + "Name": "finus-prod-webserver" + }, + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.prod", + "aws_vpc.finus", + "module.prod.aws_instance.webserver", + "module.prod.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.prod", + "mode": "managed", + "type": "aws_instance", + "name": "backend", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 2, + "attributes": { + "ami": "ami-0b0b78dcacbab728f", + "arn": "arn:aws:ec2:us-east-2:177559564758:instance/i-0dd805bcaa0a0ade3", + "associate_public_ip_address": true, + "availability_zone": "us-east-2c", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 1 + } + ], + "credit_specification": [ + { + "cpu_credits": "standard" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-0dd805bcaa0a0ade3", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t2.nano", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "finus-dev", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 2, + "http_tokens": "required", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-0d9461ec08710f2d7" + } + ], + "primary_network_interface_id": "eni-0d9461ec08710f2d7", + "private_dns": "ip-10-0-50-231.us-east-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "10.0.50.231", + "public_dns": "", + "public_ip": "3.150.23.72", + "region": "us-east-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/xvda", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": {}, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-0115ad31723e63439", + "volume_size": 8, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-05e5676ab26e11acc", + "tags": { + "Name": "finus-prod-backend" + }, + "tags_all": { + "Name": "finus-prod-backend" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0c03eb59bd6270fcf" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "i-0dd805bcaa0a0ade3", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.prod", + "aws_vpc.finus", + "module.prod.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.prod", + "mode": "managed", + "type": "aws_instance", + "name": "webserver", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 2, + "attributes": { + "ami": "ami-0b0b78dcacbab728f", + "arn": "arn:aws:ec2:us-east-2:177559564758:instance/i-0f897d2768cad0033", + "associate_public_ip_address": true, + "availability_zone": "us-east-2c", + "capacity_reservation_specification": [ + { + "capacity_reservation_preference": "open", + "capacity_reservation_target": [] + } + ], + "cpu_options": [ + { + "amd_sev_snp": "", + "core_count": 1, + "nested_virtualization": "", + "threads_per_core": 1 + } + ], + "credit_specification": [ + { + "cpu_credits": "standard" + } + ], + "disable_api_stop": false, + "disable_api_termination": false, + "ebs_block_device": [], + "ebs_optimized": false, + "enable_primary_ipv6": null, + "enclave_options": [ + { + "enabled": false + } + ], + "ephemeral_block_device": [], + "force_destroy": false, + "get_password_data": false, + "hibernation": false, + "host_id": "", + "host_resource_group_arn": null, + "iam_instance_profile": "", + "id": "i-0f897d2768cad0033", + "instance_initiated_shutdown_behavior": "stop", + "instance_lifecycle": "", + "instance_market_options": [], + "instance_state": "running", + "instance_type": "t2.nano", + "ipv6_address_count": 0, + "ipv6_addresses": [], + "key_name": "finus-dev", + "launch_template": [], + "maintenance_options": [ + { + "auto_recovery": "default" + } + ], + "metadata_options": [ + { + "http_endpoint": "enabled", + "http_protocol_ipv6": "disabled", + "http_put_response_hop_limit": 2, + "http_tokens": "required", + "instance_metadata_tags": "disabled" + } + ], + "monitoring": false, + "network_interface": [], + "outpost_arn": "", + "password_data": "", + "placement_group": "", + "placement_group_id": "", + "placement_partition_number": 0, + "primary_network_interface": [ + { + "delete_on_termination": true, + "network_interface_id": "eni-0e2de615da26dd0d9" + } + ], + "primary_network_interface_id": "eni-0e2de615da26dd0d9", + "private_dns": "ip-10-0-57-91.us-east-2.compute.internal", + "private_dns_name_options": [ + { + "enable_resource_name_dns_a_record": false, + "enable_resource_name_dns_aaaa_record": false, + "hostname_type": "ip-name" + } + ], + "private_ip": "10.0.57.91", + "public_dns": "", + "public_ip": "18.190.215.135", + "region": "us-east-2", + "root_block_device": [ + { + "delete_on_termination": true, + "device_name": "/dev/xvda", + "encrypted": false, + "iops": 3000, + "kms_key_id": "", + "tags": {}, + "tags_all": {}, + "throughput": 125, + "volume_id": "vol-05d61c7cb18058c36", + "volume_size": 8, + "volume_type": "gp3" + } + ], + "secondary_network_interface": [], + "secondary_private_ips": [], + "security_groups": [], + "source_dest_check": true, + "spot_instance_request_id": "", + "subnet_id": "subnet-05e5676ab26e11acc", + "tags": { + "Name": "finus-prod-webserver" + }, + "tags_all": { + "Name": "finus-prod-webserver" + }, + "tenancy": "default", + "timeouts": null, + "user_data": null, + "user_data_base64": null, + "user_data_replace_on_change": false, + "volume_tags": null, + "vpc_security_group_ids": [ + "sg-0c03eb59bd6270fcf" + ] + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "i-0f897d2768cad0033", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", + "dependencies": [ + "aws_security_group.default", + "aws_subnet.prod", + "aws_vpc.finus", + "module.prod.data.aws_ami.amazon_linux" + ] + } + ] + }, + { + "module": "module.prod", + "mode": "managed", + "type": "aws_route_table_association", + "name": "assoc", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "gateway_id": "", + "id": "rtbassoc-0b1e2801d2830d94c", + "region": "us-east-2", + "route_table_id": "rtb-0f24beedbed7e02a3", + "subnet_id": "subnet-05e5676ab26e11acc", + "timeouts": null + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjozMDAwMDAwMDAwMDAsImRlbGV0ZSI6MzAwMDAwMDAwMDAwLCJ1cGRhdGUiOjEyMDAwMDAwMDAwMH19", + "dependencies": [ + "aws_internet_gateway.default", + "aws_route_table.route_table", + "aws_subnet.prod", + "aws_vpc.finus" + ] + } + ] + } + ], + "check_results": null +} diff --git a/deployment/scripts/install-deps.sh b/deployment/scripts/install-deps.sh new file mode 100755 index 0000000..c932239 --- /dev/null +++ b/deployment/scripts/install-deps.sh @@ -0,0 +1,30 @@ +#!/bin/bash +install() { + test -f "$(which $1 2>&1)" + if [[ ! $? -eq 0 ]]; then + echo "Installing $1" + sudo yum install $2 -y + else + echo "$1 already installed" + fi +} + +install docker docker +install node nodejs +install tmux tmux + +docker compose > /dev/null +if [[ ! $? -eq 0 ]]; then + echo "Installing docker compose" + #from https://gist.github.com/thimslugga/36019e15b2a47a48c495b661d18faa6d + sudo mkdir -p /usr/local/lib/docker/cli-plugins + sudo curl -sL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-"$(uname -m)" \ + -o /usr/local/lib/docker/cli-plugins/docker-compose + # Set ownership to root and make executable + test -f /usr/local/lib/docker/cli-plugins/docker-compose \ + && sudo chown root:root /usr/local/lib/docker/cli-plugins/docker-compose + test -f /usr/local/lib/docker/cli-plugins/docker-compose \ + && sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose +else + echo "Docker compose already installed" +fi diff --git a/deployment/scripts/start.sh b/deployment/scripts/start.sh new file mode 100755 index 0000000..fe3bfba --- /dev/null +++ b/deployment/scripts/start.sh @@ -0,0 +1,3 @@ +#!/bin/bash +./upgrade-images.sh +tmux new-session -d -s upgrade-images 'node upgrade-on-new-image.js' diff --git a/deployment/scripts/upgrade-images.sh b/deployment/scripts/upgrade-images.sh new file mode 100644 index 0000000..a5f9fde --- /dev/null +++ b/deployment/scripts/upgrade-images.sh @@ -0,0 +1,3 @@ +#!/bin/bash +docker compose down +docker compose up -d --pull always diff --git a/deployment/scripts/upgrade-on-new-image.js b/deployment/scripts/upgrade-on-new-image.js new file mode 100644 index 0000000..a501332 --- /dev/null +++ b/deployment/scripts/upgrade-on-new-image.js @@ -0,0 +1,60 @@ +//Note: this code is all chatGPT written. + +const http = require('http'); +const { spawn } = require('child_process'); + +const PORT = 4444; +const SECOND = 1000; +const COOLDOWN = 10 * SECOND; + +let lastRun = 0; +let restarting = false; + +const server = http.createServer((req, res) => { + let body = ''; + + req.on('data', chunk => { + body += chunk; + }); + + req.on('end', () => { + const now = Date.now(); + + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('OK'); + + if (now - lastRun < COOLDOWN) { + console.log(`[${new Date().toISOString()}] Webhook ignored due to cooldown`); + return + } + lastRun = now; + + if (restarting) { + console.log(`[${new Date().toISOString()}] Webhook ignored due to in-progress restart`); + return + } + restarting = true; + + console.log(`[${new Date().toISOString()}] Upgrading services...`); + const script = spawn('./upgrade-images.sh'); + + script.on('close', (code) => { + restarting = false; + if(code === 0) { + console.log(`[${new Date().toISOString()}] Finished upgrading services`); + } else { + console.log(`[${new Date().toISOString()}] Failed to upgrade services. Code: ${code}`); + } + }); + + script.on('error', (err) => { + console.error(`[${new Date().toISOString()}] Script error:`, err); + res.writeHead(500, { 'Content-Type': 'text/plain' }); + res.end('Error'); + }); + }); +}); + +server.listen(PORT, () => { + console.log(`Webhook listener running on port ${PORT}`); +}); diff --git a/deployment/.env b/deployment/services/.env similarity index 100% rename from deployment/.env rename to deployment/services/.env diff --git a/deployment/default_container.env b/deployment/services/default_container.env similarity index 100% rename from deployment/default_container.env rename to deployment/services/default_container.env diff --git a/deployment/docker-compose.yml b/deployment/services/docker-compose.yml similarity index 100% rename from deployment/docker-compose.yml rename to deployment/services/docker-compose.yml diff --git a/deployment/secrets.env b/deployment/services/secrets.env similarity index 100% rename from deployment/secrets.env rename to deployment/services/secrets.env diff --git a/deployment/webserver/.env b/deployment/webserver/.env new file mode 100644 index 0000000..5b234a0 --- /dev/null +++ b/deployment/webserver/.env @@ -0,0 +1,3 @@ +#THIS IS NOT .gitignored'D! +DOCKER_HUB_USERNAME=measureonecodetwice +RELEASE_VERSION=latest diff --git a/deployment/webserver/docker-compose.yml b/deployment/webserver/docker-compose.yml new file mode 100644 index 0000000..353d002 --- /dev/null +++ b/deployment/webserver/docker-compose.yml @@ -0,0 +1,5 @@ +services: + webserver: + image: $DOCKER_HUB_USERNAME/finus-webserver:$RELEASE_VERSION + ports: + - "80:80" From aa54a2d94d608ec9458cdfd41fbf2cd5b8b7b485 Mon Sep 17 00:00:00 2001 From: Logan Date: Thu, 26 Mar 2026 17:43:18 -0500 Subject: [PATCH 03/17] Make upgrade-images.sh executable --- deployment/scripts/upgrade-images.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 deployment/scripts/upgrade-images.sh diff --git a/deployment/scripts/upgrade-images.sh b/deployment/scripts/upgrade-images.sh old mode 100644 new mode 100755 From 6ac006df598df03f5a2f6532bcb5fdd439757888 Mon Sep 17 00:00:00 2001 From: Logan Date: Thu, 26 Mar 2026 17:46:49 -0500 Subject: [PATCH 04/17] add sudo to docker commands --- deployment/scripts/upgrade-images.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) mode change 100755 => 100644 deployment/scripts/upgrade-images.sh diff --git a/deployment/scripts/upgrade-images.sh b/deployment/scripts/upgrade-images.sh old mode 100755 new mode 100644 index a5f9fde..a96c7ab --- a/deployment/scripts/upgrade-images.sh +++ b/deployment/scripts/upgrade-images.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker compose down -docker compose up -d --pull always +sudo docker compose down +sudo docker compose up -d --pull always From fd2767312c2c352767c4d4c9e74d5899fa1d04ce Mon Sep 17 00:00:00 2001 From: Logan Date: Thu, 26 Mar 2026 17:52:31 -0500 Subject: [PATCH 05/17] added readme.md --- deployment/README.md | 4 ++++ deployment/webserver/.env | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 deployment/README.md diff --git a/deployment/README.md b/deployment/README.md new file mode 100644 index 0000000..9ed169f --- /dev/null +++ b/deployment/README.md @@ -0,0 +1,4 @@ +The `services` and `webserver` folders contain all the files to deploy Finus microservices and webserver respectively. +To run a service +1. `cp scripts/*` to either `services` or `webserver` +2. in whichever folder you chose run `./start.sh` diff --git a/deployment/webserver/.env b/deployment/webserver/.env index 5b234a0..c3ae092 100644 --- a/deployment/webserver/.env +++ b/deployment/webserver/.env @@ -1,3 +1,3 @@ -#THIS IS NOT .gitignored'D! +#THIS ENV FILE IS NOT GIT IGNORED DOCKER_HUB_USERNAME=measureonecodetwice RELEASE_VERSION=latest From d594494efcb5013f4c4aae40d512f4673e02f583 Mon Sep 17 00:00:00 2001 From: Logan Date: Thu, 26 Mar 2026 17:55:02 -0500 Subject: [PATCH 06/17] make upgrade-images.sh executable --- deployment/scripts/upgrade-images.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 deployment/scripts/upgrade-images.sh diff --git a/deployment/scripts/upgrade-images.sh b/deployment/scripts/upgrade-images.sh old mode 100644 new mode 100755 From f3f8dd167f35bdddcfab090e33e49f841a16434c Mon Sep 17 00:00:00 2001 From: Logan Date: Thu, 26 Mar 2026 17:56:53 -0500 Subject: [PATCH 07/17] start docker daemon in start.sh --- deployment/scripts/start.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/deployment/scripts/start.sh b/deployment/scripts/start.sh index fe3bfba..5d3b009 100755 --- a/deployment/scripts/start.sh +++ b/deployment/scripts/start.sh @@ -1,3 +1,4 @@ #!/bin/bash +sudo systemctl start docker ./upgrade-images.sh tmux new-session -d -s upgrade-images 'node upgrade-on-new-image.js' From 44753584d6cdb4101200aebd63ee26a2e9dd08a7 Mon Sep 17 00:00:00 2001 From: Logan Date: Fri, 27 Mar 2026 10:55:59 -0500 Subject: [PATCH 08/17] make everything use constants to get ready for AWS hosting --- app/client/src/App.tsx | 7 ++----- app/client/src/api/Account.ts | 3 ++- app/client/src/api/AuthAPI.ts | 4 ++-- app/client/src/api/Income.ts | 3 ++- app/client/src/api/Transaction.ts | 3 ++- app/client/src/utils/constants.ts | 8 ++------ app/server/services/python/analytics/src/main.py | 12 ++++++++++-- app/server/services/ts/common/expressUtils.ts | 9 ++++++++- 8 files changed, 30 insertions(+), 19 deletions(-) diff --git a/app/client/src/App.tsx b/app/client/src/App.tsx index 5db48fb..96e80ad 100644 --- a/app/client/src/App.tsx +++ b/app/client/src/App.tsx @@ -15,10 +15,7 @@ import AppLayout from "./components/AppLayout.tsx"; //import { requestAuth } from "./api/AuthAPI"; //import { resolveUserFromToken } from "./utils/token"; // import type { AuthApiResponse, AuthSession, AuthUser } from "./pages/authTypes"; - -const SESSION_STORAGE_KEY = "finus-session"; -const API_BASE_URL = - import.meta.env.VITE_API_BASE_URL ?? "http://localhost:3000"; +import { BASE_URL, SESSION_STORAGE_KEY } from "@/utils/constants"; function isValidAuthUser(value: unknown): value is AuthUser { if (!value || typeof value !== "object") { @@ -106,7 +103,7 @@ async function requestAuth( payload: Record, ): Promise { try { - const response = await fetch(`${API_BASE_URL}${path}`, { + const response = await fetch(`${BASE_URL}${path}`, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/app/client/src/api/Account.ts b/app/client/src/api/Account.ts index 9177437..3e5ca58 100644 --- a/app/client/src/api/Account.ts +++ b/app/client/src/api/Account.ts @@ -1,8 +1,9 @@ import type { updateResponse } from "../types/responseTypes"; import type { Account } from "../types/AccountType"; import type { AuthSession } from "@/types/authTypes"; +import { BASE_URL } from "@/utils/constants"; -const requestUrl = "http://localhost:3000/api/accounts"; +const requestUrl = `${BASE_URL}/api/accounts`; //Sends a request to get different accounts the user has export async function getUserAccounts( diff --git a/app/client/src/api/AuthAPI.ts b/app/client/src/api/AuthAPI.ts index 36f2c27..09a3652 100644 --- a/app/client/src/api/AuthAPI.ts +++ b/app/client/src/api/AuthAPI.ts @@ -1,12 +1,12 @@ import type { AuthApiResponse } from "@/types/authTypes"; -import { API_BASE_URL } from "@/utils/constants"; +import { BASE_URL } from "@/utils/constants"; export async function requestAuth( path: string, payload: Record, ): Promise { try { - const response = await fetch(`${API_BASE_URL}${path}`, { + const response = await fetch(`${BASE_URL}${path}`, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/app/client/src/api/Income.ts b/app/client/src/api/Income.ts index 7d66c89..dff2958 100644 --- a/app/client/src/api/Income.ts +++ b/app/client/src/api/Income.ts @@ -1,8 +1,9 @@ import type { updateResponse } from "../types/responseTypes"; import { type Income } from "../types/IncomeType"; import type { AuthSession } from "@/types/authTypes"; +import { BASE_URL } from "@/utils/constants"; -const requestUrl = "http://localhost:3000/api/income"; +const requestUrl = `${BASE_URL}/api/income`; export async function getIncome(session: AuthSession): Promise { try { diff --git a/app/client/src/api/Transaction.ts b/app/client/src/api/Transaction.ts index 3eb91f7..e69460d 100644 --- a/app/client/src/api/Transaction.ts +++ b/app/client/src/api/Transaction.ts @@ -2,8 +2,9 @@ import type { AuthSession } from "@/types/authTypes"; import type { updateResponse } from "../types/responseTypes"; import type { Transaction } from "../types/Transaction"; import type { TransactionDraft } from "@/utils/ConvertTransaction"; +import { BASE_URL } from "@/utils/constants"; -const requestUrl = "http://localhost:3000/api/transactions"; +const requestUrl = `${BASE_URL}/api/transactions`; //Sends a GET request to get the list of user transactions for the account export async function getTransactions( diff --git a/app/client/src/utils/constants.ts b/app/client/src/utils/constants.ts index 08d5070..e80185f 100644 --- a/app/client/src/utils/constants.ts +++ b/app/client/src/utils/constants.ts @@ -1,7 +1,3 @@ -export const BASE_URL = "http://localhost:3000"; //change this to the public link once microservices are hosted on AWS - -export const SIDEBAR_WIDTH = 260; - +export const BASE_URL = "http://localhost:3000"; export const SESSION_STORAGE_KEY = "finus-session"; -export const API_BASE_URL = - import.meta.env.VITE_API_BASE_URL ?? "http://localhost:3000"; +export const SIDEBAR_WIDTH = 260; diff --git a/app/server/services/python/analytics/src/main.py b/app/server/services/python/analytics/src/main.py index 240a046..0c71fcf 100644 --- a/app/server/services/python/analytics/src/main.py +++ b/app/server/services/python/analytics/src/main.py @@ -14,7 +14,15 @@ app = FastAPI() # CORS setup -origins = ["http://localhost:8080", "http://localhost:3000", "http://127.0.0.1:8080", "http://127.0.0.1:3000"] +origins = [ + "http://localhost:8080", + "http://localhost:3000", + "http://127.0.0.1:8080", + "http://127.0.0.1:3000", + "http://18.190.215.135", #prod webserver + "http://3.142.125.202", #dev webserver +] + app.add_middleware( CORSMiddleware, allow_origins=origins, @@ -121,4 +129,4 @@ async def get_budget( if __name__ == "__main__": port = int(os.getenv("PORT", 8000)) - uvicorn.run(app, port=port) \ No newline at end of file + uvicorn.run(app, port=port) diff --git a/app/server/services/ts/common/expressUtils.ts b/app/server/services/ts/common/expressUtils.ts index 30d7c2d..8fc8d58 100644 --- a/app/server/services/ts/common/expressUtils.ts +++ b/app/server/services/ts/common/expressUtils.ts @@ -1,7 +1,14 @@ import cors from "cors"; const defaultMethods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"]; -const defaultOrigins = ["http://localhost", "http://localhost:8080"]; +const defaultOrigins = [ + "http://localhost", + "http://localhost:8080", + "http://3.150.23.72", + "http://18.190.215.135", //prod webserver + "http://3.142.125.202", //dev webserver +]; + export function buildCorsConfig(opts?: { origins?: string | string[]; methods?: string[]; From 1a7a8695f1c3308a4ed95de27fa242ddd0b5119a Mon Sep 17 00:00:00 2001 From: Logan Date: Fri, 27 Mar 2026 11:15:16 -0500 Subject: [PATCH 09/17] localhost -> api_gateway_url in config --- app/client/src/utils/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/src/utils/constants.ts b/app/client/src/utils/constants.ts index e80185f..246d51d 100644 --- a/app/client/src/utils/constants.ts +++ b/app/client/src/utils/constants.ts @@ -1,3 +1,3 @@ -export const BASE_URL = "http://localhost:3000"; +export const BASE_URL = process.env.API_GATEWAY_URL; export const SESSION_STORAGE_KEY = "finus-session"; export const SIDEBAR_WIDTH = 260; From 9d3d70e0d887aff825529632926ed4df097ab70a Mon Sep 17 00:00:00 2001 From: Logan Date: Fri, 27 Mar 2026 11:31:23 -0500 Subject: [PATCH 10/17] added HTTP security group for webserver --- deployment/infrastructure/main.tf | 60 ++++++-- .../infrastructure/modules/finus/main.tf | 10 +- deployment/infrastructure/terraform.tfstate | 122 ++++++++++++++++- .../infrastructure/terraform.tfstate.backup | 129 +++++++++++++++++- 4 files changed, 299 insertions(+), 22 deletions(-) diff --git a/deployment/infrastructure/main.tf b/deployment/infrastructure/main.tf index b4b96b1..01ada4e 100644 --- a/deployment/infrastructure/main.tf +++ b/deployment/infrastructure/main.tf @@ -24,6 +24,34 @@ resource "aws_internet_gateway" "default" { vpc_id = aws_vpc.finus.id } +resource "aws_security_group" "webserver" { + name = "finus-webserver" + vpc_id = aws_vpc.finus.id + + ingress { + description = "HTTP from anywhere" + cidr_blocks = ["0.0.0.0/0"] + from_port = "80" + to_port = "80" + protocol = "tcp" + } +} + +resource "aws_security_group" "services" { + name = "finus-services" + vpc_id = aws_vpc.finus.id + + ingress { + description = "TCP 3000 from anywhere" + cidr_blocks = ["0.0.0.0/0"] + from_port = "3000" + to_port = "3000" + protocol = "tcp" + } +} + + + resource "aws_security_group" "default" { name = "finus-default" vpc_id = aws_vpc.finus.id @@ -66,24 +94,28 @@ resource "aws_route_table" "route_table" { module "prod" { source = "./modules/finus" - environment_name = "prod" - ssh_key_pair_name = "finus-dev" - security_group_ids = [aws_security_group.default.id] - subnet_id = aws_subnet.prod.id - vpc_id = aws_vpc.finus.id - gateway_id = aws_internet_gateway.default.id - route_table_id = aws_route_table.route_table.id + environment_name = "prod" + ssh_key_pair_name = "finus-dev" + security_group_ids = [aws_security_group.default.id] + webserver_security_group_ids = [aws_security_group.webserver.id] + backend_security_group_ids = [aws_security_group.services.id] + subnet_id = aws_subnet.prod.id + vpc_id = aws_vpc.finus.id + gateway_id = aws_internet_gateway.default.id + route_table_id = aws_route_table.route_table.id } module "dev" { source = "./modules/finus" - environment_name = "dev" - ssh_key_pair_name = "finus-dev" - security_group_ids = [aws_security_group.default.id] - subnet_id = aws_subnet.dev.id - vpc_id = aws_vpc.finus.id - gateway_id = aws_internet_gateway.default.id - route_table_id = aws_route_table.route_table.id + environment_name = "dev" + ssh_key_pair_name = "finus-dev" + security_group_ids = [aws_security_group.default.id] + webserver_security_group_ids = [aws_security_group.webserver.id] + backend_security_group_ids = [aws_security_group.services.id] + subnet_id = aws_subnet.dev.id + vpc_id = aws_vpc.finus.id + gateway_id = aws_internet_gateway.default.id + route_table_id = aws_route_table.route_table.id } diff --git a/deployment/infrastructure/modules/finus/main.tf b/deployment/infrastructure/modules/finus/main.tf index cf683eb..6ad2b7d 100644 --- a/deployment/infrastructure/modules/finus/main.tf +++ b/deployment/infrastructure/modules/finus/main.tf @@ -7,6 +7,12 @@ variable "ssh_key_pair_name" { variable "security_group_ids" { type = list(string) } +variable "webserver_security_group_ids" { + type = list(string) +} +variable "backend_security_group_ids" { + type = list(string) +} variable "subnet_id" { type = string } @@ -34,7 +40,7 @@ resource "aws_instance" "backend" { ami = data.aws_ami.amazon_linux.id subnet_id = var.subnet_id key_name = var.ssh_key_pair_name - vpc_security_group_ids = var.security_group_ids + vpc_security_group_ids = concat(var.security_group_ids, var.backend_security_group_ids) tags = { Name = join("", ["finus-", var.environment_name, "-backend"]) @@ -46,7 +52,7 @@ resource "aws_instance" "webserver" { ami = data.aws_ami.amazon_linux.id subnet_id = var.subnet_id key_name = var.ssh_key_pair_name - vpc_security_group_ids = var.security_group_ids + vpc_security_group_ids = concat(var.security_group_ids, var.webserver_security_group_ids) tags = { Name = join("", ["finus-", var.environment_name, "-webserver"]) diff --git a/deployment/infrastructure/terraform.tfstate b/deployment/infrastructure/terraform.tfstate index ef3d053..8a641c7 100644 --- a/deployment/infrastructure/terraform.tfstate +++ b/deployment/infrastructure/terraform.tfstate @@ -1,7 +1,7 @@ { "version": 4, "terraform_version": "1.14.7", - "serial": 112, + "serial": 122, "lineage": "ae3e9505-c9b9-9084-580e-4ba9a7f0ba2f", "outputs": {}, "resources": [ @@ -164,6 +164,110 @@ } ] }, + { + "mode": "managed", + "type": "aws_security_group", + "name": "services", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:security-group/sg-0e428a46b850e036a", + "description": "Managed by Terraform", + "egress": [], + "id": "sg-0e428a46b850e036a", + "ingress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "TCP 3000 from anywhere", + "from_port": 3000, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "tcp", + "security_groups": [], + "self": false, + "to_port": 3000 + } + ], + "name": "finus-services", + "name_prefix": "", + "owner_id": "177559564758", + "region": "us-east-2", + "revoke_rules_on_delete": false, + "tags": {}, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "sg-0e428a46b850e036a", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6OTAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIxIn0=", + "dependencies": [ + "aws_vpc.finus" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_security_group", + "name": "webserver", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:security-group/sg-0973f01390a9d839a", + "description": "Managed by Terraform", + "egress": [], + "id": "sg-0973f01390a9d839a", + "ingress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "HTTP from anywhere", + "from_port": 80, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "tcp", + "security_groups": [], + "self": false, + "to_port": 80 + } + ], + "name": "finus-webserver", + "name_prefix": "", + "owner_id": "177559564758", + "region": "us-east-2", + "revoke_rules_on_delete": false, + "tags": null, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "sg-0973f01390a9d839a", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6OTAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIxIn0=", + "dependencies": [ + "aws_vpc.finus" + ] + } + ] + }, { "mode": "managed", "type": "aws_subnet", @@ -447,6 +551,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", "dependencies": [ "aws_security_group.default", + "aws_security_group.services", "aws_subnet.dev", "aws_vpc.finus", "module.dev.aws_instance.backend", @@ -499,6 +604,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", "dependencies": [ "aws_security_group.default", + "aws_security_group.webserver", "aws_subnet.dev", "aws_vpc.finus", "module.dev.aws_instance.webserver", @@ -641,7 +747,8 @@ "user_data_replace_on_change": false, "volume_tags": null, "vpc_security_group_ids": [ - "sg-0c03eb59bd6270fcf" + "sg-0c03eb59bd6270fcf", + "sg-0e428a46b850e036a" ] }, "sensitive_attributes": [], @@ -654,6 +761,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", "dependencies": [ "aws_security_group.default", + "aws_security_group.services", "aws_subnet.dev", "aws_vpc.finus", "module.dev.data.aws_ami.amazon_linux" @@ -795,6 +903,7 @@ "user_data_replace_on_change": false, "volume_tags": null, "vpc_security_group_ids": [ + "sg-0973f01390a9d839a", "sg-0c03eb59bd6270fcf" ] }, @@ -808,6 +917,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", "dependencies": [ "aws_security_group.default", + "aws_security_group.webserver", "aws_subnet.dev", "aws_vpc.finus", "module.dev.data.aws_ami.amazon_linux" @@ -974,6 +1084,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", "dependencies": [ "aws_security_group.default", + "aws_security_group.services", "aws_subnet.prod", "aws_vpc.finus", "module.prod.aws_instance.backend", @@ -1026,6 +1137,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", "dependencies": [ "aws_security_group.default", + "aws_security_group.webserver", "aws_subnet.prod", "aws_vpc.finus", "module.prod.aws_instance.webserver", @@ -1168,7 +1280,8 @@ "user_data_replace_on_change": false, "volume_tags": null, "vpc_security_group_ids": [ - "sg-0c03eb59bd6270fcf" + "sg-0c03eb59bd6270fcf", + "sg-0e428a46b850e036a" ] }, "sensitive_attributes": [], @@ -1181,6 +1294,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", "dependencies": [ "aws_security_group.default", + "aws_security_group.services", "aws_subnet.prod", "aws_vpc.finus", "module.prod.data.aws_ami.amazon_linux" @@ -1322,6 +1436,7 @@ "user_data_replace_on_change": false, "volume_tags": null, "vpc_security_group_ids": [ + "sg-0973f01390a9d839a", "sg-0c03eb59bd6270fcf" ] }, @@ -1335,6 +1450,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", "dependencies": [ "aws_security_group.default", + "aws_security_group.webserver", "aws_subnet.prod", "aws_vpc.finus", "module.prod.data.aws_ami.amazon_linux" diff --git a/deployment/infrastructure/terraform.tfstate.backup b/deployment/infrastructure/terraform.tfstate.backup index 6d85dc9..cdbe6d6 100644 --- a/deployment/infrastructure/terraform.tfstate.backup +++ b/deployment/infrastructure/terraform.tfstate.backup @@ -1,7 +1,7 @@ { "version": 4, "terraform_version": "1.14.7", - "serial": 110, + "serial": 117, "lineage": "ae3e9505-c9b9-9084-580e-4ba9a7f0ba2f", "outputs": {}, "resources": [ @@ -113,6 +113,19 @@ ], "id": "sg-0c03eb59bd6270fcf", "ingress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "Docker webhook", + "from_port": 4444, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "tcp", + "security_groups": [], + "self": false, + "to_port": 4444 + }, { "cidr_blocks": [ "0.0.0.0/0" @@ -151,6 +164,106 @@ } ] }, + { + "mode": "managed", + "type": "aws_security_group", + "name": "services", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "schema_version": 1, + "attributes": { + "arn": "arn:aws:ec2:us-east-2:177559564758:security-group/sg-0e428a46b850e036a", + "description": "Managed by Terraform", + "egress": [], + "id": "sg-0e428a46b850e036a", + "ingress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "TCP 3000 from anywhere", + "from_port": 3000, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "tcp", + "security_groups": [], + "self": false, + "to_port": 3000 + } + ], + "name": "finus-services", + "name_prefix": "", + "owner_id": "177559564758", + "region": "us-east-2", + "revoke_rules_on_delete": false, + "tags": null, + "tags_all": {}, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "identity": { + "account_id": "177559564758", + "id": "sg-0e428a46b850e036a", + "region": "us-east-2" + }, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6OTAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIxIn0=", + "dependencies": [ + "aws_vpc.finus" + ] + } + ] + }, + { + "mode": "managed", + "type": "aws_security_group", + "name": "webserver", + "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", + "instances": [ + { + "status": "tainted", + "schema_version": 1, + "attributes": { + "arn": null, + "description": "Managed by Terraform", + "egress": [], + "id": "sg-09cfeb172160ecaa3", + "ingress": [ + { + "cidr_blocks": [ + "0.0.0.0/0" + ], + "description": "HTTP from anywhere", + "from_port": 80, + "ipv6_cidr_blocks": [], + "prefix_list_ids": [], + "protocol": "http", + "security_groups": [], + "self": false, + "to_port": 80 + } + ], + "name": "finus-webserver", + "name_prefix": null, + "owner_id": null, + "region": "us-east-2", + "revoke_rules_on_delete": false, + "tags": null, + "tags_all": null, + "timeouts": null, + "vpc_id": "vpc-0e13d5c774ed2b3c8" + }, + "sensitive_attributes": [], + "identity_schema_version": 0, + "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6OTAwMDAwMDAwMDAwfSwic2NoZW1hX3ZlcnNpb24iOiIxIn0=", + "dependencies": [ + "aws_vpc.finus" + ] + } + ] + }, { "mode": "managed", "type": "aws_subnet", @@ -434,6 +547,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", "dependencies": [ "aws_security_group.default", + "aws_security_group.services", "aws_subnet.dev", "aws_vpc.finus", "module.dev.aws_instance.backend", @@ -486,6 +600,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", "dependencies": [ "aws_security_group.default", + "aws_security_group.webserver", "aws_subnet.dev", "aws_vpc.finus", "module.dev.aws_instance.webserver", @@ -628,7 +743,8 @@ "user_data_replace_on_change": false, "volume_tags": null, "vpc_security_group_ids": [ - "sg-0c03eb59bd6270fcf" + "sg-0c03eb59bd6270fcf", + "sg-0e428a46b850e036a" ] }, "sensitive_attributes": [], @@ -641,6 +757,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", "dependencies": [ "aws_security_group.default", + "aws_security_group.services", "aws_subnet.dev", "aws_vpc.finus", "module.dev.data.aws_ami.amazon_linux" @@ -795,6 +912,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", "dependencies": [ "aws_security_group.default", + "aws_security_group.webserver", "aws_subnet.dev", "aws_vpc.finus", "module.dev.data.aws_ami.amazon_linux" @@ -961,6 +1079,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", "dependencies": [ "aws_security_group.default", + "aws_security_group.services", "aws_subnet.prod", "aws_vpc.finus", "module.prod.aws_instance.backend", @@ -1013,6 +1132,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiZGVsZXRlIjoxODAwMDAwMDAwMDAsInJlYWQiOjkwMDAwMDAwMDAwMCwidXBkYXRlIjozMDAwMDAwMDAwMDB9fQ==", "dependencies": [ "aws_security_group.default", + "aws_security_group.webserver", "aws_subnet.prod", "aws_vpc.finus", "module.prod.aws_instance.webserver", @@ -1155,7 +1275,8 @@ "user_data_replace_on_change": false, "volume_tags": null, "vpc_security_group_ids": [ - "sg-0c03eb59bd6270fcf" + "sg-0c03eb59bd6270fcf", + "sg-0e428a46b850e036a" ] }, "sensitive_attributes": [], @@ -1168,6 +1289,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", "dependencies": [ "aws_security_group.default", + "aws_security_group.services", "aws_subnet.prod", "aws_vpc.finus", "module.prod.data.aws_ami.amazon_linux" @@ -1322,6 +1444,7 @@ "private": "eyJlMmJmYjczMC1lY2FhLTExZTYtOGY4OC0zNDM2M2JjN2M0YzAiOnsiY3JlYXRlIjo2MDAwMDAwMDAwMDAsImRlbGV0ZSI6MTIwMDAwMDAwMDAwMCwicmVhZCI6OTAwMDAwMDAwMDAwLCJ1cGRhdGUiOjYwMDAwMDAwMDAwMH0sInNjaGVtYV92ZXJzaW9uIjoiMiJ9", "dependencies": [ "aws_security_group.default", + "aws_security_group.webserver", "aws_subnet.prod", "aws_vpc.finus", "module.prod.data.aws_ami.amazon_linux" From 9992ba265edc6defab44389a93007eb99ab9c021 Mon Sep 17 00:00:00 2001 From: Logan Date: Fri, 27 Mar 2026 12:21:10 -0500 Subject: [PATCH 11/17] finishing touches --- app/client/.env | 2 +- app/client/src/utils/constants.ts | 2 +- app/server/.env | 2 +- app/server/services/ts/auth/src/index.ts | 2 +- app/server/services/ts/common/expressUtils.ts | 10 +++------- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/app/client/.env b/app/client/.env index f955b13..e0ce3dd 100644 --- a/app/client/.env +++ b/app/client/.env @@ -3,4 +3,4 @@ #docker-file DOCKER_HUB_USERNAME=measureonecodetwice -RELEASE_VERSION=0.0.1 +RELEASE_VERSION=latest diff --git a/app/client/src/utils/constants.ts b/app/client/src/utils/constants.ts index 246d51d..bb3cdfa 100644 --- a/app/client/src/utils/constants.ts +++ b/app/client/src/utils/constants.ts @@ -1,3 +1,3 @@ -export const BASE_URL = process.env.API_GATEWAY_URL; +export const BASE_URL = "http://3.150.23.72:3000"; export const SESSION_STORAGE_KEY = "finus-session"; export const SIDEBAR_WIDTH = 260; diff --git a/app/server/.env b/app/server/.env index 314a0ae..b747e28 100644 --- a/app/server/.env +++ b/app/server/.env @@ -3,7 +3,7 @@ #CI DOCKER_HUB_USERNAME=measureonecodetwice -RELEASE_VERSION=local +RELEASE_VERSION=latest #DEPLOYMENT MYSQL_ROOT_PASSWORD=dummypw diff --git a/app/server/services/ts/auth/src/index.ts b/app/server/services/ts/auth/src/index.ts index 147bc3f..be6111a 100644 --- a/app/server/services/ts/auth/src/index.ts +++ b/app/server/services/ts/auth/src/index.ts @@ -9,8 +9,8 @@ import { getConnectionPool } from "@/sqlUtil"; const pool = getConnectionPool(); const app = express(); -app.use(express.json()); app.use(buildCorsConfig()); +app.use(express.json()); app.post("/signup", async (req, res) => { let body: SignupBody; diff --git a/app/server/services/ts/common/expressUtils.ts b/app/server/services/ts/common/expressUtils.ts index 8fc8d58..4ab42ca 100644 --- a/app/server/services/ts/common/expressUtils.ts +++ b/app/server/services/ts/common/expressUtils.ts @@ -1,20 +1,16 @@ import cors from "cors"; const defaultMethods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"]; -const defaultOrigins = [ +const origins = [ "http://localhost", "http://localhost:8080", - "http://3.150.23.72", "http://18.190.215.135", //prod webserver "http://3.142.125.202", //dev webserver ]; -export function buildCorsConfig(opts?: { - origins?: string | string[]; - methods?: string[]; -}) { +export function buildCorsConfig(opts?: { methods?: string[] }) { return cors({ - origin: opts?.origin ?? defaultOrigins, + origin: origins, methods: opts?.methods ?? defaultMethods, credentials: true, allowedHeaders: ["Content-Type", "Authorization"], From 6e9d725496deff1f1a663a3bc212d06dfd44db88 Mon Sep 17 00:00:00 2001 From: Logan Date: Fri, 27 Mar 2026 12:25:38 -0500 Subject: [PATCH 12/17] script to remove all workflow runs - useful to get rid of troubleshooting runs --- .github/workflows/delete_all_workflow_runs.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100755 .github/workflows/delete_all_workflow_runs.sh diff --git a/.github/workflows/delete_all_workflow_runs.sh b/.github/workflows/delete_all_workflow_runs.sh new file mode 100755 index 0000000..8dd8e80 --- /dev/null +++ b/.github/workflows/delete_all_workflow_runs.sh @@ -0,0 +1,11 @@ +REPO="MeasureOneCodeTwice/Finus" + +# Optional: count runs first +gh api --paginate "/repos/$REPO/actions/runs?per_page=100" --jq '.workflow_runs[].id' | wc -l + +# Delete everything +gh api --paginate "/repos/$REPO/actions/runs?per_page=100" --jq '.workflow_runs[].id' \ +| while read -r RUN_ID; do + echo "Deleting run $RUN_ID" + gh api -X DELETE "/repos/$REPO/actions/runs/$RUN_ID" >/dev/null + done From 6d7a8be7a908d264f5bcf4896152d64045fef705 Mon Sep 17 00:00:00 2001 From: Roman Tebel Date: Fri, 27 Mar 2026 13:13:27 -0500 Subject: [PATCH 13/17] disabling db reliant integration tests --- .github/workflows/run_tests.yml | 3 +-- app/server/docker-compose.test.yml | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 192b8a0..1a6d321 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -32,8 +32,7 @@ jobs: analytics-test \ market-test \ market-mutation-test \ - market-integration-test \ - user-integration-test + market-integration-test do echo "::group::Running $service" if ! docker compose -f docker-compose.yml -f docker-compose.test.yml run --rm "$service"; then diff --git a/app/server/docker-compose.test.yml b/app/server/docker-compose.test.yml index 096f10c..76428b5 100644 --- a/app/server/docker-compose.test.yml +++ b/app/server/docker-compose.test.yml @@ -58,17 +58,17 @@ services: market: condition: service_started - user-integration-test: - build: - context: ./services/ts - dockerfile: user/Dockerfile.integration.test - env_file: - - ./default_container.env - environment: - - JWT_SECRET + # user-integration-test: + # build: + # context: ./services/ts + # dockerfile: user/Dockerfile.integration.test + # env_file: + # - ./default_container.env + # environment: + # - JWT_SECRET - depends_on: - database: - condition: service_healthy - api-gateway: - condition: service_started + # depends_on: + # database: + # condition: service_healthy + # api-gateway: + # condition: service_started From 746b14cf46362492e10a18db593eb61adbab6ac9 Mon Sep 17 00:00:00 2001 From: Logan Date: Fri, 27 Mar 2026 14:00:59 -0500 Subject: [PATCH 14/17] temp --- app/client/src/App.tsx | 2 +- app/server/services/ts/common/expressUtils.ts | 2 +- deployment/services/default_container.env | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/client/src/App.tsx b/app/client/src/App.tsx index 128d4d3..cc953f1 100644 --- a/app/client/src/App.tsx +++ b/app/client/src/App.tsx @@ -143,7 +143,7 @@ function App() { async function syncServerResetKey() { try { - const response = await fetch(`${API_BASE_URL}/client-state/reset-key`); + const response = await fetch(`${BASE_URL}/client-state/reset-key`); const data = (await response.json().catch(() => null)) as { resetKey?: unknown; } | null; diff --git a/app/server/services/ts/common/expressUtils.ts b/app/server/services/ts/common/expressUtils.ts index 4ab42ca..e9b0bc6 100644 --- a/app/server/services/ts/common/expressUtils.ts +++ b/app/server/services/ts/common/expressUtils.ts @@ -24,7 +24,7 @@ export async function handleServerError( ) { await func().catch((e) => res.status(500).json({ - message: message ?? e.message, + message: e.message ?? message, }), ); } diff --git a/deployment/services/default_container.env b/deployment/services/default_container.env index 6800694..ce8659e 100644 --- a/deployment/services/default_container.env +++ b/deployment/services/default_container.env @@ -4,6 +4,6 @@ USER_SERVICE_ADDR=http://user:8000 ANALYTICS_SERVICE_ADDR=http://analytics:8000 MARKET_SERVICE_ADDR=http://market:8000 -MYSQL_HOST=database +MYSQL_HOST=finus-dev-database-1.cluster-cv8mki0o4cg9.us-east-2.rds.amazonaws.com MYSQL_PORT=3306 DB_NAME=finus From ac032c29fd5a915d8fd15125f09dbca80122b30a Mon Sep 17 00:00:00 2001 From: Logan Date: Fri, 27 Mar 2026 12:55:36 -0500 Subject: [PATCH 15/17] removed node modules in server directory --- .../vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 app/server/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json diff --git a/app/server/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json b/app/server/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json deleted file mode 100644 index c418db7..0000000 --- a/app/server/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +++ /dev/null @@ -1 +0,0 @@ -{"version":"4.1.1","results":[[":services/ts/user/test/logic_snapshot.test.ts",{"duration":24.460399999999993,"failed":true}],[":services/ts/user/test/dates.test.ts",{"duration":28.86179999999996,"failed":true}],[":services/ts/user/test/logic_expenses.test.ts",{"duration":14.341799999999978,"failed":false}],[":services/ts/auth/test/logic.test.ts",{"duration":0,"failed":true}],[":services/ts/user/test/logic_transactions.test.ts",{"duration":8.949200000000019,"failed":false}],[":services/ts/auth/test/parsing.test.ts",{"duration":0,"failed":true}],[":services/ts/auth/test/validation.test.ts",{"duration":6.108399999999989,"failed":false}]]} \ No newline at end of file From d5f1396d78d9a44c24107561cdde3e24e94b5dc2 Mon Sep 17 00:00:00 2001 From: Logan Date: Wed, 1 Apr 2026 15:01:46 -0500 Subject: [PATCH 16/17] Changed release version to local so ci-pipeline pulls uses newly built images for test base images. --- app/server/.env | 2 +- app/server/services/ts/user/bun.lockb | Bin 216656 -> 2353 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/server/.env b/app/server/.env index 3bff94a..a885f00 100644 --- a/app/server/.env +++ b/app/server/.env @@ -5,7 +5,7 @@ API_GATEWAY_PORT=3000 #CI DOCKER_HUB_USERNAME=measureonecodetwice -RELEASE_VERSION=latest +RELEASE_VERSION=local #DEPLOYMENT MYSQL_ROOT_PASSWORD=dummypw diff --git a/app/server/services/ts/user/bun.lockb b/app/server/services/ts/user/bun.lockb index fca2b540b9cabcfb19c28df5dd844842152af8d7..9f17393e03ce2de174baf2a01c647a97821dd9c5 100755 GIT binary patch delta 531 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

cK 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 From cb369801604c3ac5d4041b5cf0cbdf56b4f64159 Mon Sep 17 00:00:00 2001 From: Logan Date: Wed, 1 Apr 2026 21:36:47 -0500 Subject: [PATCH 17/17] add PATH back to CORS allowed methods --- app/server/services/ts/common/expressUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/server/services/ts/common/expressUtils.ts b/app/server/services/ts/common/expressUtils.ts index e9b0bc6..f4e313c 100644 --- a/app/server/services/ts/common/expressUtils.ts +++ b/app/server/services/ts/common/expressUtils.ts @@ -1,6 +1,6 @@ import cors from "cors"; -const defaultMethods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"]; +const defaultMethods = ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"]; const origins = [ "http://localhost", "http://localhost:8080",