Files
boss/src/app/api/v1/master-agent/tasks/[taskId]/recovery/route.ts
2026-06-06 19:12:18 +08:00

102 lines
3.2 KiB
TypeScript

import { NextRequest } from "next/server";
import { jsonNoStore } from "@/lib/api-response";
import { requireRequestSession } from "@/lib/boss-auth";
import { requireCsrfSafeMutation } from "@/lib/boss-csrf";
import {
canRetryMasterAgentTaskSafely,
getMasterAgentTask,
type MasterAgentTask,
retryRecoverableMasterAgentTask,
} from "@/lib/boss-data";
function stringValue(value: unknown) {
return typeof value === "string" ? value.trim() : "";
}
async function paramsTaskId(context: { params: Promise<{ taskId: string }> }) {
const params = await context.params;
return params.taskId;
}
function forbidden() {
return jsonNoStore({ ok: false, message: "FORBIDDEN" }, { status: 403 });
}
function recoveryProjection(task: MasterAgentTask) {
const phase = task.phase ?? task.status;
const lastProgressAt = task.lastProgressAt ?? task.claimedAt ?? task.requestedAt;
const canRetry = canRetryMasterAgentTaskSafely(task);
return {
taskId: task.taskId,
projectId: task.projectId,
deviceId: task.deviceId,
status: task.status,
phase,
canRetry,
safeNextAction: canRetry ? "retry" : task.status === "needs_user_action" ? "user_action" : "inspect",
diagnosis: `任务处于 ${phase},最后进度时间 ${lastProgressAt}`,
lastErrorCode: task.lastErrorCode,
lastProgressAt,
};
}
export async function GET(
request: NextRequest,
context: { params: Promise<{ taskId: string }> },
) {
const session = await requireRequestSession(request);
if (!session) {
return jsonNoStore({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const taskId = await paramsTaskId(context);
const task = await getMasterAgentTask(taskId);
if (!task) {
return jsonNoStore({ ok: false, message: "MASTER_AGENT_TASK_NOT_FOUND" }, { status: 404 });
}
if (session.role !== "highest_admin" && task.requestedByAccount !== session.account) {
return forbidden();
}
return jsonNoStore({
ok: true,
recovery: recoveryProjection(task),
});
}
export async function POST(
request: NextRequest,
context: { params: Promise<{ taskId: string }> },
) {
const csrf = requireCsrfSafeMutation(request);
if (csrf) return csrf;
const session = await requireRequestSession(request);
if (!session) {
return jsonNoStore({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
if (session.role !== "highest_admin") {
return forbidden();
}
const body = (await request.json().catch(() => ({}))) as Record<string, unknown>;
const action = stringValue(body.action);
if (action !== "retry") {
return jsonNoStore({ ok: false, message: "TASK_RECOVERY_ACTION_INVALID" }, { status: 400 });
}
const taskId = await paramsTaskId(context);
try {
const task = await retryRecoverableMasterAgentTask({
taskId,
actorAccount: session.account,
reason: stringValue(body.reason) || "管理员从恢复面板重试任务",
});
return jsonNoStore({ ok: true, action, task: recoveryProjection(task) });
} catch (error) {
const message = error instanceof Error ? error.message : "TASK_RECOVERY_FAILED";
const status = message === "MASTER_AGENT_TASK_NOT_FOUND" ? 404 : 400;
return jsonNoStore({ ok: false, message }, { status });
}
}