Files
boundless/README.md
T
吴璨 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

98 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 无界笺 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。
```sh
pnpm install
cp .env.example .env # 可选;不设置时开发口令为 boundless
pnpm dev # 同时启动服务端(3000)与 Vite(5173)
```
浏览器打开 <http://localhost:5173>。服务端数据默认存放在 `./data/`。
## 检查项目
```sh
pnpm build # 类型检查 + 构建
pnpm test:unit -- --run # 单元测试
pnpm exec playwright install chromium # 首次运行 e2e 前
pnpm exec playwright test --project=chromium # 端到端测试(会自动构建并启动服务端)
pnpm lint
```
## 部署(Docker)
```sh
echo "BOUNDLESS_PASSWORD=换成你的口令" > .env
docker compose up -d --build
```
服务监听 `127.0.0.1:3000`,数据保存在 `./data/`(SQLite 数据库、图片和会话密钥)。**备份整个 `data/` 目录即可完整备份。**
前面需要一个 HTTPS 反向代理,并且要支持 WebSocket。Caddy 最简单(自动申请证书,并会自动转发 WebSocket):
```caddyfile
board.example.com {
reverse_proxy 127.0.0.1:3000
}
```
Nginx 需要显式转发 WebSocket 和 Host:
```nginx
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](https://painterly.design-tools.workers.dev)(作者 [Sasha](https://x.com/aleksksaa)),12 幅画作的绘制代码原样收录在 `public/painterly/`,基于 [p5.js](https://p5js.org)(LGPL-2.1)与 [p5.brush](https://github.com/acamposuribe/p5.brush)(MIT)。