Files
RawMark/internal/api/openapi.json
T
eezd eb63b14c8e Add settings page with synced appearance and password change
- Store app settings as one JSON document on the server, merged per key and
  pushed to other devices; cache locally to avoid a flash on load
- Settings page groups appearance, account, shortcuts, shares, API tokens and
  trash; full screen with a section list on phones
- Font size and content width settings
- Change the login password, signing out other sessions
- Settings writes and password changes only accept a login session
2026-10-17 16:23:39 +08:00

1679 lines
40 KiB
JSON
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.
{
"openapi": "3.1.0",
"info": {
"title": "RawMark API",
"version": "1",
"description": "RawMark 的 REST API。自动化调用请在「API 令牌」中创建令牌,以 `Authorization: Bearer <token>` 访问;只读令牌只能使用 GET。分享与令牌的管理接口只接受登录会话。所有错误响应均为 `{\"error\": \"说明\"}`。"
},
"servers": [
{
"url": "/api/v1"
}
],
"security": [
{
"bearer": []
},
{
"cookie": []
}
],
"paths": {
"/health": {
"get": {
"summary": "健康检查",
"tags": [
"系统"
],
"security": [
{}
],
"responses": {
"200": {
"description": "成功",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Ok"
}
}
}
}
}
}
},
"/openapi.json": {
"get": {
"summary": "本文档",
"tags": [
"系统"
],
"security": [
{}
],
"responses": {
"200": {
"description": "OpenAPI 文档",
"content": {
"application/json": {
"schema": {
"type": "object"
}
}
}
}
}
}
},
"/auth/state": {
"get": {
"summary": "是否已初始化、当前是否已登录",
"tags": [
"登录"
],
"security": [
{}
],
"responses": {
"200": {
"description": "状态",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"setUp": {
"type": "boolean"
},
"authenticated": {
"type": "boolean"
}
}
}
}
}
}
}
}
},
"/auth/setup": {
"post": {
"summary": "首次设置登录密码(需服务器数据目录中的启动码)",
"tags": [
"登录"
],
"security": [
{}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"password": {
"type": "string"
}
},
"required": [
"code",
"password"
]
}
}
}
},
"responses": {
"200": {
"description": "成功",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Ok"
}
}
}
},
"400": {
"description": "启动码错误或密码过短",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "已初始化",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/auth/login": {
"post": {
"summary": "登录,成功后设置会话 Cookie",
"tags": [
"登录"
],
"security": [
{}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"password": {
"type": "string"
}
},
"required": [
"password"
]
}
}
}
},
"responses": {
"200": {
"description": "成功",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Ok"
}
}
}
},
"401": {
"description": "密码错误",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"429": {
"description": "失败次数过多",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/auth/logout": {
"post": {
"summary": "退出登录",
"tags": [
"登录"
],
"security": [
{}
],
"responses": {
"200": {
"description": "成功",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Ok"
}
}
}
}
}
}
},
"/auth/password": {
"post": {
"summary": "修改登录密码;当前会话保留,其他设备需重新登录",
"tags": [
"登录"
],
"security": [
{
"cookie": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"current": {
"type": "string"
},
"next": {
"type": "string"
}
},
"required": [
"current",
"next"
]
}
}
}
},
"responses": {
"200": {
"description": "成功",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Ok"
}
}
}
},
"400": {
"description": "新密码过短",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"401": {
"description": "当前密码错误",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"429": {
"description": "失败次数过多",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/settings": {
"get": {
"summary": "应用设置(全部设备共用的一份 JSON 对象)",
"tags": [
"设置"
],
"responses": {
"200": {
"description": "设置",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
}
},
"patch": {
"summary": "合并修改设置",
"tags": [
"设置"
],
"security": [
{
"cookie": []
}
],
"description": "按顶层键合并:请求中的键覆盖原值,值为 null 的键被删除。成功后经 WebSocket 推送 settings 事件。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"responses": {
"200": {
"description": "合并后的设置",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"400": {
"description": "不是 JSON 对象",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"413": {
"description": "超过 1 MB",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/tree": {
"get": {
"summary": "全部笔记与文件夹(不含 .assets 图片目录与隐藏文件)",
"tags": [
"笔记"
],
"responses": {
"200": {
"description": "目录树",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"entries": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Entry"
}
}
}
}
}
}
}
}
}
},
"/files/{path}": {
"parameters": [
{
"name": "path",
"in": "path",
"required": true,
"description": "仓库内的相对路径,如 `目录/笔记.md`(各段需 URL 编码)",
"schema": {
"type": "string"
}
}
],
"get": {
"summary": "读取笔记",
"tags": [
"笔记"
],
"responses": {
"200": {
"description": "Markdown 原文",
"headers": {
"ETag": {
"description": "内容版本(rev),带双引号",
"schema": {
"type": "string"
}
}
},
"content": {
"text/markdown": {
"schema": {
"type": "string"
}
}
}
},
"404": {
"description": "不存在",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
},
"put": {
"summary": "写入笔记(不存在的上级目录会自动创建)",
"tags": [
"笔记"
],
"description": "请求体整体作为笔记内容。`If-Match: \"rev\"` 要求当前版本一致,否则返回 412 与最新 rev;`If-None-Match: *` 要求笔记尚不存在;都不带则直接覆盖。",
"parameters": [
{
"name": "If-Match",
"in": "header",
"schema": {
"type": "string"
}
},
{
"name": "If-None-Match",
"in": "header",
"schema": {
"type": "string",
"enum": [
"*"
]
}
}
],
"requestBody": {
"required": true,
"content": {
"text/markdown": {
"schema": {
"type": "string"
}
}
}
},
"responses": {
"200": {
"description": "已写入",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"rev": {
"type": "string"
}
}
}
}
},
"headers": {
"ETag": {
"description": "内容版本(rev),带双引号",
"schema": {
"type": "string"
}
}
}
},
"400": {
"description": "路径无效",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "If-None-Match 时已存在",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"412": {
"description": "版本冲突",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"rev": {
"type": "string",
"description": "服务器上的当前版本;为空表示已不存在"
}
}
}
}
}
},
"413": {
"description": "超过 20 MB",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
},
"delete": {
"summary": "把笔记或文件夹移入回收站(连同 .assets)",
"tags": [
"笔记"
],
"responses": {
"200": {
"description": "回收站条目",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TrashItem"
}
}
}
},
"404": {
"description": "不存在",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/move": {
"post": {
"summary": "移动或重命名笔记/文件夹",
"tags": [
"笔记"
],
"description": "笔记的 .assets 目录随之移动,改名时同步改写正文中指向自身图片目录的链接;分享链接随之更新。不改写其他笔记指向它的链接。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"from": {
"type": "string"
},
"to": {
"type": "string"
}
},
"required": [
"from",
"to"
]
}
}
}
},
"responses": {
"200": {
"description": "成功",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Ok"
}
}
}
},
"404": {
"description": "源不存在",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "目标已存在",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/folders": {
"post": {
"summary": "新建文件夹",
"tags": [
"笔记"
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"path": {
"type": "string"
}
},
"required": [
"path"
]
}
}
}
},
"responses": {
"200": {
"description": "成功",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Ok"
}
}
}
},
"409": {
"description": "已存在",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/assets": {
"post": {
"summary": "上传图片到笔记的 .assets 目录",
"tags": [
"图片"
],
"parameters": [
{
"name": "note",
"in": "query",
"required": true,
"description": "所属笔记路径",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
"file": {
"type": "string",
"format": "binary"
}
},
"required": [
"file"
]
}
}
}
},
"responses": {
"200": {
"description": "相对笔记所在目录的链接,可直接写入 Markdown",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"link": {
"type": "string"
}
}
}
}
}
},
"413": {
"description": "超过 50 MB",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/raw/{path}": {
"parameters": [
{
"name": "path",
"in": "path",
"required": true,
"description": "仓库内的相对路径,如 `目录/笔记.md`(各段需 URL 编码)",
"schema": {
"type": "string"
}
}
],
"get": {
"summary": "读取仓库中的原始文件(图片等)",
"tags": [
"图片"
],
"responses": {
"200": {
"description": "文件内容",
"content": {
"application/octet-stream": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
},
"404": {
"description": "不存在",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/search": {
"get": {
"summary": "全文搜索",
"tags": [
"笔记"
],
"description": "空格分隔多个词,全部命中才返回;最多 50 条。",
"parameters": [
{
"name": "q",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "结果",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"results": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SearchResult"
}
}
}
}
}
}
}
}
}
},
"/trash": {
"get": {
"summary": "回收站列表",
"tags": [
"回收站"
],
"responses": {
"200": {
"description": "条目",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TrashItem"
}
}
}
}
}
}
}
}
}
},
"/trash/{id}/restore": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"post": {
"summary": "从回收站恢复",
"tags": [
"回收站"
],
"responses": {
"200": {
"description": "恢复的条目",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TrashItem"
}
}
}
},
"404": {
"description": "不存在",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"409": {
"description": "原位置已有同名文件",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/trash/{id}": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"delete": {
"summary": "彻底删除回收站条目",
"tags": [
"回收站"
],
"responses": {
"200": {
"description": "成功",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Ok"
}
}
}
},
"404": {
"description": "不存在",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/events": {
"get": {
"summary": "WebSocket:仓库变更推送",
"tags": [
"笔记"
],
"description": "升级为 WebSocket 后,服务器推送 JSON 数组,元素为 Event。客户端不发送消息。",
"responses": {
"101": {
"description": "切换为 WebSocket"
}
}
}
},
"/shares": {
"get": {
"summary": "分享列表",
"tags": [
"分享"
],
"security": [
{
"cookie": []
}
],
"parameters": [
{
"name": "path",
"in": "query",
"description": "只列出该笔记的分享",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "分享",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"shares": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Share"
}
}
}
}
}
}
}
}
},
"post": {
"summary": "创建只读分享",
"tags": [
"分享"
],
"security": [
{
"cookie": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"password": {
"type": "string",
"description": "访问密码,留空为不设密码"
},
"expiresIn": {
"type": "integer",
"description": "有效时长(秒),0 为永久"
}
},
"required": [
"path"
]
}
}
}
},
"responses": {
"200": {
"description": "新分享,访问地址为 /s/{token}",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Share"
}
}
}
},
"400": {
"description": "不是笔记",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "笔记不存在",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/shares/{token}": {
"parameters": [
{
"name": "token",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"delete": {
"summary": "撤销分享",
"tags": [
"分享"
],
"security": [
{
"cookie": []
}
],
"responses": {
"200": {
"description": "成功",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Ok"
}
}
}
},
"404": {
"description": "不存在",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/tokens": {
"get": {
"summary": "API 令牌列表",
"tags": [
"API 令牌"
],
"security": [
{
"cookie": []
}
],
"responses": {
"200": {
"description": "令牌",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"tokens": {
"type": "array",
"items": {
"$ref": "#/components/schemas/APIToken"
}
}
}
}
}
}
}
}
},
"post": {
"summary": "创建 API 令牌",
"tags": [
"API 令牌"
],
"security": [
{
"cookie": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"scope": {
"type": "string",
"enum": [
"read",
"write"
]
}
},
"required": [
"name",
"scope"
]
}
}
}
},
"responses": {
"200": {
"description": "token 明文只返回这一次",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"token": {
"type": "string"
},
"info": {
"$ref": "#/components/schemas/APIToken"
}
}
}
}
}
},
"400": {
"description": "名称或范围无效",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/tokens/{id}": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"delete": {
"summary": "吊销 API 令牌",
"tags": [
"API 令牌"
],
"security": [
{
"cookie": []
}
],
"responses": {
"200": {
"description": "成功",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Ok"
}
}
}
},
"404": {
"description": "不存在",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/public/shares/{token}": {
"parameters": [
{
"name": "token",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"get": {
"summary": "访客读取分享的笔记",
"tags": [
"分享(公开)"
],
"security": [
{}
],
"responses": {
"200": {
"description": "笔记",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"content": {
"type": "string"
},
"updatedAt": {
"type": "integer"
}
}
}
}
}
},
"401": {
"description": "需要访问密码",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"passwordRequired": {
"type": "boolean"
}
}
}
}
}
},
"404": {
"description": "分享不存在、已过期或笔记已删除",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/public/shares/{token}/unlock": {
"parameters": [
{
"name": "token",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"post": {
"summary": "输入分享密码,成功后设置访客 Cookie",
"tags": [
"分享(公开)"
],
"security": [
{}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"password": {
"type": "string"
}
},
"required": [
"password"
]
}
}
}
},
"responses": {
"200": {
"description": "成功",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Ok"
}
}
}
},
"401": {
"description": "密码错误",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"404": {
"description": "分享不存在或已失效",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"429": {
"description": "失败次数过多",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/public/shares/{token}/assets/{path}": {
"parameters": [
{
"name": "token",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "path",
"in": "path",
"required": true,
"description": "相对笔记所在目录的图片路径,只能位于该笔记的 .assets 目录",
"schema": {
"type": "string"
}
}
],
"get": {
"summary": "访客读取分享笔记中的图片",
"tags": [
"分享(公开)"
],
"security": [
{}
],
"responses": {
"200": {
"description": "文件内容",
"content": {
"application/octet-stream": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
},
"404": {
"description": "不存在或不在允许范围内",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
}
},
"components": {
"securitySchemes": {
"bearer": {
"type": "http",
"scheme": "bearer",
"description": "API 令牌(rm_ 开头)"
},
"cookie": {
"type": "apiKey",
"in": "cookie",
"name": "rawmark_session",
"description": "浏览器登录会话"
}
},
"schemas": {
"Error": {
"type": "object",
"properties": {
"error": {
"type": "string"
}
},
"required": [
"error"
]
},
"Ok": {
"type": "object",
"properties": {
"ok": {
"type": "boolean"
}
}
},
"Entry": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"dir": {
"type": "boolean"
},
"size": {
"type": "integer"
},
"mtime": {
"type": "integer",
"description": "修改时间(Unix 毫秒)"
}
}
},
"TrashItem": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"path": {
"type": "string",
"description": "原路径"
},
"dir": {
"type": "boolean"
},
"deletedAt": {
"type": "integer",
"description": "Unix 毫秒"
}
}
},
"SearchResult": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"line": {
"type": "integer",
"description": "第一处匹配所在行(从 1 开始);只有路径匹配时为 0"
},
"snippet": {
"type": "string"
}
}
},
"Event": {
"type": "object",
"description": "tree:目录结构变化;changed:内容变化(rev 为新版本);deleted:删除;moved:移动(from → path);settings:设置变化",
"properties": {
"type": {
"type": "string",
"enum": [
"tree",
"changed",
"deleted",
"moved",
"settings"
]
},
"path": {
"type": "string"
},
"from": {
"type": "string"
},
"rev": {
"type": "string"
}
}
},
"Share": {
"type": "object",
"properties": {
"token": {
"type": "string"
},
"path": {
"type": "string"
},
"createdAt": {
"type": "integer"
},
"expiresAt": {
"type": "integer",
"description": "Unix 毫秒,0 为永久"
},
"hasPassword": {
"type": "boolean"
},
"exists": {
"type": "boolean",
"description": "笔记是否仍存在"
}
}
},
"APIToken": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"scope": {
"type": "string",
"enum": [
"read",
"write"
]
},
"createdAt": {
"type": "integer"
},
"lastUsedAt": {
"type": "integer",
"description": "Unix 毫秒,0 为从未使用"
}
}
}
}
}
}