基于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这一层。

文章标签