Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 即页 环境变量示例
# 纸鹊 Magpie 环境变量示例
# 复制为 .env 后填入真实值。docker-compose 会自动读取当前目录的 .env

# 运行环境。生产环境务必设为 production
Expand All @@ -9,7 +9,7 @@ PORT=8858

# 数据目录(数据库、会话、上传文件)。留空则默认 ./data。
# 用于把数据放到独立卷/分区;docker-compose 已把 ./data 挂载到 /app/data,通常无需改动。
# JPAGE_DATA_DIR=/app/data
# MAGPIE_DATA_DIR=/app/data

# 管理员用户名。首次启动且 users 表为空时生效;留空则默认 "admin"
ADMIN_USER=admin
Expand Down Expand Up @@ -53,7 +53,7 @@ SMTP_PORT=465
SMTP_SECURE=true
SMTP_USER=noreply@example.com
SMTP_PASS=your-password
SMTP_FROM="即页 <noreply@example.com>"
SMTP_FROM="纸鹊 Magpie <noreply@example.com>"
APP_URL=http://localhost:8858

# --- 反馈邮件接收地址(可选)---
Expand Down
97 changes: 16 additions & 81 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,87 +1,22 @@
# npm 自动发版:打 v* tag 触发,跑测试 + 校验版本号一致 + npm publish。
#
# 用法(本地):
# 1. 改 package.json 的 version(如 1.5.1)
# 2. commit + push 到 main
# 3. git tag v1.5.1 && git push origin v1.5.1
# 4. 本 workflow 自动跑:test → 校验 tag==version → npm publish
#
# 前置(仓库设置,一次性):
# GitHub 仓库 Settings → Secrets and variables → Actions → New repository secret
# Name: NPM_TOKEN
# Value: 在 npm 生成的 granular access token(@code2rich scope, Read and write)
# 生成地址:https://www.npmjs.com/settings/code2richnpm/tokens/granular-access-tokens/new
#
# 版本一致性:tag 名(去 v 前缀)必须等于 package.json 的 version,否则失败。
# 这样能防止「tag 标 v1.6.0 但 package.json 还写着 1.5.0」这类手滑。

name: Release

# Package preparation only. No registry publication occurs in this workflow.
name: Prepare npm package
on:
push:
tags: ['v*']

# 同一 tag 不要并发跑(理论上 git push tag 是原子的,但保险起见)
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

workflow_dispatch:
permissions:
contents: read
jobs:
release:
name: Publish to npm
package:
runs-on: ubuntu-latest
# 同时跑测试矩阵确认绿(轻量:只 Node 20,CI workflow 已覆盖 20+22)
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
# 不启用 cache: 'npm':见 ci.yml 同款注释(EEXIST bug)
registry-url: 'https://registry.npmjs.org'

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Run tests (unit + integration)
run: npm test

- name: Build frontend bundle
run: npm run build

- name: Verify tag matches package version
run: |
TAG="${GITHUB_REF_NAME#v}"
PKG=$(node -p "require('./package.json').version")
echo "tag (去 v 前缀): $TAG"
echo "package.json version: $PKG"
if [ "$TAG" != "$PKG" ]; then
echo "::error::tag ($TAG) 与 package.json version ($PKG) 不一致"
exit 1
fi

