feat(web): add Ops page and proxy control on :8600
Vite listens on 8500 and proxies /ops to the control plane. Admin Ops UI covers pause/resume, scale, drain/force restart, and restart-needed banner. Co-authored-by: Nasyarobby Putra <nasyarobby@gmail.com>
This commit is contained in:
@@ -13,14 +13,24 @@ Workflow runner with a sandboxed script engine, SQLite run history, and an admin
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
# Redis required for the workflow queue
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
- UI (dev): http://localhost:8500
|
||||
- API: http://localhost:8700
|
||||
- UI (dev): http://localhost:5173
|
||||
|
||||
The first account created becomes **admin**. Later accounts are created from Users.
|
||||
|
||||
### Process modes
|
||||
|
||||
| Command | Processes | Ports |
|
||||
|---|---|---|
|
||||
| `pnpm dev` | Monolith (`runner.js` = API + worker) + Vite | UI **8500**, API **8700** |
|
||||
| `pnpm dev:pm2` | Control + PM2 HTTP + PM2 workers + Vite | UI **8500**, control **8600**, API **8700** |
|
||||
|
||||
`pnpm dev:pm2` is the mode for Ops (start/stop HTTP, scale workers, drain restart). Control owns SQLite migrations; HTTP/workers do not migrate.
|
||||
|
||||
## Script contract
|
||||
|
||||
Each script is `async function main(ctx)` and **must** return:
|
||||
@@ -50,13 +60,31 @@ Optional `script.meta.reads = "ctx"` documents expression hosts. `meta.input` /
|
||||
|
||||
| Command | Description |
|
||||
|---|---|
|
||||
| `pnpm dev` | Server + Vite together |
|
||||
| `pnpm dev:server` | API/runner only |
|
||||
| `pnpm dev:web` | UI only (proxies `/api` to :8700) |
|
||||
| `pnpm dev` | Monolith server + Vite (no PM2) |
|
||||
| `pnpm dev:pm2` | Control + PM2 HTTP/workers + Vite (Ops UI) |
|
||||
| `pnpm dev:server` | Monolith API/runner only |
|
||||
| `pnpm dev:web` | UI only (proxies `/api` → :8700, `/ops` → :8600) |
|
||||
| `pnpm build` | Production UI build |
|
||||
| `pnpm start` | Serve API and built UI from :8700 |
|
||||
| `pnpm start` | Monolith: API + worker + built UI |
|
||||
| `pnpm start:control` | Control plane only (migrates, manages PM2 children) |
|
||||
| `pnpm start:api` | HTTP API + cron enqueue (`JFLOW_ROLE=api`) |
|
||||
| `pnpm start:worker` | BullMQ worker only |
|
||||
| `pnpm migrate` | Apply SQLite migrations |
|
||||
|
||||
## Ops (control plane)
|
||||
|
||||
Admin UI route **Ops** (`/ops`) talks to the control process.
|
||||
|
||||
| Action | Behavior |
|
||||
|---|---|
|
||||
| Pause / resume | BullMQ `queue.pause()` / `resume()` — cron/HTTP still enqueue |
|
||||
| Reload workflows | Redis pub/sub → all live HTTP/worker processes re-read YAML |
|
||||
| Scale workers | PM2 scale; scale-down drains active jobs unless `force` |
|
||||
| Drain restart | Pause → wait active=0 → stop children → migrate → recreate → resume |
|
||||
| Force restart | Same without waiting (interrupts active runs; orphans marked `worker_lost`) |
|
||||
|
||||
Desired state is stored in `packages/server/data/control-state.json` (generation, worker count, restart-needed). Plugin installs (later) bump generation and set restart-needed; you apply with Drain restart.
|
||||
|
||||
## Environment
|
||||
|
||||
| Variable | Default | Notes |
|
||||
@@ -68,11 +96,13 @@ Optional `script.meta.reads = "ctx"` documents expression hosts. `meta.input` /
|
||||
| `REDIS_PASS` | — | Optional Redis AUTH password (sent via ioredis `password`). Prefer this over embedding credentials in `REDIS_URL` so logs stay clean. |
|
||||
| `JFLOW_QUEUE_NAME` | `jerapah-workflows` | BullMQ queue name. |
|
||||
| `JFLOW_WORKER_CONCURRENCY` | `5` | Max parallel workflow jobs per worker process. |
|
||||
| `JFLOW_ROLE` | `all` | Which duties this process performs: `all` (HTTP/admin + cron producer + worker), `api` (HTTP/admin + cron enqueue only), or `worker` (consume queue only). Use separate processes in production when you want to scale workers independently. |
|
||||
| `JFLOW_ROLE` | `all` | `all` (HTTP + cron + worker), `api`, or `worker`. Prefer `pnpm start:api` / `start:worker` under control. |
|
||||
| `JFLOW_CONFIG_GENERATION` | `1` | Set by control/PM2 so children report config generation in heartbeats. |
|
||||
| `JFLOW_CONTROL_PORT` | `8600` | Control ops API port. |
|
||||
| `JFLOW_LOG_LEVEL` | `debug` | Pino level |
|
||||
| `JFLOW_RETENTION_DAYS` | `30` | Run history prune |
|
||||
| `JFLOW_CORS_ORIGIN` | `http://localhost:5173` | Vite origin in dev |
|
||||
| `PORT` | `8700` | HTTP port |
|
||||
| `JFLOW_CORS_ORIGIN` | `http://localhost:8500` | Vite origin in dev |
|
||||
| `PORT` | `8700` | HTTP API port |
|
||||
| `NODE_ENV` | — | Set `production` for secure cookies |
|
||||
|
||||
Workflow runs are **queued** via BullMQ. HTTP and manual triggers return `202 { runId, status: "queued" }` immediately; poll `GET /api/runs/:id` for progress (`queued` → `running` → `success` \| `failed`). Cron remains an in-process producer that enqueues jobs on each tick.
|
||||
@@ -83,7 +113,10 @@ Workflow runs are **queued** via BullMQ. HTTP and manual triggers return `202 {
|
||||
pnpm install
|
||||
pnpm build
|
||||
# Redis must be reachable at REDIS_URL (set REDIS_PASS if Redis requires AUTH)
|
||||
JFLOW_JWT_SECRET=... JFLOW_SECRETS_KEY=... REDIS_URL=redis://127.0.0.1:6379 REDIS_PASS=... NODE_ENV=production pnpm start
|
||||
# Recommended: run control (migrates + manages PM2 HTTP/workers)
|
||||
JFLOW_JWT_SECRET=... JFLOW_SECRETS_KEY=... REDIS_URL=redis://127.0.0.1:6379 REDIS_PASS=... NODE_ENV=production pnpm start:control
|
||||
# Or monolith (dev-style):
|
||||
# ... pnpm start
|
||||
```
|
||||
|
||||
The server serves `packages/web/dist` when that folder exists.
|
||||
With control, serve the built UI from Vite preview, a reverse proxy, or set `JFLOW_SERVE_UI=1` on the HTTP process.
|
||||
|
||||
Reference in New Issue
Block a user