feat: expose boss via nginx base path
This commit is contained in:
10
README.md
10
README.md
@@ -93,7 +93,7 @@ BOSS_DATA_FILE=.boss-data/local-dev.json npm run dev
|
|||||||
默认云端入口:
|
默认云端入口:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
http://111.231.132.51:43210
|
http://111.231.132.51/boss/
|
||||||
```
|
```
|
||||||
|
|
||||||
这就是你当前最短的“主账号对话入口”。
|
这就是你当前最短的“主账号对话入口”。
|
||||||
@@ -154,7 +154,7 @@ npm run worker -- \
|
|||||||
打开云端控制台:
|
打开云端控制台:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
http://111.231.132.51:43210
|
http://111.231.132.51/boss/
|
||||||
```
|
```
|
||||||
|
|
||||||
这是最符合产品策略的入口,也是主控面。
|
这是最符合产品策略的入口,也是主控面。
|
||||||
@@ -163,9 +163,9 @@ http://111.231.132.51:43210
|
|||||||
仓库里自带一个简单 CLI,可以直接发消息给 Boss:
|
仓库里自带一个简单 CLI,可以直接发消息给 Boss:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
BOSS_SERVER_URL=http://111.231.132.51:43210 ./scripts/boss_chat.sh create "Boss 主控对话"
|
BOSS_SERVER_URL=http://111.231.132.51/boss ./scripts/boss_chat.sh create "Boss 主控对话"
|
||||||
BOSS_SERVER_URL=http://111.231.132.51:43210 ./scripts/boss_chat.sh send "先调研这个问题,不要急着改代码。"
|
BOSS_SERVER_URL=http://111.231.132.51/boss ./scripts/boss_chat.sh send "先调研这个问题,不要急着改代码。"
|
||||||
BOSS_SERVER_URL=http://111.231.132.51:43210 ./scripts/boss_chat.sh status
|
BOSS_SERVER_URL=http://111.231.132.51/boss ./scripts/boss_chat.sh status
|
||||||
```
|
```
|
||||||
|
|
||||||
这条 CLI 入口后面也很容易改造成 Telegram / Slack / 企业微信 webhook。
|
这条 CLI 入口后面也很容易改造成 Telegram / Slack / 企业微信 webhook。
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
PORT: 43210
|
PORT: 43210
|
||||||
BOSS_DATA_FILE: .boss-data/cloud-store.json
|
BOSS_DATA_FILE: .boss-data/cloud-store.json
|
||||||
|
BOSS_BASE_PATH: /boss
|
||||||
ports:
|
ports:
|
||||||
- "43210:43210"
|
- "43210:43210"
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -10,6 +10,24 @@ const state = {
|
|||||||
banner: null,
|
banner: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function normalizeBasePath(pathname) {
|
||||||
|
if (!pathname || pathname === "/") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
const appBasePath = normalizeBasePath(new URL(".", import.meta.url).pathname);
|
||||||
|
|
||||||
|
function resolveUrl(path) {
|
||||||
|
if (/^https?:\/\//.test(path)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${appBasePath}${path}`;
|
||||||
|
}
|
||||||
|
|
||||||
const TASK_GROUPS = [
|
const TASK_GROUPS = [
|
||||||
{ title: "进行中", statuses: ["assigned", "running"] },
|
{ title: "进行中", statuses: ["assigned", "running"] },
|
||||||
{ title: "等待处理", statuses: ["planning", "queued"] },
|
{ title: "等待处理", statuses: ["planning", "queued"] },
|
||||||
@@ -90,7 +108,7 @@ function renderBanner() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function request(url, options = {}) {
|
async function request(url, options = {}) {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(resolveUrl(url), {
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
@@ -791,7 +809,7 @@ elements.resetDemo.addEventListener("click", async () => {
|
|||||||
render();
|
render();
|
||||||
});
|
});
|
||||||
|
|
||||||
const stream = new EventSource("/api/events/stream");
|
const stream = new EventSource(resolveUrl("/api/events/stream"));
|
||||||
|
|
||||||
stream.onopen = () => {
|
stream.onopen = () => {
|
||||||
const wasReconnecting = state.connection === "reconnecting";
|
const wasReconnecting = state.connection === "reconnecting";
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Boss Control Plane</title>
|
<title>Boss Control Plane</title>
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
|
||||||
<link rel="stylesheet" href="/styles.css" />
|
<link rel="stylesheet" href="./styles.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="shell">
|
<div class="shell">
|
||||||
@@ -107,6 +107,6 @@
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="module" src="/app.js"></script>
|
<script type="module" src="./app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ export CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
|||||||
export AG_SERVER_SKILL="${AG_SERVER_SKILL:-$CODEX_HOME/skills/ai-glasses-server-debug}"
|
export AG_SERVER_SKILL="${AG_SERVER_SKILL:-$CODEX_HOME/skills/ai-glasses-server-debug}"
|
||||||
export AG_SERVER="${AG_SERVER:-$AG_SERVER_SKILL/scripts/server_ssh.sh}"
|
export AG_SERVER="${AG_SERVER:-$AG_SERVER_SKILL/scripts/server_ssh.sh}"
|
||||||
|
|
||||||
"$AG_SERVER" exec "set -euo pipefail; cd $(printf '%q' "$remote_dir"); if sudo docker ps --format '{{.Names}}' | grep -qx 'boss-control-plane'; then sudo docker compose -f compose.cloud.yaml -p boss ps; echo '---'; curl -fsS http://127.0.0.1:43210/api/health; else ./scripts/server_status.sh; fi"
|
"$AG_SERVER" exec "set -euo pipefail; cd $(printf '%q' "$remote_dir"); if sudo docker ps --format '{{.Names}}' | grep -qx 'boss-control-plane'; then sudo docker compose -f compose.cloud.yaml -p boss ps; echo '---'; curl -fsS http://127.0.0.1:43210/boss/api/health; else BOSS_BASE_PATH=/boss ./scripts/server_status.sh; fi"
|
||||||
|
|||||||
@@ -45,11 +45,40 @@ if [[ "\$DEPLOY_MODE" != "node" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "\$docker_ok" -eq 1 ]]; then
|
if [[ "\$docker_ok" -eq 1 ]]; then
|
||||||
|
sudo python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
path = Path("/etc/nginx/sites-enabled/hybrid_updates.conf")
|
||||||
|
text = path.read_text()
|
||||||
|
if "location /boss/" not in text:
|
||||||
|
block = """
|
||||||
|
location = /boss {
|
||||||
|
return 302 /boss/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /boss/ {
|
||||||
|
proxy_pass http://127.0.0.1:43210;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
proxy_send_timeout 3600s;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
marker = " location / {\n"
|
||||||
|
text = text.replace(marker, block + marker, 1)
|
||||||
|
path.write_text(text)
|
||||||
|
PY
|
||||||
|
sudo nginx -t
|
||||||
|
sudo systemctl reload nginx
|
||||||
echo "__BOSS_DEPLOY_OK__"
|
echo "__BOSS_DEPLOY_OK__"
|
||||||
echo "mode=docker"
|
echo "mode=docker"
|
||||||
sudo docker compose -f compose.cloud.yaml -p boss ps
|
sudo docker compose -f compose.cloud.yaml -p boss ps
|
||||||
sleep 3
|
sleep 3
|
||||||
curl -fsS http://127.0.0.1:43210/api/health
|
curl -fsS http://127.0.0.1:43210/boss/api/health
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -60,10 +89,39 @@ fi
|
|||||||
|
|
||||||
npm install
|
npm install
|
||||||
npm run build
|
npm run build
|
||||||
PORT=43210 BOSS_DATA_FILE=.boss-data/cloud-store.json ./scripts/server_start.sh
|
PORT=43210 BOSS_DATA_FILE=.boss-data/cloud-store.json BOSS_BASE_PATH=/boss ./scripts/server_start.sh
|
||||||
|
sudo python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
path = Path("/etc/nginx/sites-enabled/hybrid_updates.conf")
|
||||||
|
text = path.read_text()
|
||||||
|
if "location /boss/" not in text:
|
||||||
|
block = """
|
||||||
|
location = /boss {
|
||||||
|
return 302 /boss/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /boss/ {
|
||||||
|
proxy_pass http://127.0.0.1:43210;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
proxy_send_timeout 3600s;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
marker = " location / {\n"
|
||||||
|
text = text.replace(marker, block + marker, 1)
|
||||||
|
path.write_text(text)
|
||||||
|
PY
|
||||||
|
sudo nginx -t
|
||||||
|
sudo systemctl reload nginx
|
||||||
echo "__BOSS_DEPLOY_OK__"
|
echo "__BOSS_DEPLOY_OK__"
|
||||||
echo "mode=node"
|
echo "mode=node"
|
||||||
./scripts/server_status.sh
|
BOSS_BASE_PATH=/boss ./scripts/server_status.sh
|
||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ data_dir="${BOSS_DATA_DIR:-.boss-data}"
|
|||||||
data_file="${BOSS_DATA_FILE:-$data_dir/server-store.json}"
|
data_file="${BOSS_DATA_FILE:-$data_dir/server-store.json}"
|
||||||
pid_file="${BOSS_PID_FILE:-$data_dir/server.pid}"
|
pid_file="${BOSS_PID_FILE:-$data_dir/server.pid}"
|
||||||
log_file="${BOSS_LOG_FILE:-$data_dir/server.log}"
|
log_file="${BOSS_LOG_FILE:-$data_dir/server.log}"
|
||||||
|
base_path="${BOSS_BASE_PATH:-}"
|
||||||
|
if [[ -n "$base_path" && "$base_path" != /* ]]; then
|
||||||
|
base_path="/$base_path"
|
||||||
|
fi
|
||||||
|
base_path="${base_path%/}"
|
||||||
|
health_url="http://127.0.0.1:${port}${base_path}/api/health"
|
||||||
|
|
||||||
mkdir -p "$data_dir"
|
mkdir -p "$data_dir"
|
||||||
|
|
||||||
@@ -27,7 +33,7 @@ nohup env PORT="$port" BOSS_DATA_FILE="$data_file" node dist/server.js >>"$log_f
|
|||||||
echo $! > "$pid_file"
|
echo $! > "$pid_file"
|
||||||
|
|
||||||
for _ in {1..30}; do
|
for _ in {1..30}; do
|
||||||
if curl -fsS "http://127.0.0.1:${port}/api/health" >/dev/null 2>&1; then
|
if curl -fsS "$health_url" >/dev/null 2>&1; then
|
||||||
echo "Boss server started on :${port}"
|
echo "Boss server started on :${port}"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ port="${PORT:-43210}"
|
|||||||
data_dir="${BOSS_DATA_DIR:-.boss-data}"
|
data_dir="${BOSS_DATA_DIR:-.boss-data}"
|
||||||
pid_file="${BOSS_PID_FILE:-$data_dir/server.pid}"
|
pid_file="${BOSS_PID_FILE:-$data_dir/server.pid}"
|
||||||
log_file="${BOSS_LOG_FILE:-$data_dir/server.log}"
|
log_file="${BOSS_LOG_FILE:-$data_dir/server.log}"
|
||||||
|
base_path="${BOSS_BASE_PATH:-}"
|
||||||
|
if [[ -n "$base_path" && "$base_path" != /* ]]; then
|
||||||
|
base_path="/$base_path"
|
||||||
|
fi
|
||||||
|
base_path="${base_path%/}"
|
||||||
|
health_url="http://127.0.0.1:${port}${base_path}/api/health"
|
||||||
|
|
||||||
if [[ -f "$pid_file" ]]; then
|
if [[ -f "$pid_file" ]]; then
|
||||||
pid="$(cat "$pid_file")"
|
pid="$(cat "$pid_file")"
|
||||||
@@ -18,7 +24,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "---"
|
echo "---"
|
||||||
curl -fsS "http://127.0.0.1:${port}/api/health" || true
|
curl -fsS "$health_url" || true
|
||||||
echo
|
echo
|
||||||
echo "---"
|
echo "---"
|
||||||
tail -n 40 "$log_file" 2>/dev/null || true
|
tail -n 40 "$log_file" 2>/dev/null || true
|
||||||
|
|||||||
@@ -5,6 +5,22 @@ import { BossEngine } from "./engine.js";
|
|||||||
|
|
||||||
const engine = new BossEngine();
|
const engine = new BossEngine();
|
||||||
const app = Fastify({ logger: process.env.BOSS_DEBUG === "1" });
|
const app = Fastify({ logger: process.env.BOSS_DEBUG === "1" });
|
||||||
|
const basePath = normalizeBasePath(process.env.BOSS_BASE_PATH ?? "");
|
||||||
|
|
||||||
|
function normalizeBasePath(input: string) {
|
||||||
|
if (!input || input === "/") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const normalized = input.startsWith("/") ? input : `/${input}`;
|
||||||
|
return normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
function withBase(pathname: string) {
|
||||||
|
if (!basePath) {
|
||||||
|
return pathname;
|
||||||
|
}
|
||||||
|
return pathname === "/" ? `${basePath}/` : `${basePath}${pathname}`;
|
||||||
|
}
|
||||||
|
|
||||||
app.setErrorHandler((error, request, reply) => {
|
app.setErrorHandler((error, request, reply) => {
|
||||||
const message =
|
const message =
|
||||||
@@ -38,61 +54,61 @@ app.setErrorHandler((error, request, reply) => {
|
|||||||
|
|
||||||
await app.register(fastifyStatic, {
|
await app.register(fastifyStatic, {
|
||||||
root: path.resolve(process.cwd(), "public"),
|
root: path.resolve(process.cwd(), "public"),
|
||||||
prefix: "/",
|
prefix: withBase("/"),
|
||||||
});
|
});
|
||||||
|
|
||||||
function ok() {
|
function ok() {
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get("/api/health", async () => ({
|
app.get(withBase("/api/health"), async () => ({
|
||||||
status: "ok",
|
status: "ok",
|
||||||
sessions: engine.getState().sessions.length,
|
sessions: engine.getState().sessions.length,
|
||||||
workers: engine.getState().workers.length,
|
workers: engine.getState().workers.length,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
app.get("/api/bootstrap", async () => engine.bootstrap());
|
app.get(withBase("/api/bootstrap"), async () => engine.bootstrap());
|
||||||
|
|
||||||
app.get("/api/events", async (request) => {
|
app.get(withBase("/api/events"), async (request) => {
|
||||||
const query = request.query as { limit?: string };
|
const query = request.query as { limit?: string };
|
||||||
const limit = Number(query.limit ?? 100);
|
const limit = Number(query.limit ?? 100);
|
||||||
return engine.listEvents(Number.isFinite(limit) ? limit : 100);
|
return engine.listEvents(Number.isFinite(limit) ? limit : 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/api/sessions", async () => engine.listSessions());
|
app.get(withBase("/api/sessions"), async () => engine.listSessions());
|
||||||
|
|
||||||
app.post("/api/sessions", async (request) => {
|
app.post(withBase("/api/sessions"), async (request) => {
|
||||||
const body = (request.body ?? {}) as { title?: string };
|
const body = (request.body ?? {}) as { title?: string };
|
||||||
return engine.createSession(body.title);
|
return engine.createSession(body.title);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/sessions/:sessionId/archive", async (request) => {
|
app.post(withBase("/api/sessions/:sessionId/archive"), async (request) => {
|
||||||
const params = request.params as { sessionId: string };
|
const params = request.params as { sessionId: string };
|
||||||
return engine.archiveSession(params.sessionId);
|
return engine.archiveSession(params.sessionId);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/sessions/:sessionId/restore", async (request) => {
|
app.post(withBase("/api/sessions/:sessionId/restore"), async (request) => {
|
||||||
const params = request.params as { sessionId: string };
|
const params = request.params as { sessionId: string };
|
||||||
return engine.restoreSession(params.sessionId);
|
return engine.restoreSession(params.sessionId);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/api/sessions/:sessionId", async (request) => {
|
app.get(withBase("/api/sessions/:sessionId"), async (request) => {
|
||||||
const params = request.params as { sessionId: string };
|
const params = request.params as { sessionId: string };
|
||||||
return engine.getSession(params.sessionId);
|
return engine.getSession(params.sessionId);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/api/tasks/:taskId", async (request) => {
|
app.get(withBase("/api/tasks/:taskId"), async (request) => {
|
||||||
const params = request.params as { taskId: string };
|
const params = request.params as { taskId: string };
|
||||||
return engine.getTask(params.taskId);
|
return engine.getTask(params.taskId);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/sessions/:sessionId/messages", async (request) => {
|
app.post(withBase("/api/sessions/:sessionId/messages"), async (request) => {
|
||||||
const params = request.params as { sessionId: string };
|
const params = request.params as { sessionId: string };
|
||||||
const body = (request.body ?? {}) as { content?: string; channel?: string };
|
const body = (request.body ?? {}) as { content?: string; channel?: string };
|
||||||
return engine.addMessage(params.sessionId, body.content ?? "", body.channel ?? "web");
|
return engine.addMessage(params.sessionId, body.content ?? "", body.channel ?? "web");
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/api/events/stream", async (_request, reply) => {
|
app.get(withBase("/api/events/stream"), async (_request, reply) => {
|
||||||
reply.raw.setHeader("Content-Type", "text/event-stream");
|
reply.raw.setHeader("Content-Type", "text/event-stream");
|
||||||
reply.raw.setHeader("Cache-Control", "no-cache");
|
reply.raw.setHeader("Cache-Control", "no-cache");
|
||||||
reply.raw.setHeader("Connection", "keep-alive");
|
reply.raw.setHeader("Connection", "keep-alive");
|
||||||
@@ -113,14 +129,14 @@ app.get("/api/events/stream", async (_request, reply) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/api/workers", async () => engine.getState().workers);
|
app.get(withBase("/api/workers"), async () => engine.getState().workers);
|
||||||
|
|
||||||
app.get("/api/workers/:workerId", async (request) => {
|
app.get(withBase("/api/workers/:workerId"), async (request) => {
|
||||||
const params = request.params as { workerId: string };
|
const params = request.params as { workerId: string };
|
||||||
return engine.getWorker(params.workerId);
|
return engine.getWorker(params.workerId);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/workers/register", async (request) => {
|
app.post(withBase("/api/workers/register"), async (request) => {
|
||||||
const body = request.body as {
|
const body = request.body as {
|
||||||
name?: string;
|
name?: string;
|
||||||
os?: "windows" | "macos" | "linux";
|
os?: "windows" | "macos" | "linux";
|
||||||
@@ -133,25 +149,25 @@ app.post("/api/workers/register", async (request) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/workers/:workerId/heartbeat", async (request) => {
|
app.post(withBase("/api/workers/:workerId/heartbeat"), async (request) => {
|
||||||
const params = request.params as { workerId: string };
|
const params = request.params as { workerId: string };
|
||||||
const body = (request.body ?? {}) as { load?: number };
|
const body = (request.body ?? {}) as { load?: number };
|
||||||
return engine.heartbeat(params.workerId, body.load ?? 0);
|
return engine.heartbeat(params.workerId, body.load ?? 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/workers/:workerId/offline", async (request) => {
|
app.post(withBase("/api/workers/:workerId/offline"), async (request) => {
|
||||||
const params = request.params as { workerId: string };
|
const params = request.params as { workerId: string };
|
||||||
return engine.markWorkerOffline(params.workerId);
|
return engine.markWorkerOffline(params.workerId);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/workers/:workerId/claim-next", async (request) => {
|
app.post(withBase("/api/workers/:workerId/claim-next"), async (request) => {
|
||||||
const params = request.params as { workerId: string };
|
const params = request.params as { workerId: string };
|
||||||
return {
|
return {
|
||||||
task: engine.claimNextTask(params.workerId),
|
task: engine.claimNextTask(params.workerId),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/tasks/:taskId/progress", async (request) => {
|
app.post(withBase("/api/tasks/:taskId/progress"), async (request) => {
|
||||||
const params = request.params as { taskId: string };
|
const params = request.params as { taskId: string };
|
||||||
const body = request.body as {
|
const body = request.body as {
|
||||||
workerId: string;
|
workerId: string;
|
||||||
@@ -168,7 +184,7 @@ app.post("/api/tasks/:taskId/progress", async (request) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/tasks/:taskId/complete", async (request) => {
|
app.post(withBase("/api/tasks/:taskId/complete"), async (request) => {
|
||||||
const params = request.params as { taskId: string };
|
const params = request.params as { taskId: string };
|
||||||
const body = request.body as {
|
const body = request.body as {
|
||||||
workerId: string;
|
workerId: string;
|
||||||
@@ -177,7 +193,7 @@ app.post("/api/tasks/:taskId/complete", async (request) => {
|
|||||||
return engine.completeTask(params.taskId, body.workerId, body.summary);
|
return engine.completeTask(params.taskId, body.workerId, body.summary);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/tasks/:taskId/fail", async (request) => {
|
app.post(withBase("/api/tasks/:taskId/fail"), async (request) => {
|
||||||
const params = request.params as { taskId: string };
|
const params = request.params as { taskId: string };
|
||||||
const body = request.body as {
|
const body = request.body as {
|
||||||
workerId: string;
|
workerId: string;
|
||||||
@@ -186,27 +202,27 @@ app.post("/api/tasks/:taskId/fail", async (request) => {
|
|||||||
return engine.failTask(params.taskId, body.workerId, body.errorMessage);
|
return engine.failTask(params.taskId, body.workerId, body.errorMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/tasks/:taskId/pause", async (request) => {
|
app.post(withBase("/api/tasks/:taskId/pause"), async (request) => {
|
||||||
const params = request.params as { taskId: string };
|
const params = request.params as { taskId: string };
|
||||||
return engine.pauseTask(params.taskId);
|
return engine.pauseTask(params.taskId);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/tasks/:taskId/cancel", async (request) => {
|
app.post(withBase("/api/tasks/:taskId/cancel"), async (request) => {
|
||||||
const params = request.params as { taskId: string };
|
const params = request.params as { taskId: string };
|
||||||
return engine.cancelTask(params.taskId);
|
return engine.cancelTask(params.taskId);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/tasks/:taskId/resume", async (request) => {
|
app.post(withBase("/api/tasks/:taskId/resume"), async (request) => {
|
||||||
const params = request.params as { taskId: string };
|
const params = request.params as { taskId: string };
|
||||||
return engine.resumeTask(params.taskId);
|
return engine.resumeTask(params.taskId);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/tasks/:taskId/requeue", async (request) => {
|
app.post(withBase("/api/tasks/:taskId/requeue"), async (request) => {
|
||||||
const params = request.params as { taskId: string };
|
const params = request.params as { taskId: string };
|
||||||
return engine.requeueTask(params.taskId);
|
return engine.requeueTask(params.taskId);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/approvals/:approvalId/respond", async (request) => {
|
app.post(withBase("/api/approvals/:approvalId/respond"), async (request) => {
|
||||||
const params = request.params as { approvalId: string };
|
const params = request.params as { approvalId: string };
|
||||||
const body = request.body as {
|
const body = request.body as {
|
||||||
approved: boolean;
|
approved: boolean;
|
||||||
@@ -215,12 +231,12 @@ app.post("/api/approvals/:approvalId/respond", async (request) => {
|
|||||||
return engine.respondApproval(params.approvalId, body.approved, body.responder ?? "user");
|
return engine.respondApproval(params.approvalId, body.approved, body.responder ?? "user");
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/demo/reset", async (request) => {
|
app.post(withBase("/api/demo/reset"), async (request) => {
|
||||||
const body = (request.body ?? {}) as { preserveWorkers?: boolean };
|
const body = (request.body ?? {}) as { preserveWorkers?: boolean };
|
||||||
return engine.resetDemo(body.preserveWorkers ?? true);
|
return engine.resetDemo(body.preserveWorkers ?? true);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/api/reconcile", async () => engine.reconcileNow());
|
app.post(withBase("/api/reconcile"), async () => engine.reconcileNow());
|
||||||
|
|
||||||
const port = Number(process.env.PORT ?? 43210);
|
const port = Number(process.env.PORT ?? 43210);
|
||||||
await app.listen({ port, host: "0.0.0.0" });
|
await app.listen({ port, host: "0.0.0.0" });
|
||||||
|
|||||||
Reference in New Issue
Block a user