# ── Accounts Service — Cloud Run ────────────────────────────────────────────── # Handles auth, billing, subscriptions, marketplace. # Connects to Cloud SQL via built-in Auth Proxy (Unix socket volume mount). # All three regions share the same Cloud SQL instance (us-central1). locals { accounts_labels = { "managed-by" = "terraform" "service" = "neuron-accounts" } sql_instance = google_sql_database_instance.main.connection_name } # ── Prod — us-central1 ──────────────────────────────────────────────────────── resource "google_cloud_run_v2_service" "accounts_us" { name = "accounts-prod-us" location = "us-central1" project = var.project_id ingress = "INGRESS_TRAFFIC_ALL" labels = local.accounts_labels template { service_account = google_service_account.accounts.email scaling { min_instance_count = 1 max_instance_count = 50 } containers { image = local.accounts_image resources { limits = { cpu = "1" memory = "512Mi" } cpu_idle = true } env { name = "ENV" value = "production" } env { name = "ACCOUNTS_PORT" value = "8080" } env { name = "ACCOUNTS_DATABASE_URL" value_source { secret_key_ref { secret = google_secret_manager_secret.accounts_database_url.secret_id version = "latest" } } } env { name = "JWT_SECRET" value_source { secret_key_ref { secret = google_secret_manager_secret.jwt_secret.secret_id version = "latest" } } } env { name = "STRIPE_SECRET_KEY" value_source { secret_key_ref { secret = "stripe-secret-key" version = "latest" } } } env { name = "STRIPE_WEBHOOK_SECRET" value_source { secret_key_ref { secret = "stripe-webhook-secret" version = "latest" } } } env { name = "STRIPE_PRICE_PROFESSIONAL" value_source { secret_key_ref { secret = "stripe-price-professional" version = "latest" } } } env { name = "STRIPE_PRICE_FOUNDING" value_source { secret_key_ref { secret = "stripe-price-founding" version = "latest" } } } ports { container_port = 8080 name = "http1" } startup_probe { http_get { path = "/health" port = 8080 } initial_delay_seconds = 2 timeout_seconds = 5 period_seconds = 5 failure_threshold = 10 } liveness_probe { http_get { path = "/health" port = 8080 } timeout_seconds = 5 period_seconds = 30 failure_threshold = 3 } volume_mounts { name = "cloudsql" mount_path = "/cloudsql" } } volumes { name = "cloudsql" cloud_sql_instance { instances = [local.sql_instance] } } } traffic { type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST" percent = 100 } depends_on = [ google_project_iam_member.accounts_secret_accessor, google_project_iam_member.accounts_sql_client, google_secret_manager_secret_version.accounts_database_url, google_secret_manager_secret_version.jwt_secret, ] } # ── Prod — europe-west1 ─────────────────────────────────────────────────────── resource "google_cloud_run_v2_service" "accounts_eu" { name = "accounts-prod-eu" location = "europe-west1" project = var.project_id ingress = "INGRESS_TRAFFIC_ALL" labels = local.accounts_labels template { service_account = google_service_account.accounts.email scaling { min_instance_count = 1 max_instance_count = 50 } containers { image = local.accounts_image resources { limits = { cpu = "1" memory = "512Mi" } cpu_idle = true } env { name = "ENV" value = "production" } env { name = "ACCOUNTS_PORT" value = "8080" } env { name = "ACCOUNTS_DATABASE_URL" value_source { secret_key_ref { secret = google_secret_manager_secret.accounts_database_url.secret_id version = "latest" } } } env { name = "JWT_SECRET" value_source { secret_key_ref { secret = google_secret_manager_secret.jwt_secret.secret_id version = "latest" } } } env { name = "STRIPE_SECRET_KEY" value_source { secret_key_ref { secret = "stripe-secret-key" version = "latest" } } } env { name = "STRIPE_WEBHOOK_SECRET" value_source { secret_key_ref { secret = "stripe-webhook-secret" version = "latest" } } } env { name = "STRIPE_PRICE_PROFESSIONAL" value_source { secret_key_ref { secret = "stripe-price-professional" version = "latest" } } } env { name = "STRIPE_PRICE_FOUNDING" value_source { secret_key_ref { secret = "stripe-price-founding" version = "latest" } } } ports { container_port = 8080 name = "http1" } startup_probe { http_get { path = "/health" port = 8080 } initial_delay_seconds = 2 timeout_seconds = 5 period_seconds = 5 failure_threshold = 10 } liveness_probe { http_get { path = "/health" port = 8080 } timeout_seconds = 5 period_seconds = 30 failure_threshold = 3 } volume_mounts { name = "cloudsql" mount_path = "/cloudsql" } } volumes { name = "cloudsql" cloud_sql_instance { instances = [local.sql_instance] } } } traffic { type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST" percent = 100 } depends_on = [ google_project_iam_member.accounts_secret_accessor, google_project_iam_member.accounts_sql_client, ] } # ── Prod — asia-northeast1 ──────────────────────────────────────────────────── resource "google_cloud_run_v2_service" "accounts_apac" { name = "accounts-prod-apac" location = "asia-northeast1" project = var.project_id ingress = "INGRESS_TRAFFIC_ALL" labels = local.accounts_labels template { service_account = google_service_account.accounts.email scaling { min_instance_count = 1 max_instance_count = 50 } containers { image = local.accounts_image resources { limits = { cpu = "1" memory = "512Mi" } cpu_idle = true } env { name = "ENV" value = "production" } env { name = "ACCOUNTS_PORT" value = "8080" } env { name = "ACCOUNTS_DATABASE_URL" value_source { secret_key_ref { secret = google_secret_manager_secret.accounts_database_url.secret_id version = "latest" } } } env { name = "JWT_SECRET" value_source { secret_key_ref { secret = google_secret_manager_secret.jwt_secret.secret_id version = "latest" } } } env { name = "STRIPE_SECRET_KEY" value_source { secret_key_ref { secret = "stripe-secret-key" version = "latest" } } } env { name = "STRIPE_WEBHOOK_SECRET" value_source { secret_key_ref { secret = "stripe-webhook-secret" version = "latest" } } } env { name = "STRIPE_PRICE_PROFESSIONAL" value_source { secret_key_ref { secret = "stripe-price-professional" version = "latest" } } } env { name = "STRIPE_PRICE_FOUNDING" value_source { secret_key_ref { secret = "stripe-price-founding" version = "latest" } } } ports { container_port = 8080 name = "http1" } startup_probe { http_get { path = "/health" port = 8080 } initial_delay_seconds = 2 timeout_seconds = 5 period_seconds = 5 failure_threshold = 10 } liveness_probe { http_get { path = "/health" port = 8080 } timeout_seconds = 5 period_seconds = 30 failure_threshold = 3 } volume_mounts { name = "cloudsql" mount_path = "/cloudsql" } } volumes { name = "cloudsql" cloud_sql_instance { instances = [local.sql_instance] } } } traffic { type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST" percent = 100 } depends_on = [ google_project_iam_member.accounts_secret_accessor, google_project_iam_member.accounts_sql_client, ] }