Jobber/DEPLOY_GITEA_VM_CRON_TELEGRAM.md
ilia 9576c3d7a1 feat: workplace filter, job dedup, company skip docs, deploy notes
- 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
2026-04-04 14:44:52 -04:00

5.9 KiB
Raw Blame History

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:

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:

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):

    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:

    cp .env.example .env
    # Edit .env: MODEL / LLM keys, RXRESUME_*, search settings, etc.
    
  4. Start the stack:

    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):

# 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:

#!/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
sudo chmod +x /usr/local/bin/jobops-pipeline-run.sh

Optional: set JOBOPS_URL in roots 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:

  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:

https://api.telegram.org/bot<BOT_TOKEN>/sendMessage

Body (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):

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

git remote -v
git push gitea main          # your Gitea
git push origin main         # upstream GitHub (if you have rights)

  • 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.