diff --git a/faceai/.env.development b/faceai/.env.development new file mode 100644 index 00000000..ad8c4008 --- /dev/null +++ b/faceai/.env.development @@ -0,0 +1,35 @@ +NODE_ENV=development +FACEAI_CLIENT_DEV_IMAGE=node:20-alpine +FACEAI_PROCESSOR_DEV_IMAGE=regalami-faceai-processor-local +FACEAI_PORT=3001 +FACEAI_PUBLISHED_PORT=3001 +FACEAI_LEGACY_PHP_PORT=8080 +FACEAI_FRONTEND_URL=http://localhost:3001 +FACEAI_PUBLIC_BASE_URL=http://localhost:3001 +FACEAI_LEGACY_RETURN_URL=http://localhost:8080/faceai_return.php +FACEAI_LEGACY_HOME_URL=http://localhost:8080/index.jsp +FACEAI_ENABLE_LOCAL_LEGACY_STATIC=1 +FACEAI_LOCAL_LEGACY_STATIC_ROOT=/legacy-www +FACEAI_SHARED_SECRET=disagio-spaghetti-science-lol-boh +FACEAI_SESSION_COOKIE=rus_faceai_session +FACEAI_REDIS_URL=redis://redis:6379 +FACEAI_QUEUE_NAME=faceai-searches +FACEAI_RUNTIME_ROOT=/data/runtime +FACEAI_UPLOAD_ROOT=/data/runtime/uploads +FACEAI_LOG_ROOT=/data/logs +FACEAI_PKL_ROOT=/data/pkl +FACEAI_MATCHER_BINARY=/app/bin/face_matcher +FACEAI_WORKER_CONCURRENCY=2 +FACEAI_WORKER_TIMEOUT_MS=300000 +FACEAI_PROCESSOR_HEARTBEAT_GRACE_MS=20000 +FACEAI_PROCESSOR_HEARTBEAT_INTERVAL_MS=5000 +FACEAI_PROCESSOR_HEARTBEAT_TTL_SECONDS=20 +FACEAI_FEATURE_ENABLED=1 +FACEAI_BACKEND_INTERNAL_URL=http://faceai:3001 +FACEAI_ALLOW_DEV_HANDOFF=1 +FACEAI_IDENTITY_COOKIE=rus_faceai_identity +LIVE_SITE_BASE_URL=https://www.regalamiunsorriso.it +LIVE_SITE_LOGIN_URL=https://www.regalamiunsorriso.it/login_clienti-it.html +LIVE_SITE_RACE_URL=https://www.regalamiunsorriso.it/42%20HALF%20MARATHON%20FIRENZE_gara-1018545---96-1.html +LIVE_SITE_USERNAME=piero +LIVE_SITE_PASSWORD=AA25061958Pg! \ No newline at end of file diff --git a/faceai/.env.example b/faceai/.env.example index b9ba03fb..20fd0a9c 100644 --- a/faceai/.env.example +++ b/faceai/.env.example @@ -1,22 +1,46 @@ -PORT=3001 -FACEAI_FRONTEND_URL=http://localhost:5173 -FACEAI_PUBLIC_BASE_URL=http://localhost:3001 -FACEAI_LEGACY_RETURN_URL=http://localhost:3001/dev/legacy/return -FACEAI_LEGACY_HOME_URL=http://localhost:8080/index.jsp -FACEAI_ENABLE_LOCAL_LEGACY_STATIC=1 -FACEAI_LOCAL_LEGACY_STATIC_ROOT=k:\various\regalamiunsorriso\www +# Compose usage: +# docker compose -f docker-compose.yml --env-file .env.production up -d +# docker compose --env-file .env.development up --build + +NODE_ENV=production +FACEAI_CLIENT_IMAGE=forgejo.maddoscientisto.net/maddo/faceai-client:latest +FACEAI_PROCESSOR_IMAGE=forgejo.maddoscientisto.net/maddo/faceai-processor:latest +FACEAI_REDIS_IMAGE=redis:7-alpine +FACEAI_CLIENT_CONTAINER_NAME=regalami-faceai +FACEAI_PROCESSOR_CONTAINER_NAME=regalami-faceai-processor +FACEAI_REDIS_CONTAINER_NAME=regalami-faceai-redis +FACEAI_CLIENT_DEV_IMAGE=node:20-alpine +FACEAI_PROCESSOR_DEV_IMAGE=regalami-faceai-processor-local +FACEAI_PORT=3001 +FACEAI_PUBLISHED_PORT=3001 +FACEAI_LEGACY_PHP_PORT=8080 +FACEAI_FRONTEND_URL=https://ai.regalamiunsorriso.it +FACEAI_PUBLIC_BASE_URL=https://ai.regalamiunsorriso.it +FACEAI_LEGACY_RETURN_URL=https://www.regalamiunsorriso.it/faceai_return.php +FACEAI_LEGACY_HOME_URL=https://www.regalamiunsorriso.it +FACEAI_ENABLE_LOCAL_LEGACY_STATIC=0 +FACEAI_LOCAL_LEGACY_STATIC_ROOT=/legacy-www FACEAI_SHARED_SECRET=change-me FACEAI_SESSION_COOKIE=rus_faceai_session +FACEAI_IDENTITY_COOKIE=rus_faceai_identity FACEAI_REDIS_URL=redis://redis:6379 FACEAI_QUEUE_NAME=faceai-searches FACEAI_RUNTIME_ROOT=/data/runtime FACEAI_UPLOAD_ROOT=/data/runtime/uploads FACEAI_LOG_ROOT=/data/logs FACEAI_PKL_ROOT=/data/pkl +FACEAI_RUNTIME_BIND=/mnt/storage/data/faceai/runtime +FACEAI_LOG_BIND=/mnt/storage/data/faceai/logs +FACEAI_PKL_BIND=/mnt/nas12/nas2/RUS FACEAI_MATCHER_BINARY=/app/bin/face_matcher +FACEAI_WORKER_CONCURRENCY=8 +FACEAI_WORKER_TIMEOUT_MS=300000 FACEAI_PROCESSOR_HEARTBEAT_GRACE_MS=20000 FACEAI_PROCESSOR_HEARTBEAT_INTERVAL_MS=5000 FACEAI_PROCESSOR_HEARTBEAT_TTL_SECONDS=20 +FACEAI_FEATURE_ENABLED=1 +FACEAI_BACKEND_INTERNAL_URL=http://faceai:3001 +FACEAI_ALLOW_DEV_HANDOFF=1 LIVE_SITE_BASE_URL=https://www.regalamiunsorriso.it LIVE_SITE_LOGIN_URL=https://www.regalamiunsorriso.it/login_clienti-it.html LIVE_SITE_RACE_URL=https://www.regalamiunsorriso.it/42%20HALF%20MARATHON%20FIRENZE_gara-1018545---96-1.html diff --git a/faceai/.env.production b/faceai/.env.production new file mode 100644 index 00000000..983d424d --- /dev/null +++ b/faceai/.env.production @@ -0,0 +1,28 @@ +NODE_ENV=production +FACEAI_CLIENT_IMAGE=forgejo.maddoscientisto.net/maddo/faceai-client:latest +FACEAI_PROCESSOR_IMAGE=forgejo.maddoscientisto.net/maddo/faceai-processor:latest +FACEAI_REDIS_IMAGE=redis:7-alpine +FACEAI_CLIENT_CONTAINER_NAME=regalami-faceai +FACEAI_PROCESSOR_CONTAINER_NAME=regalami-faceai-processor +FACEAI_REDIS_CONTAINER_NAME=regalami-faceai-redis +FACEAI_PORT=3001 +FACEAI_PUBLISHED_PORT=3001 +FACEAI_FRONTEND_URL=https://ai.regalamiunsorriso.it +FACEAI_PUBLIC_BASE_URL=https://ai.regalamiunsorriso.it +FACEAI_LEGACY_RETURN_URL=https://www.regalamiunsorriso.it/faceai_return.php +FACEAI_LEGACY_HOME_URL=https://www.regalamiunsorriso.it +FACEAI_SHARED_SECRET=disagio-spaghetti-science-lol-boh +FACEAI_SESSION_COOKIE=rus_faceai_session +FACEAI_REDIS_URL=redis://redis:6379 +FACEAI_QUEUE_NAME=faceai-searches +FACEAI_RUNTIME_ROOT=/data/runtime +FACEAI_UPLOAD_ROOT=/data/runtime/uploads +FACEAI_LOG_ROOT=/data/logs +FACEAI_PKL_ROOT=/data/pkl +FACEAI_RUNTIME_BIND=/mnt/storage/data/faceai/runtime +FACEAI_LOG_BIND=/mnt/storage/data/faceai/logs +FACEAI_PKL_BIND=/mnt/nas12/nas2/RUS +FACEAI_MATCHER_BINARY=/app/bin/face_matcher +FACEAI_WORKER_CONCURRENCY=8 +FACEAI_WORKER_TIMEOUT_MS=300000 +FACEAI_ENABLE_LOCAL_LEGACY_STATIC=0 \ No newline at end of file diff --git a/faceai/README.md b/faceai/README.md index 7d0ad999..978be30c 100644 --- a/faceai/README.md +++ b/faceai/README.md @@ -63,10 +63,17 @@ From this folder: ```bash npm install npm run build -docker compose up --build +docker compose --env-file .env.development up --build ``` -The checked-in `docker-compose.yml` starts: +The checked-in Compose setup now uses: + +- `docker-compose.yml` as the production-ready base stack +- `docker-compose.override.yml` as the local development overlay +- `.env.production` for production-oriented values +- `.env.development` for the local simulator workflow + +The local development stack started by the command above combines the base file and the override and starts: - FaceAI public site on `http://localhost:3001` - processor runner on the internal Compose network @@ -85,7 +92,7 @@ The `processor` service is built from `docker/processor.Dockerfile` using the re The checked-in local Compose stack now mirrors the relevant Node service logs to both Docker stdout/stderr and `faceai/logs` on the host. -After `docker compose up --build`, inspect: +After `docker compose --env-file .env.development up --build`, inspect: - `faceai/logs/backend.log` for backend startup and API-side failures - `faceai/logs/processor.log` for worker startup, queue processing, and uncaught processor errors @@ -131,7 +138,7 @@ npm run build If you want to stop and remove the local containers afterward, run: ```bash -docker compose down +docker compose --env-file .env.development down ``` ### Automated End-To-End Test @@ -149,7 +156,7 @@ npm run test:e2e The suite will: - build the frontend bundle -- start `docker compose up --build -d` +- start `docker compose --env-file .env.development up --build -d` - open `http://localhost:8080/faceai_simulator.php?raceId=202&lang=it` - click the `Face ID` launch button injected by `www/_js/rus-ecom-240621.js` - upload `test_pkl/test_images/DSC_1960.JPG` @@ -237,7 +244,7 @@ One workable loop is: ```bash npm install -docker compose up redis -d +docker compose --env-file .env.development up redis -d npm run dev ``` @@ -245,7 +252,9 @@ Then start the processor in a second shell, either with its own local environmen ## Docker Compose Deployment For The Public Site And Matcher Runner -The checked-in `docker-compose.yml` is for local integration testing because it also includes the PHP simulator and local bind mounts. For hosted deployment, keep the same three-service application topology but remove `legacy-php` and replace the local mounts with the real production paths on the host. +The checked-in `docker-compose.yml` is now the production-ready base stack for hosted deployment. The checked-in `docker-compose.override.yml` is the development overlay that restores the local PHP simulator, workspace bind mounts, and development-oriented commands. + +Because Docker Compose auto-loads `docker-compose.override.yml` when it is present in the same directory, production-style runs from this workspace must explicitly select only the base file. The public FaceAI site and the matcher runner can both use the same application image. The difference is only the process command: @@ -254,102 +263,25 @@ The public FaceAI site and the matcher runner can both use the same application If that shared image also embeds or mounts the current Linux `face_matcher` build, make sure the base OS provides `GLIBC_2.38` or newer and includes `libxcb1`. A Debian Trixie-based image with that package installed satisfies the requirement; a Bookworm-based image does not. -### Production Compose Example +### Production Compose Commands -This example assumes: +This setup assumes: - FaceAI runtime files, logs, and matcher binaries live under `/var/docker/faceai` on the host - the NAS export is already mounted on the host at `/mnt/nas12` via `/etc/fstab`, for example `192.168.10.247:/public /mnt/nas12 nfs rw,noatime 0 0` - the race dataset root is available on the host at `/mnt/nas12/nas2/RUS` -Replace the registry path and secrets with the real deployment values. +Set the real production values in `.env.production`, then run: -```yaml -services: - faceai: - image: forgejo.maddoscientisto.net/maddo/faceai-client:latest - container_name: regalami-faceai - restart: unless-stopped - command: - - node - - docker/run-with-log-file.mjs - - /data/logs/backend.log - - npm - - run - - start - environment: - NODE_ENV: production - PORT: 3001 - FACEAI_FRONTEND_URL: https://ai.regalamiunsorriso.it - FACEAI_PUBLIC_BASE_URL: https://ai.regalamiunsorriso.it - FACEAI_LEGACY_RETURN_URL: https://www.regalamiunsorriso.it/faceai_return.php - FACEAI_SHARED_SECRET: disagio-spaghetti-science-lol-boh - FACEAI_SESSION_COOKIE: rus_faceai_session - FACEAI_REDIS_URL: redis://redis:6379 - FACEAI_QUEUE_NAME: faceai-searches - FACEAI_RUNTIME_ROOT: /data/runtime - FACEAI_UPLOAD_ROOT: /data/runtime/uploads - FACEAI_LOG_ROOT: /data/logs - FACEAI_PKL_ROOT: /data/pkl - FACEAI_PROCESSOR_HEARTBEAT_GRACE_MS: 20000 - FACEAI_ENABLE_LOCAL_LEGACY_STATIC: 0 - volumes: - - /mnt/storage/data/faceai/runtime:/data/runtime - - /mnt/storage/data/faceai/logs:/data/logs - - /mnt/nas12/nas2/RUS:/data/pkl:ro - ports: - - "3001:3001" - healthcheck: - test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3001/health').then(async (response) => { const payload = await response.json().catch(() => ({})); if (!response.ok || payload.ok !== true) { console.error(JSON.stringify(payload)); process.exit(1); } }).catch((error) => { console.error(error.stack || error.message); process.exit(1); })"] - interval: 10s - timeout: 5s - retries: 6 - start_period: 20s - depends_on: - redis: - condition: service_healthy +```bash +docker compose -f docker-compose.yml --env-file .env.production up -d +``` - processor: - image: forgejo.maddoscientisto.net/maddo/faceai-processor:latest - container_name: regalami-faceai-processor - restart: unless-stopped - command: - - node - - docker/run-with-log-file.mjs - - /data/logs/processor.log - - npm - - run - - start:processor - environment: - NODE_ENV: production - FACEAI_REDIS_URL: redis://redis:6379 - FACEAI_QUEUE_NAME: faceai-searches - FACEAI_RUNTIME_ROOT: /data/runtime - FACEAI_LOG_ROOT: /data/logs - FACEAI_PKL_ROOT: /data/pkl - FACEAI_MATCHER_BINARY: /app/bin/face_matcher - FACEAI_PROCESSOR_HEARTBEAT_INTERVAL_MS: 5000 - FACEAI_PROCESSOR_HEARTBEAT_TTL_SECONDS: 20 - FACEAI_WORKER_CONCURRENCY: 2 - FACEAI_WORKER_TIMEOUT_MS: 300000 - volumes: - - /mnt/storage/data/faceai/runtime:/data/runtime - - /mnt/storage/data/faceai/logs:/data/logs - - /mnt/nas12/nas2/RUS:/data/pkl:ro - depends_on: - redis: - condition: service_healthy +To pull newer images before a rollout: - redis: - image: redis:7-alpine - container_name: regalami-faceai-redis - restart: unless-stopped - command: redis-server --appendonly no - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 5s - timeout: 3s - retries: 12 +```bash +docker compose -f docker-compose.yml --env-file .env.production pull +docker compose -f docker-compose.yml --env-file .env.production up -d ``` This pattern assumes a reverse proxy on the host publishes `https://ai.regalamiunsorriso.it` and forwards to `127.0.0.1:3001`. The processor is internal-only and does not expose any public port. @@ -451,16 +383,37 @@ This scaffold can now be deployed with the public site, processor, and Redis, bu So the Compose deployment is appropriate for hosted integration and controlled production-like rollout, but not yet for the final hardened architecture described in the integration plan. -## Environment +## Environment Files -Defaults are already set for local development, but these can be overridden: +The repository now keeps separate env files for the two compose workflows: + +- `.env.production`: production-oriented values used with the base compose file only +- `.env.development`: local simulator values used with the base file plus the override + +To start the local development stack: + +```bash +docker compose --env-file .env.development up --build +``` + +To start the production-style stack from this workspace without loading the development override: + +```bash +docker compose -f docker-compose.yml --env-file .env.production up -d +``` + +If you need a template that lists all supported variables, use `.env.example`. + +The most important development defaults are: ```text -PORT=3001 -FACEAI_FRONTEND_URL=http://localhost:5173 +NODE_ENV=development +FACEAI_PORT=3001 +FACEAI_FRONTEND_URL=http://localhost:3001 FACEAI_PUBLIC_BASE_URL=http://localhost:3001 -FACEAI_LEGACY_RETURN_URL=http://localhost:3001/dev/legacy/return +FACEAI_LEGACY_RETURN_URL=http://localhost:8080/faceai_return.php FACEAI_LEGACY_HOME_URL=http://localhost:8080/index.jsp +FACEAI_FEATURE_ENABLED=1 FACEAI_SHARED_SECRET=change-me FACEAI_SESSION_COOKIE=rus_faceai_session FACEAI_REDIS_URL=redis://redis:6379 @@ -469,19 +422,19 @@ FACEAI_RUNTIME_ROOT=/data/runtime FACEAI_UPLOAD_ROOT=/data/runtime/uploads FACEAI_LOG_ROOT=/data/logs FACEAI_PKL_ROOT=/data/pkl -FACEAI_MATCHER_BINARY=/opt/face-recognition/face_matcher +FACEAI_MATCHER_BINARY=/app/bin/face_matcher ``` If you want FaceAI to return through the new PHP bridge prepared under `www`, point `FACEAI_LEGACY_RETURN_URL` to that endpoint instead, for example `http://localhost/faceai_return.php` or the equivalent URL in your local PHP setup. -In the provided Docker Compose stack, that wiring is already done with: +In the development override, that wiring is already done with: ```text FACEAI_LEGACY_RETURN_URL=http://localhost:8080/faceai_return.php FACEAI_LEGACY_HOME_URL=http://localhost:8080/index.jsp ``` -The log wiring is also already done in the checked-in Compose file with a host bind mount for `./logs:/data/logs`, so both the backend and the processor write persistent diagnostics into the workspace while also remaining visible through Docker and Portainer container logs. +The development override also keeps the log wiring with `./logs:/data/logs`, so both the backend and the processor write persistent diagnostics into the workspace while also remaining visible through Docker and Portainer container logs. The Compose contract now also includes an HTTP healthcheck on the public FaceAI service and a Redis readiness check. That makes `docker compose ps` meaningful during rollout: `faceai` only becomes healthy after `GET /health` returns `{"ok":true}`, and both the public site and the processor wait for Redis readiness before their own startup sequence begins. @@ -491,12 +444,12 @@ The local PHP simulator also needs the legacy bridge feature flag enabled: FACEAI_FEATURE_ENABLED=1 ``` -The checked-in `docker-compose.yml` now sets that on the `legacy-php` service so the simulator can launch the FaceAI handoff flow locally. +The checked-in `docker-compose.override.yml` sets that on the `legacy-php` service so the simulator can launch the FaceAI handoff flow locally. ## Notes - Search orchestration now uses Redis and a dedicated processor worker. -- The checked-in Compose file is meant for local integration testing, not as-is production use. +- The checked-in base Compose file is production-oriented, while the checked-in override is development-only. - The local legacy simulator is intentionally backend-driven so the handoff can be tested without compiling the existing Java application. - `www/faceai_simulator.php` exists only for local testing. It does not replace the actual JSP race page. - The final legacy integration still needs a real signed identity source and a real return-filter implementation on the old site. diff --git a/faceai/docker-compose.override.yml b/faceai/docker-compose.override.yml new file mode 100644 index 00000000..29288a66 --- /dev/null +++ b/faceai/docker-compose.override.yml @@ -0,0 +1,91 @@ +services: + faceai: + image: ${FACEAI_CLIENT_DEV_IMAGE:-node:20-alpine} + working_dir: /app + command: + - node + - docker/run-with-log-file.mjs + - ${FACEAI_BACKEND_LOG_FILE:-/data/logs/backend.log} + - npm + - run + - start + - --workspace + - "@regalami/faceai-backend" + environment: + NODE_ENV: ${NODE_ENV:-development} + PORT: ${FACEAI_PORT:-3001} + FACEAI_FRONTEND_URL: ${FACEAI_FRONTEND_URL:-http://localhost:3001} + FACEAI_PUBLIC_BASE_URL: ${FACEAI_PUBLIC_BASE_URL:-http://localhost:3001} + FACEAI_LEGACY_RETURN_URL: ${FACEAI_LEGACY_RETURN_URL:-http://localhost:8080/faceai_return.php} + FACEAI_LEGACY_HOME_URL: ${FACEAI_LEGACY_HOME_URL:-http://localhost:8080/index.jsp} + FACEAI_SHARED_SECRET: ${FACEAI_SHARED_SECRET:-change-me} + FACEAI_SESSION_COOKIE: ${FACEAI_SESSION_COOKIE:-rus_faceai_session} + FACEAI_REDIS_URL: ${FACEAI_REDIS_URL:-redis://redis:6379} + FACEAI_QUEUE_NAME: ${FACEAI_QUEUE_NAME:-faceai-searches} + FACEAI_RUNTIME_ROOT: ${FACEAI_RUNTIME_ROOT:-/data/runtime} + FACEAI_UPLOAD_ROOT: ${FACEAI_UPLOAD_ROOT:-/data/runtime/uploads} + FACEAI_LOG_ROOT: ${FACEAI_LOG_ROOT:-/data/logs} + FACEAI_PKL_ROOT: ${FACEAI_PKL_ROOT:-/data/pkl} + FACEAI_ENABLE_LOCAL_LEGACY_STATIC: ${FACEAI_ENABLE_LOCAL_LEGACY_STATIC:-1} + FACEAI_LOCAL_LEGACY_STATIC_ROOT: ${FACEAI_LOCAL_LEGACY_STATIC_ROOT:-/legacy-www} + FACEAI_PROCESSOR_HEARTBEAT_GRACE_MS: ${FACEAI_PROCESSOR_HEARTBEAT_GRACE_MS:-20000} + volumes: + - .:/app + - ./logs:${FACEAI_LOG_ROOT:-/data/logs} + - ../www:${FACEAI_LOCAL_LEGACY_STATIC_ROOT:-/legacy-www}:ro + - ../test_pkl:${FACEAI_PKL_ROOT:-/data/pkl}:ro + - faceai-runtime:${FACEAI_RUNTIME_ROOT:-/data/runtime} + + processor: + build: + context: .. + dockerfile: faceai/docker/processor.Dockerfile + image: ${FACEAI_PROCESSOR_DEV_IMAGE:-regalami-faceai-processor-local} + working_dir: /app + command: + - node + - docker/run-with-log-file.mjs + - ${FACEAI_PROCESSOR_LOG_FILE:-/data/logs/processor.log} + - npm + - run + - start + - --workspace + - "@regalami/faceai-processor" + environment: + NODE_ENV: ${NODE_ENV:-development} + FACEAI_REDIS_URL: ${FACEAI_REDIS_URL:-redis://redis:6379} + FACEAI_QUEUE_NAME: ${FACEAI_QUEUE_NAME:-faceai-searches} + FACEAI_RUNTIME_ROOT: ${FACEAI_RUNTIME_ROOT:-/data/runtime} + FACEAI_LOG_ROOT: ${FACEAI_LOG_ROOT:-/data/logs} + FACEAI_PKL_ROOT: ${FACEAI_PKL_ROOT:-/data/pkl} + FACEAI_MATCHER_BINARY: ${FACEAI_MATCHER_BINARY:-/app/bin/face_matcher} + FACEAI_WORKER_CONCURRENCY: ${FACEAI_WORKER_CONCURRENCY:-2} + FACEAI_WORKER_TIMEOUT_MS: ${FACEAI_WORKER_TIMEOUT_MS:-300000} + FACEAI_PROCESSOR_HEARTBEAT_INTERVAL_MS: ${FACEAI_PROCESSOR_HEARTBEAT_INTERVAL_MS:-5000} + FACEAI_PROCESSOR_HEARTBEAT_TTL_SECONDS: ${FACEAI_PROCESSOR_HEARTBEAT_TTL_SECONDS:-20} + volumes: + - ./logs:${FACEAI_LOG_ROOT:-/data/logs} + - ../test_pkl:${FACEAI_PKL_ROOT:-/data/pkl}:ro + - faceai-runtime:${FACEAI_RUNTIME_ROOT:-/data/runtime} + + legacy-php: + image: ${FACEAI_LEGACY_PHP_IMAGE:-php:8.3-apache} + container_name: ${FACEAI_LEGACY_PHP_CONTAINER_NAME:-regalami-legacy-php} + restart: unless-stopped + environment: + FACEAI_FEATURE_ENABLED: ${FACEAI_FEATURE_ENABLED:-1} + FACEAI_BACKEND_INTERNAL_URL: ${FACEAI_BACKEND_INTERNAL_URL:-http://faceai:3001} + FACEAI_FRONTEND_URL: ${FACEAI_FRONTEND_URL:-http://localhost:3001} + FACEAI_SHARED_SECRET: ${FACEAI_SHARED_SECRET:-change-me} + FACEAI_ALLOW_DEV_HANDOFF: ${FACEAI_ALLOW_DEV_HANDOFF:-1} + FACEAI_IDENTITY_COOKIE: ${FACEAI_IDENTITY_COOKIE:-rus_faceai_identity} + volumes: + - ../www:/var/www/html + ports: + - "${FACEAI_LEGACY_PHP_PORT:-8080}:80" + depends_on: + faceai: + condition: service_healthy + +volumes: + faceai-runtime: \ No newline at end of file diff --git a/faceai/docker-compose.yml b/faceai/docker-compose.yml index 616a7c37..0aba1a99 100644 --- a/faceai/docker-compose.yml +++ b/faceai/docker-compose.yml @@ -1,44 +1,39 @@ services: faceai: - image: node:20-alpine - container_name: regalami-faceai + image: ${FACEAI_CLIENT_IMAGE:-forgejo.maddoscientisto.net/maddo/faceai-client:latest} + container_name: ${FACEAI_CLIENT_CONTAINER_NAME:-regalami-faceai} restart: unless-stopped - working_dir: /app command: - node - docker/run-with-log-file.mjs - - /data/logs/backend.log + - ${FACEAI_BACKEND_LOG_FILE:-/data/logs/backend.log} - npm - run - start - - --workspace - - "@regalami/faceai-backend" environment: - PORT: 3001 - FACEAI_FRONTEND_URL: http://localhost:3001 - FACEAI_PUBLIC_BASE_URL: http://localhost:3001 - FACEAI_LEGACY_RETURN_URL: http://localhost:8080/faceai_return.php - FACEAI_LEGACY_HOME_URL: http://localhost:8080/index.jsp - FACEAI_PKL_ROOT: /data/pkl - FACEAI_ENABLE_LOCAL_LEGACY_STATIC: 1 - FACEAI_LOCAL_LEGACY_STATIC_ROOT: /legacy-www - FACEAI_SHARED_SECRET: disagio-spaghetti-science-lol-boh - FACEAI_SESSION_COOKIE: rus_faceai_session - FACEAI_REDIS_URL: redis://redis:6379 - FACEAI_RUNTIME_ROOT: /data/runtime - FACEAI_UPLOAD_ROOT: /data/runtime/uploads - FACEAI_LOG_ROOT: /data/logs - FACEAI_PROCESSOR_HEARTBEAT_GRACE_MS: 20000 + NODE_ENV: ${NODE_ENV:-production} + PORT: ${FACEAI_PORT:-3001} + FACEAI_FRONTEND_URL: ${FACEAI_FRONTEND_URL:-https://ai.regalamiunsorriso.it} + FACEAI_PUBLIC_BASE_URL: ${FACEAI_PUBLIC_BASE_URL:-https://ai.regalamiunsorriso.it} + FACEAI_LEGACY_RETURN_URL: ${FACEAI_LEGACY_RETURN_URL:-https://www.regalamiunsorriso.it/faceai_return.php} + FACEAI_LEGACY_HOME_URL: ${FACEAI_LEGACY_HOME_URL:-https://www.regalamiunsorriso.it} + FACEAI_SHARED_SECRET: ${FACEAI_SHARED_SECRET:-change-me} + FACEAI_SESSION_COOKIE: ${FACEAI_SESSION_COOKIE:-rus_faceai_session} + FACEAI_REDIS_URL: ${FACEAI_REDIS_URL:-redis://redis:6379} + FACEAI_QUEUE_NAME: ${FACEAI_QUEUE_NAME:-faceai-searches} + FACEAI_RUNTIME_ROOT: ${FACEAI_RUNTIME_ROOT:-/data/runtime} + FACEAI_UPLOAD_ROOT: ${FACEAI_UPLOAD_ROOT:-/data/runtime/uploads} + FACEAI_LOG_ROOT: ${FACEAI_LOG_ROOT:-/data/logs} + FACEAI_PKL_ROOT: ${FACEAI_PKL_ROOT:-/data/pkl} + FACEAI_ENABLE_LOCAL_LEGACY_STATIC: ${FACEAI_ENABLE_LOCAL_LEGACY_STATIC:-0} volumes: - - .:/app - - ./logs:/data/logs - - ../www:/legacy-www:ro - - ../test_pkl:/data/pkl:ro - - faceai-runtime:/data/runtime + - ${FACEAI_RUNTIME_BIND:-/mnt/storage/data/faceai/runtime}:${FACEAI_RUNTIME_ROOT:-/data/runtime} + - ${FACEAI_LOG_BIND:-/mnt/storage/data/faceai/logs}:${FACEAI_LOG_ROOT:-/data/logs} + - ${FACEAI_PKL_BIND:-/mnt/nas12/nas2/RUS}:${FACEAI_PKL_ROOT:-/data/pkl}:ro ports: - - "3001:3001" + - "${FACEAI_PUBLISHED_PORT:-3001}:${FACEAI_PORT:-3001}" healthcheck: - test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3001/health').then(async (response) => { const payload = await response.json().catch(() => ({})); if (!response.ok || payload.ok !== true) { console.error(JSON.stringify(payload)); process.exit(1); } }).catch((error) => { console.error(error.stack || error.message); process.exit(1); })"] + test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:' + (process.env.PORT || '3001') + '/health').then(async (response) => { const payload = await response.json().catch(() => ({})); if (!response.ok || payload.ok !== true) { console.error(JSON.stringify(payload)); process.exit(1); } }).catch((error) => { console.error(error.stack || error.message); process.exit(1); })"] interval: 10s timeout: 5s retries: 6 @@ -48,69 +43,41 @@ services: condition: service_healthy processor: - build: - context: .. - dockerfile: faceai/docker/processor.Dockerfile - image: regalami-faceai-processor-local - container_name: regalami-faceai-processor + image: ${FACEAI_PROCESSOR_IMAGE:-forgejo.maddoscientisto.net/maddo/faceai-processor:latest} + container_name: ${FACEAI_PROCESSOR_CONTAINER_NAME:-regalami-faceai-processor} restart: unless-stopped - working_dir: /app command: - node - docker/run-with-log-file.mjs - - /data/logs/processor.log + - ${FACEAI_PROCESSOR_LOG_FILE:-/data/logs/processor.log} - npm - run - - start - - --workspace - - "@regalami/faceai-processor" + - start:processor environment: - FACEAI_REDIS_URL: redis://redis:6379 - FACEAI_QUEUE_NAME: faceai-searches - FACEAI_RUNTIME_ROOT: /data/runtime - FACEAI_LOG_ROOT: /data/logs - FACEAI_PKL_ROOT: /data/pkl - FACEAI_WORKER_CONCURRENCY: 2 - FACEAI_MATCHER_BINARY: /app/bin/face_matcher - FACEAI_PROCESSOR_HEARTBEAT_INTERVAL_MS: 5000 - FACEAI_PROCESSOR_HEARTBEAT_TTL_SECONDS: 20 + NODE_ENV: ${NODE_ENV:-production} + FACEAI_REDIS_URL: ${FACEAI_REDIS_URL:-redis://redis:6379} + FACEAI_QUEUE_NAME: ${FACEAI_QUEUE_NAME:-faceai-searches} + FACEAI_RUNTIME_ROOT: ${FACEAI_RUNTIME_ROOT:-/data/runtime} + FACEAI_LOG_ROOT: ${FACEAI_LOG_ROOT:-/data/logs} + FACEAI_PKL_ROOT: ${FACEAI_PKL_ROOT:-/data/pkl} + FACEAI_MATCHER_BINARY: ${FACEAI_MATCHER_BINARY:-/app/bin/face_matcher} + FACEAI_WORKER_CONCURRENCY: ${FACEAI_WORKER_CONCURRENCY:-8} + FACEAI_WORKER_TIMEOUT_MS: ${FACEAI_WORKER_TIMEOUT_MS:-300000} volumes: - - ./logs:/data/logs - - ../test_pkl:/data/pkl:ro - - faceai-runtime:/data/runtime + - ${FACEAI_RUNTIME_BIND:-/mnt/storage/data/faceai/runtime}:${FACEAI_RUNTIME_ROOT:-/data/runtime} + - ${FACEAI_LOG_BIND:-/mnt/storage/data/faceai/logs}:${FACEAI_LOG_ROOT:-/data/logs} + - ${FACEAI_PKL_BIND:-/mnt/nas12/nas2/RUS}:${FACEAI_PKL_ROOT:-/data/pkl}:ro depends_on: redis: condition: service_healthy redis: - image: redis:7-alpine - container_name: regalami-faceai-redis + image: ${FACEAI_REDIS_IMAGE:-redis:7-alpine} + container_name: ${FACEAI_REDIS_CONTAINER_NAME:-regalami-faceai-redis} restart: unless-stopped - command: redis-server --appendonly no + command: ${FACEAI_REDIS_COMMAND:-redis-server --appendonly no} healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 12 - - legacy-php: - image: php:8.3-apache - container_name: regalami-legacy-php - restart: unless-stopped - environment: - FACEAI_FEATURE_ENABLED: 1 - FACEAI_BACKEND_INTERNAL_URL: http://faceai:3001 - FACEAI_FRONTEND_URL: http://localhost:3001 - FACEAI_SHARED_SECRET: disagio-spaghetti-science-lol-boh - FACEAI_ALLOW_DEV_HANDOFF: 1 - FACEAI_IDENTITY_COOKIE: rus_faceai_identity - volumes: - - ../www:/var/www/html - ports: - - "8080:80" - depends_on: - faceai: - condition: service_healthy - -volumes: - faceai-runtime: diff --git a/faceai/package.json b/faceai/package.json index 473af682..b48d265a 100644 --- a/faceai/package.json +++ b/faceai/package.json @@ -11,6 +11,10 @@ "dev:backend": "npm run dev --workspace @regalami/faceai-backend", "dev:frontend": "npm run dev --workspace @regalami/faceai-frontend", "dev:processor": "npm run dev --workspace @regalami/faceai-processor", + "compose:prod:up": "docker compose -f docker-compose.yml --env-file .env.production up -d", + "compose:prod:down": "docker compose -f docker-compose.yml --env-file .env.production down", + "compose:dev:up": "docker compose --env-file .env.development up --build", + "compose:dev:down": "docker compose --env-file .env.development down", "build": "npm run build --workspace @regalami/faceai-frontend && npm run build --workspace @regalami/faceai-backend", "start": "npm run start --workspace @regalami/faceai-backend", "start:processor": "npm run start --workspace @regalami/faceai-processor", diff --git a/stacks/faceai.yml b/stacks/faceai.yml index 3fde04cb..dcf526b2 100644 --- a/stacks/faceai.yml +++ b/stacks/faceai.yml @@ -25,7 +25,6 @@ services: FACEAI_UPLOAD_ROOT: /data/runtime/uploads FACEAI_LOG_ROOT: /data/logs FACEAI_PKL_ROOT: /data/pkl - FACEAI_PROCESSOR_HEARTBEAT_GRACE_MS: 20000 FACEAI_ENABLE_LOCAL_LEGACY_STATIC: 0 volumes: - /mnt/storage/data/faceai/runtime:/data/runtime @@ -34,7 +33,7 @@ services: ports: - "3001:3001" healthcheck: - test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3001/health').then(async (response) => { const payload = await response.json().catch(() => ({})); if (!response.ok || payload.ok !== true) { console.error(JSON.stringify(payload)); process.exit(1); } }).catch((error) => { console.error(error.stack || error.message); process.exit(1); })"] + test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3001/health | grep -q '\"ok\":true'"] interval: 10s timeout: 5s retries: 6 @@ -62,8 +61,6 @@ services: FACEAI_LOG_ROOT: /data/logs FACEAI_PKL_ROOT: /data/pkl FACEAI_MATCHER_BINARY: /app/bin/face_matcher - FACEAI_PROCESSOR_HEARTBEAT_INTERVAL_MS: 5000 - FACEAI_PROCESSOR_HEARTBEAT_TTL_SECONDS: 20 FACEAI_WORKER_CONCURRENCY: 8 FACEAI_WORKER_TIMEOUT_MS: 300000 volumes: