update doc

This commit is contained in:
ryan
2026-06-19 11:45:22 +08:00
parent d78449cbc9
commit 7eb943f02f
12 changed files with 341 additions and 1646 deletions
+333 -16
View File
@@ -16,25 +16,342 @@
* **[docs/design/architecture.md](./docs/design/architecture.md)**:理解 Server、Agent、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. **设计先行**:
* 开发新功能或重要特性时,必须在 `docs/design/` 下创建/更新对应的设计文档,理清架构与核心决策。
* 新增的设计文档应同步更新至 `docs/design/architecture.md` 及在 `docs/config.ts` 中注册侧边栏路由。
* 若实现内容超出产品边界,必须先修改设计文档,再编码实现。
2. **遵守约束**:
* 必须严格遵循 `docs/guideline/` 下的所有开发准则与开发约束规范,不得绕过任何规范。
* 涉及前端改造或管理端 UI 时,必须遵守 `docs/guideline/development-constraints.md` 中的前端规范。
* 开发新功能或重要特性时,必须在 `docs/design/` 下创建/更新对应的设计文档,理清架构与核心决策。
* 新增的设计文档应同步更新至 `docs/design/architecture.md` 及在 `docs/config.ts` 中注册侧边栏路由。
* 若实现内容超出产品边界,必须先修改设计文档,再编码实现。
3. **开发计划与交接**:
* 正在进行的开发计划或 AI 接手交接发生变化时,在 `docs/plan/` 下更新对应的开发计划或接手文档,并使用相应模板初始化。
* 正在进行的开发计划或 AI 接手交接发生变化时,在 `docs/plan/` 下更新对应的开发计划或接手文档,并使用相应模板初始化。
4. **文档与变更日志**:
* 当相关内容发生变化时,同步更新对应的**中文文档**(不要同步英文文档)。
* 代码或配置变更完成后,必须在 [`docs/changelog/index.md`](./docs/changelog/index.md) 的 `[Unreleased]` 区块补充对应变更条目。
* **纯文档变更(如 `docs/` 下的 Markdown 文档、README 等)不需要写入 changelog。**
* 当相关内容发生变化时,同步更新对应的**中文文档**(不要同步英文文档)。
* 代码或配置变更完成后,必须在 [`docs/changelog/index.md`](./docs/changelog/index.md) 的 `[Unreleased]` 区块补充对应变更条目。
* **纯文档变更(如 `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` 中注册新服务。
+2 -2
View File
@@ -1,4 +1,4 @@
Apache License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
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");
you may not use this file except in compliance with the License.
+6 -5
View File
@@ -1,8 +1,9 @@
OpenFlare
Copyright 2022-2026 OpenFlare Authors
This product includes software developed by the OpenFlare authors and
contributors and is licensed under the Apache License, Version 2.0.
This product includes software derived from Wavelet.
You may obtain a copy of the License at:
http://www.apache.org/licenses/LICENSE-2.0
Wavelet:
Copyright 2025 Arctel.net
Licensed under the Apache License, Version 2.0.
This distribution includes modifications by Arctel.net.
-188
View File
@@ -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),执行时仍按数据模型、接口、前端页面、迁移测试与文档联动的顺序推进。
-189
View File
@@ -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 式错误处理
- 函数超过合理长度仍继续堆逻辑
- 修改无关代码
- 未经说明改变已有行为
- 无测试地修改核心逻辑
- 引入大型依赖只为解决小问题
- 写完代码不说明验证方式
- 不理解现有架构就直接重构
当你发现现有代码已经比较混乱时:
- 不要一次性大重构。
- 先局部止血。
- 新代码尽量写在清晰边界内。
- 对旧代码只做必要改动。
- 如果需要重构,先提出分阶段计划。
请始终以“长期维护这个项目的人”的标准来写代码,而不是以“完成一次性任务”的标准来写代码。
-331
View File
@@ -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
View File
@@ -1 +0,0 @@
Agents.md
-201
View File
@@ -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.
-9
View File
@@ -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.
-352
View File
@@ -1,352 +0,0 @@
# wavelet
🚀 A modern, production-ready full-stack boilerplate for building scalable web applications
[中文](./README_zh.md)
[![License: Apache2.0](https://img.shields.io/badge/License-Apache2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Go Version](https://img.shields.io/badge/Go-1.25+-blue.svg)](https://golang.org/)
[![Next.js](https://img.shields.io/badge/Next.js-16-black.svg)](https://nextjs.org/)
[![React](https://img.shields.io/badge/React-19-blue.svg)](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).
-352
View File
@@ -1,352 +0,0 @@
# wavelet
🚀 现代化、生产就绪的全栈应用脚手架
[English](./README.md)
[![License: Apache2.0](https://img.shields.io/badge/License-Apache2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Go Version](https://img.shields.io/badge/Go-1.25+-blue.svg)](https://golang.org/)
[![Next.js](https://img.shields.io/badge/Next.js-16-black.svg)](https://nextjs.org/)
[![React](https://img.shields.io/badge/React-19-blue.svg)](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) 开源。