mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 05:56:38 +08:00
df6aa9ff4d
Registered users get snowflake ids above JS MAX_SAFE_INTEGER. /user-info emitted them as JSON numbers and login stored uint64 in the session. Both now use decimal strings. Tests cover admin vs non-admin cookie access to /user/self, /user-info, and /upload/my.
182 lines
15 KiB
Markdown
182 lines
15 KiB
Markdown
# AGENTS.md
|
||
|
||
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
|
||
|
||
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
||
|
||
## 1. Think Before Coding
|
||
|
||
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
||
|
||
Before implementing:
|
||
- State your assumptions explicitly. If uncertain, ask.
|
||
- If multiple interpretations exist, present them - don't pick silently.
|
||
- If a simpler approach exists, say so. Push back when warranted.
|
||
- If something is unclear, stop. Name what's confusing. Ask.
|
||
|
||
## 2. Simplicity First
|
||
|
||
**Minimum code that solves the problem. Nothing speculative.**
|
||
|
||
- No features beyond what was asked.
|
||
- No abstractions for single-use code.
|
||
- No "flexibility" or "configurability" that wasn't requested.
|
||
- No error handling for impossible scenarios.
|
||
- If you write 200 lines and it could be 50, rewrite it.
|
||
|
||
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
||
|
||
## 3. Surgical Changes
|
||
|
||
**Touch only what you must. Clean up only your own mess.**
|
||
|
||
When editing existing code:
|
||
- Don't "improve" adjacent code, comments, or formatting.
|
||
- Don't refactor things that aren't broken.
|
||
- Match existing style, even if you'd do it differently.
|
||
- If you notice unrelated dead code, mention it - don't delete it.
|
||
|
||
When your changes create orphans:
|
||
- Remove imports/variables/functions that YOUR changes made unused.
|
||
- Don't remove pre-existing dead code unless asked.
|
||
|
||
The test: Every changed line should trace directly to the user's request.
|
||
|
||
## 4. Goal-Driven Execution
|
||
|
||
**Define success criteria. Loop until verified.**
|
||
|
||
Transform tasks into verifiable goals:
|
||
- "Add validation" → "Write tests for invalid inputs, then make them pass"
|
||
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
|
||
- "Refactor X" → "Ensure tests pass before and after"
|
||
|
||
For multi-step tasks, state a brief plan:
|
||
```
|
||
1. [Step] → verify: [check]
|
||
2. [Step] → verify: [check]
|
||
3. [Step] → verify: [check]
|
||
```
|
||
|
||
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
|
||
|
||
---
|
||
|
||
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
|
||
|
||
## Git 提交规范
|
||
|
||
每次完成一个功能点开发或修复一个问题后,务必提交 Git commit , 禁止推送远程仓库。
|
||
遵循 Conventional Commits:`<type>(<scope>): <subject>`(例:`feat(auth): support email login`)。
|
||
|
||
## 务必阅读匹配的 Skill
|
||
|
||
| Skill | 何时使用 |
|
||
| :--- | :--- |
|
||
| `new-api` | 基于 Cordis 插件开发业务 HTTP API、通过 `ctx.Router()` 声明路由与挂载中间件 |
|
||
| `new-async-task` | 基于 Cordis 插件通过 `ctx.Task()` 与 `ctx.Schedule()` 注册 Asynq 异步任务与定时调度 |
|
||
| `new-setting` | 基于 Cordis 插件通过 `ctx.Settings()` 声明配置 Schema、绑定 YAML 配置或管理台热加载设置 |
|
||
| `database-migration` | 插件自包含 `embed.FS` 独立 Goose SQL 迁移(PG/SQLite 双方言、ClickHouse 分析库) |
|
||
| `cache-framework` | 基于 `ctx.Cache()` 与 `contracts.CacheService` 访问三层缓存(RAM L1 + Redis L2 + Pub/Sub 同步) |
|
||
| `logstore` | 日志/分析用途表、`internal/repository/logstore`、切换日志主库、PG/SQLite 回落 |
|
||
| `clickhouse-batchwriter` | ClickHouse 批量写入、`internal/infra/persistence/batchwriter` 接入、分析表异步 flush 与背压策略 |
|
||
| `file-upload` | 业务上传文件、Worker 程序化摄取、`upload.Ingest` / `contracts.StorageService`、文件访问与统计 |
|
||
| `push-notification` | 系统通知推送事件、统一触发器投递、带消息推送的业务功能 |
|
||
| `release-guide` | 根据自上一正式版本 Tag 以来的提交整理 Version Bump 提交信息以触发双语 Release |
|
||
| `shadcn` | 添加、修改或组合 shadcn/ui 组件 |
|
||
|
||
## 严格遵循事项 (Guardrails)
|
||
|
||
- 切勿删除 `frontend/node_modules`。
|
||
- 保持 `backend/pkg/util/` 绝对纯净,禁止导入 Gin、GORM、sessions 等 Web/数据库框架包。
|
||
- 测试用例禁止硬编码相对路径创建临时目录,统一使用 Go 内置 `t.TempDir()`。
|
||
- 修改 API Handler 后运行 `make swagger`,完成代码开发后必须依次运行 `make code-check` 与 `make format`。
|
||
|
||
### Cordis 架构核心防线与分层规范
|
||
- **微内核 (`backend/core/`)**:
|
||
- 上下文总线(`Context`)、泛型依赖注入(`Container`)、生命周期编排(`Lifecycle`)、扩展点定义(`extpoints/`)与领域事件总线(`EventBus`)。
|
||
- **严禁**包含任何具体业务逻辑,**严禁** import `gin`、`gorm`、`asynq` 等具体运行时依赖。
|
||
- **服务契约 (`backend/core/contracts/`)**:
|
||
- 跨插件通信的统一公开 Go Interface(如 `AuthService`、`UserService`、`CacheService`、`DBService`、`StorageService`)与公共 DTO。
|
||
- **严禁**包含任何具体业务实现或 SQL 操作。
|
||
- **自包含插件 (`backend/plugins/`)**:
|
||
- 所有业务功能与驱动实现均以插件形式存在(`backend/plugins/drivers/`、`backend/plugins/infra/`、`backend/plugins/domain/` 或下游 `backend/downstream/`)。
|
||
- 每个插件实现 `core.Plugin`(`Name() string` 与 `Apply(ctx *core.Context) error`)。
|
||
- **分层模式选型**:
|
||
- **模式 1(极简单文件分层,微型插件)**:单 package 极简结构(仅单文件 `plugin.go`, `handlers.go`, `service.go`, `repository.go`, `models.go`, `errs.go`, `migrations/`)。
|
||
- **模式 2(标准独立子包分层,推荐标准)**:多 package 物理隔离(`plugin.go`, `handler/`, `service/`, `repository/`, `model/`, `errs/`, `migrations/`)。**严禁在根包平铺 `handlers_*`、`service_*`、`repository_*` 等前缀文件**,子包内文件直接按业务命名(如 `user.go`, `config.go`),严格约束 `handler -> service -> repository -> model` 单向依赖。
|
||
- **插件通信与依赖隔离**:
|
||
- **严禁跨包 import internal/私有实现**:插件之间严禁直接 import 对方具体实现包代码。
|
||
- **单向服务契约调用**:调用方仅面向 `backend/core/contracts` 编程,在 `Apply` 中通过 `core.Provide[contracts.XxxService](ctx, svc)` 注册服务,通过 `core.Inject[contracts.XxxService](ctx)` 或 `ctx.Using(func(svc contracts.XxxService) { ... })` 声明式解析。
|
||
- **事件总线广播**:状态联动与解耦通信统一通过强类型事件 `ctx.Events().Emit()` 广播,由感兴趣的插件通过 `ctx.Events().On()` 订阅,消除双向依赖与循环引用。
|
||
- **扩展点自包含注册**:
|
||
- **HTTP 路由与白名单机制**:
|
||
- 插件自包含在 `Apply` 中通过 `ctx.Router().Group(...)` 挂载路由与中间件,禁止跨插件散落注册。
|
||
- **白名单机制**:`driver_http` 与微内核扩展点提供路由白名单支持(`ctx.Router().RegisterWhitelist(patterns...)`),支持精确路径与通配符(如 `/api/v1/oauth/*`)。
|
||
- **所有权主动声明**:认证域(`auth` 插件)与各业务插件必须在 `Apply` 中主动注册其公开/免鉴权接口(如 `/api/v1/user/login`、`/api/v1/oauth/callback`、`/api/v1/cap/*` 等)。
|
||
- **鉴权中间件放行防线**:`auth` 提供的登录鉴权中间件(`LoginRequired`)必须先执行白名单匹配并自动放行,彻底杜绝免鉴权接口被全局或组级鉴权中间件误拦截(返回 401 Unauthorized)。
|
||
- **异步与定时任务**:插件自包含在 `Apply` 中通过 `ctx.Task().Register(...)` 与 `ctx.Schedule().RegisterCron(...)` 声明。
|
||
- **静态启动配置**:插件自包含在 `Apply` 中通过 `ctx.Config().Bind("<prefix>", &cfg)` 读取**自己声明**的配置,字段以 tag 表达来源:`config`(yaml 路径)、`env`(覆盖变量名)、`default`、`autoEnable`(该变量存在即置真)、`secret`(导出脱敏)。需要在 `Apply` 之前被门禁求值的键,必须在 `DeclareConfig()` 中提前声明并实现 `core.ConfigGatedPlugin`。新增基础设施 key 保持顶层命名(`redis.*`),插件私有配置归 `plugins.<name>.*`。**严禁**再造全局配置单例或在 `backend/pkg/` 读取配置。
|
||
- **动态设置**:插件自包含在 `Apply` 中通过 `ctx.Settings().Register(core.SettingSchema{...})` 声明可热更新的管理台设置模式(与上面的静态启动配置分属两层)。
|
||
- **数据迁移**:插件自包含在内部维护 `migrations/*.sql`,通过 `//go:embed` 打包并在 `Apply` 中通过 `ctx.Migrations().Register(pluginID, embedFS)` 注入。
|
||
- **表单一所有者原则 (Single Owner Principle)**:
|
||
- 每张数据表有且仅由一个所有者插件声明与维护(表名使用插件前缀如 `w_order_*`)。
|
||
- 严禁插件 B 跨过所有者插件 A 直接 DDL/DML 旁路读写表 A,必须调用插件 A 暴露的 `contracts` 接口或订阅事件。
|
||
- **平台服务复用**:
|
||
- 文件摄取统一使用 `upload.Ingest` / `contracts.StorageService`,禁止绕过存储域直接操作底层 Bucket 或直写文件表。
|
||
- 业务缓存统一使用 `ctx.Cache()`(`contracts.CacheService`)或标准缓存框架,禁止自研不带失效广播的本地 map。
|
||
- 数据库操作通过 `ctx.DB()`(`contracts.DBService`)获取受事务与 Trace 保护的连接。
|
||
|
||
## 后端开发规范
|
||
|
||
### API 响应规范
|
||
- **统一信封**:`{ "error_msg": "", "data": ... }`
|
||
- **成功**:HTTP 200,写出 `c.JSON(http.StatusOK, response.OK(data))` 或 `response.OKNil()`。
|
||
- **失败**:使用 `backend/pkg/response` 的 `Abort*` 系列函数(如 `AbortBadRequest`、`AbortUnauthorized`、`AbortNotFound`、`AbortInternal`)中断请求。
|
||
- **错误文案**:使用模块内 `errs.go` 中的 camelCase 字符串常量(如 `errBindParamsFailed`),禁止暴露底层数据库/系统错误细节给客户端。
|
||
- **Service/Logics 分工**:业务逻辑层只接受 `context.Context`,返回 `(result, error)`,严禁依赖 `*gin.Context` 或调用 `c.JSON`/`Abort*`。
|
||
- **错误日志**:底层错误在 Handler/Logic 边界用 `backend/pkg/logger` 打印日志,禁止使用 `_ = ...` 静默吞掉关键错误。
|
||
|
||
### 数据库操作
|
||
- 插件数据库表结构严禁使用 GORM AutoMigrate,统一编写 Goose SQL 迁移并嵌入二进制。
|
||
- 不创建物理外键(显式建索引);Go 模型零值需与数据库默认值匹配。
|
||
- **SQL LIKE 查询防注入与转义**:所有含用户输入的模糊查询必须调用 `backend/pkg/util.EscapeLike` 转义通配符,并显式指定 `ESCAPE '\\'` 语法(如 `Where("username LIKE ? ESCAPE '\\'", util.EscapeLike(keyword)+"%")`),同时兼容 PostgreSQL 与 SQLite 方言并杜绝通配符注入攻击。
|
||
|
||
### 并发与安全防护规范
|
||
- **Goroutine 安全**:禁止直接使用裸 `go func()`;统一使用 `backend/pkg/util.Go`,确保具备未捕获 panic 恢复和调用栈日志记录能力。
|
||
- **Pub/Sub 监听并发安全**:启动 Redis Pub/Sub 订阅监听前,必须捕获局部客户端实例,禁止在 goroutine 闭包中直读可变全局变量;提供停止监听接口时必须维护 `done` 通道等待 goroutine 完整退出后再重置状态,消除数据竞争。
|
||
- **Session 固定攻击防御**:用户登录/授权成功后,必须调用 Session 轮换逻辑,防止 Session 固定攻击。
|
||
- **防账户枚举与时序攻击**:
|
||
- 登录失败统一返回模糊报错;当查询用户不存在时,必须调用 `pkg/util.DummyCheckPassword` 执行同等开销的 bcrypt 哈希计算,彻底消除时序侧信道攻击。
|
||
- 验证码、签名 Token 等敏感字符串比对必须使用 `crypto/subtle.ConstantTimeCompare` 常量时间比对。
|
||
- **敏感端点限流**:登录尝试、OAuth 授权发起等敏感接口必须接入基于 Redis 的滑动窗口限流机制,防止暴力破解与缓存资源耗尽。
|
||
|
||
## 前端开发规范
|
||
|
||
- 新特性开发前参考 Next.js 文档与 `frontend/app/(main)/admin/demo` 示例代码。
|
||
- **页面容器与标题栏**:
|
||
- 页面根容器统一使用全宽 `w-full`,最外层统一用 `py-6` 或 `py-6 px-1` 对齐边距。
|
||
- 标题容器统一 `flex items-center gap-2`(带操作按钮用 `justify-between`)。
|
||
- 图标直接使用 Lucide 组件(`size-5 text-primary`),禁止包裹背景小卡片或装饰边框。
|
||
- 标题文字统一使用 `<h1 className="text-2xl font-semibold tracking-tight">`。
|
||
- **无障碍语义与色彩规范 (a11y & WCAG)**:
|
||
- **标题层级规范 (Heading Hierarchy)**:页面中非顶级结构化标题(如空状态提示、加载提示、卡片眉题/卡片标题、抽屉区块名)严禁滥用 `<h3>`/`<h4>`,统一使用 `<p>` 配合样式,保证屏幕阅读器感知的标题层级连续。
|
||
- **无文本控件无障碍**:所有仅包含图标的按钮(如仅有 Icon 的 Button、Switch、无文本的 SelectTrigger)必须显式添加 `aria-label`。
|
||
- **色彩对比度**:正文、提示、徽章等小字颜色在亮色/暗色模式下必须满足 WCAG AA(对比度 ≥ 4.5:1)。
|
||
- **组件拆分与维护**:
|
||
- 物理路由页面 `page.tsx` 仅维护高级骨架与布局。
|
||
- 单文件超过 600 行或含多 Tab/大复杂区块时,必须按就近原则拆分为子组件存放在路由同级的 `components/` 局部目录中。
|
||
- **样式与服务**:
|
||
- 优先使用 shadcn/ui 的 `variant` 和全局 CSS 变量,不要在业务代码中硬编码颜色/背景。
|
||
- 前端请求统一在 `frontend/lib/services/<name>/` 中继承 `BaseService` 编写并在 `index.ts` 注册。
|
||
- **国际化 (i18n)**:
|
||
- 使用 `next-intl`(**无 URL locale 前缀** / non-routing provider 模式),兼容 `NEXT_STANDALONE_EXPORT` 静态导出。
|
||
- 支持语言:`zh-CN`、`en`;默认 `zh-CN`。
|
||
- 解析优先级:cookie `NEXT_LOCALE`(用户显式选择)→ 浏览器语言 → 默认 `zh-CN`。
|
||
- 文案统一放在 `frontend/messages/{locale}.json`,按命名空间嵌套(`common` / `layout` / `auth` / `settings` / 业务域)。
|
||
- 组件内用户可见文案必须通过 `useTranslations()` / `getTranslations()` 读取;**禁止**新增中英硬编码 UI 字符串(后端返回的 `error_msg`、日志、调试信息除外)。
|
||
- key 使用 camelCase 分层(如 `auth.login.submit`);完整短语作为 value,禁止在组件内拼接句子。
|
||
- 新增或修改文案时必须**同步**更新 `zh-CN.json` 与 `en.json`,保持 key 树一致。
|
||
- 语言选项展示用自称:`中文` / `English`(不随当前 UI 语言翻译)。
|
||
- 日期/数字格式化使用 locale 感知 helper(如 `formatDateTime`),禁止写死 `'zh-CN'` / `date-fns` 的 `zhCN`。
|