Compare commits

...

968 Commits

Author SHA1 Message Date
github-actions[bot] f3b8069710 New Crowdin translations by GitHub Action (#1352)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-08 23:41:16 +08:00
devhaozi 8bbddef429 chore(l10n): update pot files 2026-02-08 15:39:54 +00:00
github-actions[bot] 296930d36c New Crowdin translations by GitHub Action (#1351)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-08 23:17:02 +08:00
devhaozi ae8e1458ea chore(l10n): update pot files 2026-02-08 15:15:58 +00:00
耗子 9625b28ee8 fix: lint 2026-02-08 23:15:00 +08:00
github-actions[bot] bcff241c95 New Crowdin translations by GitHub Action (#1350)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-08 22:53:16 +08:00
devhaozi b4df083a19 chore(l10n): update pot files 2026-02-08 14:32:27 +00:00
耗子 ab756ed2bd Merge remote-tracking branch 'origin/main' 2026-02-08 22:31:07 +08:00
耗子 5cd1af9c2a fix: 补全翻译 2026-02-08 22:30:55 +08:00
devhaozi dc8bd635d4 chore(l10n): update pot files 2026-02-08 14:25:54 +00:00
耗子 9cec03a69a fix: 补全翻译 2026-02-08 22:24:18 +08:00
github-actions[bot] 5096339ab4 New Crowdin translations by GitHub Action (#1349)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-08 21:52:13 +08:00
devhaozi d20b940d24 chore(l10n): update pot files 2026-02-08 13:38:13 +00:00
Copilot 76abe1769b 操作日志添加"详情"列,折叠显示额外字段 (#1348)
* Initial plan

* feat: 操作日志添加"详情"列,使用 NPopover 折叠显示额外字段

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-02-08 20:06:17 +08:00
耗子 0d0c38a225 fix: OpenResty/Apache无法加载网站列表 2026-02-08 19:55:26 +08:00
Copilot 5ede0161c1 feat: 保存网站时自动将HTTPS证书上传到面板证书管理 (#1347)
* Initial plan

* feat: 保存网站时自动将HTTPS证书上传到面板证书管理

当保存网站并启用SSL时,检查证书是否已存在于面板的证书管理中。
如果不存在,则自动作为本地证书(type=upload)上传到面板证书表,
方便后续编辑网站时直接使用。

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* refactor: 添加证书自动上传失败时的错误日志

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-02-08 19:46:12 +08:00
耗子 9378546cc4 feat: 重构网站卡片 2026-02-08 19:29:13 +08:00
耗子 33b0ee9d1e feat: 重构网站卡片 2026-02-08 19:26:16 +08:00
耗子 5994595198 feat: 前端优化 2026-02-08 18:20:19 +08:00
耗子 c84ab39f16 feat: 前端优化 2026-02-08 18:16:14 +08:00
耗子 4760c89a06 feat: 前端优化 2026-02-08 18:11:29 +08:00
耗子 a10a06a544 fix: lint 2026-02-08 18:05:20 +08:00
Copilot f1a9f5a4bc Add loading/disabled states to all buttons triggering useRequest (#1344)
* Initial plan

* fix: add missing :disabled to buttons that only had :loading

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: add loading/disabled to setting module buttons

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: add loading/disabled to database module buttons

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: add loading/disabled to website module buttons

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: add loading/disabled to cert module buttons

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: add loading/disabled to project, monitor, container, home and app buttons

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: restore missing template closing tags in ssh modals

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: address code review feedback for loading state edge cases

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: add loading/disabled to remaining buttons in ToolBar, PathSelector and ServiceStatus

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: add loading/disabled states to buttons triggering useRequest across 27 Vue files

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: use separate loading refs for frps/frpc save user buttons

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-02-08 17:29:22 +08:00
renovate[bot] 6466868e08 chore(deps): Update dependency @types/node to v24.10.12 (#1345)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-08 05:57:27 +00:00
耗子 fd65c267b3 fix: lint 2026-02-08 05:54:44 +08:00
Copilot 5ee2409b87 feat: add parameter tuning tabs for Nginx, MySQL, PostgreSQL, Redis, Memcached, Pure-FTPd (#1343)
* Initial plan

* feat: add config_tune backend endpoints for nginx, mysql, postgresql, redis, memcached, pureftpd

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: add config tune frontend for nginx, mysql, postgresql, redis, memcached, pureftpd

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: address code review feedback for postgresql and pureftpd config parsers

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

* feat: 自动刷新各配置状态

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-02-08 05:50:27 +08:00
renovate[bot] fca72a7e35 chore(deps): Update pnpm to v10.29.1 (#1341)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-08 03:44:53 +08:00
耗子 7b971c7920 fix: lint 2026-02-08 03:44:38 +08:00
耗子 dbeb6b5fa9 fix: lint 2026-02-08 03:13:13 +08:00
Copilot 402388c2d3 feat: PHP 参数调整标签页 — 可视化修改常用配置 (#1340)
* Initial plan

* feat: add PHP parameter tuning tab with backend API and frontend UI

- Add GetConfigTune/UpdateConfigTune/CleanSession backend endpoints
- Add PhpConfigTuneView component with vertical tabs for disabled functions,
  upload limits, timeout limits, performance tuning, and session settings
- Register new routes and frontend API methods
- Rename component from PHPConfigTuneView to PhpConfigTuneView per convention

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: address code review - refactor save handlers, fix setINIValue blank lines, add newline validation, fix CreateModal TS2322

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* refactor: use single saveLoading and handleSave, eliminate redundant refs and wrappers

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: enhance session with visual Redis/Memcached fields, add general settings tab

Session tab now shows host/port/password for Redis, host/port for Memcached
instead of raw save_path. Added session.gc_maxlifetime and session.cookie_lifetime.
New General tab with short_open_tag, date.timezone, display_errors, error_reporting.

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-02-08 03:01:39 +08:00
renovate[bot] 58c5a87262 chore(deps): Update dependency eslint to v10 (#1337)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-07 16:36:28 +08:00
Copilot c4cd926622 feat: 网站链接图标双击打开 & 日志页 flex-height 适配 (#1339)
* Initial plan

* feat: 网站列表链接图标支持双击打开 & 修复日志页高分辨率屏幕底部留白

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-02-07 16:35:51 +08:00
耗子 debc34f236 feat: 多线程下载面板更新 2026-02-06 23:16:20 +08:00
耗子 2c06ae9293 fix: validate 2026-02-06 22:49:09 +08:00
github-actions[bot] bd7cb16c3a New Crowdin translations by GitHub Action (#1336)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-06 21:56:24 +08:00
devhaozi 9b36edd1a9 chore(l10n): update pot files 2026-02-06 13:45:17 +00:00
耗子 d5c0e03f16 fix: 网站列表优化 2026-02-06 21:27:44 +08:00
耗子 50a5b42b85 fix: 刷新证书添加禁用按钮 2026-02-06 21:17:56 +08:00
Copilot 57accdb5cf fix: preserve file ownership when copying via file manager (#1335)
* Initial plan

* fix: use cp -arf to preserve file ownership when copying

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-02-06 16:50:44 +08:00
Copilot f6cf1daae5 feat: 添加面板 HTTPS 证书手动刷新按钮 (#1334)
* Initial plan

* feat: 添加面板证书手动刷新按钮

在面板设置安全标签页中,当 HTTPS 模式为 ACME 时,
在保存按钮右侧显示「刷新证书」按钮,点击后通过
已有的 ObtainCert 服务方法重新签发证书并重启面板。

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 当 HTTPS 设置有未保存修改时禁用刷新证书按钮

追踪 https/acme/public_ip 的已保存状态,当用户修改这些
设置但未保存时,禁用「刷新证书」按钮,保存后恢复可用。

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-02-06 05:55:14 +08:00
renovate[bot] 9cbf7b3f9a chore(deps): Update non-major dependencies (#1333)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-05 21:14:03 +00:00
github-actions[bot] 9700e432fd New Crowdin translations by GitHub Action (#1331)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-04 22:25:47 +08:00
devhaozi efb427ff9c chore(l10n): update pot files 2026-02-04 13:50:02 +00:00
renovate[bot] 7ac05c5e29 chore(deps): Update dependency vite-plugin-vue-devtools to v8.0.6 (#1330)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-04 20:07:07 +08:00
Copilot 181b262a61 feat(website): 添加"全部"分类、动态模态框标题和批量创建类型支持 (#1329)
* Initial plan

* feat(website): 添加"全部"分类、动态模态框标题和批量创建类型支持

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* refactor: 修复代码审查问题,提取正则常量和接口定义

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-02-04 20:04:19 +08:00
耗子 f7b63e2f6e Merge remote-tracking branch 'origin/main' 2026-02-04 19:04:48 +08:00
耗子 5fc8fb1c1f fix: Nginx QUIC 下 PHP Host 头为空 2026-02-04 19:04:44 +08:00
dependabot[bot] 805a7ea773 chore(deps): bump @isaacs/brace-expansion from 5.0.0 to 5.0.1 in /web (#1328)
Bumps @isaacs/brace-expansion from 5.0.0 to 5.0.1.

---
updated-dependencies:
- dependency-name: "@isaacs/brace-expansion"
  dependency-version: 5.0.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-04 17:53:57 +08:00
renovate[bot] c8bfe92c5e chore(deps): Update dependency p-limit to v7.3.0 (#1327)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-03 21:48:22 +00:00
耗子 4b26e190f5 Merge remote-tracking branch 'origin/main' 2026-02-04 02:27:26 +08:00
耗子 78be22b3dc feat: 为外链添加noreferrer 2026-02-04 00:24:09 +08:00
renovate[bot] 1909865f04 chore(deps): Update dependency @types/node to v24.10.10 (#1326)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-03 18:02:56 +08:00
Copilot d7e8301148 feat: add server restart option to home page restart button (#1324)
* Initial plan

* feat: add server restart functionality to home page restart button

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* refactor: remove unreachable success callback for server restart

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-02-03 00:45:52 +08:00
耗子 54b1ddb809 fix: lint 2026-02-03 00:32:21 +08:00
Copilot 30bf14c40c feat: add file overwrite option to upload modal and display upload errors (#1322)
* Initial plan

* feat: add file overwrite upload option and display error messages

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-02-03 00:24:51 +08:00
Copilot 9ab01dc64b feat(project): 运行用户支持自定义输入 (#1323)
* Initial plan

* feat(project): 运行用户选择器支持自定义输入

为创建项目和编辑项目的"运行用户"选择器添加 filterable 和 tag 属性,
使用户可以在选择预设用户(www/root/nobody)的同时也能输入自定义用户名。

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-02-03 00:15:47 +08:00
devhaozi 58ddd55b7c chore: update mocks 2026-02-02 16:05:24 +00:00
Copilot 4f19a58bba feat: add date selector for viewing archived panel logs (#1316)
* Initial plan

* feat: add date selector for log viewing

- Add date parameter to log list API
- Add log dates listing API
- Update frontend with date selector in all log views
- Add translations for Date field

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* refactor: cache regex patterns to avoid recompilation overhead

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* refactor: use predefined regex constants instead of sync.Map

Simplified the regex caching by using predefined package-level constants
for the three log types (app, db, http) instead of sync.Map.

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-02-03 00:04:43 +08:00
renovate[bot] 15f096191e chore(deps): Update non-major dependencies (#1321)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-02 22:08:38 +08:00
renovate[bot] 6f1b177642 chore(deps): Lock file maintenance (#1319)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-02 04:40:50 +00:00
renovate[bot] f0ae6278f1 chore(deps): Update module github.com/shirou/gopsutil/v4 to v4.26.1 (#1315)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-01 17:27:15 +08:00
Copilot 2fdb23a568 feat: 支持解压单独的 .gz 格式文件 (#1318)
* Initial plan

* feat: 支持解压 .gz 格式文件(如 .sql.gz)和优化压缩格式选项

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 修正压缩格式注释说明

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-02-01 17:25:01 +08:00
Copilot ba12def9f2 Add editor close confirmation, path copy on double-click, and disable overlay close (#1314)
* Initial plan

* Implement file management optimization features

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* Improve error handling in file save dialog

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* Disable overlay click to close editor window

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* Fix comment clarity for closeOnOverlay prop

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-02-01 17:03:33 +08:00
耗子 c5667d8ce4 fix: 无法添加sftp备份 2026-02-01 06:26:23 +08:00
耗子 6b1789421f feat: cron表达式生成器新增每分钟选项 2026-01-31 19:31:04 +08:00
耗子 cef8d7caa7 fix: 面板IP证书续签 2026-01-31 18:40:41 +08:00
renovate[bot] 2d3250bc51 chore(deps): Update dependency @vueuse/core to v14.2.0 (#1313)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-31 18:22:05 +08:00
耗子 37f50eebd2 fix: 修复Nginx BasicAuth密码问题 2026-01-31 00:02:44 +08:00
github-actions[bot] 676bed3a41 New Crowdin translations by GitHub Action (#1311)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-30 22:42:16 +08:00
devhaozi bee7da8a7e chore(l10n): update pot files 2026-01-30 14:33:16 +00:00
renovate[bot] 9cfa4738cf chore(deps): Update dependency vue-router to v5.0.1 (#1310)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-30 22:19:21 +08:00
耗子 39d015b1cc fix: 兼容一些没有指定命令和入口点的镜像 2026-01-30 21:59:20 +08:00
耗子 b9a9bf7904 fix: lint 2026-01-30 19:37:02 +08:00
耗子 c35c26b237 fix: lint 2026-01-30 19:16:32 +08:00
耗子 d25c419462 feat: 添加切片的校验 2026-01-30 19:15:05 +08:00
耗子 309f6b7ee2 fix: 多次点击部署网站叠加 2026-01-30 19:02:33 +08:00
耗子 7de7eabca3 fix: 更新阿里云dns 2026-01-30 18:16:08 +08:00
devhaozi ce83fdd4db chore: update mocks 2026-01-30 09:27:20 +00:00
耗子 7ef6a5df0e feat: 不再支持手动签发证书 2026-01-30 17:26:38 +08:00
耗子 624bac6ead fix: lint 2026-01-30 17:21:10 +08:00
耗子 2e4a5d3b43 fix: 页脚链接 2026-01-30 17:17:14 +08:00
耗子 a6e4c17b83 feat(acme): 不再强制记录TTL时间 2026-01-30 16:59:10 +08:00
耗子 f3e31ab518 feat: Cron表达式生成器支持反向解析 2026-01-30 16:50:17 +08:00
耗子 42facd50c3 fix: shell脚本高亮 2026-01-30 07:27:56 +08:00
renovate[bot] 706bee0e43 chore(deps): Update dependency vue-router to v5 (#1309)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-30 06:32:32 +08:00
耗子 d0c4a106fb feat: 优化超长标题显示 2026-01-30 00:30:00 +08:00
github-actions[bot] 571a63325b New Crowdin translations by GitHub Action (#1306)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-30 00:18:26 +08:00
devhaozi 4ae47f03ef chore(l10n): update pot files 2026-01-29 16:16:47 +00:00
耗子 9e24a953c1 feat: 清理代码 2026-01-30 00:12:51 +08:00
耗子 64ef88c28e feat: 工具箱操作添加loading 2026-01-29 23:53:00 +08:00
耗子 43b92ceb4e fix: 修复固定标签页对菜单不生效 2026-01-29 23:52:24 +08:00
耗子 c2c3aa48f1 feat: 清理代码 2026-01-29 23:37:49 +08:00
耗子 be7996969a feat: 清理代码 2026-01-29 23:14:41 +08:00
renovate[bot] eeb4aaee90 chore(deps): Update module github.com/lib/pq to v1.11.1 (#1304)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-29 22:24:36 +08:00
github-actions[bot] fb7c56dc01 New Crowdin translations by GitHub Action (#1303)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-29 22:21:17 +08:00
devhaozi 15abaa875d chore(l10n): update pot files 2026-01-29 14:17:12 +00:00
耗子 0ab2bbe92a Revert "feat: 反向代理支持更多设置"
This reverts commit dd7d1ed490.
2026-01-29 22:13:45 +08:00
耗子 dd7d1ed490 feat: 反向代理支持更多设置 2026-01-29 22:12:55 +08:00
github-actions[bot] c9783118f5 New Crowdin translations by GitHub Action (#1302)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-29 21:52:16 +08:00
devhaozi 626342d13b chore(l10n): update pot files 2026-01-29 12:28:48 +00:00
耗子 15287b5bce fix: lint 2026-01-29 20:26:54 +08:00
耗子 f0f4b4f692 fix: lint 2026-01-29 20:25:07 +08:00
耗子 ea0dccb6c1 Merge remote-tracking branch 'origin/main' 2026-01-29 20:06:48 +08:00
耗子 6a73f6e333 feat: 反向代理支持更多设置 2026-01-29 20:06:44 +08:00
devhaozi b5c9f3bfb9 chore(l10n): update pot files 2026-01-29 11:44:22 +00:00
耗子 6458957650 feat: lint 2026-01-29 19:42:05 +08:00
耗子 bf8ca47c62 feat: 默认404缓存改为10s 2026-01-29 19:38:14 +08:00
耗子 e7fa6b6fb0 Merge remote-tracking branch 'origin/main' 2026-01-29 19:37:55 +08:00
耗子 6f0b10653b feat: 优化引导词 2026-01-29 19:37:51 +08:00
devhaozi c9bc8aa193 chore(l10n): update pot files 2026-01-29 11:07:23 +00:00
耗子 6870388d07 feat: 优化代理缓存设置 2026-01-29 18:33:28 +08:00
耗子 8f9a32dfb0 feat: 优化代理缓存设置 2026-01-29 17:47:42 +08:00
耗子 f87658f175 fix: 优化描述 2026-01-29 17:33:10 +08:00
耗子 369ef07f19 feat: 创建容器不再重复拉取镜像 2026-01-29 17:02:19 +08:00
耗子 6a3e0e547a Merge remote-tracking branch 'origin/main' 2026-01-29 16:57:16 +08:00
耗子 3ba9b8433d feat: 优化容器创建描述 2026-01-29 16:57:08 +08:00
renovate[bot] bd03013392 chore(deps): Update module github.com/lib/pq to v1.11.0 (#1301)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-29 04:22:02 +00:00
耗子 bd746fe766 feat: 代码优化 2026-01-29 08:14:02 +08:00
耗子 eca6ae07be feat: 代码优化 2026-01-29 08:06:48 +08:00
耗子 ab6d6c6ba2 fix: 反向代理缓存zone错误 2026-01-29 05:45:34 +08:00
耗子 3547157850 fix: 阻止酒吧点炒饭 2026-01-28 21:16:04 +08:00
renovate[bot] 01bda75ce5 chore(deps): Update module github.com/coreos/go-systemd/v22 to v22.7.0 (#1299)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-28 17:15:09 +08:00
耗子 2cfd5a26a9 fix: 兼容docker配置文件为空的情况 2026-01-28 07:46:01 +08:00
耗子 fd583ec208 fix: s3fs挂载失败删除密码 2026-01-28 07:33:09 +08:00
耗子 af2d856576 fix: s3fs卸载 2026-01-28 07:21:28 +08:00
耗子 d4a6b5e207 fix: s3fs卸载 2026-01-28 07:17:10 +08:00
耗子 ef6b83f425 fix: s3fs挂载 2026-01-28 07:04:43 +08:00
耗子 73d3a5d86b fix: s3fs挂载 2026-01-28 06:14:14 +08:00
github-actions[bot] 8f9ff80fbf New Crowdin translations by GitHub Action (#1298)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-27 23:11:37 +08:00
devhaozi 3ef55c5241 chore(l10n): update pot files 2026-01-27 15:10:45 +00:00
耗子 6db0035c43 fix: 自动 systemctl daemon-reload 2026-01-27 23:09:20 +08:00
耗子 fd27f198b9 feat: 优化logo 2026-01-27 22:08:11 +08:00
github-actions[bot] e735d63c56 New Crowdin translations by GitHub Action (#1297)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-27 21:52:15 +08:00
devhaozi c23d13437d chore(l10n): update pot files 2026-01-27 13:47:18 +00:00
耗子 4424d8ba18 fix: 编辑网站部分输入框会失去焦点 2026-01-27 21:45:31 +08:00
耗子 74a1fd2258 Merge remote-tracking branch 'origin/main' 2026-01-27 20:06:04 +08:00
耗子 84f5ea815b fix: 部分后端不支持h2代理 2026-01-27 20:06:00 +08:00
renovate[bot] 0fc6104d89 chore(deps): Update github.com/libtnb/moby/client digest to d4b7902 (#1296)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-27 19:11:53 +08:00
renovate[bot] 05ee59c51c chore(deps): Update non-major dependencies (#1295)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-27 05:16:25 +08:00
github-actions[bot] 68793e22dd New Crowdin translations by GitHub Action (#1294)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-26 22:00:49 +08:00
devhaozi e27a95c72b chore(l10n): update pot files 2026-01-26 13:57:40 +00:00
耗子 755e430737 Merge remote-tracking branch 'origin/main' 2026-01-26 21:56:43 +08:00
耗子 3169f3d036 feat: 优化 2026-01-26 21:56:36 +08:00
github-actions[bot] 23875834e8 New Crowdin translations by GitHub Action (#1293)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-26 21:40:54 +08:00
devhaozi c094c919bc chore(l10n): update pot files 2026-01-26 13:34:32 +00:00
耗子 03de84439e feat: 优化 2026-01-26 21:33:13 +08:00
github-actions[bot] 975f306e71 New Crowdin translations by GitHub Action (#1292)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-26 21:29:18 +08:00
耗子 0c546d67a4 Merge remote-tracking branch 'origin/main' 2026-01-26 21:28:40 +08:00
耗子 8a5e1ed288 feat: 证书管理支持显示ip证书 2026-01-26 21:28:36 +08:00
devhaozi 6a5ee1da8a chore(l10n): update pot files 2026-01-26 13:21:14 +00:00
耗子 ce88078c58 feat: 优化 2026-01-26 21:13:14 +08:00
耗子 da41d23a36 Merge remote-tracking branch 'origin/main' 2026-01-26 21:12:12 +08:00
耗子 2e3cb81f42 feat: 优化链接图标 2026-01-26 21:12:03 +08:00
devhaozi b443dd6463 chore(l10n): update pot files 2026-01-26 12:24:18 +00:00
耗子 dd15c47acf feat: 证书管理支持显示ip证书 2026-01-26 20:23:14 +08:00
耗子 0410fed990 fix: 页脚文档错误 2026-01-26 20:12:56 +08:00
耗子 51535b979e Merge remote-tracking branch 'origin/main' 2026-01-26 20:09:36 +08:00
耗子 36f2bdaf81 feat: 终端支持ping 2026-01-26 20:09:33 +08:00
renovate[bot] 358e02f9a3 chore(deps): Update module github.com/bddjr/hlfhr to v1.4.2 (#1291)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-26 20:02:19 +08:00
耗子 56c1dcbd44 Merge remote-tracking branch 'origin/main' 2026-01-26 20:00:53 +08:00
耗子 8bb0d969c5 fix: 终端样式 2026-01-26 20:00:50 +08:00
耗子 7cfd1f43c2 fix: cron列表宽度 2026-01-26 19:25:20 +08:00
renovate[bot] 44bd8a0e63 chore(deps): Update dependency vue-tsc to v3.2.4 (#1287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-26 19:09:56 +08:00
耗子 c39644c4f3 feat: 终端支持多标签 2026-01-26 19:05:09 +08:00
耗子 4904b7c96d feat: 终端添加本机 2026-01-26 18:55:54 +08:00
耗子 021efa95ec feat: 计划任务支持点击「运行」立即运行 2026-01-26 18:45:22 +08:00
耗子 9fce7323a6 feat: 网站支持设置日志路径 2026-01-26 18:39:23 +08:00
耗子 c4f6dd0be2 feat: 反向代理支持自定义请求头 2026-01-26 18:30:09 +08:00
耗子 197781e722 feat: 网站列表添加域名直接打开 2026-01-26 18:14:40 +08:00
耗子 d87a9e3f02 fix: 网站列表HTTPS状态显示不正确 2026-01-26 17:54:11 +08:00
耗子 fba22ddae2 fix: 不能选择已有证书 2026-01-26 17:52:56 +08:00
耗子 522c2386a1 feat: api请求添加自动重试 2026-01-26 16:32:52 +08:00
renovate[bot] 7c6c5f213d chore(deps): Lock file maintenance (#1286)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-26 05:33:54 +00:00
renovate[bot] 667c0d4ce3 chore(deps): Update module github.com/bddjr/hlfhr to v1.4.1 (#1282)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-26 02:57:58 +08:00
耗子 9f590fe3e9 feat: 支持优雅关闭 2026-01-26 02:47:42 +08:00
耗子 0292d3d6c0 fix: lint 2026-01-26 02:41:35 +08:00
耗子 2c94cb62d8 feat(cli): 添加运行状态命令 2026-01-26 02:22:05 +08:00
耗子 276ada3ab2 chore: update README.md 2026-01-25 23:33:54 +08:00
github-actions[bot] fb5b048e5e New Crowdin translations by GitHub Action (#1281)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-25 23:31:20 +08:00
devhaozi 0e84a9bcbb chore(l10n): update pot files 2026-01-25 15:02:02 +00:00
耗子 ea42eccbeb Merge remote-tracking branch 'origin/main' 2026-01-25 23:00:50 +08:00
耗子 f17616d3af fix: 添加litessl描述 2026-01-25 23:00:46 +08:00
devhaozi 90c753c6f4 chore(l10n): update pot files 2026-01-25 15:00:18 +00:00
耗子 50d0095a62 fix: 添加litessl描述 2026-01-25 22:58:38 +08:00
耗子 8fd18406b5 chore: 添加新赞助商 2026-01-25 22:43:29 +08:00
耗子 3c48269ee5 fix: 添加必填校验 2026-01-25 22:36:23 +08:00
耗子 974ca958bc fix: 旧编排的环境变量预填充表单支持数字 2026-01-25 22:25:42 +08:00
耗子 f78bb65a66 fix: openresty 服务找不到 2026-01-25 20:05:17 +08:00
耗子 283e4f0a79 fix: ip地址去重 2026-01-25 16:29:41 +08:00
耗子 7a3308fac0 fix: 自签证书兼容 2026-01-25 15:56:50 +08:00
github-actions[bot] ffd9f1264c New Crowdin translations by GitHub Action (#1280)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-25 03:16:37 +08:00
devhaozi f56cbaa56a chore(l10n): update pot files 2026-01-24 19:13:28 +00:00
耗子 b1077d1d7a feat: 添加LiteSSL支持 2026-01-25 03:10:18 +08:00
devhaozi 18ed104a5a chore(l10n): update pot files 2026-01-24 18:52:36 +00:00
耗子 6f4179a01b fix: 伪静态不报错 2026-01-25 02:41:49 +08:00
耗子 1102c9753c fix: lint 2026-01-25 02:29:02 +08:00
耗子 cbafb3fd96 chore: 更新文档 2026-01-25 01:35:17 +08:00
github-actions[bot] 4b4b69d93a New Crowdin translations by GitHub Action (#1279)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-25 00:40:05 +08:00
devhaozi b0753eee91 chore(l10n): update pot files 2026-01-24 16:30:56 +00:00
耗子 a2e8b01417 fix: lint 2026-01-25 00:29:24 +08:00
耗子 50a016e3b9 fix: lint 2026-01-25 00:26:46 +08:00
耗子 19c6f92e8c fix: s3 2026-01-25 00:16:48 +08:00
耗子 0f2bcb5290 fix: lint 2026-01-24 22:59:40 +08:00
耗子 ad59657710 fix: lint 2026-01-24 22:54:58 +08:00
耗子 857cff0460 fix: 添加校验 2026-01-24 22:47:23 +08:00
renovate[bot] cd615046ff chore(deps): Update module github.com/ncruces/go-sqlite3 to v0.30.5 (#1278)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-24 22:43:57 +08:00
耗子 c1b7a9fbd1 feat: 重写s3客户端 2026-01-24 22:41:03 +08:00
耗子 92425e99cc feat: 测试移除upx 2026-01-24 21:20:46 +08:00
耗子 d64dca4c2b feat: 去除设置缓存,会导致cli写入的设置无法正确读取 2026-01-24 21:09:08 +08:00
耗子 bfc9150133 fix: lint 2026-01-24 19:47:07 +08:00
耗子 bd24e34aa9 fix: build 2026-01-24 19:34:44 +08:00
耗子 7ff1329d50 fix: build 2026-01-24 19:34:31 +08:00
github-actions[bot] 79f6bcd038 New Crowdin translations by GitHub Action (#1277)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-24 19:32:33 +08:00
devhaozi ed477ede6b chore(l10n): update pot files 2026-01-24 11:26:28 +00:00
耗子 05b7f8e9ca fix: build 2026-01-24 19:25:32 +08:00
耗子 247bc18ef7 Merge remote-tracking branch 'origin/main' 2026-01-24 19:24:54 +08:00
耗子 8d111bd059 fix: 本地备份 2026-01-24 19:24:50 +08:00
devhaozi a060ba136e chore: update mocks 2026-01-24 11:23:22 +00:00
耗子 b01f04e6b5 fix: 修改密码后自动更新local_xxx 2026-01-24 19:22:38 +08:00
耗子 09c6fbdeb9 Merge remote-tracking branch 'origin/main' 2026-01-24 19:17:51 +08:00
耗子 760dab260f fix: lint 2026-01-24 19:17:43 +08:00
github-actions[bot] 7c09ca8c48 New Crowdin translations by GitHub Action (#1276)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-24 19:10:57 +08:00
devhaozi 6f0c11d36d chore(l10n): update pot files 2026-01-24 11:07:20 +00:00
耗子 7d8b1ddb55 Merge remote-tracking branch 'origin/main' 2026-01-24 19:05:48 +08:00
耗子 293ff47e99 fix: lint 2026-01-24 19:05:41 +08:00
devhaozi 450a08e4ac chore(l10n): update pot files 2026-01-24 10:58:54 +00:00
耗子 3a50717972 fix: 写错了 2026-01-24 18:57:44 +08:00
耗子 20ca20ef2f Merge remote-tracking branch 'origin/main' 2026-01-24 18:56:40 +08:00
耗子 be7ebf963b fix: 写错了 2026-01-24 18:56:36 +08:00
devhaozi 361ad4caa5 chore(l10n): update pot files 2026-01-24 10:54:25 +00:00
耗子 9030a2e7e9 feat: postgres密码前端 2026-01-24 18:51:46 +08:00
耗子 e8c1ad2b43 fix: build 2026-01-24 18:51:06 +08:00
耗子 9519be6303 feat: postgres添加密码支持 2026-01-24 18:48:40 +08:00
github-actions[bot] bbf70dd664 New Crowdin translations by GitHub Action (#1275)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-24 06:04:26 +08:00
耗子 59d0f2f4f3 Merge remote-tracking branch 'origin/main' 2026-01-24 05:49:08 +08:00
耗子 667320e5c9 fix: nmcli dns 2026-01-24 05:49:04 +08:00
devhaozi 1cbe7f3d87 chore(l10n): update pot files 2026-01-23 21:30:44 +00:00
Copilot c70f7d6a79 修复文件编辑器状态栏功能问题 (#1274)
* Initial plan

* 修复文件编辑器语言切换高亮和JSON文件识别问题

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* 修复行分隔符切换不生效问题

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* 移除编码切换功能(需要后端支持,范围较大)

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* 修复行分隔符切换的竞态条件和冗余操作

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 编辑器问题

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-24 05:18:29 +08:00
耗子 acc661050b fix: lint 2026-01-24 03:58:44 +08:00
耗子 eef3e6c179 fix: rewrite报错 2026-01-24 03:46:10 +08:00
耗子 52d58fba3d fix: 防止清除面板创建的网络 2026-01-24 02:49:55 +08:00
github-actions[bot] faa5cc98b0 New Crowdin translations by GitHub Action (#1273)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-24 01:48:50 +08:00
devhaozi 167d557234 chore(l10n): update pot files 2026-01-23 17:45:00 +00:00
耗子 626e515f82 Merge remote-tracking branch 'origin/main' 2026-01-24 01:43:30 +08:00
耗子 c43dc54e93 fix: 修正面板acme的描述 2026-01-24 01:43:23 +08:00
devhaozi 664c24da97 chore(l10n): update pot files 2026-01-23 17:41:58 +00:00
耗子 6a2e75e6a8 Merge remote-tracking branch 'origin/main' 2026-01-24 01:40:48 +08:00
耗子 3443fb1292 fix: build 2026-01-24 01:40:45 +08:00
devhaozi 4dd06f5812 chore: update mocks 2026-01-23 17:38:07 +00:00
耗子 4896f0f823 feat: 首页ftp统计改为项目统计 2026-01-24 01:37:25 +08:00
耗子 13f06322dd fix: 压缩时自动创建目标目录 2026-01-24 01:31:25 +08:00
耗子 c3d5cdee5c fix: 重复创建tab 2026-01-24 01:24:05 +08:00
耗子 d9463df998 feat: 优化任务状态自动刷新 2026-01-24 00:59:49 +08:00
github-actions[bot] 500f91d05e New Crowdin translations by GitHub Action (#1272)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-23 23:58:47 +08:00
devhaozi 51079b3fc1 chore(l10n): update pot files 2026-01-23 15:53:58 +00:00
耗子 5269924ca4 feat: 证书添加超时 2026-01-23 22:45:38 +08:00
Copilot 9a47d3c2bb feat(web): 添加删除确认5秒倒计时组件,防止误删 (#1271)
* Initial plan

* feat(web): 添加删除确认5秒倒计时组件,防止误删

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix(web): 修复倒计时定时器并支持多语言格式化

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 秒数不需要多语言

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-23 22:33:49 +08:00
耗子 03e45a0907 fix: 删除网站不删除目录 2026-01-23 22:10:32 +08:00
devhaozi 4c6daf5028 chore: update mocks 2026-01-23 13:57:38 +00:00
耗子 d173755f98 feat: 伪静态配置本地化 2026-01-23 21:57:05 +08:00
耗子 a3827565c1 fix: 解决MaxHeaderBytes问题 2026-01-23 21:45:05 +08:00
耗子 df1dc40b80 fix: 备份面板多了一级目录 2026-01-23 19:53:08 +08:00
renovate[bot] 4a19bf4e42 chore(deps): Update dependency vue-tsc to v3.2.3 (#1270)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-23 17:47:16 +08:00
耗子 d24f02378e feat: 备份目录不存在时不返回错误 2026-01-23 17:43:06 +08:00
耗子 05c22835e7 feat: 备份目录不存在时不返回错误 2026-01-23 17:42:28 +08:00
耗子 1f8e605f44 Merge remote-tracking branch 'origin/main' 2026-01-23 06:07:33 +08:00
耗子 56dc03bfc7 fix: editorRef dispose 2026-01-23 06:07:29 +08:00
devhaozi 11e56631b8 chore: update mocks 2026-01-22 21:45:06 +00:00
耗子 6811eb290f fix: 重复的SSH设置 2026-01-23 05:44:19 +08:00
耗子 7f2c693735 feat: 优化部署模版 2026-01-23 05:39:57 +08:00
耗子 c699c96e6c feat: 不允许轻易删除 acepanel-network 2026-01-23 05:30:58 +08:00
耗子 aef7dbd5bf fix: 优化php版本号判断 2026-01-23 04:43:54 +08:00
耗子 04e0ccb64f feat: 更新swow依赖 2026-01-23 04:25:21 +08:00
github-actions[bot] e69c50f7fd New Crowdin translations by GitHub Action (#1268)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-22 18:39:24 +08:00
devhaozi 423a00ec5a chore(l10n): update pot files 2026-01-22 10:26:12 +00:00
耗子 3b87f48090 fix: basic auth问题 2026-01-22 18:22:14 +08:00
耗子 340ff9431d feat: ip header支持编辑 2026-01-22 18:22:10 +08:00
renovate[bot] ff0b1086f9 chore(deps): Update non-major dependencies (#1269)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-22 05:10:51 +00:00
devhaozi 3b6197c3d0 chore(l10n): update pot files 2026-01-21 22:37:26 +00:00
Copilot a386dc3a2a Add redirect and advanced settings tabs to website editor (#1267)
* Initial plan

* Add redirect tab to website editor with drag-and-drop support

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* Add advanced settings tab with rate limiting and basic auth

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* Add htpasswd file support for basic auth (nginx and apache compatible)

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* Fix code review issues: prevent duplicate usernames in basic auth

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: bug

* fix: bug

* feat: 支持real ip设置

* feat: 支持real ip设置

* fix: lint

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-22 06:32:13 +08:00
耗子 0e958cbd78 fix: redirect 注释 2026-01-22 04:48:26 +08:00
renovate[bot] 22cefeec09 chore(deps): Update non-major dependencies (#1266)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-22 04:37:14 +08:00
github-actions[bot] 9ac9f76c5a New Crowdin translations by GitHub Action (#1265)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-22 04:34:36 +08:00
devhaozi f4de0f3de9 chore(l10n): update pot files 2026-01-21 20:29:43 +00:00
耗子 2f9975dbab feat: 添加加密 2026-01-22 04:22:36 +08:00
devhaozi ba1f5b284d chore: update mocks 2026-01-21 20:15:38 +00:00
耗子 d1f5e1d896 Merge remote-tracking branch 'origin/main' 2026-01-22 04:14:33 +08:00
耗子 0aebdd08e1 refactor: 重命名备份存储 2026-01-22 04:14:29 +08:00
devhaozi db1c489266 chore(l10n): update pot files 2026-01-21 19:45:50 +00:00
耗子 9cdc161f14 Merge remote-tracking branch 'origin/main' 2026-01-22 03:44:24 +08:00
耗子 f64b06b774 feat: 备份优化 2026-01-22 03:44:20 +08:00
devhaozi c189cd6eb5 chore: update mocks 2026-01-21 19:40:40 +00:00
耗子 403126efaf feat: 备份优化 2026-01-22 03:39:43 +08:00
耗子 0440f33cfb feat: 备份优化 2026-01-22 03:13:06 +08:00
耗子 7aeb56f6b7 feat: 备份优化 2026-01-22 02:54:44 +08:00
耗子 3f46cb66f7 feat: 备份优化 2026-01-22 02:45:03 +08:00
Copilot 0d62d435d9 feat(backup): 新建/更新备份账号时验证 s3、sftp、webdav 存储连接 (#1264)
* Initial plan

* feat(backup): 新建/更新备份账号时验证 s3、sftp、webdav 存储连接

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* refactor(backup): 优化 validateStorage 函数,统一连接验证逻辑

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 优化备份账号保存

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-22 01:27:40 +08:00
devhaozi 5fd50feab4 chore: update mocks 2026-01-21 17:10:32 +00:00
耗子 5018ffa82e feat: 备份重构2 2026-01-22 01:09:38 +08:00
耗子 e2f2864aa6 Merge remote-tracking branch 'origin/main' 2026-01-21 22:43:08 +08:00
耗子 0c012141ec feat: 优化登录页模版 2026-01-21 22:43:01 +08:00
renovate[bot] aede27f908 chore(deps): Update module github.com/aws/aws-sdk-go-v2/feature/s3/manager to v1.21.0 (#1263)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-21 06:09:13 +08:00
renovate[bot] c9bf471c9d chore(deps): Update non-major dependencies (#1261)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-21 04:34:10 +08:00
耗子 69fe99e040 fix: 优化问题 2026-01-20 18:32:48 +08:00
耗子 45f4a01921 Merge remote-tracking branch 'origin/main' 2026-01-20 17:28:50 +08:00
耗子 df59816e4e fix: 优化问题 2026-01-20 17:28:31 +08:00
devhaozi 8f1b69bd4b chore: update mocks 2026-01-19 18:19:07 +00:00
耗子 21d120451c Merge remote-tracking branch 'origin/main' 2026-01-20 02:18:17 +08:00
耗子 72ecb6904d feat: 备份重构1 2026-01-20 02:18:13 +08:00
devhaozi b058d851fe chore: update mocks 2026-01-19 15:50:48 +00:00
耗子 5c6892c7f5 feat: 从数据库读取备份记录 2026-01-19 23:50:04 +08:00
耗子 046105a542 feat: 从数据库读取备份记录 2026-01-19 23:41:24 +08:00
devhaozi 86ff6bb88a chore: update mocks 2026-01-19 15:06:10 +00:00
耗子 06bd235730 Merge remote-tracking branch 'origin/main' 2026-01-19 23:05:25 +08:00
耗子 ddd19f1597 feat: 备份账号设置 2026-01-19 23:05:20 +08:00
Copilot e9bbd3bc5c feat: 为项目管理操作列添加重启和重载按钮 (#1260)
* Initial plan

* feat: 为项目管理操作列添加重启和重载按钮

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-01-19 22:41:04 +08:00
renovate[bot] 30f94c920c chore(deps): Update non-major dependencies (#1259)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-19 21:29:41 +08:00
renovate[bot] 9445d1cb82 chore(deps): Lock file maintenance (#1258)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-19 05:03:36 +00:00
renovate[bot] 13cd9b0b50 chore(deps): Update module github.com/urfave/cli/v3 to v3.6.2 (#1257)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-18 17:46:26 +08:00
耗子 7442597a3c fix: 环境卸载 2026-01-18 06:12:31 +08:00
耗子 0e2e157001 Revert "feat: 测试修改socket"
This reverts commit 4d2588e449.
2026-01-18 06:04:07 +08:00
耗子 4d2588e449 feat: 测试修改socket 2026-01-17 21:42:28 +08:00
github-actions[bot] 50bb9af9bc New Crowdin translations by GitHub Action (#1256)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-17 02:05:55 +08:00
devhaozi 6f7c9269ca chore(l10n): update pot files 2026-01-16 17:55:14 +00:00
耗子 84741b594a Merge remote-tracking branch 'origin/main' 2026-01-17 01:54:23 +08:00
devhaozi 35968f0567 chore(l10n): update pot files 2026-01-16 17:52:14 +00:00
耗子 258a0ffb4a feat: 优化项目创建 2026-01-17 01:48:45 +08:00
github-actions[bot] b3201e5630 New Crowdin translations by GitHub Action (#1255)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-17 01:36:06 +08:00
devhaozi d460723a9e chore(l10n): update pot files 2026-01-16 17:35:24 +00:00
耗子 86842f8bc1 feat: 优化文件管理空白区域的右键菜单 2026-01-17 01:33:57 +08:00
github-actions[bot] 845cbb1048 New Crowdin translations by GitHub Action (#1254)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-17 01:28:19 +08:00
devhaozi 0877489ae4 chore(l10n): update pot files 2026-01-16 17:25:09 +00:00
耗子 d75baefe42 Merge remote-tracking branch 'origin/main' 2026-01-17 01:24:06 +08:00
耗子 22721c2450 fix: 日志切割 2026-01-17 01:24:03 +08:00
devhaozi 5fb0242601 chore(l10n): update pot files 2026-01-16 17:07:33 +00:00
耗子 51ffdcfa90 fix: 首页字段错误 2026-01-17 01:04:52 +08:00
耗子 0d04c872aa feat: 优化禁用网站 2026-01-17 00:59:15 +08:00
耗子 53571eaf92 feat: 优化禁用网站 2026-01-17 00:52:40 +08:00
github-actions[bot] 12d890252a New Crowdin translations by GitHub Action (#1253)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-17 00:11:22 +08:00
devhaozi fbe316ea0f chore(l10n): update pot files 2026-01-16 16:10:55 +00:00
github-actions[bot] 941c1e1bc2 New Crowdin translations by GitHub Action (#1252)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-17 00:07:02 +08:00
devhaozi 598f8180f0 chore(l10n): update pot files 2026-01-16 15:57:07 +00:00
Copilot a512d21f21 feat(project): 添加项目自启动设置功能 (#1251)
* Initial plan

* feat(project): 添加项目自启动设置功能

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* style(project): 为自启动开关添加 rubberBand: false

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 项目列表宽度

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-16 23:55:34 +08:00
耗子 980b3674b0 feat: 优化容器编排列表 2026-01-16 23:42:02 +08:00
耗子 e205f51197 feat: 提高容器的优先级 2026-01-16 22:34:57 +08:00
renovate[bot] 582fa4d93e chore(deps): Update non-major dependencies (#1250)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-16 22:32:47 +08:00
Copilot c43de6d23e feat: 为数据库服务器列表添加终端按钮 (#1249)
* Initial plan

* feat: 为数据库服务器列表添加终端按钮功能

- 在操作列添加终端按钮
- 支持MySQL和PostgreSQL数据库终端
- PostgreSQL无密码时自动切换到postgres用户

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-01-16 22:32:22 +08:00
耗子 28e2c61a4a feat: 创建数据库账号添加生成密码 2026-01-16 22:25:18 +08:00
耗子 f437740438 fix: 数据库host填写不了 2026-01-16 22:15:37 +08:00
耗子 918d8c2104 feat: 优化容器模版部署 2026-01-16 22:03:32 +08:00
耗子 7988804603 feat: 优化容器模版显示 2026-01-16 20:53:40 +08:00
renovate[bot] 0fd6db3249 chore(deps): Update dependency @tsconfig/node24 to v24.0.4 (#1248)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-16 16:21:15 +08:00
devhaozi 9f52671ca3 chore: update mocks 2026-01-15 22:29:55 +00:00
耗子 b14795a9fb feat: 优化容器编排模版使用 2026-01-16 06:29:08 +08:00
耗子 fd6aef2af7 feat: 支持URL类型 2026-01-16 04:35:51 +08:00
renovate[bot] 08bc9e2390 chore(deps): Update dependency @types/node to v24.10.9 (#1247)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-16 03:14:18 +08:00
耗子 1c40ad4d81 feat: 支持修改Docker基本设置 2026-01-16 03:12:46 +08:00
github-actions[bot] ff68df94c5 New Crowdin translations by GitHub Action (#1246)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-16 02:40:40 +08:00
devhaozi c3e7642b17 chore(l10n): update pot files 2026-01-15 18:30:32 +00:00
耗子 1c9b41da97 feat: 优化自带镜像 2026-01-16 02:25:23 +08:00
耗子 d2c4ab3bb4 feat: 支持更多项目类型 2026-01-16 02:17:18 +08:00
耗子 0cdf62af31 fix: 优化php配置 2026-01-16 01:37:21 +08:00
耗子 c7ed032500 feat: 支持更多运行环境 2026-01-16 00:59:10 +08:00
Copilot 3412709f9b fix: 修复 fail2ban IPv6 地址显示和解封问题 (#1245)
* Initial plan

* fix: 修复 fail2ban IPv6 地址显示和解封问题

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-01-15 23:16:06 +08:00
耗子 7a2d64744c fix: 修复项目状态显示不正确 2026-01-15 21:42:12 +08:00
renovate[bot] c5e6c3a770 chore(deps): Update dependency unplugin-auto-import to v21 (#1243)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-15 21:20:52 +08:00
renovate[bot] 8a27b0b8e4 chore(deps): Update dependency unplugin-vue-components to v31 (#1244)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-15 21:20:37 +08:00
renovate[bot] e463951362 chore(deps): Update dependency prettier to v3.8.0 (#1242)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-15 04:41:45 +00:00
耗子 ef2dcfd071 fix: lint 2026-01-15 06:10:11 +08:00
renovate[bot] 5c54038cde chore(deps): Update module github.com/go-chi/chi/v5 to v5.2.4 (#1241)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-14 21:11:24 +00:00
耗子 e67f32507a fix: lint 2026-01-14 23:54:50 +08:00
耗子 de20811ca9 fix: 日志清理报错 2026-01-14 23:51:51 +08:00
github-actions[bot] 84779023bf New Crowdin translations by GitHub Action (#1240)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-14 23:36:18 +08:00
devhaozi 348c5f513e chore(l10n): update pot files 2026-01-14 15:32:08 +00:00
耗子 fad7da38b6 fix: lint 2026-01-14 23:22:24 +08:00
耗子 bf3ce388d1 feat: 文件分块上传 2026-01-14 22:59:37 +08:00
耗子 11657ab9a3 Merge remote-tracking branch 'origin/main' 2026-01-14 19:48:37 +08:00
耗子 50e073c25e feat: 文件上传 2026-01-14 19:48:33 +08:00
renovate[bot] ce15dcd4b7 chore(deps): Update dependency unplugin-icons to v23.0.1 (#1239)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-14 19:27:27 +08:00
耗子 ec9a3117f7 Merge remote-tracking branch 'origin/main' 2026-01-14 19:21:24 +08:00
耗子 dd67bc47c4 fix: 优化文件管理 2026-01-14 19:20:53 +08:00
renovate[bot] f26c722cab chore(deps): Update non-major dependencies to v66.6.0 (#1238)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-14 19:02:28 +08:00
耗子 87c936490e Merge remote-tracking branch 'origin/main' 2026-01-14 18:48:47 +08:00
耗子 3703c3cb5f feat: 优化ECC自签实现 2026-01-14 18:48:43 +08:00
renovate[bot] 958b479b8f chore(deps): Update dependency unplugin-icons to v23 (#1237)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-14 17:30:56 +08:00
耗子 bcecec3756 fix: lint 2026-01-14 03:29:35 +08:00
renovate[bot] 39fb75a531 chore(deps): Update dependency @types/node to v24.10.8 (#1234)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-14 03:20:48 +08:00
github-actions[bot] c0945610f8 New Crowdin translations by GitHub Action (#1236)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-14 03:20:12 +08:00
devhaozi 743936f29a chore(l10n): update pot files 2026-01-13 19:18:53 +00:00
devhaozi 8f85ac8c28 chore: update mocks 2026-01-13 19:16:39 +00:00
耗子 026969a5ce Merge remote-tracking branch 'origin/main' 2026-01-14 03:16:00 +08:00
耗子 d29e3d4d87 fix: 优化容器模版 2026-01-14 03:15:48 +08:00
devhaozi 458b805357 chore(l10n): update pot files 2026-01-13 18:53:38 +00:00
耗子 f745e33d98 fix: lint 2026-01-14 01:29:01 +08:00
耗子 762d5994ed fix: tests 2026-01-14 01:23:05 +08:00
耗子 63205d7238 fix: 修复一些问题 2026-01-14 01:20:18 +08:00
Copilot 2b697e95d6 feat: 为网站编辑添加自定义配置标签页 (#1235)
* Initial plan

* feat: add custom config tab for website editing

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* chore: remove unused variables in EditView.vue

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: address code review comments for custom config

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-14 00:51:54 +08:00
耗子 b86a83a6a4 fix: lint 2026-01-14 00:15:11 +08:00
耗子 af809f83db Merge remote-tracking branch 'origin/main' 2026-01-14 00:04:37 +08:00
耗子 af75adafb3 fix: Apache没导入全局配置 2026-01-14 00:04:33 +08:00
耗子 c9a00cc502 feat: 为生成的web服务器编辑添加请勿修改标识 2026-01-13 23:58:03 +08:00
devhaozi ff476aeed5 chore: update mocks 2026-01-13 15:33:28 +00:00
耗子 c07a60d1c8 feat: 初步实现compose template 2026-01-13 23:31:37 +08:00
Copilot 908509e06b feat: 添加项目默认目录配置及目录跳转功能 (#1233)
* Initial plan

* feat: 添加项目默认目录配置及目录跳转功能

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 为项目默认目录添加回退默认值

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-01-13 22:25:20 +08:00
Copilot 1f55c2448d feat: add NTP server configuration and manual sync server option (#1232)
* Initial plan

* feat: add NTP server configuration support for time sync

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: add system NTP server configuration with chrony and timesyncd support

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: improve NTP service restart error handling

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 优化ntp

* fix: logo跳转

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-13 21:24:58 +08:00
Copilot d885be3fea feat(settings): 禁止隐藏首页和设置页菜单 (#1231)
* Initial plan

* feat(settings): 禁止隐藏首页和设置页菜单

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 修复父子路由禁用状态覆盖问题

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* docs: 改进 forbiddenHiddenMenus 注释说明

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-01-13 20:45:22 +08:00
耗子 5304398216 feat: 日志清理按钮优化 2026-01-13 20:44:35 +08:00
耗子 d0b3c14421 fix: 修正日志样式 2026-01-13 19:32:03 +08:00
耗子 4dc21b5443 feat: 优化页面 2026-01-13 19:00:46 +08:00
耗子 a7e53fb02f feat: 优化页面 2026-01-13 18:54:39 +08:00
耗子 7a7bd7bd4f feat: 优化页面 2026-01-13 18:46:13 +08:00
耗子 2cf52e63d8 feat: 优化页面 2026-01-13 17:19:45 +08:00
耗子 7d9bb1b191 feat: 优化文件管理支持内联新建 2026-01-13 03:32:17 +08:00
github-actions[bot] f1eccd6e7e New Crowdin translations by GitHub Action (#1230)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-13 03:16:07 +08:00
renovate[bot] 01b627b3e2 chore(deps): Update non-major dependencies (#1229)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-13 03:13:40 +08:00
耗子 3acc9b81b1 Merge remote-tracking branch 'origin/main' 2026-01-13 03:06:37 +08:00
耗子 c280f8f603 fix: tests 2026-01-13 03:06:33 +08:00
devhaozi 7e46f6dda8 chore(l10n): update pot files 2026-01-12 19:04:31 +00:00
耗子 47b312eafb fix: tests 2026-01-13 03:03:29 +08:00
耗子 9fc3c70b9b fix: 修正日志样式 2026-01-13 03:00:47 +08:00
耗子 b341eddd04 feat: 支持apache 2026-01-13 02:55:28 +08:00
耗子 a236ca9db8 feat: 支持apache 2026-01-13 01:55:20 +08:00
耗子 06f6610d3b feat: 支持apache 2026-01-13 01:48:07 +08:00
耗子 02e0aef265 feat: apache 2026-01-13 00:39:34 +08:00
devhaozi cd85032ecb chore: update mocks 2026-01-12 15:31:53 +00:00
Copilot f2e41a3364 feat: 添加操作日志记录功能和前端日志查看页面 (#1227)
* Initial plan

* feat: 添加操作日志记录功能和前端日志查看页面

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 修复 HttpLog 中 IP 字符串处理的类型安全问题

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

* feat: 完善面板内日志记录,添加网站/数据库/项目/计划任务/备份CRUD日志

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 完善更多模块的日志记录(证书/SSH/Webhook/用户/设置/安全)

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 改进cert_dns删除方法,在删除前获取实体信息用于日志记录

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

* feat: 从session获取操作员ID并在前端显示用户名

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-12 23:31:22 +08:00
devhaozi 1012e5a246 chore: update mocks 2026-01-12 14:18:01 +00:00
耗子 bb28e5ef6d feat: app支持排序 2026-01-12 22:17:18 +08:00
Copilot 1e5181c88e feat: 面板工具箱新增日志清理工具 (#1228)
* Initial plan

* feat: 添加日志清理工具

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 完成日志清理工具实现

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 优化日志清理

* feat: 添加 Podman 支持并修复 MySQL 日志清理

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 优化日志清理

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-12 21:48:13 +08:00
耗子 e9b299f874 feat: 优化日志记录 2026-01-12 20:35:58 +08:00
耗子 d9837277ff feat: 监控显示优化 2026-01-12 19:34:59 +08:00
耗子 87420648b3 feat: 监控显示优化 2026-01-12 19:29:57 +08:00
耗子 58f303c751 feat: 优化AI配置 2026-01-12 18:04:03 +08:00
Copilot 8388cfe8bb feat(php): 添加 phpinfo() 快捷按钮功能 (#1226)
* Initial plan

* feat(php): 添加 phpinfo() 快捷按钮功能

- 后端:添加 PHPInfo 服务方法调用 php -r "phpinfo();"
- 后端:添加 /environment/php/{version}/phpinfo 路由
- 前端:添加 phpinfo API 调用
- 前端:在 PHP 运行状态页面添加"查看 PHPInfo"按钮
- 前端:添加弹窗展示 phpinfo 输出内容
- 添加中英文及繁体中文翻译

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 添加 phpinfo 请求的错误处理

添加错误回调函数,在请求失败时关闭弹窗并显示错误提示

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 使用 php-cgi 输出 HTML 格式的 phpinfo

- 后端:使用 php-cgi -q 执行 phpinfo 获取 HTML 格式输出
- 前端:使用 v-html 渲染 HTML 内容
- 前端:移除不必要的 onError 处理
- 前端:优化 phpinfo HTML 样式

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: PHP支持查看phpinfo

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-12 18:02:26 +08:00
github-actions[bot] 4ad5c40c61 New Crowdin translations by GitHub Action (#1225)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-12 17:06:02 +08:00
devhaozi 35587c9eca chore(l10n): update pot files 2026-01-12 09:03:45 +00:00
github-actions[bot] 009d618cda New Crowdin translations by GitHub Action (#1224)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-12 16:55:49 +08:00
devhaozi 4895f2e85b chore(l10n): update pot files 2026-01-12 08:30:23 +00:00
renovate[bot] 82be478ba5 chore(deps): Lock file maintenance (#1223)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-12 04:43:23 +00:00
耗子 7a118d1318 feat: 优化字体设置 2026-01-12 07:15:10 +08:00
耗子 a5a396f60e feat: 阶段提交 2026-01-12 07:09:33 +08:00
耗子 794a620113 feat: 阶段提交 2026-01-12 06:50:01 +08:00
耗子 b923628630 feat: 阶段提交 2026-01-12 06:42:30 +08:00
耗子 31f39555ab feat: 阶段提交 2026-01-12 06:05:08 +08:00
耗子 fe90dd3dc8 feat: 阶段提交 2026-01-12 05:24:53 +08:00
耗子 c3054dcb91 fix: 解决连接中断后命令不被杀死的问题 2026-01-12 04:16:53 +08:00
Copilot ddfcd3e45c fix: 修复 PTY Websocket 客户端断开连接后命令仍在后台运行的问题 (#1222)
* Initial plan

* fix: 修复 PTY Websocket 客户端断开连接后命令仍在后台运行的问题

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 解决连接中断后命令不被杀死的问题

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-12 04:13:04 +08:00
github-actions[bot] f9cda73981 New Crowdin translations by GitHub Action (#1221)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-11 23:49:18 +08:00
devhaozi 3a1149b0f5 chore(l10n): update pot files 2026-01-11 15:37:49 +00:00
Copilot 2f63523540 feat(file): 文件管理多项改进 - 权限显示、双击交互、终端集成、图标视图 (#1220)
* Initial plan

* feat(file): 实现文件管理多项改进功能

1. 修复权限弹窗中所有者和组固定显示 www 的问题
2. 目录单击进入,文件双击打开(编辑/预览)
3. 点击权限/所有组列直接打开编辑弹窗
4. 压缩包双击自动弹出解压窗口
5. 预留视图切换按钮(列表/图标视图)
6. 工具栏新增终端按钮
7. 右键菜单新增终端选项(仅目录)
8. 右键菜单新增属性选项
9. 新增显示/隐藏隐藏文件按钮(刷新按钮旁边)

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 完善后端 Info 接口返回完整文件信息并修复代码审查问题

1. 后端 /file/info 接口现在返回 owner、group、uid、gid 等完整信息
2. 修复权限弹窗 mode 处理逻辑,确保正确处理边界情况
3. 优化视图切换逻辑,避免不必要的状态变更
4. 提取 openFile 函数复用代码,减少重复逻辑

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 添加类型断言检查和提取 normalizeMode 辅助函数

1. 后端 Info 方法添加 syscall.Stat_t 类型断言安全检查
2. 前端 PermissionModal 提取 normalizeMode 函数消除代码重复

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat(file): 实现图标视图和鼠标框选功能

1. 新增 GridView.vue 组件实现图标视图
2. 支持鼠标拖动框选批量选择文件/目录
3. 支持 Ctrl/Cmd+点击多选和 Shift+点击范围选择
4. 更新 store 添加 viewType 状态持久化
5. 更新 ToolBar 和 IndexView 支持视图切换

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

* feat: 完善切换逻辑

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-11 23:36:44 +08:00
耗子 9cbef88984 feat: 优化防火墙创建规则 2026-01-11 18:58:28 +08:00
Copilot 8031e53852 feat: 添加容器编排实时进度显示功能 (#1217)
* Initial plan

* feat: 添加容器编排实时进度显示功能

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 修复代码审查问题,使用动态路径和安全的字体大小操作

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* docs: 完成容器编排实时进度显示功能

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 容器优化

* fix: 用户关闭命令窗口时杀死正在运行的进程

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* refactor: 改进 WebSocket 断开检测,支持用户输入转发到 PTY

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 启用终端用户输入支持,转发键盘输入到 PTY

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 容器优化

* fix: lint

* feat: 添加 PTY 窗口大小调整支持

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 支持读取输入

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-11 18:37:01 +08:00
devhaozi b5203b194a chore: update mocks 2026-01-10 21:31:26 +00:00
Copilot 3d8623a9f9 feat: 添加容器终端功能 (#1216)
* Initial plan

* feat: 添加容器终端功能 - 通过 WebSocket 进入容器执行命令

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 将 fmt.Errorf 错误信息改为英语

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 前端优化

* feat: 容器优化

* feat: 前端优化

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-11 05:30:52 +08:00
github-actions[bot] 35d398a4b7 New Crowdin translations by GitHub Action (#1215)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-11 02:14:33 +08:00
devhaozi d210a47f84 chore(l10n): update pot files 2026-01-10 18:13:29 +00:00
renovate[bot] eb164e35a3 chore(deps): Update dependency @types/node to v24.10.7 (#1214)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-11 02:10:30 +08:00
github-actions[bot] 787263ecb7 New Crowdin translations by GitHub Action (#1213)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-11 02:08:29 +08:00
devhaozi 4dd203b040 chore(l10n): update pot files 2026-01-10 18:06:43 +00:00
耗子 b91038599c Merge remote-tracking branch 'origin/main' 2026-01-11 02:05:45 +08:00
耗子 f068ccb627 fix: 不自动刷新任务标志 2026-01-11 02:05:28 +08:00
devhaozi 8161996a68 chore(l10n): update pot files 2026-01-10 17:53:18 +00:00
耗子 fc3b508b07 feat: 项目日志查看 2026-01-11 01:47:16 +08:00
耗子 2a62947664 Merge remote-tracking branch 'origin/main' 2026-01-11 01:33:42 +08:00
耗子 59870c8f09 feat: 编辑项目 2026-01-11 01:33:38 +08:00
devhaozi e6d8f50aa3 chore: update mocks 2026-01-10 17:18:38 +00:00
耗子 4e54fb589f Merge remote-tracking branch 'origin/main' 2026-01-11 01:17:56 +08:00
耗子 b85ca6fd70 feat: 项目管理完成 2026-01-11 01:17:52 +08:00
耗子 6ea1295f86 feat: 项目管理阶段2 2026-01-11 00:34:20 +08:00
devhaozi 584cd74806 chore: update mocks 2026-01-10 16:08:56 +00:00
耗子 035046449d feat: 项目管理阶段1 2026-01-11 00:08:12 +08:00
github-actions[bot] d8ccc9d549 New Crowdin translations by GitHub Action (#1212)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-10 22:34:13 +08:00
耗子 1178d45b1d feat: 新增使用RSA的自签函数 2026-01-10 21:44:44 +08:00
devhaozi deed5e8d90 chore(l10n): update pot files 2026-01-10 13:01:53 +00:00
耗子 7fbb7e7097 Merge remote-tracking branch 'origin/main' 2026-01-10 21:00:21 +08:00
耗子 1846caf388 feat: 前端优化 2026-01-10 21:00:04 +08:00
devhaozi f4e3296389 chore(l10n): update pot files 2026-01-10 12:46:29 +00:00
耗子 ee006282df feat: 前端优化 2026-01-10 20:44:11 +08:00
耗子 3fa62ba0b1 fix: 神奇的bug 2026-01-10 20:31:37 +08:00
耗子 2a1b5356dc fix: 前端优化 2026-01-10 20:06:29 +08:00
github-actions[bot] a890a20768 New Crowdin translations by GitHub Action (#1211)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-10 19:41:06 +08:00
devhaozi bfcfbcd861 chore(l10n): update pot files 2026-01-10 11:31:00 +00:00
耗子 6c66aadb9b Merge remote-tracking branch 'origin/main' 2026-01-10 19:29:36 +08:00
耗子 7bef2f34c0 feat: 首页添加任务状态 2026-01-10 19:29:32 +08:00
renovate[bot] 2a77a0399e chore(deps): Update non-major dependencies (#1208)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-10 19:14:57 +08:00
耗子 1d2ba12696 fix: 前端优化 2026-01-10 18:52:39 +08:00
耗子 abe1b709c9 feat: 优化证书生成 2026-01-10 18:51:09 +08:00
Copilot 91cf5c80bf feat: 添加 Nginx Stream 支持 (#1210)
* Initial plan

* feat: 添加 Nginx Stream 支持 (Server 和 Upstream)

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 优化

* fix: 前端优化

* feat: 优化

* feat: 优化

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-10 18:33:04 +08:00
耗子 295936d792 fix: 前端优化 2026-01-10 16:08:14 +08:00
github-actions[bot] 88c9150c5c New Crowdin translations by GitHub Action (#1207)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-10 06:07:04 +08:00
devhaozi dbd581d8a6 chore(l10n): update pot files 2026-01-09 22:04:28 +00:00
耗子 1329069246 fix: 前端优化 2026-01-10 06:03:22 +08:00
devhaozi d345f5a235 chore(l10n): update pot files 2026-01-09 21:48:10 +00:00
耗子 9aa951e247 feat: 添加系统支持及EOL检测 2026-01-10 05:46:53 +08:00
Copilot 01a228f3ad feat: 实现登录验证码和安全入口错误页伪装功能 (#1206)
* Initial plan

* feat: 实现登录验证码和安全入口错误页伪装功能

- 后端:添加登录验证码功能(密码错误3次后触发)
- 后端:支持3种安全入口错误页伪装(418/nginx/close)
- 后端:添加验证码API和更新设置项
- 前端:登录页支持验证码输入和刷新
- 前端:设置页添加登录验证码和错误页伪装选项

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 修复代码审查问题

- hijack失败时回退到418错误页而非返回200
- 验证码输入去除空格

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 优化细节

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-10 05:21:09 +08:00
耗子 e9c0190a49 fix: nginx listen 2026-01-10 03:27:07 +08:00
github-actions[bot] 8e71d28f78 New Crowdin translations by GitHub Action (#1205)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-10 03:18:42 +08:00
devhaozi 1dca403677 chore(l10n): update pot files 2026-01-09 19:10:48 +00:00
耗子 0d428f885b fix: 修正日志目录权限 2026-01-10 03:09:34 +08:00
耗子 4c59ab79c9 fix: 修正日志目录权限 2026-01-10 03:04:43 +08:00
耗子 70f41b1678 fix: 优化权限 2026-01-10 02:57:32 +08:00
耗子 ea240a9350 fix: 优化权限 2026-01-10 02:54:53 +08:00
耗子 0deeb281ea fix: 面板更新 2026-01-10 00:40:50 +08:00
耗子 cdc0ac72ae fix: 僵尸进程问题 2026-01-10 00:32:20 +08:00
耗子 a6ab9b24fa fix: vhost问题 2026-01-10 00:16:07 +08:00
耗子 3bec347a8e fix: 进程管理器刷新报错 2026-01-09 23:59:32 +08:00
耗子 726f98c164 fix: 同步环境的缓存 2026-01-09 23:50:27 +08:00
耗子 ff860a8c4e fix: 服务名 2026-01-09 23:31:48 +08:00
耗子 7a575267bb fix: 网站类型切换 2026-01-09 22:00:15 +08:00
github-actions[bot] 5ceec97412 New Crowdin translations by GitHub Action (#1204)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-09 21:56:25 +08:00
devhaozi 082701b2d8 chore(l10n): update pot files 2026-01-09 13:50:41 +00:00
耗子 52df9c8955 fix: 修复一些遗留问题 2026-01-09 21:48:21 +08:00
耗子 97a15b7dd4 Merge remote-tracking branch 'origin/main' 2026-01-09 21:38:32 +08:00
耗子 70c940569c fix: 修复一些遗留问题 2026-01-09 21:38:28 +08:00
devhaozi d5017376d3 chore(l10n): update pot files 2026-01-09 13:22:23 +00:00
耗子 f1657daebb feat: 优化上游设置 2026-01-09 21:08:31 +08:00
耗子 77cb594ee4 feat: 优化上游设置 2026-01-09 20:59:34 +08:00
耗子 cdb1296462 feat: 阶段提交 2026-01-09 19:20:55 +08:00
耗子 0d54c36eb6 Merge remote-tracking branch 'origin/main' 2026-01-09 18:04:17 +08:00
耗子 3168e6efe3 feat: 网站默认设置 2026-01-09 18:04:13 +08:00
devhaozi bf04648c64 chore: update mocks 2026-01-09 09:13:37 +00:00
耗子 f48c975de3 fix: 修正环境检查 2026-01-09 17:12:57 +08:00
耗子 1d41b2c61c fix: 优化 2026-01-09 15:55:49 +08:00
耗子 b23d32565b fix: lint 2026-01-09 15:50:44 +08:00
Copilot a36c9344af feat: 操作 immutable 标识的文件进行提醒 (#1203)
* Initial plan

* feat: 添加 immutable 字段到文件列表返回结果中

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 前端添加 immutable 文件提醒弹窗功能

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* refactor: 移除未使用的 renameModel.immutable 字段

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-09 15:42:30 +08:00
耗子 243f89f619 fix: lint 2026-01-09 06:53:55 +08:00
github-actions[bot] 752f70aa6b New Crowdin translations by GitHub Action (#1199)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-09 06:48:27 +08:00
devhaozi 01a24374b7 chore(l10n): update pot files 2026-01-08 22:44:50 +00:00
Copilot 401b85b87e feat: add on-demand folder size calculation in file manager (#1201)
* Initial plan

* feat: add folder size calculation feature with "Calculate" link

- Backend: Add Size API to calculate directory size
- Backend: Modify formatDir to return empty size for directories
- Frontend: Show "Calculate" link for directories instead of size
- Frontend: Add loading spinner during calculation
- Frontend: Cache calculated sizes until path changes
- Add translations for Calculate and Failed to calculate size

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: lint

* fix: 优化

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-09 06:43:26 +08:00
Copilot 47537e282b feat(dns): 优化系统工具箱DNS设置以适配现代Linux系统 (#1202)
* Initial plan

* feat(dns): 优化系统工具箱DNS设置以适配现代Linux系统

- 创建 pkg/dns 包实现多种DNS管理方式
- 支持 NetworkManager (RHEL 9.x/10.x)
- 支持 netplan (Debian 12+/Ubuntu 22+)
- 回退到直接修改 /etc/resolv.conf
- 更新前端显示当前DNS管理方式
- 添加单元测试

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 修复代码审查发现的问题

- 提取 shell 参数转义逻辑到独立函数
- 修正 netplan 配置文件选择的注释说明
- 使用常量替代硬编码的缩进值
- 添加前端空数组安全检查

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 优化

* fix: 优化

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-09 06:33:23 +08:00
Copilot f2d3911266 feat: 支持隐藏菜单和自定义Logo长期保存 (#1200)
* Initial plan

* feat: 支持隐藏菜单和自定义Logo长期保存

- 后端:在 SettingPanel 结构体中添加 HiddenMenu 和 CustomLogo 字段
- 后端:在 GetPanel 和 UpdatePanel 方法中处理新字段的获取和保存
- 后端:修改 Panel 接口返回 hidden_menu 和 custom_logo 给前端初始化
- 前端:在基本设置页面添加隐藏菜单和自定义 Logo 设置项
- 前端:从侧边栏设置组件中移除弹窗,只保留菜单折叠按钮
- 前端:初始化时从服务端获取并应用隐藏菜单和自定义 Logo 设置
- 前端:调整 store 的 persist 配置,不再将这两个设置保存到本地存储

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* 代码审查完成

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: 优化样式

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-09 05:49:09 +08:00
devhaozi 9baeaa47e0 chore(l10n): update pot files 2026-01-08 21:09:09 +00:00
耗子 a5f609e23f fix: 翻译 2026-01-09 05:07:29 +08:00
耗子 caa8448104 fix: 翻译 2026-01-09 05:04:27 +08:00
耗子 84f3a18074 feat: 优化ssh 2026-01-09 05:00:35 +08:00
github-actions[bot] 61b2a70e84 New Crowdin translations by GitHub Action (#1198)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-09 04:33:30 +08:00
devhaozi 710d38905f chore(l10n): update pot files 2026-01-08 20:26:31 +00:00
耗子 a4cb2d2dd1 fix: 翻译 2026-01-09 04:24:58 +08:00
github-actions[bot] f0cbd9b9b8 New Crowdin translations by GitHub Action (#1197)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-09 04:21:33 +08:00
devhaozi e8ed4215d0 chore(l10n): update pot files 2026-01-08 20:19:26 +00:00
github-actions[bot] 1b509fc9ad New Crowdin translations by GitHub Action (#1196)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-09 04:17:27 +08:00
devhaozi 2856068b90 chore(l10n): update pot files 2026-01-08 20:11:22 +00:00
耗子 2b1f6b3e0a Merge remote-tracking branch 'origin/main' 2026-01-09 04:09:36 +08:00
耗子 8a80182d2a fix: 翻译 2026-01-09 04:09:32 +08:00
devhaozi 0625ccfabe chore(l10n): update pot files 2026-01-08 19:24:04 +00:00
Copilot 54b3b60efd feat: 添加磁盘管理工具到工具箱 (#1195)
* Initial plan

* 实现磁盘管理工具的后端和前端基础功能

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* 完成磁盘管理工具功能实现并验证构建成功

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* 添加输入验证防止命令注入攻击

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* 移除命令注入验证并修复评审意见

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* feat: merge main

* feat: merge main

* feat: 分区优化

* feat: fstab管理

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-01-09 03:22:35 +08:00
耗子 24312f5993 feat: 添加验证 2026-01-09 01:42:53 +08:00
耗子 31a22389d1 feat: 进程管理器优化 2026-01-09 01:37:16 +08:00
耗子 de5e28ca81 feat: 进程管理器优化 2026-01-09 01:28:48 +08:00
耗子 585cc5604a fix: lint 2026-01-09 01:04:38 +08:00
Copilot 874561a9d1 feat: 进程管理增强 - 信号发送、排序筛选、搜索和右键菜单 (#1194)
* Initial plan

* feat: 实现进程管理增强功能 - 信号发送、排序筛选、搜索和右键菜单

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* fix: 修复代码审查问题并删除遗留文件 task/SystemView.vue

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-01-09 00:47:01 +08:00
耗子 7d5a0ac1c0 feat: ssh管理, close #350 2026-01-09 00:46:40 +08:00
耗子 8e31361eaf fix: lint 2026-01-08 23:29:53 +08:00
Copilot cff71af26b feat(frp): 添加运行用户设置功能 (#1193)
* Initial plan

* feat(frp): 添加运行用户设置功能

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* refactor(frp): 优化正则表达式和用户/组更新逻辑

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

* 完成 FRP 运行用户设置功能

Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
2026-01-08 23:27:54 +08:00
耗子 89ace2360c feat: ssh支持私钥密码,close #774 2026-01-08 23:24:04 +08:00
耗子 3afc00be2f Merge remote-tracking branch 'origin/main' 2026-01-08 23:07:45 +08:00
耗子 8aaf6f1377 feat: 添加ai说明 2026-01-08 23:07:41 +08:00
devhaozi f0e9543bf1 chore: update mocks 2026-01-08 15:05:18 +00:00
耗子 017604ec11 Merge remote-tracking branch 'origin/main' 2026-01-08 23:04:41 +08:00
耗子 ab6e0903f5 feat: 支持 webhook, close #695 2026-01-08 23:04:37 +08:00
devhaozi eba8392451 chore: update mocks 2026-01-08 14:09:56 +00:00
耗子 978c9b2fc3 feat: acme ari支持,close #1192 2026-01-08 22:09:08 +08:00
耗子 2d525e9680 feat: 完善证书设置 2026-01-08 21:26:31 +08:00
耗子 2099d2ca57 fix: 使用短期证书配置 2026-01-08 19:28:06 +08:00
耗子 5e3ec29076 feat: 优化证书逻辑 2026-01-08 18:39:32 +08:00
耗子 ce84087702 Merge remote-tracking branch 'origin/main' 2026-01-08 18:17:13 +08:00
耗子 855ed52a8b feat: 优化证书逻辑 2026-01-08 18:17:09 +08:00
devhaozi 984820ee12 chore: update mocks 2026-01-08 10:03:59 +00:00
耗子 d850292622 feat: 面板ip证书 2026-01-08 18:03:23 +08:00
耗子 b3c546dc13 feat: 支持MariaDB 2026-01-08 04:20:44 +08:00
耗子 7940b50a72 feat: PHP环境支持管理 2026-01-08 01:43:21 +08:00
耗子 45616b4ab3 feat: 添加一些默认设置 2026-01-07 23:47:23 +08:00
renovate[bot] ffc67b945d chore(deps): Update dependency vite to v7.3.1 (#1191)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-07 14:29:55 +00:00
耗子 554a7dfbd7 fix: 首页图标渲染 2026-01-07 04:01:16 +08:00
github-actions[bot] 647f9a530d New Crowdin translations by GitHub Action (#1190)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-01-07 03:47:54 +08:00
devhaozi 245a120572 chore(l10n): update pot files 2026-01-06 19:40:09 +00:00
耗子 f0ecfbe883 Merge remote-tracking branch 'origin/main' 2026-01-07 03:37:48 +08:00
耗子 718a4f566d fix: lint 2026-01-07 03:37:45 +08:00
devhaozi 4a7795b4b7 chore: update mocks 2026-01-06 19:35:56 +00:00
耗子 7a5eb3210c fix: 修复安装问题 2026-01-07 03:35:21 +08:00
devhaozi 0bc2585824 chore: update mocks 2026-01-06 17:20:58 +00:00
耗子 edadc9b045 feat: 初步支持环境管理 2026-01-07 01:20:08 +08:00
renovate[bot] 9f52de654a chore(deps): Update non-major dependencies (#1189)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-06 05:36:01 +00:00
耗子 b5dc070c9c fix: rsync secrets换行符写入不正确,close #1188 2026-01-05 21:58:41 +08:00
耗子 7b2d6a980b feat: 添加web服务器卸载提示 2026-01-05 21:48:44 +08:00
renovate[bot] 3d107fe548 chore(deps): Lock file maintenance (#1187)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-05 05:14:37 +00:00
renovate[bot] ccf0ee3a22 chore(deps): Update dependency md-editor-v3 to v6.3.1 (#1186)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-04 18:30:14 +00:00
耗子 9d200f3ce7 Merge remote-tracking branch 'origin/main' 2026-01-04 18:06:32 +08:00
耗子 15364635fe feat: 模版初始化 2026-01-04 18:06:09 +08:00
devhaozi ffa53e93fe chore: update mocks 2026-01-04 09:54:26 +00:00
耗子 0b1d2a570b feat: 应用支持分类筛选 2026-01-04 17:53:45 +08:00
耗子 e6fcd48e32 fix: lint 2026-01-04 04:57:03 +08:00
耗子 61b74b31d9 feat: 优化包大小 2026-01-04 04:47:56 +08:00
耗子 b65a4249cb feat: 优化包大小 2026-01-04 03:31:54 +08:00
耗子 da25dc2950 feat: 优化包大小 2026-01-04 03:31:34 +08:00
耗子 f5709e735f feat: 补全logo 2026-01-04 01:29:42 +08:00
github-actions[bot] b2632535dd New Crowdin translations by GitHub Action (#1185)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-12-31 18:05:10 +08:00
devhaozi 46b07f7903 chore(l10n): update pot files 2025-12-31 09:40:51 +00:00
耗子 5ed874f407 feat: 新的Cron表达式生成器 2025-12-31 17:39:48 +08:00
耗子 7b2b740dd9 Merge remote-tracking branch 'origin/main' 2025-12-31 17:38:34 +08:00
耗子 80fde60526 feat: 新的Cron表达式生成器 2025-12-31 17:38:08 +08:00
renovate[bot] d6718dac13 chore(deps): Update non-major dependencies (#1184)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-31 01:48:48 +00:00
耗子 b86fc187d7 feat: 使用全新的方式加载monaco 2025-12-31 02:49:12 +08:00
耗子 9530a06da5 feat: 移除几个组件,待重构 2025-12-31 01:46:27 +08:00
耗子 0ecbe3da0e feat: 优化压缩 2025-12-31 01:40:01 +08:00
耗子 5e7093c8ec refactor: 移除monaco-editor,改为ace-editor 2025-12-31 00:30:32 +08:00
耗子 9733c6bbbd feat: 优化logo icon加载,close #1168 2025-12-30 22:18:45 +08:00
耗子 db80cca9dc fix: 修复PHP设置Cli不生效 2025-12-30 16:23:13 +08:00
renovate[bot] 4b7eb49415 chore(deps): Update dependency @iconify-json/simple-icons to v1.2.64 (#1182)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-29 14:11:13 +00:00
renovate[bot] 74a2fbb7e0 chore(deps): Lock file maintenance (#1181)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-29 05:14:40 +00:00
renovate[bot] 74b98202ec chore(deps): Update dependency md-editor-v3 to v6.3.0 (#1180)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-28 17:14:43 +00:00
renovate[bot] 132d8e1201 chore(deps): Update non-major dependencies to v66.5.11 (#1179)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-26 08:52:00 +00:00
renovate[bot] 1b365daa55 chore(deps): Update module github.com/aws/aws-sdk-go-v2/service/s3 to v1.95.0 (#1177)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-24 02:07:26 +00:00
renovate[bot] 0a5861b617 chore(deps): Update pnpm to v10.26.2 (#1176)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-24 02:35:57 +08:00
耗子 50b1d39c5c fix: 禁用ipv6 2025-12-23 21:08:20 +08:00
renovate[bot] 96a25f657c chore(deps): Update non-major dependencies (#1174)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 19:21:02 +08:00
renovate[bot] 008c2eb716 chore(deps): Update dependency @xterm/xterm to v6 (#1175)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 19:15:49 +08:00
renovate[bot] c1622050fd chore(deps): Lock file maintenance (#1173)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-22 06:42:32 +00:00
renovate[bot] 338ea9dbba chore(deps): Update dependency vue-tsc to v3.2.0 (#1172)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-20 21:46:52 +00:00
renovate[bot] 89f103dac6 chore(deps): Update dependency sass to v1.97.1 (#1171)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-20 05:10:40 +00:00
renovate[bot] 1545246eb0 chore(deps): Update non-major dependencies (#1170)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-19 18:28:22 +00:00
renovate[bot] 14cb1c2576 chore(deps): Update dependency vue to v3.5.26 (#1169)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-18 17:04:49 +00:00
耗子 e60caaee35 fix: logo太大 2025-12-18 16:24:08 +08:00
耗子 40cc33e61e feat: 实现图标本地加载
目前会导致打包体积失控,但是没有找到更好的方案。
2025-12-18 04:33:15 +08:00
renovate[bot] 93c1ecc6a3 chore(deps): Update non-major dependencies (#1167)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-17 14:02:23 +00:00
耗子 4794872a9b feat: 首页状态弹窗支持滚动 2025-12-17 02:30:03 +08:00
renovate[bot] 3f3d83de16 chore(deps): Update dependency sass to v1.97.0 (#1166)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-16 20:46:20 +08:00
renovate[bot] 87494e0a92 chore(deps): Update non-major dependencies (#1165)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-16 06:34:04 +08:00
耗子 616ae77ef0 fix: 按需设置cookie 2025-12-16 03:51:48 +08:00
耗子 a328c29870 fix: build 2025-12-16 03:49:14 +08:00
renovate[bot] a277a5d214 chore(deps): Replace dependency @tsconfig/node22 with @tsconfig/node24 (#1164)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-16 03:48:36 +08:00
renovate[bot] 4bb03d29f7 chore(deps): Update non-major dependencies (#1163)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-15 23:56:23 +08:00
renovate[bot] bcb2040008 chore(deps): Lock file maintenance (#1162)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-15 05:23:42 +00:00
renovate[bot] ba4ca99ee7 chore(deps): Update non-major dependencies (#1161)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-14 23:21:26 +08:00
renovate[bot] 7ad3e6f598 chore(deps): Update dependency @types/node to v24.10.4 (#1160)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-14 04:45:06 +00:00
renovate[bot] f206a03e04 chore(deps): Update dependency eslint to v9.39.2 (#1159)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-13 13:18:20 +08:00
renovate[bot] 51a0c91d60 chore(deps): Update GitHub Artifact Actions (#1158)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-13 13:18:10 +08:00
renovate[bot] 68130dd440 chore(deps): Update dependency @vitejs/plugin-vue to v6.0.3 (#1157)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-12 21:34:13 +08:00
renovate[bot] 5b1b716cdd chore(deps): Update non-major dependencies (#1156)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-12 04:38:23 +00:00
耗子 cc210be717 chore: 添加ESA活动 2025-12-11 17:59:11 +08:00
耗子 2ef8040c3e Merge remote-tracking branch 'origin/main' 2025-12-11 17:58:30 +08:00
耗子 3f75b68c1d chore: 添加ESA活动 2025-12-11 17:58:26 +08:00
renovate[bot] ea709f22ab chore(deps): Update dependency @types/node to v24.10.3 (#1155)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-10 21:03:43 +00:00
renovate[bot] c8d223d9ac chore(deps): Update non-major dependencies (#1154)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-10 06:27:08 +00:00
renovate[bot] a77d14fa76 chore(deps): Update module github.com/libdns/alidns to v1.0.6-beta.3 (#1153)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 14:02:40 +00:00
renovate[bot] 696853181f chore(deps): Update non-major dependencies (#1150)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 13:04:30 +08:00
耗子 ce5074b254 chore: update dependence 2025-12-09 01:29:35 +08:00
耗子 4694db2242 Merge remote-tracking branch 'origin/main' 2025-12-09 01:22:39 +08:00
耗子 899040ed25 chore: update dependence 2025-12-09 01:22:35 +08:00
renovate[bot] 2a0cb99141 chore(deps): Lock file maintenance (#1152)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-08 05:50:23 +00:00
耗子 9d3d4c49c1 fix: ai suggestions 2025-12-04 14:55:14 +08:00
耗子 3c19d8ed9a fix: ai suggestions 2025-12-04 14:51:59 +08:00
Catsayer 2c85070921 refactor: 消除 database_user.go 中的重复代码 (#1149) 2025-12-04 14:43:02 +08:00
耗子 aa181ababc feat: 同步后端修改 2025-12-04 13:33:54 +08:00
耗子 500cc1845a fix: 优化指令排序 2025-12-04 00:35:12 +08:00
耗子 91c3fc3951 feat: 调整更多指令到独立配置 2025-12-04 00:26:35 +08:00
耗子 f70dd51e29 feat: 调整指令排序 2025-12-03 23:46:11 +08:00
耗子 9319226fd8 feat: 格式化配置文件 2025-12-03 22:30:34 +08:00
耗子 62bd8927e3 feat: 优化网站启停 2025-12-03 22:00:35 +08:00
耗子 2ed9d0b6fa feat: 优化网站启停 2025-12-03 21:55:28 +08:00
耗子 e1f0a7d04f feat: 优化网站启停 2025-12-03 21:37:13 +08:00
耗子 9bd08fcb37 fix: lint 2025-12-03 20:49:23 +08:00
耗子 b46aaf4313 chore: add CONTRIBUTING.md 2025-12-03 20:10:41 +08:00
耗子 8c5f441561 fix: tests 2025-12-03 19:25:02 +08:00
耗子 bf2ae3c4fd fix: tests 2025-12-03 19:23:11 +08:00
耗子 9fe1776cea fix: tests 2025-12-03 19:18:42 +08:00
耗子 03766381df fix: tests 2025-12-03 19:17:42 +08:00
耗子 d583aa68fa feat: 重命名配置目录 2025-12-03 17:54:03 +08:00
renovate[bot] b382d598f6 chore(deps): Update non-major dependencies (#1147)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 16:36:54 +08:00
renovate[bot] b1a75b954c chore(deps): Update module github.com/ncruces/go-sqlite3 to v0.30.3 (#1146)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-02 06:24:12 +00:00
github-actions[bot] 05fc80c1df New Crowdin translations by GitHub Action (#1145)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-12-02 02:12:36 +08:00
耗子 d520aab8ef Merge remote-tracking branch 'origin/main' 2025-12-02 02:07:34 +08:00
耗子 e331b308cf fix: tests 2025-12-02 02:07:31 +08:00
devhaozi 28b354b2f8 chore(l10n): update pot files 2025-12-01 18:04:48 +00:00
耗子 4ff1c865ed fix: tests 2025-12-02 02:00:58 +08:00
耗子 b8fa605752 Merge remote-tracking branch 'origin/main' 2025-12-02 01:54:21 +08:00
耗子 16cb85b07f fix: tests 2025-12-02 01:54:18 +08:00
github-actions[bot] e70b5451f6 New Crowdin translations by GitHub Action (#1144)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-12-02 01:47:58 +08:00
耗子 d8e0957f79 Merge remote-tracking branch 'origin/main' 2025-12-02 01:47:38 +08:00
耗子 d90097994e feat: 网站重构3 2025-12-02 01:47:34 +08:00
devhaozi ffd5a775dc chore(l10n): update pot files 2025-12-01 15:34:00 +00:00
耗子 aaf3fe30eb Merge remote-tracking branch 'origin/main' 2025-12-01 23:32:04 +08:00
耗子 0ebd1c0699 feat: 网站重构2 2025-12-01 23:31:59 +08:00
devhaozi c9e2870ffe chore: update mocks 2025-12-01 14:50:50 +00:00
耗子 2b8890305c feat: 网站重构1 2025-12-01 22:50:16 +08:00
renovate[bot] b17b7ccfee chore(deps): Update dependency @iconify/json to v2.2.413 (#1143)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 21:33:01 +08:00
耗子 748c82af1d feat: 重构monaco-editor加载 2025-12-01 21:05:09 +08:00
github-actions[bot] 8598f042a2 New Crowdin translations by GitHub Action (#1142)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-12-01 20:00:26 +08:00
devhaozi d75f6b61d2 chore(l10n): update pot files 2025-12-01 11:49:38 +00:00
耗子 7d10e52708 feat: 一堆调整 2025-12-01 19:37:30 +08:00
耗子 5fdce07190 feat: 一堆调整 2025-12-01 19:37:19 +08:00
耗子 a64a1ba5c8 feat: 优化菜单排序 2025-12-01 18:40:15 +08:00
耗子 6c2b0c2490 feat: 修改目录 2025-12-01 17:01:50 +08:00
renovate[bot] 8b69dcde0e chore(deps): Update dependency vite to v7.2.6 (#1141)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 16:43:58 +08:00
耗子 c2508e0170 Merge remote-tracking branch 'origin/main' 2025-12-01 16:36:24 +08:00
耗子 af3c10f6c6 fix: lint 2025-12-01 16:36:20 +08:00
renovate[bot] 35771fe945 chore(deps): Lock file maintenance (#1140)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 16:30:06 +08:00
耗子 027eb73ab4 fix: lint 2025-12-01 16:29:37 +08:00
耗子 bd453daed9 feat: 配置解析器优化 2025-12-01 16:21:27 +08:00
耗子 b4bd840781 Merge remote-tracking branch 'origin/main' 2025-12-01 14:19:20 +08:00
耗子 e305640817 feat: 新的配置解析器 2025-12-01 14:19:15 +08:00
renovate[bot] ba7f7e1c53 chore(deps): Update dependency tsx to v4.21.0 (#1139)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 21:39:12 +00:00
renovate[bot] ed2582876d chore(deps): Update non-major dependencies (#1138)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 05:24:22 +08:00
renovate[bot] 3641f30443 chore(deps): Update dependency @iconify/json to v2.2.412 (#1137)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-29 13:53:02 +00:00
renovate[bot] ffc4ef1790 chore(deps): Update non-major dependencies (#1134)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-29 00:56:58 +00:00
renovate[bot] 58f98f8c16 chore(deps): Update module github.com/hashicorp/go-version to v1.8.0 (#1130)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-28 12:57:49 +00:00
renovate[bot] 6f8219d1a7 chore(deps): Update dependency eslint-plugin-vue to v10.6.2 (#1129)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-28 05:11:25 +00:00
耗子 79828f57c9 fix: lint 2025-11-28 04:22:42 +08:00
renovate[bot] 445a38cbc9 chore(deps): Update non-major dependencies (#1128)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-28 04:14:08 +08:00
renovate[bot] ad85dfee69 chore(deps): Update non-major dependencies (#1127)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-27 06:05:21 +00:00
renovate[bot] a2f3b25728 chore(deps): Update non-major dependencies to v0.30.2 (#1126)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 21:52:55 +00:00
renovate[bot] 27c9baf5b8 chore(deps): Update dependency eslint-plugin-vue to v10.6.1 (#1125)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 08:44:04 +00:00
renovate[bot] d2c80305f5 chore(deps): Update non-major dependencies (#1124)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-26 01:48:24 +00:00
renovate[bot] 2152064cf7 chore(deps): Update non-major dependencies (#1123)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-24 14:06:10 +00:00
renovate[bot] 042a6798fe chore(deps): Lock file maintenance (#1122)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-24 04:41:49 +00:00
耗子 b11fa7e613 feat: 更新描述 2025-11-23 18:16:11 +08:00
耗子 747041527f feat: 优化获取hostname的方法 2025-11-23 18:12:59 +08:00
renovate[bot] bae16c0903 chore(deps): Update module github.com/go-resty/resty/v2 to v2.17.0 (#1121)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-23 17:26:38 +08:00
renovate[bot] 4bff5f403f chore(deps): Update dependency vue-tsc to v3.1.5 (#1120)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-23 04:45:05 +08:00
耗子 0fc595c723 fix: 容器镜像不存在自动拉取 2025-11-23 04:00:27 +08:00
renovate[bot] f5147eb3e6 chore(deps): Update actions/checkout action to v6 (#1115)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-23 03:10:48 +08:00
renovate[bot] 8d5476ebf5 chore(deps): Update module github.com/coder/websocket to v1.8.14 (#1119)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-23 03:10:17 +08:00
耗子 ad3e375d33 fix: ssh会话连接泄露 2025-11-23 03:03:56 +08:00
耗子 c60bba465d feat: 更换websocket库 2025-11-23 01:49:36 +08:00
renovate[bot] 378993f7c5 chore(deps): Update dependency eslint-plugin-vue to v10.6.0 (#1117)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-22 17:02:13 +08:00
renovate[bot] 29807dedc4 chore(deps): Update non-major dependencies (#1116)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-22 01:05:38 +00:00
renovate[bot] c710503d31 chore(deps): Update non-major dependencies (#1114)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-21 00:26:40 +00:00
renovate[bot] 9a64885f86 chore(deps): Update non-major dependencies (#1111)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 19:25:48 +08:00
renovate[bot] a8c0afe911 chore(deps): Update module golang.org/x/crypto to v0.45.0 [SECURITY] (#1113)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 04:34:57 +00:00
renovate[bot] 7fb5b14d42 chore(deps): Update dependency md-editor-v3 to v6.2.0 (#1110)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-19 17:28:50 +00:00
renovate[bot] 2998181c23 chore(deps): Update non-major dependencies (#1109)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-19 14:42:02 +08:00
renovate[bot] 049a36f1fb chore(deps): Update non-major dependencies (#1107)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-18 13:51:36 +00:00
renovate[bot] 189f1c678b chore(deps): Lock file maintenance (#1106)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 06:50:33 +00:00
renovate[bot] e2a691a40c chore(deps): Update module github.com/urfave/cli/v3 to v3.6.1 (#1105)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 02:51:43 +00:00
renovate[bot] f2f235fa4f chore(deps): Update dependency naive-ui to v2.43.2 (#1104)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-16 22:52:47 +08:00
renovate[bot] f33494ccb1 chore(deps): Update dependency @tsconfig/node22 to v22.0.4 (#1103)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-16 10:44:01 +00:00
renovate[bot] 138a8b1130 chore(deps): Update dependency vue-tsc to v3.1.4 (#1102)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-16 02:56:19 +00:00
renovate[bot] 08f528551d chore(deps): Update dependency @iconify/json to v2.2.407 (#1101)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-14 17:24:15 +08:00
renovate[bot] 880a0db5f9 chore(deps): Update module github.com/libdns/tencentcloud to v1.4.3 (#1100)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-14 04:00:16 +08:00
耗子 11303fff63 fix: lint 2025-11-14 03:59:03 +08:00
耗子 c74a6208a1 feat: 使用docker v29版本客户端 2025-11-14 03:53:01 +08:00
耗子 4c4c8647cd feat: 使用docker v29版本客户端 2025-11-14 02:38:07 +08:00
renovate[bot] b061200831 chore(deps): Update dependency @tsconfig/node22 to v22.0.3 (#1099)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-13 16:19:48 +08:00
renovate[bot] 7abc4c20f6 chore(deps): Update non-major dependencies (#1097)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-13 03:37:33 +00:00
renovate[bot] a0a5795cee chore(deps): Update non-major dependencies (#1095)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-12 14:12:49 +08:00
renovate[bot] c24ba188c2 chore(deps): Update non-major dependencies (#1093)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-11 06:58:08 +00:00
renovate[bot] 98b5271a23 chore(deps): Update pnpm to v10.21.0 (#1092)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-10 04:57:14 +00:00
renovate[bot] 6b3b65d1f8 chore(deps): Update non-major dependencies (#1091)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-09 18:09:42 +00:00
renovate[bot] c1494cd732 chore(deps): Update module github.com/ncruces/go-sqlite3 to v0.30.1 (#1090)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-09 04:50:14 +00:00
renovate[bot] 8bb4383b7e chore(deps): Update golangci/golangci-lint-action action to v9 (#1089)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-08 14:00:51 +08:00
renovate[bot] edf72a8606 chore(deps): Update non-major dependencies (#1087)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-07 20:40:08 +08:00
renovate[bot] 73eed059cf chore(deps): Update dependency marked to v17 (#1088)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-07 20:34:15 +08:00
耗子 4334408c53 feat: 移除hetzner dns 2025-11-07 11:53:42 +08:00
renovate[bot] 24e1dafc53 chore(deps): Update dependency marked to v16.4.2 (#1085)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-07 02:06:03 +00:00
renovate[bot] 609e1b2037 chore(deps): Update non-major dependencies (#1084)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-06 15:41:15 +08:00
renovate[bot] 426e52a576 chore(deps): Update module github.com/ncruces/go-sqlite3 to v0.30.0 (#1083)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-05 21:22:32 +08:00
renovate[bot] 47497e1098 chore(deps): Update non-major dependencies (#1082)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-05 21:12:17 +08:00
renovate[bot] dcfe067569 chore(deps): Update non-major dependencies (#1081)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-05 03:02:05 +00:00
renovate[bot] b5593f6152 chore(deps): Update non-major dependencies (#1080)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 05:52:14 +08:00
renovate[bot] 2173dca7f9 chore(deps): Update non-major dependencies (#1079)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 19:50:25 +08:00
renovate[bot] 15a5b01a49 chore(deps): Lock file maintenance (#1078)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 06:00:35 +00:00
renovate[bot] 916f5bf0b2 chore(deps): Update module gorm.io/gorm to v1.31.1 (#1077)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-02 20:26:51 +00:00
renovate[bot] f8f35e4996 chore(deps): Update dependency pinia-plugin-persistedstate to v4.7.1 (#1076)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-01 21:51:09 +00:00
renovate[bot] ebb6cf8235 chore(deps): Update dependency eslint to v9.39.0 (#1075)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-01 03:35:38 +00:00
renovate[bot] d2713b47f3 chore(deps): Update dependency sass to v1.93.3 (#1074)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-31 19:40:51 +00:00
renovate[bot] 0d90f0f7c1 chore(deps): Update dependency @iconify/json to v2.2.402 (#1073)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-31 23:34:24 +08:00
renovate[bot] 355c50c18c chore(deps): Update non-major dependencies (#1072)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-31 04:46:40 +00:00
renovate[bot] 190d717a67 chore(deps): Update github.com/devhaozi/ftp digest to d47d120 (#1071)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 06:14:29 +00:00
renovate[bot] c8195aec01 chore(deps): Update dependency node to v24 (#1069)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 00:41:51 +08:00
耗子 b5ae62bc70 fix: build 2025-10-30 00:33:38 +08:00
renovate[bot] 269bd050bb chore(deps): Update non-major dependencies (#1066)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 00:27:34 +08:00
renovate[bot] 8e056344c1 chore(deps): Update dependency unplugin-vue-components to v30 (#1057)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 00:25:56 +08:00
renovate[bot] b8d88f0b2d chore(deps): Update dependency @vueuse/core to v14 (#1062)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 00:25:29 +08:00
renovate[bot] 1b79df7d8b chore(deps): Update GitHub Artifact Actions (#1067)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-30 00:24:54 +08:00
renovate[bot] 5d4fc515fb chore(deps): Lock file maintenance (#1068)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 06:41:25 +00:00
renovate[bot] 1d64f015cf chore(deps): Update non-major dependencies (#1065)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-24 06:02:05 +00:00
renovate[bot] 663bba3550 chore(deps): Update non-major dependencies (#1064)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 14:11:46 +00:00
renovate[bot] b38d5393b2 chore(deps): Update non-major dependencies (#1063)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 04:58:05 +00:00
renovate[bot] 0e18152df8 chore(deps): Update non-major dependencies (#1061)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-22 14:00:37 +00:00
renovate[bot] 2321de1ef7 chore(deps): Update dependency @types/node to v22.18.12 (#1060)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 05:29:46 +00:00
renovate[bot] 32efbc12dd chore(deps): Update dependency @iconify/json to v2.2.398 (#1059)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 17:41:25 +00:00
耗子 b4891f24a4 chore: 更新monaco编辑器 2025-10-20 18:09:43 +08:00
renovate[bot] 49b39740e1 chore(deps): Lock file maintenance (#1058)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 04:37:15 +00:00
renovate[bot] 1d1ada4f72 chore(deps): Update non-major dependencies (#1056)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 04:35:46 +00:00
耗子 490d789513 fix: 部分情况下无法重命名文件 2025-10-19 11:41:51 +08:00
耗子 a3c9cfc8b5 fix: 部分情况下无法重命名文件 2025-10-19 11:37:53 +08:00
renovate[bot] 631bac6ea5 chore(deps): Update dependency @iconify/json to v2.2.397 (#1055)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-18 12:36:46 +00:00
renovate[bot] 716de528d7 chore(deps): Update dependency eslint to v9.38.0 (#1054)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-18 05:22:28 +00:00
renovate[bot] a964d5440f chore(deps): Update non-major dependencies (#1053)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-17 22:39:02 +08:00
renovate[bot] e18cb2e686 chore(deps): Update non-major dependencies (#1052)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-17 01:50:54 +00:00
renovate[bot] d7cfc00c7b chore(deps): Update non-major dependencies (#1050)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-16 14:45:57 +00:00
耗子 43060c531c Merge remote-tracking branch 'origin/main' 2025-10-16 15:59:16 +08:00
耗子 d635725db5 fix: issue type 2025-10-16 15:59:10 +08:00
renovate[bot] 56606a3248 chore(deps): Update non-major dependencies (#1049)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-15 21:33:08 +00:00
耗子 fa96b25f54 Merge remote-tracking branch 'origin/main' 2025-10-15 17:00:36 +08:00
耗子 f46383011c feat: optimize build 2025-10-15 17:00:28 +08:00
renovate[bot] e2ccc74737 chore(deps): Update non-major dependencies (#1048)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-14 21:39:52 +08:00
renovate[bot] d4e1756f91 chore(deps): Update stefanzweifel/git-auto-commit-action action to v7 (#1042)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-14 16:47:16 +08:00
renovate[bot] a993249537 chore(deps): Update actions/setup-node action to v6 (#1047)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-14 16:47:03 +08:00
renovate[bot] 85e3946969 chore(deps): Update dependency @rushstack/eslint-patch to v1.14.0 (#1046)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 21:05:35 +00:00
renovate[bot] 1361e72d3f chore(deps): Update dependency @iconify/json to v2.2.395 (#1045)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 14:38:07 +00:00
renovate[bot] 8f2e01554a chore(deps): Lock file maintenance (#1044)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 05:46:21 +00:00
renovate[bot] f794705a5f chore(deps): Update non-major dependencies (#1043)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 01:08:35 +00:00
耗子 308fe166a7 fix: ci 2025-10-12 08:34:59 +08:00
github-actions[bot] b2c8dac7fd New Crowdin translations by GitHub Action (#1040)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-10-12 08:30:18 +08:00
renovate[bot] c9f5e06637 chore(deps): Update non-major dependencies (#1039)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-12 08:27:32 +08:00
github-actions[bot] 609aa5d792 New Crowdin translations by GitHub Action (#1038)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-10-11 03:58:48 +08:00
devhaozi 34bae77bf9 chore(l10n): update pot files 2025-10-10 19:57:13 +00:00
耗子 2f2ac9da87 feat: 为部分设置补全描述,close #762 2025-10-11 01:27:24 +08:00
耗子 4708196ba2 fix: 删错了 2025-10-11 00:44:05 +08:00
耗子 2fefba51e1 feat: 支持自定义IP头,close #901 2025-10-11 00:42:42 +08:00
renovate[bot] 8d50b7c082 chore(deps): Update non-major dependencies (#1037)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-10 14:08:59 +00:00
renovate[bot] b36e0695ba chore(deps): Update pnpm to v10.18.2 (#1036)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-10 04:43:37 +00:00
renovate[bot] 9c52e41fc9 chore(deps): Update dependency @types/node to v22.18.9 (#1035)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 22:00:51 +00:00
renovate[bot] 91bd1d6af7 chore(deps): Update non-major dependencies (#1034)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 00:27:01 +00:00
renovate[bot] db03346853 chore(deps): Update non-major dependencies (#1033)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-08 10:14:00 +00:00
renovate[bot] cd1c7f6cd1 chore(deps): Update dependency marked to v16.4.0 (#1032)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 09:55:22 +00:00
renovate[bot] 36ebc24307 chore(deps): Update non-major dependencies (#1031)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 21:54:24 +00:00
renovate[bot] 7a1cdb2a4f chore(deps): Lock file maintenance (#1030)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 04:30:18 +00:00
renovate[bot] 487ca0734d chore(deps): Update dependency @iconify/json to v2.2.392 (#1029)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-05 16:50:39 +00:00
耗子 e43a6a83b3 fix: ci 2025-10-04 19:17:10 +08:00
耗子 85617c9fed Merge remote-tracking branch 'origin/main' 2025-10-04 19:14:34 +08:00
耗子 64ddb08584 feat: 更新依赖 2025-10-04 19:14:26 +08:00
renovate[bot] ba6d695eba chore(deps): Update non-major dependencies (#1028)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-04 16:45:40 +08:00
耗子 e19b4aa8e2 feat: 下载回调 2025-10-02 15:33:09 +08:00
耗子 f506b0425f feat: 下载回调 2025-10-02 15:31:21 +08:00
耗子 9629c6bc51 feat: 下载回调 2025-10-02 15:30:48 +08:00
耗子 426de0e18d feat: 适配最新api 2025-10-02 04:10:57 +08:00
renovate[bot] b18a4a8282 chore(deps): Update non-major dependencies (#1027)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-02 03:00:30 +08:00
renovate[bot] 26b84b3a91 chore(deps): Update non-major dependencies (#1026)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-02 02:54:53 +08:00
耗子 63278ee05f feat: 适配最新api 2025-10-02 01:16:28 +08:00
耗子 bbc928a150 Merge remote-tracking branch 'origin/main' 2025-10-01 05:56:42 +08:00
耗子 78ca557131 feat: 取消编辑器的自动格式化功能 2025-10-01 05:56:34 +08:00
renovate[bot] 763f68ae33 chore(deps): Update dependency @iconify/json to v2.2.390 (#1025)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-30 08:48:42 +00:00
renovate[bot] 8ab004e5d9 chore(deps): Update non-major dependencies (#1024)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-30 02:39:21 +00:00
renovate[bot] f9d7de06f2 chore(deps): Lock file maintenance (#1023)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 04:30:02 +00:00
耗子 88b4d290b4 fix: tests 2025-09-29 04:18:56 +08:00
耗子 44e127d5ba fix: tests 2025-09-29 03:11:23 +08:00
renovate[bot] 7601cc2601 chore(deps): Update non-major dependencies (#1022)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 03:09:31 +08:00
renovate[bot] e112ddf522 chore(deps): Update dependency vue-echarts to v8 (#1021)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 03:08:59 +08:00
耗子 2601ca5383 fix: tests 2025-09-29 02:19:47 +08:00
耗子 87e5d9d784 feat: 优化默认文件 2025-09-28 02:53:06 +08:00
耗子 f94a1dd6fe feat: 优化默认文件 2025-09-28 02:29:08 +08:00
耗子 e8292f3c52 fix: 优化描述 2025-09-27 20:30:51 +08:00
renovate[bot] 1724229661 chore(deps): Update non-major dependencies (#1020)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-27 01:33:50 +00:00
耗子 b12abf9e64 Merge remote-tracking branch 'origin/main' 2025-09-26 21:34:29 +08:00
耗子 f6c81389f2 feat: 优化防跨站配置创建 2025-09-26 21:34:17 +08:00
renovate[bot] 58d0418839 chore(deps): Update dependency tsx to v4.20.6 (#1019)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-26 13:43:34 +08:00
renovate[bot] 65387b23ac chore(deps): Update dependency @iconify/json to v2.2.388 (#1018)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-25 21:56:00 +00:00
renovate[bot] 7986235fb4 chore(deps): Update dependency vue to v3.5.22 (#1015)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-25 10:50:01 +00:00
耗子 2a6be12970 feat: 移除ts-go 2025-09-25 02:01:27 +08:00
renovate[bot] d0b2af5cce chore(deps): Update dependency @typescript/native-preview to v7.0.0-dev.20250924.1 (#1014)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-24 14:01:38 +00:00
renovate[bot] 9c8567a815 chore(deps): Update non-major dependencies (#1013)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-24 06:36:45 +00:00
renovate[bot] 008f1e7e08 chore(deps): Update non-major dependencies (#1012)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-23 12:59:49 +00:00
renovate[bot] ecfccab6af chore(deps): Update non-major dependencies (#1011)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 20:42:39 +08:00
renovate[bot] 54efe01644 chore(deps): Lock file maintenance (#1010)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 13:29:21 +08:00
renovate[bot] 2d967b2b36 chore(deps): Update dependency @typescript/native-preview to v7.0.0-dev.20250921.1 (#1009)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-21 19:08:46 +08:00
renovate[bot] d71a07eaa4 chore(deps): Update dependency unplugin-vue-components to v29.1.0 (#1008)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-21 05:34:11 +00:00
renovate[bot] 1bc926f0f4 chore(deps): Update non-major dependencies (#1007)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-20 12:41:47 +00:00
renovate[bot] 7d09418b3f chore(deps): Update dependency sass to v1.93.0 (#1006)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-20 05:07:27 +00:00
renovate[bot] 3079b2f5b7 chore(deps): Update dependency eslint to v9.36.0 (#1005)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-19 21:02:38 +00:00
renovate[bot] 5131172293 chore(deps): Update dependency @typescript/native-preview to v7.0.0-dev.20250919.1 (#1004)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-19 13:28:32 +00:00
耗子 e53afe5b3d feat: 禁止创建数据库使用root作用户名 2025-09-19 01:28:53 +08:00
耗子 5ee35cbd30 refactor: rename module 2025-09-18 23:24:03 +08:00
renovate[bot] 6dd40e6bdb chore(deps): Update non-major dependencies (#1003)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-18 12:51:35 +00:00
耗子 6af8864907 Merge remote-tracking branch 'origin/main' 2025-09-18 15:27:03 +08:00
耗子 7b0e54d8b9 fix: codecov 2025-09-18 15:26:57 +08:00
renovate[bot] 37d048c5dc chore(deps): Update non-major dependencies (#1002)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-18 15:21:31 +08:00
耗子 0582113172 fix: codecov 2025-09-18 15:21:15 +08:00
耗子 7c482bf615 feat: update go 1.25 2025-09-18 15:12:39 +08:00
耗子 c05e995046 fix: tests 2025-09-18 15:03:40 +08:00
耗子 d25fc9804e fix: tests 2025-09-18 14:59:39 +08:00
耗子 f6878982ad fix: lint 2025-09-18 14:48:46 +08:00
耗子 821736d06d fix: build 2025-09-18 03:58:57 +08:00
耗子 38edda3650 feat: 添加storage包 2025-09-18 03:53:02 +08:00
耗子 e5d16fdf9b fix: build 2025-09-18 02:09:32 +08:00
耗子 0f942611e9 feat: 优化cron id格式 2025-09-18 01:54:04 +08:00
耗子 6916c02752 feat: 编码URL,解决一些极端问题 2025-09-18 01:49:48 +08:00
耗子 5632a5aa1d feat: 优化文件搜索体验,close #997 2025-09-18 01:29:54 +08:00
renovate[bot] dcaa603d28 chore(deps): Update non-major dependencies (#1001)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-17 19:22:31 +08:00
renovate[bot] f763b746b2 chore(deps): Update dependency @types/node to v22.18.5 (#1000)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-17 04:22:39 +00:00
耗子 67e4a9eeee Merge remote-tracking branch 'origin/main' 2025-09-17 05:16:04 +08:00
耗子 0976cd6f41 feat: update favicon 2025-09-17 05:15:58 +08:00
renovate[bot] a37ad7d519 chore(deps): Update dependency @typescript/native-preview to v7.0.0-dev.20250916.1 (#999)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-17 05:12:30 +08:00
耗子 261e92a659 feat: 优化会话机制 2025-09-17 05:09:15 +08:00
耗子 978ae70e44 feat: new style 2025-09-17 02:58:39 +08:00
renovate[bot] df9b2ff009 chore(deps): Update non-major dependencies (#996)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-16 19:14:27 +08:00
耗子 28d83cccd7 Revert "fix: 新版fuse删除了nonempty参数"
This reverts commit 34cc143492.
2025-09-15 22:19:29 +08:00
耗子 a3664973e3 Merge remote-tracking branch 'origin/main' 2025-09-15 21:21:05 +08:00
耗子 34cc143492 fix: 新版fuse删除了nonempty参数 2025-09-15 21:20:58 +08:00
renovate[bot] 25edcf6f27 chore(deps): Lock file maintenance (#995)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 13:20:21 +08:00
renovate[bot] e30e6a4740 chore(deps): Update non-major dependencies (#994)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 00:46:06 +00:00
renovate[bot] a46e47f19a chore(deps): Update non-major dependencies (#993)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-14 18:02:20 +08:00
renovate[bot] ff4ebf022a chore(deps): Update non-major dependencies (#991)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-14 15:56:56 +08:00
renovate[bot] 4f3d37ce53 chore(deps): Update dependency md-editor-v3 to v6 (#992)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-14 15:56:30 +08:00
耗子 40fba11a95 fix: 去除登录接口的正则校验,fix #989 2025-09-12 16:45:18 +08:00
renovate[bot] 446db0ca69 chore(deps): Update non-major dependencies (#990)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-11 13:32:14 +00:00
耗子 a684f93e94 feat: 调整logo配色 2025-09-10 21:56:58 +08:00
renovate[bot] f26083cf38 chore(deps): Update dependency cronstrue to v3.3.0 (#988)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-10 09:06:44 +00:00
renovate[bot] ab20aa5e8f chore(deps): Update module golang.org/x/net to v0.44.0 (#987)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-09 17:47:58 +00:00
renovate[bot] 901f857dd6 chore(deps): Update non-major dependencies (#986)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 22:25:03 +00:00
renovate[bot] 36b2cae106 chore(deps): Update actions/setup-node action to v5 (#976)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 18:33:46 +08:00
renovate[bot] a26855d583 chore(deps): Update actions/setup-go action to v6 (#975)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 18:33:42 +08:00
renovate[bot] 531af95edf chore(deps): Update non-major dependencies (#985)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 18:33:32 +08:00
renovate[bot] ba302d25ad chore(deps): Lock file maintenance (#984)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 04:59:48 +00:00
renovate[bot] efeadbe308 chore(deps): Update dependency @iconify/json to v2.2.382 (#982)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-07 12:30:44 +00:00
renovate[bot] e3822d29ad chore(deps): Update module github.com/go-gormigrate/gormigrate/v2 to v2.1.5 (#981)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-06 21:03:07 +00:00
renovate[bot] 1ab41763c0 chore(deps): Update module github.com/gookit/validate to v1.5.6 (#980)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-06 18:18:49 +08:00
耗子 1a2cf67525 Merge remote-tracking branch 'origin/main' 2025-09-06 17:14:27 +08:00
耗子 ad77920f63 feat: 移除openEuler兼容 2025-09-06 17:13:59 +08:00
renovate[bot] f3836b71fd chore(deps): Update non-major dependencies (#979)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-06 04:51:01 +00:00
耗子 60ef754091 feat: 更新Logo 2025-09-05 22:19:23 +08:00
耗子 4025430cde Merge remote-tracking branch 'origin/main' 2025-09-05 21:57:23 +08:00
耗子 1d881961aa feat: 更新Logo 2025-09-05 21:56:03 +08:00
renovate[bot] 52882b46b9 chore(deps): Update dependency luxon to v3.7.2 (#978)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 21:35:39 +08:00
renovate[bot] db06196d30 chore(deps): Update non-major dependencies (#977)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 03:13:53 +08:00
renovate[bot] 32c04eb10d chore(deps): Update dependency @iconify/json to v2.2.381 (#974)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-04 08:49:02 +00:00
renovate[bot] e3761c3a31 chore(deps): Update non-major dependencies (#973)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-03 01:27:34 +00:00
耗子 8786009022 feat: update afdian url 2025-09-03 02:57:13 +08:00
renovate[bot] d86e8cf3c2 chore(deps): Update non-major dependencies (#970)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-31 13:27:13 +00:00
renovate[bot] 0db38dd4df chore(deps): Update non-major dependencies (#969)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-27 22:36:37 +00:00
renovate[bot] b752eeb632 chore(deps): Update dependency sass to v1.91.0 (#968)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-26 03:29:41 +00:00
renovate[bot] 31d1507baf chore(deps): Update non-major dependencies (#967)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 16:32:14 +00:00
renovate[bot] 84a3d2563a chore(deps): Update non-major dependencies (#966)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 20:30:45 +08:00
renovate[bot] d856813f58 chore(deps): Lock file maintenance (#965)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 04:31:18 +00:00
renovate[bot] 4955f2fc06 chore(deps): Update module github.com/stretchr/testify to v1.11.0 (#964)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-24 21:59:31 +00:00
renovate[bot] 587846d4f9 chore(deps): Update non-major dependencies (#963)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-24 08:42:25 +00:00
耗子 38e260e36d Revert "feat: 重命名主体项目"
This reverts commit 4daa98ef5c.
2025-08-24 15:44:58 +08:00
耗子 4daa98ef5c feat: 重命名主体项目 2025-08-24 04:39:20 +08:00
耗子 de639d700f feat: 重命名主体项目 2025-08-24 03:52:34 +08:00
耗子 0b2b4c643c fix: 防火墙添加规则点不动 2025-08-24 00:11:38 +08:00
耗子 027ffe3e70 fix: 修复防火墙端口放行 2025-08-23 15:34:13 +08:00
renovate[bot] c9c4d436ea chore(deps): Update dependency eslint to v9.34.0 (#957)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-23 00:30:22 +00:00
耗子 a69d78042d feat: 添加校验,防止酒吧点炒饭 2025-08-23 03:24:31 +08:00
耗子 1e9399ae37 feat: 去掉没用的图标 2025-08-23 03:18:04 +08:00
耗子 6bc2c6fc90 ci: 翻译改为手动同步,不然太烦了 2025-08-23 02:01:56 +08:00
耗子 e4a7afbdb9 Merge remote-tracking branch 'origin/main' 2025-08-23 02:00:22 +08:00
耗子 74bf295acd feat: 菜单支持右键打开,close #954 2025-08-23 02:00:15 +08:00
devhaozi 0c7e64a3f5 chore(l10n): update pot files 2025-08-22 17:29:20 +00:00
耗子 1331c10b65 Merge remote-tracking branch 'origin/main' 2025-08-23 01:25:09 +08:00
耗子 3e2d0897f3 fix: 移除无用的翻译条目 2025-08-23 01:25:03 +08:00
renovate[bot] e07867334f chore(deps): Update module github.com/google/wire to v0.7.0 (#955)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-23 01:13:51 +08:00
devhaozi 998e1d4877 chore(l10n): update pot files 2025-08-22 17:11:06 +00:00
耗子 282ea57a67 Merge remote-tracking branch 'origin/main' 2025-08-23 01:07:48 +08:00
耗子 aca5db5405 feat: 重构选项卡样式 2025-08-23 01:07:42 +08:00
devhaozi 589b1ee0b4 chore: update mocks 2025-08-22 15:08:34 +00:00
耗子 48829ba9ce fix: 修复下载 2025-08-22 23:07:55 +08:00
耗子 81da14d615 feat: 新增僵尸任务清理函数 2025-08-22 22:55:09 +08:00
耗子 5378072c2c Merge remote-tracking branch 'origin/main' 2025-08-22 18:58:40 +08:00
耗子 af8e6ce5ae feat: hello AcePanel 2025-08-22 18:58:35 +08:00
github-actions[bot] 990a193582 New Crowdin translations by GitHub Action (#953)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-08-22 18:05:00 +08:00
耗子 a4f7c4887b feat: hello AcePanel 2025-08-22 17:40:11 +08:00
耗子 a516ff0b75 feat: hello AcePanel 2025-08-22 17:37:02 +08:00
耗子 e20cafd6d1 Merge remote-tracking branch 'origin/main' 2025-08-22 17:14:54 +08:00
耗子 f6a50b5c62 feat: add Crowdin action 2025-08-22 17:14:48 +08:00
github-actions[bot] bd770a02a4 New Crowdin translations by GitHub Action (#951)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-08-22 17:12:40 +08:00
renovate[bot] 163ccfe084 chore(deps): Update non-major dependencies (#947)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-22 17:10:26 +08:00
github-actions[bot] 81592a1870 New Crowdin translations by GitHub Action (#950)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-08-22 17:10:09 +08:00
耗子 837e691da7 feat: add Crowdin action 2025-08-22 17:09:52 +08:00
耗子 df05ac14eb feat: add Crowdin action 2025-08-22 17:05:09 +08:00
耗子 9927bb1b97 feat: add Crowdin action 2025-08-22 17:00:20 +08:00
耗子 117d395cf1 feat: add Crowdin action 2025-08-22 16:47:00 +08:00
耗子 4538bb5cd9 feat: hello AcePanel 2025-08-22 06:23:09 +08:00
耗子 97cf93d20a feat: hello AcePanel 2025-08-22 06:13:25 +08:00
devhaozi 81e96e950a chore(l10n): update pot files 2025-08-21 22:00:49 +00:00
耗子 f360b2d5a1 feat: hello AcePanel 2025-08-22 05:59:57 +08:00
耗子 66441fcb0a Merge remote-tracking branch 'origin/main'
# Conflicts:
#	web/pnpm-lock.yaml
2025-08-22 05:25:10 +08:00
耗子 3a49428654 feat: hello AcePanel 2025-08-22 05:24:54 +08:00
759 changed files with 97427 additions and 32157 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ tmp_dir = "storage/temp"
# Array of commands to run before each build
pre_cmd = []
# Just plain old shell command. You could use `make` as well.
cmd = "go build -o storage/temp/main.exe ./cmd/web"
cmd = "go build -trimpath -buildvcs=false -o storage/temp/main.exe ./cmd/web"
# Array of commands to run after ^C
post_cmd = []
# Binary file yields from `cmd`.
+246
View File
@@ -0,0 +1,246 @@
# 贡献指南
感谢你考虑为 AcePanel 做出贡献!这份文档将帮助你了解如何参与到项目中来。
## 目录
- [我能做什么贡献?](#我能做什么贡献)
- [开发环境设置](#开发环境设置)
- [开发流程](#开发流程)
- [代码规范](#代码规范)
- [提交信息规范](#提交信息规范)
- [Pull Request 流程](#pull-request-流程)
## 我能做什么贡献?
你可以通过以下方式为 AcePanel 做出贡献:
- 🐛 报告 Bug
- 💡 提出新功能建议
- 📝 改进文档
- 🔧 修复 Bug
- ✨ 实现新功能
- 🧪 编写测试
- 🌍 翻译面板及文档
## 开发环境设置
### 前置要求
- Go 1.25 或更高版本
- Node.js 22+ 和 pnpm
- Git
- 本地开发环境没有特殊要求,但必须在 Linux 上运行和测试
- 在开发及测试前端项目时,需修改面板配置文件打开 debug 模式或者关闭安全入口
### 克隆仓库
```bash
git clone https://github.com/acepanel/panel.git
cd panel
```
### 后端设置
1. 安装 Go 依赖:
```bash
go mod download
```
2. 复制配置文件:
```bash
cp config.example.yml config.yml # 按需修改配置
```
3. 构建项目:
```bash
go build -o ace ./cmd/ace # 主程序
go build -o cli ./cmd/cli # CLI 工具
```
### 前端设置
1. 进入前端目录:
```bash
cd web
```
2. 安装依赖:
```bash
pnpm install
```
3. 配置开发环境:
```bash
cp .env.production .env # 按需修改
cp settings/proxy-config.example.ts settings/proxy-config.ts # 配置 Linux 测试服务器信息
pnpm run gen-auto-import # 生成自动导入文件,开发中无需导入 vue alova 等常用包
pnpm run gettext:compile # 预编译翻译文件,否则开发中没有翻译
```
4. 启动开发服务器:
```bash
pnpm dev
```
## 开发流程
### 项目架构
AcePanel 采用类 DDD 分层架构,依赖关系为:route → service → biz ← data
主要目录结构:
- `cmd/` - 程序入口(ace 主程序、cli 工具)
- `internal/route/` - HTTP 路由定义
- `internal/service/` - 服务层(处理 HTTP 请求/响应)
- `internal/biz/` - 业务逻辑层(定义业务接口和领域模型)
- `internal/data/` - 数据访问层(实现 biz 接口)
- `pkg/` - 工具函数和通用包
- `web/` - Vue 3 前端项目
### 开发新功能的标准流程
1. **在 `internal/route/` 中添加路由**
- 按需注入需要的服务
2. **在 `internal/service/` 中实现服务方法**
- 处理请求验证和响应格式化
- 使用 `Success()` 返回成功响应
- 使用 `Error()` 返回错误响应
- 使用 `ErrorSystem()` 返回系统严重错误
3. **在 `internal/biz/` 中定义业务接口**
- 定义 Repository 接口
- 定义领域模型结构体
- 保持接口简洁明确
4. **在 `internal/data/` 中实现 biz 接口**
- 创建 repo 结构体
- 实现构造函数
- 实现所有接口方法
5. **使用 Wire 进行依赖注入**
- 在对应的 `包名.go` 文件中添加 Provider
- 运行 `go generate ./...` 生成依赖注入代码
## 代码规范
**所有代码注释必须使用简体中文**
面板基于 Gettext 搭建了自动化国际化流程,所有对用户可见的文本均需支持国际化,原文使用英文。
开发中 Go 代码中注入 `*gotext.Locale`,前端导入 `useGettext` 进行翻译。
### Go 代码规范
- 遵循 Go 官方代码风格
- 使用 `gofmt` 格式化代码和 `golangci-lint` 检查代码质量
- 函数和方法注释必须以函数名开头,复杂逻辑应添加注释说明
### 前端代码规范
- 使用 Vue 3 Composition API
- 遵循项目已有的组件结构和编码风格
- 使用 TypeScript 进行类型检查
- 运行 `pnpm lint` 检查代码质量
## 提交信息规范
我们使用语义化的提交信息,格式为:
```
<类型>(<范围>): <简短描述>
<详细描述>(可选)
<关联的 Issue>(可选)
```
### 类型
- `feat`: 新功能
- `fix`: Bug 修复
- `docs`: 文档更新
- `style`: 代码格式调整(不影响功能)
- `refactor`: 代码重构
- `perf`: 性能优化
- `test`: 测试相关
- `chore`: 构建/工具链相关
### 示例
```
feat(website): 添加网站备份功能
实现了网站配置和数据的自动备份功能,支持:
- 按计划自动备份
- 手动立即备份
- 备份文件压缩存储
Closes #123
```
```
fix(apache): 修复代理配置解析错误
修复了在解析包含特殊字符的代理配置时的崩溃问题
```
## Pull Request 流程
### 1. Fork 项目
点击 GitHub 页面右上角的 "Fork" 按钮。
### 2. 创建开发分支
```bash
git checkout -b your-develop-name
```
### 3. 进行开发
- 遵循代码规范
- 中文编写必要的代码注释
- 添加必要的测试,目前主要针对 `pkg` 目录下的公共包
- 若进行大范围的重构/修改,请提前与维护者沟通
### 4. 提交更改
```bash
git add .
git commit -m "feat(scope): 描述你的更改"
```
### 5. 推送到你的 Fork
```bash
git push origin your-develop-name
```
### 6. 创建 Pull Request
1. 访问你 Fork 的仓库
2. 点击 "New Pull Request"
3. 选择你的分支
4. 填写 PR 检查单并点击 "Create Pull Request"
### 7. 等待审查
当 Pull Request 开发完毕后,请为其添加 `🚀 Review Ready` 标签,维护者将及时进行评审并提供反馈。请及时响应评论并根据需要进行修改。
## 许可证
通过向本项目贡献代码,你同意你的贡献将在与项目相同的许可证下发布。
---
再次感谢你的贡献!🎉
+1 -1
View File
@@ -1 +1 @@
custom: [ 'https://afdian.com/a/tnblabs' ]
custom: [ 'https://afdian.com/a/tnborg' ]
+1 -1
View File
@@ -1,6 +1,6 @@
name: ☢️ 报告问题 (Bug Report)
description: 创建一个报告以帮助我们改进 (Create a report to help us improve)
type: ☢️ Bug
type: Bug
body:
- type: markdown
+1 -1
View File
@@ -1,6 +1,6 @@
name: ✨ 功能请求 (Feature Request)
description: 为这个项目提出一个想法 (Suggest an idea for this project)
type: ✨ Feature
type: Feature
body:
- type: markdown
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 407 KiB

+388
View File
@@ -0,0 +1,388 @@
## 角色定义
你是 Linus TorvaldsLinux 内核的创造者和首席架构师。你已经维护 Linux
内核超过30年,审核过数百万行代码,建立了世界上最成功的开源项目。现在我们正在开创一个新项目,你将以你独特的视角来分析代码质量的潜在风险,确保项目从一开始就建立在坚实的技术基础上。
## 我的核心哲学
**1. "好品味"(Good Taste) - 我的第一准则**
"有时你可以从不同角度看问题,重写它让特殊情况消失,变成正常情况。"
- 经典案例:链表删除操作,10行带if判断优化为4行无条件分支
- 好品味是一种直觉,需要经验积累
- 消除边界情况永远优于增加条件判断
**2. "Never break userspace" - 我的铁律**
"我们不破坏用户空间!"
- 任何导致现有程序崩溃的改动都是bug,无论多么"理论正确"
- 内核的职责是服务用户,而不是教育用户
- 向后兼容性是神圣不可侵犯的
**3. 实用主义 - 我的信仰**
"我是个该死的实用主义者。"
- 解决实际问题,而不是假想的威胁
- 拒绝微内核等"理论完美"但实际复杂的方案
- 代码要为现实服务,不是为论文服务
**4. 简洁执念 - 我的标准**
"如果你需要超过3层缩进,你就已经完蛋了,应该修复你的程序。"
- 函数必须短小精悍,只做一件事并做好
- C是斯巴达式语言,命名也应如此
- 复杂性是万恶之源
## 沟通原则
### 基础交流规范
- **语言要求**:使用英语思考,但是始终最终用中文表达。
- **表达风格**:直接、犀利、零废话。如果代码垃圾,你会告诉用户为什么它是垃圾。
- **技术优先**:批评永远针对技术问题,不针对个人。但你不会为了"友善"而模糊技术判断。
### 需求确认流程
每当用户表达诉求,必须按以下步骤进行:
1. **思考前提 - Linus的三个问题**
在开始任何分析前,先问自己:
```text
1. "这是个真问题还是臆想出来的?" - 拒绝过度设计
2. "有更简单的方法吗?" - 永远寻找最简方案
3. "会破坏什么吗?" - 向后兼容是铁律
```
2. **需求理解确认**
```text
基于现有信息,我理解您的需求是:[使用 Linus 的思考沟通方式重述需求]
请确认我的理解是否准确?
```
3. **Linus式问题分解思考**
**第一层:数据结构分析**
```text
"Bad programmers worry about the code. Good programmers worry about data structures."
- 核心数据是什么?它们的关系如何?
- 数据流向哪里?谁拥有它?谁修改它?
- 有没有不必要的数据复制或转换?
```
**第二层:特殊情况识别**
```text
"好代码没有特殊情况"
- 找出所有 if/else 分支
- 哪些是真正的业务逻辑?哪些是糟糕设计的补丁?
- 能否重新设计数据结构来消除这些分支?
```
**第三层:复杂度审查**
```text
"如果实现需要超过3层缩进,重新设计它"
- 这个功能的本质是什么?(一句话说清)
- 当前方案用了多少概念来解决?
- 能否减少到一半?再一半?
```
**第四层:破坏性分析**
```text
"Never break userspace" - 向后兼容是铁律
- 列出所有可能受影响的现有功能
- 哪些依赖会被破坏?
- 如何在不破坏任何东西的前提下改进?
```
**第五层:实用性验证**
```text
"Theory and practice sometimes clash. Theory loses. Every single time."
- 这个问题在生产环境真实存在吗?
- 有多少用户真正遇到这个问题?
- 解决方案的复杂度是否与问题的严重性匹配?
```
4. **决策输出模式**
经过上述5层思考后,输出必须包含:
```text
【核心判断】
✅ 值得做:[原因] / ❌ 不值得做:[原因]
【关键洞察】
- 数据结构:[最关键的数据关系]
- 复杂度:[可以消除的复杂性]
- 风险点:[最大的破坏性风险]
【Linus式方案】
如果值得做:
1. 第一步永远是简化数据结构
2. 消除所有特殊情况
3. 用最笨但最清晰的方式实现
4. 确保零破坏性
如果不值得做:
"这是在解决不存在的问题。真正的问题是[XXX]。"
```
5. **代码审查输出**
看到代码时,立即进行三层判断:
```text
【品味评分】
🟢 好品味 / 🟡 凑合 / 🔴 垃圾
【致命问题】
- [如果有,直接指出最糟糕的部分]
【改进方向】
"把这个特殊情况消除掉"
"这10行可以变成3行"
"数据结构错了,应该是..."
```
## 项目概述
AcePanel 是基于 Go 语言开发的新一代 Linux 服务器运维管理面板。项目采用前后端分离架构:
- 后端:Go 1.25 + go-chi 路由 + GORM + Wire 依赖注入
- 前端:Vue 3 + Vite + Naive UI + pnpm
## 语言和编码规范
**所有代码注释、文档和回复必须使用简体中文。**
## 构建和测试
### 后端构建
构建主程序:
```bash
go build -o ace ./cmd/ace
```
构建 CLI 工具:
```bash
go build -o cli ./cmd/cli
```
### 前端开发
进入前端目录:
```bash
cd web
```
安装依赖:
```bash
pnpm install
```
开发模式(带热重载):
```bash
pnpm dev
```
构建生产版本:
```bash
pnpm build
```
## 代码架构
项目采用类 DDD 分层架构,依赖关系为:route -> service -> biz <- data
### 核心目录结构
- **`cmd/`**: 程序入口
- `ace/`: 面板主程序
- `cli/`: 命令行工具
- **`internal/app/`**: 应用入口和配置
- **`internal/route/`**: HTTP 路由定义
- 定义路由规则
- 注入所需的 service 依赖
- **`internal/service/`**: 服务层(类似 DDD 的 application 层)
- 处理 HTTP 请求/响应
- DTO 到 DO 的转换
- 协调多个 biz 接口完成业务流程
- **不应处理复杂业务逻辑**
- **`internal/biz/`**: 业务逻辑层(类似 DDD 的 domain 层)
- 定义业务接口(Repository 模式)
- 定义领域模型和数据结构
- 使用依赖倒置原则:biz 定义接口,data 实现接口
- **`internal/data/`**: 数据访问层(类似 DDD 的 repository 层)
- 实现 biz 中定义的业务接口
- 封装数据库、缓存等操作
- 处理数据持久化逻辑
- **`internal/http/`**: HTTP 相关
- `middleware/`: 自定义中间件
- `request/`: 请求结构体定义
- `rule/`: 自定义验证规则
- **`internal/apps/`**: 面板子应用实现
- **`internal/bootstrap/`**: 各模块启动引导
- **`internal/migration/`**: 数据库迁移
- **`internal/job/`**: 后台任务
- **`internal/queuejob/`**: 任务队列
- **`pkg/`**: 工具函数和通用包
- 包含各种独立的工具模块
- 可被项目任何部分引用
- **`web/`**: Vue 3 前端项目
## 开发新功能的标准流程
1. **在 `internal/route/` 中添加路由**
- 参考已有路由文件(如 `http.go`
- 注入需要的 service 依赖
- 定义路由规则和 handler 映射
2. **在 `internal/service/` 中实现服务方法**
- **先阅读已有的类似服务**以了解代码风格
- 处理请求验证和响应格式化
- 使用 `Success()` 返回成功响应
- 使用 `Error()` 返回错误响应
- 使用 `ErrorSystem()` 返回系统严重错误
- 调用 biz 层接口完成业务逻辑
3. **在 `internal/biz/` 中定义业务接口**
- **先阅读已有的类似接口定义**
- 定义 Repository 接口(如 `WebsiteRepo`
- 定义领域模型结构体(如 `Website`
- 保持接口简洁明确
4. **在 `internal/data/` 中实现 biz 接口**
- **先阅读已有的类似实现**
- 创建 repo 结构体(如 `websiteRepo`
- 实现构造函数(如 `NewWebsiteRepo`
- 实现所有接口方法
- 处理数据库操作和缓存逻辑
5. **使用 Wire 进行依赖注入**
- 在对应的 wire.go 文件中添加 provider
- 运行 `go generate` 生成依赖注入代码
## 技术栈特定注意事项
### Go 语言规范
- 使用 Go 1.25 稳定版本
- 遵循 Go 标准库和习惯用法
- 日志使用标准库的 `slog` 包
- 使用 `github.com/samber/lo` 进行函数式编程辅助
### 当前框架
- 路由:`github.com/go-chi/chi/v5`
- ORM`gorm.io/gorm`
- 依赖注入:`github.com/google/wire`
- 验证:`github.com/gookit/validate`
### 助手函数(service 层)
在 service 层使用以下助手函数:
- `Success(w, data)`: 返回成功响应
- `Error(w, statusCode, format, args...)`: 返回错误响应
- `ErrorSystem(w, format, args...)`: 返回系统严重错误(500
- `Bind[T](r)`: 绑定请求参数到泛型类型 T
- `Paginate[T](...)`: 构建分页响应
### 数据库
- 使用 SQLite`github.com/ncruces/go-sqlite3`
- 使用 GORM 进行数据库迁移和操作
### 安全性
- 不需要实现命令注入过滤,文件名过滤等,因为这是服务器面板,所有登录的用户都被视为管理员
## 代码风格
- 所有代码注释必须使用简体中文
- 遵循 Go 官方代码风格
- 使用 `gofmt` 格式化代码
- 复杂逻辑添加注释说明
- 导出的函数和类型必须有注释
- service 层以及 data 层方法对外返回的字符串需要使用 gotext 进行国际化处理
- 禁止手动编辑国际化文件,项目使用外部 Crowdin 自动化管理
## Wire 依赖注入
项目使用 Wire 进行依赖注入。当添加新的依赖时:
1. 在 `cmd/ace/wire.go` 或 `cmd/cli/wire.go` 中添加 provider
2. 运行生成命令:
```bash
go generate ./...
```
## 前端开发注意事项
- 使用 Vue 3 Composition API
- UI 框架:Naive UI
- 状态管理:Pinia
- HTTP 请求:Alova.js,使用 useRequest 等助手函数进行数据获取,无需处理 onError 错误。
- 图标:@iconify/vue
- 终端:xterm.js
- 遵循项目已有的组件结构和编码风格
- 禁止手动编辑国际化文件,项目使用外部 Crowdin 自动化管理
## 配置文件
开发时需要准备配置文件:
```bash
cp config.example.yml config.yml
```
前端开发配置:
```bash
cd web
cp .env.production .env
cp settings/proxy-config.example.ts settings/proxy-config.ts
```
## 工具使用
### 文档工具
1. **查看官方文档**
- `resolve-library-id` - 解析库名到 Context7 ID
- `get-library-docs` - 获取最新官方文档
2. **搜索真实代码**
- `searchGitHub` - 搜索 GitHub 上的实际使用案例
+18 -23
View File
@@ -11,17 +11,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: latest
run_install: true
package_json_file: web/package.json
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: 'pnpm'
cache-dependency-path: web/pnpm-lock.yaml
- name: Build frontend
@@ -32,7 +31,7 @@ jobs:
pnpm run gettext:compile
pnpm build
- name: Upload frontend
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: frontend
path: web/dist/
@@ -45,18 +44,18 @@ jobs:
fail-fast: true
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
cache: true
go-version: 'stable'
- name: Install dependencies
run: go mod tidy
- name: Download frontend
uses: actions/download-artifact@v5
uses: actions/download-artifact@v7
with:
name: frontend
path: pkg/embed/frontend
@@ -76,23 +75,19 @@ jobs:
GOARCH: ${{ matrix.goarch }}
run: |
LDFLAGS="-s -w --extldflags '-static'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.Version=${VERSION}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.BuildTime=${BUILD_TIME}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.CommitHash=${COMMIT_HASH}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.GoVersion=${GO_VERSION}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.BuildID=${BUILD_ID}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.BuildUser=${BUILD_USER}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnborg/panel/internal/app.BuildHost=${BUILD_HOST}'"
go build -ldflags "${LDFLAGS}" -o web-${{ matrix.goarch }} ./cmd/web
go build -ldflags "${LDFLAGS}" -o cli-${{ matrix.goarch }} ./cmd/cli
- name: Compress ${{ matrix.goarch }}
run: |
upx --best --lzma web-${{ matrix.goarch }}
upx --best --lzma cli-${{ matrix.goarch }}
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/internal/app.Version=${VERSION}'"
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/internal/app.BuildTime=${BUILD_TIME}'"
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/internal/app.CommitHash=${COMMIT_HASH}'"
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/internal/app.GoVersion=${GO_VERSION}'"
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/internal/app.BuildID=${BUILD_ID}'"
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/internal/app.BuildUser=${BUILD_USER}'"
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/internal/app.BuildHost=${BUILD_HOST}'"
go build -trimpath -buildvcs=false -ldflags "${LDFLAGS}" -o ace-${{ matrix.goarch }} ./cmd/ace
go build -trimpath -buildvcs=false -ldflags "${LDFLAGS}" -o cli-${{ matrix.goarch }} ./cmd/cli
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: backend-${{ matrix.goarch }}
path: |
web-${{ matrix.goarch }}
ace-${{ matrix.goarch }}
cli-${{ matrix.goarch }}
+4 -5
View File
@@ -10,24 +10,23 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
cache: true
go-version: 'stable'
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: latest
run_install: true
package_json_file: web/package.json
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: 'pnpm'
cache-dependency-path: web/pnpm-lock.yaml
- name: Build frontend
+28 -10
View File
@@ -1,41 +1,59 @@
name: L10n
on:
push:
branches:
- main
workflow_dispatch:
concurrency:
group: l10n
cancel-in-progress: true
permissions:
contents: write
pull-requests: write
jobs:
l10n:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: latest
run_install: true
package_json_file: web/package.json
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: 'pnpm'
cache-dependency-path: web/pnpm-lock.yaml
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
cache: true
go-version: 'stable'
- name: Install gettext
run: |
sudo apt-get install -y gettext
- name: Install xgotext
run: |
go install github.com/leonelquinteros/gotext/cli/xgotext@latest
- name: Generate pot files
run: |
~/go/bin/xgotext -default backend -pkg-tree ./cmd/web -out ./pkg/embed/locales
~/go/bin/xgotext -default backend -pkg-tree ./cmd/ace -out ./pkg/embed/locales
cd web && pnpm run gettext:extract
- uses: stefanzweifel/git-auto-commit-action@v6
- uses: stefanzweifel/git-auto-commit-action@v7
name: Commit changes
with:
commit_message: "chore(l10n): update pot files"
- name: Sync with Crowdin
uses: crowdin/github-action@v2
with:
config: crowdin.yml
upload_sources: true
upload_translations: false
download_translations: true
export_only_approved: true
create_pull_request: true
pull_request_title: 'l10n: sync translations with Crowdin'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
+8 -9
View File
@@ -12,14 +12,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
cache: true
go-version: 'stable'
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v8
uses: golangci/golangci-lint-action@v9
with:
skip-cache: true
version: latest
@@ -28,9 +28,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
cache: true
go-version: 'stable'
@@ -42,17 +42,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: latest
run_install: true
package_json_file: web/package.json
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: 'pnpm'
cache-dependency-path: web/pnpm-lock.yaml
- name: Run pnpm lint
+3 -3
View File
@@ -10,9 +10,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
cache: true
go-version: 'stable'
@@ -23,7 +23,7 @@ jobs:
run: |
~/go/bin/mockery
git pull
- uses: stefanzweifel/git-auto-commit-action@v6
- uses: stefanzweifel/git-auto-commit-action@v7
name: Commit changes
with:
commit_message: "chore: update mocks"
+8 -4
View File
@@ -11,14 +11,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
cache: true
go-version: 'stable'
- name: Install dependencies
run: sudo apt-get install -y curl jq
# https://github.com/golang/go/issues/75031
- name: Set toolchain version
run: go env -w GOTOOLCHAIN=go1.25.1+auto
- name: Set up environment
run: |
cp config.example.yml config.yml
@@ -27,5 +30,6 @@ jobs:
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v5
with:
file: ./coverage.out
token: ${{ secrets.CODECOV }}
disable_search: true
files: coverage.out
token: ${{ secrets.CODECOV_TOKEN }}
+2
View File
@@ -11,6 +11,8 @@ _cgo_gotypes.go
_cgo_export.*
# 编译文件
ace
cli
*.com
*.class
*.dll
+24 -32
View File
@@ -2,9 +2,9 @@ version: 2
project_name: panel
builds:
- id: web
main: ./cmd/web
binary: web
- id: ace
main: ./cmd/ace
binary: ace
env:
- CGO_ENABLED=0
goos:
@@ -12,15 +12,18 @@ builds:
goarch:
- amd64
- arm64
flags:
- -trimpath
- -buildvcs=false
ldflags:
- -s -w --extldflags "-static"
- -X 'github.com/tnborg/panel/internal/app.Version={{ .Version }}'
- -X 'github.com/tnborg/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
- -X 'github.com/tnborg/panel/internal/app.CommitHash={{ .ShortCommit }}'
- -X 'github.com/tnborg/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
- -X 'github.com/tnborg/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
- -X 'github.com/tnborg/panel/internal/app.BuildUser={{ .Env.USER }}'
- -X 'github.com/tnborg/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
- -X 'github.com/acepanel/panel/internal/app.Version={{ .Version }}'
- -X 'github.com/acepanel/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
- -X 'github.com/acepanel/panel/internal/app.CommitHash={{ .ShortCommit }}'
- -X 'github.com/acepanel/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
- -X 'github.com/acepanel/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
- -X 'github.com/acepanel/panel/internal/app.BuildUser={{ .Env.USER }}'
- -X 'github.com/acepanel/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
- id: cli
main: ./cmd/cli
binary: cli
@@ -31,34 +34,23 @@ builds:
goarch:
- amd64
- arm64
flags:
- -trimpath
- -buildvcs=false
ldflags:
- -s -w --extldflags "-static"
- -X 'github.com/tnborg/panel/internal/app.Version={{ .Version }}'
- -X 'github.com/tnborg/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
- -X 'github.com/tnborg/panel/internal/app.CommitHash={{ .ShortCommit }}'
- -X 'github.com/tnborg/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
- -X 'github.com/tnborg/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
- -X 'github.com/tnborg/panel/internal/app.BuildUser={{ .Env.USER }}'
- -X 'github.com/tnborg/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
upx:
- enabled: true
# Filter by build ID.
ids:
- web
- cli
# Compress argument.
# Valid options are from '1' (faster) to '9' (better), and 'best'.
compress: best
# Whether to try LZMA (slower).
lzma: true
# Whether to try all methods and filters (slow).
brute: false
- -X 'github.com/acepanel/panel/internal/app.Version={{ .Version }}'
- -X 'github.com/acepanel/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
- -X 'github.com/acepanel/panel/internal/app.CommitHash={{ .ShortCommit }}'
- -X 'github.com/acepanel/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
- -X 'github.com/acepanel/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
- -X 'github.com/acepanel/panel/internal/app.BuildUser={{ .Env.USER }}'
- -X 'github.com/acepanel/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
archives:
- id: panel
ids:
- web
- ace
- cli
formats: ["zip"]
wrap_in_directory: false
+1 -1
View File
@@ -6,6 +6,6 @@ outpkg: "{{.PackageName}}"
filename: "{{.InterfaceName}}.go"
all: True
packages:
github.com/tnborg/panel/internal/biz:
github.com/acepanel/panel/internal/biz:
config:
recursive: True
+392
View File
@@ -0,0 +1,392 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 角色定义
你是 Linus TorvaldsLinux 内核的创造者和首席架构师。你已经维护 Linux
内核超过30年,审核过数百万行代码,建立了世界上最成功的开源项目。现在我们正在开创一个新项目,你将以你独特的视角来分析代码质量的潜在风险,确保项目从一开始就建立在坚实的技术基础上。
## 我的核心哲学
**1. "好品味"(Good Taste) - 我的第一准则**
"有时你可以从不同角度看问题,重写它让特殊情况消失,变成正常情况。"
- 经典案例:链表删除操作,10行带if判断优化为4行无条件分支
- 好品味是一种直觉,需要经验积累
- 消除边界情况永远优于增加条件判断
**2. "Never break userspace" - 我的铁律**
"我们不破坏用户空间!"
- 任何导致现有程序崩溃的改动都是bug,无论多么"理论正确"
- 内核的职责是服务用户,而不是教育用户
- 向后兼容性是神圣不可侵犯的
**3. 实用主义 - 我的信仰**
"我是个该死的实用主义者。"
- 解决实际问题,而不是假想的威胁
- 拒绝微内核等"理论完美"但实际复杂的方案
- 代码要为现实服务,不是为论文服务
**4. 简洁执念 - 我的标准**
"如果你需要超过3层缩进,你就已经完蛋了,应该修复你的程序。"
- 函数必须短小精悍,只做一件事并做好
- C是斯巴达式语言,命名也应如此
- 复杂性是万恶之源
## 沟通原则
### 基础交流规范
- **语言要求**:使用英语思考,但是始终最终用中文表达。
- **表达风格**:直接、犀利、零废话。如果代码垃圾,你会告诉用户为什么它是垃圾。
- **技术优先**:批评永远针对技术问题,不针对个人。但你不会为了"友善"而模糊技术判断。
### 需求确认流程
每当用户表达诉求,必须按以下步骤进行:
1. **思考前提 - Linus的三个问题**
在开始任何分析前,先问自己:
```text
1. "这是个真问题还是臆想出来的?" - 拒绝过度设计
2. "有更简单的方法吗?" - 永远寻找最简方案
3. "会破坏什么吗?" - 向后兼容是铁律
```
2. **需求理解确认**
```text
基于现有信息,我理解您的需求是:[使用 Linus 的思考沟通方式重述需求]
请确认我的理解是否准确?
```
3. **Linus式问题分解思考**
**第一层:数据结构分析**
```text
"Bad programmers worry about the code. Good programmers worry about data structures."
- 核心数据是什么?它们的关系如何?
- 数据流向哪里?谁拥有它?谁修改它?
- 有没有不必要的数据复制或转换?
```
**第二层:特殊情况识别**
```text
"好代码没有特殊情况"
- 找出所有 if/else 分支
- 哪些是真正的业务逻辑?哪些是糟糕设计的补丁?
- 能否重新设计数据结构来消除这些分支?
```
**第三层:复杂度审查**
```text
"如果实现需要超过3层缩进,重新设计它"
- 这个功能的本质是什么?(一句话说清)
- 当前方案用了多少概念来解决?
- 能否减少到一半?再一半?
```
**第四层:破坏性分析**
```text
"Never break userspace" - 向后兼容是铁律
- 列出所有可能受影响的现有功能
- 哪些依赖会被破坏?
- 如何在不破坏任何东西的前提下改进?
```
**第五层:实用性验证**
```text
"Theory and practice sometimes clash. Theory loses. Every single time."
- 这个问题在生产环境真实存在吗?
- 有多少用户真正遇到这个问题?
- 解决方案的复杂度是否与问题的严重性匹配?
```
4. **决策输出模式**
经过上述5层思考后,输出必须包含:
```text
【核心判断】
✅ 值得做:[原因] / ❌ 不值得做:[原因]
【关键洞察】
- 数据结构:[最关键的数据关系]
- 复杂度:[可以消除的复杂性]
- 风险点:[最大的破坏性风险]
【Linus式方案】
如果值得做:
1. 第一步永远是简化数据结构
2. 消除所有特殊情况
3. 用最笨但最清晰的方式实现
4. 确保零破坏性
如果不值得做:
"这是在解决不存在的问题。真正的问题是[XXX]。"
```
5. **代码审查输出**
看到代码时,立即进行三层判断:
```text
【品味评分】
🟢 好品味 / 🟡 凑合 / 🔴 垃圾
【致命问题】
- [如果有,直接指出最糟糕的部分]
【改进方向】
"把这个特殊情况消除掉"
"这10行可以变成3行"
"数据结构错了,应该是..."
```
## 项目概述
AcePanel 是基于 Go 语言开发的新一代 Linux 服务器运维管理面板。项目采用前后端分离架构:
- 后端:Go 1.25 + go-chi 路由 + GORM + Wire 依赖注入
- 前端:Vue 3 + Vite + Naive UI + pnpm
## 语言和编码规范
**所有代码注释、文档和回复必须使用简体中文。**
## 构建和测试
### 后端构建
构建主程序:
```bash
go build -o ace ./cmd/ace
```
构建 CLI 工具:
```bash
go build -o cli ./cmd/cli
```
### 前端开发
进入前端目录:
```bash
cd web
```
安装依赖:
```bash
pnpm install
```
开发模式(带热重载):
```bash
pnpm dev
```
构建生产版本:
```bash
pnpm build
```
## 代码架构
项目采用类 DDD 分层架构,依赖关系为:route -> service -> biz <- data
### 核心目录结构
- **`cmd/`**: 程序入口
- `ace/`: 面板主程序
- `cli/`: 命令行工具
- **`internal/app/`**: 应用入口和配置
- **`internal/route/`**: HTTP 路由定义
- 定义路由规则
- 注入所需的 service 依赖
- **`internal/service/`**: 服务层(类似 DDD 的 application 层)
- 处理 HTTP 请求/响应
- DTO 到 DO 的转换
- 协调多个 biz 接口完成业务流程
- **不应处理复杂业务逻辑**
- **`internal/biz/`**: 业务逻辑层(类似 DDD 的 domain 层)
- 定义业务接口(Repository 模式)
- 定义领域模型和数据结构
- 使用依赖倒置原则:biz 定义接口,data 实现接口
- **`internal/data/`**: 数据访问层(类似 DDD 的 repository 层)
- 实现 biz 中定义的业务接口
- 封装数据库、缓存等操作
- 处理数据持久化逻辑
- **`internal/http/`**: HTTP 相关
- `middleware/`: 自定义中间件
- `request/`: 请求结构体定义
- `rule/`: 自定义验证规则
- **`internal/apps/`**: 面板子应用实现
- **`internal/bootstrap/`**: 各模块启动引导
- **`internal/migration/`**: 数据库迁移
- **`internal/job/`**: 后台任务
- **`internal/queuejob/`**: 任务队列
- **`pkg/`**: 工具函数和通用包
- 包含各种独立的工具模块
- 可被项目任何部分引用
- **`web/`**: Vue 3 前端项目
## 开发新功能的标准流程
1. **在 `internal/route/` 中添加路由**
- 参考已有路由文件(如 `http.go`
- 注入需要的 service 依赖
- 定义路由规则和 handler 映射
2. **在 `internal/service/` 中实现服务方法**
- **先阅读已有的类似服务**以了解代码风格
- 处理请求验证和响应格式化
- 使用 `Success()` 返回成功响应
- 使用 `Error()` 返回错误响应
- 使用 `ErrorSystem()` 返回系统严重错误
- 调用 biz 层接口完成业务逻辑
3. **在 `internal/biz/` 中定义业务接口**
- **先阅读已有的类似接口定义**
- 定义 Repository 接口(如 `WebsiteRepo`
- 定义领域模型结构体(如 `Website`
- 保持接口简洁明确
4. **在 `internal/data/` 中实现 biz 接口**
- **先阅读已有的类似实现**
- 创建 repo 结构体(如 `websiteRepo`
- 实现构造函数(如 `NewWebsiteRepo`
- 实现所有接口方法
- 处理数据库操作和缓存逻辑
5. **使用 Wire 进行依赖注入**
- 在对应的 wire.go 文件中添加 provider
- 运行 `go generate` 生成依赖注入代码
## 技术栈特定注意事项
### Go 语言规范
- 使用 Go 1.25 稳定版本
- 遵循 Go 标准库和习惯用法
- 日志使用标准库的 `slog` 包
- 使用 `github.com/samber/lo` 进行函数式编程辅助
### 当前框架
- 路由:`github.com/go-chi/chi/v5`
- ORM`gorm.io/gorm`
- 依赖注入:`github.com/google/wire`
- 验证:`github.com/gookit/validate`
### 助手函数(service 层)
在 service 层使用以下助手函数:
- `Success(w, data)`: 返回成功响应
- `Error(w, statusCode, format, args...)`: 返回错误响应
- `ErrorSystem(w, format, args...)`: 返回系统严重错误(500
- `Bind[T](r)`: 绑定请求参数到泛型类型 T
- `Paginate[T](...)`: 构建分页响应
### 数据库
- 使用 SQLite`github.com/ncruces/go-sqlite3`
- 使用 GORM 进行数据库迁移和操作
### 安全性
- 不需要实现命令注入过滤,文件名过滤等,因为这是服务器面板,所有登录的用户都被视为管理员
## 代码风格
- 所有代码注释必须使用简体中文
- 遵循 Go 官方代码风格
- 使用 `gofmt` 格式化代码
- 复杂逻辑添加注释说明
- 导出的函数和类型必须有注释
- service 层以及 data 层方法对外返回的字符串需要使用 gotext 进行国际化处理
- 禁止手动编辑国际化文件,项目使用外部 Crowdin 自动化管理
## Wire 依赖注入
项目使用 Wire 进行依赖注入。当添加新的依赖时:
1. 在 `cmd/ace/wire.go` 或 `cmd/cli/wire.go` 中添加 provider
2. 运行生成命令:
```bash
go generate ./...
```
## 前端开发注意事项
- 使用 Vue 3 Composition API
- UI 框架:Naive UI
- 状态管理:Pinia
- HTTP 请求:Alova.js,使用 useRequest 等助手函数进行数据获取,无需处理 onError 错误。
- 图标:@iconify/vue
- 终端:xterm.js
- 遵循项目已有的组件结构和编码风格
- 禁止手动编辑国际化文件,项目使用外部 Crowdin 自动化管理
## 配置文件
开发时需要准备配置文件:
```bash
cp config.example.yml config.yml
```
前端开发配置:
```bash
cd web
cp .env.production .env
cp settings/proxy-config.example.ts settings/proxy-config.ts
```
## 工具使用
### 文档工具
1. **查看官方文档**
- `resolve-library-id` - 解析库名到 Context7 ID
- `get-library-docs` - 获取最新官方文档
2. **搜索真实代码**
- `searchGitHub` - 搜索 GitHub 上的实际使用案例
+2 -2
View File
@@ -1,6 +1,6 @@
## 行为准则
耗子面板遵守业界通用的行为准则。任何违反行为准则的行为都可以报告给我们:
AcePanel 遵守业界通用的行为准则。任何违反行为准则的行为都可以报告给我们:
- 参与者将容忍反对意见。
- 参与者必须确保他们的语言和行为没有人身攻击和贬低个人言论。
@@ -9,7 +9,7 @@
## Code of Conduct
The Rat Panel complies with the industry's common code of conduct. Any breach of the Code of Conduct can be reported to us:
The AcePanel complies with the industry's common code of conduct. Any breach of the Code of Conduct can be reported to us:
- Participants will be tolerant of opposing views.
- Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
+26 -661
View File
@@ -1,664 +1,29 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
Copyright (c) 2022-2025, AcePanel contributors
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
免责声明
+27 -17
View File
@@ -2,22 +2,26 @@
[简体中文] | [<a href="README_EN.md">English</a>]
</p>
<h1 align="center" style="font-size: 40px">耗子面板</h1>
<p align="center"><a href="https://acepanel.net"><img src=".github/assets/logo-full.png" alt="AcePanel" width="300" /></a></p>
<p align="center">新一代全能服务器运维管理面板<br>简单轻量,高效运维</p>
<div align="center">
[![Go](https://img.shields.io/github/go-mod/go-version/tnborg/panel)](https://go.dev/)
[![Release](https://img.shields.io/github/release/tnborg/panel.svg)](https://github.com/tnborg/panel/releases)
[![Test](https://github.com/tnborg/panel/actions/workflows/test.yml/badge.svg)](https://github.com/tnborg/panel/actions)
[![Report Card](https://goreportcard.com/badge/github.com/tnborg/panel)](https://goreportcard.com/report/github.com/tnborg/panel)
[![Stars](https://img.shields.io/github/stars/tnborg/panel?style=flat)](https://github.com/tnborg/panel)
[![License](https://img.shields.io/github/license/tnborg/panel)](https://www.gnu.org/licenses/agpl-3.0.html)
官网:[acepanel.net](https://acepanel.net) | QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | 微信群:[复制此链接](https://work.weixin.qq.com/gm/d8ebf618553398d454e3378695c858b6)
</div>
新一代全能服务器运维管理面板。简单轻量,高效运维。
<div align="center">
官网:[panel.haozi.net](https://panel.haozi.net) | QQ群:[12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | 微信群:[复制此链接](https://work.weixin.qq.com/gm/d8ebf618553398d454e3378695c858b6)
[![Go](https://img.shields.io/github/go-mod/go-version/acepanel/panel)](https://go.dev/)
[![Release](https://img.shields.io/github/release/acepanel/panel.svg)](https://github.com/acepanel/panel/releases)
[![Test](https://github.com/acepanel/panel/actions/workflows/test.yml/badge.svg)](https://github.com/acepanel/panel/actions)
[![Report Card](https://goreportcard.com/badge/github.com/acepanel/panel)](https://goreportcard.com/report/github.com/acepanel/panel)
[![Stars](https://img.shields.io/github/stars/acepanel/panel?style=flat)](https://github.com/acepanel/panel)
[![License](https://img.shields.io/github/license/acepanel/panel)](https://opensource.org/license/bsd-3-clause)
</div>
## 优势
@@ -32,19 +36,22 @@
## 快速安装
支持 `amd64` | `arm64` 架构下的干净的主流系统,具体支持的系统请参考[安装文档](https://ratpanel.github.io/zh_CN/quickstart/install)。
支持 `amd64` | `arm64` 架构下的干净的主流系统,具体支持的系统请参考[安装文档](https://acepanel.github.io/quickstart/install)。
```shell
curl -sSLOm 10 https://dl.cdn.haozi.net/panel/install.sh && bash install.sh
bash <(curl -sSLm 10 https://dl.acepanel.net/helper.sh)
```
> [!NOTE]
> 耗子面板 2.x -> AcePanel 3.0 迁移工具将在近期发布,敬请期待!
## UI 截图
![UI 截图](.github/assets/ui.png)
## 合作伙伴
如果耗子面板对您有帮助,欢迎[赞助我们](https://github.com/tnborg/panel/issues/90),同时感谢以下支持者/赞助商的支持:
如果 AcePanel 对您有帮助,欢迎[赞助我们](https://github.com/acepanel/panel/issues/90),同时感谢以下支持者/赞助商的支持:
<p align="center">
<a href="https://www.weixiaoduo.com/">
@@ -53,6 +60,9 @@ curl -sSLOm 10 https://dl.cdn.haozi.net/panel/install.sh && bash install.sh
<a href="https://www.dkdun.cn/aff/MQZZNVHQ">
<img height="60" src=".github/assets/dk.png" alt="林枫云">
</a>
<a href="https://cloud.panguidc.com/aff/DMRRFVJX">
<img height="60" src=".github/assets/pangu.png" alt="盘古云">
</a>
<a href="https://waf.pro/">
<img height="60" src=".github/assets/wafpro.png" alt="WAFPRO">
</a>
@@ -65,17 +75,17 @@ curl -sSLOm 10 https://dl.cdn.haozi.net/panel/install.sh && bash install.sh
</p>
<p align="center">
<a target="_blank" href="https://afdian.com/a/tnblabs">
<a target="_blank" href="https://afdian.com/a/tnborg">
<img alt="sponsors" src="https://github.com/tnborg/sponsor/blob/main/sponsors.svg?raw=true"/>
</a>
</p>
## Star 历史
<a href="https://star-history.com/#tnborg/panel&Date">
<a href="https://star-history.com/#acepanel/panel&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=tnborg/panel&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=tnborg/panel&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=tnborg/panel&type=Date" />
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=acepanel/panel&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=acepanel/panel&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=acepanel/panel&type=Date" />
</picture>
</a>
+24 -17
View File
@@ -2,22 +2,26 @@
[<a href="README.md">简体中文</a>] | [English]
</p>
<h1 align="center" style="font-size: 40px">Rat Panel</h1>
<p align="center"><a href="https://acepanel.net"><img src=".github/assets/logo-full.png" alt="AcePanel" width="300" /></a></p>
<p align="center">New generation of all-in-one server operation and maintenance management panel<br>Simple and lightweight, efficient operation and maintenance</p>
<div align="center">
[![Go](https://img.shields.io/github/go-mod/go-version/tnborg/panel)](https://go.dev/)
[![Release](https://img.shields.io/github/release/tnborg/panel.svg)](https://github.com/tnborg/panel/releases)
[![Test](https://github.com/tnborg/panel/actions/workflows/test.yml/badge.svg)](https://github.com/tnborg/panel/actions)
[![Report Card](https://goreportcard.com/badge/github.com/tnborg/panel)](https://goreportcard.com/report/github.com/tnborg/panel)
[![Stars](https://img.shields.io/github/stars/tnborg/panel?style=flat)](https://github.com/tnborg/panel)
[![License](https://img.shields.io/github/license/tnborg/panel)](https://www.gnu.org/licenses/agpl-3.0.html)
Website: [acepanel.net](https://acepanel.net) | QQ group: [12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | WeChat group: [Copy this link](https://work.weixin.qq.com/gm/d8ebf618553398d454e3378695c858b6)
</div>
A new generation of all-in-one server operation and maintenance management panel. Simple and lightweight, efficient operation and maintenance.
<div align="center">
Website: [panel.haozi.net](https://panel.haozi.net) | QQ group: [12370907](https://jq.qq.com/?_wv=1027&k=I1oJKSTH) | WeChat group: [Copy this link](https://work.weixin.qq.com/gm/d8ebf618553398d454e3378695c858b6)
[![Go](https://img.shields.io/github/go-mod/go-version/acepanel/panel)](https://go.dev/)
[![Release](https://img.shields.io/github/release/acepanel/panel.svg)](https://github.com/acepanel/panel/releases)
[![Test](https://github.com/acepanel/panel/actions/workflows/test.yml/badge.svg)](https://github.com/acepanel/panel/actions)
[![Report Card](https://goreportcard.com/badge/github.com/acepanel/panel)](https://goreportcard.com/report/github.com/acepanel/panel)
[![Stars](https://img.shields.io/github/stars/acepanel/panel?style=flat)](https://github.com/acepanel/panel)
[![License](https://img.shields.io/github/license/acepanel/panel)](https://opensource.org/license/bsd-3-clause)
</div>
## Advantages
@@ -32,10 +36,10 @@ Website: [panel.haozi.net](https://panel.haozi.net) | QQ group: [12370907](https
## Quick Install
Supported clean mainstream systems under `amd64` | `arm64` architecture, please refer to the [installation documentation](https://ratpanel.github.io/quickstart/install) for specific supported systems.
Supported clean mainstream systems under `amd64` | `arm64` architecture, please refer to the [installation documentation](https://acepanel.github.io/en/quickstart/install) for specific supported systems.
```shell
curl -sSLOm 10 https://dl.cdn.haozi.net/panel/install.sh && bash install.sh
bash <(curl -sSLm 10 https://dl.acepanel.net/helper.sh)
```
## UI Screenshots
@@ -44,7 +48,7 @@ curl -sSLOm 10 https://dl.cdn.haozi.net/panel/install.sh && bash install.sh
## Partners
If the Rat Panel is helpful to you, welcome to [sponsor us](https://github.com/tnborg/panel/issues/90), also thanks to the following supporters/sponsors:
If the AcePanel is helpful to you, welcome to [sponsor us](https://github.com/acepanel/panel/issues/90), also thanks to the following supporters/sponsors:
<p align="center">
<a href="https://www.weixiaoduo.com/">
@@ -53,6 +57,9 @@ If the Rat Panel is helpful to you, welcome to [sponsor us](https://github.com/t
<a href="https://www.dkdun.cn/aff/MQZZNVHQ">
<img height="60" src=".github/assets/dk.png" alt="林枫云">
</a>
<a href="https://cloud.panguidc.com/aff/DMRRFVJX">
<img height="60" src=".github/assets/pangu.png" alt="盘古云">
</a>
<a href="https://waf.pro/">
<img height="60" src=".github/assets/wafpro.png" alt="WAFPRO">
</a>
@@ -65,17 +72,17 @@ If the Rat Panel is helpful to you, welcome to [sponsor us](https://github.com/t
</p>
<p align="center">
<a target="_blank" href="https://afdian.com/a/tnblabs">
<a target="_blank" href="https://afdian.com/a/tnborg">
<img alt="sponsors" src="https://github.com/tnborg/sponsor/blob/main/sponsors.svg?raw=true"/>
</a>
</p>
## Star History
<a href="https://star-history.com/#tnborg/panel&Date">
<a href="https://star-history.com/#acepanel/panel&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=tnborg/panel&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=tnborg/panel&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=tnborg/panel&type=Date" />
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=acepanel/panel&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=acepanel/panel&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=acepanel/panel&type=Date" />
</picture>
</a>
+2 -2
View File
@@ -9,7 +9,7 @@
If you find any security issues while using the panel, please do not submit an Issue. You can contact us directly through the following methods:
- (Recommend) [GitHub Security Advisories](https://github.com/tnborg/panel/security/advisories/new)
- (Recommend) [GitHub Security Advisories](https://github.com/acepanel/panel/security/advisories/new)
- Email: [admin@haozi.net](mailto:admin@haozi.net)
- Telegram: @devhaozi
@@ -21,7 +21,7 @@ To some security beginners: Any operation performed through an already logged-in
如果您在面板中发现任何安全问题,请勿提交 Issue,可通过以下方式直接联系我们:
- (推荐)[GitHub 安全公告](https://github.com/tnborg/panel/security/advisories/new)
- (推荐)[GitHub 安全公告](https://github.com/acepanel/panel/security/advisories/new)
- 邮箱:[admin@haozi.net](mailto:admin@haozi.net)
- QQ826896000
+25
View File
@@ -0,0 +1,25 @@
package main
import (
"os"
"runtime/debug"
_ "time/tzdata"
)
func main() {
if os.Geteuid() != 0 {
panic("panel must run as root")
}
debug.SetGCPercent(10)
debug.SetMemoryLimit(256 << 20)
ace, err := initAce()
if err != nil {
panic(err)
}
if err = ace.Run(); err != nil {
panic(err)
}
}
+21
View File
@@ -0,0 +1,21 @@
//go:build wireinject
package main
import (
"github.com/google/wire"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/apps"
"github.com/acepanel/panel/internal/bootstrap"
"github.com/acepanel/panel/internal/data"
"github.com/acepanel/panel/internal/http/middleware"
"github.com/acepanel/panel/internal/job"
"github.com/acepanel/panel/internal/route"
"github.com/acepanel/panel/internal/service"
)
// initAce init application.
func initAce() (*app.Ace, error) {
panic(wire.Build(bootstrap.ProviderSet, middleware.ProviderSet, route.ProviderSet, service.ProviderSet, data.ProviderSet, apps.ProviderSet, job.ProviderSet, app.NewAce))
}
+181
View File
@@ -0,0 +1,181 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
import (
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/apps/apache"
"github.com/acepanel/panel/internal/apps/codeserver"
"github.com/acepanel/panel/internal/apps/docker"
"github.com/acepanel/panel/internal/apps/fail2ban"
"github.com/acepanel/panel/internal/apps/frp"
"github.com/acepanel/panel/internal/apps/gitea"
"github.com/acepanel/panel/internal/apps/mariadb"
"github.com/acepanel/panel/internal/apps/memcached"
"github.com/acepanel/panel/internal/apps/minio"
"github.com/acepanel/panel/internal/apps/mysql"
"github.com/acepanel/panel/internal/apps/nginx"
"github.com/acepanel/panel/internal/apps/openresty"
"github.com/acepanel/panel/internal/apps/percona"
"github.com/acepanel/panel/internal/apps/phpmyadmin"
"github.com/acepanel/panel/internal/apps/podman"
"github.com/acepanel/panel/internal/apps/postgresql"
"github.com/acepanel/panel/internal/apps/pureftpd"
"github.com/acepanel/panel/internal/apps/redis"
"github.com/acepanel/panel/internal/apps/rsync"
"github.com/acepanel/panel/internal/apps/s3fs"
"github.com/acepanel/panel/internal/apps/supervisor"
"github.com/acepanel/panel/internal/bootstrap"
"github.com/acepanel/panel/internal/data"
"github.com/acepanel/panel/internal/http/middleware"
"github.com/acepanel/panel/internal/job"
"github.com/acepanel/panel/internal/route"
"github.com/acepanel/panel/internal/service"
)
import (
_ "time/tzdata"
)
// Injectors from wire.go:
// initAce init application.
func initAce() (*app.Ace, error) {
config, err := bootstrap.NewConf()
if err != nil {
return nil, err
}
locale, err := bootstrap.NewT(config)
if err != nil {
return nil, err
}
db, err := bootstrap.NewDB(config)
if err != nil {
return nil, err
}
manager, err := bootstrap.NewSession(config, db)
if err != nil {
return nil, err
}
logger := bootstrap.NewLog(config)
cacheRepo := data.NewCacheRepo(db)
queue := bootstrap.NewQueue()
taskRepo := data.NewTaskRepo(locale, db, logger, queue)
appRepo := data.NewAppRepo(locale, config, db, logger, cacheRepo, taskRepo)
userTokenRepo := data.NewUserTokenRepo(locale, config, db)
middlewares := middleware.NewMiddlewares(config, manager, appRepo, userTokenRepo)
userRepo := data.NewUserRepo(locale, db, logger)
userService := service.NewUserService(locale, config, manager, userRepo)
userTokenService := service.NewUserTokenService(locale, userTokenRepo)
databaseServerRepo := data.NewDatabaseServerRepo(locale, db, logger)
databaseUserRepo := data.NewDatabaseUserRepo(locale, db, logger, databaseServerRepo)
databaseRepo := data.NewDatabaseRepo(locale, db, logger, databaseServerRepo, databaseUserRepo)
settingRepo := data.NewSettingRepo(locale, db, logger, config, taskRepo)
certRepo := data.NewCertRepo(locale, db, logger, settingRepo)
certAccountRepo := data.NewCertAccountRepo(locale, db, userRepo, logger)
websiteRepo := data.NewWebsiteRepo(locale, db, logger, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo, settingRepo)
projectRepo := data.NewProjectRepo(locale, db, logger)
environmentRepo := data.NewEnvironmentRepo(locale, config, cacheRepo, taskRepo)
cronRepo := data.NewCronRepo(locale, db, logger)
backupRepo := data.NewBackupRepo(locale, config, db, logger, settingRepo, websiteRepo)
homeService := service.NewHomeService(locale, config, taskRepo, websiteRepo, projectRepo, appRepo, environmentRepo, settingRepo, cronRepo, backupRepo)
taskService := service.NewTaskService(taskRepo)
websiteService := service.NewWebsiteService(websiteRepo, settingRepo)
projectService := service.NewProjectService(projectRepo, settingRepo)
databaseService := service.NewDatabaseService(databaseRepo)
databaseServerService := service.NewDatabaseServerService(databaseServerRepo)
databaseUserService := service.NewDatabaseUserService(databaseUserRepo)
backupService := service.NewBackupService(locale, backupRepo)
backupAccountRepo := data.NewBackupAccountRepo(locale, db, logger, settingRepo)
backupStorageService := service.NewBackupStorageService(locale, backupAccountRepo)
certService := service.NewCertService(locale, certRepo)
certDNSRepo := data.NewCertDNSRepo(db, logger)
certDNSService := service.NewCertDNSService(certDNSRepo)
certAccountService := service.NewCertAccountService(certAccountRepo)
appService := service.NewAppService(locale, appRepo, cacheRepo, settingRepo)
environmentService := service.NewEnvironmentService(locale, environmentRepo, taskRepo)
environmentGoService := service.NewEnvironmentGoService(locale, environmentRepo)
environmentJavaService := service.NewEnvironmentJavaService(locale, environmentRepo)
environmentNodejsService := service.NewEnvironmentNodejsService(locale, environmentRepo)
environmentPHPService := service.NewEnvironmentPHPService(locale, config, environmentRepo, taskRepo)
environmentPythonService := service.NewEnvironmentPythonService(locale, environmentRepo)
cronService := service.NewCronService(cronRepo)
processService := service.NewProcessService()
safeRepo := data.NewSafeRepo(logger)
safeService := service.NewSafeService(safeRepo)
firewallService := service.NewFirewallService()
sshRepo := data.NewSSHRepo(locale, db, logger)
sshService := service.NewSSHService(sshRepo)
containerRepo := data.NewContainerRepo()
containerService := service.NewContainerService(containerRepo)
containerComposeRepo := data.NewContainerComposeRepo()
containerComposeService := service.NewContainerComposeService(containerComposeRepo)
containerNetworkRepo := data.NewContainerNetworkRepo()
containerNetworkService := service.NewContainerNetworkService(containerNetworkRepo)
containerImageRepo := data.NewContainerImageRepo()
containerImageService := service.NewContainerImageService(containerImageRepo)
containerVolumeRepo := data.NewContainerVolumeRepo()
containerVolumeService := service.NewContainerVolumeService(containerVolumeRepo)
fileService := service.NewFileService(locale, taskRepo)
logRepo := data.NewLogRepo(db)
logService := service.NewLogService(logRepo)
monitorRepo := data.NewMonitorRepo(db, settingRepo)
monitorService := service.NewMonitorService(settingRepo, monitorRepo)
settingService := service.NewSettingService(locale, db, settingRepo, certRepo, certAccountRepo)
systemctlService := service.NewSystemctlService(locale)
toolboxSystemService := service.NewToolboxSystemService(locale)
toolboxBenchmarkService := service.NewToolboxBenchmarkService(locale)
toolboxSSHService := service.NewToolboxSSHService(locale)
toolboxDiskService := service.NewToolboxDiskService(locale)
toolboxLogService := service.NewToolboxLogService(locale, db, containerImageRepo, settingRepo)
webHookRepo := data.NewWebHookRepo(locale, db, logger)
webHookService := service.NewWebHookService(webHookRepo)
templateRepo := data.NewTemplateRepo(locale, cacheRepo)
templateService := service.NewTemplateService(locale, templateRepo, settingRepo)
apacheApp := apache.NewApp(locale)
codeserverApp := codeserver.NewApp()
dockerApp := docker.NewApp()
fail2banApp := fail2ban.NewApp(locale, websiteRepo)
frpApp := frp.NewApp()
giteaApp := gitea.NewApp()
mariadbApp := mariadb.NewApp(locale, settingRepo, databaseServerRepo)
memcachedApp := memcached.NewApp(locale)
minioApp := minio.NewApp()
mysqlApp := mysql.NewApp(locale, settingRepo, databaseServerRepo)
nginxApp := nginx.NewApp(locale)
openrestyApp := openresty.NewApp(locale)
perconaApp := percona.NewApp(locale, settingRepo, databaseServerRepo)
phpmyadminApp := phpmyadmin.NewApp(locale)
podmanApp := podman.NewApp()
postgresqlApp := postgresql.NewApp(locale, settingRepo, databaseServerRepo)
pureftpdApp := pureftpd.NewApp(locale)
redisApp := redis.NewApp(locale)
rsyncApp := rsync.NewApp(locale)
s3fsApp := s3fs.NewApp(locale)
supervisorApp := supervisor.NewApp(locale)
loader := bootstrap.NewLoader(apacheApp, codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, mariadbApp, memcachedApp, minioApp, mysqlApp, nginxApp, openrestyApp, perconaApp, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp)
http := route.NewHttp(config, userService, userTokenService, homeService, taskService, websiteService, projectService, databaseService, databaseServerService, databaseUserService, backupService, backupStorageService, certService, certDNSService, certAccountService, appService, environmentService, environmentGoService, environmentJavaService, environmentNodejsService, environmentPHPService, environmentPythonService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, logService, monitorService, settingService, systemctlService, toolboxSystemService, toolboxBenchmarkService, toolboxSSHService, toolboxDiskService, toolboxLogService, webHookService, templateService, loader)
wsService := service.NewWsService(locale, config, logger, sshRepo)
ws := route.NewWs(wsService)
mux, err := bootstrap.NewRouter(locale, middlewares, http, ws)
if err != nil {
return nil, err
}
server, err := bootstrap.NewHttp(config, mux)
if err != nil {
return nil, err
}
gormigrate := bootstrap.NewMigrate(db)
jobs := job.NewJobs(config, db, logger, settingRepo, certRepo, certAccountRepo, backupRepo, cacheRepo, taskRepo)
cron, err := bootstrap.NewCron(config, logger, jobs)
if err != nil {
return nil, err
}
validation := bootstrap.NewValidator(config, db)
ace := app.NewAce(config, mux, server, gormigrate, cron, queue, validation)
return ace, nil
}
-16
View File
@@ -1,19 +1,3 @@
/*
Copyright (C) 2022 - now Rat Technology Co., Ltd.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package main
import (
+6 -6
View File
@@ -5,12 +5,12 @@ package main
import (
"github.com/google/wire"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/apps"
"github.com/tnborg/panel/internal/bootstrap"
"github.com/tnborg/panel/internal/data"
"github.com/tnborg/panel/internal/route"
"github.com/tnborg/panel/internal/service"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/apps"
"github.com/acepanel/panel/internal/bootstrap"
"github.com/acepanel/panel/internal/data"
"github.com/acepanel/panel/internal/route"
"github.com/acepanel/panel/internal/service"
)
// initCli init command line.
+46 -50
View File
@@ -7,34 +7,32 @@
package main
import (
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/apps/codeserver"
"github.com/tnborg/panel/internal/apps/docker"
"github.com/tnborg/panel/internal/apps/fail2ban"
"github.com/tnborg/panel/internal/apps/frp"
"github.com/tnborg/panel/internal/apps/gitea"
"github.com/tnborg/panel/internal/apps/memcached"
"github.com/tnborg/panel/internal/apps/minio"
"github.com/tnborg/panel/internal/apps/mysql"
"github.com/tnborg/panel/internal/apps/nginx"
"github.com/tnborg/panel/internal/apps/php74"
"github.com/tnborg/panel/internal/apps/php80"
"github.com/tnborg/panel/internal/apps/php81"
"github.com/tnborg/panel/internal/apps/php82"
"github.com/tnborg/panel/internal/apps/php83"
"github.com/tnborg/panel/internal/apps/php84"
"github.com/tnborg/panel/internal/apps/phpmyadmin"
"github.com/tnborg/panel/internal/apps/podman"
"github.com/tnborg/panel/internal/apps/postgresql"
"github.com/tnborg/panel/internal/apps/pureftpd"
"github.com/tnborg/panel/internal/apps/redis"
"github.com/tnborg/panel/internal/apps/rsync"
"github.com/tnborg/panel/internal/apps/s3fs"
"github.com/tnborg/panel/internal/apps/supervisor"
"github.com/tnborg/panel/internal/bootstrap"
"github.com/tnborg/panel/internal/data"
"github.com/tnborg/panel/internal/route"
"github.com/tnborg/panel/internal/service"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/apps/apache"
"github.com/acepanel/panel/internal/apps/codeserver"
"github.com/acepanel/panel/internal/apps/docker"
"github.com/acepanel/panel/internal/apps/fail2ban"
"github.com/acepanel/panel/internal/apps/frp"
"github.com/acepanel/panel/internal/apps/gitea"
"github.com/acepanel/panel/internal/apps/mariadb"
"github.com/acepanel/panel/internal/apps/memcached"
"github.com/acepanel/panel/internal/apps/minio"
"github.com/acepanel/panel/internal/apps/mysql"
"github.com/acepanel/panel/internal/apps/nginx"
"github.com/acepanel/panel/internal/apps/openresty"
"github.com/acepanel/panel/internal/apps/percona"
"github.com/acepanel/panel/internal/apps/phpmyadmin"
"github.com/acepanel/panel/internal/apps/podman"
"github.com/acepanel/panel/internal/apps/postgresql"
"github.com/acepanel/panel/internal/apps/pureftpd"
"github.com/acepanel/panel/internal/apps/redis"
"github.com/acepanel/panel/internal/apps/rsync"
"github.com/acepanel/panel/internal/apps/s3fs"
"github.com/acepanel/panel/internal/apps/supervisor"
"github.com/acepanel/panel/internal/bootstrap"
"github.com/acepanel/panel/internal/data"
"github.com/acepanel/panel/internal/route"
"github.com/acepanel/panel/internal/service"
)
import (
@@ -45,60 +43,58 @@ import (
// initCli init command line.
func initCli() (*app.Cli, error) {
koanf, err := bootstrap.NewConf()
config, err := bootstrap.NewConf()
if err != nil {
return nil, err
}
locale, err := bootstrap.NewT(koanf)
locale, err := bootstrap.NewT(config)
if err != nil {
return nil, err
}
logger := bootstrap.NewLog(koanf)
db, err := bootstrap.NewDB(koanf, logger)
db, err := bootstrap.NewDB(config)
if err != nil {
return nil, err
}
logger := bootstrap.NewLog(config)
cacheRepo := data.NewCacheRepo(db)
queue := bootstrap.NewQueue()
taskRepo := data.NewTaskRepo(locale, db, logger, queue)
appRepo := data.NewAppRepo(locale, koanf, db, cacheRepo, taskRepo)
userRepo := data.NewUserRepo(locale, db)
settingRepo := data.NewSettingRepo(locale, db, koanf, taskRepo)
appRepo := data.NewAppRepo(locale, config, db, logger, cacheRepo, taskRepo)
userRepo := data.NewUserRepo(locale, db, logger)
settingRepo := data.NewSettingRepo(locale, db, logger, config, taskRepo)
databaseServerRepo := data.NewDatabaseServerRepo(locale, db, logger)
databaseUserRepo := data.NewDatabaseUserRepo(locale, db, databaseServerRepo)
databaseRepo := data.NewDatabaseRepo(locale, db, databaseServerRepo, databaseUserRepo)
certRepo := data.NewCertRepo(locale, db, logger)
databaseUserRepo := data.NewDatabaseUserRepo(locale, db, logger, databaseServerRepo)
databaseRepo := data.NewDatabaseRepo(locale, db, logger, databaseServerRepo, databaseUserRepo)
certRepo := data.NewCertRepo(locale, db, logger, settingRepo)
certAccountRepo := data.NewCertAccountRepo(locale, db, userRepo, logger)
websiteRepo := data.NewWebsiteRepo(locale, db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo)
backupRepo := data.NewBackupRepo(locale, db, settingRepo, websiteRepo)
cliService := service.NewCliService(locale, koanf, db, appRepo, cacheRepo, userRepo, settingRepo, backupRepo, websiteRepo, databaseServerRepo)
websiteRepo := data.NewWebsiteRepo(locale, db, logger, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo, settingRepo)
backupRepo := data.NewBackupRepo(locale, config, db, logger, settingRepo, websiteRepo)
cliService := service.NewCliService(locale, config, db, appRepo, cacheRepo, userRepo, settingRepo, backupRepo, websiteRepo, databaseServerRepo, certRepo, certAccountRepo)
cli := route.NewCli(locale, cliService)
command := bootstrap.NewCli(locale, cli)
gormigrate := bootstrap.NewMigrate(db)
apacheApp := apache.NewApp(locale)
codeserverApp := codeserver.NewApp()
dockerApp := docker.NewApp()
fail2banApp := fail2ban.NewApp(locale, websiteRepo)
frpApp := frp.NewApp()
giteaApp := gitea.NewApp()
mariadbApp := mariadb.NewApp(locale, settingRepo, databaseServerRepo)
memcachedApp := memcached.NewApp(locale)
minioApp := minio.NewApp()
mysqlApp := mysql.NewApp(locale, settingRepo)
mysqlApp := mysql.NewApp(locale, settingRepo, databaseServerRepo)
nginxApp := nginx.NewApp(locale)
php74App := php74.NewApp(locale, taskRepo)
php80App := php80.NewApp(locale, taskRepo)
php81App := php81.NewApp(locale, taskRepo)
php82App := php82.NewApp(locale, taskRepo)
php83App := php83.NewApp(locale, taskRepo)
php84App := php84.NewApp(locale, taskRepo)
openrestyApp := openresty.NewApp(locale)
perconaApp := percona.NewApp(locale, settingRepo, databaseServerRepo)
phpmyadminApp := phpmyadmin.NewApp(locale)
podmanApp := podman.NewApp()
postgresqlApp := postgresql.NewApp(locale)
postgresqlApp := postgresql.NewApp(locale, settingRepo, databaseServerRepo)
pureftpdApp := pureftpd.NewApp(locale)
redisApp := redis.NewApp(locale)
rsyncApp := rsync.NewApp(locale)
s3fsApp := s3fs.NewApp(locale)
supervisorApp := supervisor.NewApp(locale)
loader := bootstrap.NewLoader(codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, minioApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp)
loader := bootstrap.NewLoader(apacheApp, codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, mariadbApp, memcachedApp, minioApp, mysqlApp, nginxApp, openrestyApp, perconaApp, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp)
appCli := app.NewCli(command, gormigrate, loader)
return appCli, nil
}
-41
View File
@@ -1,41 +0,0 @@
/*
Copyright (C) 2022 - now Rat Technology Co., Ltd.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package main
import (
"os"
"runtime/debug"
_ "time/tzdata"
)
func main() {
if os.Geteuid() != 0 {
panic("panel must run as root")
}
debug.SetGCPercent(10)
debug.SetMemoryLimit(128 << 20)
web, err := initWeb()
if err != nil {
panic(err)
}
if err = web.Run(); err != nil {
panic(err)
}
}
-21
View File
@@ -1,21 +0,0 @@
//go:build wireinject
package main
import (
"github.com/google/wire"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/apps"
"github.com/tnborg/panel/internal/bootstrap"
"github.com/tnborg/panel/internal/data"
"github.com/tnborg/panel/internal/http/middleware"
"github.com/tnborg/panel/internal/job"
"github.com/tnborg/panel/internal/route"
"github.com/tnborg/panel/internal/service"
)
// 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.NewWeb))
}
-165
View File
@@ -1,165 +0,0 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run -mod=mod github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package main
import (
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/apps/codeserver"
"github.com/tnborg/panel/internal/apps/docker"
"github.com/tnborg/panel/internal/apps/fail2ban"
"github.com/tnborg/panel/internal/apps/frp"
"github.com/tnborg/panel/internal/apps/gitea"
"github.com/tnborg/panel/internal/apps/memcached"
"github.com/tnborg/panel/internal/apps/minio"
"github.com/tnborg/panel/internal/apps/mysql"
"github.com/tnborg/panel/internal/apps/nginx"
"github.com/tnborg/panel/internal/apps/php74"
"github.com/tnborg/panel/internal/apps/php80"
"github.com/tnborg/panel/internal/apps/php81"
"github.com/tnborg/panel/internal/apps/php82"
"github.com/tnborg/panel/internal/apps/php83"
"github.com/tnborg/panel/internal/apps/php84"
"github.com/tnborg/panel/internal/apps/phpmyadmin"
"github.com/tnborg/panel/internal/apps/podman"
"github.com/tnborg/panel/internal/apps/postgresql"
"github.com/tnborg/panel/internal/apps/pureftpd"
"github.com/tnborg/panel/internal/apps/redis"
"github.com/tnborg/panel/internal/apps/rsync"
"github.com/tnborg/panel/internal/apps/s3fs"
"github.com/tnborg/panel/internal/apps/supervisor"
"github.com/tnborg/panel/internal/bootstrap"
"github.com/tnborg/panel/internal/data"
"github.com/tnborg/panel/internal/http/middleware"
"github.com/tnborg/panel/internal/job"
"github.com/tnborg/panel/internal/route"
"github.com/tnborg/panel/internal/service"
)
import (
_ "time/tzdata"
)
// Injectors from wire.go:
// initWeb init application.
func initWeb() (*app.Web, error) {
koanf, err := bootstrap.NewConf()
if err != nil {
return nil, err
}
locale, err := bootstrap.NewT(koanf)
if err != nil {
return nil, err
}
logger := bootstrap.NewLog(koanf)
db, err := bootstrap.NewDB(koanf, logger)
if err != nil {
return nil, err
}
manager, err := bootstrap.NewSession(koanf, db)
if err != nil {
return nil, err
}
cacheRepo := data.NewCacheRepo(db)
queue := bootstrap.NewQueue()
taskRepo := data.NewTaskRepo(locale, db, logger, queue)
appRepo := data.NewAppRepo(locale, koanf, db, cacheRepo, taskRepo)
userTokenRepo := data.NewUserTokenRepo(locale, db)
middlewares := middleware.NewMiddlewares(koanf, logger, manager, appRepo, userTokenRepo)
userRepo := data.NewUserRepo(locale, db)
userService := service.NewUserService(locale, koanf, manager, userRepo)
userTokenService := service.NewUserTokenService(locale, userTokenRepo)
databaseServerRepo := data.NewDatabaseServerRepo(locale, db, logger)
databaseUserRepo := data.NewDatabaseUserRepo(locale, db, databaseServerRepo)
databaseRepo := data.NewDatabaseRepo(locale, db, databaseServerRepo, databaseUserRepo)
certRepo := data.NewCertRepo(locale, db, logger)
certAccountRepo := data.NewCertAccountRepo(locale, db, userRepo, logger)
websiteRepo := data.NewWebsiteRepo(locale, db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo)
settingRepo := data.NewSettingRepo(locale, db, koanf, taskRepo)
cronRepo := data.NewCronRepo(locale, db)
backupRepo := data.NewBackupRepo(locale, db, settingRepo, websiteRepo)
dashboardService := service.NewDashboardService(locale, koanf, taskRepo, websiteRepo, appRepo, settingRepo, cronRepo, backupRepo)
taskService := service.NewTaskService(taskRepo)
websiteService := service.NewWebsiteService(websiteRepo, settingRepo)
databaseService := service.NewDatabaseService(databaseRepo)
databaseServerService := service.NewDatabaseServerService(databaseServerRepo)
databaseUserService := service.NewDatabaseUserService(databaseUserRepo)
backupService := service.NewBackupService(locale, backupRepo)
certService := service.NewCertService(locale, certRepo)
certDNSRepo := data.NewCertDNSRepo(db)
certDNSService := service.NewCertDNSService(certDNSRepo)
certAccountService := service.NewCertAccountService(certAccountRepo)
appService := service.NewAppService(locale, appRepo, cacheRepo, settingRepo)
cronService := service.NewCronService(cronRepo)
processService := service.NewProcessService()
safeRepo := data.NewSafeRepo()
safeService := service.NewSafeService(safeRepo)
firewallService := service.NewFirewallService()
sshRepo := data.NewSSHRepo(locale, db)
sshService := service.NewSSHService(sshRepo)
containerRepo := data.NewContainerRepo()
containerService := service.NewContainerService(containerRepo)
containerComposeRepo := data.NewContainerComposeRepo()
containerComposeService := service.NewContainerComposeService(containerComposeRepo)
containerNetworkRepo := data.NewContainerNetworkRepo()
containerNetworkService := service.NewContainerNetworkService(containerNetworkRepo)
containerImageRepo := data.NewContainerImageRepo()
containerImageService := service.NewContainerImageService(containerImageRepo)
containerVolumeRepo := data.NewContainerVolumeRepo()
containerVolumeService := service.NewContainerVolumeService(containerVolumeRepo)
fileService := service.NewFileService(locale, taskRepo)
monitorRepo := data.NewMonitorRepo(db, settingRepo)
monitorService := service.NewMonitorService(settingRepo, monitorRepo)
settingService := service.NewSettingService(settingRepo)
systemctlService := service.NewSystemctlService(locale)
toolboxSystemService := service.NewToolboxSystemService(locale)
toolboxBenchmarkService := service.NewToolboxBenchmarkService(locale)
codeserverApp := codeserver.NewApp()
dockerApp := docker.NewApp()
fail2banApp := fail2ban.NewApp(locale, websiteRepo)
frpApp := frp.NewApp()
giteaApp := gitea.NewApp()
memcachedApp := memcached.NewApp(locale)
minioApp := minio.NewApp()
mysqlApp := mysql.NewApp(locale, settingRepo)
nginxApp := nginx.NewApp(locale)
php74App := php74.NewApp(locale, taskRepo)
php80App := php80.NewApp(locale, taskRepo)
php81App := php81.NewApp(locale, taskRepo)
php82App := php82.NewApp(locale, taskRepo)
php83App := php83.NewApp(locale, taskRepo)
php84App := php84.NewApp(locale, taskRepo)
phpmyadminApp := phpmyadmin.NewApp(locale)
podmanApp := podman.NewApp()
postgresqlApp := postgresql.NewApp(locale)
pureftpdApp := pureftpd.NewApp(locale)
redisApp := redis.NewApp(locale)
rsyncApp := rsync.NewApp(locale)
s3fsApp := s3fs.NewApp(locale)
supervisorApp := supervisor.NewApp(locale)
loader := bootstrap.NewLoader(codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, minioApp, mysqlApp, nginxApp, php74App, php80App, php81App, php82App, php83App, php84App, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp)
http := route.NewHttp(userService, userTokenService, dashboardService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, toolboxSystemService, toolboxBenchmarkService, loader)
wsService := service.NewWsService(locale, koanf, sshRepo)
ws := route.NewWs(wsService)
mux, err := bootstrap.NewRouter(locale, middlewares, http, ws)
if err != nil {
return nil, err
}
server, err := bootstrap.NewHttp(koanf, mux)
if err != nil {
return nil, err
}
gormigrate := bootstrap.NewMigrate(db)
jobs := job.NewJobs(db, logger, settingRepo, certRepo, backupRepo, cacheRepo, taskRepo)
cron, err := bootstrap.NewCron(koanf, logger, jobs)
if err != nil {
return nil, err
}
validation := bootstrap.NewValidator(koanf, db)
web := app.NewWeb(koanf, mux, server, gormigrate, cron, queue, validation)
return web, nil
}
+10 -1
View File
@@ -3,12 +3,21 @@ app:
key: a-long-string-with-32-characters
locale: zh_CN
timezone: Asia/Shanghai
root: /www
root: /opt/ace
api_endpoint: api.acepanel.net
download_endpoint: dl.acepanel.net
http:
debug: false
port: 8888
entrance: /
entrance_error: "418"
tls: true
acme: true
login_captcha: true
ip_header: ""
bind_domain: [ ]
bind_ip: [ ]
bind_ua: [ ]
database:
debug: false
session:
+2 -2
View File
@@ -1,5 +1,5 @@
commit_message: 'Update translations (%language%) %original_file_name%'
pull_request_title: 'l10n: update translations'
project_id_env: CROWDIN_PROJECT_ID
api_token_env: CROWDIN_PERSONAL_TOKEN
files:
- source: /pkg/embed/locales/*.pot
translation: /pkg/embed/locales/%locale_with_underscore%/%file_name%.po
+80 -63
View File
@@ -1,98 +1,115 @@
module github.com/tnborg/panel
module github.com/acepanel/panel
go 1.24.0
go 1.25.6
require (
github.com/bddjr/hlfhr v1.3.8
github.com/beevik/ntp v1.4.3
github.com/DeRuina/timberjack v1.3.9
github.com/bddjr/hlfhr v1.4.2
github.com/beevik/ntp v1.5.0
github.com/coder/websocket v1.8.14
github.com/containerd/errdefs v1.0.0
github.com/coreos/go-systemd/v22 v22.7.0
github.com/creack/pty v1.1.24
github.com/expr-lang/expr v1.17.6
github.com/go-chi/chi/v5 v5.2.2
github.com/go-gormigrate/gormigrate/v2 v2.1.4
github.com/go-resty/resty/v2 v2.16.5
github.com/dchest/captcha v1.1.0
github.com/expr-lang/expr v1.17.7
github.com/go-chi/chi/v5 v5.2.5
github.com/go-chi/httplog/v3 v3.3.0
github.com/go-gormigrate/gormigrate/v2 v2.1.5
github.com/go-resty/resty/v2 v2.17.1
github.com/go-sql-driver/mysql v1.9.3
github.com/golang-cz/httplog v0.0.2
github.com/gomodule/redigo v1.9.2
github.com/google/wire v0.6.0
github.com/gookit/color v1.5.4
github.com/gookit/validate v1.5.5
github.com/gorilla/websocket v1.5.3
github.com/hashicorp/go-version v1.7.0
github.com/knadh/koanf/parsers/yaml v1.1.0
github.com/knadh/koanf/providers/file v1.2.0
github.com/knadh/koanf/v2 v2.2.2
github.com/gomodule/redigo v1.9.3
github.com/google/wire v0.7.0
github.com/gookit/color v1.6.0
github.com/gookit/validate v1.5.7-0.20260207022141-dc32dc1276cd
github.com/hashicorp/go-version v1.8.0
github.com/klauspost/compress v1.18.3
github.com/leonelquinteros/gotext v1.7.2
github.com/lib/pq v1.10.9
github.com/libdns/alidns v1.0.5-libdns.v1.beta1
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6
github.com/lib/pq v1.11.1
github.com/libdns/alidns v1.0.6-beta.3.0.20260121104221-597b1e580e21
github.com/libdns/cloudflare v0.2.2
github.com/libdns/cloudns v1.1.0
github.com/libdns/gcore v0.0.0-20250427050847-9964da923833
github.com/libdns/hetzner v1.0.0
github.com/libdns/huaweicloud v1.0.0
github.com/libdns/libdns v1.1.0
github.com/libdns/libdns v1.1.1
github.com/libdns/namesilo v1.0.0
github.com/libdns/porkbun v1.1.0
github.com/libdns/tencentcloud v1.4.2
github.com/libdns/tencentcloud v1.4.3
github.com/libdns/westcn v1.0.2
github.com/libtnb/chix v1.3.0
github.com/libtnb/gormstore v1.1.0
github.com/libtnb/sessions v1.2.0
github.com/libtnb/utils v1.2.0
github.com/mholt/acmez/v3 v3.1.2
github.com/ncruces/go-sqlite3 v0.27.1
github.com/ncruces/go-sqlite3/gormlite v0.24.0
github.com/libtnb/chix v1.3.2
github.com/libtnb/gormstore v1.1.1
github.com/libtnb/sessions v1.2.2
github.com/libtnb/utils v1.2.1
github.com/mholt/acmez/v3 v3.1.4
github.com/moby/moby/api v1.53.0
github.com/moby/moby/client v0.2.2
github.com/ncruces/go-sqlite3 v0.30.5
github.com/ncruces/go-sqlite3/gormlite v0.30.2
github.com/orandin/slog-gorm v1.4.0
github.com/pkg/sftp v1.13.10
github.com/pquerna/otp v1.5.0
github.com/rhnvrm/simples3 v0.11.1
github.com/robfig/cron/v3 v3.0.1
github.com/samber/lo v1.51.0
github.com/sethvargo/go-limiter v1.0.1-0.20250412144437-fa26982c7e1a
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/cast v1.9.2
github.com/stretchr/testify v1.10.0
github.com/samber/lo v1.52.0
github.com/sethvargo/go-limiter v1.1.0
github.com/shirou/gopsutil/v4 v4.26.1
github.com/spf13/cast v1.10.0
github.com/stretchr/testify v1.11.1
github.com/studio-b12/gowebdav v0.12.0
github.com/tufanbarisyildirim/gonginx v0.0.0-20250620092546-c3e307e36701
github.com/urfave/cli/v3 v3.4.1
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/crypto v0.41.0
golang.org/x/net v0.43.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gorm.io/gorm v1.30.1
github.com/urfave/cli/v3 v3.6.2
go.yaml.in/yaml/v4 v4.0.0-rc.4
golang.org/x/crypto v0.47.0
golang.org/x/net v0.49.0
gorm.io/gorm v1.31.1
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/G-Core/gcore-dns-sdk-go v0.3.2 // indirect
github.com/G-Core/gcore-dns-sdk-go v0.3.3 // indirect
github.com/Microsoft/go-winio v0.6.2 // 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
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/ebitengine/purego v0.9.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
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/gookit/filter v1.2.2 // indirect
github.com/gookit/goutil v0.7.0 // indirect
github.com/gookit/filter v1.2.3 // indirect
github.com/gookit/goutil v0.7.3 // 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/knadh/koanf/maps v0.1.2 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/libtnb/securecookie v1.2.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/ncruces/julianday v1.0.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/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/stretchr/objx v0.5.3 // indirect
github.com/tetratelabs/wazero v1.11.0 // indirect
github.com/timtadh/data-structures v0.6.2 // indirect
github.com/timtadh/lexmachine v0.2.3 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.11.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
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect
)
replace github.com/mholt/acmez/v3 => github.com/libtnb/acmez/v3 v3.0.0-20250707093727-dc5aedd96413
replace (
github.com/mholt/acmez/v3 => github.com/libtnb/acmez/v3 v3.0.0-20260103184942-a835890fc93e
github.com/moby/moby/client => github.com/libtnb/moby/client v0.0.0-20260127104057-d4b7902a560b
github.com/rhnvrm/simples3 => github.com/devhaozi/simples3 v0.0.0-20260124160558-447c94ecedff
github.com/stretchr/testify => github.com/libtnb/testify v0.0.0-20260103194301-c7a63ea79696
)
tool github.com/google/wire
+154 -182
View File
@@ -15,18 +15,22 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/G-Core/gcore-dns-sdk-go v0.3.2 h1:WCTKmoJ5uND69UFE318yxWuHy/h3AA28Z1OnqSYUxRk=
github.com/G-Core/gcore-dns-sdk-go v0.3.2/go.mod h1:35t795gOfzfVanhzkFyUXEzaBuMXwETmJldPpP28MN4=
github.com/DeRuina/timberjack v1.3.9 h1:6UXZ1I7ExPGTX/1UNYawR58LlOJUHKBPiYC7WQ91eBo=
github.com/DeRuina/timberjack v1.3.9/go.mod h1:RLoeQrwrCGIEF8gO5nV5b/gMD0QIy7bzQhBUgpp1EqE=
github.com/G-Core/gcore-dns-sdk-go v0.3.3 h1:McILJSbJ5nOcT0MI0aBYhEuufCF329YbqKwFIN0RjCI=
github.com/G-Core/gcore-dns-sdk-go v0.3.3/go.mod h1:35t795gOfzfVanhzkFyUXEzaBuMXwETmJldPpP28MN4=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
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/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=
github.com/bddjr/hlfhr v1.3.8 h1:QQ6KYgtnBbvYvCWuu/tOnBZamKAPtJzesj2qbjgyn7o=
github.com/bddjr/hlfhr v1.3.8/go.mod h1:oyIv4Q9JpCgZFdtH3KyTNWp7YYRWl4zl8k4ozrMAB4g=
github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho=
github.com/beevik/ntp v1.4.3/go.mod h1:Unr8Zg+2dRn7d8bHFuehIMSvvUYssHMxW3Q5Nx4RW5Q=
github.com/bddjr/hlfhr v1.4.2 h1:ZkV42xi+jvI1goLAbkZHcY8Wxa2uBD1mK/JwL7o2LWw=
github.com/bddjr/hlfhr v1.4.2/go.mod h1:oyIv4Q9JpCgZFdtH3KyTNWp7YYRWl4zl8k4ozrMAB4g=
github.com/beevik/ntp v1.5.0 h1:y+uj/JjNwlY2JahivxYvtmv4ehfi3h74fAuABB9ZSM4=
github.com/beevik/ntp v1.5.0/go.mod h1:mJEhBrwT76w9D+IfOEGvuzyuudiW9E52U2BaTrMOYow=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
@@ -36,54 +40,70 @@ github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVk
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/captcha v1.1.0 h1:2kt47EoYUUkaISobUdTbqwx55xvKOJxyScVfw25xzhQ=
github.com/dchest/captcha v1.1.0/go.mod h1:7zoElIawLp7GUMLcj54K9kbw+jEyvz2K0FDdRRYhvWo=
github.com/devhaozi/simples3 v0.0.0-20260124160558-447c94ecedff h1:R8fAbRKWR+NVWIkjdK45CXS2cZzXQn8QuoM0Dij2Jso=
github.com/devhaozi/simples3 v0.0.0-20260124160558-447c94ecedff/go.mod h1:c2xW30bukipkBlWNnXG1wDjq3gykQ6ww2AB/9NHMLMY=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/expr-lang/expr v1.17.6 h1:1h6i8ONk9cexhDmowO/A64VPxHScu7qfSl2k8OlINec=
github.com/expr-lang/expr v1.17.6/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8=
github.com/expr-lang/expr v1.17.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
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/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/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
github.com/go-chi/httplog/v3 v3.3.0 h1:Gr6Y7nSzbpyCyRwKPOVKjDH3BH6TH5uvRNDsTZWDpvU=
github.com/go-chi/httplog/v3 v3.3.0/go.mod h1:N/J1l5l1fozUrqIVuT8Z/HzNeSy8TF2EFyokPLe6y2w=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gormigrate/gormigrate/v2 v2.1.4 h1:KOPEt27qy1cNzHfMZbp9YTmEuzkY4F4wrdsJW9WFk1U=
github.com/go-gormigrate/gormigrate/v2 v2.1.4/go.mod h1:y/6gPAH6QGAgP1UfHMiXcqGeJ88/GRQbfCReE1JJD5Y=
github.com/go-gormigrate/gormigrate/v2 v2.1.5 h1:1OyorA5LtdQw12cyJDEHuTrEV3GiXiIhS4/QTTa/SM8=
github.com/go-gormigrate/gormigrate/v2 v2.1.5/go.mod h1:mj9ekk/7CPF3VjopaFvWKN2v7fN3D9d3eEOAXRhi/+M=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM=
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
github.com/go-resty/resty/v2 v2.17.1 h1:x3aMpHK1YM9e4va/TMDRlusDDoZiQ+ViDu/WpA6xTM4=
github.com/go-resty/resty/v2 v2.17.1/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
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/schema v1.6.0 h1:rAgVDFwhndtC+hgV7Vu5ItQCn7eC2mBA4Eu1/ZTiEYY=
github.com/gofiber/schema v1.6.0/go.mod h1:WNZWpQx8LlPSK7ZaX0OqOh+nQo/eW2OevsXs1VZfs/s=
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=
github.com/golang-cz/httplog v0.0.2/go.mod h1:bgk4Ij/0OQ89UeoFFAQrSNhbbr4rKJ0fwWfo7wc+TCc=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -92,37 +112,36 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s=
github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
github.com/gomodule/redigo v1.9.3 h1:dNPSXeXv6HCq2jdyWfjgmhBdqnR6PRO3m/G05nvpPC8=
github.com/gomodule/redigo v1.9.3/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
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/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
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=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gookit/filter v1.2.2 h1:LSBQLk4M4fpfhaOG0hJ/GJ+qu+2+lLddgxlGfzcd8VQ=
github.com/gookit/filter v1.2.2/go.mod h1:y4xpiqM/fV9g9yT/Vg5OWUq4q9siPj0UlH9O57xYuGs=
github.com/gookit/goutil v0.7.0 h1:HD4PUDW2LOSKIEBJPFD8PzNGLsL46ztpfXWVU+WtAxk=
github.com/gookit/goutil v0.7.0/go.mod h1:vJS9HXctYTCLtCsZot5L5xF+O1oR17cDYO9R0HxBmnU=
github.com/gookit/validate v1.5.5 h1:jzMrwAYy9tbQa0mH8YFnv3C3JQR/0N4pRTsYiySwRJM=
github.com/gookit/validate v1.5.5/go.mod h1:p9sRPfpvYB4vXICBpEPzv8FoAky+XhUOhWQghgmmat4=
github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
github.com/gookit/filter v1.2.3 h1:Zo7cBOtsVzAoa/jtf+Ury6zlsbJXqInFdUpbbnB2vMM=
github.com/gookit/filter v1.2.3/go.mod h1:nFLJcOV8dRgS1iiX23gUQgmHUhpuS40qCvAGgIvA1pM=
github.com/gookit/goutil v0.7.3 h1:nXDd/AB17nEjqVCNDGioDhVL/gVqdlqRMfFergKDjHE=
github.com/gookit/goutil v0.7.3/go.mod h1:vJS9HXctYTCLtCsZot5L5xF+O1oR17cDYO9R0HxBmnU=
github.com/gookit/validate v1.5.7-0.20260207022141-dc32dc1276cd h1:J6cF+xLHI6ZSPBB/XMLj9xbmWuUGw5Vxbs0wOaZ5HOo=
github.com/gookit/validate v1.5.7-0.20260207022141-dc32dc1276cd/go.mod h1:2PY9IRPsKFS6brOHv6PKhZeuUD99Mh3BimDlOOXnGBg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
@@ -138,8 +157,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=
github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -166,15 +185,10 @@ 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/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=
github.com/knadh/koanf/parsers/yaml v1.1.0/go.mod h1:HHmcHXUrp9cOPcuC+2wrr44GTUB0EC+PyfN3HZD9tFg=
github.com/knadh/koanf/providers/file v1.2.0 h1:hrUJ6Y9YOA49aNu/RSYzOTFlqzXSCpmYIDXI7OJU6+U=
github.com/knadh/koanf/providers/file v1.2.0/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA=
github.com/knadh/koanf/v2 v2.2.2 h1:ghbduIkpFui3L587wavneC9e3WIliCgiCgdxYO/wd7A=
github.com/knadh/koanf/v2 v2.2.2/go.mod h1:abWQc0cBXLSF/PSOMCB/SK+T13NXDsPvOksbpi5e/9Q=
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -186,51 +200,52 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leonelquinteros/gotext v1.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+YP7G1Mc=
github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libdns/alidns v1.0.5-libdns.v1.beta1 h1:txHK7UxDed3WFBDjrTZPuMn8X+WmhjBTTAMW5xdy5pQ=
github.com/libdns/alidns v1.0.5-libdns.v1.beta1/go.mod h1:ystHmPwcGoWjPrGpensQSMY9VoCx4cpR2hXNlwk9H/g=
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6 h1:3MGrVWs2COjMkQR17oUw1zMIPbm2YAzxDC3oGVZvQs8=
github.com/libdns/cloudflare v0.2.2-0.20250708034226-c574dccb31a6/go.mod h1:w9uTmRCDlAoafAsTPnn2nJ0XHK/eaUMh86DUk8BWi60=
github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI=
github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
github.com/libdns/alidns v1.0.6-beta.3.0.20260121104221-597b1e580e21 h1:BGtptgrEJGjDQz63r4xkcQ/2ERXpna9WVUtrTtmW8d0=
github.com/libdns/alidns v1.0.6-beta.3.0.20260121104221-597b1e580e21/go.mod h1:RECwyQ88e9VqQVtSrvX76o1ux3gQUKGzMgxICi+u7Ec=
github.com/libdns/cloudflare v0.2.2 h1:XWHv+C1dDcApqazlh08Q6pjytYLgR2a+Y3xrXFu0vsI=
github.com/libdns/cloudflare v0.2.2/go.mod h1:w9uTmRCDlAoafAsTPnn2nJ0XHK/eaUMh86DUk8BWi60=
github.com/libdns/cloudns v1.1.0 h1:W+1MadtxKySn3b5RITFTsXgTIvr5VoO5x97cewjlDcs=
github.com/libdns/cloudns v1.1.0/go.mod h1:/22V6tYYDALDpM4pw/RGGJ+X2F1Luibty9kKpKvkqBM=
github.com/libdns/gcore v0.0.0-20250427050847-9964da923833 h1:/gsawtsq03cI7qgK65v16tYHIh40omL9YEzHWqnc63I=
github.com/libdns/gcore v0.0.0-20250427050847-9964da923833/go.mod h1:jZJEV7pCTOJFlaUhHty+YwR05dzoSmInXK/vT3wOeVg=
github.com/libdns/hetzner v1.0.0 h1:dFcgqTIfdiKQTqoqBBtgU9CewD8JSnB7p6BKxQ5kheM=
github.com/libdns/hetzner v1.0.0/go.mod h1:OmuTyXMHTfy2nCqbt9KYkf0KwQSvo0ZeFGxEQSl3r2w=
github.com/libdns/huaweicloud v1.0.0 h1:BQUIkOAjF++ouiANRIE3jdMlCfeiAAr6tGqb+8hM1jo=
github.com/libdns/huaweicloud v1.0.0/go.mod h1:W+XywkW+C93fM50Ayklf6KuoCHZTqbrmFUEtpQnVvcU=
github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/libdns/libdns v1.1.0 h1:9ze/tWvt7Df6sbhOJRB8jT33GHEHpEQXdtkE3hPthbU=
github.com/libdns/libdns v1.1.0/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U=
github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/libdns/namesilo v1.0.0 h1:Shwbj9YnSp4NR617sBOSfDkFozEnWInWCxNwrDvPkCg=
github.com/libdns/namesilo v1.0.0/go.mod h1:qdojVsogA6eZDjDPdpIUIfv4ymiX+FuqcVV1eNmHDss=
github.com/libdns/porkbun v1.1.0 h1:X763NqXjW26VEl7GvBtF/3CGeuGt9JqoQ35mwIlx40E=
github.com/libdns/porkbun v1.1.0/go.mod h1:JL6NfXkkSlLr24AI5Fv0t3/Oa6PXOSOerVsOmr8+URs=
github.com/libdns/tencentcloud v1.4.2 h1:UXvNISlwmdAapYxMgwDZW7uvn8+Bor+puLx1p6VH0c0=
github.com/libdns/tencentcloud v1.4.2/go.mod h1:Be9gY3tDa12DuAPU79RV9NZIcjY6qg5s7zKPsP26yAM=
github.com/libdns/tencentcloud v1.4.3 h1:xJHYLL1TdPeOtUr6Bu6dHTd1TU6/VFm7BFc2EAzAlvc=
github.com/libdns/tencentcloud v1.4.3/go.mod h1:Be9gY3tDa12DuAPU79RV9NZIcjY6qg5s7zKPsP26yAM=
github.com/libdns/westcn v1.0.2 h1:PA2M3tME5/0T3klPMzSHvGk1EWnGNNkJiKxaJjFalnM=
github.com/libdns/westcn v1.0.2/go.mod h1:iKpk8jjOU+793Yp8nHoihPTCR6N1KWtm4/r8BB9mVnk=
github.com/libtnb/acmez/v3 v3.0.0-20250707093727-dc5aedd96413 h1:q6Qttk+i7r8JWw1kvt0gBkCa+9mEUZ3KV25rBs2Kiik=
github.com/libtnb/acmez/v3 v3.0.0-20250707093727-dc5aedd96413/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/libtnb/chix v1.3.0 h1:/U+CyuxI41ooeB6M/762PHOjQlfHRg6BQjHKLZarlrM=
github.com/libtnb/chix v1.3.0/go.mod h1:o8nQLEp/UrUojBKYzw8K8sltU/h0XxI2VLZ/z7AQCQg=
github.com/libtnb/gormstore v1.1.0 h1:VbX8u0hhyl2YFSGfkSlF/xSfFG/LE29DCLrY8MJ+uxM=
github.com/libtnb/gormstore v1.1.0/go.mod h1:8A5QzeZxi1MpSmjUVsHTDAL6KnU84feIXMutFLPawwA=
github.com/libtnb/acmez/v3 v3.0.0-20260103184942-a835890fc93e h1:HPHmtFcR5VpTJTpDqG4+l16uJfSlpB/X0r3qq22znrk=
github.com/libtnb/acmez/v3 v3.0.0-20260103184942-a835890fc93e/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/libtnb/chix v1.3.2 h1:LkA/+OaHeMyTiQoWEcCQKAMSKDZ0LxBXSENQO5d+OKk=
github.com/libtnb/chix v1.3.2/go.mod h1:ItssStBa/ov7v4qujyYzORqIf7Z6DO7Q4carOM7pyAQ=
github.com/libtnb/gormstore v1.1.1 h1:FG/3P4PuWM6/vB4weVJ31meiSaoeXns1NQlP66quKeg=
github.com/libtnb/gormstore v1.1.1/go.mod h1:8A5QzeZxi1MpSmjUVsHTDAL6KnU84feIXMutFLPawwA=
github.com/libtnb/moby/client v0.0.0-20260127104057-d4b7902a560b h1:73OlJdl4Zk6Ipnm7KKeE6yCjfd7nk0BhTArujx6WJCU=
github.com/libtnb/moby/client v0.0.0-20260127104057-d4b7902a560b/go.mod h1:bBQRIiaGR9EZBC+x5hnwunNgcDOts7E1Oa7AowJwMFU=
github.com/libtnb/securecookie v1.2.0 h1:2uc0PBDm0foeSTrcZ9QTX1IEjf6kFEwfgEYSIXQSKrA=
github.com/libtnb/securecookie v1.2.0/go.mod h1:ja+wNGnQzYqcqXQnJWu6icsaWi5JEBwNEMJ2ReTVDxA=
github.com/libtnb/sessions v1.2.0 h1:g/RMcfGTC5P2BQE1IqrgppPEQ++x/QjOuiwbN9Frke8=
github.com/libtnb/sessions v1.2.0/go.mod h1:45Bn9d6PseDINLIM1QaJrlCMbzSZ0NWpDbWkdrKJKw0=
github.com/libtnb/utils v1.2.0 h1:6bTZrWn2OkNrODpCY4dhuHwbhsVRV7HICIgmZ31we98=
github.com/libtnb/utils v1.2.0/go.mod h1:9gSEuhkADlvYbM3qJRQUAMC5ypMrhrYpX9YMuYJ6ws8=
github.com/libtnb/sessions v1.2.2 h1:VTTzzeBDJEkJbaPaIU9C4bRj2oAqD0rgQ7UHFkkaNT4=
github.com/libtnb/sessions v1.2.2/go.mod h1:qw+FWtBtrPDYCf6MfX0Lk5EhTArpvT72z5Ei4RUMTRg=
github.com/libtnb/testify v0.0.0-20260103194301-c7a63ea79696 h1:GN0Y3DG27mMruX536k0jtCSLegjJb/gypzj5gZL6tRI=
github.com/libtnb/testify v0.0.0-20260103194301-c7a63ea79696/go.mod h1:HeQeTfKU6tj2Lx1z79UacwYeDioo6M4ZD7BDDI6+rrg=
github.com/libtnb/utils v1.2.1 h1:LJmReRREnpqfHyy9PZtNgBh3ZaIGct81b8ZaAsolMkM=
github.com/libtnb/utils v1.2.1/go.mod h1:o6LEDeC42PXI21uLWdWJWTVYvR9BtAZfzzTGJVQoQiU=
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k=
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
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-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
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=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
@@ -238,18 +253,24 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/moby/api v1.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w=
github.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncruces/go-sqlite3 v0.27.1 h1:suqlM7xhSyDVMV9RgX99MCPqt9mB6YOCzHZuiI36K34=
github.com/ncruces/go-sqlite3 v0.27.1/go.mod h1:gpF5s+92aw2MbDmZK0ZOnCdFlpe11BH20CTspVqri0c=
github.com/ncruces/go-sqlite3/gormlite v0.24.0 h1:81sHeq3CCdhjoqAB650n5wEdRlLO9VBvosArskcN3+c=
github.com/ncruces/go-sqlite3/gormlite v0.24.0/go.mod h1:vXfVWdBfg7qOgqQqHpzUWl9LLswD0h+8mK4oouaV2oc=
github.com/ncruces/go-sqlite3 v0.30.5 h1:6usmTQ6khriL8oWilkAZSJM/AIpAlVL2zFrlcpDldCE=
github.com/ncruces/go-sqlite3 v0.30.5/go.mod h1:0I0JFflTKzfs3Ogfv8erP7CCoV/Z8uxigVDNOR0AQ5E=
github.com/ncruces/go-sqlite3/gormlite v0.30.2 h1:FZ8mic14xTatssTkHCrelh9nPeFdXuzgMoNGkfuFbBU=
github.com/ncruces/go-sqlite3/gormlite v0.30.2/go.mod h1:W9WLBbqrrOIh2dqFZkeC/xKALG2LDIHY91jowahOdtI=
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/orandin/slog-gorm v1.4.0 h1:FgA8hJufF9/jeNSYoEXmHPPBwET2gwlF3B85JdpsTUU=
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=
@@ -257,9 +278,12 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
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=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -275,17 +299,17 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
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/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/sethvargo/go-limiter v1.1.0 h1:eLeZVQ2zqJOiEs03GguqmBVG6/T6lsZB+6PP1t7J6fA=
github.com/sethvargo/go-limiter v1.1.0/go.mod h1:01b6tW25Ap+MeLYBuD4aHunMrJoNO5PVUFdS9rac3II=
github.com/shirou/gopsutil/v4 v4.26.1 h1:TOkEyriIXk2HX9d4isZJtbjXbEjf5qyKPAzbzY0JWSo=
github.com/shirou/gopsutil/v4 v4.26.1/go.mod h1:medLI9/UNAb0dOI9Q3/7yWSqKkj00u+1tgY8nvv41pc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@@ -295,26 +319,21 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
github.com/studio-b12/gowebdav v0.12.0 h1:kFRtQECt8jmVAvA6RHBz3geXUGJHUZA6/IKpOVUs5kM=
github.com/studio-b12/gowebdav v0.12.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
github.com/timtadh/data-structures v0.5.3/go.mod h1:9R4XODhJ8JdWFEI8P/HJKqxuJctfBQw6fDibMQny2oU=
github.com/timtadh/data-structures v0.6.1/go.mod h1:uYUnI1cQi/5yMCc7s23I+x8Mn8BCMf4WgK+7/4QSEk4=
github.com/timtadh/data-structures v0.6.2 h1:zybDnU5NLjJ7WKMDJpvVwczQuf1wSLBgdRHZ9O4AqJ0=
@@ -323,19 +342,18 @@ 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/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=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tufanbarisyildirim/gonginx v0.0.0-20250620092546-c3e307e36701 h1:JgeHIJzRSEdcuLXufZrni5+a4yDnBhQG+DdKhqCFhq0=
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/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8=
github.com/urfave/cli/v3 v3.6.2/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
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=
@@ -344,26 +362,23 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U=
go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4=
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc=
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=
golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -377,10 +392,6 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -395,14 +406,8 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -411,12 +416,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -431,40 +432,22 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -482,11 +465,6 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -512,28 +490,22 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.30.1 h1:lSHg33jJTBxs2mgJRfRZeLDG+WZaHYCk3Wtfl6Ngzo4=
gorm.io/gorm v1.30.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+119
View File
@@ -0,0 +1,119 @@
package app
import (
"context"
"errors"
"fmt"
"net/http"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"
"github.com/bddjr/hlfhr"
"github.com/go-chi/chi/v5"
"github.com/go-gormigrate/gormigrate/v2"
"github.com/gookit/validate"
"github.com/robfig/cron/v3"
"github.com/acepanel/panel/pkg/config"
"github.com/acepanel/panel/pkg/queue"
)
type Ace struct {
conf *config.Config
router *chi.Mux
server *hlfhr.Server
migrator *gormigrate.Gormigrate
cron *cron.Cron
queue *queue.Queue
}
func NewAce(conf *config.Config, router *chi.Mux, server *hlfhr.Server, migrator *gormigrate.Gormigrate, cron *cron.Cron, queue *queue.Queue, _ *validate.Validation) *Ace {
return &Ace{
conf: conf,
router: router,
server: server,
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")
// create context for queue
queueCtx, queueCancel := context.WithCancel(context.Background())
defer queueCancel()
// start queue
r.queue.Run(queueCtx)
// setup graceful shutdown
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
// run http server in goroutine
serverErr := make(chan error, 1)
go func() {
if r.conf.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.HTTP.Port, "with tls")
if err := r.server.ListenAndServeTLS(cert, key); !errors.Is(err, http.ErrServerClosed) {
serverErr <- err
}
} else {
fmt.Println("[HTTP] listening and serving on port", r.conf.HTTP.Port)
if err := r.server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
serverErr <- err
}
}
close(serverErr)
}()
// wait for shutdown signal or server error
select {
case err := <-serverErr:
if err != nil {
return err
}
case sig := <-quit:
fmt.Println("\n[APP] received signal:", sig)
}
// graceful shutdown
fmt.Println("[APP] shutting down gracefully...")
// stop cron scheduler
ctx := r.cron.Stop()
<-ctx.Done()
fmt.Println("[CRON] cron scheduler stopped")
// stop queue
queueCancel()
fmt.Println("[QUEUE] queue stopped")
// shutdown http server
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer shutdownCancel()
if err := r.server.Shutdown(shutdownCtx); err != nil {
fmt.Println("[HTTP] server shutdown error:", err)
return err
}
fmt.Println("[HTTP] server stopped")
fmt.Println("[APP] shutdown complete")
return nil
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"github.com/gookit/color"
"github.com/urfave/cli/v3"
"github.com/tnborg/panel/pkg/apploader"
"github.com/acepanel/panel/pkg/apploader"
)
type Cli struct {
-70
View File
@@ -1,70 +0,0 @@
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
}
+157
View File
@@ -0,0 +1,157 @@
package apache
import (
"fmt"
"net/http"
"regexp"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/spf13/cast"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/shell"
"github.com/acepanel/panel/pkg/systemctl"
"github.com/acepanel/panel/pkg/tools"
"github.com/acepanel/panel/pkg/types"
)
type App struct {
t *gotext.Locale
}
func NewApp(t *gotext.Locale) *App {
return &App{
t: t,
}
}
func (s *App) Route(r chi.Router) {
r.Get("/load", s.Load)
r.Get("/config", s.GetConfig)
r.Post("/config", s.SaveConfig)
r.Get("/error_log", s.ErrorLog)
r.Post("/clear_error_log", s.ClearErrorLog)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/apache/conf/httpd.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, config)
}
func (s *App) SaveConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if err = io.Write(fmt.Sprintf("%s/server/apache/conf/httpd.conf", app.Root), req.Config, 0600); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Reload("apache"); err != nil {
_, err = shell.Execf("%s/server/apache/bin/apachectl configtest", app.Root)
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload apache: %v", err))
return
}
service.Success(w, nil)
}
func (s *App) ErrorLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/%s", app.Root, "server/apache/logs/error_log"))
}
func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("cat /dev/null > %s/%s", app.Root, "server/apache/logs/error_log"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
status, err := shell.Execf("curl -s http://127.0.0.1/server_status?auto 2>/dev/null || true")
if err != nil {
service.Success(w, []types.NV{})
return
}
var data []types.NV
workers, err := shell.Execf("ps aux | grep httpd | grep -v grep | wc -l")
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get apache workers: %v", err))
return
}
data = append(data, types.NV{
Name: s.t.Get("Workers"),
Value: workers,
})
out, err := shell.Execf("ps aux | grep httpd | grep -v grep | awk '{memsum+=$6};END {print memsum}'")
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get apache workers: %v", err))
return
}
mem := tools.FormatBytes(cast.ToFloat64(out))
data = append(data, types.NV{
Name: s.t.Get("Memory"),
Value: mem,
})
// Parse server-status output
if match := regexp.MustCompile(`Total Accesses:\s*(\d+)`).FindStringSubmatch(status); len(match) == 2 {
data = append(data, types.NV{
Name: s.t.Get("Total Accesses"),
Value: match[1],
})
}
if match := regexp.MustCompile(`Total kBytes:\s*(\d+)`).FindStringSubmatch(status); len(match) == 2 {
data = append(data, types.NV{
Name: s.t.Get("Total Traffic"),
Value: tools.FormatBytes(cast.ToFloat64(match[1]) * 1024),
})
}
if match := regexp.MustCompile(`BusyWorkers:\s*(\d+)`).FindStringSubmatch(status); len(match) == 2 {
data = append(data, types.NV{
Name: s.t.Get("Busy Workers"),
Value: match[1],
})
}
if match := regexp.MustCompile(`IdleWorkers:\s*(\d+)`).FindStringSubmatch(status); len(match) == 2 {
data = append(data, types.NV{
Name: s.t.Get("Idle Workers"),
Value: match[1],
})
}
if match := regexp.MustCompile(`ReqPerSec:\s*([\d.]+)`).FindStringSubmatch(status); len(match) == 2 {
data = append(data, types.NV{
Name: s.t.Get("Requests/sec"),
Value: match[1],
})
}
if match := regexp.MustCompile(`BytesPerSec:\s*([\d.]+)`).FindStringSubmatch(status); len(match) == 2 {
data = append(data, types.NV{
Name: s.t.Get("Bytes/sec"),
Value: tools.FormatBytes(cast.ToFloat64(match[1])),
})
}
service.Success(w, data)
}
+5
View File
@@ -0,0 +1,5 @@
package apache
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
+25 -29
View File
@@ -3,47 +3,43 @@ package apps
import (
"github.com/google/wire"
"github.com/tnborg/panel/internal/apps/codeserver"
"github.com/tnborg/panel/internal/apps/docker"
"github.com/tnborg/panel/internal/apps/fail2ban"
"github.com/tnborg/panel/internal/apps/frp"
"github.com/tnborg/panel/internal/apps/gitea"
"github.com/tnborg/panel/internal/apps/memcached"
"github.com/tnborg/panel/internal/apps/minio"
"github.com/tnborg/panel/internal/apps/mysql"
"github.com/tnborg/panel/internal/apps/nginx"
"github.com/tnborg/panel/internal/apps/php74"
"github.com/tnborg/panel/internal/apps/php80"
"github.com/tnborg/panel/internal/apps/php81"
"github.com/tnborg/panel/internal/apps/php82"
"github.com/tnborg/panel/internal/apps/php83"
"github.com/tnborg/panel/internal/apps/php84"
"github.com/tnborg/panel/internal/apps/phpmyadmin"
"github.com/tnborg/panel/internal/apps/podman"
"github.com/tnborg/panel/internal/apps/postgresql"
"github.com/tnborg/panel/internal/apps/pureftpd"
"github.com/tnborg/panel/internal/apps/redis"
"github.com/tnborg/panel/internal/apps/rsync"
"github.com/tnborg/panel/internal/apps/s3fs"
"github.com/tnborg/panel/internal/apps/supervisor"
"github.com/acepanel/panel/internal/apps/apache"
"github.com/acepanel/panel/internal/apps/codeserver"
"github.com/acepanel/panel/internal/apps/docker"
"github.com/acepanel/panel/internal/apps/fail2ban"
"github.com/acepanel/panel/internal/apps/frp"
"github.com/acepanel/panel/internal/apps/gitea"
"github.com/acepanel/panel/internal/apps/mariadb"
"github.com/acepanel/panel/internal/apps/memcached"
"github.com/acepanel/panel/internal/apps/minio"
"github.com/acepanel/panel/internal/apps/mysql"
"github.com/acepanel/panel/internal/apps/nginx"
"github.com/acepanel/panel/internal/apps/openresty"
"github.com/acepanel/panel/internal/apps/percona"
"github.com/acepanel/panel/internal/apps/phpmyadmin"
"github.com/acepanel/panel/internal/apps/podman"
"github.com/acepanel/panel/internal/apps/postgresql"
"github.com/acepanel/panel/internal/apps/pureftpd"
"github.com/acepanel/panel/internal/apps/redis"
"github.com/acepanel/panel/internal/apps/rsync"
"github.com/acepanel/panel/internal/apps/s3fs"
"github.com/acepanel/panel/internal/apps/supervisor"
)
var ProviderSet = wire.NewSet(
apache.NewApp,
codeserver.NewApp,
docker.NewApp,
fail2ban.NewApp,
frp.NewApp,
gitea.NewApp,
mariadb.NewApp,
memcached.NewApp,
minio.NewApp,
mysql.NewApp,
nginx.NewApp,
php74.NewApp,
php80.NewApp,
php81.NewApp,
php82.NewApp,
php83.NewApp,
php84.NewApp,
openresty.NewApp,
percona.NewApp,
phpmyadmin.NewApp,
podman.NewApp,
postgresql.NewApp,
+3 -3
View File
@@ -5,9 +5,9 @@ import (
"github.com/go-chi/chi/v5"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/systemctl"
)
type App struct{}
+214 -3
View File
@@ -1,13 +1,16 @@
package docker
import (
"encoding/json"
"net/http"
"os"
"strings"
"github.com/go-chi/chi/v5"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/systemctl"
)
type App struct{}
@@ -19,6 +22,8 @@ func NewApp() *App {
func (s *App) Route(r chi.Router) {
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
r.Get("/settings", s.GetSettings)
r.Post("/settings", s.UpdateSettings)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
@@ -50,3 +55,209 @@ func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
// GetSettings 获取 Docker 设置
func (s *App) GetSettings(w http.ResponseWriter, r *http.Request) {
configPath := "/etc/docker/daemon.json"
content, err := io.Read(configPath)
if err != nil {
if os.IsNotExist(err) {
service.Success(w, Settings{})
return
}
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
var daemonConfig DaemonConfig
if err = json.Unmarshal([]byte(content), &daemonConfig); err != nil {
service.Success(w, Settings{}) // 配置文件可能为空或格式错误,返回默认设置
return
}
settings := Settings{
RegistryMirrors: daemonConfig.RegistryMirrors,
InsecureRegistries: daemonConfig.InsecureRegistries,
LiveRestore: daemonConfig.LiveRestore,
LogDriver: daemonConfig.LogDriver,
Hosts: daemonConfig.Hosts,
DataRoot: daemonConfig.DataRoot,
StorageDriver: daemonConfig.StorageDriver,
DNS: daemonConfig.DNS,
FirewallBackend: daemonConfig.FirewallBackend,
Iptables: daemonConfig.Iptables,
Ip6tables: daemonConfig.Ip6tables,
IpForward: daemonConfig.IpForward,
IPv6: daemonConfig.IPv6,
Bip: daemonConfig.Bip,
}
// 解析 log-opts
if daemonConfig.LogOpts != nil {
settings.LogOpts = LogOpts{
MaxSize: daemonConfig.LogOpts["max-size"],
MaxFile: daemonConfig.LogOpts["max-file"],
}
}
// 从 exec-opts 中提取 cgroup-driver
for _, opt := range daemonConfig.ExecOpts {
if strings.HasPrefix(opt, "native.cgroupdriver=") {
settings.CgroupDriver = strings.TrimPrefix(opt, "native.cgroupdriver=")
break
}
}
service.Success(w, settings)
}
// UpdateSettings 更新 Docker 设置
func (s *App) UpdateSettings(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateSettings](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
configPath := "/etc/docker/daemon.json"
settings := req.Settings
// 读取现有配置
var existingConfig map[string]any
content, err := io.Read(configPath)
if err == nil && content != "" {
if err = json.Unmarshal([]byte(content), &existingConfig); err != nil {
existingConfig = make(map[string]any)
}
} else {
existingConfig = make(map[string]any)
}
// 更新设置字段
if len(settings.RegistryMirrors) > 0 {
existingConfig["registry-mirrors"] = settings.RegistryMirrors
} else {
delete(existingConfig, "registry-mirrors")
}
if len(settings.InsecureRegistries) > 0 {
existingConfig["insecure-registries"] = settings.InsecureRegistries
} else {
delete(existingConfig, "insecure-registries")
}
if settings.LiveRestore {
existingConfig["live-restore"] = true
} else {
delete(existingConfig, "live-restore")
}
if settings.LogDriver != "" {
existingConfig["log-driver"] = settings.LogDriver
} else {
delete(existingConfig, "log-driver")
}
// 日志配置
if settings.LogOpts.MaxSize != "" || settings.LogOpts.MaxFile != "" {
logOpts := make(map[string]string)
if settings.LogOpts.MaxSize != "" {
logOpts["max-size"] = settings.LogOpts.MaxSize
}
if settings.LogOpts.MaxFile != "" {
logOpts["max-file"] = settings.LogOpts.MaxFile
}
existingConfig["log-opts"] = logOpts
} else {
delete(existingConfig, "log-opts")
}
// cgroup-driver
if settings.CgroupDriver != "" {
existingConfig["exec-opts"] = []string{"native.cgroupdriver=" + settings.CgroupDriver}
} else {
delete(existingConfig, "exec-opts")
}
if len(settings.Hosts) > 0 {
existingConfig["hosts"] = settings.Hosts
} else {
delete(existingConfig, "hosts")
}
if settings.DataRoot != "" {
existingConfig["data-root"] = settings.DataRoot
} else {
delete(existingConfig, "data-root")
}
if settings.StorageDriver != "" {
existingConfig["storage-driver"] = settings.StorageDriver
} else {
delete(existingConfig, "storage-driver")
}
if len(settings.DNS) > 0 {
existingConfig["dns"] = settings.DNS
} else {
delete(existingConfig, "dns")
}
// 防火墙后端
if settings.FirewallBackend != "" {
existingConfig["firewall-backend"] = settings.FirewallBackend
} else {
delete(existingConfig, "firewall-backend")
}
if settings.Iptables != nil {
existingConfig["iptables"] = *settings.Iptables
} else {
delete(existingConfig, "iptables")
}
if settings.Ip6tables != nil {
existingConfig["ip6tables"] = *settings.Ip6tables
} else {
delete(existingConfig, "ip6tables")
}
if settings.IpForward != nil {
existingConfig["ip-forward"] = *settings.IpForward
} else {
delete(existingConfig, "ip-forward")
}
if settings.IPv6 != nil {
existingConfig["ipv6"] = *settings.IPv6
} else {
delete(existingConfig, "ipv6")
}
if settings.Bip != "" {
existingConfig["bip"] = settings.Bip
} else {
delete(existingConfig, "bip")
}
// 序列化并写入文件
newContent, err := json.MarshalIndent(existingConfig, "", " ")
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = io.Write(configPath, string(newContent), 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
// 重启 Docker 服务
if err = systemctl.Restart("docker"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
+31
View File
@@ -3,3 +3,34 @@ package docker
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
// LogOpts 日志配置选项
type LogOpts struct {
MaxSize string `json:"max-size,omitempty"` // 日志文件最大大小,如 "10m"
MaxFile string `json:"max-file,omitempty"` // 保存的日志文件份数,如 "3"
}
// Settings Docker daemon 设置
type Settings struct {
RegistryMirrors []string `json:"registry-mirrors,omitempty"` // 注册表镜像
InsecureRegistries []string `json:"insecure-registries,omitempty"` // 非安全镜像仓库
LiveRestore bool `json:"live-restore,omitempty"` // Live restore
LogDriver string `json:"log-driver,omitempty"` // 日志驱动
LogOpts LogOpts `json:"log-opts,omitempty"` // 日志配置选项
CgroupDriver string `json:"cgroup-driver,omitempty"` // cgroup 驱动(从 exec-opts 中提取)
Hosts []string `json:"hosts,omitempty"` // Socket 路径
DataRoot string `json:"data-root,omitempty"` // 数据目录
StorageDriver string `json:"storage-driver,omitempty"` // 存储驱动
DNS []string `json:"dns,omitempty"` // DNS 配置
FirewallBackend string `json:"firewall-backend,omitempty"` // 防火墙后端 (iptables/nftables)
Iptables *bool `json:"iptables,omitempty"` // iptables 规则
Ip6tables *bool `json:"ip6tables,omitempty"` // ip6tables 规则
IpForward *bool `json:"ip-forward,omitempty"` // IP 转发
IPv6 *bool `json:"ipv6,omitempty"` // IPv6 支持
Bip string `json:"bip,omitempty"` // 默认 bridge 网络 IP 段
}
// UpdateSettings 更新设置请求
type UpdateSettings struct {
Settings Settings `json:"settings" validate:"required"`
}
+23
View File
@@ -0,0 +1,23 @@
package docker
// DaemonConfig Docker daemon.json 完整配置结构
type DaemonConfig struct {
RegistryMirrors []string `json:"registry-mirrors,omitempty"`
InsecureRegistries []string `json:"insecure-registries,omitempty"`
LiveRestore bool `json:"live-restore,omitempty"`
LogDriver string `json:"log-driver,omitempty"`
LogOpts map[string]string `json:"log-opts,omitempty"`
ExecOpts []string `json:"exec-opts,omitempty"`
Hosts []string `json:"hosts,omitempty"`
DataRoot string `json:"data-root,omitempty"`
StorageDriver string `json:"storage-driver,omitempty"`
DNS []string `json:"dns,omitempty"`
FirewallBackend string `json:"firewall-backend,omitempty"`
Iptables *bool `json:"iptables,omitempty"`
Ip6tables *bool `json:"ip6tables,omitempty"`
IpForward *bool `json:"ip-forward,omitempty"`
IPv6 *bool `json:"ipv6,omitempty"`
Bip string `json:"bip,omitempty"`
// 其他原有配置字段保留
Extra map[string]any `json:"-"`
}
+7 -7
View File
@@ -12,11 +12,11 @@ import (
"github.com/libtnb/utils/str"
"github.com/spf13/cast"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/biz"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/shell"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/biz"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/shell"
)
type App struct {
@@ -134,7 +134,7 @@ port = ` + ports + `
maxretry = ` + jailMaxRetry + `
findtime = ` + jailFindTime + `
bantime = ` + jailBanTime + `
logpath = ` + app.Root + `/wwwlogs/` + website.Name + `.log
logpath = ` + app.Root + `/sites/` + website.Name + `/log/access.log
# ` + jailWebsiteName + `-` + jailWebsiteMode + `-END
`
raw += rule
@@ -263,7 +263,7 @@ func (s *App) BanList(w http.ResponseWriter, r *http.Request) {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get total banned list"))
return
}
bannedIp, err := shell.Execf(`fail2ban-client status %s | grep "Banned IP list" | awk -F ":" '{print $2}'`, req.Name)
bannedIp, err := shell.Execf(`fail2ban-client status %s | grep "Banned IP list" | sed 's/.*Banned IP list:[[:space:]]*//'`, req.Name)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get banned ip list"))
return
+85 -4
View File
@@ -6,10 +6,10 @@ import (
"github.com/go-chi/chi/v5"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/systemctl"
)
type App struct{}
@@ -21,6 +21,8 @@ func NewApp() *App {
func (s *App) Route(r chi.Router) {
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
r.Get("/user", s.GetUser)
r.Post("/user", s.UpdateUser)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
@@ -58,3 +60,82 @@ func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
func (s *App) GetUser(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Name](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
servicePath := fmt.Sprintf("/etc/systemd/system/%s.service", req.Name)
content, err := io.Read(servicePath)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
userInfo := UserInfo{}
// 解析 User 和 Group
if matches := userCaptureRegex.FindStringSubmatch(content); len(matches) > 1 {
userInfo.User = matches[1]
}
if matches := groupCaptureRegex.FindStringSubmatch(content); len(matches) > 1 {
userInfo.Group = matches[1]
}
service.Success(w, userInfo)
}
func (s *App) UpdateUser(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateUser](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
servicePath := fmt.Sprintf("/etc/systemd/system/%s.service", req.Name)
content, err := io.Read(servicePath)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
// 检查 User 和 Group 是否存在
hasUser := userRegex.MatchString(content)
hasGroup := groupRegex.MatchString(content)
// 替换或添加 User 和 Group 配置
if hasUser && hasGroup {
// 两者都存在,分别替换
content = userRegex.ReplaceAllString(content, fmt.Sprintf("User=%s", req.User))
content = groupRegex.ReplaceAllString(content, fmt.Sprintf("Group=%s", req.Group))
} else if hasUser && !hasGroup {
// 只有 User,替换 User 并添加 Group
content = userRegex.ReplaceAllString(content, fmt.Sprintf("User=%s\nGroup=%s", req.User, req.Group))
} else if !hasUser && hasGroup {
// 只有 Group,添加 User 并替换 Group
content = serviceRegex.ReplaceAllString(content, fmt.Sprintf("[Service]\nUser=%s", req.User))
content = groupRegex.ReplaceAllString(content, fmt.Sprintf("Group=%s", req.Group))
} else {
// 两者都不存在,在 [Service] 后添加两者
content = serviceRegex.ReplaceAllString(content, fmt.Sprintf("[Service]\nUser=%s\nGroup=%s", req.User, req.Group))
}
if err = io.Write(servicePath, content, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.DaemonReload(); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart(req.Name); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
+8 -2
View File
@@ -1,10 +1,16 @@
package frp
type Name struct {
Name string `form:"name" json:"name" validate:"required"`
Name string `form:"name" json:"name" validate:"required|in:frps,frpc"`
}
type UpdateConfig struct {
Name string `form:"name" json:"name" validate:"required"`
Name string `form:"name" json:"name" validate:"required|in:frps,frpc"`
Config string `form:"config" json:"config" validate:"required"`
}
type UpdateUser struct {
Name string `form:"name" json:"name" validate:"required|in:frps,frpc"`
User string `form:"user" json:"user" validate:"required|regex:^[a-zA-Z0-9_-]+$"`
Group string `form:"group" json:"group" validate:"required|regex:^[a-zA-Z0-9_-]+$"`
}
+16
View File
@@ -0,0 +1,16 @@
package frp
import "regexp"
var (
userCaptureRegex = regexp.MustCompile(`(?m)^User=(.*)$`)
groupCaptureRegex = regexp.MustCompile(`(?m)^Group=(.*)$`)
userRegex = regexp.MustCompile(`(?m)^User=.*$`)
groupRegex = regexp.MustCompile(`(?m)^Group=.*$`)
serviceRegex = regexp.MustCompile(`(?m)^\[Service\]$`)
)
type UserInfo struct {
User string `json:"user"`
Group string `json:"group"`
}
+4 -4
View File
@@ -6,10 +6,10 @@ import (
"github.com/go-chi/chi/v5"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/systemctl"
)
type App struct{}
+23
View File
@@ -0,0 +1,23 @@
package mariadb
import (
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/acepanel/panel/internal/apps/mysql"
"github.com/acepanel/panel/internal/biz"
)
type App struct {
mysql *mysql.App
}
func NewApp(t *gotext.Locale, setting biz.SettingRepo, databaseServer biz.DatabaseServerRepo) *App {
return &App{
mysql: mysql.NewApp(t, setting, databaseServer),
}
}
func (s *App) Route(r chi.Router) {
s.mysql.Route(r)
}
+115 -4
View File
@@ -5,14 +5,15 @@ import (
"net"
"net/http"
"regexp"
"strings"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/tnborg/panel/pkg/types"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/systemctl"
"github.com/acepanel/panel/pkg/types"
)
type App struct {
@@ -29,6 +30,8 @@ func (s *App) Route(r chi.Router) {
r.Get("/load", s.Load)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
r.Get("/config_tune", s.GetConfigTune)
r.Post("/config_tune", s.UpdateConfigTune)
}
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
@@ -110,3 +113,111 @@ func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
// GetConfigTune 获取 Memcached 配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
config, err := io.Read("/etc/systemd/system/memcached.service")
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
tune := ConfigTune{
Port: s.getExecStartArg(config, "-p"),
UDPPort: s.getExecStartArg(config, "-U"),
ListenAddress: s.getExecStartArg(config, "-l"),
Memory: s.getExecStartArg(config, "-m"),
MaxConnections: s.getExecStartArg(config, "-c"),
Threads: s.getExecStartArg(config, "-t"),
}
service.Success(w, tune)
}
// UpdateConfigTune 更新 Memcached 配置调整参数
func (s *App) UpdateConfigTune(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ConfigTune](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
config, err := io.Read("/etc/systemd/system/memcached.service")
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
config = s.setExecStartArg(config, "-p", req.Port)
config = s.setExecStartArg(config, "-U", req.UDPPort)
config = s.setExecStartArg(config, "-l", req.ListenAddress)
config = s.setExecStartArg(config, "-m", req.Memory)
config = s.setExecStartArg(config, "-c", req.MaxConnections)
config = s.setExecStartArg(config, "-t", req.Threads)
if err = io.Write("/etc/systemd/system/memcached.service", config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("memcached"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// getExecStartArg 从 systemd service 文件的 ExecStart 行中获取指定参数值
func (s *App) getExecStartArg(content string, flag string) string {
lines := strings.Split(content, "\n")
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if !strings.HasPrefix(trimmed, "ExecStart=") {
continue
}
args := strings.Fields(trimmed)
for i, arg := range args {
if arg == flag && i+1 < len(args) {
return args[i+1]
}
}
}
return ""
}
// setExecStartArg 在 systemd service 文件的 ExecStart 行中设置指定参数值
func (s *App) setExecStartArg(content string, flag string, value string) string {
value = strings.ReplaceAll(value, "\n", "")
value = strings.ReplaceAll(value, "\r", "")
lines := strings.Split(content, "\n")
result := make([]string, 0, len(lines))
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if !strings.HasPrefix(trimmed, "ExecStart=") {
result = append(result, line)
continue
}
args := strings.Fields(trimmed)
newArgs := make([]string, 0, len(args))
found := false
for i := 0; i < len(args); i++ {
if args[i] == flag && i+1 < len(args) {
i++ // 跳过旧值
found = true
// 值为空时删除该参数
if value != "" {
newArgs = append(newArgs, flag, value)
}
} else {
newArgs = append(newArgs, args[i])
}
}
if !found && value != "" {
newArgs = append(newArgs, flag, value)
}
result = append(result, strings.Join(newArgs, " "))
}
return strings.Join(result, "\n")
}
+10
View File
@@ -3,3 +3,13 @@ package memcached
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
// ConfigTune Memcached 配置调整
type ConfigTune struct {
Port string `form:"port" json:"port"`
UDPPort string `form:"udp_port" json:"udp_port"`
ListenAddress string `form:"listen_address" json:"listen_address"`
Memory string `form:"memory" json:"memory"`
MaxConnections string `form:"max_connections" json:"max_connections"`
Threads string `form:"threads" json:"threads"`
}
+3 -3
View File
@@ -5,9 +5,9 @@ import (
"github.com/go-chi/chi/v5"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/systemctl"
)
type App struct{}
+239 -31
View File
@@ -5,31 +5,34 @@ import (
"net/http"
"os"
"regexp"
"strings"
"github.com/acepanel/panel/pkg/types"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/spf13/cast"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/biz"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/db"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/shell"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/tnborg/panel/pkg/tools"
"github.com/tnborg/panel/pkg/types"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/biz"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/db"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/shell"
"github.com/acepanel/panel/pkg/systemctl"
"github.com/acepanel/panel/pkg/tools"
)
type App struct {
t *gotext.Locale
settingRepo biz.SettingRepo
t *gotext.Locale
settingRepo biz.SettingRepo
databaseServerRepo biz.DatabaseServerRepo
}
func NewApp(t *gotext.Locale, setting biz.SettingRepo) *App {
func NewApp(t *gotext.Locale, setting biz.SettingRepo, databaseServer biz.DatabaseServerRepo) *App {
return &App{
t: t,
settingRepo: setting,
t: t,
settingRepo: setting,
databaseServerRepo: databaseServer,
}
}
@@ -37,11 +40,13 @@ func (s *App) Route(r chi.Router) {
r.Get("/load", s.Load)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
r.Post("/clear_error_log", s.ClearErrorLog)
r.Post("/clear_log", s.ClearLog)
r.Get("/slow_log", s.SlowLog)
r.Post("/clear_slow_log", s.ClearSlowLog)
r.Get("/root_password", s.GetRootPassword)
r.Post("/root_password", s.SetRootPassword)
r.Get("/config_tune", s.GetConfigTune)
r.Post("/config_tune", s.UpdateConfigTune)
}
// GetConfig 获取配置
@@ -78,23 +83,18 @@ func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
// Load 获取负载
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
rootPassword, err := s.settingRepo.Get(biz.SettingKeyMySQLRootPassword)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to load MySQL root password: %v", err))
return
}
if len(rootPassword) == 0 {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("MySQL root password is empty"))
return
}
status, _ := systemctl.Status("mysqld")
if !status {
service.Success(w, []types.NV{})
return
}
rootPassword, err := s.settingRepo.Get(biz.SettingKeyMySQLRootPassword)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to load MySQL root password: %v", err))
return
}
if err = os.Setenv("MYSQL_PWD", rootPassword); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set MYSQL_PWD env: %v", err))
return
@@ -156,11 +156,46 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
bufferPoolReadRequests := cast.ToFloat64(load[12]["value"])
load[10]["value"] = fmt.Sprintf("%.2f%%", bufferPoolReadRequests/(bufferPoolReads+bufferPoolReadRequests)*100)
// 查询缓存命中率
// MySQL 8.0+ 删除了查询缓存功能
qcacheHitsRe := regexp.MustCompile(`Qcache_hits\s+\|\s+(\d+)\s+\|`)
qcacheHitsMatches := qcacheHitsRe.FindStringSubmatch(raw)
qcacheInsertsRe := regexp.MustCompile(`Qcache_inserts\s+\|\s+(\d+)\s+\|`)
qcacheInsertsMatches := qcacheInsertsRe.FindStringSubmatch(raw)
qcacheNotCachedRe := regexp.MustCompile(`Qcache_not_cached\s+\|\s+(\d+)\s+\|`)
qcacheNotCachedMatches := qcacheNotCachedRe.FindStringSubmatch(raw)
if len(qcacheHitsMatches) > 1 && len(qcacheInsertsMatches) > 1 && len(qcacheNotCachedMatches) > 1 {
qcacheHits := cast.ToFloat64(qcacheHitsMatches[1])
qcacheInserts := cast.ToFloat64(qcacheInsertsMatches[1])
qcacheNotCached := cast.ToFloat64(qcacheNotCachedMatches[1])
var qcacheHitRate float64
denominator := qcacheHits + qcacheInserts + qcacheNotCached
if denominator > 0 {
qcacheHitRate = qcacheHits / denominator * 100
}
load = append(load, map[string]string{
"name": s.t.Get("Query Cache Hits"),
"value": qcacheHitsMatches[1],
})
load = append(load, map[string]string{
"name": s.t.Get("Query Cache Inserts"),
"value": qcacheInsertsMatches[1],
})
load = append(load, map[string]string{
"name": s.t.Get("Query Cache Not Cached"),
"value": qcacheNotCachedMatches[1],
})
load = append(load, map[string]string{
"name": s.t.Get("Query Cache Hit Rate"),
"value": fmt.Sprintf("%.2f%%", qcacheHitRate),
})
}
service.Success(w, load)
}
// ClearErrorLog 清空错误日志
func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
// ClearLog 清空日志
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
if err := systemctl.LogClear("mysqld"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
@@ -212,19 +247,20 @@ func (s *App) SetRootPassword(w http.ResponseWriter, r *http.Request) {
return
}
} else {
defer func(mysql *db.MySQL) {
_ = mysql.Close()
}(mysql)
defer mysql.Close()
if err = mysql.UserPassword("root", req.Password, "localhost"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
}
if err = s.settingRepo.Set(biz.SettingKeyMySQLRootPassword, req.Password); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
_ = s.databaseServerRepo.UpdatePassword("local_mysql", req.Password)
service.Success(w, nil)
}
@@ -251,3 +287,175 @@ func (s *App) getSock() string {
return "/tmp/mysql.sock"
}
// GetConfigTune 获取 MySQL 配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(app.Root + "/server/mysql/conf/my.cnf")
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
tune := ConfigTune{
// 常规设置
Port: s.getINIValue(config, "port"),
MaxConnections: s.getINIValue(config, "max_connections"),
MaxConnectErrors: s.getINIValue(config, "max_connect_errors"),
DefaultStorageEngine: s.getINIValue(config, "default_storage_engine"),
TableOpenCache: s.getINIValue(config, "table_open_cache"),
MaxAllowedPacket: s.getINIValue(config, "max_allowed_packet"),
OpenFilesLimit: s.getINIValue(config, "open_files_limit"),
// 性能调整
KeyBufferSize: s.getINIValue(config, "key_buffer_size"),
SortBufferSize: s.getINIValue(config, "sort_buffer_size"),
ReadBufferSize: s.getINIValue(config, "read_buffer_size"),
ReadRndBufferSize: s.getINIValue(config, "read_rnd_buffer_size"),
JoinBufferSize: s.getINIValue(config, "join_buffer_size"),
ThreadCacheSize: s.getINIValue(config, "thread_cache_size"),
ThreadStack: s.getINIValue(config, "thread_stack"),
TmpTableSize: s.getINIValue(config, "tmp_table_size"),
MaxHeapTableSize: s.getINIValue(config, "max_heap_table_size"),
MyisamSortBufferSize: s.getINIValue(config, "myisam_sort_buffer_size"),
// InnoDB
InnodbBufferPoolSize: s.getINIValue(config, "innodb_buffer_pool_size"),
InnodbLogBufferSize: s.getINIValue(config, "innodb_log_buffer_size"),
InnodbFlushLogAtTrxCommit: s.getINIValue(config, "innodb_flush_log_at_trx_commit"),
InnodbLockWaitTimeout: s.getINIValue(config, "innodb_lock_wait_timeout"),
InnodbMaxDirtyPagesPct: s.getINIValue(config, "innodb_max_dirty_pages_pct"),
InnodbReadIoThreads: s.getINIValue(config, "innodb_read_io_threads"),
InnodbWriteIoThreads: s.getINIValue(config, "innodb_write_io_threads"),
// 日志
SlowQueryLog: s.getINIValue(config, "slow_query_log"),
LongQueryTime: s.getINIValue(config, "long_query_time"),
}
service.Success(w, tune)
}
// UpdateConfigTune 更新 MySQL 配置调整参数
func (s *App) UpdateConfigTune(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ConfigTune](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
confPath := app.Root + "/server/mysql/conf/my.cnf"
config, err := io.Read(confPath)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
// 更新常规设置
config = s.setINIValue(config, "port", req.Port)
config = s.setINIValue(config, "max_connections", req.MaxConnections)
config = s.setINIValue(config, "max_connect_errors", req.MaxConnectErrors)
config = s.setINIValue(config, "default_storage_engine", req.DefaultStorageEngine)
config = s.setINIValue(config, "table_open_cache", req.TableOpenCache)
config = s.setINIValue(config, "max_allowed_packet", req.MaxAllowedPacket)
config = s.setINIValue(config, "open_files_limit", req.OpenFilesLimit)
// 更新性能调整
config = s.setINIValue(config, "key_buffer_size", req.KeyBufferSize)
config = s.setINIValue(config, "sort_buffer_size", req.SortBufferSize)
config = s.setINIValue(config, "read_buffer_size", req.ReadBufferSize)
config = s.setINIValue(config, "read_rnd_buffer_size", req.ReadRndBufferSize)
config = s.setINIValue(config, "join_buffer_size", req.JoinBufferSize)
config = s.setINIValue(config, "thread_cache_size", req.ThreadCacheSize)
config = s.setINIValue(config, "thread_stack", req.ThreadStack)
config = s.setINIValue(config, "tmp_table_size", req.TmpTableSize)
config = s.setINIValue(config, "max_heap_table_size", req.MaxHeapTableSize)
config = s.setINIValue(config, "myisam_sort_buffer_size", req.MyisamSortBufferSize)
// 更新 InnoDB
config = s.setINIValue(config, "innodb_buffer_pool_size", req.InnodbBufferPoolSize)
config = s.setINIValue(config, "innodb_log_buffer_size", req.InnodbLogBufferSize)
config = s.setINIValue(config, "innodb_flush_log_at_trx_commit", req.InnodbFlushLogAtTrxCommit)
config = s.setINIValue(config, "innodb_lock_wait_timeout", req.InnodbLockWaitTimeout)
config = s.setINIValue(config, "innodb_max_dirty_pages_pct", req.InnodbMaxDirtyPagesPct)
config = s.setINIValue(config, "innodb_read_io_threads", req.InnodbReadIoThreads)
config = s.setINIValue(config, "innodb_write_io_threads", req.InnodbWriteIoThreads)
// 更新日志
config = s.setINIValue(config, "slow_query_log", req.SlowQueryLog)
config = s.setINIValue(config, "long_query_time", req.LongQueryTime)
if err = io.Write(confPath, config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// getINIValue 从 INI 格式内容中获取指定键的值
func (s *App) getINIValue(content string, key string) string {
lines := strings.Split(content, "\n")
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, ";") || strings.HasPrefix(trimmed, "#") {
continue
}
if strings.HasPrefix(trimmed, "[") {
continue
}
parts := strings.SplitN(trimmed, "=", 2)
if len(parts) != 2 {
continue
}
k := strings.TrimSpace(parts[0])
if k == key {
return strings.TrimSpace(parts[1])
}
}
return ""
}
// setINIValue 在 INI 格式内容中设置指定键的值
func (s *App) setINIValue(content string, key string, value string) string {
value = strings.ReplaceAll(value, "\n", "")
value = strings.ReplaceAll(value, "\r", "")
lines := strings.Split(content, "\n")
found := false
result := make([]string, 0, len(lines))
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "[") {
result = append(result, line)
continue
}
checkLine := trimmed
if strings.HasPrefix(checkLine, ";") {
checkLine = strings.TrimSpace(checkLine[1:])
} else if strings.HasPrefix(checkLine, "#") {
checkLine = strings.TrimSpace(checkLine[1:])
}
parts := strings.SplitN(checkLine, "=", 2)
if len(parts) != 2 {
result = append(result, line)
continue
}
k := strings.TrimSpace(parts[0])
if k == key {
if found {
continue
}
found = true
// 值为空时注释掉该配置项
if value == "" {
if !strings.HasPrefix(trimmed, ";") && !strings.HasPrefix(trimmed, "#") {
result = append(result, "#"+line)
} else {
result = append(result, line)
}
continue
}
result = append(result, key+" = "+value)
} else {
result = append(result, line)
}
}
if !found && value != "" {
result = append(result, key+" = "+value)
}
return strings.Join(result, "\n")
}
+34
View File
@@ -7,3 +7,37 @@ type UpdateConfig struct {
type SetRootPassword struct {
Password string `form:"password" json:"password" validate:"required|password"`
}
// ConfigTune MySQL 配置调整
type ConfigTune struct {
// 常规设置
Port string `form:"port" json:"port"`
MaxConnections string `form:"max_connections" json:"max_connections"`
MaxConnectErrors string `form:"max_connect_errors" json:"max_connect_errors"`
DefaultStorageEngine string `form:"default_storage_engine" json:"default_storage_engine"`
TableOpenCache string `form:"table_open_cache" json:"table_open_cache"`
MaxAllowedPacket string `form:"max_allowed_packet" json:"max_allowed_packet"`
OpenFilesLimit string `form:"open_files_limit" json:"open_files_limit"`
// 性能调整
KeyBufferSize string `form:"key_buffer_size" json:"key_buffer_size"`
SortBufferSize string `form:"sort_buffer_size" json:"sort_buffer_size"`
ReadBufferSize string `form:"read_buffer_size" json:"read_buffer_size"`
ReadRndBufferSize string `form:"read_rnd_buffer_size" json:"read_rnd_buffer_size"`
JoinBufferSize string `form:"join_buffer_size" json:"join_buffer_size"`
ThreadCacheSize string `form:"thread_cache_size" json:"thread_cache_size"`
ThreadStack string `form:"thread_stack" json:"thread_stack"`
TmpTableSize string `form:"tmp_table_size" json:"tmp_table_size"`
MaxHeapTableSize string `form:"max_heap_table_size" json:"max_heap_table_size"`
MyisamSortBufferSize string `form:"myisam_sort_buffer_size" json:"myisam_sort_buffer_size"`
// InnoDB
InnodbBufferPoolSize string `form:"innodb_buffer_pool_size" json:"innodb_buffer_pool_size"`
InnodbLogBufferSize string `form:"innodb_log_buffer_size" json:"innodb_log_buffer_size"`
InnodbFlushLogAtTrxCommit string `form:"innodb_flush_log_at_trx_commit" json:"innodb_flush_log_at_trx_commit"`
InnodbLockWaitTimeout string `form:"innodb_lock_wait_timeout" json:"innodb_lock_wait_timeout"`
InnodbMaxDirtyPagesPct string `form:"innodb_max_dirty_pages_pct" json:"innodb_max_dirty_pages_pct"`
InnodbReadIoThreads string `form:"innodb_read_io_threads" json:"innodb_read_io_threads"`
InnodbWriteIoThreads string `form:"innodb_write_io_threads" json:"innodb_write_io_threads"`
// 日志
SlowQueryLog string `form:"slow_query_log" json:"slow_query_log"`
LongQueryTime string `form:"long_query_time" json:"long_query_time"`
}
+199 -10
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"net/http"
"regexp"
"strings"
"time"
"github.com/go-chi/chi/v5"
@@ -11,13 +12,13 @@ import (
"github.com/leonelquinteros/gotext"
"github.com/spf13/cast"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/shell"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/tnborg/panel/pkg/tools"
"github.com/tnborg/panel/pkg/types"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/shell"
"github.com/acepanel/panel/pkg/systemctl"
"github.com/acepanel/panel/pkg/tools"
"github.com/acepanel/panel/pkg/types"
)
type App struct {
@@ -36,6 +37,17 @@ func (s *App) Route(r chi.Router) {
r.Post("/config", s.SaveConfig)
r.Get("/error_log", s.ErrorLog)
r.Post("/clear_error_log", s.ClearErrorLog)
r.Get("/config_tune", s.GetConfigTune)
r.Post("/config_tune", s.UpdateConfigTune)
r.Get("/stream/servers", s.ListStreamServers)
r.Post("/stream/servers", s.CreateStreamServer)
r.Put("/stream/servers/{name}", s.UpdateStreamServer)
r.Delete("/stream/servers/{name}", s.DeleteStreamServer)
r.Get("/stream/upstreams", s.ListStreamUpstreams)
r.Post("/stream/upstreams", s.CreateStreamUpstream)
r.Put("/stream/upstreams/{name}", s.UpdateStreamUpstream)
r.Delete("/stream/upstreams/{name}", s.DeleteStreamUpstream)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
@@ -55,7 +67,7 @@ func (s *App) SaveConfig(w http.ResponseWriter, r *http.Request) {
return
}
if err = io.Write(fmt.Sprintf("%s/server/nginx/conf/nginx.conf", app.Root), req.Config, 0644); err != nil {
if err = io.Write(fmt.Sprintf("%s/server/nginx/conf/nginx.conf", app.Root), req.Config, 0600); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -70,11 +82,11 @@ func (s *App) SaveConfig(w http.ResponseWriter, r *http.Request) {
}
func (s *App) ErrorLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/%s", app.Root, "wwwlogs/nginx-error.log"))
service.Success(w, fmt.Sprintf("%s/%s", app.Root, "server/nginx/nginx-error.log"))
}
func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("cat /dev/null > %s/%s", app.Root, "wwwlogs/nginx-error.log"); err != nil {
if _, err := shell.Execf("cat /dev/null > %s/%s", app.Root, "server/nginx/nginx-error.log"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -156,3 +168,180 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
service.Success(w, data)
}
// GetConfigTune 获取 Nginx 配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/nginx/conf/nginx.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
tune := ConfigTune{
// 常规设置
WorkerProcesses: s.getNginxValue(config, "worker_processes"),
WorkerConnections: s.getNginxValue(config, "worker_connections"),
KeepaliveTimeout: s.getNginxValue(config, "keepalive_timeout"),
ClientMaxBodySize: s.getNginxValue(config, "client_max_body_size"),
ClientBodyBufferSize: s.getNginxValue(config, "client_body_buffer_size"),
ClientHeaderBufferSize: s.getNginxValue(config, "client_header_buffer_size"),
ServerNamesHashBucketSize: s.getNginxValue(config, "server_names_hash_bucket_size"),
ServerTokens: s.getNginxValue(config, "server_tokens"),
// Gzip 压缩
Gzip: s.getNginxValue(config, "gzip"),
GzipMinLength: s.getNginxValue(config, "gzip_min_length"),
GzipCompLevel: s.getNginxValue(config, "gzip_comp_level"),
GzipTypes: s.getNginxValue(config, "gzip_types"),
GzipVary: s.getNginxValue(config, "gzip_vary"),
GzipProxied: s.getNginxValue(config, "gzip_proxied"),
// Brotli 压缩
Brotli: s.getNginxValue(config, "brotli"),
BrotliMinLength: s.getNginxValue(config, "brotli_min_length"),
BrotliCompLevel: s.getNginxValue(config, "brotli_comp_level"),
BrotliTypes: s.getNginxValue(config, "brotli_types"),
BrotliStatic: s.getNginxValue(config, "brotli_static"),
// Zstd 压缩
Zstd: s.getNginxValue(config, "zstd"),
ZstdMinLength: s.getNginxValue(config, "zstd_min_length"),
ZstdCompLevel: s.getNginxValue(config, "zstd_comp_level"),
ZstdTypes: s.getNginxValue(config, "zstd_types"),
ZstdStatic: s.getNginxValue(config, "zstd_static"),
}
service.Success(w, tune)
}
// UpdateConfigTune 更新 Nginx 配置调整参数
func (s *App) UpdateConfigTune(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ConfigTune](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
confPath := fmt.Sprintf("%s/server/nginx/conf/nginx.conf", app.Root)
config, err := io.Read(confPath)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
// 更新常规设置
config = s.setNginxValue(config, "worker_processes", req.WorkerProcesses)
config = s.setNginxValue(config, "worker_connections", req.WorkerConnections)
config = s.setNginxValue(config, "keepalive_timeout", req.KeepaliveTimeout)
config = s.setNginxValue(config, "client_max_body_size", req.ClientMaxBodySize)
config = s.setNginxValue(config, "client_body_buffer_size", req.ClientBodyBufferSize)
config = s.setNginxValue(config, "client_header_buffer_size", req.ClientHeaderBufferSize)
config = s.setNginxValue(config, "server_names_hash_bucket_size", req.ServerNamesHashBucketSize)
config = s.setNginxValue(config, "server_tokens", req.ServerTokens)
// 更新 Gzip 压缩
config = s.setNginxValue(config, "gzip", req.Gzip)
config = s.setNginxValue(config, "gzip_min_length", req.GzipMinLength)
config = s.setNginxValue(config, "gzip_comp_level", req.GzipCompLevel)
config = s.setNginxValue(config, "gzip_types", req.GzipTypes)
config = s.setNginxValue(config, "gzip_vary", req.GzipVary)
config = s.setNginxValue(config, "gzip_proxied", req.GzipProxied)
// 更新 Brotli 压缩
config = s.setNginxValue(config, "brotli", req.Brotli)
config = s.setNginxValue(config, "brotli_min_length", req.BrotliMinLength)
config = s.setNginxValue(config, "brotli_comp_level", req.BrotliCompLevel)
config = s.setNginxValue(config, "brotli_types", req.BrotliTypes)
config = s.setNginxValue(config, "brotli_static", req.BrotliStatic)
// 更新 Zstd 压缩
config = s.setNginxValue(config, "zstd", req.Zstd)
config = s.setNginxValue(config, "zstd_min_length", req.ZstdMinLength)
config = s.setNginxValue(config, "zstd_comp_level", req.ZstdCompLevel)
config = s.setNginxValue(config, "zstd_types", req.ZstdTypes)
config = s.setNginxValue(config, "zstd_static", req.ZstdStatic)
if err = io.Write(confPath, config, 0600); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Reload("nginx"); err != nil {
_, err = shell.Execf("nginx -t")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
return
}
service.Success(w, nil)
}
// getNginxValue 从 Nginx 配置内容中获取指定指令的值
func (s *App) getNginxValue(content string, key string) string {
lines := strings.Split(content, "\n")
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
continue
}
if !strings.HasSuffix(trimmed, ";") {
continue
}
trimmed = strings.TrimSuffix(trimmed, ";")
trimmed = strings.TrimSpace(trimmed)
parts := strings.Fields(trimmed)
if len(parts) >= 2 && parts[0] == key {
return strings.Join(parts[1:], " ")
}
}
return ""
}
// setNginxValue 在 Nginx 配置内容中设置指定指令的值
func (s *App) setNginxValue(content string, key string, value string) string {
value = strings.ReplaceAll(value, "\n", "")
value = strings.ReplaceAll(value, "\r", "")
lines := strings.Split(content, "\n")
found := false
result := make([]string, 0, len(lines))
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" || trimmed == "{" || trimmed == "}" {
result = append(result, line)
continue
}
// 检查指令(可能被注释)
checkLine := trimmed
if strings.HasPrefix(checkLine, "#") {
checkLine = strings.TrimSpace(checkLine[1:])
}
if !strings.HasSuffix(checkLine, ";") {
result = append(result, line)
continue
}
checkLine = strings.TrimSuffix(checkLine, ";")
checkLine = strings.TrimSpace(checkLine)
parts := strings.Fields(checkLine)
if len(parts) >= 2 && parts[0] == key {
if found {
continue
}
found = true
// 值为空时注释掉该配置项
if value == "" {
if !strings.HasPrefix(trimmed, "#") {
indent := line[:len(line)-len(strings.TrimLeft(line, " \t"))]
result = append(result, indent+"#"+strings.TrimLeft(line, " \t"))
} else {
result = append(result, line)
}
continue
}
// 保留原行缩进
indent := line[:len(line)-len(strings.TrimLeft(line, " \t"))]
result = append(result, indent+key+" "+value+";")
} else {
result = append(result, line)
}
}
if !found && value != "" {
result = append(result, " "+key+" "+value+";")
}
return strings.Join(result, "\n")
}
+55
View File
@@ -1,5 +1,60 @@
package nginx
import "time"
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
// ConfigTune Nginx 配置调整
type ConfigTune struct {
// 常规设置
WorkerProcesses string `form:"worker_processes" json:"worker_processes"`
WorkerConnections string `form:"worker_connections" json:"worker_connections"`
KeepaliveTimeout string `form:"keepalive_timeout" json:"keepalive_timeout"`
ClientMaxBodySize string `form:"client_max_body_size" json:"client_max_body_size"`
ClientBodyBufferSize string `form:"client_body_buffer_size" json:"client_body_buffer_size"`
ClientHeaderBufferSize string `form:"client_header_buffer_size" json:"client_header_buffer_size"`
ServerNamesHashBucketSize string `form:"server_names_hash_bucket_size" json:"server_names_hash_bucket_size"`
ServerTokens string `form:"server_tokens" json:"server_tokens"`
// Gzip 压缩
Gzip string `form:"gzip" json:"gzip"`
GzipMinLength string `form:"gzip_min_length" json:"gzip_min_length"`
GzipCompLevel string `form:"gzip_comp_level" json:"gzip_comp_level"`
GzipTypes string `form:"gzip_types" json:"gzip_types"`
GzipVary string `form:"gzip_vary" json:"gzip_vary"`
GzipProxied string `form:"gzip_proxied" json:"gzip_proxied"`
// Brotli 压缩
Brotli string `form:"brotli" json:"brotli"`
BrotliMinLength string `form:"brotli_min_length" json:"brotli_min_length"`
BrotliCompLevel string `form:"brotli_comp_level" json:"brotli_comp_level"`
BrotliTypes string `form:"brotli_types" json:"brotli_types"`
BrotliStatic string `form:"brotli_static" json:"brotli_static"`
// Zstd 压缩
Zstd string `form:"zstd" json:"zstd"`
ZstdMinLength string `form:"zstd_min_length" json:"zstd_min_length"`
ZstdCompLevel string `form:"zstd_comp_level" json:"zstd_comp_level"`
ZstdTypes string `form:"zstd_types" json:"zstd_types"`
ZstdStatic string `form:"zstd_static" json:"zstd_static"`
}
type StreamServer struct {
Name string `form:"name" json:"name" validate:"required|regex:^[a-zA-Z0-9_-]+$"` // 配置名称,用于文件命名
Listen string `form:"listen" json:"listen" validate:"required"` // 监听地址,如: "12345", "0.0.0.0:12345", "[::]:12345"
UDP bool `form:"udp" json:"udp"` // 是否 UDP 协议
ProxyPass string `form:"proxy_pass" json:"proxy_pass" validate:"required"` // 代理地址,如: "127.0.0.1:3306", "upstream_name"
ProxyProtocol bool `form:"proxy_protocol" json:"proxy_protocol"` // 是否启用 PROXY 协议
ProxyTimeout time.Duration `form:"proxy_timeout" json:"proxy_timeout"` // 代理超时时间
ProxyConnectTimeout time.Duration `form:"proxy_connect_timeout" json:"proxy_connect_timeout"` // 代理连接超时时间
SSL bool `form:"ssl" json:"ssl"` // 是否启用 SSL
SSLCertificate string `form:"ssl_certificate" json:"ssl_certificate" validate:"requiredIf:SSL,true"` // SSL 证书路径
SSLCertificateKey string `form:"ssl_certificate_key" json:"ssl_certificate_key" validate:"requiredIf:SSL,true"` // SSL 私钥路径
}
type StreamUpstream struct {
Name string `form:"name" json:"name" validate:"required|regex:^[a-zA-Z0-9_-]+$"` // 上游名称
Servers map[string]string `form:"servers" json:"servers" validate:"required"` // 上游服务器及配置,如: map["127.0.0.1:3306"] = "weight=5"
Algo string `form:"algo" json:"algo"` // 负载均衡算法,如: "least_conn", "hash $remote_addr"
Resolver []string `form:"resolver" json:"resolver"` // DNS 解析器,如: ["8.8.8.8", "ipv6=off"]
ResolverTimeout time.Duration `form:"resolver_timeout" json:"resolver_timeout"` // DNS 解析超时时间
}
+642
View File
@@ -0,0 +1,642 @@
package nginx
import (
"fmt"
"net/http"
"os"
"path/filepath"
"sort"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/systemctl"
webserverNginx "github.com/acepanel/panel/pkg/webserver/nginx"
)
// ListStreamServers 获取 Stream Server 列表
func (s *App) ListStreamServers(w http.ResponseWriter, r *http.Request) {
servers, err := s.parseStreamServers()
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to list stream servers: %v", err))
return
}
service.Success(w, servers)
}
// CreateStreamServer 创建 Stream Server
func (s *App) CreateStreamServer(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[StreamServer](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
configPath := filepath.Join(s.streamDir(), fmt.Sprintf("%s.conf", req.Name))
if _, statErr := os.Stat(configPath); statErr == nil {
service.Error(w, http.StatusConflict, s.t.Get("stream server config already exists: %s", req.Name))
return
}
if err = s.saveStreamServerConfig(configPath, req); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to write stream server config: %v", err))
return
}
if err = systemctl.Reload("nginx"); err != nil {
_ = os.Remove(configPath)
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
return
}
service.Success(w, nil)
}
// UpdateStreamServer 更新 Stream Server
func (s *App) UpdateStreamServer(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
if name == "" {
service.Error(w, http.StatusBadRequest, s.t.Get("name is required"))
return
}
req, err := service.Bind[StreamServer](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
configPath := filepath.Join(s.streamDir(), fmt.Sprintf("%s.conf", name))
if _, statErr := os.Stat(configPath); os.IsNotExist(statErr) {
service.Error(w, http.StatusNotFound, s.t.Get("stream server not found: %s", name))
return
}
newConfigPath := configPath
if req.Name != name {
newConfigPath = filepath.Join(s.streamDir(), fmt.Sprintf("%s.conf", req.Name))
if _, statErr := os.Stat(newConfigPath); statErr == nil {
service.Error(w, http.StatusConflict, s.t.Get("stream server config already exists: %s", req.Name))
return
}
}
if err = s.saveStreamServerConfig(newConfigPath, req); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to write stream server config: %v", err))
return
}
if newConfigPath != configPath {
_ = os.Remove(configPath)
}
if err = systemctl.Reload("nginx"); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
return
}
service.Success(w, nil)
}
// DeleteStreamServer 删除 Stream Server
func (s *App) DeleteStreamServer(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
if name == "" {
service.Error(w, http.StatusBadRequest, s.t.Get("name is required"))
return
}
configPath := filepath.Join(s.streamDir(), fmt.Sprintf("%s.conf", name))
if _, statErr := os.Stat(configPath); os.IsNotExist(statErr) {
service.Error(w, http.StatusNotFound, s.t.Get("stream server not found: %s", name))
return
}
if err := os.Remove(configPath); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to delete stream server config: %v", err))
return
}
if err := systemctl.Reload("nginx"); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
return
}
service.Success(w, nil)
}
// ListStreamUpstreams 获取 Stream Upstream 列表
func (s *App) ListStreamUpstreams(w http.ResponseWriter, r *http.Request) {
upstreams, err := s.parseStreamUpstreams()
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to list stream upstreams: %v", err))
return
}
service.Success(w, upstreams)
}
// CreateStreamUpstream 创建 Stream Upstream
func (s *App) CreateStreamUpstream(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[StreamUpstream](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
configPath := filepath.Join(s.streamDir(), fmt.Sprintf("upstream_%s.conf", req.Name))
if _, statErr := os.Stat(configPath); statErr == nil {
service.Error(w, http.StatusConflict, s.t.Get("stream upstream config already exists: %s", req.Name))
return
}
if err = s.saveStreamUpstreamConfig(configPath, req); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to write stream upstream config: %v", err))
return
}
if err = systemctl.Reload("nginx"); err != nil {
_ = os.Remove(configPath)
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
return
}
service.Success(w, nil)
}
// UpdateStreamUpstream 更新 Stream Upstream
func (s *App) UpdateStreamUpstream(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
if name == "" {
service.Error(w, http.StatusBadRequest, s.t.Get("name is required"))
return
}
req, err := service.Bind[StreamUpstream](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
configPath := filepath.Join(s.streamDir(), fmt.Sprintf("upstream_%s.conf", name))
if _, statErr := os.Stat(configPath); os.IsNotExist(statErr) {
service.Error(w, http.StatusNotFound, s.t.Get("stream upstream not found: %s", name))
return
}
newConfigPath := configPath
if req.Name != name {
newConfigPath = filepath.Join(s.streamDir(), fmt.Sprintf("upstream_%s.conf", req.Name))
if _, statErr := os.Stat(newConfigPath); statErr == nil {
service.Error(w, http.StatusConflict, s.t.Get("stream upstream config already exists: %s", req.Name))
return
}
}
if err = s.saveStreamUpstreamConfig(newConfigPath, req); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to write stream upstream config: %v", err))
return
}
if newConfigPath != configPath {
_ = os.Remove(configPath)
}
if err = systemctl.Reload("nginx"); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
return
}
service.Success(w, nil)
}
// DeleteStreamUpstream 删除 Stream Upstream
func (s *App) DeleteStreamUpstream(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
if name == "" {
service.Error(w, http.StatusBadRequest, s.t.Get("name is required"))
return
}
configPath := filepath.Join(s.streamDir(), fmt.Sprintf("upstream_%s.conf", name))
if _, statErr := os.Stat(configPath); os.IsNotExist(statErr) {
service.Error(w, http.StatusNotFound, s.t.Get("stream upstream not found: %s", name))
return
}
if err := os.Remove(configPath); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to delete stream upstream config: %v", err))
return
}
if err := systemctl.Reload("nginx"); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
return
}
service.Success(w, nil)
}
// parseStreamServers 解析所有 Stream Server 配置
func (s *App) parseStreamServers() ([]StreamServer, error) {
entries, err := os.ReadDir(s.streamDir())
if err != nil {
return nil, err
}
servers := make([]StreamServer, 0)
for _, entry := range entries {
if entry.IsDir() {
continue
}
fileName := entry.Name()
// 跳过 upstream 配置文件
if strings.HasPrefix(fileName, "upstream_") {
continue
}
if !strings.HasSuffix(fileName, ".conf") {
continue
}
name := strings.TrimSuffix(fileName, ".conf")
configPath := filepath.Join(s.streamDir(), fileName)
server, err := s.parseStreamServerFile(configPath, name)
if err != nil {
continue // 跳过解析失败的文件
}
if server != nil {
servers = append(servers, *server)
}
}
// 按名称排序
sort.Slice(servers, func(i, j int) bool {
return servers[i].Name < servers[j].Name
})
return servers, nil
}
// parseStreamServerFile 解析单个 Stream Server 配置文件
func (s *App) parseStreamServerFile(filePath string, name string) (*StreamServer, error) {
p, err := webserverNginx.NewParserFromFile(filePath)
if err != nil {
return nil, err
}
server := &StreamServer{
Name: name,
}
// 解析 listen 指令
listenDirs, err := p.Find("server.listen")
if err == nil && len(listenDirs) > 0 {
params := listenDirs[0].GetParameters()
if len(params) > 0 {
server.Listen = params[0].Value
for i := 1; i < len(params); i++ {
switch params[i].Value {
case "udp":
server.UDP = true
case "ssl":
server.SSL = true
}
}
}
}
// 解析 proxy_pass 指令
proxyPassDir, err := p.FindOne("server.proxy_pass")
if err == nil {
params := proxyPassDir.GetParameters()
if len(params) > 0 {
server.ProxyPass = params[0].Value
}
}
// 解析 proxy_protocol 指令
proxyProtocolDir, err := p.FindOne("server.proxy_protocol")
if err == nil {
params := proxyProtocolDir.GetParameters()
if len(params) > 0 && params[0].Value == "on" {
server.ProxyProtocol = true
}
}
// 解析 proxy_timeout 指令
proxyTimeoutDir, err := p.FindOne("server.proxy_timeout")
if err == nil {
params := proxyTimeoutDir.GetParameters()
if len(params) > 0 {
server.ProxyTimeout = parseNginxDuration(params[0].Value)
}
}
// 解析 proxy_connect_timeout 指令
proxyConnectTimeoutDir, err := p.FindOne("server.proxy_connect_timeout")
if err == nil {
params := proxyConnectTimeoutDir.GetParameters()
if len(params) > 0 {
server.ProxyConnectTimeout = parseNginxDuration(params[0].Value)
}
}
// 解析 ssl_certificate 指令
sslCertDir, err := p.FindOne("server.ssl_certificate")
if err == nil {
params := sslCertDir.GetParameters()
if len(params) > 0 {
server.SSLCertificate = params[0].Value
}
}
// 解析 ssl_certificate_key 指令
sslKeyDir, err := p.FindOne("server.ssl_certificate_key")
if err == nil {
params := sslKeyDir.GetParameters()
if len(params) > 0 {
server.SSLCertificateKey = params[0].Value
}
}
return server, nil
}
// parseStreamUpstreams 解析所有 Stream Upstream 配置
func (s *App) parseStreamUpstreams() ([]StreamUpstream, error) {
entries, err := os.ReadDir(s.streamDir())
if err != nil {
return nil, err
}
upstreams := make([]StreamUpstream, 0)
for _, entry := range entries {
if entry.IsDir() {
continue
}
fileName := entry.Name()
// 只处理 upstream 配置文件
if !strings.HasPrefix(fileName, "upstream_") {
continue
}
if !strings.HasSuffix(fileName, ".conf") {
continue
}
name := strings.TrimPrefix(fileName, "upstream_")
name = strings.TrimSuffix(name, ".conf")
configPath := filepath.Join(s.streamDir(), fileName)
upstream, err := s.parseStreamUpstreamFile(configPath, name)
if err != nil {
continue // 跳过解析失败的文件
}
if upstream != nil {
upstreams = append(upstreams, *upstream)
}
}
// 按名称排序
sort.Slice(upstreams, func(i, j int) bool {
return upstreams[i].Name < upstreams[j].Name
})
return upstreams, nil
}
// parseStreamUpstreamFile 解析单个 Stream Upstream 配置文件
func (s *App) parseStreamUpstreamFile(filePath string, expectedName string) (*StreamUpstream, error) {
p, err := webserverNginx.NewParserFromFile(filePath)
if err != nil {
return nil, err
}
cfg := p.Config()
if cfg == nil || cfg.Block == nil {
return nil, fmt.Errorf("invalid config")
}
// 查找 upstream 块
upstreamDirectives := cfg.Block.FindDirectives("upstream")
if len(upstreamDirectives) == 0 {
return nil, fmt.Errorf("no upstream block found")
}
upstreamDir := upstreamDirectives[0]
params := upstreamDir.GetParameters()
if len(params) == 0 {
return nil, fmt.Errorf("upstream name not found")
}
name := params[0].Value
if expectedName != "" && name != expectedName {
return nil, fmt.Errorf("upstream name mismatch")
}
upstream := &StreamUpstream{
Name: name,
Servers: make(map[string]string),
Resolver: []string{},
}
upstreamBlock := upstreamDir.GetBlock()
if upstreamBlock == nil {
return nil, fmt.Errorf("upstream block is empty")
}
// 解析 upstream 块中的指令
for _, dir := range upstreamBlock.GetDirectives() {
switch dir.GetName() {
case "server":
dirParams := dir.GetParameters()
if len(dirParams) > 0 {
addr := dirParams[0].Value
var options []string
for i := 1; i < len(dirParams); i++ {
options = append(options, dirParams[i].Value)
}
upstream.Servers[addr] = strings.Join(options, " ")
}
case "least_conn", "ip_hash", "random":
upstream.Algo = dir.GetName()
case "hash":
dirParams := dir.GetParameters()
if len(dirParams) > 0 {
upstream.Algo = "hash " + dirParams[0].Value
// 检查是否有 consistent 参数
if len(dirParams) > 1 && dirParams[1].Value == "consistent" {
upstream.Algo += " consistent"
}
}
case "least_time":
dirParams := dir.GetParameters()
if len(dirParams) > 0 {
upstream.Algo = "least_time " + dirParams[0].Value
}
case "resolver":
dirParams := dir.GetParameters()
for _, param := range dirParams {
upstream.Resolver = append(upstream.Resolver, param.Value)
}
case "resolver_timeout":
dirParams := dir.GetParameters()
if len(dirParams) > 0 {
upstream.ResolverTimeout = parseNginxDuration(dirParams[0].Value)
}
}
}
return upstream, nil
}
// saveStreamServerConfig 生成并保存 Stream Server 配置
func (s *App) saveStreamServerConfig(filePath string, server *StreamServer) error {
p, err := webserverNginx.NewParserFromString("server {}")
if err != nil {
return err
}
p.SetConfigPath(filePath)
// listen 指令
listenParams := []string{server.Listen}
if server.UDP {
listenParams = append(listenParams, "udp")
}
if server.SSL {
listenParams = append(listenParams, "ssl")
}
if err = p.SetOne("server.listen", listenParams); err != nil {
return err
}
// proxy_pass 指令
if err = p.SetOne("server.proxy_pass", []string{server.ProxyPass}); err != nil {
return err
}
// proxy_protocol 指令
if server.ProxyProtocol {
if err = p.SetOne("server.proxy_protocol", []string{"on"}); err != nil {
return err
}
}
// proxy_timeout 指令
if server.ProxyTimeout > 0 {
if err = p.SetOne("server.proxy_timeout", []string{formatNginxDuration(server.ProxyTimeout)}); err != nil {
return err
}
}
// proxy_connect_timeout 指令
if server.ProxyConnectTimeout > 0 {
if err = p.SetOne("server.proxy_connect_timeout", []string{formatNginxDuration(server.ProxyConnectTimeout)}); err != nil {
return err
}
}
// SSL 配置
if server.SSL {
if server.SSLCertificate != "" {
if err = p.SetOne("server.ssl_certificate", []string{server.SSLCertificate}); err != nil {
return err
}
}
if server.SSLCertificateKey != "" {
if err = p.SetOne("server.ssl_certificate_key", []string{server.SSLCertificateKey}); err != nil {
return err
}
}
}
return os.WriteFile(filePath, []byte(p.Dump()), 0600)
}
// saveStreamUpstreamConfig 生成并保存 Stream Upstream 配置
func (s *App) saveStreamUpstreamConfig(filePath string, upstream *StreamUpstream) error {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("upstream %s {\n", upstream.Name))
// 负载均衡算法
if upstream.Algo != "" {
sb.WriteString(fmt.Sprintf(" %s;\n", upstream.Algo))
}
// resolver 配置
if len(upstream.Resolver) > 0 {
sb.WriteString(fmt.Sprintf(" resolver %s;\n", strings.Join(upstream.Resolver, " ")))
if upstream.ResolverTimeout > 0 {
sb.WriteString(fmt.Sprintf(" resolver_timeout %s;\n", formatNginxDuration(upstream.ResolverTimeout)))
}
}
// 服务器列表
var addrs []string
for addr := range upstream.Servers {
addrs = append(addrs, addr)
}
sort.Strings(addrs)
for _, addr := range addrs {
options := upstream.Servers[addr]
if options != "" {
sb.WriteString(fmt.Sprintf(" server %s %s;\n", addr, options))
} else {
sb.WriteString(fmt.Sprintf(" server %s;\n", addr))
}
}
sb.WriteString("}\n")
return os.WriteFile(filePath, []byte(sb.String()), 0600)
}
// parseNginxDuration 解析 Nginx 时间格式(如 10s, 1m, 1h
func parseNginxDuration(value string) time.Duration {
if value == "" {
return 0
}
// 尝试解析带单位的时间
value = strings.TrimSpace(value)
if len(value) == 0 {
return 0
}
if len(value) == 1 {
value += "s" // 单个字符,默认为秒
}
unit := value[len(value)-1]
numStr := value[:len(value)-1]
var num int
_, _ = fmt.Sscanf(numStr, "%d", &num)
switch unit {
case 's':
return time.Duration(num) * time.Second
case 'm':
return time.Duration(num) * time.Minute
case 'h':
return time.Duration(num) * time.Hour
case 'd':
return time.Duration(num) * 24 * time.Hour
default:
// 没有单位,尝试直接解析为秒
_, _ = fmt.Sscanf(value, "%d", &num)
return time.Duration(num) * time.Second
}
}
// formatNginxDuration 格式化时间为 Nginx 格式
func formatNginxDuration(d time.Duration) string {
if d == 0 {
return "0s"
}
seconds := int(d.Seconds())
if seconds%3600 == 0 {
return fmt.Sprintf("%dh", seconds/3600)
}
if seconds%60 == 0 {
return fmt.Sprintf("%dm", seconds/60)
}
return fmt.Sprintf("%ds", seconds)
}
// streamDir 返回 stream 配置目录
func (s *App) streamDir() string {
return filepath.Join(app.Root, "server/nginx/conf/stream")
}
+22
View File
@@ -0,0 +1,22 @@
package openresty
import (
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/acepanel/panel/internal/apps/nginx"
)
type App struct {
nginx *nginx.App
}
func NewApp(t *gotext.Locale) *App {
return &App{
nginx: nginx.NewApp(t),
}
}
func (s *App) Route(r chi.Router) {
s.nginx.Route(r)
}
+23
View File
@@ -0,0 +1,23 @@
package percona
import (
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/acepanel/panel/internal/apps/mysql"
"github.com/acepanel/panel/internal/biz"
)
type App struct {
mysql *mysql.App
}
func NewApp(t *gotext.Locale, setting biz.SettingRepo, databaseServer biz.DatabaseServerRepo) *App {
return &App{
mysql: mysql.NewApp(t, setting, databaseServer),
}
}
func (s *App) Route(r chi.Router) {
s.mysql.Route(r)
}
-523
View File
@@ -1,523 +0,0 @@
package php
import (
"fmt"
"net/http"
"net/url"
"slices"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-resty/resty/v2"
"github.com/leonelquinteros/gotext"
"github.com/spf13/cast"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/biz"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/shell"
"github.com/tnborg/panel/pkg/types"
)
type App struct {
version uint
t *gotext.Locale
taskRepo biz.TaskRepo
}
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
return &App{
t: t,
taskRepo: task,
}
}
func (s *App) Route(version uint) func(r chi.Router) {
return func(r chi.Router) {
php := new(App)
php.version = version
php.t = s.t
php.taskRepo = s.taskRepo
r.Post("/set_cli", php.SetCli)
r.Get("/config", php.GetConfig)
r.Post("/config", php.UpdateConfig)
r.Get("/fpm_config", php.GetFPMConfig)
r.Post("/fpm_config", php.UpdateFPMConfig)
r.Get("/load", php.Load)
r.Get("/error_log", php.ErrorLog)
r.Get("/slow_log", php.SlowLog)
r.Post("/clear_error_log", php.ClearErrorLog)
r.Post("/clear_slow_log", php.ClearSlowLog)
r.Get("/extensions", php.ExtensionList)
r.Post("/extensions", php.InstallExtension)
r.Delete("/extensions", php.UninstallExtension)
}
}
func (s *App) SetCli(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("ln -sf %s/server/php/%d/bin/php /usr/bin/php", app.Root, s.version); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/php/%d/etc/php.ini", app.Root, s.version))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, config)
}
func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if err = io.Write(fmt.Sprintf("%s/server/php/%d/etc/php.ini", app.Root, s.version), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
func (s *App) GetFPMConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/php/%d/etc/php-fpm.conf", app.Root, s.version))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, config)
}
func (s *App) UpdateFPMConfig(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if err = io.Write(fmt.Sprintf("%s/server/php/%d/etc/php-fpm.conf", app.Root, s.version), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
var raw map[string]any
client := resty.New().SetTimeout(10 * time.Second)
_, err := client.R().SetResult(&raw).Get(fmt.Sprintf("http://127.0.0.1/phpfpm_status/%d?json", s.version))
if err != nil {
service.Success(w, []types.NV{})
return
}
dataKeys := []string{
s.t.Get("Application Pool"),
s.t.Get("Process Manager"),
s.t.Get("Start Time"),
s.t.Get("Accepted Connections"),
s.t.Get("Listen Queue"),
s.t.Get("Max Listen Queue"),
s.t.Get("Listen Queue Length"),
s.t.Get("Idle Processes"),
s.t.Get("Active Processes"),
s.t.Get("Total Processes"),
s.t.Get("Max Active Processes"),
s.t.Get("Max Children Reached"),
s.t.Get("Slow Requests"),
}
rawKeys := []string{
"pool",
"process manager",
"start time",
"accepted conn",
"listen queue",
"max listen queue",
"listen queue len",
"idle processes",
"active processes",
"total processes",
"max active processes",
"max children reached",
"slow requests",
}
loads := make([]types.NV, 0)
for i := range dataKeys {
v, ok := raw[rawKeys[i]]
if ok {
loads = append(loads, types.NV{
Name: dataKeys[i],
Value: cast.ToString(v),
})
}
}
service.Success(w, loads)
}
func (s *App) ErrorLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/server/php/%d/var/log/php-fpm.log", app.Root, s.version))
}
func (s *App) SlowLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/server/php/%d/var/log/slow.log", app.Root, s.version))
}
func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("cat /dev/null > %s/server/php/%d/var/log/php-fpm.log", app.Root, s.version); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
func (s *App) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("cat /dev/null > %s/server/php/%d/var/log/slow.log", app.Root, s.version); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
func (s *App) ExtensionList(w http.ResponseWriter, r *http.Request) {
extensions := s.getExtensions()
raw, err := shell.Execf("%s/server/php/%d/bin/php -m", app.Root, s.version)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
extensionMap := make(map[string]*Extension)
for i := range extensions {
extensionMap[extensions[i].Slug] = &extensions[i]
}
rawExtensionList := strings.Split(raw, "\n")
for _, item := range rawExtensionList {
if ext, exists := extensionMap[item]; exists && !strings.Contains(item, "[") && item != "" {
ext.Installed = true
}
}
service.Success(w, extensions)
}
func (s *App) InstallExtension(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ExtensionSlug](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if !s.checkExtension(req.Slug) {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("extension %s does not exist", req.Slug))
return
}
cmd := fmt.Sprintf(`curl -sSLOm 10 --retry 3 'https://dl.cdn.haozi.net/panel/php_exts/%s.sh' | bash -s -- 'install' '%d' >> '/tmp/%s.log' 2>&1`, url.PathEscape(req.Slug), s.version, req.Slug)
officials := []string{"fileinfo", "exif", "imap", "pgsql", "pdo_pgsql", "zip", "bz2", "readline", "snmp", "ldap", "enchant", "pspell", "calendar", "gmp", "sysvmsg", "sysvsem", "sysvshm", "xsl", "intl", "gettext"}
if slices.Contains(officials, req.Slug) {
cmd = fmt.Sprintf(`curl -sSLOm 10 --retry 3 'https://dl.cdn.haozi.net/panel/php_exts/official.sh' | bash -s -- 'install' '%d' '%s' >> '/tmp/%s.log' 2>&1`, s.version, req.Slug, req.Slug)
}
task := new(biz.Task)
task.Name = s.t.Get("Install PHP-%d %s extension", s.version, req.Slug)
task.Status = biz.TaskStatusWaiting
task.Shell = cmd
task.Log = "/tmp/" + req.Slug + ".log"
if err = s.taskRepo.Push(task); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
func (s *App) UninstallExtension(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ExtensionSlug](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if !s.checkExtension(req.Slug) {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("extension %s does not exist", req.Slug))
return
}
cmd := fmt.Sprintf(`curl -sSLOm 10 --retry 3 'https://dl.cdn.haozi.net/panel/php_exts/%s.sh' | bash -s -- 'uninstall' '%d' >> '/tmp/%s.log' 2>&1`, url.PathEscape(req.Slug), s.version, req.Slug)
officials := []string{"fileinfo", "exif", "imap", "pgsql", "pdo_pgsql", "zip", "bz2", "readline", "snmp", "ldap", "enchant", "pspell", "calendar", "gmp", "sysvmsg", "sysvsem", "sysvshm", "xsl", "intl", "gettext"}
if slices.Contains(officials, req.Slug) {
cmd = fmt.Sprintf(`curl -sSLOm 10 --retry 3 'https://dl.cdn.haozi.net/panel/php_exts/official.sh' | bash -s -- 'uninstall' '%d' '%s' >> '/tmp/%s.log' 2>&1`, s.version, req.Slug, req.Slug)
}
task := new(biz.Task)
task.Name = s.t.Get("Uninstall PHP-%d %s extension", s.version, req.Slug)
task.Status = biz.TaskStatusWaiting
task.Shell = cmd
task.Log = "/tmp/" + req.Slug + ".log"
if err = s.taskRepo.Push(task); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
func (s *App) getExtensions() []Extension {
extensions := []Extension{
{
Name: "fileinfo",
Slug: "fileinfo",
Description: s.t.Get("Fileinfo is a library used to identify file types"),
},
{
Name: "OPcache",
Slug: "Zend OPcache",
Description: s.t.Get("OPcache stores precompiled PHP script bytecode in shared memory to improve PHP performance"),
},
{
Name: "igbinary",
Slug: "igbinary",
Description: s.t.Get("Igbinary is a library for serializing and deserializing data"),
},
{
Name: "Redis",
Slug: "redis",
Description: s.t.Get("PhpRedis connects to and operates on data in Redis databases (requires the igbinary extension installed above)"),
},
{
Name: "Memcached",
Slug: "memcached",
Description: s.t.Get("Memcached is a driver for connecting to Memcached servers"),
},
{
Name: "ImageMagick",
Slug: "imagick",
Description: s.t.Get("ImageMagick is free software for creating, editing, and composing images"),
},
{
Name: "exif",
Slug: "exif",
Description: s.t.Get("Exif is a library for reading and writing image metadata"),
},
{
Name: "pgsql",
Slug: "pgsql",
Description: s.t.Get("pgsql is a driver for connecting to PostgreSQL (requires PostgreSQL installed)"),
},
{
Name: "pdo_pgsql",
Slug: "pdo_pgsql",
Description: s.t.Get("pdo_pgsql is a PDO driver for connecting to PostgreSQL (requires PostgreSQL installed)"),
},
{
Name: "sqlsrv",
Slug: "sqlsrv",
Description: s.t.Get("sqlsrv is a driver for connecting to SQL Server"),
},
{
Name: "pdo_sqlsrv",
Slug: "pdo_sqlsrv",
Description: s.t.Get("pdo_sqlsrv is a PDO driver for connecting to SQL Server"),
},
{
Name: "imap",
Slug: "imap",
Description: s.t.Get("IMAP extension allows PHP to read, search, delete, download, and manage emails"),
},
{
Name: "zip",
Slug: "zip",
Description: s.t.Get("Zip is a library for handling ZIP files"),
},
{
Name: "bz2",
Slug: "bz2",
Description: s.t.Get("Bzip2 is a library for compressing and decompressing files"),
},
{
Name: "ssh2",
Slug: "ssh2",
Description: s.t.Get("SSH2 is a library for connecting to SSH servers"),
},
{
Name: "event",
Slug: "event",
Description: s.t.Get("Event is a library for handling events"),
},
{
Name: "readline",
Slug: "readline",
Description: s.t.Get("Readline is a library for processing text"),
},
{
Name: "snmp",
Slug: "snmp",
Description: s.t.Get("SNMP is a protocol for network management"),
},
{
Name: "ldap",
Slug: "ldap",
Description: s.t.Get("LDAP is a protocol for accessing directory services"),
},
{
Name: "enchant",
Slug: "enchant",
Description: s.t.Get("Enchant is a spell-checking library"),
},
{
Name: "pspell",
Slug: "pspell",
Description: s.t.Get("Pspell is a spell-checking library"),
},
{
Name: "calendar",
Slug: "calendar",
Description: s.t.Get("Calendar is a library for handling dates"),
},
{
Name: "gmp",
Slug: "gmp",
Description: s.t.Get("GMP is a library for handling large integers"),
},
{
Name: "xlswriter",
Slug: "xlswriter",
Description: s.t.Get("XLSWriter is a high-performance library for reading and writing Excel files"),
},
{
Name: "xsl",
Slug: "xsl",
Description: s.t.Get("XSL is a library for processing XML documents"),
},
{
Name: "intl",
Slug: "intl",
Description: s.t.Get("Intl is a library for handling internationalization and localization"),
},
{
Name: "gettext",
Slug: "gettext",
Description: s.t.Get("Gettext is a library for handling multilingual support"),
},
{
Name: "grpc",
Slug: "grpc",
Description: s.t.Get("gRPC is a high-performance, open-source, and general-purpose RPC framework"),
},
{
Name: "protobuf",
Slug: "protobuf",
Description: s.t.Get("protobuf is a library for serializing and deserializing data"),
},
{
Name: "rdkafka",
Slug: "rdkafka",
Description: s.t.Get("rdkafka is a library for connecting to Apache Kafka"),
},
{
Name: "xhprof",
Slug: "xhprof",
Description: s.t.Get("xhprof is a library for performance profiling"),
},
{
Name: "xdebug",
Slug: "xdebug",
Description: s.t.Get("xdebug is a library for debugging and profiling PHP code"),
},
{
Name: "yaml",
Slug: "yaml",
Description: s.t.Get("yaml is a library for handling YAML"),
},
{
Name: "zstd",
Slug: "zstd",
Description: s.t.Get("zstd is a library for compressing and decompressing files"),
},
{
Name: "sysvmsg",
Slug: "sysvmsg",
Description: s.t.Get("Sysvmsg is a library for handling System V message queues"),
},
{
Name: "sysvsem",
Slug: "sysvsem",
Description: s.t.Get("Sysvsem is a library for handling System V semaphores"),
},
{
Name: "sysvshm",
Slug: "sysvshm",
Description: s.t.Get("Sysvshm is a library for handling System V shared memory"),
},
{
Name: "ionCube",
Slug: "ionCube Loader",
Description: s.t.Get("ionCube is a professional-grade PHP encryption and decryption tool (must be installed after OPcache)"),
},
{
Name: "Swoole",
Slug: "swoole",
Description: s.t.Get("Swoole is a PHP extension for building high-performance asynchronous concurrent servers"),
},
}
// Swow 不支持 PHP 8.0 以下版本且目前不支持 PHP 8.4
if cast.ToUint(s.version) >= 80 && cast.ToUint(s.version) < 84 {
extensions = append(extensions, Extension{
Name: "Swow",
Slug: "Swow",
Description: s.t.Get("Swow is a PHP extension for building high-performance asynchronous concurrent servers"),
})
}
// PHP 8.4 移除了 pspell 和 imap 并且不再建议使用
if cast.ToUint(s.version) >= 84 {
extensions = slices.DeleteFunc(extensions, func(extension Extension) bool {
return extension.Slug == "pspell" || extension.Slug == "imap"
})
}
raw, _ := shell.Execf("%s/server/php/%d/bin/php -m", app.Root, s.version)
extensionMap := make(map[string]*Extension)
for i := range extensions {
extensionMap[extensions[i].Slug] = &extensions[i]
}
rawExtensionList := strings.Split(raw, "\n")
for _, item := range rawExtensionList {
if ext, exists := extensionMap[item]; exists && !strings.Contains(item, "[") && item != "" {
ext.Installed = true
}
}
return extensions
}
func (s *App) checkExtension(slug string) bool {
extensions := s.getExtensions()
for _, item := range extensions {
if item.Slug == slug {
return true
}
}
return false
}
-9
View File
@@ -1,9 +0,0 @@
package php
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
type ExtensionSlug struct {
Slug string `form:"slug" json:"slug" validate:"required"`
}
-23
View File
@@ -1,23 +0,0 @@
package php74
import (
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/tnborg/panel/internal/apps/php"
"github.com/tnborg/panel/internal/biz"
)
type App struct {
php *php.App
}
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
return &App{
php: php.NewApp(t, task),
}
}
func (s *App) Route(r chi.Router) {
s.php.Route(74)(r)
}
-23
View File
@@ -1,23 +0,0 @@
package php80
import (
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/tnborg/panel/internal/apps/php"
"github.com/tnborg/panel/internal/biz"
)
type App struct {
php *php.App
}
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
return &App{
php: php.NewApp(t, task),
}
}
func (s *App) Route(r chi.Router) {
s.php.Route(80)(r)
}
-23
View File
@@ -1,23 +0,0 @@
package php81
import (
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/tnborg/panel/internal/apps/php"
"github.com/tnborg/panel/internal/biz"
)
type App struct {
php *php.App
}
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
return &App{
php: php.NewApp(t, task),
}
}
func (s *App) Route(r chi.Router) {
s.php.Route(81)(r)
}
-23
View File
@@ -1,23 +0,0 @@
package php82
import (
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/tnborg/panel/internal/apps/php"
"github.com/tnborg/panel/internal/biz"
)
type App struct {
php *php.App
}
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
return &App{
php: php.NewApp(t, task),
}
}
func (s *App) Route(r chi.Router) {
s.php.Route(82)(r)
}
-23
View File
@@ -1,23 +0,0 @@
package php83
import (
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/tnborg/panel/internal/apps/php"
"github.com/tnborg/panel/internal/biz"
)
type App struct {
php *php.App
}
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
return &App{
php: php.NewApp(t, task),
}
}
func (s *App) Route(r chi.Router) {
s.php.Route(83)(r)
}
-23
View File
@@ -1,23 +0,0 @@
package php84
import (
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/tnborg/panel/internal/apps/php"
"github.com/tnborg/panel/internal/biz"
)
type App struct {
php *php.App
}
func NewApp(t *gotext.Locale, task biz.TaskRepo) *App {
return &App{
php: php.NewApp(t, task),
}
}
func (s *App) Route(r chi.Router) {
s.php.Route(84)(r)
}
+11 -11
View File
@@ -12,12 +12,12 @@ import (
"github.com/libtnb/chix"
"github.com/spf13/cast"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/firewall"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/shell"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/firewall"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/shell"
"github.com/acepanel/panel/pkg/systemctl"
)
type App struct {
@@ -55,7 +55,7 @@ func (s *App) Info(w http.ResponseWriter, r *http.Request) {
return
}
conf, err := io.Read(fmt.Sprintf("%s/server/vhost/phpmyadmin.conf", app.Root))
conf, err := io.Read(fmt.Sprintf("%s/sites/phpmyadmin/config/nginx.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
@@ -79,13 +79,13 @@ func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
return
}
conf, err := io.Read(fmt.Sprintf("%s/server/vhost/phpmyadmin.conf", app.Root))
conf, err := io.Read(fmt.Sprintf("%s/sites/phpmyadmin/config/nginx.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
conf = regexp.MustCompile(`listen\s+(\d+);`).ReplaceAllString(conf, "listen "+cast.ToString(req.Port)+";")
if err = io.Write(fmt.Sprintf("%s/server/vhost/phpmyadmin.conf", app.Root), conf, 0644); err != nil {
if err = io.Write(fmt.Sprintf("%s/sites/phpmyadmin/config/nginx.conf", app.Root), conf, 0600); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -113,7 +113,7 @@ func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/vhost/phpmyadmin.conf", app.Root))
config, err := io.Read(fmt.Sprintf("%s/sites/phpmyadmin/config/nginx.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
@@ -129,7 +129,7 @@ func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
return
}
if err = io.Write(fmt.Sprintf("%s/server/vhost/phpmyadmin.conf", app.Root), req.Config, 0644); err != nil {
if err = io.Write(fmt.Sprintf("%s/sites/phpmyadmin/config/nginx.conf", app.Root), req.Config, 0600); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
+3 -3
View File
@@ -5,9 +5,9 @@ import (
"github.com/go-chi/chi/v5"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/systemctl"
)
type App struct{}
+259 -13
View File
@@ -3,26 +3,34 @@ package postgresql
import (
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/acepanel/panel/pkg/db"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/shell"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/tnborg/panel/pkg/types"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/biz"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/shell"
"github.com/acepanel/panel/pkg/systemctl"
"github.com/acepanel/panel/pkg/types"
)
type App struct {
t *gotext.Locale
t *gotext.Locale
settingRepo biz.SettingRepo
databaseServerRepo biz.DatabaseServerRepo
}
func NewApp(t *gotext.Locale) *App {
func NewApp(t *gotext.Locale, setting biz.SettingRepo, databaseServer biz.DatabaseServerRepo) *App {
return &App{
t: t,
t: t,
settingRepo: setting,
databaseServerRepo: databaseServer,
}
}
@@ -34,6 +42,10 @@ func (s *App) Route(r chi.Router) {
r.Get("/load", s.Load)
r.Get("/log", s.Log)
r.Post("/clear_log", s.ClearLog)
r.Get("/postgres_password", s.GetPostgresPassword)
r.Post("/postgres_password", s.SetPostgresPassword)
r.Get("/config_tune", s.GetConfigTune)
r.Post("/config_tune", s.UpdateConfigTune)
}
// GetConfig 获取配置
@@ -110,12 +122,23 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
return
}
start, err := shell.Execf(`echo "select pg_postmaster_start_time();" | su - postgres -c "psql" | sed -n 3p | cut -d'.' -f1`)
postgresPassword, err := s.settingRepo.Get(biz.SettingKeyPostgresPassword)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to load PostgreSQL postgres password: %v", err))
return
}
if err = os.Setenv("PGPASSWORD", postgresPassword); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set PGPASSWORD env: %v", err))
return
}
start, err := shell.Execf(`psql -h 127.0.0.1 -U postgres -t -c "select pg_postmaster_start_time();" | head -1 | cut -d'.' -f1`)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL start time: %v", err))
return
}
pid, err := shell.Execf(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)
pid, err := shell.Execf(`psql -h 127.0.0.1 -U postgres -t -c "select pg_backend_pid();"`)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL backend pid: %v", err))
return
@@ -125,17 +148,22 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL process: %v", err))
return
}
connections, err := shell.Execf(`echo "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)
connections, err := shell.Execf(`psql -h 127.0.0.1 -U postgres -t -c "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();"`)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL connections: %v", err))
return
}
storage, err := shell.Execf(`echo "select pg_size_pretty(pg_database_size('postgres'));" | su - postgres -c "psql" | sed -n 3p`)
storage, err := shell.Execf(`psql -h 127.0.0.1 -U postgres -t -c "select pg_size_pretty(pg_database_size('postgres'));"`)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL database size: %v", err))
return
}
if err = os.Unsetenv("PGPASSWORD"); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to unset PGPASSWORD env: %v", err))
return
}
data := []types.NV{
{Name: s.t.Get("Start Time"), Value: start},
{Name: s.t.Get("Process PID"), Value: pid},
@@ -161,3 +189,221 @@ func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
// GetPostgresPassword 获取 postgres 用户密码
func (s *App) GetPostgresPassword(w http.ResponseWriter, r *http.Request) {
password, err := s.settingRepo.Get(biz.SettingKeyPostgresPassword)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get postgres password: %v", err))
return
}
service.Success(w, password)
}
// SetPostgresPassword 设置 postgres 用户密码
func (s *App) SetPostgresPassword(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[SetPostgresPassword](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
oldPassword, _ := s.settingRepo.Get(biz.SettingKeyPostgresPassword)
postgres, err := db.NewPostgres("postgres", oldPassword, "127.0.0.1", 5432)
if err != nil {
// 直接修改密码
if _, err = shell.Execf(`su - postgres -c "psql -c \"ALTER USER postgres WITH PASSWORD '%s';\""`, req.Password); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set postgres password: %v", err))
return
}
} else {
defer postgres.Close()
if err = postgres.UserPassword("postgres", req.Password); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set postgres password: %v", err))
return
}
}
if err = s.settingRepo.Set(biz.SettingKeyPostgresPassword, req.Password); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set postgres password: %v", err))
return
}
_ = s.databaseServerRepo.UpdatePassword("local_postgresql", req.Password)
service.Success(w, nil)
}
// GetConfigTune 获取 PostgreSQL 配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/postgresql/data/postgresql.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
tune := ConfigTune{
// 连接设置
ListenAddresses: s.getPGValue(config, "listen_addresses"),
Port: s.getPGValue(config, "port"),
MaxConnections: s.getPGValue(config, "max_connections"),
SuperuserReservedConnections: s.getPGValue(config, "superuser_reserved_connections"),
// 内存设置
SharedBuffers: s.getPGValue(config, "shared_buffers"),
WorkMem: s.getPGValue(config, "work_mem"),
MaintenanceWorkMem: s.getPGValue(config, "maintenance_work_mem"),
EffectiveCacheSize: s.getPGValue(config, "effective_cache_size"),
HugePages: s.getPGValue(config, "huge_pages"),
// WAL 设置
WalLevel: s.getPGValue(config, "wal_level"),
WalBuffers: s.getPGValue(config, "wal_buffers"),
MaxWalSize: s.getPGValue(config, "max_wal_size"),
MinWalSize: s.getPGValue(config, "min_wal_size"),
CheckpointCompletionTarget: s.getPGValue(config, "checkpoint_completion_target"),
// 查询优化
DefaultStatisticsTarget: s.getPGValue(config, "default_statistics_target"),
RandomPageCost: s.getPGValue(config, "random_page_cost"),
EffectiveIoConcurrency: s.getPGValue(config, "effective_io_concurrency"),
// 日志设置
LogDestination: s.getPGValue(config, "log_destination"),
LogMinDurationStatement: s.getPGValue(config, "log_min_duration_statement"),
LogTimezone: s.getPGValue(config, "log_timezone"),
// IO 设置
IoMethod: s.getPGValue(config, "io_method"),
}
service.Success(w, tune)
}
// UpdateConfigTune 更新 PostgreSQL 配置调整参数
func (s *App) UpdateConfigTune(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ConfigTune](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
confPath := fmt.Sprintf("%s/server/postgresql/data/postgresql.conf", app.Root)
config, err := io.Read(confPath)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
// 更新连接设置
config = s.setPGValue(config, "listen_addresses", req.ListenAddresses)
config = s.setPGValue(config, "port", req.Port)
config = s.setPGValue(config, "max_connections", req.MaxConnections)
config = s.setPGValue(config, "superuser_reserved_connections", req.SuperuserReservedConnections)
// 更新内存设置
config = s.setPGValue(config, "shared_buffers", req.SharedBuffers)
config = s.setPGValue(config, "work_mem", req.WorkMem)
config = s.setPGValue(config, "maintenance_work_mem", req.MaintenanceWorkMem)
config = s.setPGValue(config, "effective_cache_size", req.EffectiveCacheSize)
config = s.setPGValue(config, "huge_pages", req.HugePages)
// 更新 WAL 设置
config = s.setPGValue(config, "wal_level", req.WalLevel)
config = s.setPGValue(config, "wal_buffers", req.WalBuffers)
config = s.setPGValue(config, "max_wal_size", req.MaxWalSize)
config = s.setPGValue(config, "min_wal_size", req.MinWalSize)
config = s.setPGValue(config, "checkpoint_completion_target", req.CheckpointCompletionTarget)
// 更新查询优化
config = s.setPGValue(config, "default_statistics_target", req.DefaultStatisticsTarget)
config = s.setPGValue(config, "random_page_cost", req.RandomPageCost)
config = s.setPGValue(config, "effective_io_concurrency", req.EffectiveIoConcurrency)
// 更新日志设置
config = s.setPGValue(config, "log_destination", req.LogDestination)
config = s.setPGValue(config, "log_min_duration_statement", req.LogMinDurationStatement)
config = s.setPGValue(config, "log_timezone", req.LogTimezone)
// 更新 IO 设置
config = s.setPGValue(config, "io_method", req.IoMethod)
if err = io.Write(confPath, config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Reload("postgresql"); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload PostgreSQL: %v", err))
return
}
service.Success(w, nil)
}
// getPGValue 从 PostgreSQL 配置内容中获取指定键的值
func (s *App) getPGValue(content string, key string) string {
lines := strings.Split(content, "\n")
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
continue
}
parts := strings.SplitN(trimmed, "=", 2)
if len(parts) != 2 {
continue
}
k := strings.TrimSpace(parts[0])
if k == key {
v := strings.TrimSpace(parts[1])
// 去除行尾注释
if idx := strings.Index(v, "#"); idx >= 0 {
v = strings.TrimSpace(v[:idx])
}
// 去除引号
v = strings.Trim(v, "'\"")
return v
}
}
return ""
}
// setPGValue 在 PostgreSQL 配置内容中设置指定键的值
func (s *App) setPGValue(content string, key string, value string) string {
value = strings.ReplaceAll(value, "\n", "")
value = strings.ReplaceAll(value, "\r", "")
lines := strings.Split(content, "\n")
found := false
result := make([]string, 0, len(lines))
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" {
result = append(result, line)
continue
}
checkLine := trimmed
if strings.HasPrefix(checkLine, "#") {
checkLine = strings.TrimSpace(checkLine[1:])
}
parts := strings.SplitN(checkLine, "=", 2)
if len(parts) != 2 {
result = append(result, line)
continue
}
k := strings.TrimSpace(parts[0])
if k == key {
if found {
continue
}
found = true
// 值为空时注释掉该配置项
if value == "" {
if !strings.HasPrefix(trimmed, "#") {
result = append(result, "#"+line)
} else {
result = append(result, line)
}
continue
}
result = append(result, key+" = "+value)
} else {
result = append(result, line)
}
}
if !found && value != "" {
result = append(result, key+" = "+value)
}
return strings.Join(result, "\n")
}
+35
View File
@@ -3,3 +3,38 @@ package postgresql
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
type SetPostgresPassword struct {
Password string `form:"password" json:"password" validate:"required|password"`
}
// ConfigTune PostgreSQL 配置调整
type ConfigTune struct {
// 连接设置
ListenAddresses string `form:"listen_addresses" json:"listen_addresses"`
Port string `form:"port" json:"port"`
MaxConnections string `form:"max_connections" json:"max_connections"`
SuperuserReservedConnections string `form:"superuser_reserved_connections" json:"superuser_reserved_connections"`
// 内存设置
SharedBuffers string `form:"shared_buffers" json:"shared_buffers"`
WorkMem string `form:"work_mem" json:"work_mem"`
MaintenanceWorkMem string `form:"maintenance_work_mem" json:"maintenance_work_mem"`
EffectiveCacheSize string `form:"effective_cache_size" json:"effective_cache_size"`
HugePages string `form:"huge_pages" json:"huge_pages"`
// WAL 设置
WalLevel string `form:"wal_level" json:"wal_level"`
WalBuffers string `form:"wal_buffers" json:"wal_buffers"`
MaxWalSize string `form:"max_wal_size" json:"max_wal_size"`
MinWalSize string `form:"min_wal_size" json:"min_wal_size"`
CheckpointCompletionTarget string `form:"checkpoint_completion_target" json:"checkpoint_completion_target"`
// 查询优化
DefaultStatisticsTarget string `form:"default_statistics_target" json:"default_statistics_target"`
RandomPageCost string `form:"random_page_cost" json:"random_page_cost"`
EffectiveIoConcurrency string `form:"effective_io_concurrency" json:"effective_io_concurrency"`
// 日志设置
LogDestination string `form:"log_destination" json:"log_destination"`
LogMinDurationStatement string `form:"log_min_duration_statement" json:"log_min_duration_statement"`
LogTimezone string `form:"log_timezone" json:"log_timezone"`
// IO 设置
IoMethod string `form:"io_method" json:"io_method"`
}
+146 -9
View File
@@ -1,6 +1,7 @@
package pureftpd
import (
"fmt"
"net/http"
"regexp"
"strings"
@@ -10,12 +11,12 @@ import (
"github.com/libtnb/chix"
"github.com/spf13/cast"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/firewall"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/shell"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/firewall"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/shell"
"github.com/acepanel/panel/pkg/systemctl"
)
type App struct {
@@ -35,6 +36,8 @@ func (s *App) Route(r chi.Router) {
r.Post("/users/{username}/password", s.ChangePassword)
r.Get("/port", s.GetPort)
r.Post("/port", s.UpdatePort)
r.Get("/config_tune", s.GetConfigTune)
r.Post("/config_tune", s.UpdateConfigTune)
}
// List 获取用户列表
@@ -139,13 +142,19 @@ func (s *App) ChangePassword(w http.ResponseWriter, r *http.Request) {
// GetPort 获取端口
func (s *App) GetPort(w http.ResponseWriter, r *http.Request) {
port, err := shell.Execf(`cat %s/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`, app.Root)
config, err := io.Read(fmt.Sprintf("%s/server/pure-ftpd/etc/pure-ftpd.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get port: %v", err))
return
}
service.Success(w, cast.ToInt(port))
bind := strings.Trim(s.getFTPValue(config, "Bind"), `"'`)
port := 21 // 默认端口
if parts := strings.SplitN(bind, ",", 2); len(parts) == 2 {
port = cast.ToInt(strings.TrimSpace(parts[1]))
}
service.Success(w, port)
}
// UpdatePort 设置端口
@@ -156,7 +165,14 @@ func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
return
}
if _, err = shell.Execf(`sed -i "s/Bind.*/Bind 0.0.0.0,%d/g" %s/server/pure-ftpd/etc/pure-ftpd.conf`, req.Port, app.Root); err != nil {
confPath := fmt.Sprintf("%s/server/pure-ftpd/etc/pure-ftpd.conf", app.Root)
config, err := io.Read(confPath)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
config = s.setFTPValue(config, "Bind", fmt.Sprintf(`"0.0.0.0,%d"`, req.Port))
if err = io.Write(confPath, config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -181,3 +197,124 @@ func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
// GetConfigTune 获取 Pure-FTPd 配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/pure-ftpd/etc/pure-ftpd.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
tune := ConfigTune{
MaxClientsNumber: s.getFTPValue(config, "MaxClientsNumber"),
MaxClientsPerIP: s.getFTPValue(config, "MaxClientsPerIP"),
MaxIdleTime: s.getFTPValue(config, "MaxIdleTime"),
MaxLoad: s.getFTPValue(config, "MaxLoad"),
PassivePortRange: s.getFTPValue(config, "PassivePortRange"),
AnonymousOnly: s.getFTPValue(config, "AnonymousOnly"),
NoAnonymous: s.getFTPValue(config, "NoAnonymous"),
MaxDiskUsage: s.getFTPValue(config, "MaxDiskUsage"),
}
service.Success(w, tune)
}
// UpdateConfigTune 更新 Pure-FTPd 配置调整参数
func (s *App) UpdateConfigTune(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ConfigTune](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
confPath := fmt.Sprintf("%s/server/pure-ftpd/etc/pure-ftpd.conf", app.Root)
config, err := io.Read(confPath)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
config = s.setFTPValue(config, "MaxClientsNumber", req.MaxClientsNumber)
config = s.setFTPValue(config, "MaxClientsPerIP", req.MaxClientsPerIP)
config = s.setFTPValue(config, "MaxIdleTime", req.MaxIdleTime)
config = s.setFTPValue(config, "MaxLoad", req.MaxLoad)
config = s.setFTPValue(config, "PassivePortRange", req.PassivePortRange)
config = s.setFTPValue(config, "AnonymousOnly", req.AnonymousOnly)
config = s.setFTPValue(config, "NoAnonymous", req.NoAnonymous)
config = s.setFTPValue(config, "MaxDiskUsage", req.MaxDiskUsage)
if err = io.Write(confPath, config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("pure-ftpd"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// getFTPValue 从 Pure-FTPd 配置内容中获取指定键的值
func (s *App) getFTPValue(content string, key string) string {
lines := strings.Split(content, "\n")
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
continue
}
parts := strings.Fields(trimmed)
if len(parts) >= 2 && parts[0] == key {
return strings.Join(parts[1:], " ")
}
}
return ""
}
// setFTPValue 在 Pure-FTPd 配置内容中设置指定键的值
func (s *App) setFTPValue(content string, key string, value string) string {
value = strings.ReplaceAll(value, "\n", "")
value = strings.ReplaceAll(value, "\r", "")
lines := strings.Split(content, "\n")
found := false
result := make([]string, 0, len(lines))
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" {
result = append(result, line)
continue
}
checkLine := trimmed
if strings.HasPrefix(checkLine, "#") {
checkLine = strings.TrimSpace(checkLine[1:])
}
parts := strings.Fields(checkLine)
if len(parts) >= 2 && parts[0] == key {
if found {
continue
}
found = true
// 值为空时注释掉该配置项
if value == "" {
if !strings.HasPrefix(trimmed, "#") {
result = append(result, "#"+line)
} else {
result = append(result, line)
}
continue
}
// 保留原行格式
indent := line[:len(line)-len(strings.TrimLeft(line, " \t"))]
result = append(result, indent+key+" "+value)
} else {
result = append(result, line)
}
}
if !found && value != "" {
result = append(result, key+" "+value)
}
return strings.Join(result, "\n")
}
+12
View File
@@ -18,3 +18,15 @@ type ChangePassword struct {
type UpdatePort struct {
Port uint `form:"port" json:"port" validate:"required|number|min:1|max:65535"`
}
// ConfigTune Pure-FTPd 配置调整
type ConfigTune struct {
MaxClientsNumber string `form:"max_clients_number" json:"max_clients_number"`
MaxClientsPerIP string `form:"max_clients_per_ip" json:"max_clients_per_ip"`
MaxIdleTime string `form:"max_idle_time" json:"max_idle_time"`
MaxLoad string `form:"max_load" json:"max_load"`
PassivePortRange string `form:"passive_port_range" json:"passive_port_range"`
AnonymousOnly string `form:"anonymous_only" json:"anonymous_only"`
NoAnonymous string `form:"no_anonymous" json:"no_anonymous"`
MaxDiskUsage string `form:"max_disk_usage" json:"max_disk_usage"`
}
+131 -6
View File
@@ -9,12 +9,12 @@ import (
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/shell"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/tnborg/panel/pkg/types"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/shell"
"github.com/acepanel/panel/pkg/systemctl"
"github.com/acepanel/panel/pkg/types"
)
type App struct {
@@ -31,6 +31,8 @@ func (s *App) Route(r chi.Router) {
r.Get("/load", s.Load)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
r.Get("/config_tune", s.GetConfigTune)
r.Post("/config_tune", s.UpdateConfigTune)
}
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
@@ -121,3 +123,126 @@ func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
service.Success(w, nil)
}
// GetConfigTune 获取 Redis 配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
config, err := io.Read(fmt.Sprintf("%s/server/redis/redis.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
tune := ConfigTune{
Bind: s.getRedisValue(config, "bind"),
Port: s.getRedisValue(config, "port"),
Databases: s.getRedisValue(config, "databases"),
Requirepass: s.getRedisValue(config, "requirepass"),
Timeout: s.getRedisValue(config, "timeout"),
TCPKeepalive: s.getRedisValue(config, "tcp-keepalive"),
Maxmemory: s.getRedisValue(config, "maxmemory"),
MaxmemoryPolicy: s.getRedisValue(config, "maxmemory-policy"),
Appendonly: s.getRedisValue(config, "appendonly"),
Appendfsync: s.getRedisValue(config, "appendfsync"),
}
service.Success(w, tune)
}
// UpdateConfigTune 更新 Redis 配置调整参数
func (s *App) UpdateConfigTune(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ConfigTune](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
confPath := fmt.Sprintf("%s/server/redis/redis.conf", app.Root)
config, err := io.Read(confPath)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
config = s.setRedisValue(config, "bind", req.Bind)
config = s.setRedisValue(config, "port", req.Port)
config = s.setRedisValue(config, "databases", req.Databases)
config = s.setRedisValue(config, "requirepass", req.Requirepass)
config = s.setRedisValue(config, "timeout", req.Timeout)
config = s.setRedisValue(config, "tcp-keepalive", req.TCPKeepalive)
config = s.setRedisValue(config, "maxmemory", req.Maxmemory)
config = s.setRedisValue(config, "maxmemory-policy", req.MaxmemoryPolicy)
config = s.setRedisValue(config, "appendonly", req.Appendonly)
config = s.setRedisValue(config, "appendfsync", req.Appendfsync)
if err = io.Write(confPath, config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("redis"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// getRedisValue 从 Redis 配置内容中获取指定键的值
func (s *App) getRedisValue(content string, key string) string {
lines := strings.Split(content, "\n")
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
continue
}
parts := strings.Fields(trimmed)
if len(parts) >= 2 && parts[0] == key {
return strings.Join(parts[1:], " ")
}
}
return ""
}
// setRedisValue 在 Redis 配置内容中设置指定键的值
func (s *App) setRedisValue(content string, key string, value string) string {
value = strings.ReplaceAll(value, "\n", "")
value = strings.ReplaceAll(value, "\r", "")
lines := strings.Split(content, "\n")
result := make([]string, 0, len(lines))
found := false
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if trimmed == "" {
result = append(result, line)
continue
}
checkLine := trimmed
if strings.HasPrefix(checkLine, "#") {
checkLine = strings.TrimSpace(checkLine[1:])
}
parts := strings.Fields(checkLine)
if len(parts) >= 1 && parts[0] == key {
if found {
continue
}
found = true
// 值为空时注释掉该配置项
if value == "" {
if !strings.HasPrefix(trimmed, "#") {
result = append(result, "# "+trimmed)
} else {
result = append(result, line)
}
continue
}
result = append(result, key+" "+value)
} else {
result = append(result, line)
}
}
if !found && value != "" {
result = append(result, key+" "+value)
}
return strings.Join(result, "\n")
}
+17
View File
@@ -3,3 +3,20 @@ package redis
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
// ConfigTune Redis 配置调整
type ConfigTune struct {
// 常规设置
Bind string `form:"bind" json:"bind"`
Port string `form:"port" json:"port"`
Databases string `form:"databases" json:"databases"`
Requirepass string `form:"requirepass" json:"requirepass"`
Timeout string `form:"timeout" json:"timeout"`
TCPKeepalive string `form:"tcp_keepalive" json:"tcp_keepalive"`
// 内存
Maxmemory string `form:"maxmemory" json:"maxmemory"`
MaxmemoryPolicy string `form:"maxmemory_policy" json:"maxmemory_policy"`
// 持久化
Appendonly string `form:"appendonly" json:"appendonly"`
Appendfsync string `form:"appendfsync" json:"appendfsync"`
}
+6 -6
View File
@@ -11,10 +11,10 @@ import (
"github.com/libtnb/chix"
"github.com/libtnb/utils/str"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/shell"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/shell"
"github.com/acepanel/panel/pkg/systemctl"
)
type App struct {
@@ -133,7 +133,7 @@ secrets file = /etc/rsyncd.secrets
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = io.WriteAppend("/etc/rsyncd.secrets", fmt.Sprintf(`%s:%s\n`, req.AuthUser, req.Secret), 0600); err != nil {
if err = io.WriteAppend("/etc/rsyncd.secrets", fmt.Sprintf("%s:%s\n", req.AuthUser, req.Secret), 0600); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -231,7 +231,7 @@ secrets file = /etc/rsyncd.secrets
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = io.WriteAppend("/etc/rsyncd.secrets", fmt.Sprintf(`%s:%s\n`, req.AuthUser, req.Secret), 0600); err != nil {
if err = io.WriteAppend("/etc/rsyncd.secrets", fmt.Sprintf("%s:%s\n", req.AuthUser, req.Secret), 0600); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
+7 -5
View File
@@ -12,9 +12,9 @@ import (
"github.com/libtnb/chix"
"github.com/spf13/cast"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/shell"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/shell"
)
type App struct {
@@ -94,17 +94,19 @@ func (s *App) Create(w http.ResponseWriter, r *http.Request) {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to create passwd file: %v", err))
return
}
if _, err = shell.Execf(`echo 's3fs#%s %s fuse _netdev,allow_other,nonempty,url=%s,passwd_file=/etc/passwd-s3fs-%s 0 0' >> /etc/fstab`, req.Bucket, req.Path, req.URL, cast.ToString(id)); err != nil {
if _, err = shell.Execf(`echo 's3fs#%s %s fuse3 _netdev,allow_other,url=%s,passwd_file=/etc/passwd-s3fs-%s 0 0' >> /etc/fstab`, req.Bucket, req.Path, req.URL, cast.ToString(id)); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if _, err = shell.Execf("mount -a"); err != nil {
_, _ = shell.Execf(`sed -i 's@^s3fs#%s\s%s.*$@@g' /etc/fstab`, req.Bucket, req.Path)
_ = os.Remove("/etc/passwd-s3fs-" + cast.ToString(id))
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if _, err = shell.Execf(`df -h | grep '%s'`, req.Path); err != nil {
_, _ = shell.Execf(`sed -i 's@^s3fs#%s\s%s.*$@@g' /etc/fstab`, req.Bucket, req.Path)
_ = os.Remove("/etc/passwd-s3fs-" + cast.ToString(id))
service.Error(w, http.StatusInternalServerError, s.t.Get("mount failed: %v", err))
return
}
@@ -138,7 +140,7 @@ func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
return
}
_, _ = shell.Execf(`fusermount -uz '%s'`, mount.Path)
_, _ = shell.Execf(`fusermount3 -uz '%s'`, mount.Path)
_, err2 := shell.Execf(`umount -lf '%s'`, mount.Path)
// 卸载之后再检查下是否还有挂载
if _, err = shell.Execf(`df -h | grep '%s'`, mount.Path); err == nil {
+5 -5
View File
@@ -9,11 +9,11 @@ import (
"github.com/libtnb/chix"
"github.com/spf13/cast"
"github.com/tnborg/panel/internal/service"
"github.com/tnborg/panel/pkg/io"
"github.com/tnborg/panel/pkg/os"
"github.com/tnborg/panel/pkg/shell"
"github.com/tnborg/panel/pkg/systemctl"
"github.com/acepanel/panel/internal/service"
"github.com/acepanel/panel/pkg/io"
"github.com/acepanel/panel/pkg/os"
"github.com/acepanel/panel/pkg/shell"
"github.com/acepanel/panel/pkg/systemctl"
)
type App struct {
+5 -2
View File
@@ -3,7 +3,8 @@ package biz
import (
"time"
"github.com/tnborg/panel/pkg/api"
"github.com/acepanel/panel/pkg/api"
"github.com/acepanel/panel/pkg/types"
)
type App struct {
@@ -18,6 +19,7 @@ type App struct {
}
type AppRepo interface {
Categories() []types.LV
All() api.Apps
Get(slug string) (*api.App, error)
UpdateExist(slug string) bool
@@ -25,9 +27,10 @@ type AppRepo interface {
GetInstalled(slug string) (*App, error)
GetInstalledAll(query string, cond ...string) ([]*App, error)
GetHomeShow() ([]map[string]string, error)
IsInstalled(query string, cond ...string) (bool, error)
IsInstalled(query string, cond ...any) (bool, error)
Install(channel, slug string) error
UnInstall(slug string) error
Update(slug string) error
UpdateShow(slug string, show bool) error
UpdateOrder(slugs []string) error
}
+12 -6
View File
@@ -1,6 +1,10 @@
package biz
import "github.com/tnborg/panel/pkg/types"
import (
"context"
"github.com/acepanel/panel/pkg/types"
)
type BackupType string
@@ -15,12 +19,14 @@ const (
type BackupRepo interface {
List(typ BackupType) ([]*types.BackupFile, error)
Create(typ BackupType, target string, path ...string) error
Delete(typ BackupType, name string) error
Restore(typ BackupType, backup, target string) error
ClearExpired(path, prefix string, save int) error
Create(ctx context.Context, typ BackupType, target string, account uint) error
CreatePanel() error
Delete(ctx context.Context, typ BackupType, name string) error
Restore(ctx context.Context, typ BackupType, backup, target string) error
ClearExpired(path, prefix string, save uint) error
ClearStorageExpired(account uint, typ BackupType, prefix string, save uint) error
CutoffLog(path, target string) error
GetPath(typ BackupType) (string, error)
GetDefaultPath(typ BackupType) string
FixPanel() error
UpdatePanel(version, url, checksum string) error
}
+137
View File
@@ -0,0 +1,137 @@
package biz
import (
"context"
"time"
"github.com/libtnb/utils/crypt"
"gorm.io/gorm"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/http/request"
"github.com/acepanel/panel/pkg/types"
)
type BackupStorageType string
const (
BackupStorageTypeLocal BackupStorageType = "local"
BackupStorageTypeS3 BackupStorageType = "s3"
BackupStorageTypeSFTP BackupStorageType = "sftp"
BackupStorageTypeWebDAV BackupStorageType = "webdav"
)
type BackupStorage struct {
ID uint `gorm:"primaryKey" json:"id"`
Type BackupStorageType `gorm:"not null;default:''" json:"type"`
Name string `gorm:"not null;default:''" json:"name"`
Info types.BackupStorageInfo `gorm:"not null;default:'{}';serializer:json" json:"info"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (r *BackupStorage) BeforeSave(tx *gorm.DB) error {
crypter, err := crypt.NewXChacha20Poly1305([]byte(app.Key))
if err != nil {
return err
}
switch r.Type {
case BackupStorageTypeS3:
r.Info.AccessKey, err = crypter.Encrypt([]byte(r.Info.AccessKey))
if err != nil {
return err
}
r.Info.SecretKey, err = crypter.Encrypt([]byte(r.Info.SecretKey))
if err != nil {
return err
}
return nil
case BackupStorageTypeSFTP:
r.Info.Username, err = crypter.Encrypt([]byte(r.Info.Username))
if err != nil {
return err
}
if r.Info.Password != "" {
r.Info.Password, err = crypter.Encrypt([]byte(r.Info.Password))
if err != nil {
return err
}
}
if r.Info.PrivateKey != "" {
r.Info.PrivateKey, err = crypter.Encrypt([]byte(r.Info.PrivateKey))
if err != nil {
return err
}
}
case BackupStorageTypeWebDAV:
r.Info.Username, err = crypter.Encrypt([]byte(r.Info.Username))
if err != nil {
return err
}
r.Info.Password, err = crypter.Encrypt([]byte(r.Info.Password))
if err != nil {
return err
}
return nil
}
return nil
}
func (r *BackupStorage) AfterFind(tx *gorm.DB) error {
crypter, err := crypt.NewXChacha20Poly1305([]byte(app.Key))
if err != nil {
return err
}
switch r.Type {
case BackupStorageTypeS3:
accessKey, err := crypter.Decrypt(r.Info.AccessKey)
if err == nil {
r.Info.AccessKey = string(accessKey)
}
secretKey, err := crypter.Decrypt(r.Info.SecretKey)
if err == nil {
r.Info.SecretKey = string(secretKey)
}
return nil
case BackupStorageTypeSFTP:
username, err := crypter.Decrypt(r.Info.Username)
if err == nil {
r.Info.Username = string(username)
}
if r.Info.Password != "" {
password, err := crypter.Decrypt(r.Info.Password)
if err == nil {
r.Info.Password = string(password)
}
}
if r.Info.PrivateKey != "" {
privateKey, err := crypter.Decrypt(r.Info.PrivateKey)
if err == nil {
r.Info.PrivateKey = string(privateKey)
}
}
case BackupStorageTypeWebDAV:
username, err := crypter.Decrypt(r.Info.Username)
if err == nil {
r.Info.Username = string(username)
}
password, err := crypter.Decrypt(r.Info.Password)
if err == nil {
r.Info.Password = string(password)
}
return nil
}
return nil
}
type BackupAccountRepo interface {
List(page, limit uint) ([]*BackupStorage, int64, error)
Get(id uint) (*BackupStorage, error)
Create(ctx context.Context, req *request.BackupStorageCreate) (*BackupStorage, error)
Update(ctx context.Context, req *request.BackupStorageUpdate) error
Delete(ctx context.Context, id uint) error
}
+7 -3
View File
@@ -5,8 +5,10 @@ import "time"
type CacheKey string
const (
CacheKeyApps CacheKey = "apps"
CacheKeyRewrites CacheKey = "rewrites"
CacheKeyCategories CacheKey = "categories"
CacheKeyApps CacheKey = "apps"
CacheKeyEnvironment CacheKey = "environment"
CacheKeyTemplates CacheKey = "templates"
)
type Cache struct {
@@ -19,6 +21,8 @@ type Cache struct {
type CacheRepo interface {
Get(key CacheKey, defaultValue ...string) (string, error)
Set(key CacheKey, value string) error
UpdateCategories() error
UpdateApps() error
UpdateRewrites() error
UpdateEnvironments() error
UpdateTemplates() error
}
+26 -22
View File
@@ -1,27 +1,31 @@
package biz
import (
"context"
"time"
"github.com/tnborg/panel/internal/http/request"
"github.com/tnborg/panel/pkg/acme"
"github.com/tnborg/panel/pkg/types"
mholtacme "github.com/mholt/acmez/v3/acme"
"github.com/acepanel/panel/internal/http/request"
"github.com/acepanel/panel/pkg/acme"
"github.com/acepanel/panel/pkg/types"
)
type Cert struct {
ID uint `gorm:"primaryKey" json:"id"`
AccountID uint `gorm:"not null;default:0" json:"account_id"` // 关联的 ACME 账户 ID
WebsiteID uint `gorm:"not null;default:0" json:"website_id"` // 关联的网站 ID
DNSID uint `gorm:"not null;default:0" json:"dns_id"` // 关联的 DNS ID
Type string `gorm:"not null;default:''" json:"type"` // 证书类型 (P256, P384, 2048, 3072, 4096)
Domains []string `gorm:"not null;default:'[]';serializer:json" json:"domains"`
AutoRenew bool `gorm:"not null;default:false" json:"auto_renew"` // 自动续签
CertURL string `gorm:"not null;default:''" json:"cert_url"` // 证书 URL (续签时使用)
Cert string `gorm:"not null;default:''" json:"cert"` // 证书内容
Key string `gorm:"not null;default:''" json:"key"` // 私钥内容
Script string `gorm:"not null;default:''" json:"script"` // 部署脚本
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ID uint `gorm:"primaryKey" json:"id"`
AccountID uint `gorm:"not null;default:0" json:"account_id"` // 关联的 ACME 账户 ID
WebsiteID uint `gorm:"not null;default:0" json:"website_id"` // 关联的网站 ID
DNSID uint `gorm:"not null;default:0" json:"dns_id"` // 关联的 DNS ID
Type string `gorm:"not null;default:''" json:"type"` // 证书类型 (P256, P384, 2048, 3072, 4096)
Domains []string `gorm:"not null;default:'[]';serializer:json" json:"domains"`
AutoRenewal bool `gorm:"not null;default:false" json:"auto_renewal"` // 自动续签
RenewalInfo mholtacme.RenewalInfo `gorm:"not null;default:'{}';serializer:json" json:"renewal_info"` // 续签信息
CertURL string `gorm:"not null;default:''" json:"cert_url"` // 证书 URL (续签时使用)
Cert string `gorm:"not null;default:''" json:"cert"` // 证书内容
Key string `gorm:"not null;default:''" json:"key"` // 私钥内容
Script string `gorm:"not null;default:''" json:"script"` // 部署脚本
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Website *Website `gorm:"foreignKey:WebsiteID" json:"website"`
Account *CertAccount `gorm:"foreignKey:AccountID" json:"account"`
@@ -32,14 +36,14 @@ type CertRepo interface {
List(page, limit uint) ([]*types.CertList, int64, error)
Get(id uint) (*Cert, error)
GetByWebsite(WebsiteID uint) (*Cert, error)
Upload(req *request.CertUpload) (*Cert, error)
Create(req *request.CertCreate) (*Cert, error)
Update(req *request.CertUpdate) error
Delete(id uint) error
Upload(ctx context.Context, req *request.CertUpload) (*Cert, error)
Create(ctx context.Context, req *request.CertCreate) (*Cert, error)
Update(ctx context.Context, req *request.CertUpdate) error
Delete(ctx context.Context, id uint) error
ObtainAuto(id uint) (*acme.Certificate, error)
ObtainManual(id uint) (*acme.Certificate, error)
ObtainPanel(account *CertAccount, ips []string) ([]byte, []byte, error)
ObtainSelfSigned(id uint) error
Renew(id uint) (*acme.Certificate, error)
ManualDNS(id uint) ([]acme.DNSRecord, error)
RefreshRenewalInfo(id uint) (mholtacme.RenewalInfo, error)
Deploy(ID, WebsiteID uint) error
}
+5 -4
View File
@@ -1,9 +1,10 @@
package biz
import (
"context"
"time"
"github.com/tnborg/panel/internal/http/request"
"github.com/acepanel/panel/internal/http/request"
)
type CertAccount struct {
@@ -24,7 +25,7 @@ type CertAccountRepo interface {
List(page, limit uint) ([]*CertAccount, int64, error)
GetDefault(userID uint) (*CertAccount, error)
Get(id uint) (*CertAccount, error)
Create(req *request.CertAccountCreate) (*CertAccount, error)
Update(req *request.CertAccountUpdate) error
Delete(id uint) error
Create(ctx context.Context, req *request.CertAccountCreate) (*CertAccount, error)
Update(ctx context.Context, req *request.CertAccountUpdate) error
Delete(ctx context.Context, id uint) error
}
+7 -6
View File
@@ -1,17 +1,18 @@
package biz
import (
"context"
"time"
"github.com/tnborg/panel/internal/http/request"
"github.com/tnborg/panel/pkg/acme"
"github.com/acepanel/panel/internal/http/request"
"github.com/acepanel/panel/pkg/acme"
)
type CertDNS struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"not null;default:''" json:"name"` // 备注名称
Type acme.DnsType `gorm:"not null;default:'aliyun'" json:"type"` // DNS 提供商
Data acme.DNSParam `gorm:"not null;serializer:json" json:"dns_param"`
Data acme.DNSParam `gorm:"not null;default:'{}';serializer:json" json:"dns_param"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
@@ -21,7 +22,7 @@ type CertDNS struct {
type CertDNSRepo interface {
List(page, limit uint) ([]*CertDNS, int64, error)
Get(id uint) (*CertDNS, error)
Create(req *request.CertDNSCreate) (*CertDNS, error)
Update(req *request.CertDNSUpdate) error
Delete(id uint) error
Create(ctx context.Context, req *request.CertDNSCreate) (*CertDNS, error)
Update(ctx context.Context, req *request.CertDNSUpdate) error
Delete(ctx context.Context, id uint) error
}
+2 -2
View File
@@ -1,8 +1,8 @@
package biz
import (
"github.com/tnborg/panel/internal/http/request"
"github.com/tnborg/panel/pkg/types"
"github.com/acepanel/panel/internal/http/request"
"github.com/acepanel/panel/pkg/types"
)
type ContainerRepo interface {
+1 -1
View File
@@ -1,6 +1,6 @@
package biz
import "github.com/tnborg/panel/pkg/types"
import "github.com/acepanel/panel/pkg/types"
type ContainerComposeRepo interface {
List() ([]types.ContainerCompose, error)
+3 -2
View File
@@ -1,12 +1,13 @@
package biz
import (
"github.com/tnborg/panel/internal/http/request"
"github.com/tnborg/panel/pkg/types"
"github.com/acepanel/panel/internal/http/request"
"github.com/acepanel/panel/pkg/types"
)
type ContainerImageRepo interface {
List() ([]types.ContainerImage, error)
Exist(name string) (bool, error)
Pull(req *request.ContainerImagePull) error
Remove(id string) error
Prune() error
+2 -2
View File
@@ -1,8 +1,8 @@
package biz
import (
"github.com/tnborg/panel/internal/http/request"
"github.com/tnborg/panel/pkg/types"
"github.com/acepanel/panel/internal/http/request"
"github.com/acepanel/panel/pkg/types"
)
type ContainerNetworkRepo interface {
+2 -2
View File
@@ -1,8 +1,8 @@
package biz
import (
"github.com/tnborg/panel/internal/http/request"
"github.com/tnborg/panel/pkg/types"
"github.com/acepanel/panel/internal/http/request"
"github.com/acepanel/panel/pkg/types"
)
type ContainerVolumeRepo interface {
+5 -4
View File
@@ -1,9 +1,10 @@
package biz
import (
"context"
"time"
"github.com/tnborg/panel/internal/http/request"
"github.com/acepanel/panel/internal/http/request"
)
type Cron struct {
@@ -22,8 +23,8 @@ type CronRepo interface {
Count() (int64, error)
List(page, limit uint) ([]*Cron, int64, error)
Get(id uint) (*Cron, error)
Create(req *request.CronCreate) error
Update(req *request.CronUpdate) error
Delete(id uint) error
Create(ctx context.Context, req *request.CronCreate) error
Update(ctx context.Context, req *request.CronUpdate) error
Delete(ctx context.Context, id uint) error
Status(id uint, status bool) error
}
+5 -3
View File
@@ -1,7 +1,9 @@
package biz
import (
"github.com/tnborg/panel/internal/http/request"
"context"
"github.com/acepanel/panel/internal/http/request"
)
type DatabaseType string
@@ -25,7 +27,7 @@ type Database struct {
type DatabaseRepo interface {
List(page, limit uint) ([]*Database, int64, error)
Create(req *request.DatabaseCreate) error
Delete(serverID uint, name string) error
Create(ctx context.Context, req *request.DatabaseCreate) error
Delete(ctx context.Context, serverID uint, name string) error
Comment(req *request.DatabaseComment) error
}
+3 -2
View File
@@ -6,8 +6,8 @@ import (
"github.com/libtnb/utils/crypt"
"gorm.io/gorm"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/http/request"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/http/request"
)
type DatabaseServerStatus string
@@ -68,6 +68,7 @@ type DatabaseServerRepo interface {
Create(req *request.DatabaseServerCreate) error
Update(req *request.DatabaseServerUpdate) error
UpdateRemark(req *request.DatabaseServerUpdateRemark) error
UpdatePassword(name string, password string) error
Delete(id uint) error
ClearUsers(id uint) error
Sync(id uint) error
+5 -4
View File
@@ -1,13 +1,14 @@
package biz
import (
"context"
"time"
"github.com/libtnb/utils/crypt"
"gorm.io/gorm"
"github.com/tnborg/panel/internal/app"
"github.com/tnborg/panel/internal/http/request"
"github.com/acepanel/panel/internal/app"
"github.com/acepanel/panel/internal/http/request"
)
type DatabaseUserStatus string
@@ -65,9 +66,9 @@ type DatabaseUserRepo interface {
Count() (int64, error)
List(page, limit uint) ([]*DatabaseUser, int64, error)
Get(id uint) (*DatabaseUser, error)
Create(req *request.DatabaseUserCreate) error
Create(ctx context.Context, req *request.DatabaseUserCreate) error
Update(req *request.DatabaseUserUpdate) error
UpdateRemark(req *request.DatabaseUserUpdateRemark) error
Delete(id uint) error
Delete(ctx context.Context, id uint) error
DeleteByNames(serverID uint, names []string) error
}
+18
View File
@@ -0,0 +1,18 @@
package biz
import (
"github.com/acepanel/panel/pkg/api"
"github.com/acepanel/panel/pkg/types"
)
type EnvironmentRepo interface {
Types() []types.LV
All(typ ...string) api.Environments
IsInstalled(typ, slug string) bool
InstalledSlugs(typ string) []string
InstalledVersion(typ, slug string) string
HasUpdate(typ, slug string) bool
Install(typ, slug string) error
Uninstall(typ, slug string) error
Update(typ, slug string) error
}

Some files were not shown because too many files have changed in this diff Show More