diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md index 1c2e8e6..4868b54 100644 --- a/IMPLEMENTATION.md +++ b/IMPLEMENTATION.md @@ -194,7 +194,7 @@ After Docker install, re-SSH so the `docker` group membership takes effect. |-----|------|-------|------|-------| | `company_site` | django (docker) | adama + roslin (+ ai-server-4080) | prod | active/active behind NPM; beta port reserved | | `dta_service` | django (docker) | adama + roslin + ai-server-4080 | beta + prod | active/active behind NPM | -| `dta_webapp` | node/vite static | adama + roslin (+ ai-server-4080) | beta + prod | active/active; built to `/var/www/.app.ditchtheagent/html`, served by web-static nginx | +| `dta_webapp` | node/vite static | adama + roslin (+ ai-server-4080) | beta + prod | active/active; built to `/var/www/.realpath.app/html`, served by web-static nginx | | `scha` | django (docker) | adama + roslin + ai-server-4080 | prod | active/active behind NPM; beta port reserved | | `chat_web_app` | node-static (CRA) | adama + roslin + ai-server-4080 | beta + prod | active/active; built to `/var/www/.chat.aimloperations/html`, served by web-static nginx | | `chat_backend` | django (docker) | adama + roslin + ai-server-4080 | beta + prod | active/active behind NPM; Ollama `http://10.0.0.128:11434`; SearxNG `http://10.0.0.128:8088` (`SEARXNG_BASE_URL`) | @@ -256,10 +256,12 @@ chat_backend secrets must use `SEARXNG_BASE_URL=http://10.0.0.128:8088`. to host `{{ apps_env_dir }}` → git checkout at ref → copy `.env` into checkout → `docker compose build` → `up -d` → migrate (run once, shared DB). - **node-static**: git checkout at ref → `npm ci` → `npm run build:` - (writes to the app's `webroot_pattern`, e.g. `/var/www/{env}.app.ditchtheagent/html` + (writes to the app's `webroot_pattern`, e.g. `/var/www/{env}.realpath.app/html` or `/var/www/{env}.chat.aimloperations/html`). - **web-static** role: one nginx container per app host serving the static roots - on their ports (from `host_apps`); NPM balances across hosts. + on their ports (from `host_apps`); NPM balances across hosts. Before `compose up`, + removes any container currently publishing those host ports (`docker ps --filter + publish=`) so leftovers cannot block the bind, then recreates web-static. ### Reverse proxy / load balancing (NPM at 10.0.0.230) @@ -289,7 +291,7 @@ point each domain at the backend(s): with a call to `server-infra/scripts/deploy.sh --app --env --ref ` (keep the test/docker jobs). - [ ] `dta_webapp`: `npm run build:beta` / `build:prod` output to - `/var/www/beta.app.ditchtheagent/html` / `/var/www/prod.app.ditchtheagent/html`. + `/var/www/beta.realpath.app/html` / `/var/www/prod.realpath.app/html`. - [ ] `chat_web_app`: `npm run build:beta` / `build:prod` output to `/var/www/beta.chat.aimloperations/html` / `/var/www/prod.chat.aimloperations/html`. diff --git a/inventory/group_vars/all.yml b/inventory/group_vars/all.yml index 7a2ff04..0b459c0 100644 --- a/inventory/group_vars/all.yml +++ b/inventory/group_vars/all.yml @@ -77,7 +77,7 @@ app_catalog: subdir: ditch-the-agent # Document root served by nginx and written by `npm run build:`. # {env} is replaced with the entry's env (beta/prod). - webroot_pattern: "/var/www/{env}.app.ditchtheagent/html" + webroot_pattern: "/var/www/{env}.realpath.app/html" # deploy runs `npm ci` then `npm run build:`; that script writes to # {{ web_static_root }}/_dta_webapp (beta/prod), served by web-static. scha: diff --git a/roles/web-static/tasks/main.yml b/roles/web-static/tasks/main.yml index a30ee35..3e42432 100644 --- a/roles/web-static/tasks/main.yml +++ b/roles/web-static/tasks/main.yml @@ -39,9 +39,34 @@ mode: "0644" register: _compose_file + # Free host ports before bind. Catches leftover/orphan containers from older + # compose projects that still publish 8080/8081/etc. Includes the current + # web-static container when present — compose up recreates it next. + - name: web-static | find containers publishing static ports + ansible.builtin.command: + cmd: docker ps -q --filter publish={{ item }} + loop: "{{ _static_apps | map(attribute='port') | list | unique }}" + register: _port_holders + changed_when: false + become: true + + - name: web-static | unique container ids on static ports + ansible.builtin.set_fact: + _stale_port_cids: "{{ _port_holders.results | map(attribute='stdout_lines') | flatten | unique | list }}" + + - name: web-static | remove containers holding static ports + ansible.builtin.command: + cmd: "docker rm -f {{ _stale_port_cids | join(' ') }}" + when: _stale_port_cids | length > 0 + become: true + changed_when: true + register: _stale_port_rm + - name: web-static | start container ansible.builtin.command: - cmd: "docker compose up -d --remove-orphans{{ ' --force-recreate' if (_compose_file is changed) else '' }}" + cmd: >- + docker compose up -d --remove-orphans + {{ '--force-recreate' if (_compose_file is changed or (_stale_port_rm is changed)) else '' }} chdir: "{{ web_static_dir }}" become: true become_user: "{{ admin_user }}" @@ -53,5 +78,8 @@ chdir: "{{ web_static_dir }}" become: true become_user: "{{ admin_user }}" - when: _nginx_conf is changed and _compose_file is not changed + when: + - _nginx_conf is changed + - _compose_file is not changed + - _stale_port_rm is not changed changed_when: true