119 lines
3.6 KiB
JavaScript
119 lines
3.6 KiB
JavaScript
// ═══════════════════════════════════════
|
|
// 铸渊开发Agent · Notion 桥接模块
|
|
// 读取规划数据库 · 写入执行进度
|
|
// ═══════════════════════════════════════
|
|
|
|
const https = require("https");
|
|
|
|
const NOTION_API = "https://api.notion.com/v1";
|
|
const NOTION_VERSION = "2022-06-28";
|
|
|
|
function getHeaders() {
|
|
const token = process.env.NOTION_TOKEN;
|
|
if (!token) {
|
|
console.warn("[NotionBridge] NOTION_TOKEN 未配置");
|
|
return null;
|
|
}
|
|
return {
|
|
"Authorization": "Bearer " + token,
|
|
"Content-Type": "application/json",
|
|
"Notion-Version": NOTION_VERSION
|
|
};
|
|
}
|
|
|
|
function notionRequest(endpoint, method, body) {
|
|
return new Promise((resolve, reject) => {
|
|
const headers = getHeaders();
|
|
if (!headers) return reject(new Error("NOTION_TOKEN 未配置"));
|
|
|
|
const url = new URL(NOTION_API + endpoint);
|
|
const options = {
|
|
hostname: url.hostname,
|
|
path: url.pathname + url.search,
|
|
method: method,
|
|
headers: headers
|
|
};
|
|
|
|
const req = https.request(options, (res) => {
|
|
let data = "";
|
|
res.on("data", (chunk) => data += chunk);
|
|
res.on("end", () => {
|
|
try {
|
|
const json = JSON.parse(data);
|
|
if (res.statusCode >= 400) {
|
|
reject(new Error("Notion API " + res.statusCode + ": " + (json.message || data)));
|
|
} else {
|
|
resolve(json);
|
|
}
|
|
} catch (e) {
|
|
reject(new Error("解析失败: " + data.slice(0, 200)));
|
|
}
|
|
});
|
|
});
|
|
|
|
req.on("error", reject);
|
|
if (body) req.write(JSON.stringify(body));
|
|
req.end();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 从 Notion 数据库读取规划
|
|
* @param {string} databaseId - Notion 数据库 ID
|
|
* @returns {Promise<Array>} 规划条目列表
|
|
*/
|
|
async function fetchPlans(databaseId) {
|
|
const result = await notionRequest("/databases/" + databaseId + "/query", "POST", {
|
|
sorts: [{ property: "Created", direction: "descending" }],
|
|
page_size: 10
|
|
});
|
|
|
|
return (result.results || []).map((page) => {
|
|
const props = page.properties || {};
|
|
return {
|
|
notion_page_id: page.id,
|
|
title: props.Name?.title?.[0]?.plain_text || "无标题",
|
|
status: props.Status?.status?.name || "unknown",
|
|
plan_id: props.PlanID?.rich_text?.[0]?.plain_text || "",
|
|
created: page.created_time
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 更新 Notion 中某条规划的状态
|
|
* @param {string} pageId - Notion 页面 ID
|
|
* @param {string} status - 新状态
|
|
* @param {object} details - 额外详情
|
|
*/
|
|
async function updatePlanStatus(pageId, status, details = {}) {
|
|
const properties = {
|
|
Status: { status: { name: status } }
|
|
};
|
|
|
|
if (details.log_url) {
|
|
properties.LogURL = { url: details.log_url };
|
|
}
|
|
|
|
return notionRequest("/pages/" + pageId, "PATCH", { properties });
|
|
}
|
|
|
|
/**
|
|
* 向 Notion 数据库追加执行日志
|
|
* @param {string} databaseId - Notion 数据库 ID
|
|
* @param {object} entry - 日志条目 { plan_id, task_id, action, result, time }
|
|
*/
|
|
async function appendLog(databaseId, entry) {
|
|
return notionRequest("/pages", "POST", {
|
|
parent: { database_id: databaseId },
|
|
properties: {
|
|
Name: { title: [{ text: { content: entry.plan_id + " · " + entry.task_id } }] },
|
|
Status: { status: { name: entry.result === "ok" ? "Done" : "Failed" } },
|
|
LogTime: { date: { start: entry.time || new Date().toISOString() } },
|
|
Detail: { rich_text: [{ text: { content: entry.detail || entry.action } }] }
|
|
}
|
|
});
|
|
}
|
|
|
|
module.exports = { fetchPlans, updatePlanStatus, appendLog };
|