Files
2026-08-13 02:06:44 +07:00

141 lines
4.9 KiB
Python

#!/usr/bin/env python3
"""
Hämta Cibello-prod-hemligheter från AWS SSM och skriv /opt/cibello-platform/.env.
SSM är enda sanningskällan. Kör detta vid varje deploy INNAN applikationen startas om.
Inga värden loggas. set +x aktiveras automatiskt.
"""
import json
import os
import subprocess
import sys
from pathlib import Path
REGION = os.environ.get("AWS_REGION", "eu-north-1")
SSM_PREFIX = "/cibello/prod"
ENV_PATH = Path(os.environ.get("ENV_PATH", "/opt/cibello-platform/.env"))
# Icke-hemliga värden som ändå behöver styras centralt.
# Dessa kan med fördel också flyttas till SSM (String/SecureString) senare.
STATIC_CONFIG = {
"NODE_ENV": "production",
"API_PORT": "4000",
"ADMIN_PORT": "5173",
"API_BASE_URL": "https://api.cibello.app",
"VITE_API_BASE_URL": "https://api.cibello.app",
"CORS_ORIGINS": "https://cibello.app,https://admin.cibello.app,https://api.cibello.app",
"REDIS_URL": "redis://localhost:6379",
"LOG_LEVEL": "info",
"AAMOS_MODE": "gemini",
"AAMOS_API_URL": "https://amos.aamos.systems",
"EOC_URL": "https://eoc.aamos.systems",
"AAMOS_TIMEOUT_MS": "60000",
# GEMINI_MODEL hämtas från SSM (String-typ)
"GEMINI_TIMEOUT_MS": "60000",
"GEMINI_DAILY_BUDGET_USD": "25",
"ENTITLEMENT_TTL_HOURS": "24",
"ENTITLEMENT_GRACE_DAYS": "7",
"JWT_ACCESS_TTL_SECONDS": "900",
"JWT_REFRESH_TTL_SECONDS": "2592000",
"S3_MODE": "aws",
"S3_BUCKET": "cibello-production",
"S3_REGION": "eu-north-1",
"SMTP_HOST": "mail.aamos.systems",
"SMTP_PORT": "587",
"SMTP_SECURE": "false",
"SMTP_USER": "hello@cibello.app",
"MAIL_FROM": "hello@cibello.app",
"EMAIL_MODE": "smtp",
"EXPO_PUSH_ENABLED": "false",
"DATABASE_CA_CERT": "/opt/cibello-platform/certs/rds-global-bundle.pem",
}
# SSM-parameter → env-var. Värden från SSM skriver över STATIC_CONFIG om de krockar.
SSM_MAP = {
"aamos-api-key": "AAMOS_API_KEY",
"db-password": "DB_PASSWORD", # används för att bygga DATABASE_URL
"entitlement-signing-secret": "ENTITLEMENT_SIGNING_SECRET",
"eoc-push-token": "EOC_PUSH_TOKEN",
"gemini-api-key": "GEMINI_API_KEY",
"gemini-model": "GEMINI_MODEL",
"jwt-access-secret": "JWT_ACCESS_SECRET",
"jwt-refresh-secret": "JWT_REFRESH_SECRET",
"smtp-pass": "SMTP_PASS",
"ops-token": "OPS_TOKEN",
# Följande är valfria just nu men bör ligga i SSM
"google-service-account-json": "GOOGLE_SERVICE_ACCOUNT_JSON",
}
def get_parameters(names: list[str]) -> dict[str, str]:
if not names:
return {}
out: dict[str, str] = {}
invalid: list[str] = []
# AWS GetParameters max 10 namn per anrop.
for i in range(0, len(names), 10):
batch = names[i : i + 10]
cmd = [
"aws", "ssm", "get-parameters",
"--names", *batch,
"--with-decryption",
"--region", REGION,
]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
data = json.loads(result.stdout)
for p in data.get("Parameters", []):
out[p["Name"]] = p["Value"].strip()
invalid.extend(data.get("InvalidParameters", []))
for inv in invalid:
print(f"[sync-env] WARN: SSM-parameter saknas: {inv}", file=sys.stderr)
return out
def build_env(ssm: dict[str, str]) -> dict[str, str]:
env = dict(STATIC_CONFIG)
for short, var in SSM_MAP.items():
val = ssm.get(f"{SSM_PREFIX}/{short}")
if val is not None:
env[var] = val
db_password = env.get("DB_PASSWORD", "")
db_host = os.environ.get("DB_HOST", "cibello-prod-db.cvi0qcksmsfj.eu-north-1.rds.amazonaws.com")
db_name = os.environ.get("DB_NAME", "cibello")
test_db_name = os.environ.get("TEST_DB_NAME", "cibello_test")
db_user = os.environ.get("DB_USER", "cibello_admin")
db_sslmode = os.environ.get("DB_SSLMODE", "require")
if db_password:
env["DATABASE_URL"] = f"postgres://{db_user}:{db_password}@{db_host}:5432/{db_name}?sslmode={db_sslmode}"
env["TEST_DATABASE_URL"] = f"postgres://{db_user}:{db_password}@{db_host}:5432/{test_db_name}?sslmode={db_sslmode}"
# Rensa temporär nyckel
env.pop("DB_PASSWORD", None)
return env
def write_env(env: dict[str, str], dry_run: bool = False) -> None:
lines = [f"{k}={v}" for k, v in env.items()]
if dry_run:
print(f"[sync-env] DRY-RUN: skulle skriva {ENV_PATH} ({len(env)} rader)")
return
ENV_PATH.parent.mkdir(parents=True, exist_ok=True)
ENV_PATH.write_text("\n".join(lines) + "\n", encoding="utf-8")
os.chmod(ENV_PATH, 0o600)
print(f"[sync-env] skrev {ENV_PATH} ({len(env)} rader)")
def main() -> int:
dry_run = "--dry-run" in sys.argv
names = [f"{SSM_PREFIX}/{short}" for short in SSM_MAP]
ssm = get_parameters(names)
env = build_env(ssm)
write_env(env, dry_run=dry_run)
return 0
if __name__ == "__main__":
sys.exit(main())