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 };