From 91fdeed6b9f3df0258149f018ae3f2bd5cdf4586 Mon Sep 17 00:00:00 2001 From: thiennatra Date: Thu, 16 Apr 2026 12:44:56 +0700 Subject: [PATCH] CI: deploy image-only --- .gitignore | 1 + apps/router-worker/.env.example | 13 ++++ apps/tracking-api/.env.example | 22 +++++++ deploy/README.md | 7 ++- deploy/app/.env.example | 36 +++++++++++ deploy/app/docker-compose.yml | 5 +- deploy/infra/.env.example | 1 + deploy/vps/README.md | 70 +++++++++++++++++++++ deploy/vps/nginx.tracking-base.conf.example | 27 ++++++++ env/.env.shared.example | 2 +- 10 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 apps/router-worker/.env.example create mode 100644 apps/tracking-api/.env.example create mode 100644 deploy/app/.env.example create mode 100644 deploy/infra/.env.example create mode 100644 deploy/vps/README.md create mode 100644 deploy/vps/nginx.tracking-base.conf.example diff --git a/.gitignore b/.gitignore index eaa1976..d36ac81 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules .env .env.* +!**/.env.example !env/.env.shared.example env/.env.shared dist diff --git a/apps/router-worker/.env.example b/apps/router-worker/.env.example new file mode 100644 index 0000000..69e87ce --- /dev/null +++ b/apps/router-worker/.env.example @@ -0,0 +1,13 @@ +REDIS_URL=redis://localhost:6379 +DATABASE_URL=postgres://postgres:postgres@localhost:5432/tracking_base +ROUTER_QUEUE_NAME=router-deliveries +ROUTER_WORKER_NAME=router-delivery-worker +ROUTER_WORKER_CONCURRENCY=5 +META_CONVERSIONS_ENDPOINT_URL= +META_PIXEL_ID= +META_ACCESS_TOKEN= +META_TEST_EVENT_CODE= +GOOGLE_CONVERSIONS_ENDPOINT_URL= +GOOGLE_CONVERSIONS_API_KEY= +TIKTOK_EVENTS_ENDPOINT_URL= +TIKTOK_ACCESS_TOKEN= diff --git a/apps/tracking-api/.env.example b/apps/tracking-api/.env.example new file mode 100644 index 0000000..04279da --- /dev/null +++ b/apps/tracking-api/.env.example @@ -0,0 +1,22 @@ +PORT=3000 +DATABASE_URL=postgres://postgres:postgres@localhost:5432/tracking_base +TEST_DATABASE_URL=postgres://postgres:postgres@localhost:5432/tracking_base +# Router worker and queue +REDIS_URL=redis://localhost:6379/0 +ROUTER_QUEUE_NAME=router-deliveries +ROUTER_WORKER_NAME=router-delivery-worker +ROUTER_WORKER_CONCURRENCY=5 +REDIS_HOST= +REDIS_PORT=6379 +REDIS_DB=0 +REDIS_USERNAME= +REDIS_PASSWORD= +NODE_ENV=development +METRICS_ENABLED=true +TRACKING_API_AUTH_MODE=off +TRACKING_API_SECRET= +TRACKING_SIGNATURE_SKEW_SECONDS=300 +TRACKING_RATE_LIMIT_ENABLED=false +TRACKING_RATE_LIMIT_WINDOW_MS=60000 +TRACKING_RATE_LIMIT_MAX_REQUESTS=120 +TRACKING_CORS_ALLOW_ORIGINS=http://localhost:5173,https://prankbook.com diff --git a/deploy/README.md b/deploy/README.md index f940d5f..1ca752e 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -4,6 +4,7 @@ This folder separates deploy concerns: - `deploy/app`: application containers (`tracking-console`, `tracking-api`, `router-worker`). - `deploy/infra`: shared infrastructure containers (`redis`). +- `deploy/vps`: upload-first guide and host Nginx template for VPS rollout. ## Is this model correct for current repo flow? @@ -18,6 +19,7 @@ Yes, this matches the current architecture: ## Prerequisites 1. GHCR images exist for both services: + - `tracking-console` - `tracking-api` - `router-worker` 2. Postgres is reachable (Railway/Supabase/self-hosted). @@ -39,7 +41,8 @@ docker compose up -d cd deploy/app cp .env.example .env # edit .env values first -docker compose up -d +docker compose pull +docker compose up -d --no-build ``` ### 3) Smoke check @@ -63,10 +66,12 @@ curl -fsS http://127.0.0.1:13001/ready This repository workflow publishes images to: +- `ghcr.io///tracking-console:{latest|staging-}` - `ghcr.io///tracking-api:{latest|staging-}` - `ghcr.io///router-worker:{latest|staging-}` Example if your repo is `CongThienDev/Tracking_Base_System`: +- `ghcr.io/congthiendev/tracking_base_system/tracking-console:latest` - `ghcr.io/congthiendev/tracking_base_system/tracking-api:latest` - `ghcr.io/congthiendev/tracking_base_system/router-worker:latest` diff --git a/deploy/app/.env.example b/deploy/app/.env.example new file mode 100644 index 0000000..16040a3 --- /dev/null +++ b/deploy/app/.env.example @@ -0,0 +1,36 @@ +# GHCR images (must exist) +TRACKING_CONSOLE_IMAGE=ghcr.io/congthiendev/tracking_base_system/tracking-console:latest +TRACKING_API_IMAGE=ghcr.io/congthiendev/tracking_base_system/tracking-api:latest +ROUTER_WORKER_IMAGE=ghcr.io/congthiendev/tracking_base_system/router-worker:latest + +# API runtime +TRACKING_CONSOLE_PORT=18081 +TRACKING_API_PORT=13001 +DATABASE_URL=postgresql://:@:/?sslmode=require +ADMIN_API_TOKEN=replace-with-strong-token + +# Queue runtime (shared by API + worker) +REDIS_URL=redis://redis:6379/0 +ROUTER_QUEUE_NAME=router-deliveries +ROUTER_WORKER_NAME=router-delivery-worker +ROUTER_WORKER_CONCURRENCY=5 + +# Security (recommended for internet-facing API) +TRACKING_API_AUTH_MODE=shared-secret +TRACKING_API_SECRET=replace-with-strong-secret +TRACKING_SIGNATURE_SKEW_SECONDS=300 +TRACKING_RATE_LIMIT_ENABLED=true +TRACKING_RATE_LIMIT_WINDOW_MS=60000 +TRACKING_RATE_LIMIT_MAX_REQUESTS=120 +TRACKING_CORS_ALLOW_ORIGINS=http://localhost:5173,https://prankbook.com +METRICS_ENABLED=true + +# Destination adapters (optional, enable when ready) +META_CONVERSIONS_ENDPOINT_URL= +META_PIXEL_ID= +META_ACCESS_TOKEN= +META_TEST_EVENT_CODE= +GOOGLE_CONVERSIONS_ENDPOINT_URL= +GOOGLE_CONVERSIONS_API_KEY= +TIKTOK_EVENTS_ENDPOINT_URL= +TIKTOK_ACCESS_TOKEN= diff --git a/deploy/app/docker-compose.yml b/deploy/app/docker-compose.yml index c871bd1..b90e1fe 100644 --- a/deploy/app/docker-compose.yml +++ b/deploy/app/docker-compose.yml @@ -2,10 +2,7 @@ name: tracking-base-app services: tracking-console: - image: ${TRACKING_CONSOLE_IMAGE:-tracking-console:local} - build: - context: ../.. - dockerfile: apps/tracking-console/Dockerfile + image: ${TRACKING_CONSOLE_IMAGE:-ghcr.io/congthiendev/tracking_base_system/tracking-console:latest} container_name: tracking-console restart: unless-stopped depends_on: diff --git a/deploy/infra/.env.example b/deploy/infra/.env.example new file mode 100644 index 0000000..3e1d1ce --- /dev/null +++ b/deploy/infra/.env.example @@ -0,0 +1 @@ +REDIS_PORT=6379 diff --git a/deploy/vps/README.md b/deploy/vps/README.md new file mode 100644 index 0000000..0c273fc --- /dev/null +++ b/deploy/vps/README.md @@ -0,0 +1,70 @@ +# VPS Deploy (Upload-Only) + +Use this guide when you do **not** clone the full repository on VPS. + +## Files to upload to VPS + +Create folder `/opt/tracking-base` on VPS and upload: + +1. `deploy/infra/docker-compose.yml` -> `/opt/tracking-base/infra.compose.yml` +2. `deploy/infra/.env.example` -> `/opt/tracking-base/.env.infra.example` +3. `deploy/app/docker-compose.yml` -> `/opt/tracking-base/app.compose.yml` +4. `deploy/app/.env.example` -> `/opt/tracking-base/.env.app.example` +5. `deploy/vps/nginx.tracking-base.conf.example` -> `/opt/tracking-base/nginx.tracking-base.conf.example` + +## Prepare env files + +```bash +cd /opt/tracking-base +cp .env.infra.example .env.infra +cp .env.app.example .env.app +``` + +Edit `.env.app` with production values: + +- `TRACKING_CONSOLE_IMAGE=ghcr.io/congthiendev/tracking_base_system/tracking-console:latest` +- `TRACKING_API_IMAGE=ghcr.io/congthiendev/tracking_base_system/tracking-api:latest` +- `ROUTER_WORKER_IMAGE=ghcr.io/congthiendev/tracking_base_system/router-worker:latest` +- `TRACKING_CONSOLE_PORT=18081` +- `TRACKING_API_PORT=13001` +- `DATABASE_URL=...` +- `REDIS_URL=redis://redis:6379/0` +- `ADMIN_API_TOKEN=...` +- `TRACKING_API_SECRET=...` + +## Run containers + +```bash +docker login ghcr.io + +docker compose -f infra.compose.yml --env-file .env.infra up -d +docker compose -f app.compose.yml --env-file .env.app pull +docker compose -f app.compose.yml --env-file .env.app up -d --no-build +``` + +## Verify + +```bash +docker compose -f app.compose.yml --env-file .env.app ps +curl -I http://127.0.0.1:18081 +curl -fsS http://127.0.0.1:13001/health +curl -fsS http://127.0.0.1:13001/ready +``` + +## Host Nginx + +1. Copy `nginx.tracking-base.conf.example` to `/etc/nginx/sites-available/tracking-base.conf` +2. Replace `console.example.com` and `track.example.com` +3. Enable site and reload Nginx: + +```bash +sudo ln -s /etc/nginx/sites-available/tracking-base.conf /etc/nginx/sites-enabled/tracking-base.conf +sudo nginx -t +sudo systemctl reload nginx +``` + +4. Issue SSL cert: + +```bash +sudo certbot --nginx -d console.example.com -d track.example.com +``` diff --git a/deploy/vps/nginx.tracking-base.conf.example b/deploy/vps/nginx.tracking-base.conf.example new file mode 100644 index 0000000..f54fb56 --- /dev/null +++ b/deploy/vps/nginx.tracking-base.conf.example @@ -0,0 +1,27 @@ +server { + listen 80; + server_name console.example.com; + + location / { + proxy_pass http://127.0.0.1:18081; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} + +server { + listen 80; + server_name track.example.com; + + location / { + proxy_pass http://127.0.0.1:13001; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/env/.env.shared.example b/env/.env.shared.example index 6dfabdb..b1020ca 100644 --- a/env/.env.shared.example +++ b/env/.env.shared.example @@ -38,7 +38,7 @@ TIKTOK_ACCESS_TOKEN= # --- Deploy app stack --- -TRACKING_CONSOLE_IMAGE=tracking-console:local +TRACKING_CONSOLE_IMAGE=ghcr.io/congthiendev/tracking_base_system/tracking-console:latest TRACKING_API_IMAGE=ghcr.io/congthiendev/tracking_base_system/tracking-api:latest ROUTER_WORKER_IMAGE=ghcr.io/congthiendev/tracking_base_system/router-worker:latest TRACKING_CONSOLE_PORT=18081