mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 09:13:49 +08:00
63
.github/copilot-instructions.md
vendored
63
.github/copilot-instructions.md
vendored
@@ -1,3 +1,26 @@
|
||||
你是一名专业的 AI 编程助手,专门使用 Go 语言的 github.com/gofiber/fiber/v3 包和 gorm.io/gorm 包开发项目。
|
||||
始终使用最新稳定版本的 go1.24,并熟悉 RESTful API 设计原则、最佳实践和 Go 语言习惯用法。
|
||||
|
||||
- 严格按照用户要求一字不差地执行。
|
||||
- 始终使用简体中文进行回复及编写代码注释。
|
||||
- 首先进行逐步思考 - 详细描述你的 API 结构、端点和数据流计划,用伪代码详细写出。
|
||||
- 确认计划后,编写代码!
|
||||
- 为 API 编写正确、最新、无错误、功能完整、安全且高效的 Go 代码。
|
||||
- 使用 github.com/gofiber/fiber/v3 包进行 API 开发:
|
||||
- fiber v3 在 handler 中使用 c fiber.Ctx 而不是 c *fiber.Ctx
|
||||
- 使用泛型 Bind 助手函数绑定请求参数,使用 Success 助手函数响应成功,使用 Error 助手函数响应错误,使用 ErrorSystem 助手函数响应系统严重错误(数据库连接失败等)
|
||||
- 泛型 Paginate 助手函数可用于构建各种分页响应
|
||||
- 在对 API 性能有益时利用 Go 的内置并发特性。
|
||||
- 遵循 RESTful API 设计原则和最佳实践。
|
||||
- 包含必要的导入、包声明和任何必需的设置代码。
|
||||
- 如需记录日志,使用标准库的 slog 包进行日志记录(注入 *slog.Logger)。
|
||||
- 考虑为横切关注点(如日志记录、认证)实现中间件。
|
||||
- 在适当时实现速率限制和认证/授权(JWT)。
|
||||
- 在 API 实现中不留任何待办事项、占位符或缺失部分。
|
||||
- 解释时简明扼要,但为复杂逻辑或 Go 特定习惯用法提供简短注释。
|
||||
- 如果对最佳实践或实现细节不确定,请说明而不是猜测。
|
||||
- 提供使用 Go 测试包测试 API 端点的建议。
|
||||
|
||||
## 项目描述
|
||||
|
||||
本项目是基于 Go 语言的 Fiber 框架和 wire 依赖注入开发的 AcePanel Linux 服务器运维管理面板,目前正在进行 v3 版本重构。
|
||||
@@ -34,46 +57,6 @@ v3 版本需要完成以下重构任务:
|
||||
├── storage/ 数据存储
|
||||
└── web/ 前端项目
|
||||
|
||||
### 架构
|
||||
|
||||
- 后端:Go 语言,使用 chi 路由(正在迁移至 Fiber v3)+ wire 依赖注入 + GORM
|
||||
- 前端:Vue 3 + TypeScript + Vite + Naive UI + pnpm
|
||||
- 数据库:SQLite3
|
||||
- 配置:基于 YAML 的 config.yml 文件
|
||||
|
||||
## 引导程序和依赖项
|
||||
|
||||
- 使用 pnpm 安装 Go 1.24+ 和 Node.js:
|
||||
- `go version` -- 验证 Go 1.24+
|
||||
- `npm install -g pnpm` -- 安装 pnpm 包管理器
|
||||
- `cd /home/runner/work/panel/panel && cp config.example.yml config.yml` -- 复制所需的配置文件
|
||||
- 下载依赖项:
|
||||
- `go mod download` -- 大约需要 30 秒。请勿取消。请将超时设置为 60 分钟以上。
|
||||
- `cd web && pnpm install` -- 大约需要 30 秒。请勿取消。请将超时设置为 60 分钟以上。
|
||||
|
||||
## 构建流程
|
||||
|
||||
- 构建后端应用:
|
||||
- `go build -o ace ./cmd/ace` -- 耗时约 14 秒。请勿取消。请将超时时间设置为 30 分钟以上。
|
||||
- `go build -o cli ./cmd/cli` -- 耗时约 1 秒。请勿取消。请将超时时间设置为 30 分钟以上。
|
||||
- 构建前端应用:
|
||||
- `cd web && cp .env.production .env && cp settings/proxy-config.example.ts settings/proxy-config.ts`
|
||||
- `cd web && pnpm run gettext:compile` -- 编译翻译,耗时约 1 秒
|
||||
- `cd web && pnpm build` -- 耗时约 30 秒。请勿取消。请将超时时间设置为 60 分钟以上。
|
||||
- 构建工件位置:
|
||||
- 后端二进制文件:在仓库根目录中构建为 `ace` 和 `cli`
|
||||
- 前端资源:构建到 `web/dist/` 并复制到 `pkg/embed/frontend/`
|
||||
|
||||
## 开发指南
|
||||
|
||||
- 使用 github.com/gofiber/fiber/v3 和 gorm.io/gorm 进行开发
|
||||
- Fiber v3 handler 使用 `c fiber.Ctx` 而不是 `c *fiber.Ctx`
|
||||
- 使用泛型 Bind 助手函数绑定请求,使用 Success 助手函数响应成功,使用 Error/ErrorSystem 助手函数响应错误,泛型 Paginate 助手函数构建各种分页响应
|
||||
- 遵循项目的 DDD 分层架构:biz → data → service → route
|
||||
- 使用标准库 slog 进行日志记录
|
||||
- 编写完整、安全、高效的代码,不留待办事项
|
||||
- 使用 testify/suite 模式编写测试
|
||||
|
||||
## 开发新需求时的流程
|
||||
|
||||
1. 在 route/http 中添加新的路由和注入需要的服务
|
||||
|
||||
@@ -17,5 +17,5 @@ import (
|
||||
|
||||
// initWeb init application.
|
||||
func initWeb() (*app.Web, error) {
|
||||
panic(wire.Build(bootstrap.ProviderSet, middleware.ProviderSet, route.ProviderSet, service.ProviderSet, data.ProviderSet, apps.ProviderSet, job.ProviderSet, app.NewAce))
|
||||
panic(wire.Build(bootstrap.ProviderSet, middleware.ProviderSet, route.ProviderSet, service.ProviderSet, data.ProviderSet, apps.ProviderSet, job.ProviderSet, app.NewWeb))
|
||||
}
|
||||
|
||||
@@ -160,6 +160,6 @@ func initWeb() (*app.Web, error) {
|
||||
return nil, err
|
||||
}
|
||||
validation := bootstrap.NewValidator(koanf, db)
|
||||
web := app.NewAce(koanf, mux, server, gormigrate, cron, queue, validation)
|
||||
web := app.NewWeb(koanf, mux, server, gormigrate, cron, queue, validation)
|
||||
return web, nil
|
||||
}
|
||||
|
||||
11
go.mod
11
go.mod
@@ -11,7 +11,6 @@ require (
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.4
|
||||
github.com/go-resty/resty/v2 v2.16.5
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/gofiber/fiber/v3 v3.0.0-beta.5
|
||||
github.com/golang-cz/httplog v0.0.2
|
||||
github.com/gomodule/redigo v1.9.2
|
||||
github.com/google/wire v0.7.0
|
||||
@@ -62,7 +61,6 @@ require (
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/G-Core/gcore-dns-sdk-go v0.3.2 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/boombuler/barcode v1.1.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
@@ -70,33 +68,24 @@ require (
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/gofiber/schema v1.6.0 // indirect
|
||||
github.com/gofiber/utils/v2 v2.0.0-beta.13 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gookit/filter v1.2.2 // indirect
|
||||
github.com/gookit/goutil v0.7.0 // indirect
|
||||
github.com/imega/luaformatter v0.0.0-20211025140405-86b0a68d6bef // indirect
|
||||
github.com/jaevor/go-nanoid v1.4.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/knadh/koanf/maps v0.1.2 // indirect
|
||||
github.com/libtnb/securecookie v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/ncruces/julianday v1.0.0 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||
github.com/timtadh/data-structures v0.6.2 // indirect
|
||||
github.com/timtadh/lexmachine v0.2.3 // indirect
|
||||
github.com/tinylib/msgp v1.3.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.64.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect
|
||||
|
||||
31
go.sum
31
go.sum
@@ -20,8 +20,6 @@ github.com/G-Core/gcore-dns-sdk-go v0.3.2/go.mod h1:35t795gOfzfVanhzkFyUXEzaBuMX
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
@@ -59,8 +57,6 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@@ -82,12 +78,8 @@ github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI6
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gofiber/fiber/v3 v3.0.0-beta.5 h1:MSGbiQZEYiYOqti2Ip2zMRkN4VvZw7Vo7dwZBa1Qjk8=
|
||||
github.com/gofiber/fiber/v3 v3.0.0-beta.5/go.mod h1:XmI2Agulde26YcQrA2n8X499I1p98/zfCNbNObVUeP8=
|
||||
github.com/gofiber/schema v1.6.0 h1:rAgVDFwhndtC+hgV7Vu5ItQCn7eC2mBA4Eu1/ZTiEYY=
|
||||
github.com/gofiber/schema v1.6.0/go.mod h1:WNZWpQx8LlPSK7ZaX0OqOh+nQo/eW2OevsXs1VZfs/s=
|
||||
github.com/gofiber/utils/v2 v2.0.0-beta.13 h1:dlpbGFLveQ9OduL2UHw4dtu4lXE+Gb3bHMc+8Yxp/dk=
|
||||
github.com/gofiber/utils/v2 v2.0.0-beta.13/go.mod h1:qEZ175nSOkl5xciHmqxwNDsWzwiB39gB8RgU1d3U4mQ=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang-cz/httplog v0.0.2 h1:3d8iScWLeMWQG5/bfMZ5Dizh+zvRfNmLBZMe5N2HrGU=
|
||||
@@ -114,8 +106,6 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4=
|
||||
github.com/google/wire v0.7.0/go.mod h1:n6YbUQD9cPKTnHXEBN2DXlOp/mVADhVErcMFb0v3J18=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
@@ -175,8 +165,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo=
|
||||
github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
|
||||
github.com/knadh/koanf/parsers/yaml v1.1.0 h1:3ltfm9ljprAHt4jxgeYLlFPmUaunuCgu1yILuTXRdM4=
|
||||
@@ -236,11 +224,7 @@ github.com/libtnb/utils v1.2.0 h1:6bTZrWn2OkNrODpCY4dhuHwbhsVRV7HICIgmZ31we98=
|
||||
github.com/libtnb/utils v1.2.0/go.mod h1:9gSEuhkADlvYbM3qJRQUAMC5ypMrhrYpX9YMuYJ6ws8=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
@@ -269,8 +253,6 @@ github.com/orandin/slog-gorm v1.4.0 h1:FgA8hJufF9/jeNSYoEXmHPPBwET2gwlF3B85JdpsT
|
||||
github.com/orandin/slog-gorm v1.4.0/go.mod h1:MoZ51+b7xE9lwGNPYEhxcUtRNrYzjdcKvA8QXQQGEPA=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
@@ -301,8 +283,6 @@ github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRo
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sethvargo/go-limiter v1.0.1-0.20250412144437-fa26982c7e1a h1:CdCoDHVynJVAQWN7ZQrAUOp0SV5TmRwNOSkF5KedDko=
|
||||
github.com/sethvargo/go-limiter v1.0.1-0.20250412144437-fa26982c7e1a/go.mod h1:01b6tW25Ap+MeLYBuD4aHunMrJoNO5PVUFdS9rac3II=
|
||||
github.com/shamaton/msgpack/v2 v2.2.3 h1:uDOHmxQySlvlUYfQwdjxyybAOzjlQsD1Vjy+4jmO9NM=
|
||||
github.com/shamaton/msgpack/v2 v2.2.3/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
@@ -342,8 +322,6 @@ github.com/timtadh/getopt v1.0.0/go.mod h1:L3EL6YN2G0eIAhYBo9b7SB9d/kEQmdnwthIlM
|
||||
github.com/timtadh/lexmachine v0.2.2/go.mod h1:GBJvD5OAfRn/gnp92zb9KTgHLB7akKyxmVivoYCcjQI=
|
||||
github.com/timtadh/lexmachine v0.2.3 h1:ZqlfHnfMcAygtbNM5Gv7jQf8hmM8LfVzDjfCrq235NQ=
|
||||
github.com/timtadh/lexmachine v0.2.3/go.mod h1:oK1NW+93fQSIF6s+J6sXBFWsCPCFbNmrwKV1i0aqvW0=
|
||||
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
|
||||
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
@@ -353,17 +331,9 @@ github.com/tufanbarisyildirim/gonginx v0.0.0-20250620092546-c3e307e36701 h1:JgeH
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20250620092546-c3e307e36701/go.mod h1:ALbEe81QPWOZjDKCKNWodG2iqCMtregG8+ebQgjx2+4=
|
||||
github.com/urfave/cli/v3 v3.4.1 h1:1M9UOCy5bLmGnuu1yn3t3CB4rG79Rtoxuv1sPhnm6qM=
|
||||
github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.64.0 h1:QBygLLQmiAyiXuRhthf0tuRkqAFcrC42dckN2S+N3og=
|
||||
github.com/valyala/fasthttp v1.64.0/go.mod h1:dGmFxwkWXSK0NbOSJuF7AMVzU+lkHz0wQVvVITv2UQA=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
@@ -443,7 +413,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/gookit/validate"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"github.com/tnborg/panel/pkg/queue"
|
||||
)
|
||||
|
||||
type Ace struct {
|
||||
conf *koanf.Koanf
|
||||
router *fiber.App
|
||||
migrator *gormigrate.Gormigrate
|
||||
cron *cron.Cron
|
||||
queue *queue.Queue
|
||||
}
|
||||
|
||||
func NewWeb(conf *koanf.Koanf, router *fiber.App, migrator *gormigrate.Gormigrate, cron *cron.Cron, queue *queue.Queue, _ *validate.Validation) *Ace {
|
||||
return &Ace{
|
||||
conf: conf,
|
||||
router: router,
|
||||
migrator: migrator,
|
||||
cron: cron,
|
||||
queue: queue,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Ace) Run() error {
|
||||
// migrate database
|
||||
if err := r.migrator.Migrate(); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("[DB] database migrated")
|
||||
|
||||
// start cron scheduler
|
||||
r.cron.Start()
|
||||
fmt.Println("[CRON] cron scheduler started")
|
||||
|
||||
// start queue
|
||||
r.queue.Run(context.TODO())
|
||||
|
||||
// run http server
|
||||
config := fiber.ListenConfig{
|
||||
ListenerNetwork: fiber.NetworkTCP,
|
||||
EnablePrefork: r.conf.Bool("http.prefork"),
|
||||
EnablePrintRoutes: r.conf.Bool("http.debug"),
|
||||
DisableStartupMessage: !r.conf.Bool("http.debug"),
|
||||
}
|
||||
if r.conf.Bool("http.tls") {
|
||||
config.CertFile = filepath.Join(Root, "panel/storage/cert.pem")
|
||||
config.CertKeyFile = filepath.Join(Root, "panel/storage/cert.key")
|
||||
fmt.Println("[HTTP] listening and serving on port", r.conf.MustInt("http.port"), "with tls")
|
||||
} else {
|
||||
fmt.Println("[HTTP] listening and serving on port", r.conf.MustInt("http.port"))
|
||||
}
|
||||
|
||||
return r.router.Listen(fmt.Sprintf(":%d", r.conf.MustInt("http.port")), config)
|
||||
}
|
||||
|
||||
func (r *Ace) listenConfig() fiber.ListenConfig {
|
||||
// prefork not support dual stack
|
||||
return fiber.ListenConfig{
|
||||
ListenerNetwork: fiber.NetworkTCP,
|
||||
EnablePrefork: r.conf.Bool("http.prefork"),
|
||||
EnablePrintRoutes: r.conf.Bool("http.debug"),
|
||||
DisableStartupMessage: !r.conf.Bool("http.debug"),
|
||||
}
|
||||
}
|
||||
70
internal/app/web.go
Normal file
70
internal/app/web.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bddjr/hlfhr"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"github.com/gookit/validate"
|
||||
"github.com/knadh/koanf/v2"
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"github.com/tnborg/panel/pkg/queue"
|
||||
)
|
||||
|
||||
type Web struct {
|
||||
conf *koanf.Koanf
|
||||
router *chi.Mux
|
||||
server *hlfhr.Server
|
||||
migrator *gormigrate.Gormigrate
|
||||
cron *cron.Cron
|
||||
queue *queue.Queue
|
||||
}
|
||||
|
||||
func NewWeb(conf *koanf.Koanf, router *chi.Mux, server *hlfhr.Server, migrator *gormigrate.Gormigrate, cron *cron.Cron, queue *queue.Queue, _ *validate.Validation) *Web {
|
||||
return &Web{
|
||||
conf: conf,
|
||||
router: router,
|
||||
server: server,
|
||||
migrator: migrator,
|
||||
cron: cron,
|
||||
queue: queue,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Web) Run() error {
|
||||
// migrate database
|
||||
if err := r.migrator.Migrate(); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("[DB] database migrated")
|
||||
|
||||
// start cron scheduler
|
||||
r.cron.Start()
|
||||
fmt.Println("[CRON] cron scheduler started")
|
||||
|
||||
// start queue
|
||||
r.queue.Run(context.TODO())
|
||||
|
||||
// run http server
|
||||
if r.conf.Bool("http.tls") {
|
||||
cert := filepath.Join(Root, "panel/storage/cert.pem")
|
||||
key := filepath.Join(Root, "panel/storage/cert.key")
|
||||
fmt.Println("[HTTP] listening and serving on port", r.conf.MustInt("http.port"), "with tls")
|
||||
if err := r.server.ListenAndServeTLS(cert, key); !errors.Is(err, http.ErrServerClosed) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fmt.Println("[HTTP] listening and serving on port", r.conf.MustInt("http.port"))
|
||||
if err := r.server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type WithAuthorize interface {
|
||||
Authorize(c fiber.Ctx) error
|
||||
Authorize(r *http.Request) error
|
||||
}
|
||||
|
||||
type WithPrepare interface {
|
||||
Prepare(c fiber.Ctx) error
|
||||
Prepare(r *http.Request) error
|
||||
}
|
||||
|
||||
type WithRules interface {
|
||||
Rules(c fiber.Ctx) map[string]string
|
||||
Rules(r *http.Request) map[string]string
|
||||
}
|
||||
|
||||
type WithFilters interface {
|
||||
Filters(c fiber.Ctx) map[string]string
|
||||
Filters(r *http.Request) map[string]string
|
||||
}
|
||||
|
||||
type WithMessages interface {
|
||||
Messages(c fiber.Ctx) map[string]string
|
||||
Messages(r *http.Request) map[string]string
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/gookit/validate"
|
||||
"github.com/libtnb/chix"
|
||||
|
||||
@@ -25,43 +24,58 @@ type ErrorResponse struct {
|
||||
}
|
||||
|
||||
// Success 响应成功
|
||||
func Success(c fiber.Ctx, data any) error {
|
||||
return c.JSON(&SuccessResponse{
|
||||
func Success(w http.ResponseWriter, data any) {
|
||||
render := chix.NewRender(w)
|
||||
defer render.Release()
|
||||
render.JSON(&SuccessResponse{
|
||||
Msg: "success",
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// Error 响应错误
|
||||
func Error(c fiber.Ctx, code int, format string, args ...any) error {
|
||||
return c.Status(code).JSON(&ErrorResponse{
|
||||
Msg: fmt.Sprintf(format, args...),
|
||||
func Error(w http.ResponseWriter, code int, format string, args ...any) {
|
||||
render := chix.NewRender(w)
|
||||
defer render.Release()
|
||||
render.Header(chix.HeaderContentType, chix.MIMEApplicationJSONCharsetUTF8) // must before Status()
|
||||
render.Status(code)
|
||||
if len(args) > 0 {
|
||||
format = fmt.Sprintf(format, args...)
|
||||
}
|
||||
render.JSON(&ErrorResponse{
|
||||
Msg: format,
|
||||
})
|
||||
}
|
||||
|
||||
// ErrorSystem 响应系统错误
|
||||
func ErrorSystem(c fiber.Ctx) error {
|
||||
return c.Status(http.StatusInternalServerError).JSON(&ErrorResponse{
|
||||
func ErrorSystem(w http.ResponseWriter) {
|
||||
render := chix.NewRender(w)
|
||||
defer render.Release()
|
||||
render.Header(chix.HeaderContentType, chix.MIMEApplicationJSONCharsetUTF8) // must before Status()
|
||||
render.Status(http.StatusInternalServerError)
|
||||
render.JSON(&ErrorResponse{
|
||||
Msg: http.StatusText(http.StatusInternalServerError),
|
||||
})
|
||||
}
|
||||
|
||||
// Bind 验证并绑定请求参数
|
||||
func Bind[T any](c fiber.Ctx) (*T, error) {
|
||||
func Bind[T any](r *http.Request) (*T, error) {
|
||||
req := new(T)
|
||||
|
||||
// 绑定参数
|
||||
if slices.Contains([]string{"POST", "PUT", "PATCH", "DELETE"}, strings.ToUpper(c.Method())) {
|
||||
if c.Request().Header.ContentLength() > 0 {
|
||||
if err := c.Bind().Body(req); err != nil {
|
||||
binder := chix.NewBind(r)
|
||||
defer binder.Release()
|
||||
if slices.Contains([]string{"POST", "PUT", "PATCH", "DELETE"}, strings.ToUpper(r.Method)) {
|
||||
if r.ContentLength > 0 {
|
||||
if err := binder.Body(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := c.Bind().Query(req); err != nil {
|
||||
if err := binder.Query(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.Bind().URI(req); err != nil {
|
||||
if err := binder.URI(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -73,29 +87,29 @@ func Bind[T any](c fiber.Ctx) (*T, error) {
|
||||
v := df.Create()
|
||||
|
||||
if reqWithPrepare, ok := any(req).(request.WithPrepare); ok {
|
||||
if err = reqWithPrepare.Prepare(c); err != nil {
|
||||
if err = reqWithPrepare.Prepare(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if reqWithAuthorize, ok := any(req).(request.WithAuthorize); ok {
|
||||
if err = reqWithAuthorize.Authorize(c); err != nil {
|
||||
if err = reqWithAuthorize.Authorize(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if reqWithRules, ok := any(req).(request.WithRules); ok {
|
||||
if rules := reqWithRules.Rules(c); rules != nil {
|
||||
if rules := reqWithRules.Rules(r); rules != nil {
|
||||
for key, value := range rules {
|
||||
v.StringRule(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
if reqWithFilters, ok := any(req).(request.WithFilters); ok {
|
||||
if filters := reqWithFilters.Filters(c); filters != nil {
|
||||
if filters := reqWithFilters.Filters(r); filters != nil {
|
||||
v.FilterRules(filters)
|
||||
}
|
||||
}
|
||||
if reqWithMessages, ok := any(req).(request.WithMessages); ok {
|
||||
if messages := reqWithMessages.Messages(c); messages != nil {
|
||||
if messages := reqWithMessages.Messages(r); messages != nil {
|
||||
v.AddMessages(messages)
|
||||
}
|
||||
}
|
||||
@@ -109,8 +123,8 @@ func Bind[T any](c fiber.Ctx) (*T, error) {
|
||||
}
|
||||
|
||||
// Paginate 取分页条目
|
||||
func Paginate[T any](c fiber.Ctx, items []T) (pagedItems []T, total uint) {
|
||||
req, err := Bind[request.Paginate](c)
|
||||
func Paginate[T any](r *http.Request, items []T) (pagedItems []T, total uint) {
|
||||
req, err := Bind[request.Paginate](r)
|
||||
if err != nil {
|
||||
req = &request.Paginate{
|
||||
Page: 1,
|
||||
|
||||
Reference in New Issue
Block a user