- name: Publish to npm
run: npm publish
env:
# setup-node 配置的 registry 会读这个环境变量做鉴权
# publishConfig.access=public 已设,scoped 包公开发布无需 --access
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Summary
if: always()
run: |
echo "## Release ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ job.status }}" = "success" ]; then
echo "✅ 已发布 \`$(node -p "require('./package.json').name")@$(node -p "require('./package.json').version")\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "安装:\`\`\`bash" >> $GITHUB_STEP_SUMMARY
echo "npm install -g $(node -p "require('./package.json').name")" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
else
echo "❌ 发布失败,查看上面日志" >> $GITHUB_STEP_SUMMARY
fi
node-version: '22'
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm pack
- uses: actions/upload-artifact@v4
with:
name: magpie-npm-package
path: magpie-*.tgz
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,13 @@ env/
# Operations docs containing credentials
docs/market-operations.md


# Local release and plugin build artifacts
*.tgz
plugin-workspace/dist/

# Local design reviews, backups and preview caches
.hallmark/
.impeccable/
backups/
.thumbnails/
30 changes: 15 additions & 15 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# AGENTS.md

本文件为 AI 编程助手(Codex / Claude Code 等)提供即页(jpage)项目的工作指南。读者应假设对项目一无所知;所有改动请以此文件为上下文基准。
本文件为 AI 编程助手(Codex / Claude Code 等)提供纸鹊 Magpie项目的工作指南。读者应假设对项目一无所知;所有改动请以此文件为上下文基准。

---

## 项目概述

**即页(jpage)** 是一个零配置的 HTML / Markdown 即时预览与分享工具。用户上传 `.html`、`.md`/`.markdown` 或 `.zip` 文件后,即可得到在线渲染页面与短链接(`/s/:key`),无需额外部署流程。项目同时面向 AI 工作流:内置 MCP Streamable HTTP 端点与 CLI 工具,支持通过 API Token 自动上传、管理文件。
**纸鹊 Magpie** 是一个零配置的 HTML / Markdown 即时预览与分享工具。用户上传 `.html`、`.md`/`.markdown` 或 `.zip` 文件后,即可得到在线渲染页面与短链接(`/s/:key`),无需额外部署流程。项目同时面向 AI 工作流:内置 MCP Streamable HTTP 端点与 CLI 工具,支持通过 API Token 自动上传、管理文件。

核心能力:

Expand All @@ -15,7 +15,7 @@
- 文件版本历史与回滚
- 标签、分类、收藏
- 多用户 + admin / user 角色体系
- API Token(`jp_` 前缀)与全局 `MCP_TOKEN` 双认证
- API Token(`mg_` 前缀)与全局 `MCP_TOKEN` 双认证
- 内容模板市场(用户上架 → 管理员审核 → 公开使用)
- Skills 注册与打包下载

Expand Down Expand Up @@ -99,11 +99,11 @@ mcp/ # MCP 实现
tools-tags.js # 标签 tool
tools-categories.js # 分类 tool
tools-content-templates.js # 内容模板 tool
resources.js # jpage://files / jpage://file/{id}
resources.js # magpie://files / magpie://file/{id}
util.js / constants.js # 共享

bin/ # CLI 工具(npm 包入口 `jpage`)
jpage.js # CLI 入口与命令分发
bin/ # CLI 工具(npm 包入口 `magpie`)
magpie.js # CLI 入口与命令分发
args.js / config.js / client.js
commands/ # upload、ls、cat、url、mv、rm、star、tags、skills、whoami、update

Expand Down Expand Up @@ -177,7 +177,7 @@ npm run test:integration # 仅集成测试

- `test/unit/*.test.js`:纯函数与独立模块(util、render-cache、zip、fts、crypto、cli-args、cli-config)。
- `test/integration/*.test.js`:完整 Express app 集成测试(auth、users、tokens、files、versions、tags、categories、skills、admin、share、content-templates、cli)。
- `test/helpers/setup.js`:每个测试文件使用独立数据目录 `data-test-<pid>-<n>`,require server 前设置 `JPAGE_DATA_DIR`,避免并发污染。
- `test/helpers/setup.js`:每个测试文件使用独立数据目录 `data-test-<pid>-<n>`,require server 前设置 `MAGPIE_DATA_DIR`,避免并发污染。
- 手动 harness:`test/perf-harness.js`、`test/mcp-harness.js`、`test/perf-bench.js`、`test/browser-harness.js`、`test/dispatch-bench.js`、`test/run-server.sh`。

CI(`.github/workflows/ci.yml`)在 Node 20/22 矩阵上执行 `npm run lint`、`npm test`、`npm run build`。
Expand Down Expand Up @@ -238,7 +238,7 @@ CI(`.github/workflows/ci.yml`)在 Node 20/22 矩阵上执行 `npm run lint`
- `data/token-key.key` — API Token 加密密钥(未设置 `TOKEN_ENCRYPTION_KEY` 时自动生成)
- `data/backups/` — 自动备份目录(`BACKUP_CRON` 启用时)

数据目录可通过 `JPAGE_DATA_DIR` 覆盖,默认 `./data`。
数据目录可通过 `MAGPIE_DATA_DIR` 覆盖,默认 `./data`。

### 数据库 Schema

Expand Down Expand Up @@ -288,20 +288,20 @@ CI(`.github/workflows/ci.yml`)在 Node 20/22 矩阵上执行 `npm run lint`
### MCP 端点

- 路径:`POST`/`GET`/`DELETE /mcp`
- 鉴权:`Authorization: Bearer <MCP_TOKEN>` 或任意用户级 API Token(`jp_` 前缀)。未配置任何 token 时 `/mcp` 禁用。
- 鉴权:`Authorization: Bearer <MCP_TOKEN>` 或任意用户级 API Token(`mg_` 前缀)。未配置任何 token 时 `/mcp` 禁用。
- 实现:`mcp/transport.js` 管理 Streamable HTTP session;`mcp/server.js` 注册 **17 个 tools + 2 个 resources**。
- Tools 类别:
- 文件:`list_files`、`upload_file`、`get_file_content`、`delete_file`、`rename_file`、`get_file_url`、`star_file`、`unstar_file`
- 版本:`list_file_versions`、`restore_file_version`
- 标签:`list_tags`、`add_tags_to_file`
- 分类:`list_categories`、`create_category`、`set_file_category`
- 内容模板:`list_content_templates`、`get_content_template`
- Resources:`jpage://files`、`jpage://file/{id}`(内容 ≤ 256KB)
- Resources:`magpie://files`、`magpie://file/{id}`(内容 ≤ 256KB)
- 内部:tool 通过 `lib/dispatch.js` 进程内调用 REST API,复用同一 Bearer token,权限/限流/审计与 HTTP 完全一致。

### CLI

`bin/jpage.js` 提供 `jpage` 命令,token 优先级:`--token` > `JPAGE_TOKEN` 环境变量 > `MCP_TOKEN` 环境变量 > `.env`。
`bin/magpie.js` 提供 `magpie` 命令,token 优先级:`--token` > `MAGPIE_TOKEN` 环境变量 > `MCP_TOKEN` 环境变量 > `.env`。

命令:`upload`、`ls`、`cat`、`url`、`mv`、`rm`、`star`、`unstar`、`tags`、`skills`、`whoami`、`update`。

Expand All @@ -311,11 +311,11 @@ CLI 与 MCP 共用同一套 REST API,是对等的两个客户端入口。

## 安全与权限

- **鉴权三选一**:(1) session cookie `jpage.sid`;(2) 用户级 API Token `jp_...`;(3) 全局 `MCP_TOKEN`(向后兼容)。`requireAuth` 设置 `req.userId` 与 `req.userRole`。
- **鉴权三选一**:(1) session cookie `magpie.sid`;(2) 用户级 API Token `mg_...`;(3) 全局 `MCP_TOKEN`(向后兼容)。`requireAuth` 设置 `req.userId` 与 `req.userRole`。
- **角色**:`users.role` 为 `admin` 或 `user`。admin 可访问所有文件与用户;普通用户只能操作自己的文件和公开文件。
- **文件访问**:`loadFileWithPrivacy` 按 admin / 所有者 / 公开 / 未登录 分层校验;`checkFileOwnership` 用于写操作。
- **密码**:bcrypt 哈希(cost 10)。
- **API Token**:`jp_` + 32 位 base62;数据库存 SHA-256 哈希用于鉴权,可选 AES-256-GCM 密文(`token_enc`)用于界面查看/复制。每用户最多 10 个。
- **API Token**:`mg_` + 32 位 base62;数据库存 SHA-256 哈希用于鉴权,可选 AES-256-GCM 密文(`token_enc`)用于界面查看/复制。每用户最多 10 个。
- **限流**:
- 登录 `30 req / 15 min / IP`
- 注册 `5 req / 15 min / IP`
Expand Down Expand Up @@ -374,9 +374,9 @@ CLI 与 MCP 共用同一套 REST API,是对等的两个客户端入口。
## 发版与部署

- 版本号以 `package.json` 为准。
- 推荐发版:`npm version patch|minor|major` → `git push origin main` → `git push origin vX.Y.Z`,由 `.github/workflows/release.yml` 自动执行 lint、test、build、校验 tag、发布到 npm。
- 当前尚未发布 npm:使用 `npm pack` 制作本地安装包;`.github/workflows/release.yml` 仅手动验证与打包,不执行发布。正式发布前依照 `docs/RELEASING.md` 确认账号、版本及授权。
- Docker:`Dockerfile` 多阶段构建(builder / frontend / runner),`EXPOSE 8858`;`docker-compose.yml` 映射 host 8858 → container 8858,挂载 `./data:/app/data`。
- 环境变量完整清单(按需配置):`PORT`、`NODE_ENV`、`JPAGE_DATA_DIR`、`ADMIN_USER`、`ADMIN_PASSWORD`、`SESSION_SECRET`、`COOKIE_SECURE`、`MCP_TOKEN`、`MCP_IP`、`MCP_PROTOCOL`、`TOKEN_ENCRYPTION_KEY`、`SMTP_HOST`、`SMTP_PORT`、`SMTP_SECURE`、`SMTP_USER`、`SMTP_PASS`、`SMTP_FROM`、`APP_URL`、`FEEDBACK_EMAIL`、`ALLOW_REGISTRATION`、`GITHUB_CLIENT_ID`、`GITHUB_CLIENT_SECRET`、`GOOGLE_CLIENT_ID`、`GOOGLE_CLIENT_SECRET`、`WECHAT_OPEN_APP_ID`、`WECHAT_OPEN_APP_SECRET`、`MAX_FILE_VERSIONS`、`BACKUP_CRON`、`BACKUP_DIR`、`ICP_BEIAN`。
- 环境变量完整清单(按需配置):`PORT`、`NODE_ENV`、`MAGPIE_DATA_DIR`、`ADMIN_USER`、`ADMIN_PASSWORD`、`SESSION_SECRET`、`COOKIE_SECURE`、`MCP_TOKEN`、`MCP_IP`、`MCP_PROTOCOL`、`TOKEN_ENCRYPTION_KEY`、`SMTP_HOST`、`SMTP_PORT`、`SMTP_SECURE`、`SMTP_USER`、`SMTP_PASS`、`SMTP_FROM`、`APP_URL`、`FEEDBACK_EMAIL`、`ALLOW_REGISTRATION`、`GITHUB_CLIENT_ID`、`GITHUB_CLIENT_SECRET`、`GOOGLE_CLIENT_ID`、`GOOGLE_CLIENT_SECRET`、`WECHAT_OPEN_APP_ID`、`WECHAT_OPEN_APP_SECRET`、`MAX_FILE_VERSIONS`、`BACKUP_CRON`、`BACKUP_DIR`、`ICP_BEIAN`。

---

Expand Down
Loading