docs: add bilingual guides and English API descriptions
This commit is contained in:
1 parent
1e9e0678ee
commit
a874a39ea5
12 files changed
+991
-582
No files matched your search
+47
-45
@@ -1,27 +1,29 @@
|
||||
# API 与认证
|
||||
# API and authentication
|
||||
|
||||
[返回项目首页](../README.zh-CN.md) · [开发指南](development.md)
|
||||
**English** · [简体中文](api.zh-CN.md)
|
||||
|
||||
API 基础路径为 `/api/v1`。完整端点、请求字段、权限和响应以 [OpenAPI 源文件](../internal/api/openapi.json)为准,运行中的实例在 `GET /api/v1/openapi.json` 提供同一文档。测试 `TestOpenAPICoversRoutes` 核对路由覆盖。
|
||||
[Project home](../README.md) · [Development guide](development.md)
|
||||
|
||||
## 脚本接入
|
||||
The API base path is `/api/v1`. The [OpenAPI source](../internal/api/openapi.json) is authoritative for endpoints, request fields, permissions, and responses. A running instance serves the same document at `GET /api/v1/openapi.json`. `TestOpenAPICoversRoutes` checks route coverage.
|
||||
|
||||
在「设置 → 账户与安全 → API 令牌」创建只读或读写令牌。受保护 API 只接受 `Authorization: Bearer <token>`,不接受 Cookie,也不要将主令牌放进 URL。
|
||||
## Script access
|
||||
|
||||
以下只读示例从环境变量读取凭据:
|
||||
Create a read-only or read/write token under **Settings → Account and security → API tokens**. Protected APIs accept only `Authorization: Bearer <token>`; they do not accept Cookies. Never put the main token in a URL.
|
||||
|
||||
This read-only example reads credentials from environment variables:
|
||||
|
||||
```sh
|
||||
# 先在本机环境中设置 RAWMARK_URL 和 RAWMARK_TOKEN,不要提交真实值。
|
||||
# Set RAWMARK_URL and RAWMARK_TOKEN locally first; never commit real values.
|
||||
curl --fail-with-body \
|
||||
-H "Authorization: Bearer $RAWMARK_TOKEN" \
|
||||
"$RAWMARK_URL/api/v1/tree"
|
||||
```
|
||||
|
||||
`rm_` API 令牌在服务端只存哈希,保留 read/write 权限。只读令牌可读取笔记、搜索和执行只读 POST 导出,不能写入;API 令牌不能管理账户、令牌、维护或资源票据,不能为自身扩权。管理操作需要浏览器登录会话。
|
||||
The server stores only hashes of `rm_` API tokens, retaining read/write permissions. Read-only tokens can read notes, search, and use read-only POST exports, but cannot write. API tokens cannot manage the account, tokens, maintenance, or resource tickets, or expand their own permissions. Management operations require a browser sign-in session.
|
||||
|
||||
## JSON 与 HTTP 语义
|
||||
## JSON and HTTP semantics
|
||||
|
||||
普通 JSON 成功响应:
|
||||
A successful ordinary JSON response:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -31,62 +33,62 @@ curl --fail-with-body \
|
||||
}
|
||||
```
|
||||
|
||||
`code = 0` 表示成功,非零表示失败;失败时 `message` 是可展示的原因。保留真实 HTTP 状态,不把错误全部转为 200。通用错误码为 HTTP 状态乘 100,特定业务码如下:
|
||||
`code = 0` means success; nonzero means failure, with `message` providing a displayable reason. Real HTTP status codes are retained rather than converting all errors to 200. Generic error codes are the HTTP status multiplied by 100; specific business codes include:
|
||||
|
||||
| HTTP | code | 含义 |
|
||||
| HTTP | code | Meaning |
|
||||
| --- | --- | --- |
|
||||
| 400 | 40001 | 验证码错误、无效或已使用 |
|
||||
| 400 | 40002 | 验证码过期 |
|
||||
| 400 | 40003 | 修改密码时当前密码错误,不清理身份 |
|
||||
| 401 | 40101 | 登录密码错误 |
|
||||
| 412 | 41200 | 文件版本冲突,当前版本在 `data.rev` |
|
||||
| 400 | 40001 | Incorrect, invalid, or already-used captcha |
|
||||
| 400 | 40002 | Expired captcha |
|
||||
| 400 | 40003 | Incorrect current password during a password change; does not clear the identity |
|
||||
| 401 | 40101 | Incorrect sign-in password |
|
||||
| 412 | 41200 | File version conflict; the current revision is in `data.rev` |
|
||||
|
||||
401 表示需要重新认证;403 表示当前身份没有权限,不能据此注销用户。维护输入或备份内容无效返回 400,占用或空间不足返回 503,文件系统/数据库内部故障返回 500。
|
||||
401 requires reauthentication. 403 means the current identity lacks permission and must not sign the user out. Invalid maintenance input or backup contents return 400; a busy service or insufficient space returns 503; internal filesystem/database failures return 500.
|
||||
|
||||
**文件原文、下载、WebSocket、204 及 OpenAPI 文档不包 JSON 外壳**。`GET /files/{path}` 返回 Markdown 原文和 `ETag`;`PUT /files/{path}` 接收原文,成功返回包含 `rev` 的 JSON。用 `If-Match: "rev"` 防止覆盖新版本,或 `If-None-Match: *` 要求目标不存在;都不带会直接覆盖,不适合需要冲突保护的写入。
|
||||
**Raw files, downloads, WebSocket connections, 204 responses, and the OpenAPI document do not use the JSON envelope.** `GET /files/{path}` returns raw Markdown and an `ETag`; `PUT /files/{path}` accepts raw text and returns JSON containing `rev` on success. Use `If-Match: "rev"` to prevent overwriting a newer version, or `If-None-Match: *` to require that the target does not exist. Omitting both overwrites directly and is unsuitable when conflict protection is needed.
|
||||
|
||||
## 响应语言
|
||||
## Response language
|
||||
|
||||
发送 `Accept-Language: en` 或 `Accept-Language: zh-CN` 选择应用提示的语言,未指定或不支持的语言默认英文。服务端按质量权重选择支持的语言,并在 API 响应的 `Content-Language` 中返回实际语言;API 继续 `Cache-Control: no-store`。`message`、导入警告、维护状态及清理跳过原因会本地化,HTTP 状态、业务码、字段、笔记正文及用户资源名称保持不变。客户端应按状态和 `code` 判断结果,不能匹配翻译后的文案。
|
||||
Send `Accept-Language: en` or `Accept-Language: zh-CN` to select the language of application messages. Missing or unsupported languages default to English. The server selects supported languages by quality weight and returns the actual language in `Content-Language`; API responses retain `Cache-Control: no-store`. Messages, import warnings, maintenance status, and cleanup skip reasons are localized. HTTP statuses, business codes, fields, note contents, and user resource names remain unchanged. Clients must use status and `code` to determine results, not match translated text.
|
||||
|
||||
账户语言位于 `/settings` 的 `locale` 字段,仅支持 `en`、`zh-CN`;PATCH 传 `null` 删除覆盖,恢复默认英文。该字段与其他设置一同导出、导入和同步;登录页的临时选择不写入账户。请求头只影响当前响应,不修改账户语言。
|
||||
Account language is the `locale` field on `/settings`, supporting only `en` and `zh-CN`. PATCH with `null` removes the override and restores default English. The field is exported, imported, and synchronized with other settings. The sign-in page's temporary choice does not write to the account. Request headers affect only the current response, not account language.
|
||||
|
||||
## 浏览器登录
|
||||
## Browser sign-in
|
||||
|
||||
实例没有用户名、注册或角色系统。尚未初始化时,`POST /auth/setup` 使用 `{ password }` 设置密码并返回会话;已经初始化的实例不能再次初始化。
|
||||
There are no usernames, registration, or roles. Before initialization, `POST /auth/setup` sets the password using `{ password }` and returns a session. An initialized instance cannot be initialized again.
|
||||
|
||||
正常登录流程:
|
||||
Normal sign-in flow:
|
||||
|
||||
1. `POST /auth/captcha` 获取 `{ captchaId, imageDataUrl, expiresAt }`。
|
||||
2. 展示图片,用户填写密码与六位数字验证码。
|
||||
3. `POST /auth/login` 提交 `{ password, captchaId, captchaCode }`。
|
||||
4. 成功返回 `{ token, expiresAt }`,再通过 `GET /auth/me` 恢复真实身份与权限。
|
||||
1. Call `POST /auth/captcha` to get `{ captchaId, imageDataUrl, expiresAt }`.
|
||||
2. Show the image and ask for the password and six-digit captcha.
|
||||
3. Submit `{ password, captchaId, captchaCode }` to `POST /auth/login`.
|
||||
4. On success, receive `{ token, expiresAt }`, then call `GET /auth/me` to restore the actual identity and permissions.
|
||||
|
||||
以上字段均位于普通响应的 `data` 中。刷新验证码时传 `{ replaceId }` 使旧挑战失效。挑战绑定来源,五分钟过期;每次登录提交一次性消费,即使密码或验证码错误也不能重用,失败必须刷新。签发与登录均保留限流。
|
||||
These response fields are inside the ordinary envelope's `data`. To refresh a captcha, send `{ replaceId }` to invalidate the old challenge. Challenges are bound to their source, expire after five minutes, and are consumed once on every sign-in submission, even if the password or captcha is wrong. Refresh after any failure. Issuance and sign-in remain rate-limited.
|
||||
|
||||
`rms_` 浏览器会话 Token 保存在 localStorage 的 `rawmark.token`,固定三十天有效、不滑动续期,服务端仅存哈希。退出调用 `POST /auth/logout`,服务端撤销成功返回 204 后才清理本地身份;请求失败时保留身份并提示错误,不能仅删除本地 Token 宣称注销完成。
|
||||
The `rms_` browser session token is stored under localStorage key `rawmark.token`, lasts a fixed thirty days without sliding renewal, and is stored only as a hash on the server. Sign out through `POST /auth/logout`; clear local identity only after server revocation succeeds with 204. On request failure, retain identity and show the error. Merely deleting a local token is not a completed logout.
|
||||
|
||||
localStorage 可被同源脚本读取;有完整前端权限的插件必须视为可信代码,不能承诺凭据隔离。
|
||||
Same-origin scripts can read localStorage. Plugins with full frontend privileges must be treated as trusted code; credential isolation is not guaranteed.
|
||||
|
||||
## 原生资源与事件
|
||||
## Native resources and events
|
||||
|
||||
原生图片、字体、插件模块与浏览器 WebSocket 不能统一设置认证头,通过 `POST /auth/tickets` 获取受限资源 URL;主 Token 不进入 URL。票据服务端只保存哈希,每次读取检查父会话,父会话退出后即失效。
|
||||
Native images, fonts, plugin modules, and browser WebSocket connections cannot all set authentication headers. Obtain restricted resource URLs through `POST /auth/tickets`; the main token never enters the URL. The server stores only ticket hashes and checks the parent session on every read. Tickets stop working when that session signs out.
|
||||
|
||||
- 图片/字体票据不超过父会话有效期。
|
||||
- 插件票据额外绑定插件 ID 和版本,支持相对模块导入。
|
||||
- 事件握手票据一分钟有效、仅消费一次;已连接事件仍会检查父凭据。
|
||||
- 脚本 WebSocket 客户端可以通过 Bearer 请求头继续连接 `/api/v1/events`。
|
||||
- Image/font tickets do not outlive the parent session.
|
||||
- Plugin tickets also bind the plugin ID and version and support relative module imports.
|
||||
- Event handshake tickets last one minute and are consumed once. Established connections continue checking the parent credential.
|
||||
- Script WebSocket clients can still connect to `/api/v1/events` with a Bearer request header.
|
||||
|
||||
私有响应使用 `Cache-Control: no-store` 与 `Referrer-Policy: no-referrer`,API 和私有资源不进入 PWA 静态缓存。代理日志也需脱敏,见[部署文档](deployment.md#https-与反向代理)。
|
||||
Private responses use `Cache-Control: no-store` and `Referrer-Policy: no-referrer`. APIs and private resources never enter the PWA's static cache. Proxy logs also need redaction; see [Deployment and maintenance](deployment.md#https-and-reverse-proxy).
|
||||
|
||||
## 前端请求责任
|
||||
## Frontend request ownership
|
||||
|
||||
请求处理、认证与路由守卫分别位于 `http/`、`pinia/` 和 `router/`。匿名跳登录并保留合法站内回跳,拒绝外部地址与循环跳转。401 清理当前身份、权限与动态路由,403 保留身份。
|
||||
Request handling, authentication, and route guards live in `http/`, `pinia/`, and `router/`, respectively. Anonymous users are redirected to sign-in with a valid internal return path; external addresses and redirect loops are rejected. 401 clears the current identity, permissions, and dynamic routes; 403 retains identity.
|
||||
|
||||
每次替换或退出会话更新代号、取消旧请求。用户信息、路由安装、异步响应与排队写入均须检查所属会话;旧结果按 Axios 取消语义拒绝,不能覆盖或注销新身份,也不提示取消错误。
|
||||
Each session replacement or logout changes the session generation and cancels old requests. User information, route installation, asynchronous responses, and queued writes must verify session ownership. Old results reject using Axios cancellation semantics. They cannot overwrite or sign out a new identity, and cancellation errors are not shown.
|
||||
|
||||
同一错误只由一个责任层提示。请求默认负责展示,已有行内状态、保存重试或批量汇总的 API 使用 `errorOwner: 'caller'`;失败依然 reject,不以空列表或默认值伪装成功。
|
||||
Only one layer displays a given error. Requests own display by default; APIs with inline status, save retries, or bulk summaries use `errorOwner: 'caller'`. Failures still reject rather than masquerading as successful empty lists or defaults.
|
||||
|
||||
## 旧客户端迁移
|
||||
## Migrating older clients
|
||||
|
||||
统一协议需要前后端同时更新,不保留 Cookie/Bearer 或两种 JSON 格式兜底。旧 `rm_` API 令牌无需重建,但调用方要改为读取 `{ code, data, message }` 中的 `data`;文件及导出字节语义不变。旧浏览器页面需重新登录,不能连接新旧混用的后端。
|
||||
The unified protocol requires frontend and backend updates together. There is no Cookie/Bearer fallback or support for two JSON formats. Existing `rm_` API tokens need not be recreated, but callers must read `data` from `{ code, data, message }`. Raw file and export byte semantics remain unchanged. Old browser pages must sign in again and cannot use mixed old/new backends.
|
||||
Reference in new issue
Block a user