import dotenv from 'dotenv'; import { clearBlogFolder, saveFile } from "./joplin.mjs"; import path from "path"; import { fileURLToPath } from "url"; import fs from "fs"; import extract from 'extract-zip'; dotenv.config(); const __dirname = path.dirname(fileURLToPath(import.meta.url)); const blogDir = path.join(__dirname,'../content/blog/'); const zipPath = path.join(blogDir,'/siyuan.zip'); let apiBase = process.env.SIYUAN_API_BASE ? process.env.SIYUAN_API_BASE : await findSiYuanPort(); async function checkPort(base, port) { const response = await fetch(base + port + '/api/notebook/lsNotebooks', { method: 'POST', headers: { 'Content-Type': 'application/json' }, }); if(response.status !== 200) { return false; } const respJson = await response.json(); if(respJson.code === 0) { console.log(`Found SiYuan port: ${port}`); return true; } } async function findSiYuanPort() { let port; const base = 'http://127.0.0.1:'; if(await checkPort(base, 6806)) { // default port in most cases return base + 6806; } console.log("Starting to look for SiYuan port (long scan)...") for(port = 1024; port <= 65535; port++) { try { if(await checkPort(base, port)) { return base + port; } }catch(e){} } console.log("Couldn't find SiYuan port, make sure it's running!"); return null; } async function getNotebookDownload(notebookID) { const response = await fetch(apiBase + '/api/export/exportNotebookMd', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ 'notebook': notebookID }) }); if(response.status !== 200) { throw new Error(`Failed to download notebook. Status code: ${response.status}`); } const respJson = await response.json(); return apiBase + respJson.data.zip; } async function findMarkdownFiles(dirPath) { const entries = fs.readdirSync(dirPath, { withFileTypes: true }); return entries .filter(entry => entry.isFile()) .filter(entry => entry.name.endsWith('.md')) .map(entry => path.join(dirPath, entry.name)); } async function transformBlogPost(filePath) { // Read the file content const content = fs.readFileSync(filePath, 'utf-8'); // Split into lines and process const lines = content.split('\n'); let newContent = []; let inFrontmatter = false; let frontmatterFound = false; for (let line of lines) { // Handle frontmatter code fences if (!frontmatterFound && !inFrontmatter && line.startsWith('\`\`\`yaml')) { inFrontmatter = true; frontmatterFound = true; continue; // Remove the opening fence } if(inFrontmatter && line.startsWith('ogImage: ')) { line = line.replace('ogImage: assets/', 'ogImage: ./assets/'); }else{ line = line.replace('](assets/', '](./assets/'); // asset in markdown } if (inFrontmatter && line.startsWith('\`\`\`')) { inFrontmatter = false; continue; // Remove the closing fence } // Remove all lines before frontmatter if (!frontmatterFound) { continue; } if(!inFrontmatter) { line += ' ' // add spaces before newline } newContent.push(line); } // Join lines and write back to file fs.writeFileSync(filePath, newContent.join('\n')); } async function main() { console.log('Preparing for SiYuan download...') if(!process.env.SIYUAN_NOTEBOOK_ID) { console.error('required env variable is not set'); return; } let downloadLink = await getNotebookDownload(process.env.SIYUAN_NOTEBOOK_ID); await clearBlogFolder(); fs.mkdirSync(blogDir, { recursive: true }); await saveFile(downloadLink, zipPath); await extract(zipPath, { dir: blogDir }); for (const filePath of await findMarkdownFiles(blogDir)) { console.log("Processing...", filePath); void transformBlogPost(filePath); } } if (import.meta.url === `file://${process.argv[1]}`) { main().catch(console.error); }