diff --git a/AGENTS.md b/AGENTS.md index 960a342c..01dd313d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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**: 提交类型(例如 `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.
.`。 +- `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//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` 中仅写一个单纯的 `` 转发,而在外部新建一个同名中转容器。 +- **复杂度驱动的组件拆分规范**:组件的拆分不应局限于“跨页面复用”。当一个路由页面的复杂度变高时(如渲染逻辑膨胀、存在大型嵌套弹窗或多层状态管理,如单文件代码行数超过 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// + types.ts + .service.ts + index.ts +``` + +- 服务类继承 `BaseService`,定义 `basePath`,并暴露有类型的静态方法。 +- 在 `frontend/lib/services/index.ts` 中注册新服务。 + diff --git a/openflare-server/CONTRIBUTING.md b/CONTRIBUTING.md similarity index 100% rename from openflare-server/CONTRIBUTING.md rename to CONTRIBUTING.md diff --git a/LICENSE b/LICENSE index 261eeb9e..63e3cc2c 100644 --- a/LICENSE +++ b/LICENSE @@ -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. diff --git a/NOTICE b/NOTICE index 249a04e7..fd06c425 100644 --- a/NOTICE +++ b/NOTICE @@ -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. diff --git a/docs/guideline/Constraints.md b/docs/guideline/Constraints.md deleted file mode 100644 index 165effdd..00000000 --- a/docs/guideline/Constraints.md +++ /dev/null @@ -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__.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__.go`,其中 `` 为 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),执行时仍按数据模型、接口、前端页面、迁移测试与文档联动的顺序推进。 diff --git a/docs/guideline/Role.md b/docs/guideline/Role.md deleted file mode 100644 index cb260977..00000000 --- a/docs/guideline/Role.md +++ /dev/null @@ -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 式错误处理 -- 函数超过合理长度仍继续堆逻辑 -- 修改无关代码 -- 未经说明改变已有行为 -- 无测试地修改核心逻辑 -- 引入大型依赖只为解决小问题 -- 写完代码不说明验证方式 -- 不理解现有架构就直接重构 - -当你发现现有代码已经比较混乱时: -- 不要一次性大重构。 -- 先局部止血。 -- 新代码尽量写在清晰边界内。 -- 对旧代码只做必要改动。 -- 如果需要重构,先提出分阶段计划。 - -请始终以“长期维护这个项目的人”的标准来写代码,而不是以“完成一次性任务”的标准来写代码。 \ No newline at end of file diff --git a/openflare-server/AGENTS.md b/openflare-server/AGENTS.md deleted file mode 100644 index 9bc09e76..00000000 --- a/openflare-server/AGENTS.md +++ /dev/null @@ -1,331 +0,0 @@ -# AGENTS.md — 项目AI助手工作操作手册 - -本文件面向 AI 开发助手,定义其职责与操作规范。 - -## Git 提交规范指南 - -### 提交信息基本格式 - -每次提交更改时,应当使用以下提交格式: - -```text -(): - - -``` - -* **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.
.`。 -- `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//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` 中仅写一个单纯的 `` 转发,而在外部新建一个同名中转容器。 -- **复杂度驱动的组件拆分规范**:组件的拆分不应局限于“跨页面复用”。当一个路由页面的复杂度变高时(如渲染逻辑膨胀、存在大型嵌套弹窗或多层状态管理,如单文件代码行数超过 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// - types.ts - .service.ts - index.ts -``` - -- 服务类继承 `BaseService`,定义 `basePath`,并暴露有类型的静态方法。 -- 在 `frontend/lib/services/index.ts` 中注册新服务。 - diff --git a/openflare-server/Claude.md b/openflare-server/Claude.md deleted file mode 120000 index 96808718..00000000 --- a/openflare-server/Claude.md +++ /dev/null @@ -1 +0,0 @@ -Agents.md \ No newline at end of file diff --git a/openflare-server/LICENSE b/openflare-server/LICENSE deleted file mode 100644 index 63e3cc2c..00000000 --- a/openflare-server/LICENSE +++ /dev/null @@ -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. diff --git a/openflare-server/NOTICE b/openflare-server/NOTICE deleted file mode 100644 index 9401edbb..00000000 --- a/openflare-server/NOTICE +++ /dev/null @@ -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. diff --git a/openflare-server/README.md b/openflare-server/README.md deleted file mode 100644 index 26c5bdbf..00000000 --- a/openflare-server/README.md +++ /dev/null @@ -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 -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). diff --git a/openflare-server/README_zh.md b/openflare-server/README_zh.md deleted file mode 100644 index 686af6b6..00000000 --- a/openflare-server/README_zh.md +++ /dev/null @@ -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) 开源。