Files

186 lines
4.8 KiB
JavaScript

#!/usr/bin/env node
/**
* Add markdown file as a page in a Notion database
* Usage: node add-to-database.js <database-id> <page-title> <markdown-file-path>
*/
const fs = require('fs');
const https = require('https');
const NOTION_API_KEY = process.env.NOTION_API_KEY;
const NOTION_VERSION = '2025-09-03';
function notionRequest(path, method, data) {
return new Promise((resolve, reject) => {
const options = {
hostname: 'api.notion.com',
port: 443,
path: path,
method: method,
headers: {
'Authorization': `Bearer ${NOTION_API_KEY}`,
'Notion-Version': NOTION_VERSION,
'Content-Type': 'application/json'
}
};
const req = https.request(options, (res) => {
let body = '';
res.on('data', (chunk) => body += chunk);
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(JSON.parse(body));
} else {
reject(new Error(`HTTP ${res.statusCode}: ${body}`));
}
});
});
req.on('error', reject);
if (data) req.write(JSON.stringify(data));
req.end();
});
}
function parseRichText(text) {
const richText = [];
const maxLength = 2000;
if (text.length <= maxLength) {
richText.push({
type: 'text',
text: { content: text }
});
} else {
richText.push({
type: 'text',
text: { content: text.substring(0, maxLength) }
});
}
return richText;
}
function parseMarkdown(markdown) {
const lines = markdown.split('\n');
const blocks = [];
let currentParagraph = [];
const flushParagraph = () => {
if (currentParagraph.length > 0) {
const text = currentParagraph.join('\n').trim();
if (text && text.length > 0) {
blocks.push({
type: 'paragraph',
paragraph: {
rich_text: parseRichText(text)
}
});
}
currentParagraph = [];
}
};
for (let line of lines) {
if (line.startsWith('### ')) {
flushParagraph();
blocks.push({
type: 'heading_3',
heading_3: { rich_text: parseRichText(line.substring(4)) }
});
} else if (line.startsWith('## ')) {
flushParagraph();
blocks.push({
type: 'heading_2',
heading_2: { rich_text: parseRichText(line.substring(3)) }
});
} else if (line.startsWith('# ')) {
flushParagraph();
blocks.push({
type: 'heading_1',
heading_1: { rich_text: parseRichText(line.substring(2)) }
});
} else if (line.startsWith('---')) {
flushParagraph();
blocks.push({ type: 'divider', divider: {} });
} else if (line.trim() === '') {
flushParagraph();
} else {
currentParagraph.push(line);
}
}
flushParagraph();
return blocks;
}
(async () => {
// Parse arguments
const args = process.argv.slice(2);
if (args.length < 3) {
console.error('Usage: node add-to-database.js <database-id> <page-title> <markdown-file-path>');
console.error('\nExample:');
console.error(' node add-to-database.js abc123-db-id "Research Report" research.md');
process.exit(1);
}
const [dbId, title, mdPath] = args;
// Validate inputs
if (!fs.existsSync(mdPath)) {
console.error(`Error: File not found: ${mdPath}`);
process.exit(1);
}
console.log('Adding page to database...');
console.log(` Database: ${dbId}`);
console.log(` Title: ${title}`);
console.log(` Source: ${mdPath}\n`);
// Create database page
const pageData = {
parent: {
type: 'database_id',
database_id: dbId
},
properties: {
'Name': {
title: [{ text: { content: title } }]
}
}
};
console.log('Creating database entry...');
const page = await notionRequest('/v1/pages', 'POST', pageData);
console.log(`✓ Page created: ${page.id}`);
console.log(` URL: https://notion.so/${page.id.replace(/-/g, '')}`);
// Read and parse markdown
const markdown = fs.readFileSync(mdPath, 'utf8');
const blocks = parseMarkdown(markdown);
console.log(`\nParsed ${blocks.length} blocks from markdown`);
// Add content in batches
const batchSize = 100;
for (let i = 0; i < blocks.length; i += batchSize) {
const batch = blocks.slice(i, i + batchSize);
console.log(`Adding blocks ${i + 1}-${Math.min(i + batchSize, blocks.length)}...`);
await notionRequest(`/v1/blocks/${page.id}/children`, 'PATCH', {
children: batch
});
if (i + batchSize < blocks.length) {
await new Promise(resolve => setTimeout(resolve, 350));
}
}
console.log(`\n✅ Successfully added to database!`);
console.log(`📄 URL: https://notion.so/${page.id.replace(/-/g, '')}`);
console.log(`\n💡 Add additional properties (Type, Tags, Status, etc.) manually in Notion`);
})().catch(error => {
console.error('Error:', error.message);
process.exit(1);
});