Add MCP task creation endpoint

This commit is contained in:
巫凤萍
2026-08-07 11:59:45 +08:00
parent bd9b0c3871
commit 1a9a113229
8 changed files with 579 additions and 77 deletions

View File

@@ -23,8 +23,8 @@ import {
FeishuSourceError,
readFeishuSource,
type FeishuBindings,
type FeishuSource,
} from "../../../lib/feishu-client";
import { createDistributionTask } from "../../../lib/task-service";
export const runtime = "edge";
@@ -38,76 +38,6 @@ function numberValue(value: unknown, fallback = 0) {
return Number.isFinite(parsed) ? parsed : fallback;
}
async function createTaskFromSource(
source: FeishuSource,
name: string,
brand: string,
dueAt: string,
) {
const db = getRawDb();
const taskId = uid("task");
const shareToken = crypto.randomUUID().replaceAll("-", "");
await db
.prepare(
`INSERT INTO tasks
(id, name, brand, quantity, claimed_quantity, due_at, status,
source_url, source_sheet_id, source_sheet_name, source_synced_at,
share_token)
VALUES (?, ?, ?, ?, 0, ?, 'importing', ?, ?, ?, ?, ?)`,
)
.bind(
taskId,
name,
brand,
source.rows.length,
dueAt,
source.url,
source.sheetId,
source.sheetName,
source.syncedAt,
shareToken,
)
.run();
try {
const contentStatements = source.rows.map((row) => {
const contentId = uid("content");
const imageAssets = row.images.map((image) => ({
...image,
key: `content-assets/${taskId}/${contentId}/${image.index}`,
}));
return db
.prepare(
`INSERT INTO contents
(id, task_id, title, body, image_assets, status, source, source_row)
VALUES (?, ?, ?, ?, ?, 'available', ?, ?)`,
)
.bind(
contentId,
taskId,
row.title,
row.body,
JSON.stringify(imageAssets),
`飞书 · ${source.sheetName}`,
row.sourceRow,
);
});
for (let index = 0; index < contentStatements.length; index += 100) {
await db.batch(contentStatements.slice(index, index + 100));
}
await db
.prepare("UPDATE tasks SET status = 'active' WHERE id = ?")
.bind(taskId)
.run();
} catch (error) {
await db.batch([
db.prepare("DELETE FROM contents WHERE task_id = ?").bind(taskId),
db.prepare("DELETE FROM tasks WHERE id = ?").bind(taskId),
]);
throw error;
}
}
export async function POST(request: Request) {
if (!(await isAdminRequest(request))) return adminForbidden();
try {
@@ -140,11 +70,15 @@ export async function POST(request: Request) {
{ status: 400 },
);
}
const source = await readFeishuSource(
String(body.feishuUrl ?? "").trim(),
await createDistributionTask(
{
feishuUrl: String(body.feishuUrl ?? "").trim(),
name,
brand,
dueAt,
},
env as unknown as FeishuBindings,
);
await createTaskFromSource(source, name, brand, dueAt);
} else if (body.action === "claim") {
const partnerId = String(body.partnerId ?? "");
const taskId = String(body.taskId ?? "");