吴璨 fad9760265 自部署多端同步:服务端、实时协作、口令登录与油画加载动画
- 新增 Node 服务端(Hono + Hocuspocus + node:sqlite),数据保存在服务器
- 基于 Yjs 的多端实时同步,本机 IndexedDB 离线缓存,撤销只针对本机操作
- 空间(标签)有独立地址,增删改通过接口同步到所有设备
- 口令登录(签名 Cookie 会话、登录限流、同源校验)
- 图片上传到服务器,过大照片上传前自动压缩
- 历史版本(自动/手动保存、一键恢复)、跨空间搜索、在线设备
- 随机油画加载动画(Painting Loaders,本地收录 p5 / p5.brush)
- 选中样式与拖拽性能优化:拖动结束后才同步位置
- Docker / docker-compose 部署,README 与 CLAUDE.md 更新
2026-10-21 18:09:03 +08:00
2026-09-29 22:08:05 +08:00
2026-09-29 22:08:05 +08:00
2026-09-29 22:08:05 +08:00
2026-09-29 22:08:05 +08:00
2026-09-29 22:08:05 +08:00
2026-09-29 22:08:05 +08:00
2026-09-29 22:08:05 +08:00

无界笺 Boundless

一张可以无限延伸的灵感平面,自部署在自己的服务器上。所有内容保存在服务器里,手机、平板、电脑打开的都是同一张画布;在一台设备上修改,其他打开着同一空间的设备会立刻看到变化。

功能

  • 无限画布:平移、缩放、小地图、框选、对齐与均分
  • 文字便笺与图片卡片:创建、编辑、移动、缩放、复制、换色、连线
  • 多个空间(标签),每个空间有独立的地址 /s/<空间 id>
  • 多端实时同步:基于 Yjs CRDT,多台设备同时编辑也不会互相覆盖
  • 断网可用:离线时的修改保存在本机,联网后自动同步
  • 撤销只针对本机:不会把其他设备的修改一起撤掉
  • 历史版本:编辑时每 10 分钟自动存一个版本,也可以手动保存,一键恢复
  • 全局搜索:搜索当前空间并跳转,同时列出其他空间里的匹配卡片
  • 在线设备:查看哪些设备在线、各自打开的是哪个空间
  • 口令登录:单用户,修改口令后所有设备自动退出
  • 图片上传到服务器;过大的照片会在上传前自动压缩
  • 随机油画加载动画(见下方致谢)

技术栈

  • 前端:Vue 3 + TypeScript、Vite 8、Vue Flow、Pinia、Tailwind CSS 4
  • 同步:Yjs、Hocuspocus(WebSocket)、y-indexeddb(本机离线缓存)
  • 服务端:Node.js(直接运行 TypeScript)、Hono、node:sqlite
  • 测试:Vitest、Playwright

本地开发

需要 Node.js 22.18+ 或 24.12+,推荐 Node.js 24 LTS。

pnpm install
cp .env.example .env      # 可选;不设置时开发口令为 boundless
pnpm dev                  # 同时启动服务端(3000)与 Vite(5173)

浏览器打开 http://localhost:5173。服务端数据默认存放在 ./data/。

检查项目

pnpm build                                     # 类型检查 + 构建
pnpm test:unit -- --run                        # 单元测试
pnpm exec playwright install chromium          # 首次运行 e2e 前
pnpm exec playwright test --project=chromium   # 端到端测试(会自动构建并启动服务端)
pnpm lint

部署(Docker)

echo "BOUNDLESS_PASSWORD=换成你的口令" > .env
docker compose up -d --build

服务监听 127.0.0.1:3000,数据保存在 ./data/(SQLite 数据库、图片和会话密钥)。备份整个 data/ 目录即可完整备份。

前面需要一个 HTTPS 反向代理,并且要支持 WebSocket。Caddy 最简单(自动申请证书,并会自动转发 WebSocket):

board.example.com {
  reverse_proxy 127.0.0.1:3000
}

Nginx 需要显式转发 WebSocket 和 Host:

location / {
  proxy_pass http://127.0.0.1:3000;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  proxy_set_header Host $host;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_read_timeout 1d;
  client_max_body_size 25m;
}

环境变量

变量 说明 默认值
BOUNDLESS_PASSWORD 登录口令(生产环境必填) 开发时为 boundless
BOUNDLESS_SECRET 会话签名密钥 自动生成并存入数据目录
DATA_DIR 数据目录 ./data
PORT / HOST 监听端口与地址 3000 / 0.0.0.0
SESSION_DAYS 登录有效天数 90
MAX_IMAGE_MB 单张图片大小上限 20

不用 Docker 时:pnpm install && pnpm build-only && BOUNDLESS_PASSWORD=... pnpm start。

致谢

加载动画来自 Painting Loaders(作者 Sasha),12 幅画作的绘制代码原样收录在 public/painterly/,基于 p5.js(LGPL-2.1)与 p5.brush(MIT)。

S
Description
No description provided
Readme 665 KiB
Languages
Vue 38.9%
TypeScript 32.3%
CSS 28.3%
HTML 0.5%