docs: align project guidance with the modular application

This commit is contained in:
eezd committed 2026-10-25 20:47:55 +08:00
1 parent 3cd641a380
commit 8d62d07a13
11 files changed
+116 -6

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
---
name: v3-create-crud
description: 调整业务页面与表单
---
先读根 AGENTS.md,核对后端路由和现有业务调用链。页面放 `web/src/pages/<module>/`,私有 API/类型跟随模块;真正跨模块共享才放 common。复用实际 Element Plus 控件和 `http/axios` 的 requestJSON/requestRaw,不预设分页、CRUD、导出或角色能力。失败保留原因,成功后再关闭/刷新,取消确认不得伪装成实际操作成功。按行为测试成功、失败与权限边界。
+6
View File
@@ -0,0 +1,6 @@
---
name: v3-upsert-route
description: 调整登录守卫与权限路由
---
先读根 AGENTS.md、`web/src/router/` 和 `pinia/stores/{user,permission}.ts`。常驻与动态路由按现有实现登记,匿名路由使用 meta.anonymous,真实权限来自 /auth/me,无角色系统。唯一名称、懒加载;核对匿名/初始化/回跳、深链、403、401、注销及换会话,旧结果不能注册新会话路由。
+6
View File
@@ -0,0 +1,6 @@
---
name: v3-upsert-store
description: 调整 Pinia 共享状态
---
先读根 AGENTS.md 和现有 store。跨页面共享状态放 `web/src/pinia/stores/`,使用 Setup Store;页面表单和工作区临时状态留在模块。setup 外显式传入 pinia,不增加转发函数或重复镜像。认证变更通过 user store 更新会话代号,持久化仅保留已有明确需求。验证旧请求隔离与状态重置。
@@ -0,0 +1,6 @@
---
name: v3-use-composables
description: 提取或复用业务组合函数
---
先读根 AGENTS.md 及实际调用方。当前项目没有模板分页、全屏加载等示例组合函数,不凭模板声明调用不存在的能力。页面私有组合函数跟随模块;仅为实际跨模块复用提取 common,不为单次代码建立新层。异步失败明确拒绝,同一错误由负责层展示。
+6
View File
@@ -0,0 +1,6 @@
---
name: v3-use-utils
description: 复用公共工具与认证能力
---
先读根 AGENTS.md 和 `web/src/common/utils/` 的实际实现。命令、路径、通知、PWA 等按已有签名复用;认证 Token 由 user store 维护,不绕过会话与动态路由清理。没有模板示例的角色/日期工具,不引入虚构依赖。页面专用转换留在页面模块,未知外部输入在边界校验。
+5
View File
@@ -0,0 +1,5 @@
---
alwaysApply: true
---
先完整阅读仓库根目录 `AGENTS.md` 与适用的局部指引;目录职责、认证协议、验证及授权均以该文件为准,不维护另一套规则。前端路径均从 `web/` 开始。
+4
View File
@@ -9,3 +9,7 @@ web/dist/
# 本地开发数据 # 本地开发数据
.dev-data/ .dev-data/
deploy/data/ deploy/data/
# 仓库维护的工具规则(其他本地技能不入库)
.agents/skills/*
!.agents/skills/v3-*/
+5
View File
@@ -0,0 +1,5 @@
---
alwaysApply: true
---
先完整阅读仓库根目录 `AGENTS.md` 与适用的局部指引;目录职责、认证协议、验证及授权均以该文件为准,不维护另一套规则。前端路径均从 `web/` 开始。
+44 -1
View File
@@ -70,4 +70,47 @@
- 文档按任务、行为变化或项目约定维护;注释解释意图与约束,项目指引只记录已确认的事实,不额外生成无关文档或备份。 - 文档按任务、行为变化或项目约定维护;注释解释意图与约束,项目指引只记录已确认的事实,不额外生成无关文档或备份。
- 仅在已授权执行且交接信息无法从代码、Git 历史或已有文档恢复时,创建或更新 `CONTEXT.md`;简单修改无需记录。 - 仅在已授权执行且交接信息无法从代码、Git 历史或已有文档恢复时,创建或更新 `CONTEXT.md`;简单修改无需记录。
- 交接内容原位更新,保留有效目标、决策及理由、未完成事项和风险,标注未验证信息,删除过时状态;当前状态不超过 100 行,全文不超过 500 行。 - 交接内容原位更新,保留有效目标、决策及理由、未完成事项和风险,标注未验证信息,删除过时状态;当前状态不超过 100 行,全文不超过 500 行。
- 保留 `CONTEXT.md` 原有跟踪策略:本地专用文件继续忽略,不擅自停止跟踪已纳入版本控制的文件。 - 保留 `CONTEXT.md` 原有跟踪策略:本地专用文件继续忽略,不擅自停止跟踪已纳入版本控制的文件。
## 项目定位与工具链
RawMark 是单用户 Markdown 网页笔记编辑器。文件是真源;Go 标准库 HTTP、SQLite 与文件系统负责服务端,CodeMirror 6 保留原文与 Live Preview,highlight.js 使用固定 Atom One Dark。保留现有明暗主题、排版、插件、图片与备份能力,不引入模板后台示例或虚构角色。
前端采用 Vue 3.5、Router 5、Pinia 3、Element Plus、Axios、TypeScript 6、Vite 8、SCSS、UnoCSS,使用 pnpm 12.11.2;依赖以锁文件为准,不顺手升级。
## 目录与依赖边界
- `web/src/pages/<module>/`:页面入口及模块私有的组件、API、类型、业务状态、编辑器和资源;按实际需要建子目录,不建空目录或转发层。
- `web/src/common/`:确实跨模块复用的 API、外观与字体、Markdown、高亮、插件 SDK、组件和工具。不得依赖页面私有代码;模块入口可提供明确的 props/emits 契约供应用装配。
- `web/src/http/`:Axios、认证头、统一响应与请求错误;`router/`:路由与守卫;`pinia/`:实例及共享认证、权限、设置状态。工作区临时状态不强制放入全局 store。
- `web/types/auto/`:由 Vite 自动导入插件生成;修改源配置并重新构建,不手改声明。业务类型就近定义,测试随模块存放。
- `internal/api/` 定义路由、授权和 HTTP 契约,`internal/auth/` 管理密码与凭据,`internal/store/` 管理应用 SQLite;`internal/vault/` 管理真源文件,搜索索引可重建。`web/*.go` 保留生产嵌入与开发模式边界。
- 模块内部优先相对导入,跨职责使用 `@`(`web/src`)和 `@@`(`web/src/common`)。Vue 组件使用 PascalCase,其他文件使用 kebab-case;SFC 使用 Composition API 与 TypeScript。既有编辑器主题、全局 CSS 和专用 DOM 控件按业务保留,普通表单优先 Element Plus。
- 外部未知数据使用 unknown,在实际边界验证;不以 any、类型忽略或假数据掩盖契约。沿用 ESLint/Antfu,不增加另一套格式化工具。
## 认证与接口契约
- 登录是密码加后端六位图片验证码,首次初始化设置密码;无用户名、注册或角色系统。验证码有效五分钟,绑定挑战及来源,每次登录提交一次性消费,失败必须刷新;保留签发和登录限流。
- 浏览器会话 Token 为 `rms_`,保存在 localStorage `rawmark.token`,固定三十天,不滑动续期;服务端只存哈希。退出请求撤销服务端会话,成功才清理本地身份,不以只删本地 Token 代替注销。
- 受保护 API 只接受 `Authorization: Bearer <token>`,不保留 Cookie 兜底。既有 `rm_` API 令牌及 read/write 权限保留;管理接口仅会话可用,只读令牌允许只读 POST 导出,不能给自己扩权。
- 身份与权限来自 `/auth/me`。401 清理当前认证、权限和动态路由,403 保留身份;匿名跳登录、初始化与登录回跳由守卫负责,拒绝外部及循环回跳。
- 每次替换或退出会话更新代号并取消旧请求;异步响应、用户信息、路由安装和排队写入必须核对所属会话。旧结果按 Axios 取消语义拒绝,不提示、不覆盖或注销新会话。
- 普通 JSON 为 `{ code, data, message }`,成功 code=0;错误 code 非零、message 为可展示原因,保留 HTTP 状态。文件、下载、事件、204 与 OpenAPI 文档保持各自原语义,不叠 JSON。412 的当前版本在 data.rev,当前密码错误为 400,不清理身份。
- 同一错误由一个责任层展示。请求默认提示;已有行内状态、保存重试或批量汇总的 API 明确使用 errorOwner=caller。失败仍 reject,不以空列表或默认值伪装成功。
- 原生图片、字体与插件模块通过受限资源票据读取,主 Token 不进入 URL。票据持久保存哈希、每次检查父会话;事件握手票据一分钟且一次性消费,已连接事件继续核对父凭据。插件票据绑定 ID/版本并支持相对导入;API 与私有资源不进入 PWA 静态缓存。
## 命令与验证
```bash
pnpm -C web install --frozen-lockfile
scripts/dev.sh # 隔离开发数据默认 .dev-data,API_PORT/WEB_PORT 控制端口
pnpm -C web lint:check
pnpm -C web typecheck
pnpm -C web test
pnpm -C web build
/usr/local/go/bin/go vet -tags dev ./...
/usr/local/go/bin/go test ./internal/...
/usr/local/go/bin/go test -tags dev ./web
```
按实际风险补充 Go race、Docker 构建和隔离浏览器验证。只用合成数据测试初始化、导入、恢复与删除,不对生产数据执行迁移或维护;生产部署、重启、数据库迁移执行及 commit/push 均需另行明确授权。
本文件为规范唯一来源,`CLAUDE.md`、`.cursor/rules/`、`.trae/rules/` 仅指回本文件;`.agents/skills/v3-*` 仅提供对应任务的操作入口,不复制第二套规则。
+1
View File
@@ -0,0 +1 @@
@AGENTS.md
+27 -5
View File
@@ -32,14 +32,14 @@ docker run -d --name rawmark \
rawmark rawmark
``` ```
- 首次打开直接设置登录密码,之后凭密码登录;请在将新实例开放给他人访问前完成初始化。 - 首次打开直接设置登录密码,之后凭密码与图片验证码登录;请在将新实例开放给他人访问前完成初始化。
- 笔记以普通 `.md` 文件保存在 `数据目录/vault/`,粘贴、上传和导入的图片统一存放在笔记同级的 `assets/`,链接使用 `./assets/文件名`,同目录相同内容去重。笔记跨目录移动时图片跟随,仍被其他笔记引用的原图保留;删除单篇笔记不会删除共享图片。旧 `笔记名.assets/` 仍可读取、移动与恢复;外部修改会实时同步到网页。 - 笔记以普通 `.md` 文件保存在 `数据目录/vault/`,粘贴、上传和导入的图片统一存放在笔记同级的 `assets/`,链接使用 `./assets/文件名`,同目录相同内容去重。笔记跨目录移动时图片跟随,仍被其他笔记引用的原图保留;删除单篇笔记不会删除共享图片。旧 `笔记名.assets/` 仍可读取、移动与恢复;外部修改会实时同步到网页。
- 知识工具:标准 Markdown 链接构成图谱,支持正文 `#标签` 与 YAML `tags`。笔记或文件夹在应用内改名、移动时维护入链和相对出链;公式和代码示例不参与关系提取。 - 知识工具:标准 Markdown 链接构成图谱,支持正文 `#标签` 与 YAML `tags`。笔记或文件夹在应用内改名、移动时维护入链和相对出链;公式和代码示例不参与关系提取。
- 外观:浅色玻璃工作台与深色模式,默认字体为 Maple Mono Normal NL NF CN v7.9(界面、正文与代码),字体资源随应用本地提供,文字在所需字体加载完成后显示,加载失败会提示刷新;代码块使用 highlight.js,固定采用官网的 Atom One Dark 配色,与页面明暗模式独立;界面、正文与代码字体可分别选择,各字体下拉均支持导入 TTF/OTF/WOFF/WOFF2(单个不超过30 MB)及删除;Maple为不可删除的默认字体,删除正在使用的字体会恢复Maple,导入字体保存在服务器数据目录的fonts/并跨设备共用。界面字号默认 14px,可输入 14–20px 并单独重置,与编辑器及阅读字号独立;排版、玻璃透明度、选中文本工具栏均可在设置中调整并即时保存。 - 外观:浅色玻璃工作台与深色模式,默认字体为 Maple Mono Normal NL NF CN v7.9(界面、正文与代码),字体资源随应用本地提供,文字在所需字体加载完成后显示,加载失败会提示刷新;代码块使用 highlight.js,固定采用官网的 Atom One Dark 配色,与页面明暗模式独立;界面、正文与代码字体可分别选择,各字体下拉均支持导入 TTF/OTF/WOFF/WOFF2(单个不超过30 MB)及删除;Maple为不可删除的默认字体,删除正在使用的字体会恢复Maple,导入字体保存在服务器数据目录的fonts/并跨设备共用。界面字号默认 14px,可输入 14–20px 并单独重置,与编辑器及阅读字号独立;排版、玻璃透明度、选中文本工具栏均可在设置中调整并即时保存。
- 选区格式:电脑与触屏选中文字后提供外观、加粗、斜体、下划线、删除线、高亮和链接。外观可设置配色、字体、局部字号与效果;下划线及外观以受限的标准 HTML 行内标签保存在 Markdown 中,源码与导出保留标签,其他软件的显示取决于其 HTML 支持。清除样式只重置局部外观;底部不再显示格式栏。 - 选区格式:电脑与触屏选中文字后提供外观、加粗、斜体、下划线、删除线、高亮和链接。外观可设置配色、字体、局部字号与效果;下划线及外观以受限的标准 HTML 行内标签保存在 Markdown 中,源码与导出保留标签,其他软件的显示取决于其 HTML 支持。清除样式只重置局部外观;底部不再显示格式栏。
- 图片管理:侧栏默认隐藏 `assets` 和 `笔记名.assets`;右键文件夹选择「图片管理」,查看当前目录及直属附件目录的图片,支持检索、排序、网格/列表、批量上传、引用查看及预览、重命名、移动、替换。可切换整个知识库;清理孤立图片须确认候选,服务端再次核对全库引用后移入回收站,可恢复。上传至所选目录的 `assets/`,相同内容自动去重。根目录可在侧栏空白处右键或通过命令面板打开。图片改名/移动会更新笔记中的本地引用;替换保留路径,选择相同扩展名的图片(最大 50 MB),旧图片不进回收站。 - 图片管理:侧栏默认隐藏 `assets` 和 `笔记名.assets`;右键文件夹选择「图片管理」,查看当前目录及直属附件目录的图片,支持检索、排序、网格/列表、批量上传、引用查看及预览、重命名、移动、替换。可切换整个知识库;清理孤立图片须确认候选,服务端再次核对全库引用后移入回收站,可恢复。上传至所选目录的 `assets/`,相同内容自动去重。根目录可在侧栏空白处右键或通过命令面板打开。图片改名/移动会更新笔记中的本地引用;替换保留路径,选择相同扩展名的图片(最大 50 MB),旧图片不进回收站。
- `--user` 让容器以宿主机用户身份读写数据目录;不指定时容器以 UID 65532 运行,需要该用户对目录有写权限。 - `--user` 让容器以宿主机用户身份读写数据目录;不指定时容器以 UID 65532 运行,需要该用户对目录有写权限。
- 请通过 HTTPS 反向代理(Caddy、Nginx 等)访问,并转发 WebSocket(`/api/v1/events`)。 - 请通过 HTTPS 反向代理(Caddy、Nginx 等)访问,并转发 WebSocket(浏览器使用 `/api/v1/resources/<ticket>/events/`,脚本可使用 Bearer 连接 `/api/v1/events`)。
- 多选与导出:Ctrl/Cmd 点击切换选择,Shift 点击选择可见范围;右键批量删除、移动或导出 Markdown。单篇无附件直接下载 `.md`,其他情况下载包含 Markdown 与本地附件的 ZIP;重名目标不会被覆盖,批量操作会汇报单项失败。 - 多选与导出:Ctrl/Cmd 点击切换选择,Shift 点击选择可见范围;右键批量删除、移动或导出 Markdown。单篇无附件直接下载 `.md`,其他情况下载包含 Markdown 与本地附件的 ZIP;重名目标不会被覆盖,批量操作会汇报单项失败。
- 设置:桌面使用居中窗口,手机使用分类与内容两级布局;支持搜索设置并定位、常用排版项单独重置,自动保存状态与失败提示可见。外观与编辑器分别管理,安装入口位于「关于」。 - 设置:桌面使用居中窗口,手机使用分类与内容两级布局;支持搜索设置并定位、常用排版项单独重置,自动保存状态与失败提示可见。外观与编辑器分别管理,安装入口位于「关于」。
- 回收站与存储:回收站支持清空全部;默认按原删除时间保留 30 天,0 表示永不自动删除,服务启动及每小时执行清理,修改期限同样影响已有条目。设置「存储空间」进入即统计数据目录、外置笔记库及内置可选字体(不含默认 Maple),支持刷新和字体明细;不跟随内部符号链接,附件与回收站包含在笔记库总量中。 - 回收站与存储:回收站支持清空全部;默认按原删除时间保留 30 天,0 表示永不自动删除,服务启动及每小时执行清理,修改期限同样影响已有条目。设置「存储空间」进入即统计数据目录、外置笔记库及内置可选字体(不含默认 Maple),支持刷新和字体明细;不跟随内部符号链接,附件与回收站包含在笔记库总量中。
@@ -61,7 +61,7 @@ docker run -d --name rawmark \
## 开发 ## 开发
需要 Go 1.27+ 与 Node 24+。开发时不必构建前端或 Docker 镜像,一键启动: 需要 Go 1.27+、Node 24+ 与 pnpm 12.11.2(可通过 Corepack 启用)。开发时不必构建前端或 Docker 镜像,一键启动:
```sh ```sh
scripts/dev.sh # 后端 go run -tags dev + 前端 Vite 热更新,Ctrl+C 同时退出 scripts/dev.sh # 后端 go run -tags dev + 前端 Vite 热更新,Ctrl+C 同时退出
@@ -74,8 +74,30 @@ scripts/dev.sh --host # 前端监听所有网卡,手机访问 http://<电脑
检查: 检查:
```sh ```sh
cd web && npm run typecheck && npm test pnpm -C web install --frozen-lockfile
go vet -tags dev ./... && go test ./internal/... pnpm -C web lint:check
pnpm -C web typecheck
pnpm -C web test
pnpm -C web build
go vet -tags dev ./...
go test ./internal/...
go test -tags dev ./web
``` ```
字体资源已入库,普通构建无需下载或转换。重新生成时,在独立 Python 环境安装 `fonttools[woff]`,下载 [v7.9 指定字体包](https://github.com/subframe7536/maple-font/releases/download/v7.9/MapleMonoNormalNL-NF-CN.zip),运行 `python3 scripts/prepare-maple-font.py /path/to/MapleMonoNormalNL-NF-CN.zip`;生成 WOFF2 与 `maple-font.css`,保留上游许可。 字体资源已入库,普通构建无需下载或转换。重新生成时,在独立 Python 环境安装 `fonttools[woff]`,下载 [v7.9 指定字体包](https://github.com/subframe7536/maple-font/releases/download/v7.9/MapleMonoNormalNL-NF-CN.zip),运行 `python3 scripts/prepare-maple-font.py /path/to/MapleMonoNormalNL-NF-CN.zip`;生成 WOFF2 与 `maple-font.css`,保留上游许可。
## 前端结构与接口升级
前端以 [vue3-starter](https://github.com/eezd/vue3-starter/tree/fd41d62ce3f1c8338a478e0be3e2f31b53b7ea5e) 为结构基线,保留 RawMark 的编辑器、主题与布局;模板许可在 `web/LICENSE`。`web/src/pages/workspace/` 管理笔记、目录、编辑器与图片,`pages/settings/` 管理设置及维护,`pages/login/` 管理初始化与验证码登录;私有 API 和类型跟随模块。实际共享的外观、Markdown、插件 SDK 与 API 位于 `common/`;请求、路由、共享状态分别由 `http/`、`router/`、`pinia/` 管理。唯一前端构建入口为 `web/`,使用 pnpm 锁文件。
本次协议升级需前后端一起发布,旧页面与旧 Cookie 会话不能继续使用,需要重新登录。升级前备份数据库和旧镜像;启动新程序时沿用现有 SQLite 初始化流程新增 `resource_tickets` 表及过期索引,保留笔记、设置、密码和 API 令牌。生产迁移与部署须另行授权,不手动运行历史清理 SQL。回滚使用配套数据库和旧镜像;新前端不能连接旧后端。
登录流程先 `POST /api/v1/auth/captcha`,取得 `{captchaId,imageDataUrl,expiresAt}`;刷新传 `replaceId` 使旧挑战失效。再提交 `POST /api/v1/auth/login` 的 `{password,captchaId,captchaCode}`,后端校验六位数字,挑战绑定来源、五分钟过期且一次性消费(密码或验证码失败也会消费)。保留登录防暴力破解与签发限流。首次 `POST /auth/setup` 直接设置密码,不需要已有登录挑战。
登录/初始化返回 `{token,expiresAt}`。浏览器将 `rms_` 会话 Token 保存于 localStorage `rawmark.token`,所有受保护请求带 `Authorization: Bearer <token>`;服务端只存哈希、固定三十天有效,不滑动续期。`GET /auth/me` 返回真实权限及到期时间,`POST /auth/logout` 服务端撤销会话后返回 204;注销失败保留本地身份并明确报错。401 清除当前身份及动态路由,403 保留身份;旧请求或排队写入不能覆盖或注销新会话。localStorage 属于同源脚本可读的凭据,管理员安装的前端插件仍为全权限插件,不能承诺隔离。
普通 JSON 响应统一为 `{code,data,message}`,code=0 成功,message 表示成功或明确失败原因;错误保留 400/401/403/404/409/412/429/500 等 HTTP 状态。通用错误码为 HTTP 状态乘 100,40001 验证码错误/已使用、40002 验证码过期、40003 当前密码错误、40101 密码错误、41200 版本冲突;冲突版本位于 `data.rev`。文件原文、下载、WebSocket、204 和 OpenAPI 文档不包装。旧 JSON 客户端必须更新解包方式,不提供双格式兼容。
`rm_` API 令牌无需重新创建,read/write 权限不变;令牌不能管理账户、令牌、维护或资源票据。只读令牌仍可 POST 导出。第三方脚本只需保留 Bearer 请求头并按新 JSON 协议解包,文件/导出原始字节不变;WebSocket 客户端可继续以 Bearer 请求头连接 `/api/v1/events`。
浏览器原生图片、字体、插件相对模块导入及 WebSocket 无法统一设置认证头,使用 `POST /auth/tickets` 签发的受限资源 URL。图片/字体票据不超过父会话期限,插件另绑定 ID 和版本;每次读取都核对父会话,注销后立即不可读。事件握手票据一分钟有效、只能使用一次,已连接事件在发送与心跳时检查父凭据。主 Token 不放在 URL,不使用 Cookie 兜底;私有响应 no-store、Referrer-Policy=no-referrer,PWA 仅缓存静态外壳。部署代理也须避免记录票据 URL,仓库 Nginx 配置已提供脱敏日志格式;CDN/其他代理需按同样规则配置。