- Add remote/orchestrator filter by workplace (remote, not remote, unknown) with URL param - Expose isRemote on job list API; canonicalize URLs and source_job_id dedup on import - Onboarding: optional VITE_SKIP_RXRESUME_ONBOARDING for RxResume-free onboarding - Scoring UI + docs for company skip list; pipeline-run dedup note - Vitest: TZ=UTC for stable time-based tests - DEPLOY_GITEA_VM_CRON_TELEGRAM.md for VM/cron/Telegram ops Made-with: Cursor
180 lines
5.9 KiB
Markdown
180 lines
5.9 KiB
Markdown
# Deploy on a VM or container, run the pipeline on a schedule, notify Telegram
|
||
|
||
This guide assumes you already pushed this repo to Gitea, for example:
|
||
|
||
```bash
|
||
git remote add gitea gitea@10.0.30.169:ilia/Jobber.git # or: git remote set-url gitea ...
|
||
git push -u gitea main
|
||
```
|
||
|
||
If you have **uncommitted** changes, commit them first, then push again:
|
||
|
||
```bash
|
||
git add -A && git commit -m "Your message" && git push gitea main
|
||
```
|
||
|
||
---
|
||
|
||
## 1. Deploy on a Linux VM (bare metal or cloud)
|
||
|
||
1. Install **Docker** and **Docker Compose** (plugin v2).
|
||
2. Clone from your Gitea server (SSH or HTTPS):
|
||
|
||
```bash
|
||
git clone gitea@10.0.30.169:ilia/Jobber.git
|
||
cd Jobber # or job-ops if you kept that folder name
|
||
```
|
||
|
||
3. Copy and edit environment:
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
# Edit .env: MODEL / LLM keys, RXRESUME_*, search settings, etc.
|
||
```
|
||
|
||
4. Start the stack:
|
||
|
||
```bash
|
||
docker compose up -d
|
||
```
|
||
|
||
5. Open the UI: `http://<VM-IP>:3005` (port mapped in `docker-compose.yml`).
|
||
|
||
6. Persist data: the compose file mounts `./data` — back up that directory.
|
||
|
||
---
|
||
|
||
## 2. Deploy as a container (same image, any host)
|
||
|
||
Same as the VM path: only Docker is required. On the VM:
|
||
|
||
- Ensure port **3005** (or your chosen host port) is reachable if you use the UI from another machine.
|
||
- For **only** API/cron use from localhost, you can bind to `127.0.0.1:3005` by changing the `ports:` line in `docker-compose.yml` if you edit it (e.g. `"127.0.0.1:3005:3001"`).
|
||
|
||
Inside the container the app listens on **3001**; the host maps **3005 → 3001** by default.
|
||
|
||
**Cron on the host** should call the API on the host:
|
||
|
||
- UI: `http://127.0.0.1:3005` (browser)
|
||
- **API (orchestrator)**: `http://127.0.0.1:3005` — same port; requests to `/api/...` are served by the app behind the reverse proxy built into the image.
|
||
|
||
If your setup exposes the API only on an internal Docker network, use the container name and port `3001` from another container, or publish `3005` on the host and use `127.0.0.1:3005` from cron.
|
||
|
||
---
|
||
|
||
## 3. Run the pipeline three times a day (cron)
|
||
|
||
`POST /api/pipeline/run` **starts** the pipeline in the **background** and returns immediately (`{ ok: true, data: { message: "Pipeline started" } }`). That is enough for scheduling.
|
||
|
||
Example **crontab** entries (host time zone — adjust hours as you like):
|
||
|
||
```cron
|
||
# 08:00, 14:00, 20:00 daily — trigger JobOps pipeline
|
||
0 8,14,20 * * * /usr/local/bin/jobops-pipeline-run.sh >> /var/log/jobops-pipeline.log 2>&1
|
||
```
|
||
|
||
Create `/usr/local/bin/jobops-pipeline-run.sh`:
|
||
|
||
```bash
|
||
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
BASE_URL="${JOBOPS_URL:-http://127.0.0.1:3005}"
|
||
# If you set BASIC_AUTH_USER / BASIC_AUTH_PASSWORD in .env, uncomment:
|
||
# AUTH=(-u "${BASIC_AUTH_USER:?}:${BASIC_AUTH_PASSWORD:?}")
|
||
|
||
curl -sS -X POST "${BASE_URL}/api/pipeline/run" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{}' \
|
||
"${AUTH[@]:-}" \
|
||
| tee -a /var/log/jobops-pipeline.log
|
||
echo >> /var/log/jobops-pipeline.log
|
||
```
|
||
|
||
```bash
|
||
sudo chmod +x /usr/local/bin/jobops-pipeline-run.sh
|
||
```
|
||
|
||
Optional: set `JOBOPS_URL` in root’s crontab or in `/etc/environment` if the app is on another host.
|
||
|
||
**Basic Auth:** When `BASIC_AUTH_USER` and `BASIC_AUTH_PASSWORD` are set in `.env`, all non-GET API calls need Basic auth — use `curl -u user:pass` as above.
|
||
|
||
---
|
||
|
||
## 4. Telegram notifications
|
||
|
||
JobOps does **not** send Telegram directly. Practical options:
|
||
|
||
### Option A — Pipeline webhook (recommended)
|
||
|
||
1. In the app: **Settings → Webhooks** (or env `PIPELINE_WEBHOOK_URL` / `WEBHOOK_SECRET`) set a URL that receives JSON when a run **completes or fails**.
|
||
2. Point that URL to a **small relay** that translates the JSON into a Telegram `sendMessage` call.
|
||
|
||
Telegram API:
|
||
|
||
```text
|
||
https://api.telegram.org/bot<BOT_TOKEN>/sendMessage
|
||
```
|
||
|
||
Body (JSON):
|
||
|
||
```json
|
||
{
|
||
"chat_id": "<YOUR_CHAT_ID>",
|
||
"text": "Pipeline finished: ..."
|
||
}
|
||
```
|
||
|
||
You can host the relay on the same VM (Flask/FastAPI/Node, or **n8n** / **Webhook.site** + automation). Keep the **bot token** and **chat id** in env vars, not in the JobOps UI if possible.
|
||
|
||
Webhook payload shape (sanitized) includes fields like `event`, `pipelineRunId`, `jobsDiscovered`, `jobsProcessed`, `error` — see server code `notify-webhook.ts`.
|
||
|
||
### Option B — Cron wrapper: poll status, then Telegram
|
||
|
||
Because `/api/pipeline/run` returns before the run finishes, a simple approach:
|
||
|
||
1. Cron calls `jobops-pipeline-run.sh` (as above).
|
||
2. A **second** script (or same script extended) polls `GET /api/pipeline/status` until `isRunning` is false, then reads `GET /api/pipeline/runs` for the latest run and sends a short message via `curl` to Telegram.
|
||
|
||
Example **send** (replace token and chat id):
|
||
|
||
```bash
|
||
TELEGRAM_BOT_TOKEN="123456:ABC..."
|
||
CHAT_ID="your_numeric_chat_id"
|
||
MSG="$(printf 'JobOps pipeline finished. Check dashboard.')"
|
||
curl -sS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||
-H "Content-Type: application/json" \
|
||
-d "{\"chat_id\":\"${CHAT_ID}\",\"text\":$(echo "$MSG" | jq -Rs .)}"
|
||
```
|
||
|
||
Get **chat_id**: message your bot, then open `https://api.telegram.org/bot<TOKEN>/getUpdates` and read `message.chat.id`.
|
||
|
||
### Option C — External automation
|
||
|
||
Use **n8n**, **Grafana OnCall**, or similar: trigger on schedule → HTTP POST ` /api/pipeline/run` → wait/poll → Telegram node.
|
||
|
||
---
|
||
|
||
## 5. Security notes
|
||
|
||
- Do not commit `.env` or Telegram tokens to Git.
|
||
- Prefer **Basic Auth** on the instance if it is reachable from the internet.
|
||
- Restrict firewall so only your IP (or VPN) can reach port 3005 if exposed.
|
||
|
||
---
|
||
|
||
## 6. Git remotes quick reference
|
||
|
||
```bash
|
||
git remote -v
|
||
git push gitea main # your Gitea
|
||
git push origin main # upstream GitHub (if you have rights)
|
||
```
|
||
|
||
---
|
||
|
||
## Related project docs
|
||
|
||
- Self-hosting: docs site **Self-Hosting** guide (if present in your tree).
|
||
- Webhooks: **Settings** documentation for pipeline / job-complete webhooks.
|
||
- Optional env: `PIPELINE_WEBHOOK_URL`, `WEBHOOK_SECRET`, `BASIC_AUTH_USER`, `BASIC_AUTH_PASSWORD` in `.env.example`.
|