From b075006d0f092128308f344015ab91752c8cc4ca Mon Sep 17 00:00:00 2001 From: Ryan Westfall Date: Sat, 25 Jul 2026 06:26:29 -0500 Subject: [PATCH 1/2] Wire node-static deploy for chat_web_app (closes #13). Add build:prod/beta webroot scripts, Gitea CI/deploy workflows, and bake API URLs from REACT_APP_* so hosts can deploy via server-infra like dta_webapp. --- .gitea/workflows/deploy-beta.yml | 37 +++++++++++ .gitea/workflows/deploy-prod.yml | 22 +++++++ .gitea/workflows/unit-tests.yml | 28 +++++++++ README.md | 63 +++++++++++++++++++ llm-fe/.env.beta | 2 + llm-fe/package.json | 2 + llm-fe/src/axiosApi.js | 4 +- .../src/llm-fe/contexts/WebSocketContext.js | 6 +- 8 files changed, 156 insertions(+), 8 deletions(-) create mode 100644 .gitea/workflows/deploy-beta.yml create mode 100644 .gitea/workflows/deploy-prod.yml create mode 100644 .gitea/workflows/unit-tests.yml create mode 100644 llm-fe/.env.beta diff --git a/.gitea/workflows/deploy-beta.yml b/.gitea/workflows/deploy-beta.yml new file mode 100644 index 0000000..59d6d03 --- /dev/null +++ b/.gitea/workflows/deploy-beta.yml @@ -0,0 +1,37 @@ +name: Deploy Beta + +on: + workflow_dispatch: {} + +jobs: + unit-tests: + runs-on: self-hosted + defaults: + run: + working-directory: llm-fe + steps: + - uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install dependencies + run: npm ci + + - name: Run unit tests + run: npm run test:ci + + deploy-beta: + needs: unit-tests + runs-on: self-hosted + env: + SERVER_INFRA_ROOT: /home/westfarn/Documents/repos/server-infra + steps: + - name: Deploy chat_web_app beta + run: | + "$SERVER_INFRA_ROOT/scripts/deploy.sh" \ + --app chat_web_app \ + --env beta \ + --ref "${{ gitea.sha }}" diff --git a/.gitea/workflows/deploy-prod.yml b/.gitea/workflows/deploy-prod.yml new file mode 100644 index 0000000..bb055e3 --- /dev/null +++ b/.gitea/workflows/deploy-prod.yml @@ -0,0 +1,22 @@ +name: Deploy Prod + +# Runs after Unit Tests completes on master. Direct pushes only (not PRs). +on: + workflow_run: + workflows: [Unit Tests] + types: [completed] + branches: [master] + +jobs: + deploy: + if: gitea.event.workflow_run.conclusion == 'success' && gitea.event.workflow_run.event == 'push' + runs-on: self-hosted + env: + SERVER_INFRA_ROOT: /home/westfarn/Documents/repos/server-infra + steps: + - name: Deploy chat_web_app prod + run: | + "$SERVER_INFRA_ROOT/scripts/deploy.sh" \ + --app chat_web_app \ + --env prod \ + --ref "${{ gitea.event.workflow_run.head_sha }}" diff --git a/.gitea/workflows/unit-tests.yml b/.gitea/workflows/unit-tests.yml new file mode 100644 index 0000000..00c774f --- /dev/null +++ b/.gitea/workflows/unit-tests.yml @@ -0,0 +1,28 @@ +name: Unit Tests + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + test: + runs-on: self-hosted + defaults: + run: + working-directory: llm-fe + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install dependencies + run: npm ci + + - name: Run unit tests + run: npm run test:ci diff --git a/README.md b/README.md index e69de29..ef17bd7 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,63 @@ +# Chat Bot Frontend (`chat_web_app`) + +CRA/React app under `llm-fe/`. Production serve is **node-static** (not Docker): +hosts build with npm and write into `/var/www/...`; the shared **web-static** nginx +container serves those roots. Deploy is driven by `server-infra`. + +## Local development + +```bash +cd llm-fe +npm ci +npm start +``` + +Uses `.env.development` (API → local `chat_backend`, usually `http://127.0.0.1:8001`). + +## Environment files (CRA `REACT_APP_*`) + +| File | When used | Backend | +|------|-----------|---------| +| `.env.development` | `npm start` | local backend | +| `.env.production` | `npm run build` / `build:prod` | `https://chatbackend.aimloperations.com` | +| `.env.beta` | `npm run build:beta` | `https://beta.chatbackend.aimloperations.com` | + +Baked into the JS bundle at build time. No host secret file needed for this +frontend (unlike Django apps under `~/Documents/secrets/`). + +Required vars: + +- `REACT_APP_BACKEND_REST_API_BASE_URL` — REST base (trailing `/api/`) +- `REACT_APP_BACKEND_WS_API_BASE_URL` — WebSocket URL (full `ws://` / `wss://` path) + +## Production / beta build (host deploy) + +`server-infra` runs `npm ci` then `npm run build:` in `llm-fe/`. Those +scripts write static assets into: + +| Script | Document root | +|--------|----------------| +| `npm run build:prod` | `/var/www/prod.chat.aimloperations/html` | +| `npm run build:beta` | `/var/www/beta.chat.aimloperations/html` | + +Paths must match `server-infra` `app_catalog.chat_web_app.webroot_pattern`. + +Manual deploy from the control node: + +```bash +~/Documents/repos/server-infra/scripts/deploy.sh \ + --app chat_web_app --env prod --ref +``` + +## CI / CD (Gitea) + +- `.gitea/workflows/unit-tests.yml` — tests on push/PR to `master` +- `.gitea/workflows/deploy-prod.yml` — after green Unit Tests on `master` push +- `.gitea/workflows/deploy-beta.yml` — manual (`workflow_dispatch`) + +## Tests + +```bash +cd llm-fe +npm run test:ci +``` diff --git a/llm-fe/.env.beta b/llm-fe/.env.beta new file mode 100644 index 0000000..bfcd820 --- /dev/null +++ b/llm-fe/.env.beta @@ -0,0 +1,2 @@ +REACT_APP_BACKEND_REST_API_BASE_URL=https://beta.chatbackend.aimloperations.com/api/ +REACT_APP_BACKEND_WS_API_BASE_URL=wss://beta.chatbackend.aimloperations.com/ws/chat_again/ diff --git a/llm-fe/package.json b/llm-fe/package.json index 4073984..a6714c1 100644 --- a/llm-fe/package.json +++ b/llm-fe/package.json @@ -40,6 +40,8 @@ "scripts": { "start": "NODE_ENV=development react-scripts start", "build": "NODE_ENV=production react-scripts build", + "build:prod": "NODE_ENV=production react-scripts build && mkdir -p /var/www/prod.chat.aimloperations/html && cp -r ./build/* /var/www/prod.chat.aimloperations/html/", + "build:beta": "bash -c 'set -a; source .env.beta; set +a; NODE_ENV=production react-scripts build' && mkdir -p /var/www/beta.chat.aimloperations/html && cp -r ./build/* /var/www/beta.chat.aimloperations/html/", "test": "NODE_ENV=development react-scripts test", "test:ci": "CI=true NODE_ENV=development react-scripts test --watchAll=false --coverage=false", "eject": "react-scripts eject" diff --git a/llm-fe/src/axiosApi.js b/llm-fe/src/axiosApi.js index 79fbd08..5eab397 100644 --- a/llm-fe/src/axiosApi.js +++ b/llm-fe/src/axiosApi.js @@ -1,9 +1,7 @@ import axios from "axios"; const Cookies = require("js-cookie"); -const baseURL = "http://localhost:8011/api/"; -//const baseURL = 'https://chatbackend.aimloperations.com/api/'; -//const baseURL = process.env.REACT_APP_BACKEND_REST_API_BASE_URL; +const baseURL = process.env.REACT_APP_BACKEND_REST_API_BASE_URL; export const axiosInstance = axios.create({ baseURL: baseURL, diff --git a/llm-fe/src/llm-fe/contexts/WebSocketContext.js b/llm-fe/src/llm-fe/contexts/WebSocketContext.js index b86292d..7abc398 100644 --- a/llm-fe/src/llm-fe/contexts/WebSocketContext.js +++ b/llm-fe/src/llm-fe/contexts/WebSocketContext.js @@ -70,11 +70,7 @@ function WebSocketProvider({ children }) { useEffect(() => { /* WS initialization and cleanup */ if (account) { - ws.current = new WebSocket(`ws://localhost:8011/ws/chat_again/`); - //ws.current = new WebSocket(`ws://localhost:8011/ws/conditional_chat/`); - //ws.current = new WebSocket('wss://chatbackend.aimloperations.com/ws/chat_again/') - //ws.current = new WebSocket('wss://chatbackend.aimloperations.com/ws/conditional_chat/') - //ws.current = process.env.REACT_APP_BACKEND_WS_API_BASE_URL; + ws.current = new WebSocket(process.env.REACT_APP_BACKEND_WS_API_BASE_URL); ws.current.onopen = () => { setSocket(ws.current); -- 2.54.0 From 91ab3182815d1622ac6082df3ad042b2c612b63f Mon Sep 17 00:00:00 2001 From: Ryan Westfall Date: Sat, 25 Jul 2026 06:33:26 -0500 Subject: [PATCH 2/2] Expand README with local setup, env, scripts, and deploy docs. Match the dta_webapp README shape and drop the outdated lowercase stub. --- README.md | 174 +++++++++++++++++++++++++++++++++++++++++++----------- readme.md | 15 ----- 2 files changed, 139 insertions(+), 50 deletions(-) delete mode 100644 readme.md diff --git a/README.md b/README.md index ef17bd7..65e5e82 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,151 @@ -# Chat Bot Frontend (`chat_web_app`) +# Chat Web App (`chat_web_app`) -CRA/React app under `llm-fe/`. Production serve is **node-static** (not Docker): -hosts build with npm and write into `/var/www/...`; the shared **web-static** nginx -container serves those roots. Deploy is driven by `server-infra`. +Frontend for [chat.aimloperations.com](https://chat.aimloperations.com) — a React + +TypeScript single-page app for the AI ML Operations chat product. -## Local development +Companion backend: [chat_backend](https://git.aimloperations.com/ai_ml_operations/chat_backend) -```bash -cd llm-fe -npm ci -npm start +Production serve is **node-static** (not Docker). Hosts build with npm into a +document root; the shared **web-static** nginx container from +[server-infra](https://git.aimloperations.com/ai_ml_operations/server-infra) serves +those assets. Same pattern as `dta_webapp`. + +## Stack + +- **React 18**, **TypeScript**, **Create React App** (`react-scripts` 5) +- **Material UI (MUI) 5** + Emotion for components and theming +- **React Router 6** for routing +- **Axios** for REST against `chat_backend` +- **WebSocket** for live chat (`REACT_APP_BACKEND_WS_API_BASE_URL`) +- **Jest** + React Testing Library for unit tests + +## Repository layout + +``` +chat_web_app/ +├── README.md +├── .gitea/workflows/ # Unit tests + deploy-prod / deploy-beta +└── llm-fe/ # CRA application (npm root) + ├── package.json + ├── .env.development # Local dev API/WS URLs + ├── .env.beta + ├── .env.production + ├── public/ + └── src/ + ├── axiosApi.js # Axios instances (JWT + CSRF) + ├── App.tsx + ├── index.tsx + └── llm-fe/ + ├── pages/ # Route-level views + ├── components/ # Shared & feature UI + ├── contexts/ # Auth, account, WebSocket, theme + └── ui-kit/ # MD* design-system wrappers ``` -Uses `.env.development` (API → local `chat_backend`, usually `http://127.0.0.1:8001`). +## Prerequisites -## Environment files (CRA `REACT_APP_*`) +- **Node.js 20** (matches CI) +- **npm** (lockfile: `llm-fe/package-lock.json`) +- Running [chat_backend](https://git.aimloperations.com/ai_ml_operations/chat_backend) + locally for full chat/auth (default in `.env.development`: port **8001**) -| File | When used | Backend | +## Local setup + +### 1. Clone and install + +```bash +git clone ssh://git@git.aimloperations.com:30009/ai_ml_operations/chat_web_app.git +cd chat_web_app/llm-fe +npm ci +``` + +### 2. Configure environment + +CRA loads mode-specific env files. Variables must be prefixed with `REACT_APP_` +to be visible in the browser bundle. + +| File | Used when | Backend | |------|-----------|---------| -| `.env.development` | `npm start` | local backend | +| `.env.development` | `npm start` | `http://127.0.0.1:8001` | | `.env.production` | `npm run build` / `build:prod` | `https://chatbackend.aimloperations.com` | | `.env.beta` | `npm run build:beta` | `https://beta.chatbackend.aimloperations.com` | -Baked into the JS bundle at build time. No host secret file needed for this -frontend (unlike Django apps under `~/Documents/secrets/`). +Required keys (already set in the committed env files): -Required vars: +```env +REACT_APP_BACKEND_REST_API_BASE_URL=http://127.0.0.1:8001/api/ +REACT_APP_BACKEND_WS_API_BASE_URL=ws://127.0.0.1:8001/ws/chat_again/ +``` -- `REACT_APP_BACKEND_REST_API_BASE_URL` — REST base (trailing `/api/`) -- `REACT_APP_BACKEND_WS_API_BASE_URL` — WebSocket URL (full `ws://` / `wss://` path) +These are **baked into the JS at build time**. There is no host secret file for +this frontend (unlike Django apps under `~/Documents/secrets/`). Change the +committed `.env.*` files if API domains change, then redeploy. -## Production / beta build (host deploy) +Optional local overrides (gitignored): `.env.local`, `.env.development.local`, +`.env.production.local`. -`server-infra` runs `npm ci` then `npm run build:` in `llm-fe/`. Those -scripts write static assets into: +### 3. Start the backend -| Script | Document root | -|--------|----------------| -| `npm run build:prod` | `/var/www/prod.chat.aimloperations/html` | -| `npm run build:beta` | `/var/www/beta.chat.aimloperations/html` | +From the `chat_backend` repo (adjust to however that service is run locally): -Paths must match `server-infra` `app_catalog.chat_web_app.webroot_pattern`. +```bash +# example — follow chat_backend README for the current command +cd chat_backend +# run Django on the port matching .env.development (8001) +``` + +### 4. Start the dev server + +```bash +cd llm-fe +npm start +``` + +Open **http://localhost:3000**. + +## npm scripts + +| Command | Purpose | +|---------|---------| +| `npm start` | CRA dev server (port 3000) | +| `npm run build` | Production build into `llm-fe/build/` | +| `npm run build:prod` | Prod build + copy to `/var/www/prod.chat.aimloperations/html` | +| `npm run build:beta` | Beta env build + copy to `/var/www/beta.chat.aimloperations/html` | +| `npm test` | Jest watch mode | +| `npm run test:ci` | Single CI run (`CI=true`, no watch) | + +`build:prod` / `build:beta` are intended for deploy hosts (or a machine that can +write those `/var/www/...` paths). Paths must match +`server-infra` `app_catalog.chat_web_app.webroot_pattern`: + +```text +/var/www/{env}.chat.aimloperations/html +``` + +## Tests + +```bash +cd llm-fe +npm test # watch +npm run test:ci # one-shot (matches Gitea Unit Tests) +``` + +## CI / deployment + +Gitea Actions workflows in `.gitea/workflows/`: + +| Workflow | Trigger | Behavior | +|----------|---------|----------| +| `unit-tests.yml` | push/PR to `master` | `npm ci` + `npm run test:ci` in `llm-fe/` | +| `deploy-prod.yml` | after green **Unit Tests** on `master` **push** | `server-infra/scripts/deploy.sh --app chat_web_app --env prod --ref ` | +| `deploy-beta.yml` | `workflow_dispatch` | tests, then deploy `--env beta` | + +Deploy flow (on each webserver listed in `host_apps`): + +1. Git checkout at the pinned ref under `/opt/apps/src/chat_web_app_` +2. `npm ci` in `llm-fe/` +3. `npm run build:` → writes the static tree under `/var/www/...` +4. **web-static** nginx serves that root on the host port (prod **8082**, beta **8083** if enabled) Manual deploy from the control node: @@ -49,15 +154,14 @@ Manual deploy from the control node: --app chat_web_app --env prod --ref ``` -## CI / CD (Gitea) +NPM (not managed by Ansible) should balance public chat domains to: -- `.gitea/workflows/unit-tests.yml` — tests on push/PR to `master` -- `.gitea/workflows/deploy-prod.yml` — after green Unit Tests on `master` push -- `.gitea/workflows/deploy-beta.yml` — manual (`workflow_dispatch`) +- `adama:8082` + `roslin:8082` + `ai-server-4080:8082` (prod) -## Tests +See [server-infra IMPLEMENTATION.md](https://git.aimloperations.com/ai_ml_operations/server-infra). -```bash -cd llm-fe -npm run test:ci -``` +## Related repos + +- **Backend:** [chat_backend](https://git.aimloperations.com/ai_ml_operations/chat_backend) — Django API + WebSocket +- **Infra:** [server-infra](https://git.aimloperations.com/ai_ml_operations/server-infra) — `app_catalog`, `host_apps`, `deploy.sh`, web-static +- **Pattern reference:** [dta_webapp](https://git.aimloperations.com/Ditch_The_Agent/dta_webapp) — node-static frontend deploy diff --git a/readme.md b/readme.md deleted file mode 100644 index 4c02e75..0000000 --- a/readme.md +++ /dev/null @@ -1,15 +0,0 @@ -# Chat Bot Frontend - -## Setup - -To run -```console -npm start -``` - -## TODO - - [ ] Fix the refresh issue - - [ ] Make the account page work - - [ ] Make a manage user card for the account - - [ ] Get a working conversation screen - - [ ] Move the 'JWT ' to a variable \ No newline at end of file -- 2.54.0