← back to Ventura Claw

server/connectors/notion.js

35 lines

// Notion — REAL. Internal integration token (ntn_...) is simplest.
const API = "https://api.notion.com/v1";
const VERSION = "2022-06-28";
function token(c) {
  const t = c?.NOTION_TOKEN || process.env.NOTION_TOKEN;
  if (!t) throw new Error("NOTION_TOKEN not set");
  return t;
}
async function call(method, path, body, c) {
  const r = await fetch(`${API}${path}`, {
    method,
    headers: { "Authorization": `Bearer ${token(c)}`, "Notion-Version": VERSION, "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined
  });
  const d = await r.json();
  if (!r.ok) throw new Error(`notion ${path} ${r.status}: ${d.message || JSON.stringify(d).slice(0,200)}`);
  return d;
}
module.exports = {
  meta: { id: "notion", name: "Notion", category: "tasks", docsUrl: "https://developers.notion.com/", auth: "oauth2", realImpl: true },
  fields: [{ key: "NOTION_TOKEN", label: "Internal Integration Token", type: "password", required: true, hint: "ntn_... — notion.so/my-integrations. Must SHARE each db/page with your integration." }],
  configured(c) { return !!(c?.NOTION_TOKEN || process.env.NOTION_TOKEN); },
  async health(c) {
    if (!this.configured(c)) return { ok: false, reason: "no_token" };
    try { const d = await call("GET", "/users/me", null, c); return { ok: true, bot: d.name, type: d.type }; }
    catch (e) { return { ok: false, reason: e.message }; }
  },
  actions: {
    async "search"({ query, page_size }, c) { return call("POST", "/search", { query: query||"", page_size: page_size||25 }, c); },
    async "databases.query"({ database_id, page_size }, c) { return call("POST", `/databases/${database_id}/query`, { page_size: page_size||25 }, c); },
    async "page.create"({ parent, properties, children }, c) { return call("POST", "/pages", { parent, properties, children }, c); },
    async "block.append"({ block_id, children }, c) { return call("PATCH", `/blocks/${block_id}/children`, { children }, c); }
  }
};