标签: Joplin

  • 使用Joplin作为博客后端-使用marked转换joplin笔记中资源文件的地址

    使用Joplin作为博客后端-使用marked转换joplin笔记中资源文件的地址

    joplin笔记中资源文件默认使用的是id,但是如果渲染到网页,需要转换为网络地址,记录一下处理过程。

    export async function transformMarkdown(markdown?: string) {
        if (markdown == null || markdown === '') {
            return ''
        }
    
        const renderer = new marked.Renderer()
    
        marked.use({
            async: true,
            async walkTokens(token) {
                if (token.type === "image") {
                    const id = token.href.replace(':/', '')
                    const resource = await joplinResource(id)
                    token.href = `${process.env.SITE_URL}/resources/${id}.${resource.file_extension}`
                    token.title = resource.title
                }
            },
            renderer: {
                image({ href, text, title }) {
                    return `<img src="${href}" alt="${text}" title="${title}" loading="lazy" />`
                }
            }
        })
    
        return await marked.parse(markdown, {
            renderer,
            gfm: true,
            breaks: false,
            pedantic: false
        })
    }
    
  • 使用Joplin作为博客后端-基于Joplin Terminal部署服务端REST API

    使用Joplin作为博客后端-基于Joplin Terminal部署服务端REST API

    Joplin Server本身虽然有接口,但是并不能直接获取笔记数据,了解后发现Joplin Terminal是支持Data API的,这样的话,可以通过在服务器部署一套Joplin Terminal程序来从服务器提供笔记数据,唯一的问题可能就是会导致服务器存储两份笔记数据,不过也不是很大的问题,这里以通过Docker容器部署为例。

    一、安装Joplin Terminal

    # 创建数据存储文件夹
    mkdir -p joplin/cli
    
    cd joplin/cli
    
    npm i joplin
    
    # 同步配置参考官方文档 https://joplinapp.org/help/apps/terminal/
    
    # 同步数据
    npx joplin sync
    

    二、启动Joplin的Data API Server

    修改项目的package.json,增加一个启动脚本。

    {
      "type": "module",
      "scripts": {
        "joplin": "joplin server start",
        ...
      },
      "dependencies": {
        "joplin": "^3.4.1"
      }
    }
    
    npm run joplin
    

    三、docker compose配置

    services:
        ...
        joplin-api:
            image: node:22.12.0
            restart: unless-stopped
            # 这个应该根据实际的uid和gid设置,与挂载目录的/home/test有关
            user: 1000:1000
            volumes:
                # 挂载这个文件主要是为了保持权限一致,避免本地修改容器不能运行
                - /etc/passwd:/etc/passwd:ro
                # 挂载项目代码
                - ./joplin/cli:/app
                # joplin数据保存位置
                - ./joplin/data:/home/test/.config/joplin
                - ./joplin/.npm:/home/test/.npm/
            # 这个start命令参考下面
            command: ["sh", "-c", "chdir /app && npm start"]
    

    四、转发请求,配置定时任务

    joplin terminal启动的data api默认是写死的绑定127.0.0.1,需要本地启动一个反向代理服务器来做一下请求转发,同时也配置一下定时任务。
    我这里服务器用的h3,用什么其实都可以,感觉JS的Web框架语法都差不太多,这里选h3主要是为了资源占用少。

    import { H3 } from "h3"
    import { createServer } from 'node:http'
    import { toNodeHandler } from "h3/node"
    import cron from 'node-cron'
    import { exec } from "node:child_process"
    
    /**
     * 运行node-cron定时任务
     * /30 * * * /path/to/joplin sync
     */
    cron.schedule('*/15 * * * *', joplinSync)
    
    /**
     * 同步数据
     */
    export async function joplinSync() {
      exec('/app/node_modules/.bin/joplin sync', (_, stdout) => {
        console.log('joplin sync: ', stdout)
      })
    }
    
    export const app = new H3()
    
    /**
     * 反向代理外界请求到本机的41184端口
     * joplin Data API Server默认监听41184,因为Docker环境,也不太可能出现冲突的问题
     */
    app.use(
      async (event) => {
        const { url } = event.req
    
        const proxyUrl = new URL(url)
        proxyUrl.protocol = 'http'
        proxyUrl.host = 'localhost:41184'
        const proxy = await fetch(proxyUrl.toString())
        return proxy.body
      }
    )
    
    createServer(toNodeHandler(app)).listen(3000, '0.0.0.0')
    

    五、完善启动脚本

    {
      "type": "module",
      "scripts": {
        "proxy": "node --experimental-strip-types index.ts",
        "joplin": "joplin server start",
        "start": "npm run proxy & npm run joplin"
      },
      "dependencies": {
        "h3": "^2.0.1-rc.2",
        "joplin": "^3.4.1",
        "node-cron": "^4.2.1"
      },
      "devDependencies": {
        "@types/node": "^24.7.1"
      }
    }
    

    六、后续

    后面其实就简单了,感觉直接开放到公共网络不是很安全,最好还是通过容器间通信使用,真正的权限鉴定放在h3这一层。