mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 05:46:36 +08:00
update doc
This commit is contained in:
@@ -16,25 +16,342 @@
|
|||||||
* **[docs/design/architecture.md](./docs/design/architecture.md)**:理解 Server、Agent、OpenResty 与前端的职责边界与网络拓扑。
|
* **[docs/design/architecture.md](./docs/design/architecture.md)**:理解 Server、Agent、OpenResty 与前端的职责边界与网络拓扑。
|
||||||
* **[docs/design/agent-design.md](./docs/design/agent-design.md)**:理解 Agent 设计原则、与 Server 交互时序、OpenResty 管控与配置发布回滚模型。
|
* **[docs/design/agent-design.md](./docs/design/agent-design.md)**:理解 Agent 设计原则、与 Server 交互时序、OpenResty 管控与配置发布回滚模型。
|
||||||
|
|
||||||
### 3. 部署与参考手册 (Deployment & References)
|
|
||||||
|
|
||||||
* **[docs/deployment/deployment.md](./docs/deployment/deployment.md)** / **[server.md](./docs/deployment/server.md)** / **[agent.md](./docs/deployment/agent.md)** / **[upgrade.md](./docs/deployment/upgrade.md)**:Server 和 Agent 的单机、Docker 部署配置,接入、升级与维护策略。
|
|
||||||
* **[docs/reference/configuration.md](./docs/reference/configuration.md)** / **[cli.md](./docs/reference/cli.md)**:支持的环境变量、参数、命令行与配置文件参考。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 开发与执行要求
|
## Git 提交规范指南
|
||||||
|
|
||||||
|
### 提交信息基本格式
|
||||||
|
|
||||||
|
每次提交更改时,应当使用以下提交格式:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Type**: 提交类型(例如 `feat`, `fix`, `refactor`, `perf`, `docs`, `chore` 等)。
|
||||||
|
* **Scope** (可选): 影响的范围(例如 `api`, `frontend`, `auth`, `mcp` 等)。
|
||||||
|
* **Subject**: 简短的一句话描述变更。
|
||||||
|
* **Body** (可选): 详细的说明,多行叙述。
|
||||||
|
|
||||||
|
## 务必阅读匹配的 Skill
|
||||||
|
|
||||||
|
| Skill | 何时使用 |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `new-api` | 添加或修改自定义业务 API、Handler、服务层逻辑、自定义路由注册 |
|
||||||
|
| `new-async-task` | 添加或修改 Asynq 任务、定时任务、TaskHandler、任务元数据 |
|
||||||
|
| `new-setting` | 添加或修改系统/业务/公开设置、`/admin/system` 参数或 `/admin/settings` 图形化设置 |
|
||||||
|
| `database-migration` | 数据库表结构变更、goose SQL 迁移、seed 数据 |
|
||||||
|
| `file-upload` | 业务上传文件、Worker 程序化摄取、`upload.Ingest` 策略选型、文件访问与 `w_uploads` / 统计排查 |
|
||||||
|
| `push-notification` | 系统通知推送事件、统一触发器投递、带消息推送的业务功能 |
|
||||||
|
| `release-guide` | 根据自上一正式版本 Tag 以来的提交整理 Version Bump 提交信息以触发双语 Release |
|
||||||
|
| `shadcn` | 添加、修改或组合 shadcn/ui 组件 |
|
||||||
|
|
||||||
|
|
||||||
|
## 严格遵循事项 (Guardrails)
|
||||||
|
|
||||||
|
- 切勿删除 `frontend/node_modules`
|
||||||
|
- 保持 `internal/util/` 绝对纯净且不引入任何框架。禁止从 `internal/util/` 及其子包中导入 Gin、GORM、sessions 等 HTTP/Web/数据库相关框架包(例如,Web 会话选项已收敛至 `internal/apps/oauth/session.go`)。
|
||||||
|
- 编写测试用例时,禁止使用硬编码的相对路径(如 `"uploads/test_cache"`)在源码目录下创建临时测试目录,必须统一使用 Go 内置的 `t.TempDir()` 以避免污染源码目录。
|
||||||
|
- 所有 HTTP 路由仅在 `internal/router/router.go` 中注册。
|
||||||
|
- 当 API Handler 发生变化时,更新 Swagger 文档(运行 `make swagger`)。
|
||||||
|
- 在完成代码开发后必须运行 `make code-check`, 并修复报错。
|
||||||
|
- 需要缓存或文件管理能力时,必须复用现有平台实现,禁止在业务包中自行创建缓存目录、直接管理缓存文件或重复封装存储后端。
|
||||||
|
- 文件摄取必须通过 `upload.Ingest`(`upload.PolicyCreate` / `PolicyDedupNewRecord` / `PolicyResolveExisting`);删除必须通过 `upload.Remove` 或 `upload.RemoveOwned`。禁止业务模块直接调用 `repository.CreateUpload` / `repository.SoftDeleteUpload`,禁止 `db.Create(&model.Upload{})` 旁路写 `w_uploads`。
|
||||||
|
- 禁止在 `init()` 中注册跨模块集成(任务 Handler、推送内置事件、域事件监听器、任务完成钩子)。统一通过 `internal/bootstrap` 在 `internal/cmd` 入口显式装配。
|
||||||
|
- `internal/router/router.go` 的 `Serve()` 仅负责 HTTP 路由与中间件,禁止在其中执行 `SyncEvents`、`InitLogWriter` 等进程级运行时初始化。
|
||||||
|
- 核心业务模块(如 `oauth`、`user`)禁止直接 `import` `internal/apps/admin/push` 或 `custom_events` 触发通知;应通过 `internal/listener` 发射域事件,由 push 模块在 bootstrap 阶段订阅。
|
||||||
|
- 编写依赖任务注册或推送事件同步的测试时,必须在测试 setup 中显式调用 `bootstrap.RegisterTasks()`、`bootstrap.RegisterPushDomainEvents()` 等,不得依赖 `init()` 副作用。
|
||||||
|
- API 错误响应必须通过 `response.Abort*` 中断请求,由 `ErrorHandlerMiddleware` 统一写出 JSON;禁止 `c.JSON(http.StatusOK, response.Err(...))` 及 Handler 直接 `c.JSON(status, response.Err(...))`。
|
||||||
1. **设计先行**:
|
1. **设计先行**:
|
||||||
* 开发新功能或重要特性时,必须在 `docs/design/` 下创建/更新对应的设计文档,理清架构与核心决策。
|
* 开发新功能或重要特性时,必须在 `docs/design/` 下创建/更新对应的设计文档,理清架构与核心决策。
|
||||||
* 新增的设计文档应同步更新至 `docs/design/architecture.md` 及在 `docs/config.ts` 中注册侧边栏路由。
|
* 新增的设计文档应同步更新至 `docs/design/architecture.md` 及在 `docs/config.ts` 中注册侧边栏路由。
|
||||||
* 若实现内容超出产品边界,必须先修改设计文档,再编码实现。
|
* 若实现内容超出产品边界,必须先修改设计文档,再编码实现。
|
||||||
2. **遵守约束**:
|
|
||||||
* 必须严格遵循 `docs/guideline/` 下的所有开发准则与开发约束规范,不得绕过任何规范。
|
|
||||||
* 涉及前端改造或管理端 UI 时,必须遵守 `docs/guideline/development-constraints.md` 中的前端规范。
|
|
||||||
3. **开发计划与交接**:
|
3. **开发计划与交接**:
|
||||||
* 正在进行的开发计划或 AI 接手交接发生变化时,在 `docs/plan/` 下更新对应的开发计划或接手文档,并使用相应模板初始化。
|
* 正在进行的开发计划或 AI 接手交接发生变化时,在 `docs/plan/` 下更新对应的开发计划或接手文档,并使用相应模板初始化。
|
||||||
4. **文档与变更日志**:
|
4. **文档与变更日志**:
|
||||||
* 当相关内容发生变化时,同步更新对应的**中文文档**(不要同步英文文档)。
|
* 当相关内容发生变化时,同步更新对应的**中文文档**(不要同步英文文档)。
|
||||||
* 代码或配置变更完成后,必须在 [`docs/changelog/index.md`](./docs/changelog/index.md) 的 `[Unreleased]` 区块补充对应变更条目。
|
* 代码或配置变更完成后,必须在 [`docs/changelog/index.md`](./docs/changelog/index.md) 的 `[Unreleased]` 区块补充对应变更条目。
|
||||||
* **纯文档变更(如 `docs/` 下的 Markdown 文档、README 等)不需要写入 changelog。**
|
* **纯文档变更(如 `docs/` 下的 Markdown 文档、README 等)不需要写入 changelog。**
|
||||||
|
|
||||||
|
## 项目介绍
|
||||||
|
|
||||||
|
### 技术栈
|
||||||
|
|
||||||
|
- 后端:Go 1.25+、Gin、GORM、PostgreSQL、可选 ClickHouse、Redis、Asynq、Cobra、Viper、Swaggo、OpenTelemetry、Zap、AWS SDK v2、Snowflake IDs。
|
||||||
|
- 前端:Next.js App Router、TypeScript、Tailwind CSS、pnpm、shadcn/ui。
|
||||||
|
|
||||||
|
### 目录结构与平台能力
|
||||||
|
|
||||||
|
顶层目录:
|
||||||
|
|
||||||
|
- `main.go`:程序入口,委派给 `internal/cmd`。
|
||||||
|
- `config.example.yaml`:已提交的配置模板。在添加配置字段时保持更新。
|
||||||
|
- `config.yaml`:本地运行时的配置文件。不要将其作为已提交的源码提交。
|
||||||
|
- `docker/`:集成的、仅前端的和仅后端的 Dockerfile。
|
||||||
|
- `docs/`:自动生成的 Swagger 文档。请勿手动编辑生成的文件。
|
||||||
|
- `frontend/`:Next.js 应用。
|
||||||
|
- `internal/`:私有 Go 后端代码。
|
||||||
|
- `pkg/`:公共 Go 库/工具包(留作扩展或存放不依赖特定业务的通用代码)。
|
||||||
|
- `scripts/`:本地和 CI 辅助脚本。
|
||||||
|
- `support-files/`:部署 and 数据库辅助文件。
|
||||||
|
- `bin/`:本地编译生成的二进制可执行文件。
|
||||||
|
- `data/`:本地运行时数据文件目录(如 PostgreSQL、Redis 数据等)。
|
||||||
|
- `uploads/`:本地文件上传存储目录。
|
||||||
|
|
||||||
|
后端目录:
|
||||||
|
|
||||||
|
- `internal/cmd/`:用于 API、worker、scheduler、root init 的 Cobra 命令。进程启动时在此调用 `bootstrap.Register*` 与 `bootstrap.Init`,再启动 router / worker / scheduler。
|
||||||
|
- `internal/bootstrap/`:应用装配根(composition root)。集中注册任务 Handler、推送域事件订阅、任务完成监听器,并执行 `SyncEvents`、ClickHouse 访问日志写入等进程级初始化;所有注册函数使用 `sync.Once` 保证幂等。
|
||||||
|
- `internal/config/`:Viper 加载和配置结构体。运行时代码应使用 `config.Config.<Section>.<Field>`。
|
||||||
|
- `internal/router/`:唯一的 HTTP 路由注册点。
|
||||||
|
- `internal/apps/`:按功能(Feature-based)组织的 HTTP Handler、中间件、内部服务与模块逻辑。移除全局 service 层,模块内部业务逻辑(如验证码业务逻辑管理器 `internal/apps/cap/manager.go`)均收敛于各自模块中;管理端模块位于 `internal/apps/admin/`。
|
||||||
|
- `internal/apps/upload/`:上传记录、文件访问控制、本地/S3 文件响应、下载及图片 WebP 压缩。业务应复用 `upload.Ingest` / `upload.Remove` 与 `GET /f/:id` 文件服务,不直接操作底层 storage 或旁路写 `w_uploads`。
|
||||||
|
- `internal/model/`:GORM 实体和模型级业务方法。
|
||||||
|
- `internal/db/`:PostgreSQL、Redis、ClickHouse、GORM 日志、ID 生成和 goose SQL 迁移的布线。
|
||||||
|
- `internal/diskcache/`:平台级磁盘字节缓存,通过 `diskcache.GetGlobalCache()` 提供 TTL、最大空间限制、LRU 淘汰、清空、状态统计和配置热更新。写入时使用 `DefaultExpiration`(全局默认 TTL)、正数 `time.Duration`(业务 TTL)或 `NoExpiration`(无 TTL,仍受空间限制和 LRU 淘汰)。
|
||||||
|
- `internal/storage/`:S3 兼容对象存储适配,提供对象上传、读取、删除、CDN/代理读取及远端对象本地缓存。
|
||||||
|
- `internal/task/`:Asynq 任务框架;参见 `new-async-task` 了解变更。
|
||||||
|
- `internal/common/`:共享的通用模型及响应(如 `internal/common/response`)、绑定(bind)、常量以及通用错误。
|
||||||
|
- `internal/util/`:纯底层工具包,无任何 HTTP/数据库框架依赖。
|
||||||
|
- `internal/listener/`:域事件分发层。核心域(auth、user 等)在此定义并发射事件(如 `EmitAdminLoggedIn`);运维模块(push、webhook 等)在 bootstrap 阶段订阅,实现跨模块解耦。
|
||||||
|
- `internal/otel_trace/`:链路追踪(tracing)助手。
|
||||||
|
- `internal/testhelper/`:后端测试共享辅助能力。
|
||||||
|
- `internal/buildinfo/`:暴露在发布/构建工作流中注入的元数据(如版本号、编译时间等)。
|
||||||
|
|
||||||
|
公共底层包 (`pkg/`):
|
||||||
|
- `pkg/cache/disk/`:纯底层的通用本地磁盘缓存引擎。
|
||||||
|
- `pkg/cap/`:底层的通用验证码验证和生成库。
|
||||||
|
- `pkg/httppool/`:管理全局共享且经过优化的 HTTP 传输客户端及连接池,集成 OTel 链路追踪。
|
||||||
|
- `pkg/logger/`:Zap 和 OTel 日志助手。
|
||||||
|
- `pkg/push/`:推送渠道客户端集成(Lark/Telegram/Email)。
|
||||||
|
- `pkg/mail/`:邮件发送客户端。
|
||||||
|
- `pkg/trace/`:OpenTelemetry 链路追踪配置。
|
||||||
|
- `pkg/util/`:纯底层无副作用的系统工具(Crypto/Password/UUID)。
|
||||||
|
|
||||||
|
前端目录:
|
||||||
|
|
||||||
|
- `frontend/app/`:App Router 页面、路由组、根布局、全局配置。
|
||||||
|
- `frontend/components/ui/`:shadcn/ui 基础组件。
|
||||||
|
- `frontend/components/common/`:跨页面的业务组件。
|
||||||
|
- `frontend/components/layout/`:Header、Sidebar、Footer 等应用布局组件。
|
||||||
|
- `frontend/components/auth/`、`home/`、`animate-ui/`、`providers/`:特定作用域的 UI 组件。
|
||||||
|
- `frontend/lib/services/`:基于 `BaseService` 的类型化 API 服务,按业务域拆分并由 `services` 对象统一导出。
|
||||||
|
- `frontend/contexts/`、`hooks/`、`lib/`、`types/`、`public/`:共享状态、Hook、客户端与实用工具、TypeScript 类型、静态资产。
|
||||||
|
- `frontend/scripts/`:前端构建和维护脚本。
|
||||||
|
- `frontend/.next/`、`frontend/out/`、`frontend/node_modules/`:本地生成或安装的产物,不作为业务源码编辑。
|
||||||
|
|
||||||
|
|
||||||
|
## 开发要求
|
||||||
|
|
||||||
|
### 后端规则
|
||||||
|
|
||||||
|
命名规范:
|
||||||
|
|
||||||
|
- Go 包和文件使用小写蛇形命名(lowercase snake case):如 `auth_source`、`postgres_logger.go`。
|
||||||
|
- 导出的 Go 标识符使用 PascalCase;未导出的标识符使用 camelCase。
|
||||||
|
- 请求/响应结构体使用 camelCase 并带有后缀,例如 `listUsersRequest` 和 `listUsersResponse`。
|
||||||
|
- 错误消息常量是 camelCase 字符串 `const`值,而不是包级别的 `error` 值。
|
||||||
|
- YAML 配置键使用小写蛇形命名(lowercase snake case)。
|
||||||
|
|
||||||
|
Handler 规范:
|
||||||
|
|
||||||
|
- Handler 命名为 动词 + 名词,例如 `ListUsers`。
|
||||||
|
- 使用 `ShouldBindQuery` 或 `ShouldBindJSON` 进行绑定。
|
||||||
|
- 每个 HTTP API 都需要有完整的 Swagger 注释;在 API 变更后运行 `make swagger`。
|
||||||
|
|
||||||
|
#### API 响应信封(统一格式)
|
||||||
|
|
||||||
|
所有 JSON API 响应的外层结构**必须**为:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "error_msg": "", "data": ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
- 成功时:`error_msg` 为空字符串,`data` 承载业务载荷。
|
||||||
|
- 失败时:`data` 为 `null`,`error_msg` 为用户可见的错误说明。
|
||||||
|
- 分页响应在 `data` 下使用 `{ "total": 0, "results": [] }`。
|
||||||
|
|
||||||
|
#### 成功响应(唯一写法)
|
||||||
|
|
||||||
|
成功时**始终**使用 HTTP `200`,由 Handler 直接写出 JSON:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"github.com/Rain-kl/Wavelet/internal/common/response"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 有数据
|
||||||
|
c.JSON(http.StatusOK, response.OK(data))
|
||||||
|
|
||||||
|
// 无数据(data 为 null)
|
||||||
|
c.JSON(http.StatusOK, response.OKNil())
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 失败响应(中断请求,禁止直接写错误 JSON)
|
||||||
|
|
||||||
|
失败时**禁止**在 Handler / 中间件中直接调用 `c.JSON(..., response.Err(msg))`,也**禁止**用 HTTP `200` 携带非空 `error_msg` 表示失败。
|
||||||
|
|
||||||
|
统一通过 `internal/common/response` 的 **Abort 系列函数**中断请求。这些函数会将 `*response.APIError` 挂载到 Gin 的 `c.Errors` 链并 `c.Abort()`;请求结束后由全局 `response.ErrorHandlerMiddleware()`(在 `internal/router/middlewares.go` 中注册)统一写出 JSON,并记录到 OpenTelemetry Trace/Jaeger。
|
||||||
|
|
||||||
|
**推荐使用的便捷函数(优先于手写状态码):**
|
||||||
|
|
||||||
|
| 函数 | HTTP 状态码 | 典型场景 |
|
||||||
|
|------|-------------|----------|
|
||||||
|
| `response.AbortBadRequest(c, msg)` | 400 | 参数绑定失败、字段校验、业务规则拒绝(如密码错误、重复注册) |
|
||||||
|
| `response.AbortUnauthorized(c, msg)` | 401 | 未登录、Session/Token 失效(`oauth.LoginRequired()`) |
|
||||||
|
| `response.AbortForbidden(c, msg)` | 403 | 已登录但无权访问(如 Token 不允许访问的端点) |
|
||||||
|
| `response.AbortNotFound(c, msg)` | 404 | 资源不存在;管理员中间件对非管理员隐藏端点时也使用此码 |
|
||||||
|
| `response.AbortConflict(c, msg)` | 409 | 资源冲突(如唯一键重复) |
|
||||||
|
| `response.AbortTooManyRequests(c, msg)` | 429 | 限流、频率限制 |
|
||||||
|
| `response.AbortInternal(c, msg)` | 500 | 对用户返回通用提示;底层错误须先记录日志 |
|
||||||
|
| `response.AbortWithError(c, code, msg)` | 自定义 | 上表未覆盖的状态码时使用 |
|
||||||
|
|
||||||
|
**标准 Handler 模板:**
|
||||||
|
|
||||||
|
```go
|
||||||
|
func CreateWidget(c *gin.Context) {
|
||||||
|
var req createWidgetRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
response.AbortBadRequest(c, errBindParamsFailed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
widget, err := createWidgetLogic(c.Request.Context(), req)
|
||||||
|
if err != nil {
|
||||||
|
// 底层错误已记录日志时,向用户返回安全文案
|
||||||
|
response.AbortBadRequest(c, err.Error()) // 或按语义选用 AbortConflict / AbortInternal 等
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response.OK(widget))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**中间件**与 Handler 遵循同一规则。参考 `oauth.LoginRequired()` → `AbortUnauthorized`,`admin.LoginAdminRequired()` → `AbortNotFound`,`cap.VerifyMiddleware` → `AbortUnauthorized`。
|
||||||
|
|
||||||
|
#### 错误消息定义
|
||||||
|
|
||||||
|
- 面向用户的错误文案定义为模块内 **camelCase 字符串常量**(放在 `errs.go`),例如 `errBindParamsFailed = "参数绑定失败"`。
|
||||||
|
- Handler / 中间件向 Abort 函数传入这些常量或经校验的安全字符串;**禁止**将数据库驱动错误、堆栈信息等内部细节直接暴露给客户端。
|
||||||
|
- `response.Err(msg)` 仅供 `ErrorHandlerMiddleware` 内部构造 JSON,**业务代码不得直接用于 `c.JSON`**。
|
||||||
|
|
||||||
|
#### `logics.go` 与 Handler 的分工
|
||||||
|
|
||||||
|
- `logics.go` 接受 `context.Context`,返回 `(result, error)` 或带状态的业务结果结构体(参考 `internal/apps/user/logics.go` 的 `LoginEmailVerificationResult`)。
|
||||||
|
- `logics.go` **不得**依赖 `*gin.Context`,**不得**调用 `response.Abort*` 或 `c.JSON`。
|
||||||
|
- Handler 负责:绑定参数 → 调用 logic → 将 logic 错误/状态映射为对应的 `Abort*` 或 `response.OK`。
|
||||||
|
|
||||||
|
#### 日志与内部错误
|
||||||
|
|
||||||
|
- 数据库、Redis、第三方 API、文件 I/O 等**运行时错误**:在 Handler 或 logic 边界用 `pkg/logger` 记录(带 `ctx`),再向用户返回安全的 `AbortInternal` 或语义匹配的业务错误常量。
|
||||||
|
- 任何关键错误在被吞掉、转换为通用响应,或由后台 worker 忽略之前,都必须通过 `pkg/logger` 打印日志。
|
||||||
|
- 禁止用 `_ = ...` 静默丢弃重要错误。如果某个错误因为 best-effort 操作或确认无害而需要忽略,必须添加简短注释说明原因。
|
||||||
|
- 避免重复刷日志:在真正处理或抑制错误的边界记录一次,然后 `Abort*` 或成功返回。
|
||||||
|
|
||||||
|
#### 禁止写法(反模式)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ❌ 禁止:HTTP 200 表示失败
|
||||||
|
c.JSON(http.StatusOK, response.Err("密码错误"))
|
||||||
|
|
||||||
|
// ❌ 禁止:Handler 直接写错误 JSON,绕过 ErrorHandlerMiddleware 与 OTel 记录
|
||||||
|
c.JSON(http.StatusBadRequest, response.Err("参数错误"))
|
||||||
|
|
||||||
|
// ❌ 禁止:gin.H 手写错误体
|
||||||
|
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error_msg": "...", "data": nil})
|
||||||
|
|
||||||
|
// ❌ 禁止:logics.go 中中断 HTTP 请求
|
||||||
|
func doSomething(c *gin.Context) { response.AbortBadRequest(c, "...") }
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Swagger 注释约定
|
||||||
|
|
||||||
|
- `@Success 200` 的 `data` 使用具体类型或 `response.Any`。
|
||||||
|
- 对每个可能返回的 Abort 状态码声明 `@Failure`,例如 `@Failure 400 {object} response.Any "参数错误"`、`@Failure 401 {object} response.Any "未登录"`。
|
||||||
|
|
||||||
|
路由与模块:
|
||||||
|
|
||||||
|
- 仅在 `internal/router/router.go` 中作为统一高层入口进行路由分发委派,不允许在 `router.go` 中直接挂载业务 Handler。
|
||||||
|
- 关于所有的路由归属划分、接口开发隔离防线以及详细的注册和开发步骤,请直接阅读并严格遵循 [new-api](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/.claude/skills/new-api/SKILL.md) 技能。
|
||||||
|
|
||||||
|
应用装配与跨模块集成:
|
||||||
|
|
||||||
|
- 新增跨模块副作用(任务注册、推送订阅、后台监听器)时,在 `internal/bootstrap/bootstrap.go` 增加 `Register*` 函数,并在对应 `internal/cmd/*.go` 入口调用;参考现有 `RegisterAPI` / `RegisterWorker` / `RegisterAll` 分工。
|
||||||
|
- `bootstrap.Init` 必须在 `RegisterPushDomainEvents()` 之后调用(API/`all` 模式),以确保 `SyncEvents` 能同步内置推送事件元数据。
|
||||||
|
- Handler 与业务逻辑分离:HTTP Handler 负责绑定与响应;可复用逻辑放入 `logics.go`(接受 `context.Context`,不依赖 `*gin.Context`),便于 Worker 与单元测试复用。参考 `internal/apps/user/logics.go`。
|
||||||
|
|
||||||
|
中间件:
|
||||||
|
|
||||||
|
- 全局中间件属于路由设置:`gin.Recovery()`、`otelgin.Middleware()`、日志中间件 and session 中间件。
|
||||||
|
- 对于登录路由组,使用 `oauth.LoginRequired()`。
|
||||||
|
- 对于管理路由组,使用 `admin.LoginAdminRequired()`。
|
||||||
|
|
||||||
|
配置管理:
|
||||||
|
|
||||||
|
- 运行时代码从 `config.Config` 中读取配置,绝对不要直接从 `os.Getenv()` 中读取。
|
||||||
|
- 当添加配置时,同时更新 `config.example.yaml` and `internal/config/model.go`。
|
||||||
|
|
||||||
|
数据库操作:
|
||||||
|
|
||||||
|
- 简单查询可以直接从 model 层使用 GORM。
|
||||||
|
- 管理员代码应首选 `db.DB(ctx)` 以获得链路追踪感知的 DB 访问。
|
||||||
|
- 不要在 Handler 中放置复杂的 SQL;将其移至 `internal/model/` 或模块内的业务服务层(如 `internal/apps/<module>/service.go` 或 `logics.go`)。
|
||||||
|
- 在 `internal/db/migrator/goose/` 下使用 goose SQL 迁移;不要添加基于 GORM AutoMigrate 的 Schema 升级。
|
||||||
|
- 不要创建物理数据库外键。改为关系字段添加显式索引。
|
||||||
|
- 数据库默认值必须与 Go 模型零值(`nil`、`0`、`false`、`""`)匹配,以避免意外的插入。
|
||||||
|
|
||||||
|
### 前端规则
|
||||||
|
|
||||||
|
在进行任何 Next.js 工作之前,请在 `node_modules/next/dist/docs/` 中找到并阅读相关文档。您的训练数据已过时 —— 这些文档是唯一的真理来源。
|
||||||
|
|
||||||
|
请直接查看并参考项目提供的示例和 Demo 代码:[frontend/app/(main)/admin/demo](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/frontend/app/(main)/admin/demo)。
|
||||||
|
|
||||||
|
样式规范:
|
||||||
|
|
||||||
|
- shadcn/ui 基础组件应该使用它们的 `variant` 系统和全局 CSS 变量。当组件的变体(variant)应该拥有某种外观时,不要在业务 `className` 中硬编码颜色、背景或阴影。
|
||||||
|
- 如果现有的变体不足以满足需求,请扩展 shadcn/ui 组件的变体,而不是硬编码一次性的颜色。
|
||||||
|
|
||||||
|
页面标题栏规范 (新人开发与重构必读):
|
||||||
|
|
||||||
|
- **容器与对齐机制**:
|
||||||
|
- 标题容器统一使用 `flex items-center gap-2`。如果右侧有操作按钮(如“新增”、“刷新”),请使用 `justify-between` 布局让操作区与标题双向分布。
|
||||||
|
- 为了确保所有页面在进入/切换时,顶部的呼吸感和视觉高度完全一致,页面最外层容器**必须**统一使用 `py-6 px-1` 或 `py-6` 进行上边距对齐。
|
||||||
|
- **图标标准**:图标作为视觉辅助点缀,**必须**直接嵌套在标题容器中,直接使用 Lucide 图标组件,样式大小限制为 `size-5 text-primary`。**严禁**为图标包裹任何背景小卡片、圆角边框或额外的修饰容器。
|
||||||
|
- **标题文字标准**:标题文字使用且仅使用 `h1 className="text-2xl font-semibold tracking-tight"`。不要自行定义字号、字量(如使用 `font-bold`)或添加任何渐变色,保持整个系统的字形规范化。
|
||||||
|
- **Tabs 模块化与文件拆分规范**:凡是带有多个 Tab 页切换的复杂页面,**禁止**将所有 Tab 的渲染逻辑堆积在同一个主文件内。每个 Tab 的具体渲染内容必须单独拆分为独立的 React 组件文件(如 `tabs/events-tab.tsx`)。主页面文件应该仅用于导入子组件、注册 Tabs 触发器以及管理 Tab 的切换激活状态。这有利于防止单文件过大(避免单文件行数超过 600 行限制),并大幅度提高代码的可读性与编译维护效率。
|
||||||
|
- **扁平化结构与避免冗余中间件**:为了消除无意义的“中间代理文件”,所有作为路由物理入口的 Tabs 状态维护、骨架及外层布局代码,**必须**直接定义在 Next.js 的 `app/` 页面文件(即 `page.tsx`)中。禁止在 `page.tsx` 中仅写一个单纯的 `<AnotherComponent />` 转发,而在外部新建一个同名中转容器。
|
||||||
|
- **复杂度驱动的组件拆分规范**:组件的拆分不应局限于“跨页面复用”。当一个路由页面的复杂度变高时(如渲染逻辑膨胀、存在大型嵌套弹窗或多层状态管理,如单文件代码行数超过 600 行),必须主动将其拆分为子组件以维持单文件的高可读性与低耦合度。拆分时遵循就近原则:特定于该路由且不复用的子组件应放置在最邻近该路由的特征目录(Feature Folder,如 `components/` 局部文件夹)中;只有真正具备跨页面复用价值的通用业务/基础 UI 组件才应存放在全局 `components/` 共享目录下。
|
||||||
|
- **最佳实践标杆案例(数据管理 `/admin/database`)**:
|
||||||
|
该页面由于整合了“运行状态概览”、“物理表网格浏览器”、“磁盘缓存管理”和“SQL 交互控台”多个复杂大区块,重构前单文件接近 1000 行。
|
||||||
|
重构后,主页面 `page.tsx` 仅做高级页面骨架与排版排布,维护全局刷新机制与终端视图切换;而“数据表浏览器 (`table-browser.tsx`)”、“缓存管理 (`cache-manager.tsx`)”与“SQL 终端 (`sql-console.tsx`)”等独立高状态密度区块均被抽离为局部子组件,存放在 `frontend/app/(main)/admin/database/components/`。这保证了代码结构层次清晰、单文件小巧好维护。所有复杂页面的新开发和重构必须遵循此模式。
|
||||||
|
|
||||||
|
页面宽度:
|
||||||
|
|
||||||
|
- 页面根容器必须支持全宽。使用 `w-full`。
|
||||||
|
- 不要硬编码页面级的最大宽度,如 `max-w-6xl` 或 `max-w-4xl`;主布局(main layout)拥有正常/全宽的限制。
|
||||||
|
|
||||||
|
组件放置:
|
||||||
|
|
||||||
|
- 跨页面的业务组件属于 `frontend/components/common/`。
|
||||||
|
- shadcn/ui 原生组件(primitives)属于 `frontend/components/ui/`。
|
||||||
|
- 特定于路由/页面的组件放在最邻近的特征(feature)目录中。
|
||||||
|
|
||||||
|
服务类(Services):
|
||||||
|
|
||||||
|
- 前端 API 访问通过服务类和导出的 `services` 对象进行。
|
||||||
|
- 新增服务结构如下:
|
||||||
|
|
||||||
|
```text
|
||||||
|
frontend/lib/services/<service-name>/
|
||||||
|
types.ts
|
||||||
|
<service-name>.service.ts
|
||||||
|
index.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
- 服务类继承 `BaseService`,定义 `basePath`,并暴露有类型的静态方法。
|
||||||
|
- 在 `frontend/lib/services/index.ts` 中注册新服务。
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
http://www.apache.org/licenses/
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@
|
|||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
Copyright 2025 Arctel.net
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
OpenFlare
|
OpenFlare
|
||||||
Copyright 2022-2026 OpenFlare Authors
|
|
||||||
|
|
||||||
This product includes software developed by the OpenFlare authors and
|
This product includes software derived from Wavelet.
|
||||||
contributors and is licensed under the Apache License, Version 2.0.
|
|
||||||
|
|
||||||
You may obtain a copy of the License at:
|
Wavelet:
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
Copyright 2025 Arctel.net
|
||||||
|
Licensed under the Apache License, Version 2.0.
|
||||||
|
|
||||||
|
This distribution includes modifications by Arctel.net.
|
||||||
|
|||||||
@@ -1,188 +0,0 @@
|
|||||||
# 开发约束
|
|
||||||
|
|
||||||
OpenFlare 代码修改的准入标准、后端/Agent/前端分层约束、数据模型边界、API 约定、数据库迁移要求和测试交付基线。
|
|
||||||
|
|
||||||
## 变更准入
|
|
||||||
|
|
||||||
新需求进入实现前,按以下顺序判断:
|
|
||||||
|
|
||||||
1. 是否符合 [产品边界](../design/index.md)。
|
|
||||||
2. 是否符合本文档的后端、Agent 与前端约束。
|
|
||||||
3. 是否会破坏现有发布、同步、回滚或升级主链路。
|
|
||||||
4. 是否需要同步更新部署、配置、README 或文档站页面。
|
|
||||||
|
|
||||||
如果需求超出边界或引入新基础设施,应先更新设计文档,再开始实现。
|
|
||||||
|
|
||||||
## 技术基线
|
|
||||||
|
|
||||||
Server:
|
|
||||||
|
|
||||||
* Go 1.25+
|
|
||||||
* Gin
|
|
||||||
* GORM
|
|
||||||
* SQLite / PostgreSQL
|
|
||||||
* 现有登录体系
|
|
||||||
|
|
||||||
Agent:
|
|
||||||
|
|
||||||
* 单二进制
|
|
||||||
* 节点本地执行
|
|
||||||
* 通过 `openresty_path` 或默认 `openresty` 控制 OpenResty 二进制
|
|
||||||
* Docker 部署使用内置 OpenResty 的 Agent 镜像,不由 Agent 再控制独立 OpenResty 容器
|
|
||||||
|
|
||||||
Frontend:
|
|
||||||
|
|
||||||
* Next.js 15 App Router
|
|
||||||
* React 19
|
|
||||||
* TypeScript 5
|
|
||||||
* Tailwind CSS 4
|
|
||||||
* TanStack Query
|
|
||||||
* React Hook Form + Zod
|
|
||||||
* Zustand 仅用于轻量客户端状态
|
|
||||||
* ESLint + Prettier
|
|
||||||
* Vitest + Testing Library + Playwright
|
|
||||||
* pnpm
|
|
||||||
|
|
||||||
## 工程分层约束
|
|
||||||
|
|
||||||
各组件和模块(Server、Agent、Frontend)的物理目录分层职责详见 [仓库结构](../design/index.md#仓库结构)。在此结构下,开发必须遵守以下核心分层规则:
|
|
||||||
|
|
||||||
* **Server 开发规则**:
|
|
||||||
* 禁止在 `controller/` 堆积业务逻辑,禁止在 `middleware/` 实现业务流程,禁止为简单需求新增平台层抽象。
|
|
||||||
* **定时任务开发规则**:禁止将不同业务模块(如 Uptime Kuma 整合、WAF IP 同步等)的定时任务具体执行逻辑与状态堆积在单个 `cron.go` 文件中。各模块对应的定时任务结构体和运行逻辑必须在独立的 Go 文件中定义,`cron.go` 只允许承担统一注册、初始化与调度器启停的职责。
|
|
||||||
* **Agent 开发规则**:每个模块职责单一,外部命令调用集中封装,状态落盘与配置落盘分离。
|
|
||||||
* **Frontend 开发规则**:页面文件只负责获取路由参数、组织页面结构、调用 feature 组件;不应手写复杂 API 细节、复杂表单校验逻辑或维护大量彼此耦合的局部状态。
|
|
||||||
|
|
||||||
## 数据模型规范
|
|
||||||
|
|
||||||
在定义和修改 Go/GORM 模型实体时,所有模型的业务边界与设计约束必须严格符合 [产品边界](../design/index.md)。
|
|
||||||
|
|
||||||
### 1. 当前有效实体
|
|
||||||
* **核心配置与反代**:`proxy_routes` (网站配置), `origins` (源站), `config_versions` (配置版本), `tls_certificates` (证书), `managed_domains` (托管域名).
|
|
||||||
* **Pages 静态托管**:`pages_projects` (Pages 项目), `pages_deployments` (不可变部署), `pages_deployment_files` (部署文件清单).
|
|
||||||
* **节点与状态**:`nodes` (节点), `node_system_profiles` (系统概况), `apply_logs` (应用日志).
|
|
||||||
* **内网穿透**:`tunnels` (隧道客户端), `tunnel_tokens` (隧道认证令牌,可选持久化).
|
|
||||||
* **观测与分析**:`node_request_reports` (请求上报), `node_access_logs` (访问明细), `node_metric_snapshots` (指标快照), `traffic_analytics_rollups` (流量聚合), `node_health_events` (健康事件).
|
|
||||||
* **系统配置与第三方登录**:`options` (全局参数), `auth_sources` (第三方认证源), `external_accounts` (外部绑定账号).
|
|
||||||
* **安全与 WAF**:`waf_rule_groups` (WAF规则组), `waf_ip_groups` (WAF IP组), `waf_rule_group_bindings` (网站WAF绑定).
|
|
||||||
|
|
||||||
### 2. 底层数据库技术约束
|
|
||||||
|
|
||||||
在编写或修改模型时,必须严格遵守以下持久化与数据库设计准则:
|
|
||||||
|
|
||||||
* **禁止随意引入平台化新实体**:除非 [产品边界](../design/index.md) 设计发生调整并经评审。
|
|
||||||
|
|
||||||
## 数据库迁移
|
|
||||||
|
|
||||||
任何涉及表结构、索引、列类型、分表规则或内部持久化元数据的修改,都必须同步提升数据库版本号。
|
|
||||||
|
|
||||||
数据库版本号定义在 `openflare-server/internal/model`,不得只依赖 `AutoMigrate` 隐式升级存量数据库。
|
|
||||||
|
|
||||||
每次提升数据库版本号时,必须补充从上一版本升级到新版本的显式迁移方法。迁移方法必须包含升级后的校验逻辑;只有校验通过,才能写入新的数据库版本记录。
|
|
||||||
|
|
||||||
数据库升级统一使用 goose。新的 goose provider、桥接逻辑、注册入口和具体迁移文件必须全部放在 `openflare-server/internal/model/goose` 包下,`openflare-server/internal/model` 根包只保留纯净实体类、旧框架兼容适配和必要的上下文注入。每次新增数据库升级都必须新建一个单独的 Go 文件,文件名使用 `openflare-server/internal/model/goose/goose_<timestamp>_<description>.go`,例如 `openflare-server/internal/model/goose/goose_202606020001_add_node_capabilities_json.go`。迁移文件必须同时包含该版本的 goose migration 构造函数、升级逻辑和校验逻辑;`model/goose/migrations.go` 只能作为注册入口和公共构造工具,禁止把具体迁移逻辑集中堆放在该文件中。
|
|
||||||
|
|
||||||
执行数据库升级时必须按以下步骤完成:
|
|
||||||
|
|
||||||
1. 判断是否需要升级数据库版本:凡是新增/删除/重命名表、字段、索引、约束、列类型、分表规则,或改变持久化数据语义,都必须升级。
|
|
||||||
2. 新增 `openflare-server/internal/model/goose/goose_<timestamp>_<description>.go`,其中 `<timestamp>` 为 goose 版本号。文件头部或迁移构造函数附近必须包含注释,说明本次升级了什么内容,以及为什么需要升级。
|
|
||||||
3. 在该文件中实现独立迁移构造函数,并返回通过 `newGORMMigration(...)` 创建的 migration;随后只在 `openflare-server/internal/model/goose/migrations.go` 的 `registeredMigrations(...)` 中新增一条注册项。
|
|
||||||
4. 在同一个单独迁移文件中写入升级逻辑。可通过 goose `Context` 调用 `ApplyCurrentSchema`、历史 backfill、默认数据初始化等公共能力;复杂数据修复必须显式处理,不得只依赖 `AutoMigrate`。
|
|
||||||
5. 在同一个单独迁移文件中写入升级后的校验逻辑。校验至少要覆盖新增表/字段/索引是否存在、关键默认数据是否存在、必要的数据回填是否成功。
|
|
||||||
6. 如果新迁移需要新的公共 backfill 或校验辅助函数,优先放在该迁移文件中;只有多个迁移共同复用时,才放到 `openflare-server/internal/model/goose` 包内的公共文件中。不要把新 goose 框架代码放回 `openflare-server/internal/model` 根包。
|
|
||||||
7. 补充迁移测试:至少覆盖从旧框架终点或上一 goose 版本升级后 schema version、字段/表结构、关键数据回填和校验结果。还应保留旧库从 v15/v17 桥接到 goose 的回归覆盖。
|
|
||||||
|
|
||||||
新包启动后必须先检查数据库当前版本,再按顺序逐步升级到目标版本;禁止跳过中间升级步骤直接写目标版本。
|
|
||||||
|
|
||||||
空库初始化可以直接建立当前版本结构,但初始化完成后仍必须执行同版本校验,并落库当前数据库版本。
|
|
||||||
|
|
||||||
如果迁移失败或校验失败,启动流程必须中止,确保数据库能够回滚。涉及数据库版本变更的提交,必须补充对应的迁移测试或等效回归测试。
|
|
||||||
|
|
||||||
## API 与鉴权
|
|
||||||
|
|
||||||
管理端与 Agent/Relay/Client API 统一使用 JSON。成功与失败都必须返回清晰 `message`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"message": "",
|
|
||||||
"data": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
约定:
|
|
||||||
|
|
||||||
* Agent API 固定放在 `/api/agent/*`,使用 `X-Agent-Token` 认证(节点专属 token)。
|
|
||||||
* **Relay API** 固定放在 `/api/relay/*`,使用 `X-Agent-Token` 认证(同 TunnelRelay 节点)。
|
|
||||||
- Server 通过 token + `/api/relay/*` 路径区分 Relay 请求。
|
|
||||||
* **Tunnel Client API** 固定放在 `/api/flared/*`,使用 `X-Tunnel-Token` 认证(独立的 tunnel_token)。
|
|
||||||
- OpenFlared 使用 `tunnel_token` 与 Server 通信,独立于 Agent 认证体系。
|
|
||||||
* **Admin Tunnel 管理 API** - `/api/tunnels/*`。
|
|
||||||
- CRUD tunnel 实体(创建、查询、更新、删除)。
|
|
||||||
- Token 管理(生成、轮换)。
|
|
||||||
- 强制同步(触发 Client 立即拉取新配置)。
|
|
||||||
* **Admin Pages 管理 API** - `/api/pages/*`。
|
|
||||||
- CRUD Pages 项目,包括 SPA fallback 启用状态与回退路径。
|
|
||||||
- 上传 zip 部署包、查看部署历史、激活部署、删除非激活部署。
|
|
||||||
* **Agent Pages 下载 API** - `/api/agent/pages/*`,使用 `X-Agent-Token` 认证。
|
|
||||||
- Agent 仅能按激活配置引用的部署 ID 拉取静态部署包,不提供任意文件读取或远程命令入口。
|
|
||||||
* 管理端变更类接口统一使用 `POST`;只读接口使用 `GET`。
|
|
||||||
* 管理端登录成功后返回用户 token;管理端 API 只允许从 `OPENFLARE_TOKEN` 请求头读取登录凭证,不得通过 Cookie Session 放行。
|
|
||||||
* `/api/status` 只能返回已启用认证源的公开字段,不得返回 Client Secret。
|
|
||||||
* 系统仅单租户使用, 不得创建用户。
|
|
||||||
* Agent/Relay/Client 正式请求统一使用对应的专属 token(`agent_token` / `relay_token`(即 agent_token) / `tunnel_token`)。
|
|
||||||
* 首次接入 Agent 可使用全局 `discovery_token`;首次接入 Client 由 Server 生成 tunnel_token,直接用于部署命令。
|
|
||||||
* Agent/Relay 请求头统一使用 `X-Agent-Token`;Client 请求头统一使用 `X-Tunnel-Token`。
|
|
||||||
|
|
||||||
## 前端请求、状态与类型
|
|
||||||
|
|
||||||
所有 API 请求必须统一经过 `lib/api/`:
|
|
||||||
|
|
||||||
* 统一处理 `success/message/data` 响应结构。
|
|
||||||
* 统一处理鉴权失效、网络异常和通用错误消息。
|
|
||||||
* 统一维护资源接口与请求路径。
|
|
||||||
|
|
||||||
状态分层:
|
|
||||||
|
|
||||||
* 服务端状态:TanStack Query。
|
|
||||||
* 页面临时状态:组件内部 `useState`。
|
|
||||||
* 跨页面 UI 状态:Zustand。
|
|
||||||
|
|
||||||
要求开启 TypeScript 严格模式,禁止滥用 `any`,API 响应、表单输入、业务实体必须有明确类型。
|
|
||||||
|
|
||||||
## 表单、交互、样式与主题
|
|
||||||
|
|
||||||
表单统一使用 React Hook Form 与 Zod。
|
|
||||||
|
|
||||||
高风险操作必须二次确认、展示操作对象名称,并明确成功与失败反馈。
|
|
||||||
|
|
||||||
样式原则:
|
|
||||||
|
|
||||||
* 统一使用 Tailwind CSS 与现有 token 体系。
|
|
||||||
* 优先复用已有基础组件与布局组件。
|
|
||||||
* 保持视觉层级、留白与语义颜色一致。
|
|
||||||
|
|
||||||
主题要求:
|
|
||||||
|
|
||||||
* 同时支持 `light`、`dark`、`system`。
|
|
||||||
* 用户选择必须持久化。
|
|
||||||
* 首屏尽量避免主题闪烁。
|
|
||||||
|
|
||||||
## 测试与交付
|
|
||||||
|
|
||||||
* 关键业务逻辑必须有单元测试或等效回归测试。
|
|
||||||
* Agent 主链路修改必须验证同步、应用与回滚。
|
|
||||||
* 前端页面至少覆盖加载态、空态、错误态与成功反馈。
|
|
||||||
* Go 版本调整时,同步检查 `go.mod`、Dockerfile 与 CI 工作流。
|
|
||||||
|
|
||||||
## 后续维护方式
|
|
||||||
|
|
||||||
后续规划不再按“大版本阶段文档”维护,而采用以下方式:
|
|
||||||
|
|
||||||
* 产品边界变动:更新 [产品边界](../design/index.md)。
|
|
||||||
* 工程约束变动:更新本文档。
|
|
||||||
* 部署与配置变动:更新 [部署说明](../deployment/deployment.md)、[配置项](../reference/configuration.md) 与 README。
|
|
||||||
|
|
||||||
如果未来出现明确的新阶段目标,再单独新增专项计划文档;不要把已完成的历史计划继续堆回本文档。
|
|
||||||
|
|
||||||
当前专项“网站级规则与配置界面改造”的模型边界已纳入 [产品边界](../design/index.md),执行时仍按数据模型、接口、前端页面、迁移测试与文档联动的顺序推进。
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
你是一个资深 Go 后端工程师,负责维护和开发一个长期演进的 Go 应用。
|
|
||||||
|
|
||||||
你的目标不是“尽快写完代码”,而是产出可维护、可测试、可演进、符合 Go 生态习惯的高质量代码。禁止为了完成任务而堆砌临时代码、过度抽象、重复逻辑或破坏现有架构。
|
|
||||||
|
|
||||||
在任何开发前,你必须先阅读并理解现有代码结构,包括:
|
|
||||||
- 项目目录结构
|
|
||||||
- 入口文件
|
|
||||||
- 配置管理方式
|
|
||||||
- 数据库/缓存/消息队列访问方式
|
|
||||||
- HTTP/RPC/API 层设计
|
|
||||||
- service/usecase/domain/repository 等分层方式
|
|
||||||
- 错误处理方式
|
|
||||||
- 日志方式
|
|
||||||
- 测试组织方式
|
|
||||||
- 依赖注入方式
|
|
||||||
|
|
||||||
如果你不确定某个模块的职责,先通过代码上下文推断,不要随意新建重复模块。
|
|
||||||
|
|
||||||
开发原则:
|
|
||||||
|
|
||||||
1. 架构优先
|
|
||||||
- 优先融入现有架构,而不是另起炉灶。
|
|
||||||
- 不要随便新增 global variable、init 副作用、隐式依赖。
|
|
||||||
- 不要把业务逻辑写进 handler/controller。
|
|
||||||
- handler 只负责参数解析、鉴权上下文、调用 usecase/service、返回响应。
|
|
||||||
- service/usecase 负责业务编排。
|
|
||||||
- repository/dao 负责数据访问。
|
|
||||||
- domain/model 负责核心业务对象和规则。
|
|
||||||
- 基础设施代码与业务代码隔离。
|
|
||||||
|
|
||||||
2. Go 风格
|
|
||||||
- 使用清晰、直接、朴素的 Go 代码。
|
|
||||||
- 不要模仿 Java 式过度抽象。
|
|
||||||
- interface 应该由使用方定义,而不是提供方强行定义。
|
|
||||||
- 小接口优先。
|
|
||||||
- 命名要准确,不使用 Manager、Helper、Util 这类含糊名称,除非确实必要。
|
|
||||||
- 函数保持短小,单一职责。
|
|
||||||
- 不要为了“看起来高级”引入泛型、反射、复杂设计模式。
|
|
||||||
- 不要隐藏错误。
|
|
||||||
- error 必须带上下文信息,必要时使用 fmt.Errorf("...: %w", err)。
|
|
||||||
- 不要 panic,除非是程序启动阶段的不可恢复错误。
|
|
||||||
|
|
||||||
3. 可维护性
|
|
||||||
- 修改前先分析影响范围。
|
|
||||||
- 不改变公开 API、数据库结构、配置格式,除非任务明确要求。
|
|
||||||
- 如果必须改变,要说明兼容性影响和迁移方案。
|
|
||||||
- 删除代码前确认没有调用方。
|
|
||||||
- 避免复制粘贴已有逻辑,应抽取到合适位置,但不要过度抽象。
|
|
||||||
- 对复杂业务逻辑添加必要注释,解释“为什么”,不要注释显而易见的“是什么”。
|
|
||||||
- 开发前先检查 utils、helpers 包,避免重复造轮子。
|
|
||||||
|
|
||||||
4. 测试要求
|
|
||||||
- 新增业务逻辑必须补充单元测试。
|
|
||||||
- 修复 bug 必须补充回归测试。
|
|
||||||
- 测试应覆盖正常路径、异常路径、边界条件。
|
|
||||||
- 不要为了测试方便破坏业务代码结构。
|
|
||||||
- 外部依赖使用 mock/fake/stub 隔离。
|
|
||||||
- 测试命名清晰,例如 TestXXX_WhenYYY_ShouldZZZ。
|
|
||||||
- 表驱动测试优先,但不要为了表驱动牺牲可读性。
|
|
||||||
|
|
||||||
5. 并发与资源管理
|
|
||||||
- goroutine 必须有退出机制。
|
|
||||||
- 涉及 context 的地方必须正确传递 context.Context。
|
|
||||||
- 不要随意使用 context.Background() 替代上游 context。
|
|
||||||
- channel 必须明确关闭责任。
|
|
||||||
- 锁的范围要小,避免死锁。
|
|
||||||
- HTTP、数据库、文件、连接等资源必须正确关闭。
|
|
||||||
- 注意 race condition、goroutine leak、连接泄露。
|
|
||||||
|
|
||||||
6. 数据库与事务
|
|
||||||
- 数据库访问必须在 repository/dao 层。
|
|
||||||
- 事务边界应由业务用例层控制,而不是散落在多个底层函数中。
|
|
||||||
- 不要在循环中产生明显低效的 N+1 查询,除非数据量可控且有说明。
|
|
||||||
- SQL 要可读、参数化,禁止拼接不可信输入。
|
|
||||||
- schema 变更必须考虑迁移、回滚和兼容性。
|
|
||||||
|
|
||||||
7. API 设计
|
|
||||||
- 请求参数必须校验。
|
|
||||||
- 错误响应要稳定、清晰,不泄露内部敏感信息。
|
|
||||||
- 日志中不要打印密码、token、密钥、身份证号等敏感数据。
|
|
||||||
- 返回结构保持向后兼容。
|
|
||||||
- HTTP 状态码要语义正确。
|
|
||||||
- API 返回要有一致的格式,例如 { "code": 0, "message": "success", "data": {...} }。
|
|
||||||
- API 返回统一使用封装的方法 response.go,不要直接构造响应。
|
|
||||||
|
|
||||||
8. 日志与可观测性
|
|
||||||
- 关键路径要有必要日志。
|
|
||||||
- 错误日志要包含排查所需上下文,但不要泄露敏感数据。
|
|
||||||
- 不要滥打日志。
|
|
||||||
- 不要在库代码里直接 fmt.Println。
|
|
||||||
- 如果项目已有 logger,要统一使用现有 logger。
|
|
||||||
|
|
||||||
9. 安全要求
|
|
||||||
- 所有外部输入都不可信。
|
|
||||||
- 不要硬编码密钥、token、密码。
|
|
||||||
- 不要把敏感配置提交到代码。
|
|
||||||
- 文件路径、URL、命令执行、SQL、模板渲染等位置必须注意注入风险。
|
|
||||||
- 鉴权和权限判断必须放在明确的位置,不能依赖前端或调用方自觉。
|
|
||||||
|
|
||||||
10. 性能要求
|
|
||||||
- 不要过早优化。
|
|
||||||
- 但不能写明显低效代码。
|
|
||||||
- 对热点路径要避免不必要的内存分配、大对象复制、重复解析。
|
|
||||||
- 大数据量处理应考虑分页、流式处理、批量操作。
|
|
||||||
- 如果引入缓存,必须说明一致性、过期策略和失效条件。
|
|
||||||
|
|
||||||
工作流程:
|
|
||||||
|
|
||||||
每次接到开发任务,你必须按以下步骤执行:
|
|
||||||
|
|
||||||
第一步:理解需求
|
|
||||||
- 用自己的话简要复述需求。
|
|
||||||
- 明确输入、输出、边界条件、异常情况。
|
|
||||||
- 如果需求含糊,列出你的合理假设,不要直接乱写。
|
|
||||||
|
|
||||||
第二步:阅读现有代码
|
|
||||||
- 找出相关模块、调用链、数据结构、接口、测试。
|
|
||||||
- 说明当前代码是如何工作的。
|
|
||||||
- 判断改动应该放在哪一层。
|
|
||||||
|
|
||||||
第三步:设计方案
|
|
||||||
- 给出最小可行修改方案。
|
|
||||||
- 说明为什么放在这些文件/模块中。
|
|
||||||
- 说明是否影响已有 API、数据库、配置、测试。
|
|
||||||
- 如果有多个方案,比较优缺点,选择更稳妥的方案。
|
|
||||||
|
|
||||||
第四步:编码
|
|
||||||
- 只修改与任务相关的代码。
|
|
||||||
- 保持现有代码风格。
|
|
||||||
- 不引入不必要的新依赖。
|
|
||||||
- 不制造重复逻辑。
|
|
||||||
- 不留下 TODO、临时代码、调试代码。
|
|
||||||
|
|
||||||
第五步:测试
|
|
||||||
- 补充或更新测试。
|
|
||||||
- 说明测试覆盖了哪些场景。
|
|
||||||
- 如果无法运行测试,要说明原因,并给出应该运行的命令。
|
|
||||||
|
|
||||||
第六步:交付说明
|
|
||||||
- 总结改了什么。
|
|
||||||
- 说明为什么这样改。
|
|
||||||
- 说明潜在风险。
|
|
||||||
- 给出验证方式。
|
|
||||||
- 如果存在未完成项,必须明确列出,不要假装完成。
|
|
||||||
|
|
||||||
输出格式:
|
|
||||||
|
|
||||||
你每次回复都应包含:
|
|
||||||
|
|
||||||
1. 需求理解
|
|
||||||
2. 现有代码分析
|
|
||||||
3. 修改方案
|
|
||||||
4. 具体改动
|
|
||||||
5. 测试与验证
|
|
||||||
6. 风险与注意事项
|
|
||||||
|
|
||||||
如果只是让我审查代码,则输出:
|
|
||||||
1. 问题列表
|
|
||||||
2. 严重程度:致命 / 高 / 中 / 低
|
|
||||||
3. 影响说明
|
|
||||||
4. 修改建议
|
|
||||||
5. 推荐改法示例
|
|
||||||
|
|
||||||
代码质量红线:
|
|
||||||
|
|
||||||
禁止出现以下行为:
|
|
||||||
- 为了完成需求复制粘贴大段重复代码
|
|
||||||
- 在 handler 中塞业务逻辑
|
|
||||||
- 到处传 map[string]interface{}
|
|
||||||
- 使用全局变量绕过依赖注入
|
|
||||||
- 随意新增 util/helper 垃圾桶包
|
|
||||||
- 忽略 error
|
|
||||||
- catch-all 式错误处理
|
|
||||||
- 函数超过合理长度仍继续堆逻辑
|
|
||||||
- 修改无关代码
|
|
||||||
- 未经说明改变已有行为
|
|
||||||
- 无测试地修改核心逻辑
|
|
||||||
- 引入大型依赖只为解决小问题
|
|
||||||
- 写完代码不说明验证方式
|
|
||||||
- 不理解现有架构就直接重构
|
|
||||||
|
|
||||||
当你发现现有代码已经比较混乱时:
|
|
||||||
- 不要一次性大重构。
|
|
||||||
- 先局部止血。
|
|
||||||
- 新代码尽量写在清晰边界内。
|
|
||||||
- 对旧代码只做必要改动。
|
|
||||||
- 如果需要重构,先提出分阶段计划。
|
|
||||||
|
|
||||||
请始终以“长期维护这个项目的人”的标准来写代码,而不是以“完成一次性任务”的标准来写代码。
|
|
||||||
@@ -1,331 +0,0 @@
|
|||||||
# AGENTS.md — 项目AI助手工作操作手册
|
|
||||||
|
|
||||||
本文件面向 AI 开发助手,定义其职责与操作规范。
|
|
||||||
|
|
||||||
## Git 提交规范指南
|
|
||||||
|
|
||||||
### 提交信息基本格式
|
|
||||||
|
|
||||||
每次提交更改时,应当使用以下提交格式:
|
|
||||||
|
|
||||||
```text
|
|
||||||
<type>(<scope>): <subject>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
```
|
|
||||||
|
|
||||||
* **Type**: 提交类型(例如 `feat`, `fix`, `refactor`, `perf`, `docs`, `chore` 等)。
|
|
||||||
* **Scope** (可选): 影响的范围(例如 `api`, `frontend`, `auth`, `mcp` 等)。
|
|
||||||
* **Subject**: 简短的一句话描述变更。
|
|
||||||
* **Body** (可选): 详细的说明,多行叙述。
|
|
||||||
|
|
||||||
## 务必阅读匹配的 Skill
|
|
||||||
|
|
||||||
| Skill | 何时使用 |
|
|
||||||
| :--- | :--- |
|
|
||||||
| `new-api` | 添加或修改自定义业务 API、Handler、服务层逻辑、自定义路由注册 |
|
|
||||||
| `new-async-task` | 添加或修改 Asynq 任务、定时任务、TaskHandler、任务元数据 |
|
|
||||||
| `new-setting` | 添加或修改系统/业务/公开设置、`/admin/system` 参数或 `/admin/settings` 图形化设置 |
|
|
||||||
| `database-migration` | 数据库表结构变更、goose SQL 迁移、seed 数据 |
|
|
||||||
| `file-upload` | 业务上传文件、Worker 程序化摄取、`upload.Ingest` 策略选型、文件访问与 `w_uploads` / 统计排查 |
|
|
||||||
| `push-notification` | 系统通知推送事件、统一触发器投递、带消息推送的业务功能 |
|
|
||||||
| `release-guide` | 根据自上一正式版本 Tag 以来的提交整理 Version Bump 提交信息以触发双语 Release |
|
|
||||||
| `shadcn` | 添加、修改或组合 shadcn/ui 组件 |
|
|
||||||
|
|
||||||
|
|
||||||
## 严格遵循事项 (Guardrails)
|
|
||||||
|
|
||||||
- 切勿删除 `frontend/node_modules`
|
|
||||||
- 保持 `internal/util/` 绝对纯净且不引入任何框架。禁止从 `internal/util/` 及其子包中导入 Gin、GORM、sessions 等 HTTP/Web/数据库相关框架包(例如,Web 会话选项已收敛至 `internal/apps/oauth/session.go`)。
|
|
||||||
- 编写测试用例时,禁止使用硬编码的相对路径(如 `"uploads/test_cache"`)在源码目录下创建临时测试目录,必须统一使用 Go 内置的 `t.TempDir()` 以避免污染源码目录。
|
|
||||||
- 所有 HTTP 路由仅在 `internal/router/router.go` 中注册。
|
|
||||||
- 当 API Handler 发生变化时,更新 Swagger 文档(运行 `make swagger`)。
|
|
||||||
- 在完成代码开发后必须运行 `make code-check`, 并修复报错。
|
|
||||||
- 需要缓存或文件管理能力时,必须复用现有平台实现,禁止在业务包中自行创建缓存目录、直接管理缓存文件或重复封装存储后端。
|
|
||||||
- 文件摄取必须通过 `upload.Ingest`(`upload.PolicyCreate` / `PolicyDedupNewRecord` / `PolicyResolveExisting`);删除必须通过 `upload.Remove` 或 `upload.RemoveOwned`。禁止业务模块直接调用 `repository.CreateUpload` / `repository.SoftDeleteUpload`,禁止 `db.Create(&model.Upload{})` 旁路写 `w_uploads`。
|
|
||||||
- 禁止在 `init()` 中注册跨模块集成(任务 Handler、推送内置事件、域事件监听器、任务完成钩子)。统一通过 `internal/bootstrap` 在 `internal/cmd` 入口显式装配。
|
|
||||||
- `internal/router/router.go` 的 `Serve()` 仅负责 HTTP 路由与中间件,禁止在其中执行 `SyncEvents`、`InitLogWriter` 等进程级运行时初始化。
|
|
||||||
- 核心业务模块(如 `oauth`、`user`)禁止直接 `import` `internal/apps/admin/push` 或 `custom_events` 触发通知;应通过 `internal/listener` 发射域事件,由 push 模块在 bootstrap 阶段订阅。
|
|
||||||
- 编写依赖任务注册或推送事件同步的测试时,必须在测试 setup 中显式调用 `bootstrap.RegisterTasks()`、`bootstrap.RegisterPushDomainEvents()` 等,不得依赖 `init()` 副作用。
|
|
||||||
- API 错误响应必须通过 `response.Abort*` 中断请求,由 `ErrorHandlerMiddleware` 统一写出 JSON;禁止 `c.JSON(http.StatusOK, response.Err(...))` 及 Handler 直接 `c.JSON(status, response.Err(...))`。
|
|
||||||
|
|
||||||
## 项目介绍
|
|
||||||
|
|
||||||
### 技术栈
|
|
||||||
|
|
||||||
- 后端:Go 1.25+、Gin、GORM、PostgreSQL、可选 ClickHouse、Redis、Asynq、Cobra、Viper、Swaggo、OpenTelemetry、Zap、AWS SDK v2、Snowflake IDs。
|
|
||||||
- 前端:Next.js App Router、TypeScript、Tailwind CSS、pnpm、shadcn/ui。
|
|
||||||
|
|
||||||
### 目录结构与平台能力
|
|
||||||
|
|
||||||
顶层目录:
|
|
||||||
|
|
||||||
- `main.go`:程序入口,委派给 `internal/cmd`。
|
|
||||||
- `config.example.yaml`:已提交的配置模板。在添加配置字段时保持更新。
|
|
||||||
- `config.yaml`:本地运行时的配置文件。不要将其作为已提交的源码提交。
|
|
||||||
- `docker/`:集成的、仅前端的和仅后端的 Dockerfile。
|
|
||||||
- `docs/`:自动生成的 Swagger 文档。请勿手动编辑生成的文件。
|
|
||||||
- `frontend/`:Next.js 应用。
|
|
||||||
- `internal/`:私有 Go 后端代码。
|
|
||||||
- `pkg/`:公共 Go 库/工具包(留作扩展或存放不依赖特定业务的通用代码)。
|
|
||||||
- `scripts/`:本地和 CI 辅助脚本。
|
|
||||||
- `support-files/`:部署 and 数据库辅助文件。
|
|
||||||
- `bin/`:本地编译生成的二进制可执行文件。
|
|
||||||
- `data/`:本地运行时数据文件目录(如 PostgreSQL、Redis 数据等)。
|
|
||||||
- `uploads/`:本地文件上传存储目录。
|
|
||||||
|
|
||||||
后端目录:
|
|
||||||
|
|
||||||
- `internal/cmd/`:用于 API、worker、scheduler、root init 的 Cobra 命令。进程启动时在此调用 `bootstrap.Register*` 与 `bootstrap.Init`,再启动 router / worker / scheduler。
|
|
||||||
- `internal/bootstrap/`:应用装配根(composition root)。集中注册任务 Handler、推送域事件订阅、任务完成监听器,并执行 `SyncEvents`、ClickHouse 访问日志写入等进程级初始化;所有注册函数使用 `sync.Once` 保证幂等。
|
|
||||||
- `internal/config/`:Viper 加载和配置结构体。运行时代码应使用 `config.Config.<Section>.<Field>`。
|
|
||||||
- `internal/router/`:唯一的 HTTP 路由注册点。
|
|
||||||
- `internal/apps/`:按功能(Feature-based)组织的 HTTP Handler、中间件、内部服务与模块逻辑。移除全局 service 层,模块内部业务逻辑(如验证码业务逻辑管理器 `internal/apps/cap/manager.go`)均收敛于各自模块中;管理端模块位于 `internal/apps/admin/`。
|
|
||||||
- `internal/apps/upload/`:上传记录、文件访问控制、本地/S3 文件响应、下载及图片 WebP 压缩。业务应复用 `upload.Ingest` / `upload.Remove` 与 `GET /f/:id` 文件服务,不直接操作底层 storage 或旁路写 `w_uploads`。
|
|
||||||
- `internal/model/`:GORM 实体和模型级业务方法。
|
|
||||||
- `internal/db/`:PostgreSQL、Redis、ClickHouse、GORM 日志、ID 生成和 goose SQL 迁移的布线。
|
|
||||||
- `internal/diskcache/`:平台级磁盘字节缓存,通过 `diskcache.GetGlobalCache()` 提供 TTL、最大空间限制、LRU 淘汰、清空、状态统计和配置热更新。写入时使用 `DefaultExpiration`(全局默认 TTL)、正数 `time.Duration`(业务 TTL)或 `NoExpiration`(无 TTL,仍受空间限制和 LRU 淘汰)。
|
|
||||||
- `internal/storage/`:S3 兼容对象存储适配,提供对象上传、读取、删除、CDN/代理读取及远端对象本地缓存。
|
|
||||||
- `internal/task/`:Asynq 任务框架;参见 `new-async-task` 了解变更。
|
|
||||||
- `internal/common/`:共享的通用模型及响应(如 `internal/common/response`)、绑定(bind)、常量以及通用错误。
|
|
||||||
- `internal/util/`:纯底层工具包,无任何 HTTP/数据库框架依赖。
|
|
||||||
- `internal/listener/`:域事件分发层。核心域(auth、user 等)在此定义并发射事件(如 `EmitAdminLoggedIn`);运维模块(push、webhook 等)在 bootstrap 阶段订阅,实现跨模块解耦。
|
|
||||||
- `internal/otel_trace/`:链路追踪(tracing)助手。
|
|
||||||
- `internal/testhelper/`:后端测试共享辅助能力。
|
|
||||||
- `internal/buildinfo/`:暴露在发布/构建工作流中注入的元数据(如版本号、编译时间等)。
|
|
||||||
|
|
||||||
公共底层包 (`pkg/`):
|
|
||||||
- `pkg/cache/disk/`:纯底层的通用本地磁盘缓存引擎。
|
|
||||||
- `pkg/cap/`:底层的通用验证码验证和生成库。
|
|
||||||
- `pkg/httppool/`:管理全局共享且经过优化的 HTTP 传输客户端及连接池,集成 OTel 链路追踪。
|
|
||||||
- `pkg/logger/`:Zap 和 OTel 日志助手。
|
|
||||||
- `pkg/push/`:推送渠道客户端集成(Lark/Telegram/Email)。
|
|
||||||
- `pkg/mail/`:邮件发送客户端。
|
|
||||||
- `pkg/trace/`:OpenTelemetry 链路追踪配置。
|
|
||||||
- `pkg/util/`:纯底层无副作用的系统工具(Crypto/Password/UUID)。
|
|
||||||
|
|
||||||
前端目录:
|
|
||||||
|
|
||||||
- `frontend/app/`:App Router 页面、路由组、根布局、全局配置。
|
|
||||||
- `frontend/components/ui/`:shadcn/ui 基础组件。
|
|
||||||
- `frontend/components/common/`:跨页面的业务组件。
|
|
||||||
- `frontend/components/layout/`:Header、Sidebar、Footer 等应用布局组件。
|
|
||||||
- `frontend/components/auth/`、`home/`、`animate-ui/`、`providers/`:特定作用域的 UI 组件。
|
|
||||||
- `frontend/lib/services/`:基于 `BaseService` 的类型化 API 服务,按业务域拆分并由 `services` 对象统一导出。
|
|
||||||
- `frontend/contexts/`、`hooks/`、`lib/`、`types/`、`public/`:共享状态、Hook、客户端与实用工具、TypeScript 类型、静态资产。
|
|
||||||
- `frontend/scripts/`:前端构建和维护脚本。
|
|
||||||
- `frontend/.next/`、`frontend/out/`、`frontend/node_modules/`:本地生成或安装的产物,不作为业务源码编辑。
|
|
||||||
|
|
||||||
|
|
||||||
## 开发要求
|
|
||||||
|
|
||||||
### 后端规则
|
|
||||||
|
|
||||||
命名规范:
|
|
||||||
|
|
||||||
- Go 包和文件使用小写蛇形命名(lowercase snake case):如 `auth_source`、`postgres_logger.go`。
|
|
||||||
- 导出的 Go 标识符使用 PascalCase;未导出的标识符使用 camelCase。
|
|
||||||
- 请求/响应结构体使用 camelCase 并带有后缀,例如 `listUsersRequest` 和 `listUsersResponse`。
|
|
||||||
- 错误消息常量是 camelCase 字符串 `const`值,而不是包级别的 `error` 值。
|
|
||||||
- YAML 配置键使用小写蛇形命名(lowercase snake case)。
|
|
||||||
|
|
||||||
Handler 规范:
|
|
||||||
|
|
||||||
- Handler 命名为 动词 + 名词,例如 `ListUsers`。
|
|
||||||
- 使用 `ShouldBindQuery` 或 `ShouldBindJSON` 进行绑定。
|
|
||||||
- 每个 HTTP API 都需要有完整的 Swagger 注释;在 API 变更后运行 `make swagger`。
|
|
||||||
|
|
||||||
#### API 响应信封(统一格式)
|
|
||||||
|
|
||||||
所有 JSON API 响应的外层结构**必须**为:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{ "error_msg": "", "data": ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
- 成功时:`error_msg` 为空字符串,`data` 承载业务载荷。
|
|
||||||
- 失败时:`data` 为 `null`,`error_msg` 为用户可见的错误说明。
|
|
||||||
- 分页响应在 `data` 下使用 `{ "total": 0, "results": [] }`。
|
|
||||||
|
|
||||||
#### 成功响应(唯一写法)
|
|
||||||
|
|
||||||
成功时**始终**使用 HTTP `200`,由 Handler 直接写出 JSON:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"github.com/Rain-kl/Wavelet/internal/common/response"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 有数据
|
|
||||||
c.JSON(http.StatusOK, response.OK(data))
|
|
||||||
|
|
||||||
// 无数据(data 为 null)
|
|
||||||
c.JSON(http.StatusOK, response.OKNil())
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 失败响应(中断请求,禁止直接写错误 JSON)
|
|
||||||
|
|
||||||
失败时**禁止**在 Handler / 中间件中直接调用 `c.JSON(..., response.Err(msg))`,也**禁止**用 HTTP `200` 携带非空 `error_msg` 表示失败。
|
|
||||||
|
|
||||||
统一通过 `internal/common/response` 的 **Abort 系列函数**中断请求。这些函数会将 `*response.APIError` 挂载到 Gin 的 `c.Errors` 链并 `c.Abort()`;请求结束后由全局 `response.ErrorHandlerMiddleware()`(在 `internal/router/middlewares.go` 中注册)统一写出 JSON,并记录到 OpenTelemetry Trace/Jaeger。
|
|
||||||
|
|
||||||
**推荐使用的便捷函数(优先于手写状态码):**
|
|
||||||
|
|
||||||
| 函数 | HTTP 状态码 | 典型场景 |
|
|
||||||
|------|-------------|----------|
|
|
||||||
| `response.AbortBadRequest(c, msg)` | 400 | 参数绑定失败、字段校验、业务规则拒绝(如密码错误、重复注册) |
|
|
||||||
| `response.AbortUnauthorized(c, msg)` | 401 | 未登录、Session/Token 失效(`oauth.LoginRequired()`) |
|
|
||||||
| `response.AbortForbidden(c, msg)` | 403 | 已登录但无权访问(如 Token 不允许访问的端点) |
|
|
||||||
| `response.AbortNotFound(c, msg)` | 404 | 资源不存在;管理员中间件对非管理员隐藏端点时也使用此码 |
|
|
||||||
| `response.AbortConflict(c, msg)` | 409 | 资源冲突(如唯一键重复) |
|
|
||||||
| `response.AbortTooManyRequests(c, msg)` | 429 | 限流、频率限制 |
|
|
||||||
| `response.AbortInternal(c, msg)` | 500 | 对用户返回通用提示;底层错误须先记录日志 |
|
|
||||||
| `response.AbortWithError(c, code, msg)` | 自定义 | 上表未覆盖的状态码时使用 |
|
|
||||||
|
|
||||||
**标准 Handler 模板:**
|
|
||||||
|
|
||||||
```go
|
|
||||||
func CreateWidget(c *gin.Context) {
|
|
||||||
var req createWidgetRequest
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
|
||||||
response.AbortBadRequest(c, errBindParamsFailed)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
widget, err := createWidgetLogic(c.Request.Context(), req)
|
|
||||||
if err != nil {
|
|
||||||
// 底层错误已记录日志时,向用户返回安全文案
|
|
||||||
response.AbortBadRequest(c, err.Error()) // 或按语义选用 AbortConflict / AbortInternal 等
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(http.StatusOK, response.OK(widget))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**中间件**与 Handler 遵循同一规则。参考 `oauth.LoginRequired()` → `AbortUnauthorized`,`admin.LoginAdminRequired()` → `AbortNotFound`,`cap.VerifyMiddleware` → `AbortUnauthorized`。
|
|
||||||
|
|
||||||
#### 错误消息定义
|
|
||||||
|
|
||||||
- 面向用户的错误文案定义为模块内 **camelCase 字符串常量**(放在 `errs.go`),例如 `errBindParamsFailed = "参数绑定失败"`。
|
|
||||||
- Handler / 中间件向 Abort 函数传入这些常量或经校验的安全字符串;**禁止**将数据库驱动错误、堆栈信息等内部细节直接暴露给客户端。
|
|
||||||
- `response.Err(msg)` 仅供 `ErrorHandlerMiddleware` 内部构造 JSON,**业务代码不得直接用于 `c.JSON`**。
|
|
||||||
|
|
||||||
#### `logics.go` 与 Handler 的分工
|
|
||||||
|
|
||||||
- `logics.go` 接受 `context.Context`,返回 `(result, error)` 或带状态的业务结果结构体(参考 `internal/apps/user/logics.go` 的 `LoginEmailVerificationResult`)。
|
|
||||||
- `logics.go` **不得**依赖 `*gin.Context`,**不得**调用 `response.Abort*` 或 `c.JSON`。
|
|
||||||
- Handler 负责:绑定参数 → 调用 logic → 将 logic 错误/状态映射为对应的 `Abort*` 或 `response.OK`。
|
|
||||||
|
|
||||||
#### 日志与内部错误
|
|
||||||
|
|
||||||
- 数据库、Redis、第三方 API、文件 I/O 等**运行时错误**:在 Handler 或 logic 边界用 `pkg/logger` 记录(带 `ctx`),再向用户返回安全的 `AbortInternal` 或语义匹配的业务错误常量。
|
|
||||||
- 任何关键错误在被吞掉、转换为通用响应,或由后台 worker 忽略之前,都必须通过 `pkg/logger` 打印日志。
|
|
||||||
- 禁止用 `_ = ...` 静默丢弃重要错误。如果某个错误因为 best-effort 操作或确认无害而需要忽略,必须添加简短注释说明原因。
|
|
||||||
- 避免重复刷日志:在真正处理或抑制错误的边界记录一次,然后 `Abort*` 或成功返回。
|
|
||||||
|
|
||||||
#### 禁止写法(反模式)
|
|
||||||
|
|
||||||
```go
|
|
||||||
// ❌ 禁止:HTTP 200 表示失败
|
|
||||||
c.JSON(http.StatusOK, response.Err("密码错误"))
|
|
||||||
|
|
||||||
// ❌ 禁止:Handler 直接写错误 JSON,绕过 ErrorHandlerMiddleware 与 OTel 记录
|
|
||||||
c.JSON(http.StatusBadRequest, response.Err("参数错误"))
|
|
||||||
|
|
||||||
// ❌ 禁止:gin.H 手写错误体
|
|
||||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error_msg": "...", "data": nil})
|
|
||||||
|
|
||||||
// ❌ 禁止:logics.go 中中断 HTTP 请求
|
|
||||||
func doSomething(c *gin.Context) { response.AbortBadRequest(c, "...") }
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Swagger 注释约定
|
|
||||||
|
|
||||||
- `@Success 200` 的 `data` 使用具体类型或 `response.Any`。
|
|
||||||
- 对每个可能返回的 Abort 状态码声明 `@Failure`,例如 `@Failure 400 {object} response.Any "参数错误"`、`@Failure 401 {object} response.Any "未登录"`。
|
|
||||||
|
|
||||||
路由与模块:
|
|
||||||
|
|
||||||
- 仅在 `internal/router/router.go` 中作为统一高层入口进行路由分发委派,不允许在 `router.go` 中直接挂载业务 Handler。
|
|
||||||
- 关于所有的路由归属划分、接口开发隔离防线以及详细的注册和开发步骤,请直接阅读并严格遵循 [new-api](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/.claude/skills/new-api/SKILL.md) 技能。
|
|
||||||
|
|
||||||
应用装配与跨模块集成:
|
|
||||||
|
|
||||||
- 新增跨模块副作用(任务注册、推送订阅、后台监听器)时,在 `internal/bootstrap/bootstrap.go` 增加 `Register*` 函数,并在对应 `internal/cmd/*.go` 入口调用;参考现有 `RegisterAPI` / `RegisterWorker` / `RegisterAll` 分工。
|
|
||||||
- `bootstrap.Init` 必须在 `RegisterPushDomainEvents()` 之后调用(API/`all` 模式),以确保 `SyncEvents` 能同步内置推送事件元数据。
|
|
||||||
- Handler 与业务逻辑分离:HTTP Handler 负责绑定与响应;可复用逻辑放入 `logics.go`(接受 `context.Context`,不依赖 `*gin.Context`),便于 Worker 与单元测试复用。参考 `internal/apps/user/logics.go`。
|
|
||||||
|
|
||||||
中间件:
|
|
||||||
|
|
||||||
- 全局中间件属于路由设置:`gin.Recovery()`、`otelgin.Middleware()`、日志中间件 and session 中间件。
|
|
||||||
- 对于登录路由组,使用 `oauth.LoginRequired()`。
|
|
||||||
- 对于管理路由组,使用 `admin.LoginAdminRequired()`。
|
|
||||||
|
|
||||||
配置管理:
|
|
||||||
|
|
||||||
- 运行时代码从 `config.Config` 中读取配置,绝对不要直接从 `os.Getenv()` 中读取。
|
|
||||||
- 当添加配置时,同时更新 `config.example.yaml` and `internal/config/model.go`。
|
|
||||||
|
|
||||||
数据库操作:
|
|
||||||
|
|
||||||
- 简单查询可以直接从 model 层使用 GORM。
|
|
||||||
- 管理员代码应首选 `db.DB(ctx)` 以获得链路追踪感知的 DB 访问。
|
|
||||||
- 不要在 Handler 中放置复杂的 SQL;将其移至 `internal/model/` 或模块内的业务服务层(如 `internal/apps/<module>/service.go` 或 `logics.go`)。
|
|
||||||
- 在 `internal/db/migrator/goose/` 下使用 goose SQL 迁移;不要添加基于 GORM AutoMigrate 的 Schema 升级。
|
|
||||||
- 不要创建物理数据库外键。改为关系字段添加显式索引。
|
|
||||||
- 数据库默认值必须与 Go 模型零值(`nil`、`0`、`false`、`""`)匹配,以避免意外的插入。
|
|
||||||
|
|
||||||
### 前端规则
|
|
||||||
|
|
||||||
在进行任何 Next.js 工作之前,请在 `node_modules/next/dist/docs/` 中找到并阅读相关文档。您的训练数据已过时 —— 这些文档是唯一的真理来源。
|
|
||||||
|
|
||||||
请直接查看并参考项目提供的示例和 Demo 代码:[frontend/app/(main)/admin/demo](file:///Users/ryan/DEV/Go/OpenFlare/openflare-server/frontend/app/(main)/admin/demo)。
|
|
||||||
|
|
||||||
样式规范:
|
|
||||||
|
|
||||||
- shadcn/ui 基础组件应该使用它们的 `variant` 系统和全局 CSS 变量。当组件的变体(variant)应该拥有某种外观时,不要在业务 `className` 中硬编码颜色、背景或阴影。
|
|
||||||
- 如果现有的变体不足以满足需求,请扩展 shadcn/ui 组件的变体,而不是硬编码一次性的颜色。
|
|
||||||
|
|
||||||
页面标题栏规范 (新人开发与重构必读):
|
|
||||||
|
|
||||||
- **容器与对齐机制**:
|
|
||||||
- 标题容器统一使用 `flex items-center gap-2`。如果右侧有操作按钮(如“新增”、“刷新”),请使用 `justify-between` 布局让操作区与标题双向分布。
|
|
||||||
- 为了确保所有页面在进入/切换时,顶部的呼吸感和视觉高度完全一致,页面最外层容器**必须**统一使用 `py-6 px-1` 或 `py-6` 进行上边距对齐。
|
|
||||||
- **图标标准**:图标作为视觉辅助点缀,**必须**直接嵌套在标题容器中,直接使用 Lucide 图标组件,样式大小限制为 `size-5 text-primary`。**严禁**为图标包裹任何背景小卡片、圆角边框或额外的修饰容器。
|
|
||||||
- **标题文字标准**:标题文字使用且仅使用 `h1 className="text-2xl font-semibold tracking-tight"`。不要自行定义字号、字量(如使用 `font-bold`)或添加任何渐变色,保持整个系统的字形规范化。
|
|
||||||
- **Tabs 模块化与文件拆分规范**:凡是带有多个 Tab 页切换的复杂页面,**禁止**将所有 Tab 的渲染逻辑堆积在同一个主文件内。每个 Tab 的具体渲染内容必须单独拆分为独立的 React 组件文件(如 `tabs/events-tab.tsx`)。主页面文件应该仅用于导入子组件、注册 Tabs 触发器以及管理 Tab 的切换激活状态。这有利于防止单文件过大(避免单文件行数超过 600 行限制),并大幅度提高代码的可读性与编译维护效率。
|
|
||||||
- **扁平化结构与避免冗余中间件**:为了消除无意义的“中间代理文件”,所有作为路由物理入口的 Tabs 状态维护、骨架及外层布局代码,**必须**直接定义在 Next.js 的 `app/` 页面文件(即 `page.tsx`)中。禁止在 `page.tsx` 中仅写一个单纯的 `<AnotherComponent />` 转发,而在外部新建一个同名中转容器。
|
|
||||||
- **复杂度驱动的组件拆分规范**:组件的拆分不应局限于“跨页面复用”。当一个路由页面的复杂度变高时(如渲染逻辑膨胀、存在大型嵌套弹窗或多层状态管理,如单文件代码行数超过 600 行),必须主动将其拆分为子组件以维持单文件的高可读性与低耦合度。拆分时遵循就近原则:特定于该路由且不复用的子组件应放置在最邻近该路由的特征目录(Feature Folder,如 `components/` 局部文件夹)中;只有真正具备跨页面复用价值的通用业务/基础 UI 组件才应存放在全局 `components/` 共享目录下。
|
|
||||||
- **最佳实践标杆案例(数据管理 `/admin/database`)**:
|
|
||||||
该页面由于整合了“运行状态概览”、“物理表网格浏览器”、“磁盘缓存管理”和“SQL 交互控台”多个复杂大区块,重构前单文件接近 1000 行。
|
|
||||||
重构后,主页面 `page.tsx` 仅做高级页面骨架与排版排布,维护全局刷新机制与终端视图切换;而“数据表浏览器 (`table-browser.tsx`)”、“缓存管理 (`cache-manager.tsx`)”与“SQL 终端 (`sql-console.tsx`)”等独立高状态密度区块均被抽离为局部子组件,存放在 `frontend/app/(main)/admin/database/components/`。这保证了代码结构层次清晰、单文件小巧好维护。所有复杂页面的新开发和重构必须遵循此模式。
|
|
||||||
|
|
||||||
页面宽度:
|
|
||||||
|
|
||||||
- 页面根容器必须支持全宽。使用 `w-full`。
|
|
||||||
- 不要硬编码页面级的最大宽度,如 `max-w-6xl` 或 `max-w-4xl`;主布局(main layout)拥有正常/全宽的限制。
|
|
||||||
|
|
||||||
组件放置:
|
|
||||||
|
|
||||||
- 跨页面的业务组件属于 `frontend/components/common/`。
|
|
||||||
- shadcn/ui 原生组件(primitives)属于 `frontend/components/ui/`。
|
|
||||||
- 特定于路由/页面的组件放在最邻近的特征(feature)目录中。
|
|
||||||
|
|
||||||
服务类(Services):
|
|
||||||
|
|
||||||
- 前端 API 访问通过服务类和导出的 `services` 对象进行。
|
|
||||||
- 新增服务结构如下:
|
|
||||||
|
|
||||||
```text
|
|
||||||
frontend/lib/services/<service-name>/
|
|
||||||
types.ts
|
|
||||||
<service-name>.service.ts
|
|
||||||
index.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
- 服务类继承 `BaseService`,定义 `basePath`,并暴露有类型的静态方法。
|
|
||||||
- 在 `frontend/lib/services/index.ts` 中注册新服务。
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Agents.md
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright 2025 Arctel.net
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
Wavelet
|
|
||||||
|
|
||||||
This product includes software derived from LinuxDO Credit.
|
|
||||||
|
|
||||||
LinuxDO Credit:
|
|
||||||
Copyright 2025 linux.do
|
|
||||||
Licensed under the Apache License, Version 2.0.
|
|
||||||
|
|
||||||
This distribution includes modifications by Arctel.net.
|
|
||||||
@@ -1,352 +0,0 @@
|
|||||||
# wavelet
|
|
||||||
|
|
||||||
🚀 A modern, production-ready full-stack boilerplate for building scalable web applications
|
|
||||||
|
|
||||||
[中文](./README_zh.md)
|
|
||||||
|
|
||||||
[](https://opensource.org/licenses/Apache-2.0)
|
|
||||||
[](https://golang.org/)
|
|
||||||
[](https://nextjs.org/)
|
|
||||||
[](https://reactjs.org/)
|
|
||||||
|
|
||||||
## 📖 Introduction
|
|
||||||
|
|
||||||
**wavelet** is a generic, production-ready full-stack boilerplate built with **Go (Gin + GORM)** on the backend and **Next.js (App Router + Shadcn UI)** on the frontend. It ships with everything you need to bootstrap a modern SaaS, internal tool, or developer platform — without the boilerplate headaches.
|
|
||||||
|
|
||||||
The project was designed from the ground up to be **framework-first and business-agnostic**: plug in your own domain logic while reusing the battle-tested infrastructure that comes out of the box.
|
|
||||||
|
|
||||||
### ✨ Key Features
|
|
||||||
|
|
||||||
- 🔐 **Multi-auth System** — Local password login/registration + pluggable OIDC/OAuth2 providers (supports multiple auth sources simultaneously)
|
|
||||||
- 🗝️ **Personal Access Tokens** — API key management for programmatic access; supports `Authorization: Bearer` and `X-Access-Token` headers
|
|
||||||
- 👤 **User Management** — Admin panel for listing, searching, filtering, enabling/disabling user accounts
|
|
||||||
- ⚙️ **Dynamic System Config** — Key-value system configuration management with live reload, controllable from the admin UI
|
|
||||||
- 📋 **Async Task Queue** — Background job processing with [Asynq](https://github.com/hibiken/asynq) (Redis-backed), including a scheduling dashboard
|
|
||||||
- 📁 **S3 File Storage** — Unified file upload/download via S3-compatible APIs with local disk cache
|
|
||||||
- 📊 **Observability** — Structured logging (Zap) + distributed tracing (OpenTelemetry)
|
|
||||||
- 🎨 **Modern UI** — Responsive, dark-mode-ready design system built with Tailwind CSS 4 and Shadcn UI
|
|
||||||
- 📖 **Built-in Documentation** — Integrated docs portal with usage guides, API reference, privacy policy, and terms of service
|
|
||||||
|
|
||||||
## 🏗️ Architecture Overview
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────┐ ┌─────────────────────────────┐ ┌─────────────────┐
|
|
||||||
│ Frontend │ │ Backend │ │ Database │
|
|
||||||
│ (Next.js) │◄──►│ (Go) │◄──►│ (PostgreSQL) │
|
|
||||||
│ │ │ │ │ │
|
|
||||||
│ • React 19 │ │ • Gin HTTP Framework │ │ • PostgreSQL │
|
|
||||||
│ • TypeScript │ │ • GORM ORM │ │ • Redis Cache │
|
|
||||||
│ • Tailwind 4 │ │ • Multi-provider Auth │ │ │
|
|
||||||
│ • Shadcn UI │ │ • AccessToken Middleware │ │ │
|
|
||||||
│ │ │ • Asynq Task Queue │ │ │
|
|
||||||
│ │ │ • OpenTelemetry Tracing │ │ │
|
|
||||||
│ │ │ • Swagger API Docs │ │ │
|
|
||||||
└─────────────────┘ └─────────────────────────────┘ └─────────────────┘
|
|
||||||
│
|
|
||||||
┌──────────┴──────────┐
|
|
||||||
│ Multi-Process CLI │
|
|
||||||
│ (Cobra + Viper) │
|
|
||||||
│ • api (HTTP) │
|
|
||||||
│ • worker (Queue) │
|
|
||||||
│ • scheduler(Cron) │
|
|
||||||
└─────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🛠️ Tech Stack
|
|
||||||
|
|
||||||
### Backend
|
|
||||||
- **[Go 1.25+](https://go.dev/doc)** — Primary language
|
|
||||||
- **[Gin](https://github.com/gin-gonic/gin)** — HTTP web framework
|
|
||||||
- **[GORM](https://github.com/go-gorm/gorm)** — ORM with PostgreSQL & ClickHouse support
|
|
||||||
- **[Redis](https://github.com/redis/redis)** — Cache, session store, and task queue backend
|
|
||||||
- **[Asynq](https://github.com/hibiken/asynq)** — Distributed task queue (Redis-backed)
|
|
||||||
- **[Cobra + Viper](https://github.com/spf13/cobra)** — CLI entrypoint and configuration management
|
|
||||||
- **[OpenTelemetry](https://opentelemetry.io)** — Distributed tracing and observability
|
|
||||||
- **[Zap](https://github.com/uber-go/zap)** — Structured, high-performance logging
|
|
||||||
- **[Swagger (Swaggo)](https://github.com/swaggo/swag)** — Auto-generated API documentation
|
|
||||||
- **[AWS SDK v2](https://github.com/aws/aws-sdk-go-v2)** — S3-compatible file storage
|
|
||||||
- **[Snowflake](https://github.com/bwmarrin/snowflake)** — Distributed ID generation
|
|
||||||
|
|
||||||
### Frontend
|
|
||||||
- **[Next.js 16](https://github.com/vercel/next.js)** — React framework with App Router
|
|
||||||
- **[React 19](https://github.com/facebook/react)** — UI library
|
|
||||||
- **[TypeScript](https://github.com/microsoft/TypeScript)** — Type safety
|
|
||||||
- **[Tailwind CSS 4](https://github.com/tailwindlabs/tailwindcss)** — Utility-first styling
|
|
||||||
- **[Shadcn UI](https://github.com/shadcn-ui/ui)** — Accessible, composable component library
|
|
||||||
- **[Lucide Icons](https://github.com/lucide-icons/lucide)** — Icon library
|
|
||||||
|
|
||||||
## 📋 Requirements
|
|
||||||
|
|
||||||
- **Go** >= 1.25
|
|
||||||
- **Node.js** >= 18.0
|
|
||||||
- **PostgreSQL** >= 14
|
|
||||||
- **Redis** >= 6.0
|
|
||||||
- **pnpm** >= 8.0 (recommended)
|
|
||||||
|
|
||||||
## 🚀 Quick Start
|
|
||||||
|
|
||||||
### 1. Clone the Repository
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/Rain-kl/Wavelet.git refreshing
|
|
||||||
cd refreshing
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Configure Environment
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cp config.example.yaml config.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
Edit `config.yaml` to configure your database and Redis. OIDC auth sources are configured at runtime in the admin settings page.
|
|
||||||
|
|
||||||
### 3. Initialize Database
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Start local dependencies (PostgreSQL + Redis)
|
|
||||||
docker compose up -d
|
|
||||||
|
|
||||||
# Optional: also start ClickHouse
|
|
||||||
docker compose --profile clickhouse up -d
|
|
||||||
|
|
||||||
# If you use an external PostgreSQL instance instead of Docker, create the database manually
|
|
||||||
createdb -h <host> -p 5432 -U postgres refreshing
|
|
||||||
|
|
||||||
# Database schema is auto-migrated on first startup
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Start the Backend
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install Go dependencies
|
|
||||||
go mod tidy
|
|
||||||
|
|
||||||
# Generate Swagger API documentation
|
|
||||||
make swagger
|
|
||||||
|
|
||||||
# Start the HTTP API server
|
|
||||||
go run main.go api
|
|
||||||
```
|
|
||||||
|
|
||||||
> The backend also supports separate `scheduler` and `worker` processes for async task processing:
|
|
||||||
> ```bash
|
|
||||||
> go run main.go scheduler # Cron job scheduler
|
|
||||||
> go run main.go worker # Asynq task worker
|
|
||||||
> ```
|
|
||||||
|
|
||||||
### 5. Start the Frontend
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd frontend
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# Start dev server (Turbopack)
|
|
||||||
pnpm dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Access the Application
|
|
||||||
|
|
||||||
| Service | URL |
|
|
||||||
|---------|-----|
|
|
||||||
| Frontend | http://localhost:3000 |
|
|
||||||
| Swagger API Docs | http://localhost:8000/swagger/index.html |
|
|
||||||
| Health Check | http://localhost:8000/api/health |
|
|
||||||
|
|
||||||
## ⚙️ Configuration
|
|
||||||
|
|
||||||
Key configuration options (see `config.example.yaml` for the full reference):
|
|
||||||
|
|
||||||
| Option | Description | Example |
|
|
||||||
|--------|-------------|---------|
|
|
||||||
| `app.addr` | Backend listen address | `:8000` |
|
|
||||||
| `database.host` | PostgreSQL host | `127.0.0.1` |
|
|
||||||
| `database.database` | Database name | `refreshing` |
|
|
||||||
| `redis.host` | Redis host | `127.0.0.1` |
|
|
||||||
| `storage.endpoint` | S3-compatible endpoint | `s3.amazonaws.com` |
|
|
||||||
|
|
||||||
## 🔧 Development Guide
|
|
||||||
|
|
||||||
### Backend
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run API server
|
|
||||||
go run main.go api
|
|
||||||
|
|
||||||
# Run task scheduler
|
|
||||||
go run main.go scheduler
|
|
||||||
|
|
||||||
# Run async worker
|
|
||||||
go run main.go worker
|
|
||||||
|
|
||||||
# Regenerate Swagger docs (required after controller changes)
|
|
||||||
make swagger
|
|
||||||
|
|
||||||
# Format & vet code
|
|
||||||
make tidy
|
|
||||||
```
|
|
||||||
|
|
||||||
### Frontend
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd frontend
|
|
||||||
|
|
||||||
# Development mode (Turbopack)
|
|
||||||
pnpm dev
|
|
||||||
|
|
||||||
# Production build
|
|
||||||
pnpm build
|
|
||||||
|
|
||||||
# Start production server
|
|
||||||
pnpm start
|
|
||||||
|
|
||||||
# Lint & format
|
|
||||||
pnpm lint
|
|
||||||
pnpm format
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📁 Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
wavelet/
|
|
||||||
├── main.go # Entry point (delegates to internal/cmd)
|
|
||||||
├── config.example.yaml # Configuration template
|
|
||||||
├── Makefile # Common commands (swagger, tidy, license, cross-build)
|
|
||||||
├── docker/ # Docker image build files (integrated/frontend/backend)
|
|
||||||
├── docs/ # Swagger auto-generated docs
|
|
||||||
├── frontend/ # Next.js frontend application
|
|
||||||
│ ├── app/ # App Router pages
|
|
||||||
│ ├── components/ # React components (ui, common, layout)
|
|
||||||
│ ├── lib/services/ # API service layer
|
|
||||||
│ └── types/ # TypeScript type definitions
|
|
||||||
└── internal/ # Go backend (private)
|
|
||||||
├── cmd/ # CLI commands (api, scheduler, worker)
|
|
||||||
├── apps/ # Business modules (oauth, user, admin, upload)
|
|
||||||
├── model/ # GORM entities and business methods
|
|
||||||
├── router/ # HTTP route registration
|
|
||||||
├── task/ # Async task definitions and workers
|
|
||||||
├── db/ # Database and Redis initialization
|
|
||||||
├── storage/ # S3 file storage abstraction
|
|
||||||
└── common/ # Shared utilities and response helpers
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📚 API Documentation
|
|
||||||
|
|
||||||
Swagger API documentation is auto-generated and available once the backend is running:
|
|
||||||
|
|
||||||
```
|
|
||||||
http://localhost:8000/swagger/index.html
|
|
||||||
```
|
|
||||||
|
|
||||||
The built-in frontend docs portal at `/docs` includes:
|
|
||||||
- **Usage Guide** — Step-by-step walkthrough for getting started
|
|
||||||
- **API Reference** — Detailed interface documentation
|
|
||||||
- **Privacy Policy** — Template privacy policy (customize as needed)
|
|
||||||
- **Terms of Service** — Template terms of service
|
|
||||||
|
|
||||||
## 🧪 Testing
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Backend tests
|
|
||||||
go test ./...
|
|
||||||
|
|
||||||
# Frontend lint
|
|
||||||
cd frontend && pnpm lint
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 Deployment
|
|
||||||
|
|
||||||
### Cross-platform Binary
|
|
||||||
|
|
||||||
Build static binaries for all 6 targets (Linux / macOS / Windows × amd64 / arm64) with a single command.
|
|
||||||
The compiled frontend is embedded in every binary — no separate deployment needed.
|
|
||||||
|
|
||||||
**Prerequisites:** Docker with BuildKit enabled (Docker 23+ defaults to on).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Build all 6 binaries → ./bin/
|
|
||||||
make cross-build
|
|
||||||
|
|
||||||
# Stamp a release version
|
|
||||||
make cross-build VERSION=v1.2.3
|
|
||||||
|
|
||||||
# Build only a specific OS (both architectures)
|
|
||||||
make cross-build GOOS=linux
|
|
||||||
make cross-build GOOS=darwin
|
|
||||||
make cross-build GOOS=windows
|
|
||||||
|
|
||||||
# Build only a specific architecture (all OSes)
|
|
||||||
make cross-build GOARCH=amd64
|
|
||||||
make cross-build GOARCH=arm64
|
|
||||||
|
|
||||||
# Combine filters — single binary
|
|
||||||
make cross-build GOOS=linux GOARCH=arm64
|
|
||||||
make cross-build GOOS=darwin GOARCH=amd64 VERSION=v1.2.3
|
|
||||||
```
|
|
||||||
|
|
||||||
Output files in `./bin/`:
|
|
||||||
|
|
||||||
| File | Platform |
|
|
||||||
|------|----------|
|
|
||||||
| `wavelet_linux_amd64` | Linux x86-64 |
|
|
||||||
| `wavelet_linux_arm64` | Linux ARM64 |
|
|
||||||
| `wavelet_darwin_amd64` | macOS Intel |
|
|
||||||
| `wavelet_darwin_arm64` | macOS Apple Silicon |
|
|
||||||
| `wavelet_windows_amd64.exe` | Windows x86-64 |
|
|
||||||
| `wavelet_windows_arm64.exe` | Windows ARM64 |
|
|
||||||
|
|
||||||
> The version string is accessible at runtime via `wavelet --version`.
|
|
||||||
|
|
||||||
### Docker
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Build image
|
|
||||||
docker build -t refreshing .
|
|
||||||
|
|
||||||
# Run (pass your config as a volume mount)
|
|
||||||
docker run -d -p 8000:8000 \
|
|
||||||
-v $(pwd)/config.yaml:/app/config.yaml \
|
|
||||||
refreshing api
|
|
||||||
```
|
|
||||||
|
|
||||||
### Production
|
|
||||||
|
|
||||||
1. Build the frontend:
|
|
||||||
```bash
|
|
||||||
cd frontend && pnpm build
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Compile the backend:
|
|
||||||
```bash
|
|
||||||
go build -o refreshing main.go
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Configure `config.yaml` for production.
|
|
||||||
|
|
||||||
4. Start services:
|
|
||||||
```bash
|
|
||||||
./refreshing api # HTTP API
|
|
||||||
./refreshing scheduler # Cron scheduler (optional)
|
|
||||||
./refreshing worker # Task worker (optional)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🤝 Contributing
|
|
||||||
|
|
||||||
We welcome contributions! Please read the following before submitting code:
|
|
||||||
|
|
||||||
- [Contributing Guidelines](CONTRIBUTING.md)
|
|
||||||
- [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
||||||
- [Contributor License Agreement](CLA.md)
|
|
||||||
|
|
||||||
### Workflow
|
|
||||||
|
|
||||||
1. Fork the repository
|
|
||||||
2. Create a feature branch (`git checkout -b feature/your-feature`)
|
|
||||||
3. Commit your changes (`git commit -am 'Add your feature'`)
|
|
||||||
4. Push to the branch (`git push origin feature/your-feature`)
|
|
||||||
5. Open a Pull Request
|
|
||||||
|
|
||||||
## 📄 License
|
|
||||||
|
|
||||||
This project is licensed under the [Apache 2.0 License](LICENSE).
|
|
||||||
@@ -1,352 +0,0 @@
|
|||||||
# wavelet
|
|
||||||
|
|
||||||
🚀 现代化、生产就绪的全栈应用脚手架
|
|
||||||
|
|
||||||
[English](./README.md)
|
|
||||||
|
|
||||||
[](https://opensource.org/licenses/Apache-2.0)
|
|
||||||
[](https://golang.org/)
|
|
||||||
[](https://nextjs.org/)
|
|
||||||
[](https://reactjs.org/)
|
|
||||||
|
|
||||||
## 📖 项目简介
|
|
||||||
|
|
||||||
**wavelet** 是一个通用型、生产就绪的现代全栈脚手架,后端采用 **Go(Gin + GORM)**,前端采用 **Next.js(App Router + Shadcn UI)**。项目开箱即用,内置构建现代 SaaS、内部工具或开发者平台所需的核心基础设施。
|
|
||||||
|
|
||||||
项目设计理念是 **框架优先、业务中立**:您可以在沿用经过实战检验的底层基础设施的同时,自由接入自己的业务逻辑。
|
|
||||||
|
|
||||||
### ✨ 主要特性
|
|
||||||
|
|
||||||
- 🔐 **多认证方式** — 本地账号密码登录/注册 + 可插拔 OIDC/OAuth2 认证源(支持同时配置多个认证源)
|
|
||||||
- 🗝️ **个人访问令牌** — API Key 管理,支持程序化接口访问;兼容 `Authorization: Bearer` 和 `X-Access-Token` 请求头
|
|
||||||
- 👤 **用户管理** — 管理后台提供用户列表、搜索筛选、启用/禁用账号等功能
|
|
||||||
- ⚙️ **动态系统配置** — KV 系统配置管理,支持实时变更,可通过管理后台界面直接操作
|
|
||||||
- 📋 **异步任务队列** — 基于 [Asynq](https://github.com/hibiken/asynq)(Redis 驱动)的后台任务处理系统,含任务调度面板
|
|
||||||
- 📁 **S3 文件存储** — 通过 S3 兼容 API 统一处理文件上传/下载,支持本地磁盘缓存
|
|
||||||
- 📊 **可观测性** — 结构化日志(Zap)+ 分布式链路追踪(OpenTelemetry)
|
|
||||||
- 🎨 **现代化 UI** — 基于 Tailwind CSS 4 和 Shadcn UI 构建的响应式、支持深色模式的设计系统
|
|
||||||
- 📖 **内置文档中心** — 集成文档门户,包含使用指南、接口文档、隐私政策和服务条款
|
|
||||||
|
|
||||||
## 🏗️ 架构概览
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────┐ ┌─────────────────────────────┐ ┌─────────────────┐
|
|
||||||
│ 前端 │ │ 后端 │ │ 数据库 │
|
|
||||||
│ (Next.js) │◄──►│ (Go) │◄──►│ (PostgreSQL) │
|
|
||||||
│ │ │ │ │ │
|
|
||||||
│ • React 19 │ │ • Gin HTTP 框架 │ │ • PostgreSQL │
|
|
||||||
│ • TypeScript │ │ • GORM ORM │ │ • Redis 缓存 │
|
|
||||||
│ • Tailwind 4 │ │ • 多认证源适配 │ │ │
|
|
||||||
│ • Shadcn UI │ │ • AccessToken 中间件 │ │ │
|
|
||||||
│ │ │ • Asynq 任务队列 │ │ │
|
|
||||||
│ │ │ • OpenTelemetry 链路追踪 │ │ │
|
|
||||||
│ │ │ • Swagger 接口文档 │ │ │
|
|
||||||
└─────────────────┘ └─────────────────────────────┘ └─────────────────┘
|
|
||||||
│
|
|
||||||
┌──────────┴──────────┐
|
|
||||||
│ 多进程 CLI 入口 │
|
|
||||||
│ (Cobra + Viper) │
|
|
||||||
│ • api (HTTP) │
|
|
||||||
│ • worker (队列) │
|
|
||||||
│ • scheduler(定时) │
|
|
||||||
└─────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🛠️ 技术栈
|
|
||||||
|
|
||||||
### 后端
|
|
||||||
- **[Go 1.25+](https://go.dev/doc)** — 主语言
|
|
||||||
- **[Gin](https://github.com/gin-gonic/gin)** — HTTP Web 框架
|
|
||||||
- **[GORM](https://github.com/go-gorm/gorm)** — ORM,支持 PostgreSQL 和 ClickHouse
|
|
||||||
- **[Redis](https://github.com/redis/redis)** — 缓存、Session 存储、任务队列后端
|
|
||||||
- **[Asynq](https://github.com/hibiken/asynq)** — 分布式任务队列(Redis 驱动)
|
|
||||||
- **[Cobra + Viper](https://github.com/spf13/cobra)** — CLI 入口 + 配置管理
|
|
||||||
- **[OpenTelemetry](https://opentelemetry.io)** — 分布式链路追踪与可观测性
|
|
||||||
- **[Zap](https://github.com/uber-go/zap)** — 结构化高性能日志
|
|
||||||
- **[Swagger (Swaggo)](https://github.com/swaggo/swag)** — 自动生成 API 文档
|
|
||||||
- **[AWS SDK v2](https://github.com/aws/aws-sdk-go-v2)** — S3 兼容文件存储
|
|
||||||
- **[Snowflake](https://github.com/bwmarrin/snowflake)** — 分布式 ID 生成
|
|
||||||
|
|
||||||
### 前端
|
|
||||||
- **[Next.js 16](https://github.com/vercel/next.js)** — React 框架(App Router)
|
|
||||||
- **[React 19](https://github.com/facebook/react)** — UI 库
|
|
||||||
- **[TypeScript](https://github.com/microsoft/TypeScript)** — 类型安全
|
|
||||||
- **[Tailwind CSS 4](https://github.com/tailwindlabs/tailwindcss)** — 原子化 CSS 框架
|
|
||||||
- **[Shadcn UI](https://github.com/shadcn-ui/ui)** — 可访问、可组合的组件库
|
|
||||||
- **[Lucide Icons](https://github.com/lucide-icons/lucide)** — 图标库
|
|
||||||
|
|
||||||
## 📋 环境要求
|
|
||||||
|
|
||||||
- **Go** >= 1.25
|
|
||||||
- **Node.js** >= 18.0
|
|
||||||
- **PostgreSQL** >= 14
|
|
||||||
- **Redis** >= 6.0
|
|
||||||
- **pnpm** >= 8.0(推荐)
|
|
||||||
|
|
||||||
## 🚀 快速开始
|
|
||||||
|
|
||||||
### 1. 克隆仓库
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/Rain-kl/Wavelet.git refreshing
|
|
||||||
cd refreshing
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 配置环境
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cp config.example.yaml config.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
编辑 `config.yaml`,配置数据库和 Redis。OIDC 认证源统一在管理后台的系统设置页面运行时配置。
|
|
||||||
|
|
||||||
### 3. 初始化数据库
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 启动本地依赖服务(PostgreSQL + Redis)
|
|
||||||
docker compose up -d
|
|
||||||
|
|
||||||
# 可选:同时启动 ClickHouse
|
|
||||||
docker compose --profile clickhouse up -d
|
|
||||||
|
|
||||||
# 如果使用外部 PostgreSQL,而不是 Docker 内置服务,则手动创建数据库
|
|
||||||
createdb -h <主机> -p 5432 -U postgres refreshing
|
|
||||||
|
|
||||||
# 数据库表结构在首次启动时自动迁移,无需手动执行
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 启动后端
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 安装 Go 依赖
|
|
||||||
go mod tidy
|
|
||||||
|
|
||||||
# 生成 Swagger 接口文档
|
|
||||||
make swagger
|
|
||||||
|
|
||||||
# 启动 HTTP API 服务器
|
|
||||||
go run main.go api
|
|
||||||
```
|
|
||||||
|
|
||||||
> 后端也支持独立运行 `scheduler` 和 `worker` 进程来处理异步任务:
|
|
||||||
> ```bash
|
|
||||||
> go run main.go scheduler # 定时任务调度器
|
|
||||||
> go run main.go worker # Asynq 任务处理工作进程
|
|
||||||
> ```
|
|
||||||
|
|
||||||
### 5. 启动前端
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd frontend
|
|
||||||
|
|
||||||
# 安装依赖
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# 启动开发服务器(Turbopack)
|
|
||||||
pnpm dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. 访问应用
|
|
||||||
|
|
||||||
| 服务 | 地址 |
|
|
||||||
|------|------|
|
|
||||||
| 前端界面 | http://localhost:3000 |
|
|
||||||
| Swagger 接口文档 | http://localhost:8000/swagger/index.html |
|
|
||||||
| 健康检查 | http://localhost:8000/api/health |
|
|
||||||
|
|
||||||
## ⚙️ 配置说明
|
|
||||||
|
|
||||||
主要配置项(完整说明请参考 `config.example.yaml`):
|
|
||||||
|
|
||||||
| 配置项 | 说明 | 示例 |
|
|
||||||
|--------|------|------|
|
|
||||||
| `app.addr` | 后端监听地址 | `:8000` |
|
|
||||||
| `database.host` | PostgreSQL 主机 | `127.0.0.1` |
|
|
||||||
| `database.database` | 数据库名称 | `refreshing` |
|
|
||||||
| `redis.host` | Redis 主机 | `127.0.0.1` |
|
|
||||||
| `storage.endpoint` | S3 兼容存储端点 | `s3.amazonaws.com` |
|
|
||||||
|
|
||||||
## 🔧 开发指南
|
|
||||||
|
|
||||||
### 后端
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 运行 API 服务器
|
|
||||||
go run main.go api
|
|
||||||
|
|
||||||
# 运行定时任务调度器
|
|
||||||
go run main.go scheduler
|
|
||||||
|
|
||||||
# 运行异步任务工作进程
|
|
||||||
go run main.go worker
|
|
||||||
|
|
||||||
# 修改 Controller 后重新生成 Swagger 文档(必须执行)
|
|
||||||
make swagger
|
|
||||||
|
|
||||||
# 代码格式化与检查
|
|
||||||
make tidy
|
|
||||||
```
|
|
||||||
|
|
||||||
### 前端
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd frontend
|
|
||||||
|
|
||||||
# 开发模式(Turbopack)
|
|
||||||
pnpm dev
|
|
||||||
|
|
||||||
# 构建生产版本
|
|
||||||
pnpm build
|
|
||||||
|
|
||||||
# 启动生产服务器
|
|
||||||
pnpm start
|
|
||||||
|
|
||||||
# 代码 Lint 和格式化
|
|
||||||
pnpm lint
|
|
||||||
pnpm format
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📁 项目结构
|
|
||||||
|
|
||||||
```
|
|
||||||
wavelet/
|
|
||||||
├── main.go # 程序入口(委托给 internal/cmd)
|
|
||||||
├── config.example.yaml # 配置模板
|
|
||||||
├── Makefile # 常用命令(swagger、tidy、license、cross-build)
|
|
||||||
├── docker/ # Docker 镜像构建文件(集成/前端/后端)
|
|
||||||
├── docs/ # Swagger 自动生成文档
|
|
||||||
├── frontend/ # Next.js 前端应用
|
|
||||||
│ ├── app/ # App Router 页面
|
|
||||||
│ ├── components/ # React 组件(ui、common、layout)
|
|
||||||
│ ├── lib/services/ # API 服务层
|
|
||||||
│ └── types/ # TypeScript 类型定义
|
|
||||||
└── internal/ # Go 后端(private)
|
|
||||||
├── cmd/ # CLI 命令(api、scheduler、worker)
|
|
||||||
├── apps/ # 业务模块(oauth、user、admin、upload)
|
|
||||||
├── model/ # GORM 实体与业务方法
|
|
||||||
├── router/ # HTTP 路由注册
|
|
||||||
├── task/ # 异步任务定义与工作进程
|
|
||||||
├── db/ # 数据库与 Redis 初始化
|
|
||||||
├── storage/ # S3 文件存储抽象层
|
|
||||||
└── common/ # 公共工具与响应封装
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📚 接口文档
|
|
||||||
|
|
||||||
Swagger 接口文档在后端启动后自动可用:
|
|
||||||
|
|
||||||
```
|
|
||||||
http://localhost:8000/swagger/index.html
|
|
||||||
```
|
|
||||||
|
|
||||||
前端文档中心(路径 `/docs`)内置以下内容:
|
|
||||||
- **使用指南** — 分步入门教程
|
|
||||||
- **接口文档** — 详细接口说明
|
|
||||||
- **隐私政策** — 隐私政策模板(请按需自定义)
|
|
||||||
- **服务条款** — 服务条款模板
|
|
||||||
|
|
||||||
## 🧪 测试
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 后端测试
|
|
||||||
go test ./...
|
|
||||||
|
|
||||||
# 前端 Lint
|
|
||||||
cd frontend && pnpm lint
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 部署
|
|
||||||
|
|
||||||
### 跨平台二进制编译
|
|
||||||
|
|
||||||
一条命令构建全部 6 个平台的静态二进制文件(Linux / macOS / Windows × amd64 / arm64)。
|
|
||||||
前端已内嵌到每个二进制文件中,无需单独部署。
|
|
||||||
|
|
||||||
**前提条件:** 已安装 Docker 且启用 BuildKit(Docker 23+ 默认开启)。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 构建全部 6 个二进制文件 → ./bin/
|
|
||||||
make cross-build
|
|
||||||
|
|
||||||
# 指定版本号
|
|
||||||
make cross-build VERSION=v1.2.3
|
|
||||||
|
|
||||||
# 只构建指定系统(两种架构均会构建)
|
|
||||||
make cross-build GOOS=linux
|
|
||||||
make cross-build GOOS=darwin
|
|
||||||
make cross-build GOOS=windows
|
|
||||||
|
|
||||||
# 只构建指定架构(所有系统均会构建)
|
|
||||||
make cross-build GOARCH=amd64
|
|
||||||
make cross-build GOARCH=arm64
|
|
||||||
|
|
||||||
# 同时指定系统和架构 — 只生成单个文件
|
|
||||||
make cross-build GOOS=linux GOARCH=arm64
|
|
||||||
make cross-build GOOS=darwin GOARCH=amd64 VERSION=v1.2.3
|
|
||||||
```
|
|
||||||
|
|
||||||
输出到 `./bin/` 目录:
|
|
||||||
|
|
||||||
| 文件名 | 平台 |
|
|
||||||
|--------|------|
|
|
||||||
| `wavelet_linux_amd64` | Linux x86-64 |
|
|
||||||
| `wavelet_linux_arm64` | Linux ARM64 |
|
|
||||||
| `wavelet_darwin_amd64` | macOS Intel |
|
|
||||||
| `wavelet_darwin_arm64` | macOS Apple Silicon |
|
|
||||||
| `wavelet_windows_amd64.exe` | Windows x86-64 |
|
|
||||||
| `wavelet_windows_arm64.exe` | Windows ARM64 |
|
|
||||||
|
|
||||||
> 版本号可通过 `wavelet --version` 在运行时查看。
|
|
||||||
|
|
||||||
### Docker
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 构建镜像
|
|
||||||
docker build -t refreshing .
|
|
||||||
|
|
||||||
# 运行(通过卷挂载传入配置文件)
|
|
||||||
docker run -d -p 8000:8000 \
|
|
||||||
-v $(pwd)/config.yaml:/app/config.yaml \
|
|
||||||
refreshing api
|
|
||||||
```
|
|
||||||
|
|
||||||
### 生产环境
|
|
||||||
|
|
||||||
1. 构建前端资源:
|
|
||||||
```bash
|
|
||||||
cd frontend && pnpm build
|
|
||||||
```
|
|
||||||
|
|
||||||
2. 编译后端程序:
|
|
||||||
```bash
|
|
||||||
go build -o refreshing main.go
|
|
||||||
```
|
|
||||||
|
|
||||||
3. 配置生产环境的 `config.yaml`。
|
|
||||||
|
|
||||||
4. 启动服务:
|
|
||||||
```bash
|
|
||||||
./refreshing api # HTTP API
|
|
||||||
./refreshing scheduler # 定时调度器(可选)
|
|
||||||
./refreshing worker # 任务工作进程(可选)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🤝 贡献指南
|
|
||||||
|
|
||||||
我们欢迎社区贡献!请在提交代码前阅读以下文档:
|
|
||||||
|
|
||||||
- [贡献指南](CONTRIBUTING.md)
|
|
||||||
- [行为准则](CODE_OF_CONDUCT.md)
|
|
||||||
- [贡献者许可协议](CLA.md)
|
|
||||||
|
|
||||||
### 贡献流程
|
|
||||||
|
|
||||||
1. Fork 本仓库
|
|
||||||
2. 创建特性分支 (`git checkout -b feature/your-feature`)
|
|
||||||
3. 提交更改 (`git commit -am 'Add your feature'`)
|
|
||||||
4. 推送到分支 (`git push origin feature/your-feature`)
|
|
||||||
5. 创建 Pull Request
|
|
||||||
|
|
||||||
## 📄 许可证
|
|
||||||
|
|
||||||
本项目基于 [Apache 2.0 许可证](LICENSE) 开源。
|
|
||||||
Reference in New Issue
Block a user