diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index d42c9b8c..9316a424 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -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 中添加新的路由和注入需要的服务 diff --git a/cmd/ace/wire.go b/cmd/ace/wire.go index 1c6ed84b..cb0fb787 100644 --- a/cmd/ace/wire.go +++ b/cmd/ace/wire.go @@ -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)) } diff --git a/cmd/ace/wire_gen.go b/cmd/ace/wire_gen.go index 3e6d1224..1d404861 100644 --- a/cmd/ace/wire_gen.go +++ b/cmd/ace/wire_gen.go @@ -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 } diff --git a/go.mod b/go.mod index 528c27d8..8fb90f6b 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index d3991a7f..3ec67309 100644 --- a/go.sum +++ b/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= diff --git a/internal/app/ace.go b/internal/app/ace.go deleted file mode 100644 index a73ecb75..00000000 --- a/internal/app/ace.go +++ /dev/null @@ -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"), - } -} diff --git a/internal/app/web.go b/internal/app/web.go new file mode 100644 index 00000000..062dd540 --- /dev/null +++ b/internal/app/web.go @@ -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 +} diff --git a/internal/http/request/request.go b/internal/http/request/request.go index 96d6d778..48df3cea 100644 --- a/internal/http/request/request.go +++ b/internal/http/request/request.go @@ -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 } diff --git a/internal/service/helper.go b/internal/service/helper.go index ab6e590d..94eafe62 100644 --- a/internal/service/helper.go +++ b/internal/service/helper.go @@ -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,