串接說明

用 HTTP API 把系統錯誤、日報或 Job 結果送到 Operations Chat。Job Execution 是一等公民,不要只把 Cron 結果當普通文字訊息。

1. 基底網址

http://evt.nexus-ray.com

瀏覽器走 /api/...;OpenAPI:http://evt.nexus-ray.com/docs

2. 驗證

一般訊息:帳號 JWT(operator 或 admin)

curl -sS -X POST http://evt.nexus-ray.com/api/auth/token \
  -H "Content-Type: application/json" \
  -d '{"email":"operator@example.com","password":"your-password"}'

# 回應含 access_token

之後請求加 Authorization: Bearer <access_token>。Token 約 20 分鐘過期,再打一次 /api/auth/token。

若帳號已啟用 Authenticator 雙因素驗證,第一步會回 401 且 detail.mfa_required 為 true,請用 mfa_token 與 6 位驗證碼呼叫 POST /api/auth/token/mfa 取得 token。

Job Runner:內部 Token

環境變數 INTERNAL_TOKEN,放在標頭 X-Internal-Token。不走使用者帳號。

X-Internal-Token: change-me-internal-runner-token

3. 查頻道

curl -sS http://evt.nexus-ray.com/api/channels \
  -H "Authorization: Bearer $TOKEN"

記下要發話的 id(UUID)。畫面「新增頻道」建立後也會出現在此列表。

4. 發送訊息

需要 operator 或 admin。權限不足會 403。

curl -sS -X POST http://evt.nexus-ray.com/api/channels/$CHANNEL_ID/messages \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "磁碟使用率 95%",
    "kind": "error",
    "severity": "critical"
  }'
欄位說明
body必填,訊息本文(摘要,不要塞完整 log)
kindtext / error / warning / job_event
severity可選 info / warning / error / critical
thread_id可選,回覆某則訊息的 ULID

5. Job 執行結果(建議)

一次執行只投影 一則 頻道訊息;開始後用同一個 id 回報完成,訊息會原地更新。

TOKEN_HDR="X-Internal-Token: change-me-internal-runner-token"

# 開始
curl -sS -X POST http://evt.nexus-ray.com/api/internal/executions \
  -H "$TOKEN_HDR" -H "Content-Type: application/json" \
  -d '{"job_name":"daily_sftp_sync","trigger":"cron","summary":"開始同步"}'

# 完成(把 EXEC_ID 換成上一步回傳的 id)
curl -sS -X PATCH http://evt.nexus-ray.com/api/internal/executions/$EXEC_ID \
  -H "$TOKEN_HDR" -H "Content-Type: application/json" \
  -d '{
    "status": "failed",
    "exit_code": 1,
    "summary": "SFTP 連線逾時",
    "error_message": "connection timed out",
    "log_s3_key": "logs/daily_sftp_sync/2026-08-13/exec-id.log"
  }'

status 只能是 success、failed、timeout。完整 log 請上傳 S3 後只傳 log_s3_key。

6. Python 範例

import os, httpx

BASE = "http://evt.nexus-ray.com"
token = httpx.post(f"{BASE}/api/auth/token", json={
    "email": os.environ["OPS_EMAIL"],
    "password": os.environ["OPS_PASSWORD"],
}).json()["access_token"]

httpx.post(
    f"{BASE}/api/channels/{os.environ['OPS_CHANNEL_ID']}/messages",
    headers={"Authorization": f"Bearer {token}"},
    json={"body": "備份完成", "kind": "text", "severity": "info"},
).raise_for_status()

注意

  • 直接開 /api/auth/me 沒帶 Token 會 401,屬正常。
  • 過濾規則命中後會依群組選用的通知方式(郵件/LINE Message)送到已設定的多個信箱或 userId,與頻道訊息分開。
  • 請用 HTTPS 與專用 operator 帳號,不要把 admin 密碼寫進 Job。