Compare commits

...

2715 Commits

Author SHA1 Message Date
耗子 fb133971db feat(waf): 接入 acewaf 管理模块 2026-08-01 00:58:32 +08:00
renovate[bot] a4e3a8d38f chore(deps): Update dependency vue-tsc to v3.3.9 (#1739)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-31 14:04:48 +00:00
renovate[bot] ab507da99a chore(deps): Update dependency vite to v8.2.0 (#1738)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-30 22:44:29 +00:00
renovate[bot] 8e8fad1287 chore(deps): Update dependency js-sha256 to v1 (#1737)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-30 18:53:19 +08:00
renovate[bot] 03e9646e57 chore(deps): Update non-major dependencies (#1736)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-29 17:57:44 +00:00
耗子 7613d7e73e Update star history chart links in README.md 2026-07-29 19:24:38 +08:00
renovate[bot] 6156c8737b chore(deps): Update dependency @vueuse/core to v14.4.0 (#1735)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-29 06:08:55 +00:00
renovate[bot] c25443189f chore(deps): Update dependency npm-run-all2 to v9.0.3 (#1734)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-28 22:55:56 +00:00
renovate[bot] ef824b2f6d chore(deps): Update non-major dependencies (#1733)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-28 02:10:29 +00:00
耗子 c938da4f4f feat: 日志查看体验优化
- 搜索支持上/下一个、匹配计数、命中行及关键词高亮
- 暂停跟随时显示新日志气泡,点击回到底部
- 限制最大保留 5000 行,防止高频日志拖垮页面
- 新增软换行开关、复制全部,字号与换行设置持久化
- 初始加载与翻页边界提示,连接状态点悬停说明

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 20:50:55 +08:00
renovate[bot] c2bee5b6f2 chore(deps): Lock file maintenance (#1732)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-27 06:36:52 +00:00
耗子 3f339d1a53 style: format Go sources 2026-07-27 02:55:03 +08:00
耗子 ea22c2f0a7 refactor(di): restore compile-time Wire injection
Replace the samber/do runtime container with explicit constructor dependencies and generated Wire graphs for ace and cli. Keep CLI application loading isolated and verify generated files in CI.
2026-07-27 02:54:44 +08:00
github-actions[bot] ad4559eea3 New Crowdin translations by GitHub Action (#1731)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-27 01:34:45 +08:00
h2zi cdd095e7f5 chore(l10n): update pot files 2026-07-26 16:35:07 +00:00
耗子 3a2f0db45c feat: 日志支持全屏 2026-07-25 22:48:08 +08:00
耗子 4e28fc60a3 feat: 添加通知与告警模块
- 通知渠道支持配置多个 SMTP 账号,配置整体加密落库,删除时清理规则与事件设置中的引用
- 告警规则支持 18 种指标:CPU/内存/Swap/负载/磁盘用量与 inode/磁盘与网卡速率/网站 5xx 与错误率、
  服务与项目与容器与应用与数据库运行状态、证书与网站剩余天数,可配连续命中次数与静默期
- 系统事件通知覆盖证书续签失败、备份失败、后台任务失败、计划任务失败、网站到期、防篡改拦截、
  面板健康问题、面板登录与爆破、SSH 登录与爆破
- 计划任务统一经 wrapper 执行以捕获退出码,flock 抢锁失败不计为失败
- 监控页改造为系统/告警/设置三 Tab
- 数据库驱动建连打通 context 并补齐连接超时,探测不再阻塞告警评估

close #1041

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 22:45:51 +08:00
renovate[bot] 693dd36b30 chore(deps): Update dependency vite-plugin-vue-devtools to v8.2.1 (#1730)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-25 14:16:08 +00:00
renovate[bot] e46ef25e28 chore(deps): Update non-major dependencies (#1729)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-25 05:32:45 +00:00
耗子 ce50aa4653 feat: 备份恢复改为后台任务
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-25 01:45:48 +08:00
耗子 d9fee94db5 feat: 优化systemd日志加载性能
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-25 01:45:40 +08:00
耗子 7f9293e562 feat: 优化SSH日志性能 2026-07-25 00:15:43 +08:00
耗子 aca88ffeea feat: 去除设置层的缓存 2026-07-25 00:05:59 +08:00
耗子 634e361e86 feat: 优化防篡改性能 2026-07-24 23:48:23 +08:00
耗子 afbc21160f Merge remote-tracking branch 'origin/main' 2026-07-24 23:40:04 +08:00
耗子 ca49160de2 feat: 优化eBPF性能 2026-07-24 23:39:58 +08:00
renovate[bot] 0e02f7ed10 chore(deps): Update non-major dependencies (#1728)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-24 19:13:52 +08:00
耗子 d3b5f0672a Merge remote-tracking branch 'origin/main' 2026-07-24 19:06:28 +08:00
耗子 2ad637e1d7 feat: 支持下载备份 2026-07-24 19:06:22 +08:00
renovate[bot] 5413926d06 chore(deps): Update non-major dependencies (#1727)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-24 01:52:32 +00:00
耗子 0fc912e6a2 fix: 备份临时目录改到面板目录下
未指定 TMPDIR 时备份临时目录落在面板目录 tmp 下,
避免大文件撑爆 /tmp 内存盘,任务队列的独享目录取消清理机制不变

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:24:47 +08:00
renovate[bot] b333df8d2c chore(deps): Update dependency sass to v1.101.6 (#1726)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-23 06:44:45 +00:00
renovate[bot] e2ad3407aa chore(deps): Update non-major dependencies (#1725)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-23 07:05:01 +08:00
耗子 f25cd50763 fix: Java 项目 systemd 添加 SuccessExitStatus=143 且更新时保留自定义配置
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 02:47:35 +08:00
耗子 d8d1e20c5a fix: 创建 MySQL 数据库授权已有用户报错 1410
MySQL 8 起 GRANT 不再隐式创建用户,授权已有用户时改为按
mysql.user 中实际存在的 host 逐一授权,用户不存在时明确报错,
并统一空 host 参数回退为 %

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 23:31:18 +08:00
耗子 9c977c154b feat: 迁移支持 ClickHouse 数据库并修复多处问题
- 数据库迁移新增 ClickHouse 支持(结构 + SQLInsert 数据导出,远程 multiquery 导入)
- 数据库用户先于数据库迁移,修复导入时属主角色不存在
- pg_dump 加 --clean --if-exists,mysqldump 加 --routines --events
- 修复远程解压失败、SSE 流中断被静默当作成功的问题
- 临时文件改到面板目录 tmp 下,避免撑爆 /tmp 内存盘

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 22:15:17 +08:00
renovate[bot] f347b77c72 chore(deps): Update dependency eslint-plugin-oxlint to v1.75.0 (#1724)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-22 09:57:57 +00:00
renovate[bot] d356b7f5a0 chore(deps): Update non-major dependencies (#1723)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-22 01:30:34 +00:00
耗子 1b184f5178 fix: 项目 systemd 环境变量加双引号
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 06:55:35 +08:00
耗子 f4a056b601 fix: 优化 2026-07-22 01:49:45 +08:00
github-actions[bot] 02f6011c14 New Crowdin translations by GitHub Action (#1722)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-21 18:58:59 +08:00
h2zi c0a1c177b9 chore(l10n): update pot files 2026-07-21 10:57:16 +00:00
h2zi f5eea8b4a9 chore: update mocks 2026-07-21 10:55:55 +00:00
耗子 f0d8acca31 fix: 优化 2026-07-21 18:54:59 +08:00
耗子 5d314af538 fix: 优化 2026-07-21 18:37:32 +08:00
耗子 5ad6f8fa98 fix: 优化 2026-07-21 18:32:28 +08:00
renovate[bot] fa26e643b7 chore(deps): Update non-major dependencies (#1721)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-21 09:33:29 +00:00
github-actions[bot] 1528d7dfc5 New Crowdin translations by GitHub Action (#1720)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-21 06:15:36 +08:00
耗子 8a205f6ec8 fix(taskqueue): 修复 TestRunnerCancelRunning 在 CI 上偶发失败
file::memory: DSN 每个连接得到独立私有库,GORM 连接池多连接时
主 goroutine 创建的任务对 runner goroutine 不可见,runner 查不到
waiting 任务导致 waitStatus 超时。限单连接与 SQLite 单文件语义一致。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-21 06:11:15 +08:00
h2zi 595b1c4fc7 chore(l10n): update pot files 2026-07-20 22:07:05 +00:00
耗子 e0539a908a fix: 优化性能 2026-07-21 06:01:35 +08:00
耗子 90634fdb93 fix(tamper): 事件名解析改 C 字符串截断修复 ringbuf 复用污染
- 上一版为省 32 条清零指令改成只清 name[0],但用户态 TrimRight("\x00")
  只删末尾连续 NUL,ringbuf 复用旧数据时新事件的短名后会拼进旧尾部
- 恢复完整清零 + 用户态改用 cString(IndexByte 首个 NUL 截断)
- 补 inode_file_setattr optional hook 覆盖 FS_IOC_FSSETXATTR
- 暴露 UserDrops 观察模式健康检查;remove 关闭后返回错误一致化
- batchUpdateAll 加 key/value 长度保护;注释按 WHY 收敛

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-21 05:52:49 +08:00
耗子 c01ac0cbd3 fix(tamper): eBPF 模式严格化保护并消除多类绕过
- 目录 inode 纳入对象保护 + 新增 inode_rmdir/link 源/xattr/ACL hook 覆盖旁路
- 严格模式内核直拒 mkdir 与目录移入,消除"放行后异步纳管"竞态
- mountinfo 读 s_dev 修复 btrfs 子卷保护静默失效
- 事件 name 扩至 256 字节,op 最高位携带 denied 标志区分拦截/观察
- 扩展名 fail-closed:任一无法进内核表升级整树,而非静默漏保护
- 两阶段激活(先 apply 后 attach)消除空窗;start 幂等失败回滚
- Close 与 apply/remove 通过 mu 互斥,批量更新校验完成数量
- 前端 Block New Files 描述随行为更新,拦截日志刷新按钮修复

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-21 05:32:53 +08:00
耗子 9b8141a5ea fix: pgadmin性能优化 2026-07-21 02:11:57 +08:00
耗子 5263b1b730 feat(database): 数据库工具语言跟随面板并补充按钮图标
phpMyAdmin 代理登录透传浏览器语言并显式指定面板语言,
pgAdmin 登录固化面板语言映射进会话,跳转按钮展示应用图标

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 00:58:58 +08:00
耗子 41bf2531f1 feat(ssh): 文件互传支持目录传输并固定暗色主题
目录递归传输,遍历源树重建目录结构逐文件传输,进度按全树
累计字节汇报,传输前二次确认并建议大目录先压缩;互传界面
固定暗色与终端一致,修复传输按钮在暗色下不可见

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 00:58:57 +08:00
耗子 71e7c5ab5d fix(website): 修复默认站点设置与回显失效
网站列表空类型会被当作精确匹配导致查询恒空,默认站点的
读取与设置改为显式全量查询,数据层空类型对齐为不过滤

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 00:58:57 +08:00
耗子 038ef2c44f fix(tamper): 修复重启恢复延迟与统计日志时效
定时任务支持启动后立即触发,面板重启即恢复保护不留空窗;
查看拦截日志前先落库缓冲即拦即见;受保护目录改按监控口径
统计,带后缀规则不再恒为零

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 00:58:57 +08:00
耗子 aba65c4cdf refactor(safe): 安全菜单扁平化并将防篡改并入防火墙页
安全菜单直达防火墙视图,防篡改作为扫描感知后的标签页呈现,
自身子页改为竖排标签;修复规则弹窗网站列表 422、eBPF 激活
弹窗按钮文案、拦截新建开关对齐与导入按钮换行

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 00:58:37 +08:00
耗子 5ef34ab1ba refactor(app): 编译参数改为安装时输入
移除独立的编译参数按钮,应用安装弹窗内嵌可折叠的参数表单,
编译型环境安装改为带参数输入的弹窗,提交安装前自动保存

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 00:58:21 +08:00
github-actions[bot] 83750d15c5 New Crowdin translations by GitHub Action (#1719)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-20 23:54:40 +08:00
h2zi 824f06970d chore(l10n): update pot files 2026-07-20 15:42:53 +00:00
renovate[bot] 7ad1b721f7 chore(deps): Update module github.com/samber/do/v2 to v2.1.0 (#1718)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-20 23:38:57 +08:00
耗子 1f899d1e47 chore(deps): 升级 monaco-editor 至 0.56.0 适配 ESM 结构重组
0.56.0 重组了 ESM 模块并启用 exports 映射,语言包与 worker
的深路径导入改为 exports 子路径形式

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 23:30:22 +08:00
耗子 a2e46faab9 feat(task): 任务队列支持取消运行中与等待中的任务
任务执行接入 context 并放入独立进程组,取消时整组杀死;
新增可选 CancelShell 清理钩子,远程下载、压缩、备份任务
取消后自动清理残留文件,备份改用独享临时目录避免误伤并发备份;
防重改为按语言无关的任务标识 Key 匹配

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 23:16:49 +08:00
耗子 8fa5c6d881 feat(file): 文件分享下载支持面板间互传
生成免登录的临时下载链接,强制设置有效期并支持下载次数限制,
过期分享由定时任务每小时清理,链接可直接用于其他面板的远程下载。

Closes #1641

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 22:47:11 +08:00
github-actions[bot] 293da754ce New Crowdin translations by GitHub Action (#1717)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-20 22:14:17 +08:00
h2zi 0078625289 chore(l10n): update pot files 2026-07-20 14:00:57 +00:00
耗子 1bfe08e52e feat(ui): 新增安全菜单归并防火墙与防篡改
侧边栏以安全父菜单取代防火墙原位置,防火墙与防篡改作为子菜单,
旧路径重定向兼容书签与持久化标签页,补充防篡改菜单标题翻译映射

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 21:58:25 +08:00
耗子 58da2ce5c3 Merge remote-tracking branch 'origin/renovate/non-major-dependencies' 2026-07-20 20:49:59 +08:00
h2zi 8672a8456b chore(l10n): update pot files 2026-07-20 12:40:29 +00:00
renovate[bot] f00f0c7f14 chore(deps): Update module github.com/pkg/sftp to v1.13.11 2026-07-20 12:40:18 +00:00
renovate[bot] c80faa5f0f chore(deps): Update non-major dependencies (#1709)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-20 20:38:59 +08:00
renovate[bot] 1baa7c03a8 chore(deps): Update actions/setup-go action to v7 (#1715)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-20 20:16:26 +08:00
耗子 25331a7a24 fix(ci): 修复 errcheck 告警与防篡改设置校验规则
统一 defer 关闭与错误路径清理为显式忽略返回值,
tamper 设置 Mode 校验改用项目 DSL 支持的 required 规则

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 19:57:56 +08:00
耗子 d5a635c6dd feat(database): 数据库页按已添加服务器类型动态显示标签页
用户反馈类型标签页太多显示乱,改为仅展示已添加服务器的类型,
默认定位到第一个可用类型;服务器增删改后经事件总线联动刷新,
修复事件总线整型解绑误伤其他组件监听的问题

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 10:14:03 +08:00
耗子 8fc21039c0 feat(pgadmin): 全量同步 PG 服务器并保留增强 Cookie 保护
一键登录改为一次性合并面板全部 PostgreSQL 服务器(dump 查缺仅追加,
不影响手动添加的条目);代理登录透传浏览器 IP/UA,通过 Flask-Paranoid
的 sha256(IP|UA) 会话绑定校验,无需关闭 ENHANCED_COOKIE_PROTECTION;
适配 9.x React 登录页的 JSON 形态 CSRF token,Docker 实测全链路通过

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 10:14:03 +08:00
耗子 b5b5388b5c feat(app): 上架 pgAdmin 应用
pip+venv 本地部署,管理页支持改端口与重置密码,数据库 PostgreSQL 页
一键同步服务器并免登录进入 pgAdmin(凭据经 pgpass 免密)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 09:50:20 +08:00
耗子 874abf005d feat(app): 应用安装支持自定义编译参数
编译型应用与 PHP/Python 环境支持持久化的前置脚本与 configure 参数,
保存在 storage/customize 随安装更新自动生效,并接入面板备份与修复

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 06:27:39 +08:00
耗子 3c7385114f fix(file): 行内更多按钮补齐防篡改保护操作,与右键菜单一致
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 06:01:46 +08:00
耗子 2492534c28 feat(file): 文件管理集成防篡改状态展示与右键保护操作
- 列表/图标视图受保护条目显示盾牌图标,与裸 immutable 锁区分
- 右键菜单添加/移除保护(防篡改运行时显示,弹窗二次确认)
- 新增 /tamper/check_paths 批量状态查询与 /tamper/protect 单路径切换
- 保护切换经规则/排除联动:命中排除则去除排除项,目录可直建整树规则,
  路径为规则根则删规则,规则内子路径则加入排除

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 05:53:22 +08:00
耗子 5db94ee61c fix(tamper): 修复登记表脏映射与新建监控竞态并统一英文日志
- Unlock/Relock 同步维护事件回填表与统计,移动/替换文件后不再残留脏路径
- 新建目录补扫竞态窗口内已写入的文件,堵住 mkdir 后立即落文件的漏检
- chattr 整树模式下新建拦截需临时解除父目录 +a 才能删除
- inotify 监控失败聚合告警,便于发现 max_user_watches 耗尽
- 扫描器单网卡挂载失败仅跳过,聚合缓冲加上限防伪造源洪水耗尽内存
- ringbuf 解析改主机字节序,日志与错误串统一英文

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 05:53:14 +08:00
耗子 f151e29659 fix(tamper): 修复 eBPF 防护绕过并优化两处 BPF map 资源占用
- inode_rename 增查 new_dentry,堵住改名覆盖受保护文件的绕过
- 新增 inode_setattr 拦截点,堵住 truncate/chmod/chown 绕过
- 两处 hash map 加 BPF_F_NO_PREALLOC,消除数十 MB 内核内存预占
- 保护集合改批量更新,大目录树免逐键 syscall
- 扫描检测器 Supported() 在 lo 试挂 TCX,低版本内核不再误报支持
- lsm= 注入正则加词边界,防误改同后缀参数

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 05:28:26 +08:00
耗子 bea4654316 fix(tamper): eBPF 保护键改用 dev+inode 复合键消除跨文件系统误拦
仅用 inode 号做 protected map 的键会在不同文件系统间冲突:tmpfs
(/tmp、/dev/shm、/run)的 inode 号小而密集,极易与 /www 下受保护
文件的 inode 相同,导致这些 tmpfs 文件的合法写/删/改名被内核 EPERM
误拒。改为读取 inode->i_sb->s_dev 与 i_ino 组成 (dev, ino) 复合键,
用户态将 stat 的 glibc dev 编码转换为内核 s_dev 格式(MKDEV)以对齐。

顺带优化:ringbuf 事件解析改为按偏移零分配读取;移除引擎中未使用的
Clear/Count/Events 死代码。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 05:07:57 +08:00
h2zi 2b28b16338 chore: update mocks 2026-07-19 20:21:37 +00:00
耗子 a0b5a356a8 feat(tamper): 面板内置网站文件防篡改
双模式运行时可选:
- chattr 模式:inode 属性锁定(+i/+a),内核 VFS 层事前强制,全发行版通用
- eBPF-LSM 模式:纯 Go asm 手写 LSM 程序 + 运行时 BTF 查偏移(零编译),
  拦截写/删/改名并溯源进程,bpf LSM 未激活时引导改 grub 重启激活

含独立审计库、拦截日志、保护规则、fsnotify 新建监控、面板写入自动解锁恢复,
以及备份修复适配。参考 pkg/firewall/scan 的 eBPF 运行时加载范式

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 04:20:37 +08:00
耗子 c9d5fb264f feat(website): 默认设置支持切换默认站点
在内置默认配置与网站配置之间迁移 default_server 标志,
支持将未匹配域名的请求指向任意网站或恢复面板内置默认页

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 03:16:34 +08:00
耗子 90b37f5e6a feat(ssh): 终端支持主机间 SFTP 文件互传
终端窗口新增文件传输标签页,双栏浏览任意主机(含面板本机)目录,
选中文件后通过面板中转互传,传输队列实时展示进度速度并支持取消

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 03:05:51 +08:00
耗子 f72e7fa31e feat(database): MySQL 管理页支持 phpMyAdmin 一键登录跳转
支持选择面板中添加的任意 MySQL 服务器,以对应凭据代理登录并转发会话 Cookie

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 02:54:44 +08:00
耗子 1ca986e779 feat(container): 支持查看容器详情与编辑容器
详情弹窗双 Tab:格式化展示挂载/端口/网络/环境变量等基本信息,原始 Tab 直接展示 inspect 输出
编辑复用创建表单,由 inspect 回填配置,确认后删除旧容器按新配置重建

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 02:15:53 +08:00
耗子 a883cac315 feat(toolbox): 数据迁移支持忽略环境一致性验证
环境检查未通过时可勾选忽略,弹警告二次确认后放行下一步

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 02:15:43 +08:00
耗子 f904a0e53b feat(firewall): 端口规则支持 xlsx 导出与导入
导出按固定表头生成 xlsx,导入按表头定位列并逐条下发,返回成功失败数

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 02:15:33 +08:00
耗子 e6325c3348 chore(deps): 升级 vite 至 8.1.5 修复构建产物白屏
rolldown 1.0.2 起 tree-shaker 将 runtime-dom 初始化 wrapper 内联删除后
未同步清理跨 chunk 调用点,导致懒加载 chunk 运行时 ReferenceError 白屏,
rolldown 1.1.2(vite 8.1.0+)已根治,同步移除 Renovate 对 vite 的忽略

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 00:00:45 +08:00
耗子 d02f556e1c fix: 应用列表无运行状态时显示 - 而非未知标签
s3fs 等无 systemd 服务概念的应用不再渲染误导性的未知标签

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 23:56:45 +08:00
耗子 2e1d403e8b fix: phpMyAdmin 运行状态跟随 nginx 服务
phpMyAdmin 由 nginx 站点承载,状态固定返回 n/a 会在应用列表显示为未知,令用户困惑

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 23:56:45 +08:00
renovate[bot] 7d2aa8401d chore(deps): Update major dependencies (#1714)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-18 23:46:16 +08:00
耗子 c2d194b489 chore(deps): Renovate 忽略 TypeScript major 升级
vue-tsc 尚未支持 TypeScript 7 (tsgo),vuejs/language-tools#5381
仍被上游阻塞,待支持后移除该规则

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 18:07:10 +08:00
耗子 b5bdfdb110 fix(file): 补全单文件格式压缩入口并适配压缩测试
.bz2/.xz/.zst 后缀已被识别为单文件压缩格式,但 Compress 未提供
对应入口导致报 unsupported format,与 .gz 一并收敛为统一分支,
解压侧同步收敛,测试改为表驱动覆盖归档与单文件两类格式

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 18:07:05 +08:00
耗子 1450c00ec6 fix(file): 支持 .gz 单文件压缩并补全 zst 解压入口
压缩弹窗此前提供 .gz 选项但后端无对应实现,现后端按单文件 gzip 压缩,
前端多选文件时禁用 .gz 并自动回退 .zip;另补上前端 zst 后缀的解压入口

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 00:18:31 +08:00
耗子 c2505688a6 fix(file): 兼容单独压缩的 .xz/.bz2/.zst 文件解压
此前 .xz/.bz2/.zst 后缀一律按 tar 归档解压,导致非 tar 打包的单独压缩文件
(如 gitea-xxx.xz、xxx.sql.bz2)报 This does not look like a tar archive,
现参照 .gz 的处理方式区分 .tar.* 与单独压缩格式

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 00:13:07 +08:00
耗子 349dd36cc7 style(file): 消除 oxlint no-useless-spread 告警
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 20:16:09 +08:00
耗子 a9e16db711 fix(file): 修复文件管理与编辑器的状态同步问题并收敛操作入口
- 编辑器保存按状态栏行分隔符规范化,修复粘贴 CRLF 内容后 LF 文件被存成 CRLF
- 删除/翻页/排序后清理失效的选中项与目录大小缓存,修复表头复选框残留部分勾选
- 删除/重命名/移动文件后同步编辑器已打开的标签页(closePath/movePath)
- 编辑器提升至 IndexView 承载,修复切换标签页销毁未保存内容;最小化时禁用全局快捷键
- 权限弹窗打开时自取文件信息,修复顶栏入口打开时初值残留
- 批量删除/改权限改用 allSettled,部分失败时也刷新列表
- 文件操作收敛至 useFileOps/useEditorOps 统一入口,selected 归位 fileStore 按标签页隔离
- 全局事件总线事件表类型化

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 20:07:10 +08:00
renovate[bot] bd680a4082 chore(deps): Lock file maintenance (#1712)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-13 11:15:45 +00:00
耗子 8f226e988f fix: 修复端口清理、服务重载与本地 MySQL 连接问题
- 关闭 HTTPS 时清理 IPv6 等 SSL 监听,避免残留 ssl/quic 参数致 nginx 无法启动
- Web 服务未运行时跳过 reload,避免停止状态保存网站配置误报重载失败
- reload 失败补全 nginx -t / apachectl configtest 输出(原走 stderr 被丢弃)
- 本地 MySQL 优先走 unix socket 连接,规避 skip-name-resolve 下 root@localhost 无法反解匹配

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 05:17:46 +08:00
renovate[bot] a6df473e02 chore(deps): Update module github.com/libtnb/chix to v2 (#1710)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-11 04:04:49 +08:00
耗子 5efb32a661 refactor(biz): 用例层承载业务编排,data 层退为数据/外部原语
将压在 data 层胖 repo 的业务逻辑上移到 biz 用例层,让三层名副其实:
- 34 个 XxxUsecase 从纯透传空壳变为承载领域规则、跨 repo 编排、操作审计的
  真正业务层;data 层 repo 退为数据访问与外部系统原语
- 24 条原压在 repo 层的跨 repo 编排上移为 usecase→usecase/repo 依赖,仅保留
  3 处嵌在外部原语内无法剥离的 repo→repo(backup→setting/website、website→setting)
- 新增 internal/biz/helper.go:operatorID / IsNotFound / containerSock
- 表现层(service/job/command/apps/middleware)零改动,用例对外签名冻结

经全部 34 域逐语句对抗审查确认行为等价,7 处漂移已修复或知情接受。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 14:16:10 +08:00
renovate[bot] 7fb6953773 chore(deps): Update pnpm to v11.11.0 (#1708)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-10 01:50:33 +00:00
renovate[bot] 7a1566b642 chore(deps): Update module github.com/libtnb/validator/contrib/openapi to v0.2.0 (#1707)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-09 19:28:42 +00:00
耗子 a8a09de21f Merge branch 'refactor/samber-do-architecture'
# Conflicts:
#	go.mod
#	go.sum
2026-07-09 22:35:27 +08:00
耗子 cbed8e4797 refactor(job): 定时任务改为声明式贡献,测试脱离环境变量
- 每个任务合并为单个 NewXxx(i do.Injector) (Job, error) 构造函数,
  直接返回声明式 Job{Spec, Task},删除 JobFn 闭包与 Register 包装层
- 调度器改为 registry.Collect[job.Job] 统一装配,与路由贡献模型对称
- InitGlobal 导出并读取 conf.App.Root(默认 /opt/ace),
  返回 error 不再 Fatalf,修复原先无视配置 root 字段的硬编码
- 容器装配测试改用内存配置 + do.OverrideValue 注入,
  不再依赖磁盘配置与 PANEL_CONFIG 环境变量(原测试实际会失败)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 22:31:56 +08:00
renovate[bot] 4f1cfc4dee chore(deps): Update non-major dependencies (#1706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-09 13:55:07 +00:00
耗子 443516a2cf refactor!: 迁移至 samber/do 依赖注入与三层架构
依赖注入:
- 移除 google/wire(含 wire.go/wire_gen.go 与全部 ProviderSet),
  改用 samber/do v2 单注入器 + 双入口惰性构建
- 贡献模型替代命令式注册:路由 routes:、命令 commands:、
  任务 jobs: 前缀经 internal/registry 收集与校验
- 构造函数统一 func NewXxx(i do.Injector) (T, error)

三层架构:
- 补全用例层:每个 biz.XxxRepo 配 XxxUsecase,
  service/command/job 表现层只依赖用例,不再直接引用仓储

路由与文档:
- route/http.go 按域拆为声明式 Endpoint 贡献,
  Endpoint 承载登录白名单与端点限流语义
- 调试模式下提供 OpenAPI 3.1 文档:/openapi.json 与 /docs(Scalar),
  从 validate 标签生成

目录与依赖:
- internal/http/{middleware,request,rule} 拍平至 internal/*
- CLI 命令拆至 internal/command
- 日志轮转 timberjack 换为 libtnb/logrotate
- 升级 validator/cron/sessions/gormstore/sqlite/securecookie
- 数据库迁移保持 gormigrate 不变

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 21:54:22 +08:00
renovate[bot] 2483fabb0c chore(deps): Update non-major dependencies (#1704)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-09 00:47:07 +00:00
renovate[bot] 3b02563d71 chore(deps): Update non-major dependencies (#1702)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-08 12:32:48 +00:00
耗子 6f0593fbf8 feat: 升级 libtnb/validator v0.2.1,用上新校验能力
- 切片字段加 unique 防重复配置(域名/监听/IP/白名单/网卡/磁盘设备/
  NTP 服务器/应用标识/压缩路径/TLS 版本/索引/定时任务目标)
- 网站统计日期范围 End 加 after_or_equal:Start(结束不早于开始)
- 新增 tags_test.go:CheckRules 体检关键请求 + 全包 validate tag
  编译校验,坏规则名/语法错/未注册自定义规则在测试期即报错

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 23:11:28 +08:00
renovate[bot] c5fb80a4fb chore(deps): Update module github.com/go-chi/chi/v5 to v5.3.1 (#1701)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-06 20:10:51 +00:00
renovate[bot] e1cb687bbc chore(deps): Lock file maintenance (#1700)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-06 05:09:03 +00:00
renovate[bot] 555ba71067 chore(deps): Update pnpm to v11.10.0 (#1699)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-05 01:37:23 +00:00
renovate[bot] 27b0cd0168 chore(deps): Update dependency tsx to v4.23.0 (#1698)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-03 21:11:42 +00:00
耗子 260690a25e fix(web): 修复快速切换 Tab 时文件编辑器内容错乱
编辑器改为每个 tab 一个独立 Monaco model,切换 tab 用 setModel 代
替 setValue;每个 model 的 onDidChangeContent 用闭包 boundPath 回
写,杜绝异步事件与 activeTabPath 错位时把 A 的内容写进 B 的问题。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-04 00:27:19 +08:00
耗子 3bdd3711b0 fix: SQLite使用NORMAL模式启动 2026-07-03 16:29:31 +08:00
耗子 5c77144f07 fix: 修复验证器规则报错 2026-07-03 08:59:04 +08:00
github-actions[bot] 94fe6753da New Crowdin translations by GitHub Action (#1697)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-03 04:48:47 +08:00
h2zi fe67fccde3 chore(l10n): update pot files 2026-07-02 20:29:58 +00:00
耗子 6b733fc2af fix(web): 修正 HealthBanner 中扫描感知的术语措辞
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-03 04:27:34 +08:00
h2zi 44f243e94f chore(l10n): update pot files 2026-07-02 20:22:59 +00:00
耗子 756834a59a chore(web): 同步 pnpm-lock 至 vite 8.1.3
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-03 04:21:14 +08:00
耗子 f024381660 feat(panel): 数据库故障降级、健康告警与检测
- 网站统计聚合器 flush 失败也 commit(丢弃已 drain 增量),防止 DB
  长期不可写时内存无界累积导致 OOM/CPU 100%
- 势态感知同样在 upsert 失败时上报健康问题
- 新增 internal/app.Health 全局健康状态注册表,通过 /home/health 暴露
- 前端 layout 顶部新增 HealthBanner,DB 故障时红色横幅提醒并给出修复引导
- acepanel fix 检测数据库损坏改用 PRAGMA quick_check,能识别 malformed
  等仅在查询时才暴露的静默损坏
- 主库与辅助库 DSN 一律设为 synchronous(FULL),减少宿主 write-back
  缓存丢失 fsync 导致的损坏概率
- 移除 debug.SetMemoryLimit(256MB):软上限在活对象超限时会驱动 GC 反复
  追赶不可回收对象,反而放大 CPU 打满

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-03 04:21:10 +08:00
耗子 3098d9fb64 fix(web): 升级 vite 至 8.1.3 修复生产构建白屏
vite 8.0.14+ 因 rolldown 1.0.2/1.0.3 tree-shaking 决策错误
(rolldown#9515),懒加载 chunk 调用未定义的 init_runtime_dom_esm_bundler
导致白屏。rolldown 1.1.2 (vite 8.1.0+) 修复。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-03 03:50:53 +08:00
github-actions[bot] fa71b6f4ef New Crowdin translations by GitHub Action (#1696)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-07-03 02:30:50 +08:00
h2zi 39b5f10012 chore(l10n): update pot files 2026-07-02 18:18:00 +00:00
h2zi 79fa002bd6 chore: update mocks 2026-07-02 18:15:21 +00:00
耗子 00a5acbc45 feat(panel): 重写自升级流程并精简面板备份 (close #1674)
升级改为覆盖式:只替换程序文件,storage 原地不动,中断安全无需 fix;新增 WS 实时进度,前端等待重启自动刷新。面板备份改为仅打包核心文件(ace/panel.db/config.yml/cert/cli),排除统计/扫描大库,体积大幅下降。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 02:12:52 +08:00
耗子 65e2750102 fix(website): 修复 IP 作为域名的处理(尤其 IPv6)
- 抽取 IsIPv6/WrapIPv6/UnwrapIPv6 到 pkg/tools,复用于 acme panelSolver
- server_name 写入时为 IPv6 套方括号、读取时剥离,Domains 统一裸地址口径,证书 SAN 判定自然正确
- 前端打开网站链接时为 IPv6 套方括号,避免拼出非法 URL

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 01:21:04 +08:00
耗子 93e013ec56 feat(file): 文件压缩/解压改为后台任务 (close #1675)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-03 00:51:27 +08:00
renovate[bot] 07e608a3c0 chore(deps): Update dependency tsx to v4.22.5 (#1695)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-03 00:32:26 +08:00
renovate[bot] a255e9e1d8 chore(deps): Update actions/checkout action to v7 (#1678)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-03 00:32:19 +08:00
耗子 f5ad988576 feat: 替换验证器实现 2026-07-03 00:22:23 +08:00
耗子 2e18c25054 feat(website): 纯静态网站默认写入 SPA 路由回退配置
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 22:58:31 +08:00
耗子 a98142ea93 fix(website): reload 失败时返回 web 服务配置测试详情
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 22:22:17 +08:00
耗子 4d8f6fad9f fix: X-Debug 调试日志仅在调试模式下生效
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 22:22:17 +08:00
renovate[bot] 67a7a41469 chore(deps): Update module github.com/bddjr/hlfhr to v1.6.1 (#1694)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-02 19:54:47 +08:00
renovate[bot] afae33c69a chore(deps): Update module github.com/shirou/gopsutil/v4 to v4.26.6 (#1693)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-02 00:56:45 +00:00
renovate[bot] 62c5271c37 chore(deps): Update dependency vue-tsc to v3.3.6 (#1692)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-30 23:05:34 +00:00
renovate[bot] ac116a7680 chore(deps): Update non-major dependencies (#1691)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-30 09:46:34 +00:00
renovate[bot] c1092576b2 chore(deps): Lock file maintenance (#1690)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-29 18:52:14 +08:00
renovate[bot] fea51108d2 chore(deps): Update module github.com/urfave/cli/v3 to v3.10.1 (#1689)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-28 21:49:54 +08:00
renovate[bot] 69dae9be95 chore(deps): Update dependency vite-plugin-vue-devtools to v8.1.5 (#1688)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-28 15:55:22 +08:00
耗子 e7b2323c70 Merge remote-tracking branch 'origin/main' 2026-06-28 15:53:15 +08:00
耗子 371f80451b fix: 移除部分默认配置 2026-06-28 15:53:08 +08:00
耗子 6e3b35b3db fix(acme): 修复 IP/通配符证书签发失败
- nginx server_name 中的 IPv6 字面量补上方括号,避免匹配不上 LE 带方括号的 Host 头被 default_server 接管
- Apache 改用 IP-based VirtualHost(ServerName 不支持 IPv6 字面量,会让 httpd -t 失败)
- DNS-01 改用 AppendRecords 单条追加(与 Caddy/certmagic 对齐),避免 SetRecords
  在 Cloudflare 等 provider 下按 (name,type) 覆盖同名 TXT,导致主域+通配符同时签发时
  主域 challenge 被通配符 challenge 覆盖

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-28 15:48:57 +08:00
renovate[bot] 2798d7fb5d chore(deps): Update dependency eslint to v10.6.0 (#1685)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-27 01:57:16 +00:00
renovate[bot] f3696332af chore(deps): Update non-major dependencies (#1684)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-26 17:02:38 +00:00
renovate[bot] a510b81cf5 chore(deps): Update non-major dependencies (#1683)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-25 17:51:27 +00:00
renovate[bot] 4b675c587f chore(deps): Update non-major dependencies (#1682)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-23 21:50:22 +00:00
renovate[bot] 3efc9e2f66 chore(deps): Update non-major dependencies (#1681)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-23 01:52:45 +00:00
耗子 3715e6db5e fix(web): 修复网站编辑大小/时间单位反推导致的下拉失效与跳变
将单位作为独立 UI 状态保存到对象上(_size_unit / _units),
不再从字节/纳秒反推单位,避免:
- 空值切换 GB 等单位时因 buildSize(0,...)=0 导致单位丢失
- 输入 1024 MB 因恰好是 GB 整数倍而被自动跳成 1 GB
- 输入 60 秒被自动跳成 1 分钟等同类问题

覆盖 EditModal.vue 中 client_max_body_size 与 5 个 duration 字段,
以及 NginxIndex.vue 中 stream upstream resolver_timeout。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-23 02:41:01 +08:00
耗子 29247e6944 refactor(pkg/dns): 精简 netplan 结构体与去除接口遍历重复
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-22 17:37:44 +08:00
renovate[bot] 6303ac943a chore(deps): Lock file maintenance (#1680)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-22 05:05:43 +00:00
renovate[bot] 8a89a40bd3 chore(deps): Update non-major dependencies (#1679)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-22 01:59:38 +08:00
renovate[bot] 3589bec397 chore(deps): Update non-major dependencies (#1677)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-20 03:11:43 +00:00
耗子 c2a8200817 fix: 修复 CI lint 与前端类型检查报错
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-20 02:14:22 +08:00
renovate[bot] 4a22299111 chore(deps): Update non-major dependencies (#1676)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-20 02:02:50 +08:00
耗子 54354c369a fix: fail2ban SSH 多 Port 时 jail 配置换行
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-18 20:17:22 +08:00
耗子 0181dadc05 fix: 文件上传前 precheck 冲突 支持跳过/重命名/覆盖
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-18 00:57:15 +08:00
耗子 30b593f90a Merge remote-tracking branch 'origin/main' 2026-06-17 18:24:29 +08:00
耗子 c22d6a430d fix: Chmod/Chown 递归操作前解锁 immutable 文件 2026-06-17 18:24:15 +08:00
renovate[bot] 08b09d58e8 chore(deps): Update non-major dependencies to v4.0.1 (#1671)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-16 16:00:07 +08:00
dependabot[bot] 3d3188630e chore(deps-dev): bump vite from 8.0.13 to 8.0.16 in /web (#1670)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.13 to 8.0.16.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.16/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.16
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-16 15:59:32 +08:00
renovate[bot] f5c23b8a2a chore(deps): Update non-major dependencies (#1668)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-16 03:50:02 +00:00
Copilot 78718db0a5 feat: info 命令仅首次运行重置密码,后续显示星号,支持 -f 强制重置 (#1669)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-16 01:32:53 +08:00
renovate[bot] 072cbb0132 chore(deps): Update non-major dependencies to v11.7.0 (#1667)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-15 22:41:20 +08:00
renovate[bot] c3732477be chore(deps): Update non-major dependencies (#1666)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-15 15:13:30 +08:00
renovate[bot] 03d617fea1 chore(deps): Lock file maintenance (#1665)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-15 05:50:15 +00:00
renovate[bot] 39f0d2564c chore(deps): Update non-major dependencies to v3.10.0 (#1664)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-14 16:59:38 +00:00
renovate[bot] c66cdfbc1f chore(deps): Update non-major dependencies to v8.1.3 (#1663)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-14 17:30:39 +08:00
renovate[bot] 97332f9650 chore(deps): Update non-major dependencies to v3.3.5 (#1662)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-13 13:56:45 +00:00
renovate[bot] be7aea4a68 chore(deps): Update non-major dependencies (#1661)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-12 22:42:14 +00:00
renovate[bot] a71b4009fb chore(deps): Update major dependencies to v7 (#1658)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-12 16:42:32 +08:00
耗子 ef58fcd6eb fix: 证书签发使用 CA 默认证书链
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 13:50:33 +08:00
renovate[bot] e8d19a8171 chore(deps): Update non-major dependencies (#1660)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-12 09:08:09 +08:00
renovate[bot] d2ba8190ca chore(deps): Update non-major dependencies (#1659)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-10 22:00:32 +00:00
renovate[bot] 31bd29ae7a chore(deps): Update non-major dependencies to v4.0.0-rc.5 (#1657)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-09 17:41:23 +08:00
renovate[bot] 003feef87c chore(deps): Update non-major dependencies (#1656)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-08 22:29:43 +00:00
耗子 0328215564 fix: 编辑网站问题优化 2026-06-08 23:06:06 +08:00
renovate[bot] 3e0c1dd574 chore(deps): Update non-major dependencies (#1653) 2026-06-08 17:24:42 +08:00
renovate[bot] c06f7a3629 chore(deps): Lock file maintenance (#1652)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-08 04:59:06 +00:00
github-actions[bot] 14790a1551 New Crowdin translations by GitHub Action (#1650)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-06-08 02:30:11 +08:00
h2zi 21593ca29d chore(l10n): update pot files 2026-06-07 18:20:25 +00:00
耗子 1a8bd4e400 chore: go mod tidy 2026-06-08 02:18:54 +08:00
耗子 865f12c75b feat: 支持计划任务备份clickhouse和redis 2026-06-08 02:17:09 +08:00
耗子 8c28ac19ec feat: 定时备份任务支持 ClickHouse/Redis/Valkey
cron 备份脚本生成在 mysql/postgresql 之外扩展 clickhouse/redis/valkey;
前端计划任务表单新增对应备份类型(按安装状态显示),Redis/Valkey 整实例
无库名,选中后自动以实例名作为备份目标。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 01:45:09 +08:00
github-actions[bot] ee15bfa67c New Crowdin translations by GitHub Action (#1649)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-06-08 01:33:12 +08:00
h2zi c04fdfc7aa chore(l10n): update pot files 2026-06-07 17:21:48 +00:00
耗子 ded7909fdb refactor: 重写 apache/nginx 配置解析器为 AST 驱动
apache 包废弃脆弱的字符级 lexer,重写为三层单遍解析器(逻辑行合并续行 +
shell 分词 + 递归下降)+ 统一有序 AST(VirtualHost 并入 Block、参数分离
引号信息)。修复嵌套块被静默丢弃、不支持续行符、特殊字符粘连、order 表
VirtualHost 排序等缺陷;proxy/redirect/vhost 读写全部改为 AST 驱动,删除
所有正则反解析与脆弱的 Sscanf/strings.Index。

nginx 包片段读端(proxy/redirect/upstream)从正则反解析改为 gonginx AST 遍历。

types.Vhost 接口清理:用 ConfigScope 类型常量替代 stringly-typed 的
"site"/"shared",用 SetRawConfig 独立方法替代 skipComment 可变布尔参数。

新增 robustness_test.go:用 apache 官方及社区真实配置验证语义无损、幂等、边界容错。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 00:00:48 +08:00
耗子 b5083dd8fa feat: 优化s3 sdk 2026-06-07 21:47:15 +08:00
耗子 c7e078b7f8 Merge remote-tracking branch 'origin/main' 2026-06-07 21:21:33 +08:00
耗子 3a937db506 fix: ipv6证书问题 2026-06-07 21:21:28 +08:00
renovate[bot] f92c661f33 chore(deps): Update non-major dependencies to v1.52.0 (#1648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-06 21:02:41 +00:00
renovate[bot] 8c29bba051 chore(deps): Update non-major dependencies to v24.13.1 (#1647)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-06 04:43:21 +00:00
耗子 40e6f8e08f refactor: 重写s3 sdk 2026-06-05 23:33:35 +08:00
耗子 7d0a6ced8c Merge remote-tracking branch 'origin/main' 2026-06-05 19:54:50 +08:00
耗子 104cbc00f1 chore: run go mod tidy 2026-06-05 19:54:45 +08:00
h2zi 75c4f44262 chore: update mocks 2026-06-05 11:43:55 +00:00
耗子 ef44ab8f0d Merge remote-tracking branch 'origin/main' 2026-06-05 19:42:59 +08:00
耗子 568ce6b270 feat: 新增容器无极日志 2026-06-05 19:42:53 +08:00
renovate[bot] 20df3c5f81 chore(deps): Update non-major dependencies to v11.5.2 (#1646)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-05 18:55:46 +08:00
耗子 8e3999ccbe feat: 新增redis/valkey备份 2026-06-05 15:51:12 +08:00
耗子 b4efbe823f Merge remote-tracking branch 'origin/main' 2026-06-05 14:26:15 +08:00
耗子 ca218b31ec feat: 新增clickhouse复制 2026-06-05 14:26:09 +08:00
renovate[bot] e1823854bd chore(deps): Update non-major dependencies (#1645)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-04 17:56:05 +00:00
renovate[bot] 28bd1809cc chore(deps): Update non-major dependencies (#1644)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-04 00:43:01 +00:00
耗子 120baf3bd7 fix: 修复清理日志成功提示显示异常
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 04:22:59 +08:00
耗子 cbabd963df fix: 获取备份路径时自动创建目录
修复上传 MySQL 等备份文件时,因备份目录(如 /backup/mysql/)不存在导致
报错"文件不存在"的问题。将创建目录逻辑收敛到 GetDefaultPath,所有调用方均受益。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 04:05:51 +08:00
renovate[bot] 6a4f91e6cc chore(deps): Update pnpm to v11.5.1 (#1643)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-02 20:15:01 +08:00
renovate[bot] 8792d9b5cf chore(deps): Update non-major dependencies (#1642)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-02 03:40:34 +00:00
耗子 e667923c51 Merge remote-tracking branch 'origin/main' 2026-06-01 18:25:58 +08:00
耗子 c2d01c4371 fix: 修复问题 2026-06-01 18:25:52 +08:00
renovate[bot] 1b713c8f32 chore(deps): Update module github.com/shirou/gopsutil/v4 to v4.26.5 (#1640)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-01 16:25:06 +08:00
renovate[bot] 561db6a861 chore(deps): Update non-major dependencies (#1639)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-31 22:25:26 +00:00
耗子 9f3f0c8876 fix: 修复日志残留僵尸进程的问题 2026-05-31 22:43:25 +08:00
耗子 a06af8cd12 fix: 修复日志残留僵尸进程的问题 2026-05-31 22:31:26 +08:00
renovate[bot] a2c2797942 chore(deps): Update non-major dependencies (#1638)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-30 01:09:25 +00:00
renovate[bot] c39f3e29b8 chore(deps): Update non-major dependencies (#1637)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-30 00:34:07 +08:00
renovate[bot] 19f3d80717 chore(deps): Update non-major dependencies (#1635)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-27 18:47:53 +00:00
h2zi b5bdcaded6 chore: update mocks 2026-05-27 15:56:25 +00:00
fish 419cf50a37 Fix/postgresql port sync (#1636)
* refactor(postgresql): 优化配置处理并改进端口管理

- 引入 `configPath()` 方法,集中获取配置文件路径。
- 新增 `getPort()` 方法,用于从配置中动态获取 PostgreSQL 端口。
- 更新方法以使用新的配置处理机制,确保对 PostgreSQL 配置文件的访问一致性。
- 增强了配置更新期间端口变更时的错误处理和服务重启逻辑。

* fix(home): 修复数据库统计接口使用固定端口连接数据库的问题

- 更新了 HomeService,使其包含一个新的 databaseServerRepo,用于管理数据库服务器配置。
- 修改了 NewHomeService 构造函数,使其接受新的 databaseServerRepo 参数。
- 重构了数据库连接逻辑,使其利用数据库服务器存储库进行 PostgreSQL 连接。

* fix: optimize code

---------

Co-authored-by: 耗子 <haozi@loli.email>
2026-05-27 23:55:25 +08:00
耗子 b8311af661 Merge remote-tracking branch 'origin/main' 2026-05-27 21:33:45 +08:00
耗子 c8805779ed fix: optimize code 2026-05-27 21:33:39 +08:00
renovate[bot] 5a156a84cb chore(deps): Update module github.com/DeRuina/timberjack to v1.4.3 (#1634)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-27 09:14:00 +00:00
renovate[bot] 10415d3ac9 chore(deps): Update non-major dependencies (#1633)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-27 02:25:56 +00:00
耗子 16025c080b fix: 过滤非备份文件 2026-05-26 03:07:20 +08:00
耗子 f7fcd0ca8a fix: build 2026-05-26 02:39:49 +08:00
耗子 220d86348c fix: lint 2026-05-26 02:37:58 +08:00
耗子 215a8cf4d6 fix: build 2026-05-26 02:34:02 +08:00
耗子 3df31d8e7f fix: build 2026-05-26 02:24:19 +08:00
renovate[bot] 430974254e chore(deps): Lock file maintenance (#1631)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-26 02:12:52 +08:00
耗子 5884d1aeed Merge remote-tracking branch 'origin/main' 2026-05-26 02:10:06 +08:00
耗子 61769323a6 fix: web 2026-05-26 02:10:00 +08:00
renovate[bot] 3e5779bd1c chore(deps): Update non-major dependencies (#1632)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-26 02:08:43 +08:00
renovate[bot] d70688503b chore(deps): Update non-major dependencies (#1629)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-05-26 02:02:36 +08:00
耗子 85cd8ef2bb fix: lint 2026-05-26 02:01:31 +08:00
renovate[bot] 5fd4bf2270 chore(deps): Update major dependencies to v9 (#1630)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-26 01:58:24 +08:00
耗子 0a162cbf57 Merge remote-tracking branch 'origin/main' 2026-05-26 01:55:21 +08:00
耗子 a19bf8f4bf fix: web 2026-05-26 01:55:15 +08:00
github-actions[bot] 03127e7f2a New Crowdin translations by GitHub Action (#1628)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-05-26 01:24:37 +08:00
h2zi 1a58daa007 chore(l10n): update pot files 2026-05-25 17:23:04 +00:00
耗子 bac3dcc86d fix: openresty服务名写错了 2026-05-26 01:15:27 +08:00
耗子 4739f6ea78 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	go.mod
2026-05-26 01:10:40 +08:00
耗子 5077e087d5 chore: 更新依赖 2026-05-26 01:09:43 +08:00
耗子 ef9789a6b2 feat: 安全加固 2026-05-26 00:21:08 +08:00
renovate[bot] 24af0f21b1 chore(deps): Update module github.com/go-chi/chi/v5 to v5.3.0 (#1627)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-25 23:52:15 +08:00
renovate[bot] f115e6f93c chore(deps): Update module github.com/go-webauthn/webauthn to v0.17.4 (#1626)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-25 23:39:53 +08:00
renovate[bot] 4b8779bfb4 chore(deps): Lock file maintenance (#1625)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-25 12:39:52 +00:00
renovate[bot] 4407d4aec2 chore(deps): Update non-major dependencies (#1622)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-25 09:36:07 +00:00
耗子 5ef6074870 fix: 网站/数据库/项目删除及应用卸载恢复 5 秒倒计时
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 02:52:09 +08:00
耗子 2427e90591 fix: 列表操作按钮使用 NFlex 统一间距避免重叠
Closes #1610

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 02:44:18 +08:00
耗子 292f22a1c2 feat: 压缩解压及面板备份支持 tar.zst 格式
Closes #1619

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 02:35:10 +08:00
renovate[bot] 7439cf489e chore(deps): Update non-major dependencies (#1621)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-21 20:49:47 +00:00
renovate[bot] 471570db77 chore(deps): Update dependency tsx to v4.22.1 (#1620)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-20 11:15:50 +00:00
renovate[bot] bd24e6de20 chore(deps): Update dependency oxfmt to ^0.50.0 (#1618)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-19 00:57:45 +00:00
renovate[bot] c81c13be1a chore(deps): Lock file maintenance (#1617)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-18 04:34:08 +00:00
renovate[bot] 0bb3aea1df chore(deps): Update non-major dependencies (#1616)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-17 20:47:36 +00:00
renovate[bot] 8f144e3358 chore(deps): Update non-major dependencies (#1615)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-16 20:35:10 +00:00
renovate[bot] fc74228e7e chore(deps): Update pnpm to v11.1.1 (#1614)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-15 17:01:17 +00:00
renovate[bot] d1eec7f7d6 chore(deps): Update non-major dependencies (#1613)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-15 05:38:03 +00:00
renovate[bot] 05e6a10a4e chore(deps): Update dependency vite to v8.0.12 (#1612)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-14 21:07:42 +08:00
耗子 db7e342a23 feat: 禁止删除 acepanel-network 内置网络
acepanel-network 由面板自身使用,误删会导致容器编排异常。后端
Remove 拦截该网络的删除请求,前端批量删除时一并将其排除。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 19:39:01 +08:00
耗子 5495a17748 fix: 修复裸域名与泛域名共用 ACME TXT 记录时相互覆盖
abc.com 与 *.abc.com 共用 _acme-challenge.abc.com,需同时存在两条
TXT 记录,但 SetRecords 以 (name, type) 为单位覆盖,导致后写入的
challenge 覆盖前者,DNS 验证一直卡死。改为按记录名累积所有 keyAuth
一次性写入。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 19:39:01 +08:00
renovate[bot] d152f4c59f chore(deps): Update module modernc.org/sqlite to v1.50.1 (#1611)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-14 01:01:33 +00:00
renovate[bot] 823635c4d8 chore(deps): Update module github.com/urfave/cli/v3 to v3.9.0 (#1609)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-13 01:47:51 +00:00
renovate[bot] aa6c1fb47e chore(deps): Update module github.com/go-webauthn/webauthn to v0.17.3 (#1608)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-12 18:51:32 +00:00
renovate[bot] bcff04bc36 chore(deps): Update non-major dependencies (#1606)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-12 04:35:33 +00:00
renovate[bot] 8aaf4e5c78 chore(deps): Update module github.com/libtnb/cron to v0.2.1 (#1605)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-11 21:08:51 +08:00
renovate[bot] c47ba0eac9 chore(deps): Update dependency vite-plugin-vue-devtools to v8.1.2 (#1603)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-11 09:55:43 +00:00
renovate[bot] 9db21de85b chore(deps): Lock file maintenance (#1604)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-11 05:33:46 +00:00
renovate[bot] a0a98d7c4e chore(deps): Update pnpm to v11.0.8 (#1602)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-10 08:54:33 +00:00
renovate[bot] ae6a43a780 chore(deps): Update pnpm to v11.0.7 (#1601)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-10 15:18:36 +08:00
renovate[bot] 6e87b0f1b3 chore(deps): Update non-major dependencies (#1600)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 17:32:24 +00:00
耗子 e76b2ee7a9 fix: 文件管理右键删除增加二次确认
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 15:43:29 +08:00
耗子 37a883d0bc fix: 移除删除确认默认 5 秒倒计时
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 15:40:10 +08:00
renovate[bot] ac48313948 chore(deps): Update non-major dependencies (#1599)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-09 01:46:56 +00:00
耗子 37c6fadecf fix: 备份格式默认值错误 2026-05-08 21:26:23 +08:00
github-actions[bot] 122c538811 New Crowdin translations by GitHub Action (#1598)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-05-08 20:16:30 +08:00
h2zi 4d1bb40d26 chore(l10n): update pot files 2026-05-08 11:50:58 +00:00
耗子 f3e61dfc05 fix: lint 2026-05-08 19:49:36 +08:00
耗子 58186508bb feat: 前端现代化改造3 2026-05-08 19:26:32 +08:00
耗子 c41dab89f8 refactor: 替换 robfig/cron/v3 为 libtnb/cron
robfig/cron 已停止维护,迁移到 libtnb/cron(零三方依赖,API 设计更现代)

主要变更:
- pkg/cron/logger.go 删除,libtnb/cron 直接吃 *slog.Logger
- bootstrap.NewCron 用 wrap.Recover() / wrap.SkipIfRunning() 替代旧 cron.Recover / SkipIfStillRunning
- app.Ace.Run 处理 c.Start() error 返回值;c.Stop(ctx) 接受 context
- 6 个 job (Monitoring/FirewallScan/CertRenew/PanelTask/WebsiteStat/WebsiteExpire) 的 Run 签名从 () 改为 (context.Context) error
- jobs.Register 用 c.Add 替代 c.AddJob
- 重新生成 wire 代码

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:55:39 +08:00
耗子 2e049da24e fix: 登录页面去掉卡片 2026-05-08 18:47:37 +08:00
耗子 f6f22ecaf6 fix: zip 解压改用 7z 处理中文文件名
unzip 默认按 UTF-8 解析文件名,Windows GBK/CP936 编码的 zip 包会被转义成 #Uxxxx 形式
改用 7z x 替代,自动检测文件名编码

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 17:37:02 +08:00
耗子 2328456a81 fix: 修复 errcheck lint 报错
defer f.Close() 改为显式忽略返回值,跟其他地方保持一致

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 15:10:06 +08:00
耗子 f0bee01fd7 Merge remote-tracking branch 'origin/renovate/non-major-dependencies' 2026-05-08 15:07:05 +08:00
github-actions[bot] 4d6ac8371d New Crowdin translations by GitHub Action (#1597)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-05-08 14:57:20 +08:00
h2zi e5444f13d4 chore(l10n): update pot files 2026-05-07 21:32:48 +00:00
renovate[bot] ba53855292 chore(deps): Update pnpm to v11.0.5 2026-05-07 21:31:47 +00:00
renovate[bot] ab869d6b12 chore(deps): Update pnpm to v11.0.5 (#1596)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-08 05:31:18 +08:00
耗子 2d7fbeb370 refactor: 前端 UI 全量重构与日志查看器重写
- 引入设计系统 (tokens / naive-overrides / variables.scss),收敛颜色/圆角/阴影/字号/动效到唯一真相源,Naive UI 主色改回浅绿
- 新增系统组件 (PageContainer / ConfirmDialog / FormModal / FormDrawer 等),替换并删除 AppPage / CommonPage / DeleteConfirm
- 全量视图迁移到新组件:列表页用 NFlex 排版操作列,搜索框用 !w-XX 覆盖 NInput 默认 100% 宽
- 工具箱 SSH/日志清理/磁盘 重写为更紧凑的卡片+sub-tabs 布局
- 终端页重做:macOS 风格 shell + 紧凑 tab + 红绿状态点 + 主机 drawer
- 日志查看器重写:后端新增 GET /file/tail (反向分页) + WS /follow,前端用 anser 渲染 ANSI 颜色 + 原生 div 滚动 + WS 自动重连
- 修复多处 NPagination page-count 与 item-count 同时设置的警告
- 网站列表支持单击网站名打开编辑

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 05:30:20 +08:00
renovate[bot] 95790da784 chore(deps): Update pnpm to v11.0.4 (#1595)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-08 00:45:59 +08:00
renovate[bot] 2ffd099c8a chore(deps): Update pnpm to v11 (#1594)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-08 00:42:05 +08:00
耗子 f52d324073 chore(deps): 升级 vite 到 ^8.0.11
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:44:45 +08:00
耗子 787d489cfc chore: 适配 pnpm v11 的 allowBuilds 配置
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:44:20 +08:00
renovate[bot] 407148dfe3 chore(deps): Update non-major dependencies (#1593)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-07 20:28:41 +08:00
github-actions[bot] 6b1eaa1aaa New Crowdin translations by GitHub Action (#1592)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-05-07 05:16:38 +08:00
h2zi 249424332e chore(l10n): update pot files 2026-05-06 21:16:10 +00:00
耗子 68efc98c06 fix: 用户列表与文件内联输入框未占满列宽 2026-05-07 05:13:08 +08:00
h2zi aab5029848 chore(l10n): update pot files 2026-05-06 21:01:56 +00:00
耗子 b7041f9253 feat: 进程列表左键点击弹出操作菜单 2026-05-07 05:00:18 +08:00
耗子 a5a6409be0 fix: 清除日志后实时日志未刷新 2026-05-07 04:56:01 +08:00
耗子 3eb2696690 fix: environment列表无数据时返回null 2026-05-07 04:55:57 +08:00
耗子 dbcbbf9d95 feat: 备份支持指定压缩文件格式,close #1582 2026-05-07 04:31:09 +08:00
renovate[bot] 2c078d90d7 chore(deps): Update module github.com/go-webauthn/webauthn to v0.17.2 (#1591)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-06 16:37:08 +00:00
耗子 f08136242e feat: 已安装应用列表显示运行状态 2026-05-06 18:05:19 +08:00
耗子 d503d9f083 fix: docker compose在目录大小情况下获取状态失败,close #1590 2026-05-06 03:14:46 +08:00
renovate[bot] 3147f70dde chore(deps): Update module github.com/gookit/color to v1.6.1 (#1589)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-04 21:57:48 +00:00
renovate[bot] e2815a430e chore(deps): Lock file maintenance (#1588)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-04 05:07:49 +00:00
renovate[bot] 36a8646485 chore(deps): Update module github.com/libtnb/sqlite to v1.1.1 (#1586)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-03 13:25:11 +00:00
renovate[bot] 901ce774d5 chore(deps): Update module github.com/fsnotify/fsnotify to v1.10.0 (#1585)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-03 05:49:35 +00:00
renovate[bot] 0a42e198dd chore(deps): Update non-major dependencies (#1584)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-02 16:35:28 +00:00
renovate[bot] 54ad4dff86 chore(deps): Update non-major dependencies (#1581)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-30 21:57:37 +00:00
耗子 6f54b6e402 fix: De Morgan's law 2026-04-29 23:21:18 +08:00
耗子 075d9f8ee4 fix: nginx下ip证书签发失败 2026-04-29 20:27:09 +08:00
耗子 c0d8642cb5 fix: 优化代码 2026-04-28 06:05:34 +08:00
耗子 418d8af6d9 fix: 优化各列宽度 2026-04-28 04:18:39 +08:00
耗子 45b63f8cd0 fix: 容器镜像清理 2026-04-28 04:18:28 +08:00
耗子 521bf37a9f Merge remote-tracking branch 'origin/main' 2026-04-27 22:56:36 +08:00
耗子 452c5ffac8 fix: 更新错误页描述 2026-04-27 22:56:31 +08:00
h2zi f1cc5bb7d6 chore: update mocks 2026-04-27 12:31:25 +00:00
耗子 909556b3d6 refactor: 统一日志清理 2026-04-27 20:30:12 +08:00
耗子 cd64dc65f3 fix: lint 2026-04-27 19:32:20 +08:00
耗子 b3b29d79c5 fix: 优化supervisor管理器 2026-04-27 19:26:46 +08:00
耗子 d565d3bbb8 Merge remote-tracking branch 'origin/main' 2026-04-27 17:28:27 +08:00
耗子 dc62ecf1dd fix: shell 执行失败时丢失 cmd.Run 真实错误
当 cmd.Dir 不存在或 exec 启动失败时,Go 在进入 bash 前就返回错误,stderr 为空,原本只取 stderr 的错误格式会输出空 err,导致排查困难。改为同时携带 cmd.Run 的真实错误和 stderr。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:28:09 +08:00
renovate[bot] dd600d89d2 chore(deps): Lock file maintenance (#1580)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-27 05:13:33 +00:00
renovate[bot] 1d4a488e83 chore(deps): Update non-major dependencies (#1579)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-26 17:02:49 +00:00
renovate[bot] 71bb13cd66 chore(deps): Update non-major dependencies (#1578)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-25 16:35:16 +00:00
renovate[bot] 5a52efd3ec chore(deps): Update dependency eslint-plugin-vue to v10.9.0 (#1577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-25 06:00:31 +00:00
renovate[bot] fc8de12590 chore(deps): Update module github.com/DeRuina/timberjack to v1.4.2 (#1576)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-24 14:04:04 +00:00
renovate[bot] b03ad4ba86 chore(deps): Update module github.com/go-webauthn/webauthn to v0.17.0 (#1575)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-24 14:40:33 +08:00
renovate[bot] 2be313b676 chore(deps): Update dependency eslint-plugin-oxlint to v1.61.0 (#1574)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-23 22:35:10 +00:00
renovate[bot] 5d8a08c507 chore(deps): Update non-major dependencies (#1573)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-24 04:09:24 +08:00
renovate[bot] ab167e884c chore(deps): Update non-major dependencies (#1572)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-24 04:06:33 +08:00
renovate[bot] b57602deb9 chore(deps): Update dependency vite to v8.0.9 (#1571)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-23 08:50:16 +00:00
耗子 db8064fbe3 fix: 日志查看器改用 PTY + xterm 以支持滚动查看历史
journalctl 未加 -n 参数导致只能看到 10 行历史;watchEffect 每次日志更新
都强制滚到底部,导致用户即便有滚动条也无法回看历史。改用已有的 ws.pty +
xterm.js 基础设施取代 n-log,借助 xterm 的 scrollback buffer 获得与 SSH
下 tail 一致的交互体验。

- tail 预加载 1000 行并启用 -F 兼容日志轮转
- journalctl 加 --no-pager 防止 TTY 下启动分页器,预加载 1000 行
- 移除无用的 language prop 并同步清理 EditModal 的传参

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 01:45:24 +08:00
renovate[bot] aeabe9b17e chore(deps): Update dependency vue-tsc to v3.2.7 (#1570)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-22 23:18:48 +08:00
renovate[bot] cd65b13c0a chore(deps): Update module github.com/go-webauthn/webauthn to v0.16.5 (#1569)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-22 18:20:09 +08:00
github-actions[bot] 51d979e63f New Crowdin translations by GitHub Action (#1568)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-22 04:48:34 +08:00
h2zi 751513d59a chore(l10n): update pot files 2026-04-21 20:38:26 +00:00
renovate[bot] 755230161b chore(deps): Update pnpm/action-setup action to v6 (#1564)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-22 03:10:21 +08:00
h2zi 13e5771445 chore: update mocks 2026-04-21 19:07:46 +00:00
Copilot 6a7c856fe8 feat: 网站到期自动关闭 (#1561)
* Initial plan

* feat: 网站到期自动关闭功能

- biz.Website 新增 ExpireAt 字段(*time.Time,可空,默认不限时)
- 新增数据库迁移自动添加 expire_at 列
- 新增 API 端点 POST /website/{id}/expire_at
- 新增定时任务每分钟检查到期网站并自动关闭
- 前端网站列表新增到期时间列(证书有效期与备注之间)

Agent-Logs-Url: https://github.com/acepanel/panel/sessions/af33abcf-bbcd-4306-a680-76307c699774

Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>

* refactor: 优化到期任务查询和数据更新

- 到期检查任务直接查询已到期且运行中的网站,避免加载全量数据
- UpdateExpireAt 使用直接 UPDATE 避免不必要的 SELECT 查询

Agent-Logs-Url: https://github.com/acepanel/panel/sessions/af33abcf-bbcd-4306-a680-76307c699774

Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>

* refactor: ExpireAt 改为 time.Time,日志使用英文

- ExpireAt 从 *time.Time 改为 time.Time,零值表示不限时
- 日志消息改为英文,与其他 job 文件风格保持一致
- 前端适配零值时间判断

Agent-Logs-Url: https://github.com/acepanel/panel/sessions/00c49c64-7614-48d1-98bb-0d742d5f10ad

Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>

* Revert "refactor: ExpireAt 改为 time.Time,日志使用英文"

This reverts commit ac3dfdb7e4.

* fix: lint

* fix: lint

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>
Co-authored-by: 耗子 <haozi@loli.email>
2026-04-22 03:06:43 +08:00
renovate[bot] 95a5d68f7c chore(deps): Update non-major dependencies (#1567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 01:49:37 +00:00
renovate[bot] 89bdc033cc chore(deps): Update module modernc.org/sqlite to v1.49.0 (#1566)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-21 02:46:21 +08:00
renovate[bot] eb83a753b6 chore(deps): Update dependency typescript to v6.0.3 (#1565)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-20 04:56:38 +00:00
耗子 b930c52acf Merge remote-tracking branch 'origin/main' 2026-04-17 19:01:37 +08:00
耗子 0585b7c04b fix: 批量创建PHP网站报错 2026-04-17 19:01:31 +08:00
renovate[bot] 0bc4eba46f chore(deps): Update dependency eslint-plugin-oxlint to v1.60.0 (#1563)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 22:44:52 +00:00
renovate[bot] f015404523 chore(deps): Update non-major dependencies (#1562)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 22:56:51 +08:00
耗子 2a2e413e45 chore: update deps 2026-04-16 22:48:52 +08:00
Copilot 1f9407b25a fix: WebDAV 存储连接失败 — 前端表单字段与后端不匹配 (#1560)
* Initial plan

* fix: WebDAV 存储连接失败,前端表单字段绑定错误 (host → url)

Agent-Logs-Url: https://github.com/acepanel/panel/sessions/6bc5c598-6439-49f4-92d7-59a0640d6148

Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>

* fix: 将 WebDAV 标签 URL 包装 $gettext() 保持国际化一致性

Agent-Logs-Url: https://github.com/acepanel/panel/sessions/6bc5c598-6439-49f4-92d7-59a0640d6148

Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>
2026-04-16 18:37:50 +08:00
renovate[bot] d09a62e175 chore(deps): Update dependency @vitejs/plugin-vue to v6.0.6 (#1559)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-16 18:28:31 +08:00
耗子 22102b5dc7 Merge remote-tracking branch 'origin/main' 2026-04-16 15:15:19 +08:00
耗子 fcf4544224 fix: 不支持用户管理的数据库不显示 2026-04-16 15:15:13 +08:00
Copilot d301f045b1 feat: 应用更新确认弹窗显示目标版本号 (#1556)
Agent-Logs-Url: https://github.com/acepanel/panel/sessions/4897ec9f-a2e8-42bf-a691-3e1cf14c7d0c

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>
2026-04-13 16:41:38 +08:00
耗子 694847e558 Merge remote-tracking branch 'origin/main' 2026-04-13 16:33:58 +08:00
耗子 02fecb35de fix: 使用WEB服务器下 IPv6 证书签发问题 2026-04-13 16:33:52 +08:00
renovate[bot] d58577205e chore(deps): Lock file maintenance (#1555)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-13 04:58:05 +00:00
renovate[bot] 316bd54bc0 chore(deps): Update non-major dependencies (#1554)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-13 01:54:23 +00:00
renovate[bot] 2209757a62 chore(deps): Update non-major dependencies (#1553)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-13 01:34:59 +08:00
耗子 59a7f01f95 feat: Grafana添加更多数据源 2026-04-10 22:36:00 +08:00
耗子 5b8b0f0d07 feat: 移除buypass 2026-04-10 02:06:17 +08:00
耗子 1a4b377c22 fix: DNS别名映射问题 2026-04-10 00:16:44 +08:00
renovate[bot] 837aaab151 chore(deps): Lock file maintenance (#1552)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-09 22:48:20 +08:00
renovate[bot] 168dd7071e chore(deps): Update non-major dependencies (#1551)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-09 22:45:31 +08:00
github-actions[bot] b7e50aaf1d New Crowdin translations by GitHub Action (#1550)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-09 19:42:43 +08:00
h2zi cf6a6c80c1 chore(l10n): update pot files 2026-04-09 11:26:08 +00:00
耗子 a2f39d104c Merge remote-tracking branch 'origin/main' 2026-04-08 23:55:23 +08:00
耗子 97c8ad8356 feat: DNS别名使用完整域名 2026-04-08 23:55:17 +08:00
renovate[bot] 489e0702c3 chore(deps): Update module github.com/go-webauthn/webauthn to v0.16.3 (#1549)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-08 15:22:53 +08:00
耗子 db2bb0606d feat: 优化代码 2026-04-07 19:05:40 +08:00
github-actions[bot] a2d1c9c535 New Crowdin translations by GitHub Action (#1548)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-07 19:00:21 +08:00
h2zi f6ab265b6b chore(l10n): update pot files 2026-04-07 10:35:11 +00:00
github-actions[bot] 4614fb1cd1 New Crowdin translations by GitHub Action (#1547)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-04-07 15:36:49 +08:00
h2zi 965525e68e chore(l10n): update pot files 2026-04-07 06:49:24 +00:00
耗子 fdde61c8ae feat: DNS-01 CNAME 委托验证 (#1546)
* feat: DNS-01 CNAME 委托验证

* fix: address comments

* fix: lint
2026-04-07 03:45:41 +08:00
耗子 99bc8ddc8d Merge remote-tracking branch 'origin/main' 2026-04-07 03:17:46 +08:00
耗子 d4f85066a0 feat: 计划任务支持备份指定目录功能 (#1540)
在计划任务的备份类型中新增"目录"子类型,支持将任意指定目录打包压缩后
备份到本地或远程存储,并自动清理过期备份。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 02:56:29 +08:00
dependabot[bot] 5263ec4a46 chore(deps-dev): bump vite from 8.0.3 to 8.0.5 in /web (#1544)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.3 to 8.0.5.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.5
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 02:41:37 +08:00
h2zi 522d78569a chore: update mocks 2026-04-06 18:20:12 +00:00
耗子 a42f9f63d0 refactor: 重构证书签发实现 (#1543)
* refactor: 重构证书签发实现

* fix: address comments

* fix: optimize code
2026-04-07 02:19:22 +08:00
renovate[bot] 7a7e8c4460 chore(deps): Update module github.com/lib/pq to v1.12.3 (#1542)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-07 01:41:11 +08:00
zenmediasocial 116fbdda89 fix: DNS-01 挑战缺少传播等待导致 NXDOMAIN 验证失败 (#1536)
dnsSolver 未实现 acmez 的 Waiter 接口,导致 Present() 向 Cloudflare
写入 TXT 记录后,acmez 立即通知 CA 进行验证,而 DNS 记录尚未在权威
解析服务器上生效,Let's Encrypt 查询时返回 NXDOMAIN,授权失败。

修复内容:
- 为 dnsSolver 添加 Wait() 方法(实现 acmez.Waiter),在通知 CA 验证
  前等待 60 秒,确保 DNS 记录传播完成
- 将 records 字段的赋值改为 append,避免多域名证书场景下并发
  Present() 调用互相覆盖记录
- 添加 sync.Mutex 保护 records 字段的并发读写
- 放宽 SetRecords 结果数量检查(!= 1 改为 == 0),兼容更新已有记录时
  返回多条结果的情况
- 将 Renew 超时从 2 分钟延长至 10 分钟,避免加入等待后超时

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 00:50:46 +08:00
耗子 87b728ca1b fix: lint 2026-04-07 00:43:34 +08:00
renovate[bot] 2320656d2b chore(deps): Update non-major dependencies (#1541)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-06 21:46:49 +08:00
renovate[bot] 2e5d67e4b0 chore(deps): Lock file maintenance (#1539)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-06 14:47:55 +08:00
renovate[bot] 1c9ed32aaf chore(deps): Update dependency sass to v1.99.0 (#1538)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-06 04:47:21 +00:00
renovate[bot] 9df648b7e9 chore(deps): Update non-major dependencies (#1537)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-05 13:43:32 +00:00
Copilot 1079065220 feat: 为应用页面添加"已安装"Tab选项卡 (#1535)
* Initial plan

* feat: 为应用页面添加"已安装"Tab选项卡

- 后端:app/list 和 environment/list 添加 installed 过滤参数
- 前端:创建 InstalledView 组件展示已安装的应用和环境
- 前端:IndexView 添加"已安装"Tab 并设为默认选项

Agent-Logs-Url: https://github.com/acepanel/panel/sessions/9b460f4b-c69e-4a5b-8131-c68099edcb7c

Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>

* refactor: 移除 InstalledView 中未使用的 VersionModal 组件

Agent-Logs-Url: https://github.com/acepanel/panel/sessions/9b460f4b-c69e-4a5b-8131-c68099edcb7c

Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>
2026-04-03 16:15:30 +08:00
Copilot 6f8c1b410f feat: 文件编辑器未保存更改对话框添加放弃选项 (#1534)
* Initial plan

* feat: 文件编辑器保存提示添加返回/放弃/保存三种操作

Agent-Logs-Url: https://github.com/acepanel/panel/sessions/7127d3cc-b379-4b25-ae66-d1847f139551

Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>

* fix: 标签页保存失败时显示错误提示消息

Agent-Logs-Url: https://github.com/acepanel/panel/sessions/7127d3cc-b379-4b25-ae66-d1847f139551

Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: h2zi <5967335+h2zi@users.noreply.github.com>
2026-04-03 16:13:53 +08:00
耗子 6e63f565fa Merge remote-tracking branch 'origin/main' 2026-04-03 14:53:46 +08:00
耗子 c24fa4580e fix: 数据库防止酒吧点炒饭
数据库名和用户名校验正则禁止首位为数字,因为数据库不接受库名/用户名以数字开头。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 14:51:14 +08:00
renovate[bot] 2e6277848c chore(deps): Update module github.com/go-webauthn/webauthn to v0.16.2 (#1531)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-03 01:19:36 +00:00
renovate[bot] 35f9d16d81 chore(deps): Update module github.com/lib/pq to v1.12.1 (#1530)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-02 17:28:44 +00:00
renovate[bot] fcf0d49cba chore(deps): Update module github.com/hashicorp/go-version to v1.9.0 (#1529)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-02 22:34:32 +08:00
耗子 9c0f5d86d2 Merge remote-tracking branch 'origin/main' 2026-04-02 20:56:47 +08:00
耗子 97161b8628 feat: 优化PostgreSQL数据库用户删除 2026-04-02 20:56:40 +08:00
renovate[bot] 0b131b2920 chore(deps): Update dependency lodash-es to v4.18.1 [SECURITY] (#1528)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-02 17:45:19 +08:00
耗子 37c2f851e5 feat: 去除eslint 2026-04-01 23:19:29 +08:00
耗子 9ec7b47cf6 feat: 前端现代化改造3 2026-04-01 23:01:06 +08:00
耗子 a4c59d29af feat: 前端现代化改造2 2026-04-01 22:58:39 +08:00
耗子 a5396fddbb feat: 前端现代化改造 2026-04-01 22:52:58 +08:00
耗子 bc2584b7e5 feat: 前端现代化改造 2026-04-01 22:46:54 +08:00
renovate[bot] d839949684 chore(deps): Update module github.com/shirou/gopsutil/v4 to v4.26.3 (#1526)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-01 17:42:09 +08:00
renovate[bot] 3d81d1d0a3 chore(deps): Update module github.com/andybalholm/brotli to v1.2.1 (#1525)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-01 01:24:39 +00:00
renovate[bot] 0debf14dc3 chore(deps): Update module modernc.org/sqlite to v1.48.0 (#1524)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-01 02:21:11 +08:00
耗子 fc84ba4e86 Merge remote-tracking branch 'origin/main' 2026-03-31 22:01:56 +08:00
耗子 7acd24c587 fix: 移除误装的垃圾包 2026-03-31 22:01:51 +08:00
renovate[bot] 4275baabd0 chore(deps): Update module github.com/gookit/validate to v1.5.7 (#1522)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-31 13:39:46 +00:00
耗子 e6657c10e2 fix: postgres配置转义 2026-03-31 19:18:14 +08:00
耗子 9e5cc546c1 Merge remote-tracking branch 'origin/main' 2026-03-30 20:28:17 +08:00
耗子 e799cb4031 feat: 重构面板证书逻辑 2026-03-30 20:28:11 +08:00
renovate[bot] 12a6d6f39f chore(deps): Lock file maintenance (#1521)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-30 04:37:29 +00:00
耗子 c33c03d225 fix: ufw默认开启转发,close #1519 2026-03-28 19:45:48 +08:00
耗子 715e58c122 feat: 面板证书可以为空 2026-03-28 19:44:53 +08:00
耗子 9f8d684763 fix: grafana配置修改 2026-03-28 16:32:59 +08:00
耗子 1d79f8c06e fix: 修改推荐的图表 2026-03-28 00:21:52 +08:00
耗子 3fa4921b40 fix: mongodb 修改密码权限问题 2026-03-27 23:10:06 +08:00
github-actions[bot] 08bb081c66 New Crowdin translations by GitHub Action (#1518)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-03-27 22:27:17 +08:00
h2zi 4aa95c77f9 chore(l10n): update pot files 2026-03-27 14:24:47 +00:00
github-actions[bot] 5ccaa58ccb New Crowdin translations by GitHub Action (#1482)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-03-27 21:56:12 +08:00
h2zi 1ac12b4ebd chore(l10n): update pot files 2026-03-27 13:05:45 +00:00
耗子 32067e6000 feat: 添加exporter 2026-03-27 20:57:04 +08:00
耗子 37fc3b29fa Merge remote-tracking branch 'origin/main' 2026-03-27 20:33:39 +08:00
耗子 e2ce8e70f1 fix: optimize code 2026-03-27 20:32:31 +08:00
h2zi 60d4e31fac chore(l10n): update pot files 2026-03-27 12:14:27 +00:00
耗子 49b6c5af7e feat: 添加apcu模块,close #1517 2026-03-27 20:11:01 +08:00
耗子 76f80a8e65 feat: 修改PHP模块目录 2026-03-27 20:01:25 +08:00
耗子 dddb718fda feat: 更新安全说明 2026-03-27 19:58:16 +08:00
耗子 046dc09f69 feat: 更新安全说明 2026-03-27 19:56:25 +08:00
耗子 8d218e7e88 feat: 更新安全说明 2026-03-27 19:56:08 +08:00
耗子 5fdae42eb7 feat: 更新安全说明 2026-03-27 19:55:18 +08:00
耗子 d01d14f35e feat: 支持秒级计划任务,close #1511 2026-03-27 19:20:41 +08:00
耗子 5e251ac04a fix: lint 2026-03-27 17:25:18 +08:00
耗子 b12590c1c2 fix: lint 2026-03-27 17:13:23 +08:00
h2zi 4c6347d6c3 chore: update mocks 2026-03-27 09:00:20 +00:00
耗子 f66b094ec6 feat: 添加elasticsearch和opensearch 2026-03-27 16:59:24 +08:00
耗子 b0e04d2976 feat: clickhouse优化 2026-03-27 16:20:37 +08:00
耗子 68b96be537 feat: 添加clickhouse和mongodb的管理 2026-03-27 15:43:18 +08:00
耗子 8ebe63ac3d feat: 添加clickhouse和mongodb的管理 2026-03-27 15:09:06 +08:00
耗子 4df9400fb2 fix: optimize code 2026-03-27 06:11:47 +08:00
耗子 c40da3895d Merge remote-tracking branch 'origin/copilot/fix-uuid-issue-and-check-code' 2026-03-27 06:06:05 +08:00
耗子 ad409eb446 Merge remote-tracking branch 'origin/main' 2026-03-27 06:05:34 +08:00
耗子 7c7d5b6ce1 feat: 添加clickhouse和mongodb 2026-03-27 06:03:55 +08:00
renovate[bot] 5de7a8e745 chore(deps): Update codecov/codecov-action action to v6 (#1516)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-27 03:39:46 +08:00
copilot-swe-agent[bot] 2f9ccf23a0 fix(web): 降级HTTP环境的tab id与SSH粘贴读取
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Agent-Logs-Url: https://github.com/acepanel/panel/sessions/12e3737b-640d-48e1-942b-dc17ae98ad4a
2026-03-26 14:57:16 +00:00
copilot-swe-agent[bot] 3a7099b3d2 fix(web): 替换不依赖HTTPS的tab id并修复SSH剪贴板读取降级
Co-authored-by: devhaozi <115467771+devhaozi@users.noreply.github.com>
Agent-Logs-Url: https://github.com/acepanel/panel/sessions/12e3737b-640d-48e1-942b-dc17ae98ad4a
2026-03-26 14:52:47 +00:00
copilot-swe-agent[bot] e05202dafb Initial plan 2026-03-26 14:38:14 +00:00
renovate[bot] f8c99803ed chore(deps): Update dependency vite to v8.0.3 (#1514)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-26 12:38:13 +00:00
renovate[bot] 3439783518 chore(deps): Update non-major dependencies (#1513)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-25 21:06:51 +00:00
renovate[bot] c7929c2899 chore(deps): Update github.com/gookit/validate digest to d265741 (#1512)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-25 08:51:02 +00:00
renovate[bot] 0d6a1b384d chore(deps): Update non-major dependencies (#1509)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-25 00:58:22 +00:00
耗子 9216940d90 feat: 添加rocketmq和kafka 2026-03-25 03:12:18 +08:00
耗子 d1371b2bb1 feat: 添加valkey、elasticsearch和opensearch 2026-03-25 03:06:26 +08:00
耗子 e00013f801 feat: 添加valkey、elasticsearch和opensearch 2026-03-25 02:58:15 +08:00
耗子 130716216d feat: 添加alertmanager配置 2026-03-25 01:51:55 +08:00
耗子 9d22b5d1d0 feat: 添加推荐的仪表盘ID 2026-03-25 01:44:20 +08:00
耗子 b952ebf5ad fix: 修复问题 2026-03-25 01:17:05 +08:00
耗子 455f6c061d feat: 优化grafana 2026-03-25 01:06:32 +08:00
耗子 432b0662b0 chore: add svg logo 2026-03-24 22:59:54 +08:00
耗子 0ae7327105 feat: 添加prometheus和grafana支持 (#1508)
* feat: 阶段性提交

* feat: 完成开发

* feat: 阶段性提交

* feat: 阶段性提交
2026-03-24 22:45:22 +08:00
renovate[bot] 0870b100cc chore(deps): Update dependency @vue/tsconfig to v0.9.1 (#1507)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-24 06:15:18 +00:00
renovate[bot] 28b2eaa638 chore(deps): Update dependency unplugin-vue-components to v32 (#1504)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-24 03:40:29 +08:00
renovate[bot] bdcdfbf96e chore(deps): Update dependency vite-plugin-vue-devtools to v8.1.1 (#1505)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-23 18:00:50 +00:00
renovate[bot] 471db7ef69 chore(deps): Update non-major dependencies (#1503)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-23 22:43:09 +08:00
renovate[bot] be67988272 chore(deps): Lock file maintenance (#1502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-23 05:07:39 +00:00
renovate[bot] a56ef041c8 chore(deps): Update dependency vite-plugin-compression2 to v2.5.3 (#1500)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-21 17:31:05 +00:00
renovate[bot] 3b9c1757a0 chore(deps): Update module github.com/libtnb/sqlite to v1.0.1 (#1499)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-21 05:42:33 +00:00
耗子 ed06f53791 fix: lint 2026-03-21 04:30:00 +08:00
Copilot ff06049085 修复 UFW 端口转发未下发/清理 route allow 规则 (#1498)
* Initial plan

* fix: add ufw route allow/delete for port forwarding

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-03-20 18:34:47 +08:00
renovate[bot] b9b00a0117 chore(deps): Update dependency vue-router to v5.0.4 (#1497)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-20 02:09:32 +08:00
renovate[bot] 61572b0a3c chore(deps): Update dependency vite to v8.0.1 (#1496)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-19 13:24:57 +00:00
renovate[bot] 8fdbd19061 chore(deps): Update module github.com/lib/pq to v1.12.0 (#1495)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-18 21:35:38 +00:00
renovate[bot] c5e7084c8b chore(deps): Update non-major dependencies to v66.6.7 (#1494)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-18 16:56:42 +08:00
renovate[bot] 71129d6446 chore(deps): Update dependency vue-tsc to v3.2.6 (#1492)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-18 01:51:17 +08:00
renovate[bot] 615df5966b chore(deps): Update pnpm/action-setup action to v5 (#1493)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-18 01:50:43 +08:00
renovate[bot] d15ccba931 chore(deps): Update github.com/gookit/validate digest to 350536c (#1491)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-16 05:41:38 +00:00
renovate[bot] d0ac218809 chore(deps): Lock file maintenance (#1490)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-16 05:27:14 +00:00
renovate[bot] f0cf40c5f0 chore(deps): Update dependency vite-plugin-compression2 to v2.5.2 (#1489)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-15 21:13:38 +00:00
Copilot b100922bc7 fix: 文件上传覆盖运行中的二进制文件时 ETXTBSY 错误 (#1488)
* Initial plan

* fix: 强制覆盖文件时先删除再创建,避免覆盖运行中的二进制文件报 ETXTBSY 错误

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

* fix: 处理 Remove() 错误,删除失败时提前返回明确错误信息

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-03-15 20:09:03 +08:00
Copilot 69ff73a393 feat: 为应用管理(原生应用、运行环境和容器模板)添加搜索功能 (#1487)
* Initial plan

* feat: 为应用管理(原生应用、运行环境和容器模板)添加搜索功能

后端通过 GET query 参数传入关键词,按名称和描述进行大小写不敏感过滤。
容器模板额外支持按 website 字段搜索。
前端在分类标签旁添加搜索输入框,输入变化时自动触发重新查询。

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

* refactor: 优化搜索过滤代码格式和 TemplateView 一致性

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-03-15 18:07:28 +08:00
Copilot 01585dcc3b fix: preserve YAML-defined order for container template environment variables (#1486)
* Initial plan

* fix: 保留容器模板环境变量的 YAML 定义顺序

使用 yaml.Node 从原始 YAML 数据中提取环境变量键的定义顺序,
替代之前的字母序排序,确保前端展示的字段顺序与 data.yml 一致。

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-03-15 17:06:37 +08:00
Copilot fe5daca28b fix: 修复文件粘贴重命名时弹窗无限循环的回归问题 (#1483)
* Initial plan

* fix: 修复文件粘贴重命名时弹窗无限循环的回归问题

修复 PR #1474 引入的回归问题:
1. handleRename 完成后不再调用 checkAndExecute(避免重新弹出警告弹窗导致无限循环),
   改为直接 await file.exist() 检查冲突并在 while 循环内重新提示重命名
2. 重命名弹窗增加源文件路径显示,便于区分多个冲突文件

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-03-15 03:48:48 +08:00
h2zi 3be7ec8a80 chore(l10n): update pot files 2026-03-14 19:18:16 +00:00
renovate[bot] 6426d2e7d2 chore(deps): Update non-major dependencies (#1481)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-13 09:20:14 +00:00
renovate[bot] 663a303c2b chore(deps): Update dependency vite to v8 (#1480)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-13 06:29:04 +08:00
renovate[bot] 1ce4628d87 chore(deps): Update non-major dependencies (#1479)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-12 21:45:43 +00:00
耗子 d45ddac1cc fix: eBPF UDP 扫描检测过滤已知服务端口响应流量,避免 DNS/NTP 等回包误报
UDP 无连接态,无法像 TCP 靠 SYN 标志区分新连接和响应。系统发起 DNS 查询后,
响应包(源端口 53)到达临时端口,因不在监听白名单中被误报为扫描。

在 UDP 处理器中增加源端口检查,跳过已知服务端口的响应流量:
DNS(53), DHCP(67/68), NTP(123), QUIC(443), IKE(500),
DHCPv6(546/547), DoQ(853), IPsec NAT-T(4500), mDNS(5353)

Closes #1478

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 17:59:41 +08:00
renovate[bot] 820b318c00 chore(deps): Update module golang.org/x/net to v0.52.0 (#1477)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-12 08:46:30 +00:00
renovate[bot] 516f552181 chore(deps): Update module golang.org/x/crypto to v0.49.0 (#1476)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-12 01:15:42 +00:00
github-actions[bot] 228e2146ba New Crowdin translations by GitHub Action (#1475)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-03-12 03:18:00 +08:00
devhaozi 007f7339f7 chore(l10n): update pot files 2026-03-11 17:56:47 +00:00
Copilot 613ec00c90 feat: add rename option for file copy/move conflicts, fix same-directory paste error (#1474)
* Initial plan

* feat: 文件复制/移动冲突时添加重命名选项,修复同目录粘贴覆盖报错

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

* refactor: 移除不必要的 ref,改用 defaultValue 并支持 Enter 键确认重命名

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

* fix: use const for DialogReactive and remove unused import to pass lint

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-03-12 01:55:41 +08:00
github-actions[bot] 8071ea1757 New Crowdin translations by GitHub Action (#1472)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-03-12 01:04:38 +08:00
耗子 6da51ed0eb fix: build 2026-03-12 00:56:48 +08:00
耗子 aa1751f0bb Merge remote-tracking branch 'origin/main' 2026-03-12 00:54:42 +08:00
耗子 f15841f04a fix: build 2026-03-12 00:54:38 +08:00
renovate[bot] ced266145b chore(deps): Update dependency vite-plugin-compression2 to v2.5.1 (#1471)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-12 00:46:17 +08:00
h2zi 580a68202c chore(l10n): update pot files 2026-03-11 16:24:16 +00:00
renovate[bot] 81e99db86e chore(deps): Update module github.com/DeRuina/timberjack to v1.4.0 (#1470)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-12 00:22:23 +08:00
耗子 499a570efa fix: 移除ssl密码套件配置,因为aws-lc似乎不支持 2026-03-12 00:21:02 +08:00
耗子 5258ef9d00 Merge remote-tracking branch 'origin/main' 2026-03-11 21:58:21 +08:00
耗子 c34203ae7b fix: 优化代码 2026-03-11 21:58:17 +08:00
renovate[bot] d477d1c637 chore(deps): Update non-major dependencies (#1469)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-11 06:43:29 +00:00
renovate[bot] 10c98c5bb5 chore(deps): Update non-major dependencies (#1468)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-10 10:16:24 +00:00
renovate[bot] d8e8a305d3 chore(deps): Update dependency vue to v3.5.30 (#1466)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-09 17:47:56 +00:00
renovate[bot] 2dba43fd20 chore(deps): Lock file maintenance (#1465)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-09 05:23:54 +00:00
耗子 b5a65e6226 Merge remote-tracking branch 'origin/main' 2026-03-09 06:28:58 +08:00
耗子 7295baa2d5 chore: update 2026-03-09 06:28:53 +08:00
renovate[bot] 2c9b484218 chore(deps): Update dependency naive-ui to v2.44.1 (#1464)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-08 20:51:45 +00:00
renovate[bot] 0ac71f9f8a chore(deps): Update dependency naive-ui to v2.44.0 (#1462)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-08 13:41:26 +00:00
耗子 2217d2ba33 Merge remote-tracking branch 'origin/main' 2026-03-08 17:23:03 +08:00
耗子 f94345dd1c fix: 容器终端兼容sh 2026-03-08 17:22:59 +08:00
renovate[bot] 17eb9c6b63 chore(deps): Update pnpm to v10.31.0 (#1461)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-08 05:21:23 +00:00
github-actions[bot] 0104418c10 New Crowdin translations by GitHub Action (#1460)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-03-08 01:03:47 +08:00
h2zi 24b0fb1a12 chore(l10n): update pot files 2026-03-07 16:54:14 +00:00
Copilot e9c1a946e9 feat: 支持从本地目录加载容器模板(兼容 acepanel/templates 仓库格式) (#1452)
* Initial plan

* feat: 支持从本地目录加载容器模板

从 {app.Root}/templates/ 目录加载 JSON 格式的本地模板文件,
本地模板可覆盖同 slug 的远端模板。
UI 上使用 warning 标签标识本地模板。
本地模板部署时跳过远端回调。

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

* refactor: 为本地模板加载添加 slog 日志记录

目录读取失败时记录警告日志,
模板文件读取/解析/缺失 slug 时记录警告日志。

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

* refactor: 本地模板改用与 acepanel/templates 仓库一致的目录格式

从 JSON 文件格式改为目录格式:每个模板是一个子目录,
包含 data.yml + docker-compose.yml + 可选 logo.svg/logo.png。
data.yml 支持 i18n 字段,环境变量使用 map 格式。
与 github.com/acepanel/templates 仓库格式完全一致。

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

* chore: 改进 resolveLocale 注释

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

* plan: address review feedback

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

* refactor: 将模板数据类型移至 pkg/types 并清理注释括号

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

* fix: 注入 slog.Logger、方法挂载到 templateRepo、简化注释

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

* 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-03-08 00:51:51 +08:00
耗子 1afedfc4d8 Merge remote-tracking branch 'origin/main' 2026-03-08 00:42:14 +08:00
耗子 971aaddc85 feat: 计划任务运行周期支持重启后,close #1456 2026-03-08 00:42:09 +08:00
h2zi 60052aac09 chore: update mocks 2026-03-07 16:28:46 +00:00
耗子 4a6fdc45d5 feat: 部署证书时支持开启 HTTPS 及一键签发证书泛域名 DNS 选择 2026-03-08 00:27:55 +08:00
耗子 2be464777b fix: 优化证书账号显示 2026-03-07 22:06:52 +08:00
renovate[bot] 7ea9f5a340 chore(deps): Update dependency @eslint/eslintrc to v3.3.5 (#1459)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-07 05:45:17 +00:00
renovate[bot] 2142c4fd67 chore(deps): Update non-major dependencies (#1458)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-06 19:21:05 +08:00
renovate[bot] f3e9d20990 chore(deps): Update non-major dependencies (#1457)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-05 21:50:42 +00:00
renovate[bot] f1c17c4651 chore(deps): Update non-major dependencies to v66.6.5 (#1455)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-04 13:19:22 +00:00
renovate[bot] 8ecdaf3c7a chore(deps): Update non-major dependencies to v66.6.4 (#1453)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 17:41:32 +00:00
Copilot bb2f9e02b5 fix: env var with empty string default treated as required in template deploy (#1451)
* Initial plan

* 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-03-03 21:01:50 +08:00
renovate[bot] bbe0e9c4b4 chore(deps): Update module github.com/samber/lo to v1.53.0 (#1447)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-02 22:18:51 +00:00
github-actions[bot] 9925a51cde New Crowdin translations by GitHub Action (#1446)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-03-02 22:42:27 +08:00
h2zi cb29a24751 chore(l10n): update pot files 2026-03-02 14:40:59 +00:00
耗子 5f62c6dd29 Merge remote-tracking branch 'origin/main' 2026-03-02 22:38:24 +08:00
耗子 8647c10f3f fix: session及计划任务优化 2026-03-02 22:38:19 +08:00
renovate[bot] a04ac69392 chore(deps): Update non-major dependencies (#1445)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-02 19:49:51 +08:00
耗子 b011191d4c Merge remote-tracking branch 'origin/main' 2026-03-02 19:31:00 +08:00
耗子 e00c1eb1f7 fix: 文件管理path可能未定义的问题 2026-03-02 19:30:49 +08:00
renovate[bot] 597c374443 chore(deps): Lock file maintenance (#1444)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-02 09:29:50 +00:00
renovate[bot] 0a34d5f631 chore(deps): Update non-major dependencies (#1443)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-02 02:17:51 +00:00
renovate[bot] 36b8500101 chore(deps): Update non-major dependencies (#1442)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-01 16:06:01 +08:00
renovate[bot] 5044060661 chore(deps): Update module github.com/shirou/gopsutil/v4 to v4.26.2 (#1440)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-01 15:58:58 +08:00
Catsayer d96d83a5b7 合并拉取请求 #1441
*  refactor(service): 抽取环境 CLI 软链接公共能力并统一 SetCli 实现

* fix: 优化代码
2026-03-01 15:58:29 +08:00
github-actions[bot] 99fe0b2719 New Crowdin translations by GitHub Action (#1439)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-28 23:10:15 +08:00
h2zi 06bd74be8c chore(l10n): update pot files 2026-02-28 15:08:27 +00:00
renovate[bot] fe0d709370 chore(deps): Update non-major dependencies (#1438)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-28 23:07:19 +08:00
耗子 e0f5704275 fix: 竞态条件导致通行密钥无法登录 2026-02-28 23:04:28 +08:00
renovate[bot] 4eb80c8720 chore(deps): Update dependency @types/node to v24.11.0 (#1437)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-28 16:57:21 +08:00
耗子 d8213d65a0 fix: lint 2026-02-28 00:58:24 +08:00
github-actions[bot] f00ceb9d10 New Crowdin translations by GitHub Action (#1436)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-28 00:42:41 +08:00
h2zi 8f96d53e09 chore(l10n): update pot files 2026-02-27 16:20:14 +00:00
耗子 22cfddf43b fix: postgresql 2026-02-27 19:19:17 +08:00
耗子 4479e5b9e9 Merge remote-tracking branch 'origin/main' 2026-02-27 17:29:36 +08:00
耗子 dbe4ea9909 feat: 兼容418错误 2026-02-27 17:29:24 +08:00
renovate[bot] 9de68220ec chore(deps): Update dependency @types/node to v24.10.15 (#1434)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-27 05:44:56 +08:00
renovate[bot] 83de33d1ab chore(deps): Update GitHub Artifact Actions (#1435)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-27 05:44:46 +08:00
耗子 d8515f9883 feat: 监控采集间隔支持设置 2026-02-27 00:11:07 +08:00
h2zi 4ff7ce18cd chore: update mocks 2026-02-26 15:51:13 +00:00
耗子 71e259ba32 feat: 支持通行密钥登录 (#1432)
* feat: 支持通行密钥登录

* fix: 修复AI评论

* Update pkg/passkey/passkey.go

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: lint

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-02-26 23:50:18 +08:00
renovate[bot] 4873103904 chore(deps): Update pnpm to v10.30.3 (#1431)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-26 14:57:57 +00:00
耗子 55655d99cf Revert "fix: lint"
This reverts commit a67ec47c7e.
2026-02-26 16:18:05 +08:00
github-actions[bot] 830d4dca7a New Crowdin translations by GitHub Action (#1430)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-26 16:15:05 +08:00
devhaozi 2a89e060c3 chore(l10n): update pot files 2026-02-26 08:02:42 +00:00
耗子 6900092723 Merge remote-tracking branch 'origin/main' 2026-02-26 16:00:31 +08:00
耗子 8a79d90e78 fix: lint 2026-02-26 16:00:28 +08:00
renovate[bot] f5e5b687b8 chore(deps): Update non-major dependencies to v66.6.2 (#1429)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-26 15:54:58 +08:00
renovate[bot] f5628cdf4a chore(deps): Update non-major dependencies (#1428)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-26 15:50:55 +08:00
耗子 44f569b478 feat: 扫描感知支持自动屏蔽IP 2026-02-26 04:04:02 +08:00
耗子 38ec8b63d2 feat: 扫描感知支持自动屏蔽IP 2026-02-26 03:58:06 +08:00
耗子 fad297f33d fix: PostgreSQL备份清理 2026-02-26 00:18:02 +08:00
耗子 0c8a8cd382 fix: lint 2026-02-25 23:08:47 +08:00
耗子 3089a847a7 fix: 编辑数据库服务器时 Redis 类型隐藏用户名输入框
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:51:36 +08:00
renovate[bot] 4b1839169a chore(deps): Update non-major dependencies to v66.6.1 (#1426)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-25 22:51:01 +08:00
github-actions[bot] 2aeec54189 New Crowdin translations by GitHub Action (#1425)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-25 21:44:24 +08:00
h2zi ce19d4ff14 chore(l10n): update pot files 2026-02-25 13:42:23 +00:00
耗子 a67ec47c7e fix: lint 2026-02-25 21:41:01 +08:00
耗子 7fc0e0f4af fix: Docker socket 路径错误和 Redis 密码管理问题 (#1424)
1. Docker 默认 socket 路径不应拼接面板根目录前缀
2. Redis Load() 正则缺少多行标记导致无法提取密码
3. Redis 修改 requirepass 后未同步到数据库服务器记录

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 21:32:26 +08:00
耗子 bfd4896470 Merge remote-tracking branch 'origin/main' 2026-02-25 18:08:54 +08:00
耗子 12b8a4a03b chore: run go generate ./... 2026-02-25 18:08:32 +08:00
h2zi d4c853db01 chore: update mocks 2026-02-25 10:07:42 +00:00
半岛的蒟蒻bddjr e947259b86 fix: go.mod module github.com/acepanel/panel/v3 (#1423) 2026-02-25 18:07:00 +08:00
renovate[bot] c7cde220d4 chore(deps): Update non-major dependencies (#1422)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-24 21:41:34 +08:00
耗子 b4e7c924a9 feat: 默认禁用扫描感知 2026-02-24 21:01:10 +08:00
耗子 6401f2ddd8 fix: postgresql数据库无法创建备份任务 2026-02-23 23:19:00 +08:00
耗子 735258c212 feat: 调整排序 2026-02-23 22:26:56 +08:00
github-actions[bot] 358ee8d19c New Crowdin translations by GitHub Action (#1421)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-23 21:33:06 +08:00
h2zi dd5a9d3e72 chore(l10n): update pot files 2026-02-23 13:31:55 +00:00
耗子 e92bc5309b feat: 设置添加5分钟缓存 2026-02-23 20:20:53 +08:00
renovate[bot] bb3cf83b59 chore(deps): Lock file maintenance (#1420)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-23 04:41:43 +00:00
renovate[bot] 82880f0ecc chore(deps): Update module github.com/bddjr/hlfhr to v1.4.3 (#1419)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-23 05:27:15 +08:00
github-actions[bot] 7d174655ee New Crowdin translations by GitHub Action (#1418)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-22 21:43:44 +08:00
h2zi ab27b6d1bc chore(l10n): update pot files 2026-02-22 13:13:06 +00:00
耗子 308e7c685c feat: 统计添加ISP数据 2026-02-22 21:04:16 +08:00
耗子 7de339b3f7 feat: 统计添加ISP数据 2026-02-22 20:47:37 +08:00
耗子 3445d2396e feat: 数据库优化 2026-02-22 20:06:11 +08:00
h2zi 4130b8b314 chore: update mocks 2026-02-22 11:59:00 +00:00
耗子 d216a72ac9 feat: 数据库优化 2026-02-22 19:58:20 +08:00
耗子 20c1cb842c fix: lint 2026-02-22 18:30:37 +08:00
耗子 7b94af32ea fix: lint 2026-02-22 16:41:53 +08:00
耗子 1d7f0a2f22 feat: 优化IP解析性能 2026-02-22 07:39:35 +08:00
耗子 6c589d0350 revert: 网站统计默认值调整 2026-02-22 00:35:29 +08:00
github-actions[bot] a0c6422a69 New Crowdin translations by GitHub Action (#1416)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-22 00:31:07 +08:00
renovate[bot] c79e72a159 chore(deps): Update goreleaser/goreleaser-action action to v7 (#1417)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-22 00:30:58 +08:00
h2zi 2791add4ef chore(l10n): update pot files 2026-02-21 16:20:47 +00:00
耗子 f064eae300 feat: 调整修复方法 2026-02-22 00:16:01 +08:00
耗子 b86645bd58 feat: 拆分统计数据库以优化性能 2026-02-21 23:52:16 +08:00
耗子 04b4352abd feat: 优化网站统计性能 2026-02-21 23:05:14 +08:00
耗子 42055275ca feat: 优化网站统计数据压力 2026-02-21 23:01:01 +08:00
耗子 8facb097e1 Merge remote-tracking branch 'origin/main' 2026-02-21 22:13:02 +08:00
耗子 ec1e069b84 feat: 优化网站统计数据压力 2026-02-21 22:12:59 +08:00
renovate[bot] f1d5122a13 chore(deps): Update module github.com/valyala/fastjson to v1.6.10 (#1415)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-21 21:46:55 +08:00
renovate[bot] c5b4fb18d8 chore(deps): Update dependency vue-tsc to v3.2.5 (#1414)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-21 16:22:43 +08:00
renovate[bot] c507eff0df chore(deps): Update module github.com/mholt/acmez/v3 to v3.1.6 (#1412)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-21 06:17:39 +08:00
renovate[bot] e29a98ecb2 chore(deps): Update non-major dependencies (#1411)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-20 13:42:58 +00:00
耗子 2eb7aa207b fix: lint 2026-02-20 05:19:39 +08:00
github-actions[bot] e5ed1bfd47 New Crowdin translations by GitHub Action (#1410)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-20 05:16:36 +08:00
耗子 472aaada36 Merge remote-tracking branch 'origin/main' 2026-02-20 05:13:03 +08:00
耗子 31ed50fad0 fix: lint 2026-02-20 05:12:59 +08:00
renovate[bot] 540e2232fd chore(deps): Update dependency vue-router to v5.0.3 (#1409)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-20 05:07:50 +08:00
h2zi 06ff48fd3f chore(l10n): update pot files 2026-02-19 21:05:33 +00:00
耗子 f6ac168081 fix: lint 2026-02-20 05:03:20 +08:00
耗子 4004e61b55 fix: lint 2026-02-20 05:02:35 +08:00
耗子 a62cff7a73 feat: 网站统计添加入站实时数据 2026-02-20 04:57:14 +08:00
耗子 8bf30ee586 Merge remote-tracking branch 'origin/main' 2026-02-20 04:54:05 +08:00
耗子 57340482cc feat: 网站统计添加刷新按钮 2026-02-20 04:54:00 +08:00
h2zi a233efa6db chore(l10n): update pot files 2026-02-19 20:49:09 +00:00
耗子 1a907ce8a8 Merge remote-tracking branch 'origin/main' 2026-02-20 04:47:55 +08:00
耗子 64b671914c fix: lint 2026-02-20 04:47:47 +08:00
h2zi cb2180fbdb chore: update mocks 2026-02-19 20:47:41 +00:00
耗子 67a4b2f8f1 feat: 网站统计优化 2026-02-20 04:46:58 +08:00
耗子 b39785fc34 feat: 网站统计优化 2026-02-20 04:29:40 +08:00
耗子 b9cb492847 fix: Geo区域模式下数据重复 2026-02-20 03:42:19 +08:00
github-actions[bot] 0109eb9c52 New Crowdin translations by GitHub Action (#1408)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-20 03:16:35 +08:00
h2zi 959243690d chore(l10n): update pot files 2026-02-19 19:14:42 +00:00
耗子 6809969b25 feat: 新增IP库保存下载 2026-02-20 03:13:32 +08:00
renovate[bot] 87295af790 chore(deps): Update module github.com/libtnb/sqlite to v1 (#1407)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-20 03:04:14 +08:00
github-actions[bot] 370e5d9a20 New Crowdin translations by GitHub Action (#1406)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-20 02:56:56 +08:00
h2zi b495017dab chore(l10n): update pot files 2026-02-19 18:38:22 +00:00
耗子 3eeff1575c Merge remote-tracking branch 'origin/main' 2026-02-20 02:37:21 +08:00
耗子 0124481f6d fix: lint 2026-02-20 02:37:12 +08:00
h2zi d569de3e67 chore(l10n): update pot files 2026-02-19 18:24:50 +00:00
耗子 c64cf8ac18 feat: 新增IP库可选自动更新 2026-02-20 02:22:47 +08:00
h2zi 6fc7b34882 chore: update mocks 2026-02-19 16:31:54 +00:00
github-actions[bot] 016b06bfce New Crowdin translations by GitHub Action (#1405)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-20 00:31:00 +08:00
devhaozi 08c3623526 chore(l10n): update pot files 2026-02-19 16:05:17 +00:00
耗子 632bdd0b1a Merge remote-tracking branch 'origin/main' 2026-02-20 00:02:30 +08:00
耗子 2d8f014d20 feat: 新增IP地理位置显示 2026-02-20 00:02:26 +08:00
renovate[bot] 6eac8176b3 chore(deps): Update module github.com/valyala/fastjson to v1.6.9 (#1404)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-19 22:08:36 +08:00
耗子 e77863d469 fix: run go fix ./... 2026-02-19 22:08:16 +08:00
github-actions[bot] 95c54fd17a New Crowdin translations by GitHub Action (#1402)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-19 15:50:43 +08:00
devhaozi 527e49fde3 chore(l10n): update pot files 2026-02-19 07:49:58 +00:00
耗子 ced99702a9 Merge remote-tracking branch 'origin/main' 2026-02-19 15:48:53 +08:00
耗子 7190cc7947 feat: 网站统计默认启用 2026-02-19 15:48:50 +08:00
github-actions[bot] 8891420814 New Crowdin translations by GitHub Action (#1401)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-19 15:40:33 +08:00
devhaozi 81ac5d1b8a chore(l10n): update pot files 2026-02-19 07:33:16 +00:00
dependabot[bot] fe3405c4e2 chore(deps): bump filippo.io/edwards25519 from 1.1.0 to 1.1.1 (#1400)
Bumps [filippo.io/edwards25519](https://github.com/FiloSottile/edwards25519) from 1.1.0 to 1.1.1.
- [Commits](https://github.com/FiloSottile/edwards25519/compare/v1.1.0...v1.1.1)

---
updated-dependencies:
- dependency-name: filippo.io/edwards25519
  dependency-version: 1.1.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-19 15:31:29 +08:00
renovate[bot] 90cd78dd1a chore(deps): Update dependency @rushstack/eslint-patch to v1.16.0 (#1399)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-19 06:34:06 +00:00
耗子 cc895d4df7 fix: 网站统计的一系列问题 2026-02-19 06:21:53 +08:00
耗子 da70928767 fix: 网站统计的一系列问题 2026-02-19 06:09:19 +08:00
耗子 cfb3ed75bd fix: 提高ipv6拓展头解析层数 2026-02-19 05:50:09 +08:00
耗子 1e85a141f1 feat: 重写汇编 2026-02-19 05:45:30 +08:00
github-actions[bot] 6418d88f22 New Crowdin translations by GitHub Action (#1398)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-19 04:48:35 +08:00
devhaozi 013389af54 chore(l10n): update pot files 2026-02-18 20:42:28 +00:00
耗子 f0121d7240 fix: lint 2026-02-19 04:41:15 +08:00
耗子 1133468ddf Merge remote-tracking branch 'origin/main' 2026-02-19 04:32:47 +08:00
耗子 701d3e96db feat: 调整网站统计的位置 2026-02-19 04:32:37 +08:00
devhaozi 49268d3b08 chore(l10n): update pot files 2026-02-18 20:32:13 +00:00
耗子 95d38b6c02 feat: 添加一些内部数据显示 2026-02-19 04:24:34 +08:00
github-actions[bot] 9b4c6b2319 New Crowdin translations by GitHub Action (#1397)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-19 03:32:14 +08:00
耗子 8c09c790a5 Merge remote-tracking branch 'origin/main' 2026-02-19 03:30:41 +08:00
耗子 b5232d47b1 fix: lint 2026-02-19 03:30:37 +08:00
devhaozi 1f28186581 chore(l10n): update pot files 2026-02-18 19:28:03 +00:00
耗子 537f1430de fix: lint 2026-02-19 03:24:53 +08:00
耗子 f009b24ad9 fix: lint 2026-02-19 03:24:23 +08:00
耗子 93733b5692 fix: lint 2026-02-19 03:22:19 +08:00
耗子 2af6469a12 feat: 优化json解析性能 2026-02-19 03:06:48 +08:00
耗子 19a3a92216 fix: 修复网站统计一些逻辑问题 2026-02-19 03:00:19 +08:00
github-actions[bot] 16590d9b17 New Crowdin translations by GitHub Action (#1396)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-18 20:38:19 +08:00
devhaozi eaae15c092 chore(l10n): update pot files 2026-02-18 12:37:33 +00:00
github-actions[bot] b9d55f51b8 New Crowdin translations by GitHub Action (#1395)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-18 20:24:40 +08:00
devhaozi 6c9e43e701 chore(l10n): update pot files 2026-02-18 12:20:48 +00:00
耗子 8952b7bdaf fix: lint 2026-02-18 20:18:45 +08:00
github-actions[bot] b9b14f55c1 New Crowdin translations by GitHub Action (#1394)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-18 20:08:12 +08:00
耗子 6e2dd1b91f Merge remote-tracking branch 'origin/main' 2026-02-18 20:01:36 +08:00
耗子 4219c8d57e fix: lint 2026-02-18 20:01:32 +08:00
devhaozi dbaa68357e chore(l10n): update pot files 2026-02-18 12:00:33 +00:00
耗子 971b130061 feat: 完善网站统计 2026-02-18 19:58:38 +08:00
耗子 003da4ce2d feat: 完善网站统计 2026-02-18 19:57:32 +08:00
耗子 433db4cbd6 Merge remote-tracking branch 'origin/main' 2026-02-18 19:50:37 +08:00
耗子 2b48fd5fe3 feat: 完善网站统计 2026-02-18 19:50:33 +08:00
h2zi 4c468e9aca chore: update mocks 2026-02-18 11:19:40 +00:00
耗子 7d368536d0 feat: 完善网站统计 2026-02-18 19:18:50 +08:00
h2zi 1403c65d37 chore: update mocks 2026-02-18 10:18:09 +00:00
耗子 fa30728753 feat: 网站统计 2026-02-18 18:17:16 +08:00
耗子 aa2e54f3f1 fix: 将真实 IP 来源配置从动态输入改为文本域
替换 n-dynamic-input 为 textarea,支持批量粘贴多行 IP/CIDR,
提升 CDN 等大量 IP 的配置体验。

Closes #1393

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 16:00:26 +08:00
github-actions[bot] 20e3484767 New Crowdin translations by GitHub Action (#1392)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-18 05:04:20 +08:00
devhaozi f80b954993 chore(l10n): update pot files 2026-02-17 21:00:33 +00:00
github-actions[bot] 17efbab5dd New Crowdin translations by GitHub Action (#1391)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-18 04:57:03 +08:00
devhaozi 7b1d647313 chore(l10n): update pot files 2026-02-17 20:53:32 +00:00
耗子 6cf84f148b feat: 添加扫描感知禁用提醒 2026-02-18 04:52:07 +08:00
github-actions[bot] 8abae3bb4e New Crowdin translations by GitHub Action (#1390)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-18 04:41:36 +08:00
耗子 e95f904e87 Merge remote-tracking branch 'origin/main' 2026-02-18 04:41:28 +08:00
耗子 c67f2b7df0 fix: lint 2026-02-18 04:41:23 +08:00
devhaozi e6e358bd14 chore(l10n): update pot files 2026-02-17 20:41:09 +00:00
耗子 bd9ba1c552 fix: lint 2026-02-18 04:39:50 +08:00
github-actions[bot] 4b7cf9e981 New Crowdin translations by GitHub Action (#1388)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-18 04:36:35 +08:00
devhaozi b75abff1c1 chore(l10n): update pot files 2026-02-17 20:34:35 +00:00
耗子 e06917003d Merge remote-tracking branch 'origin/main' 2026-02-18 04:33:29 +08:00
耗子 375cb73e34 fix: lint 2026-02-18 04:33:19 +08:00
devhaozi 1213c39fb2 chore(l10n): update pot files 2026-02-17 20:05:54 +00:00
耗子 397f84a921 Merge remote-tracking branch 'origin/main' 2026-02-18 04:04:10 +08:00
耗子 96c03c4c5a fix: lint 2026-02-18 04:04:06 +08:00
devhaozi 5407c53619 chore: update mocks 2026-02-17 20:03:22 +00:00
耗子 37c155acb5 feat: 优化扫描感知支持搜索 2026-02-18 04:01:27 +08:00
renovate[bot] 17adaa1c8c chore(deps): Update pnpm to v10.30.0 (#1387)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-18 03:42:30 +08:00
耗子 65e390a256 feat: 计划任务支持进程锁 2026-02-18 03:42:15 +08:00
耗子 67642aa475 feat: 添加SMART和RAID状态查看 2026-02-18 03:28:06 +08:00
devhaozi da9d95284c chore: update mocks 2026-02-17 18:53:59 +00:00
耗子 9a1555c48a feat: 防火墙扫描感知功能 & 首页容器统计 & lint 修复
- 新增 eBPF 扫描感知:TC ingress 检测 SYN/UDP 扫描,端口白名单过滤
- 新增首页容器数量统计卡片,未安装容器环境时自动隐藏
- 扫描概览页:统计卡片、趋势图、Top IP/端口、事件列表
- 扫描设置页:启用开关、保留天数、网卡选择
- 统计对比改为同长度上一周期,替代固定昨日对比
- eBPF ports map MaxEntries 扩展至 65535 覆盖全端口
- 修复 WriteString(fmt.Sprintf()) lint 问题,改用 fmt.Fprintf
- 修复 database_redis.go 非常量格式字符串和 govet 警告

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 02:52:32 +08:00
耗子 d0cd178dfe fix: lint 2026-02-17 23:45:01 +08:00
耗子 acacf5d9ce Merge remote-tracking branch 'origin/main' 2026-02-17 23:38:28 +08:00
耗子 e36bb8f429 fix: lint 2026-02-17 23:38:19 +08:00
devhaozi c731125534 chore: update mocks 2026-02-17 15:31:28 +00:00
耗子 1cf3218fde feat: 支持管理redis数据库 2026-02-17 23:30:42 +08:00
耗子 b7be21c851 feat: 防火墙支持显示端口使用进程 2026-02-17 22:48:16 +08:00
耗子 2f49d0baa6 feat: 防火墙支持显示端口使用进程 2026-02-17 22:48:00 +08:00
耗子 b541a56bf9 feat: 首页新增容器数显示 2026-02-17 22:30:24 +08:00
耗子 bb722ccf3d feat: 支持自定义容器socket路径 2026-02-17 22:13:26 +08:00
耗子 f19c0d9c00 feat: 计划任务支持同步时间类型 2026-02-17 21:44:35 +08:00
耗子 e9affee707 feat: 计划任务支持访问URL类型 2026-02-17 21:39:27 +08:00
耗子 ba6c09ed70 feat: 计划任务支持批量删除 2026-02-17 21:15:03 +08:00
耗子 23e2df1ed9 fix: 网站日志轮转 2026-02-17 20:57:59 +08:00
github-actions[bot] 0bafb5b8f5 New Crowdin translations by GitHub Action (#1386)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-17 20:41:25 +08:00
devhaozi 98195d1838 chore(l10n): update pot files 2026-02-17 12:33:49 +00:00
耗子 2a143513d0 Merge remote-tracking branch 'origin/main' 2026-02-17 20:32:10 +08:00
耗子 432fcdd0a0 feat: 优化网站名的描述 2026-02-17 20:32:06 +08:00
devhaozi 6c703bf94d chore: update mocks 2026-02-17 12:28:53 +00:00
耗子 b439973995 feat: 计划任务优化 2026-02-17 20:28:07 +08:00
耗子 5c74b7eb71 fix: lint 2026-02-17 18:42:17 +08:00
耗子 edc88484ac fix: lint 2026-02-17 18:30:01 +08:00
耗子 51e4cdb095 fix: 日志目录 2026-02-17 18:20:24 +08:00
renovate[bot] e0c5f425cb chore(deps): Update dependency @vue/eslint-config-typescript to v14.7.0 (#1385)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-17 17:47:43 +08:00
耗子 2f2f17b1c7 refactor: 重构任务队列 2026-02-17 17:47:25 +08:00
devhaozi 2536dfe933 chore: update mocks 2026-02-17 09:17:03 +00:00
耗子 e95c4623c5 feat: 优化日志保存,close #1382 2026-02-17 17:16:17 +08:00
耗子 3d46a284e6 chore: 更新 2026-02-17 16:50:19 +08:00
耗子 ed33e090ed fix: 保存网站可能重复创建证书 2026-02-17 16:29:48 +08:00
耗子 0cab838499 fix: 保存网站可能重复创建证书 2026-02-17 16:01:46 +08:00
renovate[bot] 4f7b6b1c38 chore(deps): Lock file maintenance (#1384)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-16 05:51:32 +00:00
耗子 230894d618 Merge remote-tracking branch 'origin/main' 2026-02-15 06:33:23 +08:00
耗子 ec8b3dbb10 ci: 移除自动锁定 2026-02-15 06:33:19 +08:00
renovate[bot] 4a899f1c0d chore(deps): Update module github.com/expr-lang/expr to v1.17.8 (#1383)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-14 20:57:24 +00:00
renovate[bot] 49a5e36c30 chore(deps): Update dependency eslint-plugin-vue to v10.8.0 (#1381)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-14 05:01:28 +00:00
耗子 47d945710b fix: lint 2026-02-14 05:23:50 +08:00
耗子 3ab58fc817 fix: lint 2026-02-14 00:27:03 +08:00
耗子 36a6617c68 fix: lint 2026-02-14 00:22:30 +08:00
耗子 b8d2051e64 feat: update go1.26 2026-02-14 00:16:52 +08:00
github-actions[bot] da7c93308b New Crowdin translations by GitHub Action (#1380)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-13 20:30:21 +08:00
devhaozi 017c0ed9b7 chore(l10n): update pot files 2026-02-13 12:29:30 +00:00
耗子 0930007d24 fix: 登录 2026-02-13 20:28:11 +08:00
耗子 8285b8dee8 fix: lint 2026-02-13 20:10:13 +08:00
github-actions[bot] c52d42a2de New Crowdin translations by GitHub Action (#1379)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-13 19:37:15 +08:00
devhaozi 5d07932226 chore(l10n): update pot files 2026-02-13 11:36:32 +00:00
耗子 89ad4d83f3 feat: 兼容不支持br的情况 2026-02-13 19:35:03 +08:00
renovate[bot] cce1102c90 chore(deps): Update module github.com/libdns/huaweicloud to v1.0.1 (#1378)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-13 19:21:26 +08:00
耗子 f67a3e3a6f feat: nginx添加512k zone 2026-02-13 19:16:36 +08:00
耗子 7e09e20254 fix: lint 2026-02-13 17:35:13 +08:00
耗子 6fd6c958cf feat: 优化recoverer中间件 2026-02-13 03:58:22 +08:00
耗子 7fe0c3ce3c fix: 容器镜像可能panic 2026-02-13 03:50:47 +08:00
耗子 086d0eb601 fix: 容器卷可能panic 2026-02-13 03:49:45 +08:00
耗子 d7b7cf0b57 fix: 优化密码管理器自动提交 2026-02-13 03:48:28 +08:00
耗子 78a1a43f2f feat: 倒序迁移,防止打乱顺序 2026-02-13 00:55:31 +08:00
耗子 f9c1510d8a fix: lint 2026-02-13 00:41:02 +08:00
耗子 0ea2117d96 fix: 优化按钮顺序 2026-02-12 23:37:11 +08:00
renovate[bot] 2b055d6e88 chore(deps): Lock file maintenance (#1377)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-12 21:20:03 +08:00
耗子 227a7b19f3 fix: lint 2026-02-12 19:13:10 +08:00
耗子 eda6d92d66 feat: 优化更新页 2026-02-12 19:05:50 +08:00
github-actions[bot] c2441d2081 New Crowdin translations by GitHub Action (#1376)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-12 17:41:42 +08:00
devhaozi 47123317a2 chore(l10n): update pot files 2026-02-12 09:39:08 +00:00
耗子 68125fb1e6 fix: lint 2026-02-12 07:52:59 +08:00
耗子 acecdf7f48 Merge remote-tracking branch 'origin/main' 2026-02-12 07:47:14 +08:00
耗子 021646727b fix: 移除多余的handler 2026-02-12 07:47:10 +08:00
renovate[bot] 4e6d46d1e9 chore(deps): Update module github.com/libtnb/acmez/v3 to v3.0.0-20260211214515-0d13ab154236 (#1374)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-12 07:45:05 +08:00
耗子 1265ac5b34 feat: 移除自定义压缩 2026-02-12 07:43:46 +08:00
耗子 94704deb1b feat: sqlite测试 2026-02-12 07:31:47 +08:00
耗子 932f48044b feat: sqlite测试 2026-02-12 07:28:00 +08:00
耗子 0c89d77221 feat: 优化内存占用 2026-02-12 03:34:30 +08:00
github-actions[bot] e0ccab8eb7 New Crowdin translations by GitHub Action (#1371)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-12 02:23:19 +08:00
renovate[bot] 3aef5140e2 chore(deps): Update dependency marked to v17.0.2 (#1373)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-12 02:13:00 +08:00
devhaozi 10ca20f3aa chore(l10n): update pot files 2026-02-11 17:56:40 +00:00
耗子 89a55ca518 fix: lint 2026-02-12 01:33:12 +08:00
耗子 d743bef697 fix: ufw 2026-02-12 00:28:24 +08:00
耗子 50fe450d64 fix: ufw 2026-02-12 00:24:52 +08:00
耗子 74259f833c fix: 添加单测 2026-02-11 23:57:50 +08:00
耗子 ca8e36351e fix: 合并防火墙规则 2026-02-11 23:30:38 +08:00
耗子 d01a2374f5 fix: 合并防火墙规则 2026-02-11 23:21:33 +08:00
耗子 f4885c55f9 Merge remote-tracking branch 'origin/main' 2026-02-11 23:16:06 +08:00
耗子 7e8de43776 fix: ufw 2026-02-11 23:16:02 +08:00
renovate[bot] 4ded231b1c chore(deps): Update pnpm to v10.29.3 (#1372)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-11 23:06:27 +08:00
耗子 20a0080b1e fix: build 2026-02-11 23:03:45 +08:00
耗子 d2b30f211f chore: 移除h3支持,QoS麻了 2026-02-11 22:39:33 +08:00
耗子 83311409ae fix: lint 2026-02-11 21:59:29 +08:00
devhaozi 515ed686fb chore(l10n): update pot files 2026-02-11 13:20:08 +00:00
耗子 975c0d7321 fix: lint 2026-02-11 21:05:36 +08:00
耗子 0bdf1d6058 feat: UI优化 2026-02-11 21:02:58 +08:00
耗子 c5d62a46ab feat: UI优化 2026-02-11 20:38:08 +08:00
耗子 2dd6f0d9f0 feat: 文件管理支持多标签页 2026-02-11 20:12:10 +08:00
耗子 de5d76bff6 feat: 添加启用ipv6转发 2026-02-11 19:37:20 +08:00
耗子 e3742e61d8 Merge remote-tracking branch 'origin/main' 2026-02-11 19:28:55 +08:00
耗子 72c31a14b2 feat: 防火墙,SSH日志, close #1366 #1364 2026-02-11 19:28:51 +08:00
devhaozi 205d95a591 chore: update mocks 2026-02-11 11:13:50 +00:00
耗子 4df5038f91 feat: 数据库管理优化, close #1365 2026-02-11 19:12:43 +08:00
github-actions[bot] da023c5faa New Crowdin translations by GitHub Action (#1370)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-11 03:33:13 +08:00
devhaozi e6c13330d8 chore(l10n): update pot files 2026-02-10 19:26:21 +00:00
github-actions[bot] bda95465f8 New Crowdin translations by GitHub Action (#1369)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-11 03:22:56 +08:00
devhaozi c3074249f0 chore(l10n): update pot files 2026-02-10 19:08:29 +00:00
耗子 6388bcfa37 fix: migrate 2026-02-11 03:05:27 +08:00
耗子 281f35dfa1 fix: migrate 2026-02-11 03:01:49 +08:00
耗子 5d189172ca fix: migrate 2026-02-11 02:44:32 +08:00
耗子 aa23066fe5 feat: update resty to v3 2026-02-11 02:20:00 +08:00
耗子 c3313845a6 fix: lint 2026-02-11 01:44:15 +08:00
耗子 85f86f51db fix: 添加数据库用户迁移 2026-02-11 01:28:38 +08:00
耗子 374996498f fix: lint 2026-02-11 01:17:05 +08:00
耗子 a17b9d4936 Merge remote-tracking branch 'origin/main' 2026-02-10 23:00:24 +08:00
耗子 c226091c6e fix: lint 2026-02-10 23:00:20 +08:00
renovate[bot] 8902dade39 chore(deps): Update dependency @types/node to v24.10.13 (#1368)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-10 22:56:54 +08:00
renovate[bot] 5f9696f214 chore(deps): Update module github.com/lib/pq to v1.11.2 (#1363)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-10 22:52:00 +08:00
耗子 7155e7c7cb feat: 首页添加Top5进程显示 2026-02-10 22:35:29 +08:00
耗子 c56b4e00d9 feat: 监控添加Top进程记录 2026-02-10 21:55:11 +08:00
耗子 f7bf8b01cc feat: 工具箱新增网络视图 2026-02-10 21:07:05 +08:00
耗子 b7e1bd8824 feat: 新增便签小工具 2026-02-10 20:44:51 +08:00
耗子 d9b6813060 feat: 支持dotnet 2026-02-10 20:22:08 +08:00
耗子 10a816f7f7 feat: 支持dotnet 2026-02-10 20:08:26 +08:00
耗子 68e4016538 fix: lint 2026-02-10 18:08:18 +08:00
renovate[bot] 338b9cc73b chore(deps): Update dependency @vueuse/core to v14.2.1 (#1361)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-10 08:57:55 +00:00
耗子 b27ced4b92 fix: lint 2026-02-10 03:32:57 +08:00
github-actions[bot] 36fb9e76fc New Crowdin translations by GitHub Action (#1360)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-02-10 03:25:07 +08:00
devhaozi 5f7df1ef9a chore(l10n): update pot files 2026-02-09 19:12:46 +00:00
Copilot 7da411b2f3 feat: 工具箱-迁移功能 (Toolbox Migration) (#1358)
* Initial plan

* feat: add migration backend service, routes and Wire injection

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

* feat: add migration frontend - API, WebSocket, MigrationView and tab integration

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

* refactor: move migration types to pkg/types, fix code review issues

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

* chore: final cleanup

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

* feat: add refresh button on migration pre-check step

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

* feat: add SSH key exchange for rsync authentication

Generate temporary ED25519 key pair before migration, deploy public
key to remote via POST /api/toolbox_migration/ssh_key, use -i flag
in all rsync/ssh commands, cleanup key from both sides after migration.

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

* fix: lint

* fix: lint

* fix: align migration code with actual codebase patterns

- Project service name: use project name directly (not ace-project-xxx)
- MySQL dump: use MYSQL_PWD env var (not -p flag), remove --socket
- PostgreSQL dump: use PGPASSWORD env var with pg_dump -h 127.0.0.1 -U postgres
- PostgreSQL import: use PGPASSWORD with psql -h 127.0.0.1 -U postgres
- MySQL import: use MYSQL_PWD on remote
- Website create API: use correct listens/domains/type fields
- Project create API: use root_dir/exec_start/user fields
- Database create API: pass server_id properly
- Site directory: use filepath.Join(app.Root, "sites") instead of hardcoded path
- maskPassword: handle MYSQL_PWD/PGPASSWORD env var format

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

* fix: add PHP version and proxy backend to website create API body

PHP sites now include the php version number, and reverse proxy sites
include the backend address from Proxies[0].Pass. This matches the
WebsiteCreate validation rules (requiredIf:Type,php / requiredIf:Type,proxy).

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

* refactor: replace map[string]any with typed request structs for all remote API calls

Use request.WebsiteCreate, request.DatabaseCreate, request.ProjectCreate,
and request.ToolboxMigrationSSHKey instead of ad-hoc map[string]any.
This ensures all required/validated fields are properly typed and present.

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

* fix: website list empty and database migration items stuck in running status

1. Website list: pass "all" to websiteRepo.List() instead of "" — empty
   string was treated as a type filter matching nothing.

2. Database status: result was stored with name "aaa (mysql)" but
   failResult/succeedResult looked up by "aaa" — name mismatch meant
   status was never updated from "running". Use displayName consistently.

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

* fix: use acepanel CLI to get remote DB passwords, show success text in results

1. Remote DB import: use `acepanel setting get mysql_root_password` and
   `acepanel setting get postgres_password` on the remote server via SSH
   instead of guessing file paths. The $() is escaped so it executes on
   the remote side, not locally.

2. Migration results: successful items now show "Migration succeeded"
   text alongside the completion timestamp instead of a bare date.

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

* fix: lint

* refactor: 不基于rsync的迁移

* 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-02-10 02:51:03 +08:00
renovate[bot] 3c6c5aa31e chore(deps): Update non-major dependencies (#1359)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-10 02:50:14 +08:00
耗子 5c05d0d81b feat: 添加 rsync 显示 2026-02-09 20:15:42 +08:00
Copilot 0724a042ef feat: HTTP/3 (QUIC) 支持 + TLS 证书热重载 (#1356)
* Initial plan

* feat: 添加 HTTP/3 (QUIC) 协议支持,TLS 启用时自动启用

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

* refactor: 优化证书路径复用,修正注释和依赖标记

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

* feat: 添加证书热重载,TLS 握手时自动检测并加载变更的证书文件

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

* refactor: 使用 fsnotify 替代每次握手 os.Stat 实现证书热重载

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

* fix: lint

* fix: lint

* fix: 使用 wire 工具重新生成 wire_gen.go 替代手动编辑

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

* feat: 通过 Wire 注入 *tlscert.Reloader,优雅关闭时正确释放资源

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

* fix: lint

* 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-02-09 20:10:13 +08:00
耗子 261d87d6df feat: 优化远程文件下载 2026-02-09 17:28:25 +08:00
renovate[bot] f043f65f95 chore(deps): Update non-major dependencies 2026-02-09 08:57:05 +00:00
renovate[bot] 0e56569dab chore(deps): Update pnpm to v10.29.2 (#1354)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-09 16:54:39 +08:00
renovate[bot] 8ccefa6bff chore(deps): Lock file maintenance (#1353)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-09 04:54:01 +00:00
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
renovate[bot] c87f52f696 chore(deps): Update non-major dependencies (#945)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 18:32:03 +08:00
renovate[bot] bc573dca1a chore(deps): Update non-major dependencies (#944)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-21 01:10:37 +00:00
renovate[bot] ecd23f4733 chore(deps): Update non-major dependencies (#943)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 16:41:34 +00:00
renovate[bot] 19ef9f4a14 chore(deps): Update non-major dependencies (#942)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 14:26:20 +00:00
renovate[bot] aacb01e731 chore(deps): Update dependency marked to v16.2.0 (#941)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 15:21:30 +08:00
renovate[bot] ec3c508b88 chore(deps): Lock file maintenance (#940)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 05:49:28 +00:00
renovate[bot] e0cd0e390c chore(deps): Update dependency @iconify/json to v2.2.375 (#939)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-17 08:53:08 +00:00
renovate[bot] b690779caa chore(deps): Update dependency @iconify/json to v2.2.374 (#938)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-16 13:11:56 +00:00
renovate[bot] 3a1997d1f8 chore(deps): Update non-major dependencies (#936)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-15 17:28:24 +00:00
renovate[bot] dd5c201d92 chore(deps): Update module github.com/expr-lang/expr to v1.17.6 (#935)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-13 02:04:03 +00:00
renovate[bot] e23847148e chore(deps): Update non-major dependencies (#934)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 22:58:05 +08:00
renovate[bot] 19ca32f2b4 chore(deps): Update non-major dependencies (#933)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 11:35:29 +00:00
renovate[bot] 2c018c9faa chore(deps): Update non-major dependencies (#932)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 20:52:39 +00:00
renovate[bot] d527a5170b chore(deps): Update actions/checkout action to v5 (#931)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-12 02:19:58 +08:00
renovate[bot] 2e6c8324f1 chore(deps): Update dependency @iconify/json to v2.2.371 (#930)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 13:29:55 +00:00
renovate[bot] 2892b4f35b chore(deps): Lock file maintenance (#927)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 06:05:50 +00:00
renovate[bot] 84c4bb71be chore(deps): Update dependency @iconify/json to v2.2.369 (#926)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-10 20:54:32 +00:00
renovate[bot] 0e74344280 chore(deps): Update dependency @iconify/json to v2.2.368 (#925)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-10 14:52:06 +00:00
renovate[bot] 8d5d0ac90d chore(deps): Update non-major dependencies (#922)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-09 00:23:02 +00:00
renovate[bot] 96454df9a5 chore(deps): Update dependency unplugin-vue-components to v29 (#917)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 18:55:07 +08:00
renovate[bot] 4674cdd34c chore(deps): Update non-major dependencies to v66.4.2 (#921)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 18:54:57 +08:00
耗子 1beb2b26b7 ci: 优化工作流权限 2025-08-08 18:48:42 +08:00
耗子 726e937779 fix: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-08-08 18:46:20 +08:00
耗子 c82c68da9d fix: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-08-08 18:45:43 +08:00
耗子 e1274c28f7 fix: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-08-08 18:45:21 +08:00
耗子 afa40be261 fix: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-08-08 18:44:28 +08:00
耗子 ccd73ebd0d Merge remote-tracking branch 'origin/main' 2025-08-08 18:42:38 +08:00
耗子 1917f8e8d4 feat: 优化使用安全随机数生成器 2025-08-08 18:42:30 +08:00
renovate[bot] 072d847190 chore(deps): Update non-major dependencies (#920)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 04:50:39 +00:00
renovate[bot] 29d32a099d chore(deps): Update non-major dependencies (#918)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-07 13:02:01 +00:00
renovate[bot] 0854c3b053 chore(deps): Update actions/download-artifact action to v5 (#915)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-06 12:46:34 +08:00
renovate[bot] 9967b469f1 chore(deps): Update dependency sass to v1.90.0 (#914)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-06 04:24:31 +00:00
renovate[bot] a0a7656a30 chore(deps): Update dependency unplugin-auto-import to v20 (#913)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-05 18:34:29 +08:00
耗子 32a68b9a0b feat: 更新一键脚本 2025-08-04 23:42:37 +08:00
耗子 6926753f93 feat: 更新安装脚本 2025-08-04 23:19:51 +08:00
耗子 b2158eb8da feat: 更新协议 2025-08-04 23:02:35 +08:00
耗子 f29b5357e7 l10n: update translations (#912)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-08-04 18:21:34 +08:00
renovate[bot] 822d550760 chore(deps): Update non-major dependencies (#908)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-04 18:19:19 +08:00
devhaozi aa420de64e chore(l10n): update pot files 2025-08-04 10:06:02 +00:00
耗子 e398654bb8 Merge remote-tracking branch 'origin/main' 2025-08-04 18:05:10 +08:00
耗子 2cd9596a58 feat: 移除 Godaddy DNS 2025-08-04 18:05:03 +08:00
devhaozi 138f681260 chore(l10n): update pot files 2025-08-04 09:53:00 +00:00
耗子 61bb7c86ec Merge remote-tracking branch 'origin/main' 2025-08-04 17:52:06 +08:00
耗子 14d4b53369 feat: 更新 libdns 到 v1 2025-08-04 17:51:59 +08:00
devhaozi 3d585df346 chore(l10n): update pot files 2025-08-04 09:12:51 +00:00
耗子 fa9d1feffd Merge remote-tracking branch 'origin/main' 2025-08-04 17:11:55 +08:00
耗子 81f75f0852 feat: 移除 Name.com、Linode 和 Vercel DNS 2025-08-04 17:11:47 +08:00
renovate[bot] f63f8bccaf chore(deps): Lock file maintenance (#909)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-04 07:11:42 +00:00
renovate[bot] e35a09c1ef chore(deps): Update dependency vue-tsc to v3.0.5 (#907)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-02 03:35:18 +08:00
耗子 4b98079778 refactor: rename go-rat to libtnb 2025-08-02 03:33:20 +08:00
renovate[bot] 4ea4f0c455 chore(deps): Update non-major dependencies (#906)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-01 14:48:36 +08:00
renovate[bot] 30e54f59eb chore(deps): Update dependency eslint-plugin-vue to v10.4.0 (#905)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-31 16:59:06 +00:00
renovate[bot] abfa10323c chore(deps): Update dependency @iconify/json to v2.2.364 (#904)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-30 22:33:33 +08:00
耗子 7b66868b80 feat: update ci 2025-07-30 22:23:36 +08:00
renovate[bot] ebe9cf53c8 chore(deps): Update dependency echarts to v6 (#903)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-30 22:02:53 +08:00
renovate[bot] 4ec683ba69 chore(deps): Update non-major dependencies (#902)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-30 04:44:01 +00:00
renovate[bot] 6cc9cdc15c chore(deps): Update non-major dependencies (#900)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-29 02:06:00 +00:00
renovate[bot] 8907adf967 chore(deps): Update dependency @vitejs/plugin-vue to v6.0.1 (#899)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 16:45:09 +08:00
renovate[bot] 1fd42eb55f chore(deps): Update dependency vite-plugin-vue-devtools to v8 (#897)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 16:44:57 +08:00
renovate[bot] 55979ed385 chore(deps): Lock file maintenance (#898)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 06:37:53 +00:00
renovate[bot] eaa79126db chore(deps): Update dependency @iconify/json to v2.2.363 (#896)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-26 13:51:21 +00:00
renovate[bot] 6d4b0628d0 chore(deps): Update dependency eslint to v9.32.0 (#895)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-26 00:52:42 +00:00
renovate[bot] e1af869ee4 chore(deps): Update dependency vue-tsc to v3.0.4 (#892)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-25 04:27:53 +00:00
renovate[bot] 3a73192fac chore(deps): Update non-major dependencies (#891)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-24 16:57:58 +00:00
renovate[bot] fdc1dace8f chore(deps): Update non-major dependencies (#890)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-24 00:44:43 +00:00
renovate[bot] 21167c392f chore(deps): Update dependency vue to v3.5.18 (#889)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-23 14:00:26 +08:00
耗子 5584cc18cb refactor: 重命名项目 2025-07-23 04:50:25 +08:00
renovate[bot] 596c8c8385 chore(deps): Update dependency prettier-plugin-organize-imports to v4.2.0 (#888)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-22 17:33:26 +00:00
renovate[bot] c4704614de chore(deps): Lock file maintenance (#887)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 05:56:59 +00:00
renovate[bot] c57d12d53a chore(deps): Update dependency @iconify/json to v2.2.360 (#886)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-20 14:33:13 +00:00
devhaozi 28d0bb4707 chore(l10n): update pot files 2025-07-19 16:44:10 +00:00
耗子 29b8dda4ac feat(#878): 安装时随机默认端口 2025-07-20 00:43:24 +08:00
renovate[bot] b74adcf174 chore(deps): Update dependency @types/node to v22.16.5 (#884)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-19 04:55:11 +00:00
renovate[bot] 268ce78cda chore(deps): Update non-major dependencies (#883)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-18 23:36:05 +00:00
renovate[bot] 831b73f91a chore(deps): Update non-major dependencies (#882)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-18 05:40:14 +00:00
renovate[bot] 31d1bb0ebe chore(deps): Update non-major dependencies (#881)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-17 14:29:21 +00:00
devhaozi 307099db4e chore(l10n): update pot files 2025-07-16 11:16:35 +00:00
耗子 8daeedf2fa feat: ftp不再修改目录权限 2025-07-16 19:15:49 +08:00
renovate[bot] 23570d143b chore(deps): Update non-major dependencies (#879)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-16 04:38:35 +00:00
renovate[bot] 5ee8a36d75 chore(deps): Update non-major dependencies (#877)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 22:34:20 +00:00
devhaozi 507e90168d chore(l10n): update pot files 2025-07-14 13:26:45 +00:00
耗子 62780777b7 fix: 防火墙添加规则时,不添加地址无法添加 2025-07-14 21:25:56 +08:00
renovate[bot] 796b48ca8b chore(deps): Lock file maintenance (#876)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-14 04:50:05 +00:00
renovate[bot] 7d80e874f1 chore(deps): Update dependency vite-plugin-static-copy to v3.1.1 (#875)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-13 13:39:14 +00:00
renovate[bot] 93e0ca6a57 chore(deps): Update non-major dependencies (#874)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-12 03:03:21 +00:00
renovate[bot] 73f5a05811 chore(deps): Update non-major dependencies (#873)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-11 03:13:00 +00:00
renovate[bot] db6f362cce chore(deps): Update non-major dependencies (#872)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-10 16:21:17 +08:00
renovate[bot] 4d7018876c chore(deps): Update dependency @types/node to v22.16.2 (#871)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-09 05:27:27 +00:00
renovate[bot] bba7e89e53 chore(deps): Update dependency @types/node to v22.16.1 (#870)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-08 23:52:28 +00:00
renovate[bot] c3eeffc780 chore(deps): Update dependency vite to v7.0.3 (#869)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-08 15:47:28 +00:00
renovate[bot] c547b00a5f chore(deps): Update module github.com/tnb-labs/acmez/v3 to v3.0.0-20250707093727-dc5aedd96413 (#868)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 20:07:40 +08:00
耗子 e5f837799f l10n: update translations (#867)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-07-07 18:35:01 +08:00
devhaozi 25194d8c50 chore: update mocks 2025-07-07 09:29:34 +00:00
devhaozi e1f6b291bb chore(l10n): update pot files 2025-07-07 09:29:31 +00:00
耗子 df9425b202 Merge remote-tracking branch 'origin/main' 2025-07-07 17:28:46 +08:00
耗子 c50a33f323 feat: 支持修改用户名,close #803 2025-07-07 17:28:40 +08:00
devhaozi b264381b84 chore(l10n): update pot files 2025-07-07 09:19:25 +00:00
耗子 d5ee789a61 Merge remote-tracking branch 'origin/main' 2025-07-07 17:18:40 +08:00
耗子 5fa4219a31 feat: 新增证书到期时间显示,close #836 2025-07-07 17:18:35 +08:00
devhaozi 430640e93c chore(l10n): update pot files 2025-07-07 08:50:36 +00:00
耗子 135c5c844f Merge remote-tracking branch 'origin/main' 2025-07-07 16:49:59 +08:00
耗子 05721dbb47 feat: 优化注销时session处理 2025-07-07 16:49:53 +08:00
renovate[bot] 0235679821 chore(deps): Update dependency @iconify/json to v2.2.356 (#866)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 15:59:50 +08:00
devhaozi 11e206b41a chore(l10n): update pot files 2025-07-07 07:19:51 +00:00
耗子 4985eb2e1f feat: remove CleanPath middleware 2025-07-07 15:19:15 +08:00
耗子 2181653ff7 Merge remote-tracking branch 'origin/main' 2025-07-07 15:01:37 +08:00
耗子 26f7edaa24 feat: remove CleanPath middleware 2025-07-07 15:01:31 +08:00
renovate[bot] 75078c88d9 chore(deps): Lock file maintenance (#865)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 05:28:46 +00:00
耗子 329aecb444 chore: fix typo 2025-07-07 04:27:53 +08:00
耗子 1559c9f8d3 chore: Update SECURITY.md 2025-07-07 04:26:00 +08:00
renovate[bot] 26add61117 chore(deps): Update dependency vue-tsc to v3.0.1 (#864)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 03:59:10 +08:00
renovate[bot] 763c625b83 chore(deps): Update dependency vue-tsc to v3 (#857)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 03:44:01 +08:00
renovate[bot] 1375de2caa chore(deps): Update dependency cronstrue to v3 (#856)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 03:43:51 +08:00
耗子 c4b84632b4 Merge remote-tracking branch 'origin/main' 2025-07-07 03:04:26 +08:00
耗子 c20bfdc3ea fix: sqlite dsn error 2025-07-07 03:04:20 +08:00
devhaozi cc55aef306 chore(l10n): update pot files 2025-07-06 18:48:45 +00:00
耗子 ed5c74c753 fix: 优化路由路径获取方式 2025-07-07 02:47:59 +08:00
renovate[bot] 5a9ccddabd chore(deps): Update non-major dependencies to v66.3.3 (#863)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-06 12:50:23 +00:00
renovate[bot] e6b82ab9ae chore(deps): Update non-major dependencies (#861)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-04 16:35:31 +00:00
renovate[bot] 8868a125e2 chore(deps): Update non-major dependencies (#860)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-03 19:53:11 +00:00
renovate[bot] 04474acf5f chore(deps): Update non-major dependencies (#859)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 21:51:46 +00:00
renovate[bot] 6a17173b3b chore(deps): Update non-major dependencies (#858)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-02 07:13:02 +00:00
renovate[bot] 711fb33b48 chore(deps): Update non-major dependencies (#855)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 23:30:16 +00:00
renovate[bot] 3ed8fb4f95 chore(deps): Lock file maintenance (#854)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 06:26:07 +00:00
renovate[bot] 65944a72ec chore(deps): Update module go.yaml.in/yaml/v3 to v3.0.4 (#853)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 03:41:36 +08:00
renovate[bot] 24d18df86b chore(deps): Update dependency unplugin-vue-components to v28.8.0 (#852)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-29 06:12:30 +00:00
renovate[bot] b330763746 chore(deps): Update dependency pinia-plugin-persistedstate to v4.4.0 (#851)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 22:27:00 +00:00
renovate[bot] 2e914ea41a chore(deps): Update dependency @types/node to v22.15.34 (#850)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 12:40:54 +00:00
renovate[bot] 85a5d22861 chore(deps): Update dependency eslint to v9.30.0 (#849)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-28 02:13:54 +00:00
renovate[bot] 9f31a2061d chore(deps): Update non-major dependencies (#847)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-27 22:14:08 +08:00
renovate[bot] d1b54c2690 chore(deps): Update dependency marked to v16 (#848)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-27 22:13:59 +08:00
renovate[bot] 029f86d9d5 chore(deps): Update non-major dependencies (#846)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-26 21:56:28 +00:00
renovate[bot] 9de30f30b8 chore(deps): Update non-major dependencies to v66.3.2 (#845)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-26 06:10:46 +00:00
renovate[bot] 8582bfc36b chore(deps): Update non-major dependencies (#844)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-25 19:22:49 +00:00
renovate[bot] 4a2af3434c chore(deps): Update dependency @types/node to v22.15.33 (#843)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-25 01:10:46 +00:00
renovate[bot] 7f697ca734 chore(deps): Update dependency @vitejs/plugin-vue to v6 (#841)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 23:15:50 +08:00
renovate[bot] 7f08ee92ff chore(deps): Update dependency vite to v7 (#842)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 22:30:31 +08:00
renovate[bot] 2f09043915 chore(deps): Update dependency @iconify/json to v2.2.352 (#840)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 13:58:06 +00:00
renovate[bot] 50aab262a8 chore(deps): Update module github.com/ncruces/go-sqlite3 to v0.26.2 (#839)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 16:08:41 +08:00
renovate[bot] 99b033f070 chore(deps): Update non-major dependencies (#838)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-23 10:49:59 +00:00
renovate[bot] 6277a17d53 chore(deps): Lock file maintenance (#837)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-23 15:25:46 +08:00
耗子 8bb6bd1882 feat: 替换弃用的gopkg.in/yaml.v3包 2025-06-22 20:14:27 +08:00
dependabot[bot] 3161c61001 chore(deps): bump github.com/go-chi/chi/v5 (#834)
Bumps the go_modules group with 1 update in the / directory: [github.com/go-chi/chi/v5](https://github.com/go-chi/chi).


Updates `github.com/go-chi/chi/v5` from 5.2.1 to 5.2.2
- [Release notes](https://github.com/go-chi/chi/releases)
- [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-chi/chi/compare/v5.2.1...v5.2.2)

---
updated-dependencies:
- dependency-name: github.com/go-chi/chi/v5
  dependency-version: 5.2.2
  dependency-type: direct:production
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-21 03:25:49 +08:00
renovate[bot] e68a882e75 chore(deps): Update non-major dependencies (#833)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-20 17:01:30 +00:00
耗子 24066f05fc l10n: update translations (#832)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-06-20 13:36:39 +08:00
renovate[bot] 28325f4af6 chore(deps): Update non-major dependencies (#831)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-19 23:04:31 +00:00
renovate[bot] 0c57e9d628 chore(deps): Update dependency vue to v3.5.17 (#830)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-19 03:14:04 +00:00
renovate[bot] 8fb7ddcf7a chore(deps): Update dependency @iconify/json to v2.2.350 (#829)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 12:56:47 +00:00
renovate[bot] 76185a97f3 chore(deps): Update non-major dependencies to v66.2.3 (#828)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 01:11:14 +00:00
renovate[bot] 567ef561c7 chore(deps): Update non-major dependencies to v66.2.2 (#827)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-17 21:03:27 +08:00
renovate[bot] b70b9df0b7 chore(deps): Update dependency @vue/eslint-config-typescript to v14.5.1 (#826)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-17 20:56:24 +08:00
耗子 0e4fbea166 Merge remote-tracking branch 'origin/main' 2025-06-17 20:03:26 +08:00
耗子 6cf4e0c0cc feat: 数据库添加_txlock=immediate参数 2025-06-17 20:03:07 +08:00
耗子 68da337cc0 Merge remote-tracking branch 'origin/main' 2025-06-17 09:06:23 +08:00
耗子 c492639897 fix: 优化手动dns签发 2025-06-17 09:06:05 +08:00
renovate[bot] 5e10b2267e chore(deps): Update non-major dependencies (#824)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 22:43:54 +00:00
renovate[bot] 68bbbc76e2 chore(deps): Lock file maintenance (#823)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 05:34:04 +00:00
renovate[bot] 7cd1c764d2 chore(deps): Update non-major dependencies (#821)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 00:06:33 +08:00
耗子 a053a4212a feat: 屏蔽github.com/libdns/cloudns更新 2025-06-15 23:54:34 +08:00
耗子 2d0974a5d5 Merge remote-tracking branch 'origin/main' 2025-06-14 18:00:35 +08:00
耗子 d90396c8ea fix: 出站规则下,必须指定具体的地址,否则会添加成入站规则,close #822 2025-06-14 18:00:28 +08:00
renovate[bot] 877d9c57c8 chore(deps): Update non-major dependencies (#820)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-13 21:32:05 +00:00
devhaozi 275760c18a chore(l10n): update pot files 2025-06-13 05:35:09 +00:00
dependabot[bot] 9dd6063611 chore(deps): bump brace-expansion (#817)
Bumps the npm_and_yarn group with 1 update in the /web directory: [brace-expansion](https://github.com/juliangruber/brace-expansion).


Updates `brace-expansion` from 1.1.11 to 1.1.12
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/1.1.11...v1.1.12)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.12
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-13 13:34:32 +08:00
耗子 0acbb05d3f fix: 安全登录启用时,可能出现反复跳转登录页的问题 2025-06-13 02:42:20 +08:00
renovate[bot] d860e9361a chore(deps): Update non-major dependencies (#819)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-13 00:56:30 +08:00
renovate[bot] 97d76f62ae chore(deps): Update non-major dependencies (#816)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 12:51:30 +00:00
renovate[bot] 910a2d92cc chore(deps): Update non-major dependencies (#815)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-11 17:05:57 +08:00
renovate[bot] 76ef54fe0e chore(deps): Update stefanzweifel/git-auto-commit-action action to v6 (#813)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-11 16:42:36 +08:00
renovate[bot] bf41b98339 chore(deps): Update dependency tsx to v4.20.0 (#814)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-11 06:54:10 +00:00
renovate[bot] f3cec5e476 chore(deps): Update non-major dependencies (#812)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-10 13:28:58 +00:00
renovate[bot] bb5b66958c chore(deps): Update non-major dependencies (#811)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-09 22:59:48 +00:00
renovate[bot] 04fb0a1de8 chore(deps): Lock file maintenance (#810)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-09 07:43:45 +00:00
renovate[bot] f7752387da chore(deps): Update non-major dependencies (#809)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-09 00:28:06 +00:00
耗子 a7420eef34 l10n: update translations (#808)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-06-08 15:54:56 +08:00
耗子 28847bcc1f l10n: update translations (#807)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-06-08 15:53:38 +08:00
devhaozi cb24946aea chore(l10n): update pot files 2025-06-08 07:37:44 +00:00
耗子 371424114a Merge remote-tracking branch 'origin/main' 2025-06-08 15:36:57 +08:00
耗子 60086b9839 fix: 优化翻译 2025-06-08 15:36:50 +08:00
devhaozi 6d55088a0d chore(l10n): update pot files 2025-06-08 07:33:51 +00:00
耗子 c4234a11f2 Merge remote-tracking branch 'origin/main' 2025-06-08 15:32:51 +08:00
耗子 7644e1a5f6 feat: 优化防火墙创建规则 2025-06-08 15:32:45 +08:00
renovate[bot] f8a54b2860 chore(deps): Update non-major dependencies (#805)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-08 06:41:01 +00:00
renovate[bot] 8c1813b9e5 chore(deps): Update non-major dependencies (#804)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-08 01:45:07 +08:00
renovate[bot] 7a9a803451 chore(deps): Update non-major dependencies (#802)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-07 02:49:15 +00:00
renovate[bot] 681d0b5324 chore(deps): Update dependency @iconify/json to v2.2.346 (#801)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-05 16:24:31 +00:00
devhaozi a33490b316 chore(l10n): update pot files 2025-06-05 03:24:31 +00:00
耗子 fb95ff2861 fix: 登录后访问安全入口404 2025-06-05 11:23:18 +08:00
renovate[bot] 326c2585f3 chore(deps): Update module github.com/golang-cz/httplog to v0.0.2 (#800)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-04 20:57:18 +00:00
renovate[bot] 61cc26cdb0 chore(deps): Update non-major dependencies (#798)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-05 00:38:46 +08:00
devhaozi 2ecd3a020b chore(l10n): update pot files 2025-06-03 20:21:46 +00:00
耗子 c5b23feeac fix: 模态窗点击遮罩关闭时自动重置数据 2025-06-04 04:20:51 +08:00
renovate[bot] 4f39f62208 chore(deps): Update module github.com/expr-lang/expr to v1.17.5 (#797)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-04 03:46:32 +08:00
renovate[bot] 146401e9f9 chore(deps): Update github.com/golang-cz/httplog digest to 1a554d4 (#796)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-03 22:53:14 +08:00
renovate[bot] d544ca6a90 chore(deps): Update dependency md-editor-v3 to v5.6.1 (#795)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-03 22:27:14 +08:00
renovate[bot] 43248c1d88 chore(deps): Update non-major dependencies (#794)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 21:59:27 +00:00
renovate[bot] 2b5bb04bbd chore(deps): Lock file maintenance (#793)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 21:46:38 +08:00
renovate[bot] 30a5427778 chore(deps): Update non-major dependencies (#792)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 21:36:42 +08:00
耗子 c547200c05 l10n: update translations (#790)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-31 20:45:22 +08:00
耗子 7f223812dc ci: 优化l10n工作流 2025-05-31 19:55:39 +08:00
耗子 0e86c77d6d Merge remote-tracking branch 'origin/main' 2025-05-31 19:54:29 +08:00
耗子 fb24407cd1 ci: 优化l10n工作流 2025-05-31 19:54:24 +08:00
devhaozi ec7791b73d chore(l10n): update frontend pot files 2025-05-31 11:51:39 +00:00
耗子 7987cec9b9 feat: 优化编排删除 2025-05-31 19:51:09 +08:00
耗子 7be3e799be l10n: update translations (#789)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-31 19:41:34 +08:00
devhaozi 5667bb236f chore(l10n): update backend pot files 2025-05-31 11:25:35 +00:00
devhaozi 5a391a459e chore(l10n): update frontend pot files 2025-05-31 11:22:36 +00:00
耗子 60b0761de2 Merge remote-tracking branch 'origin/main' 2025-05-31 19:22:07 +08:00
耗子 952546f845 fix: 优化编排显示 2025-05-31 19:22:01 +08:00
devhaozi b7c055c43f chore(l10n): update backend pot files 2025-05-31 10:48:42 +00:00
耗子 ba4df0c76f feat: 调试模式下安装应用不检查面板版本 2025-05-31 18:48:06 +08:00
耗子 dfae3f1d8d Merge remote-tracking branch 'origin/main' 2025-05-31 17:35:43 +08:00
耗子 310d83a2aa fix: 面板容器管理界面无法删除卷,close #786 2025-05-31 17:35:37 +08:00
devhaozi 77eb802076 chore(l10n): update frontend pot files 2025-05-31 09:24:21 +00:00
耗子 913a6776dc Merge remote-tracking branch 'origin/main' 2025-05-31 17:23:53 +08:00
耗子 f245ff06c5 fix: 创建数据库用户时如果赋予权限的数据库不存在时显示异常,close #785 2025-05-31 17:23:47 +08:00
devhaozi 79901878e6 chore(l10n): update frontend pot files 2025-05-31 08:37:59 +00:00
耗子 c597e6b99c fix: 创建用户界面里的服务器数据库选择列表重复显示,close #784 2025-05-31 16:37:24 +08:00
devhaozi 3c98e6ff37 chore(l10n): update frontend pot files 2025-05-31 07:43:11 +00:00
耗子 447b0332e3 fix: 容器端口映射显示相反,close #788 2025-05-31 15:42:40 +08:00
renovate[bot] 1459b287fa chore(deps): Update non-major dependencies (#783)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 04:44:27 +00:00
renovate[bot] 252b67ce36 chore(deps): Update dependency @types/node to v22.15.28 (#782)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 01:46:07 +08:00
renovate[bot] 8119eca0c6 chore(deps): Update non-major dependencies to v66.1.3 (#781)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-31 01:30:04 +08:00
renovate[bot] f0b96417bf chore(deps): Update non-major dependencies (#779)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-30 15:06:46 +08:00
renovate[bot] b8018284c6 chore(deps): Update dependency vue to v3.5.16 (#778)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-29 05:22:29 +00:00
renovate[bot] 737986f218 chore(deps): Update dependency @types/node to v22.15.24 (#777)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-29 04:13:25 +08:00
renovate[bot] 200815bf10 chore(deps): Update non-major dependencies (#775)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-28 14:01:11 +08:00
renovate[bot] 8cff4e3575 chore(deps): Update non-major dependencies (#772)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-28 03:27:10 +00:00
耗子 64c39bd462 Merge remote-tracking branch 'origin/main' 2025-05-27 00:02:22 +08:00
耗子 4a5e629ab1 feat: 更新文档链接 2025-05-27 00:02:16 +08:00
renovate[bot] 368519f3fa chore(deps): Update dependency vue to v3.5.15 (#771)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-26 23:59:12 +08:00
耗子 8fa7f4a22b l10n: update translations (#770)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-26 23:37:38 +08:00
耗子 a8e4360c29 feat: 调整排版 2025-05-26 20:19:50 +08:00
耗子 719a9a580d feat: 添加安装脚本到文档 2025-05-26 20:18:44 +08:00
耗子 fb32151b76 feat: 优化排版 2025-05-26 20:15:09 +08:00
耗子 c50d892e52 Merge remote-tracking branch 'origin/main' 2025-05-26 20:11:32 +08:00
耗子 b31ccace85 feat: 更新logo 2025-05-26 20:11:27 +08:00
耗子 e57da7e41a l10n: update translations (#769)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-26 19:59:53 +08:00
耗子 38141c3f4e l10n: update translations (#768)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-05-26 16:29:23 +08:00
renovate[bot] 69d39e6c67 chore(deps): Update non-major dependencies (#767)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-26 15:50:46 +08:00
devhaozi 4d8a330644 chore(l10n): update frontend pot files 2025-05-26 07:36:35 +00:00
耗子 26c417f18d ci: fix pnpm 2025-05-26 15:36:05 +08:00
耗子 f6b7fcb4af Merge remote-tracking branch 'origin/main' 2025-05-26 15:32:29 +08:00
耗子 212e989782 feat: 更新包 2025-05-26 15:32:23 +08:00
devhaozi d0ece55dd1 chore(l10n): update backend pot files 2025-05-25 16:35:01 +00:00
耗子 03c0b191fe feat: 支持cidr,close #764 2025-05-26 00:34:24 +08:00
耗子 adacee1851 l10n: update translations (#763)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-05-24 23:48:42 +08:00
devhaozi 2725c4902d chore(l10n): update frontend pot files 2025-05-24 14:44:55 +00:00
耗子 77d7539880 feat: 优化登录翻译 2025-05-24 22:44:24 +08:00
耗子 560b80038e fix: 404时标题为undefined 2025-05-24 22:38:12 +08:00
耗子 c9d4c79bf8 l10n: update translations (#761)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-24 15:29:53 +08:00
devhaozi 905f13f0d3 chore(l10n): update backend pot files 2025-05-24 07:16:29 +00:00
耗子 e7128c65d9 Merge remote-tracking branch 'origin/main' 2025-05-24 15:16:02 +08:00
耗子 c15e3cc3ae fix: 优化访问入口检查 2025-05-24 15:15:57 +08:00
devhaozi d9f1145d93 chore(l10n): update backend pot files 2025-05-24 07:10:58 +00:00
耗子 0aafe8baa3 fix: 优化访问入口检查 2025-05-24 15:10:30 +08:00
devhaozi 7b0e9db8dc chore(l10n): update backend pot files 2025-05-24 06:58:24 +00:00
耗子 07881311b3 fix: 优化访问入口检查,fix #760 2025-05-24 14:57:53 +08:00
devhaozi 7d9b84c746 chore(l10n): update backend pot files 2025-05-23 02:25:09 +00:00
耗子 548165ab4a fix: 两步验证使用SHA1算法(为了兼容性) 2025-05-23 10:24:37 +08:00
耗子 a997d548f7 feat: 优化菜单排序 2025-05-19 02:15:15 +08:00
耗子 aab5cf7e94 l10n: update translations (#754)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-05-19 02:03:49 +08:00
devhaozi 382bf4daaa chore(l10n): update backend pot files 2025-05-18 17:59:56 +00:00
耗子 3e048a8683 Merge remote-tracking branch 'origin/main' 2025-05-19 01:59:23 +08:00
耗子 7fa7bb4bd0 fix: lint 2025-05-19 01:59:18 +08:00
devhaozi d41ddb0ad0 chore(l10n): update backend pot files 2025-05-18 17:54:41 +00:00
耗子 cdbadcbb65 fix: lint 2025-05-19 01:54:12 +08:00
devhaozi b14d63c54c chore(l10n): update frontend pot files 2025-05-18 17:49:24 +00:00
耗子 3d47855cc1 feat: 优化跑分算法 2025-05-19 01:48:48 +08:00
devhaozi 819db43aaa chore(l10n): update frontend pot files 2025-05-18 16:35:34 +00:00
devhaozi de859afbe7 chore(l10n): update backend pot files 2025-05-18 16:35:28 +00:00
耗子 066c840efe feat: 移动工具箱和跑分为独立应用 2025-05-19 00:34:53 +08:00
耗子 bc4fcfe6ed feat: 移除工具箱和跑分应用 2025-05-19 00:09:29 +08:00
devhaozi d43090b84f chore(l10n): update backend pot files 2025-05-18 14:45:49 +00:00
耗子 919c37d1f9 fix: 两步验证算法错误 2025-05-18 22:44:43 +08:00
耗子 319b689d79 Merge remote-tracking branch 'origin/main' 2025-05-17 21:07:19 +08:00
耗子 9a6badd46c feat: 优化日志清理使用cat /dev/null 2025-05-17 21:07:14 +08:00
devhaozi ee94b01c2d chore(l10n): update backend pot files 2025-05-17 13:04:36 +00:00
耗子 7b471967a2 feat: 移除日志切割后自动重载nginx 2025-05-17 21:04:05 +08:00
devhaozi 6e039a7fad chore(l10n): update backend pot files 2025-05-17 11:03:26 +00:00
耗子 2f56606789 feat: 用户信息取首用户 2025-05-17 19:02:52 +08:00
耗子 a3210c0a92 feat: 证书账户表单默认使用letsencrypt 2025-05-17 19:01:16 +08:00
耗子 9876821b9a l10n: update translations (#750)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-05-17 18:34:58 +08:00
devhaozi ee7f846151 chore(l10n): update frontend pot files 2025-05-17 10:23:18 +00:00
耗子 6b1478594a fix: 服务禁用自启动提示错误 2025-05-17 18:22:43 +08:00
devhaozi 6448e7c5c6 chore(l10n): update frontend pot files 2025-05-17 10:21:56 +00:00
耗子 68c088ca03 Merge remote-tracking branch 'origin/main' 2025-05-17 18:21:18 +08:00
耗子 c5ec454b26 feat: 优化服务状态显示 2025-05-17 18:21:08 +08:00
devhaozi 12f9820ec5 chore(l10n): update backend pot files 2025-05-17 08:53:44 +00:00
耗子 d5d941c62b Merge remote-tracking branch 'origin/main' 2025-05-17 16:53:15 +08:00
耗子 e5f26d2f23 feat: 禁止通过api访问ws接口 2025-05-17 16:53:10 +08:00
耗子 433f3f99f6 fix: google eab获取 2025-05-16 10:53:54 +08:00
devhaozi b866f01985 chore(l10n): update backend pot files 2025-05-16 00:39:18 +00:00
耗子 07ba6624bf feat: 优化日志切割 2025-05-16 08:38:37 +08:00
耗子 855c8cfb51 New translations frontend.pot (Chinese Simplified) (#748)
Update translations (Chinese Simplified) frontend.pot
2025-05-16 00:52:46 +08:00
耗子 70175d309e refactor: 接口信息字段调整为msg 2025-05-16 00:35:48 +08:00
耗子 04d286ca03 refactor: 接口信息字段调整为msg 2025-05-16 00:29:00 +08:00
耗子 1a821b361f l10n: update translations (#747)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-05-15 23:21:00 +08:00
耗子 ec101ba9f7 l10n: update translations (#746)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-15 20:39:41 +08:00
devhaozi 830fa5aa86 chore(l10n): update frontend pot files 2025-05-15 12:11:18 +00:00
devhaozi aed6bb7856 chore(l10n): update backend pot files 2025-05-15 12:11:04 +00:00
耗子 7df9b92c58 feat: 添加cli命令及前端优化 2025-05-15 20:10:31 +08:00
耗子 9faa2a74df Merge remote-tracking branch 'origin/main' 2025-05-15 19:21:55 +08:00
耗子 94497f226f feat: 优化网站证书更新 2025-05-15 19:21:48 +08:00
devhaozi cff90ef444 chore(l10n): update backend pot files 2025-05-15 10:37:52 +00:00
耗子 f8ae9136c0 Merge remote-tracking branch 'origin/main' 2025-05-15 18:37:15 +08:00
耗子 7716866849 feat: 优化签名错误提示 2025-05-15 18:37:09 +08:00
devhaozi c690353f3e chore(l10n): update frontend pot files 2025-05-15 09:57:31 +00:00
耗子 373827f8e5 feat: 优化图标 2025-05-15 17:56:54 +08:00
devhaozi 587436781d chore(l10n): update backend pot files 2025-05-15 01:49:45 +00:00
耗子 069353d617 Merge remote-tracking branch 'origin/main' 2025-05-15 09:49:05 +08:00
耗子 7f87030cc8 feat: 传递channel参数 2025-05-15 09:47:43 +08:00
devhaozi 9064e41775 chore(l10n): update backend pot files 2025-05-15 01:08:51 +00:00
耗子 04c8eb5cad Merge remote-tracking branch 'origin/main' 2025-05-15 09:08:12 +08:00
耗子 867c14349e feat: 优化s3fs挂载获取 2025-05-15 09:08:03 +08:00
devhaozi a59fe6fe16 chore(l10n): update backend pot files 2025-05-15 00:30:40 +00:00
耗子 71f518c6f9 feat: 初始化网站类型 2025-05-15 08:29:51 +08:00
devhaozi a7192efda0 chore: update mocks 2025-05-14 20:07:16 +00:00
devhaozi f14b606fda chore(l10n): update backend pot files 2025-05-14 20:06:53 +00:00
耗子 82e310542f Merge remote-tracking branch 'origin/main' 2025-05-15 04:06:25 +08:00
耗子 16f31120f5 feat: 添加用于更新证书的接口 2025-05-15 04:06:19 +08:00
devhaozi 05ffab5d54 chore(l10n): update frontend pot files 2025-05-14 19:40:32 +00:00
耗子 f39cceda18 feat: 优化2FA模态窗样式 2025-05-15 03:39:49 +08:00
耗子 ec8bf8fdad l10n: update translations (#743)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-15 03:26:07 +08:00
devhaozi c0f82356aa chore(l10n): update backend pot files 2025-05-14 19:22:01 +00:00
耗子 14c46eccca Merge remote-tracking branch 'origin/main' 2025-05-15 03:21:25 +08:00
耗子 354e8a1270 feat: 重构签名算法 2025-05-15 03:21:18 +08:00
devhaozi 924e69070a chore: update mocks 2025-05-14 19:20:06 +00:00
devhaozi 137624815c chore(l10n): update backend pot files 2025-05-14 19:19:53 +00:00
耗子 1adba2da49 feat: 重构签名算法 2025-05-15 03:19:16 +08:00
耗子 00c02e4b16 l10n: update translations (#741)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-05-15 02:55:36 +08:00
devhaozi 813dcaedaf chore(l10n): update frontend pot files 2025-05-14 18:34:11 +00:00
devhaozi 0ef8bddf98 chore(l10n): update backend pot files 2025-05-14 18:33:09 +00:00
耗子 6276009025 fix: 翻译错误 2025-05-15 02:32:32 +08:00
devhaozi 2dc7096a2c chore: update mocks 2025-05-14 18:19:33 +00:00
devhaozi b84655512c chore(l10n): update backend pot files 2025-05-14 18:19:18 +00:00
devhaozi 271a2cad23 chore(l10n): update frontend pot files 2025-05-14 18:19:11 +00:00
耗子 a7dd9d411b feat: 支持创建access_token 2025-05-15 02:18:35 +08:00
devhaozi 103529a9b4 chore(l10n): update frontend pot files 2025-05-14 11:52:00 +00:00
耗子 ccef25314d Merge remote-tracking branch 'origin/main' 2025-05-14 19:51:29 +08:00
耗子 9f24e1e8a6 feat: 批量创建网站后触发表格刷新 2025-05-14 19:51:20 +08:00
devhaozi 7399e1632e chore(l10n): update frontend pot files 2025-05-14 11:44:34 +00:00
耗子 90715bcfaf Merge remote-tracking branch 'origin/main' 2025-05-14 19:43:54 +08:00
耗子 a64176e363 feat: 批量创建网站,close #129 2025-05-14 19:43:46 +08:00
devhaozi 910104d400 chore(l10n): update frontend pot files 2025-05-14 11:16:42 +00:00
devhaozi 9e87be88c7 chore(l10n): update backend pot files 2025-05-14 11:16:34 +00:00
耗子 fae5d136d4 feat: 登录支持输入2fa验证码 2025-05-14 19:16:06 +08:00
devhaozi 13f090e2b7 chore: update mocks 2025-05-14 11:04:54 +00:00
devhaozi 5896ba3c87 chore(l10n): update frontend pot files 2025-05-14 11:04:38 +00:00
devhaozi 65030866e5 chore(l10n): update backend pot files 2025-05-14 11:04:33 +00:00
耗子 5fd00acd48 feat: 用户支持开启2FA 2025-05-14 19:04:03 +08:00
耗子 462d6c0789 Merge remote-tracking branch 'origin/main' 2025-05-14 16:11:28 +08:00
耗子 c6b3b9e0c4 feat: l10n工作流不在pr运行 2025-05-14 16:11:21 +08:00
耗子 63b6349a74 l10n: update translations (#737)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-05-14 13:51:41 +08:00
devhaozi 781c5cf11e chore(l10n): update backend pot files 2025-05-13 20:21:58 +00:00
耗子 8697c893c4 Merge remote-tracking branch 'origin/main' 2025-05-14 04:21:22 +08:00
耗子 896e242827 feat: 2fa命令行 2025-05-14 04:21:17 +08:00
devhaozi b891d21c85 chore: update mocks 2025-05-13 20:00:17 +00:00
devhaozi 399b6f3537 chore(l10n): update frontend pot files 2025-05-13 20:00:02 +00:00
devhaozi cf8237b9c7 chore(l10n): update backend pot files 2025-05-13 19:59:54 +00:00
耗子 daccf651b6 feat: 2fa相关接口 2025-05-14 03:59:22 +08:00
devhaozi 4386334c31 chore: update mocks 2025-05-13 19:09:26 +00:00
devhaozi 0a445a4247 chore(l10n): update backend pot files 2025-05-13 19:09:15 +00:00
devhaozi 76cb8c46ba chore(l10n): update frontend pot files 2025-05-13 19:09:09 +00:00
耗子 d27a91570c feat: 支持绑定域名、IP、UA,close #670 2025-05-14 03:08:31 +08:00
devhaozi 554183caab chore: update mocks 2025-05-13 14:05:49 +00:00
耗子 275bba850a workflow: 优化提交前pull更改,防止工作流并发下无法提交 2025-05-13 22:05:02 +08:00
devhaozi 86c5239569 chore(l10n): update backend pot files 2025-05-13 13:47:20 +00:00
耗子 681f1e0805 Merge remote-tracking branch 'origin/main' 2025-05-13 21:46:52 +08:00
耗子 b767044199 feat: 为设置添加一个缓存层 2025-05-13 21:46:44 +08:00
devhaozi 67265ef969 chore(l10n): update backend pot files 2025-05-13 13:39:44 +00:00
耗子 8410b48ac0 feat: 将session超时写入主配置 2025-05-13 21:39:14 +08:00
devhaozi e6567e5e95 chore(l10n): update backend pot files 2025-05-12 18:43:35 +00:00
耗子 715b42dfbb feat: 添加API和更新渠道的设置字段 2025-05-13 02:42:55 +08:00
devhaozi 69ddca44bf chore(l10n): update backend pot files 2025-05-12 14:46:09 +00:00
耗子 71d9151fa0 feat: 添加2fa,登录超时,绑定域名、IP、UA的设置字段 2025-05-12 22:45:36 +08:00
耗子 3fd768a99c feat: 使用自建的iconify cdn,close #699 2025-05-12 21:37:13 +08:00
renovate[bot] c02eab6bfc chore(deps): Lock file maintenance (#733)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-12 05:49:08 +00:00
renovate[bot] 148e5946cf chore(deps): Update dependency @iconify/json to v2.2.337 (#732)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-11 18:37:08 +00:00
renovate[bot] 3590191ef9 chore(deps): Update dependency vite-plugin-static-copy to v3 (#730)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-11 16:30:13 +08:00
renovate[bot] b91c1a02f5 chore(deps): Update non-major dependencies (#731)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-11 06:30:44 +00:00
renovate[bot] d50ef7d62c chore(deps): Update dependency @vue-js-cron/naive-ui to v2.0.10 (#729)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-10 17:19:33 +00:00
renovate[bot] 6ee4d67c2e chore(deps): Update dependency @vitejs/plugin-vue to v5.2.4 (#728)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-09 16:26:41 +00:00
renovate[bot] 3e325b13b4 chore(deps): Update non-major dependencies (#727)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-09 06:27:46 +00:00
renovate[bot] d6fb57f9ea chore(deps): Update non-major dependencies (#725)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-08 20:20:59 +00:00
renovate[bot] bffdf2821c chore(deps): Update module gorm.io/gorm to v1.26.1 (#723)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-08 11:21:29 +08:00
renovate[bot] c82d7bd48a chore(deps): Update non-major dependencies (#722)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-07 22:04:50 +00:00
renovate[bot] e4eb97e9dd chore(deps): Update non-major dependencies (#721)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-06 22:33:31 +00:00
renovate[bot] cbb168b3d5 chore(deps): Update non-major dependencies (#720)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-06 14:09:06 +08:00
renovate[bot] 60e817d9ea chore(deps): Update non-major dependencies (#719)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-06 14:04:19 +08:00
renovate[bot] e514d45486 chore(deps): Update dependency vite to v6.3.5 (#718)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 16:14:22 +00:00
renovate[bot] 995d5524ea chore(deps): Update dependency npm-run-all2 to v8.0.1 (#717)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 13:45:29 +08:00
renovate[bot] b1d5276813 chore(deps): Update dependency npm-run-all2 to v8 (#710)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 13:33:57 +08:00
renovate[bot] 97ee651948 chore(deps): Lock file maintenance (#715)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 13:33:39 +08:00
renovate[bot] 5ddc25acc7 chore(deps): Update dependency @iconify/json to v2.2.335 (#716)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 13:18:52 +08:00
renovate[bot] 96ae14eefb chore(deps): Update golangci/golangci-lint-action action to v8 (#714)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 13:13:22 +08:00
renovate[bot] 820728861c chore(deps): Update non-major dependencies to v66.1.0 (#713)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-04 21:22:21 +00:00
renovate[bot] b476e9aec3 chore(deps): Update dependency eslint to v9.26.0 (#712)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-03 02:03:03 +00:00
renovate[bot] 5252854963 chore(deps): Update module github.com/spf13/cast to v1.8.0 (#709)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-03 02:04:43 +08:00
耗子 fe6e9efd09 refactor: 接口使用蛇形命名 2025-05-02 19:28:52 +08:00
renovate[bot] c0be351b95 chore(deps): Update dependency @iconify/json to v2.2.334 (#708)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-02 10:49:59 +00:00
renovate[bot] 573fc88c7a chore(deps): Update dependency cronstrue to v2.60.0 (#707)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-01 18:28:15 +00:00
renovate[bot] 9032450be8 chore(deps): Update dependency @iconify/vue to v5 (#706)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-01 20:04:06 +08:00
renovate[bot] bf0cdbe493 chore(deps): Update dependency eslint-plugin-vue to v10.1.0 (#705)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-30 16:05:16 +00:00
renovate[bot] 446d2410dc chore(deps): Update dependency vite to v6.3.4 (#704)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-30 14:42:36 +08:00
renovate[bot] 17febfa9e6 chore(deps): Update non-major dependencies (#703)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-30 02:06:31 +00:00
renovate[bot] 86eaef0ae3 chore(deps): Update dependency eslint-plugin-vue to v10.0.1 (#702)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-29 13:57:02 +08:00
renovate[bot] 874ecbc886 chore(deps): Update non-major dependencies (#701)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 22:33:51 +00:00
renovate[bot] 0c4b4b0a31 chore(deps): Update module github.com/urfave/cli/v3 to v3.3.2 (#700)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 22:43:45 +08:00
renovate[bot] 5d8e773827 chore(deps): Update non-major dependencies (#698)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 22:39:32 +08:00
耗子 c9e1adc1e9 Merge remote-tracking branch 'origin/main' 2025-04-28 21:28:12 +08:00
耗子 27ce52ea13 chore: 暂时屏蔽github.com/libdns/gcore更新 2025-04-28 21:28:06 +08:00
renovate[bot] dea9702c36 chore(deps): Update module github.com/samber/lo to v1.50.0 (#696)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-27 02:25:44 +00:00
renovate[bot] 794fa29581 chore(deps): Update dependency @iconify/json to v2.2.332 (#694)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-26 11:16:30 +00:00
renovate[bot] 30686bf5a1 chore(deps): Update non-major dependencies (#693)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-25 19:42:44 +00:00
耗子 ae9f3c4d15 fix: build 2025-04-25 14:31:31 +08:00
耗子 13fc5d2a5b fix: build 2025-04-25 14:30:29 +08:00
renovate[bot] 7bc2173edb chore(deps): Update non-major dependencies 2025-04-25 04:11:23 +00:00
renovate[bot] 5317e19d6e chore(deps): Lock file maintenance (#692)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-21 05:26:30 +00:00
renovate[bot] 0910b7c068 chore(deps): Update dependency eslint to v9.25.0 (#689)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-18 22:11:13 +00:00
devhaozi c800f6642b chore(l10n): update frontend pot files 2025-04-18 17:19:39 +00:00
耗子 1b80c85ebf feat: 优化压缩的文件名处理 2025-04-19 01:19:04 +08:00
耗子 714a33313e feat: 优化开发 2025-04-19 00:50:29 +08:00
耗子 a70b18b6f4 feat: 反代生成器添加X-Cache头 2025-04-19 00:10:08 +08:00
renovate[bot] 1a3d2cdd78 chore(deps): Update non-major dependencies (#688)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-18 15:53:26 +08:00
renovate[bot] 45081b5535 chore(deps): Update non-major dependencies (#687)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-17 21:38:48 +00:00
耗子 600287c246 feat: 优化http挑战的流程,支持同时签多个域名 2025-04-17 01:26:13 +08:00
renovate[bot] a2607b4846 chore(deps): Update dependency vite-plugin-vue-devtools to v7.7.5 (#686)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-17 01:08:29 +08:00
耗子 13bb84f4f4 l10n: update translations (#684)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-17 00:46:59 +08:00
renovate[bot] e702f7813a chore(deps): Update module github.com/knadh/koanf/parsers/yaml to v1 (#682)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 21:37:42 +08:00
renovate[bot] ec8156b851 chore(deps): Update non-major dependencies (#685)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 21:37:24 +08:00
devhaozi 9da24a3238 chore(l10n): update frontend pot files 2025-04-16 07:31:21 +00:00
耗子 e390102a7c Merge remote-tracking branch 'origin/main' 2025-04-16 15:30:43 +08:00
耗子 2271084c02 fix: 修正网站启动停止的翻译 2025-04-16 15:30:35 +08:00
devhaozi dbb4a3514b chore(l10n): update frontend pot files 2025-04-16 06:20:31 +00:00
耗子 3eeefcdc9c feat: 证书默认邮箱域名改为yourdomain.com,因为example.com在lets黑名单里面 2025-04-16 14:19:52 +08:00
renovate[bot] c0be6addbc chore(deps): Update module github.com/mholt/acmez/v3 to v3.1.2 (#683)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 03:27:22 +00:00
renovate[bot] c344ade9fc chore(deps): Update non-major dependencies (#681)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-15 19:14:21 +00:00
耗子 6c2a3a26ab feat: 前端翻译仅在编译时打包 2025-04-15 18:06:56 +08:00
devhaozi 073f42a3bc chore(l10n): update frontend pot files 2025-04-15 09:55:13 +00:00
耗子 0e3007827e Merge remote-tracking branch 'origin/main' 2025-04-15 17:54:35 +08:00
耗子 8aa23cdd48 fix: 弹窗未显示错误信息 2025-04-15 17:54:30 +08:00
renovate[bot] 9765f7157d chore(deps): Update pnpm to v10.8.1 (#680)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-14 23:02:22 +00:00
耗子 ee3aabe3c8 Merge remote-tracking branch 'origin/main' 2025-04-14 19:02:41 +08:00
耗子 d39b3f66d6 feat: 容器日志只取最新100行 2025-04-14 19:02:35 +08:00
devhaozi cc26427417 chore(l10n): update frontend pot files 2025-04-14 10:03:45 +00:00
renovate[bot] 592af262da chore(deps): Update non-major dependencies (#679)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-14 18:03:17 +08:00
renovate[bot] cf64103e36 chore(deps): Lock file maintenance (#678)
* chore(deps): Lock file maintenance

* chore(l10n): update frontend pot files

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-14 18:03:09 +08:00
耗子 044b3664ed feat: 优化nginx解析器错误返回 2025-04-14 15:46:11 +08:00
耗子 99eb46c2a9 l10n: update translations (#677)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* chore(l10n): update frontend pot files
2025-04-13 20:53:08 +08:00
devhaozi 2cae2fc807 chore(l10n): update frontend pot files 2025-04-13 12:26:23 +00:00
耗子 bfc0ff5c28 fix: 漏了一个翻译 2025-04-13 20:25:41 +08:00
devhaozi 504210cee1 chore(l10n): update frontend pot files 2025-04-13 11:48:07 +00:00
耗子 1d6d388d37 l10n: update translations (#676)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* chore(l10n): update frontend pot files
2025-04-13 19:47:30 +08:00
devhaozi 727ff7a52d chore(l10n): update backend pot files 2025-04-13 11:45:08 +00:00
耗子 8638dcb9c8 fix: php 拓展和负载页报错 2025-04-13 19:44:37 +08:00
耗子 d641c78000 l10n: update translations (#675)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* chore(l10n): update frontend pot files
2025-04-13 19:08:54 +08:00
耗子 49131d1d00 fix: 修复code-server服务名错误 2025-04-13 18:48:18 +08:00
devhaozi ccc43cd175 chore(l10n): update frontend pot files 2025-04-13 10:34:25 +00:00
耗子 0131f464cd l10n: update translations (#674)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* chore(l10n): update frontend pot files

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* chore(l10n): update frontend pot files

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* chore(l10n): update frontend pot files

* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-13 18:33:48 +08:00
devhaozi 752bc0a64e chore(l10n): update frontend pot files 2025-04-13 10:31:16 +00:00
耗子 fac981e1ee fix: lint 2025-04-13 18:30:47 +08:00
耗子 c6ce1a403d Merge remote-tracking branch 'origin/main' 2025-04-13 18:14:10 +08:00
耗子 79f7b4a510 fix: build 2025-04-13 18:14:05 +08:00
devhaozi 815418627f chore(l10n): update frontend pot files 2025-04-13 10:10:31 +00:00
耗子 d82e59b4eb feat: 新增code server应用,close #82 2025-04-13 18:09:55 +08:00
耗子 a6a42f0f11 Merge remote-tracking branch 'origin/main' 2025-04-13 17:00:50 +08:00
耗子 4d6b62ac18 feat: api支持传递locale,close #155 2025-04-13 17:00:43 +08:00
devhaozi 0547157c69 chore(l10n): update frontend pot files 2025-04-13 08:55:53 +00:00
耗子 493da1552d feat: 添加App翻译 2025-04-13 16:55:16 +08:00
devhaozi 0402427b71 chore(l10n): update frontend pot files 2025-04-13 07:49:53 +00:00
耗子 623dcbce1c fix: 菜单翻译无法被自动读取 2025-04-13 15:49:23 +08:00
耗子 98d9af89ec l10n: update translations (#672)
* New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot

* New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot

* chore(l10n): update frontend pot files
2025-04-13 15:21:15 +08:00
devhaozi b4546ec0ba chore(l10n): update frontend pot files 2025-04-12 22:18:13 +00:00
耗子 f140eb3f5f Merge remote-tracking branch 'origin/main' 2025-04-13 06:17:37 +08:00
耗子 432b5466e8 feat: 实现语言切换 2025-04-13 06:17:25 +08:00
devhaozi 402562ba93 chore(l10n): update frontend pot files 2025-04-12 21:04:03 +00:00
耗子 77bcf6e086 feat: 统一去掉按钮居中,不然显示效果太抽象了 2025-04-13 05:03:29 +08:00
devhaozi d9fee73e87 chore(l10n): update frontend pot files 2025-04-12 20:52:32 +00:00
耗子 e512187b60 Merge remote-tracking branch 'origin/main' 2025-04-13 04:52:04 +08:00
耗子 31a0617383 Merge remote-tracking branch 'origin/l10n'
# Conflicts:
#	web/src/locales/zh_CN.po
#	web/src/locales/zh_TW.po
2025-04-13 04:51:52 +08:00
耗子 98d80cd359 New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot
2025-04-13 04:50:49 +08:00
耗子 b0cae832c5 New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-13 04:46:11 +08:00
耗子 00b9a859f8 New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot
2025-04-13 04:46:10 +08:00
devhaozi 8b5a7ece43 chore(l10n): update frontend pot files 2025-04-12 20:45:57 +00:00
耗子 391b2a9c68 Merge remote-tracking branch 'origin/main' 2025-04-13 04:45:23 +08:00
耗子 cf6deb665c feat: format files 2025-04-13 04:45:18 +08:00
耗子 096890bd2f New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-13 04:42:27 +08:00
耗子 4dba4e5216 New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot
2025-04-13 04:42:25 +08:00
devhaozi 2b63c50434 chore(l10n): update frontend pot files 2025-04-12 20:22:39 +00:00
耗子 0a2d9262c9 feat: 统一翻译 2025-04-13 04:22:05 +08:00
耗子 4693cc8fe3 New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-13 03:41:34 +08:00
耗子 b6bad9ea64 New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot
2025-04-13 03:41:33 +08:00
耗子 a3b0bf10e9 Merge remote-tracking branch 'origin/main' 2025-04-13 03:41:07 +08:00
耗子 a627816694 feat: 优化样式 2025-04-13 03:40:56 +08:00
devhaozi a4ea9298a4 chore(l10n): update frontend pot files 2025-04-12 19:38:36 +00:00
耗子 233bb6e23b Merge remote-tracking branch 'origin/main' 2025-04-13 03:38:03 +08:00
耗子 bdd20107c7 feat: 调整部分翻译 2025-04-13 03:37:57 +08:00
devhaozi e296008112 chore(l10n): update frontend pot files 2025-04-12 19:37:47 +00:00
耗子 3dd01e6c1f Merge remote-tracking branch 'origin/main' 2025-04-13 03:37:10 +08:00
耗子 5b633e6528 feat: 调整部分翻译 2025-04-13 03:37:03 +08:00
devhaozi 2da260bba0 chore(l10n): update frontend pot files 2025-04-12 19:23:38 +00:00
耗子 acd8403e4b Merge remote-tracking branch 'origin/main' 2025-04-13 03:23:09 +08:00
耗子 90e66175ef feat: 调整部分翻译 2025-04-13 03:23:01 +08:00
devhaozi 4f1a7cc1bf chore(l10n): update frontend pot files 2025-04-12 19:16:38 +00:00
耗子 053d7d3ca5 Merge remote-tracking branch 'origin/main' 2025-04-13 03:16:08 +08:00
耗子 dae0e8274a feat: format files 2025-04-13 03:16:04 +08:00
devhaozi 2b3d1cd220 chore(l10n): update frontend pot files 2025-04-12 19:06:53 +00:00
耗子 5f0a4ca89e feat: format files 2025-04-13 03:06:22 +08:00
耗子 67d350ebe2 ci: 测试换一种清理方式 2025-04-13 02:44:06 +08:00
耗子 66c30f3e33 ci: 清理傻逼node_modules 2025-04-13 02:43:04 +08:00
耗子 82d763fdee Merge remote-tracking branch 'origin/main' 2025-04-13 02:42:45 +08:00
耗子 7159c4cccf ci: 清理傻逼node_modules 2025-04-13 02:42:40 +08:00
耗子 60357222df New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-13 02:40:14 +08:00
耗子 f6c43cf5a0 New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot
2025-04-13 02:40:13 +08:00
devhaozi f1341c5364 chore(l10n): update frontend pot files 2025-04-12 18:37:37 +00:00
耗子 b8cb8d70d3 ci: fix vue3-gettext 2025-04-13 02:37:03 +08:00
耗子 fa7fca1f11 feat: 完善翻译工作流 2025-04-13 02:35:22 +08:00
耗子 59cfa096b4 feat: 添加前端翻译 2025-04-13 02:26:23 +08:00
耗子 706c04a21f New translations frontend.pot (Chinese Traditional)
Update translations (Chinese Traditional) frontend.pot
2025-04-13 02:05:10 +08:00
耗子 255284caad New translations frontend.pot (Chinese Simplified)
Update translations (Chinese Simplified) frontend.pot
2025-04-13 02:05:09 +08:00
耗子 85b34fc017 New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-04-13 02:05:07 +08:00
耗子 d42deb54eb New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot
2025-04-13 02:05:06 +08:00
耗子 002104b0eb feat: 添加前端翻译 2025-04-13 01:55:19 +08:00
耗子 4dc943438e feat: 添加前端翻译 2025-04-13 01:45:40 +08:00
耗子 81a320cd93 feat: 添加前端翻译 2025-04-13 01:30:26 +08:00
耗子 df32b3b5de feat: 提交部分前端翻译 2025-04-13 01:14:11 +08:00
耗子 db0679cd92 feat: 提交部分前端翻译 2025-04-13 00:34:03 +08:00
耗子 20bbbc6202 Merge remote-tracking branch 'origin/main' 2025-04-13 00:28:02 +08:00
耗子 ce652f45e6 feat: 提交部分前端翻译 2025-04-13 00:27:56 +08:00
devhaozi c0f7183bc8 chore(l10n): update pot files 2025-04-12 15:24:19 +00:00
耗子 60d8ccd1a0 feat: 补全命令行翻译 2025-04-12 23:23:47 +08:00
耗子 e9561af93a Merge remote-tracking branch 'origin/main' 2025-04-12 23:01:30 +08:00
耗子 7e0ad2c799 feat: 前端支持翻译 2025-04-12 23:01:09 +08:00
renovate[bot] fe9f1c6e95 chore(deps): Update dependency @iconify/json to v2.2.326 (#663)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-12 22:16:29 +08:00
耗子 193ec9a6ae l10n: update translations (#666)
* New translations backend.pot (Chinese Simplified)
Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
Update translations (Chinese Traditional) backend.pot
2025-04-12 22:16:14 +08:00
耗子 72c3bed203 feat: 初始化前端翻译 2025-04-12 22:10:34 +08:00
耗子 2ae259e53a Merge remote-tracking branch 'origin/main' 2025-04-12 20:40:55 +08:00
耗子 3cd25e19de feat: 更新翻译不跳过ci 2025-04-12 20:40:49 +08:00
耗子 9bdffedb7e l10n: update translations (#664)
* New translations backend.pot (Chinese Simplified)
[skip ci] Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
[skip ci] Update translations (Chinese Traditional) backend.pot
2025-04-12 20:37:19 +08:00
耗子 c298a7182e feat: 不限制locale 2025-04-12 20:33:14 +08:00
耗子 d613a06ad2 Merge remote-tracking branch 'origin/main' 2025-04-12 20:15:55 +08:00
耗子 cc2cdaa76b fix: test 2025-04-12 20:15:50 +08:00
devhaozi 2c38a51515 chore(l10n): update pot files 2025-04-12 11:58:49 +00:00
耗子 d2e89353c5 Merge remote-tracking branch 'origin/main' 2025-04-12 19:58:24 +08:00
耗子 998bc02f8c fix: 翻译错误 2025-04-12 19:58:19 +08:00
devhaozi 42e3e20eb8 chore(l10n): update pot files 2025-04-12 11:55:24 +00:00
耗子 225795db74 fix: 翻译错误 2025-04-12 19:54:58 +08:00
耗子 bd35c42eb0 Merge remote-tracking branch 'origin/main' 2025-04-12 19:53:37 +08:00
耗子 d2587e77d4 fix: 翻译错误 2025-04-12 19:53:33 +08:00
devhaozi b0018ee71b chore(l10n): update pot files 2025-04-12 11:50:23 +00:00
耗子 985f4a6e6b fix: 翻译错误 2025-04-12 19:49:50 +08:00
devhaozi cf910c111f chore(l10n): update pot files 2025-04-12 11:23:51 +00:00
耗子 d7e0cb02cd fix: 翻译错误 2025-04-12 19:23:25 +08:00
耗子 c9c6c8b23a Merge remote-tracking branch 'origin/main' 2025-04-12 19:20:06 +08:00
耗子 3d0cb594ae fix: wire gen 2025-04-12 19:20:01 +08:00
devhaozi b614c9097b chore(l10n): update pot files 2025-04-12 11:18:20 +00:00
耗子 78b94fe89c fix: add translation 2025-04-12 19:17:52 +08:00
耗子 90140f4229 fix: lint 2025-04-12 19:09:55 +08:00
devhaozi d36c3b0dae chore(l10n): update pot files 2025-04-12 11:02:14 +00:00
耗子 88b64d6fc8 Merge remote-tracking branch 'origin/main' 2025-04-12 19:01:43 +08:00
耗子 ce4725ed09 fix: lint 2025-04-12 19:01:38 +08:00
devhaozi a2efa9a70b chore(l10n): update pot files 2025-04-12 10:58:38 +00:00
耗子 5126e2030a fix: lint 2025-04-12 18:58:08 +08:00
devhaozi b7b5744f17 chore(l10n): update pot files 2025-04-12 10:56:15 +00:00
耗子 0f08c10f02 feat: 添加翻译 2025-04-12 18:55:47 +08:00
devhaozi 6b4154f4a6 chore(l10n): update pot files 2025-04-12 10:41:36 +00:00
耗子 2d45e84b66 feat: 添加翻译 2025-04-12 18:41:08 +08:00
devhaozi 3f90402164 chore(l10n): update pot files 2025-04-12 09:35:50 +00:00
耗子 53fdcfb715 feat: 添加翻译 2025-04-12 17:35:22 +08:00
耗子 2ff01579d6 l10n: update translations (#660)
* New translations cli.pot (Chinese Simplified)
[skip ci] Update translations (Chinese Simplified) cli.pot

* New translations cli.pot (Chinese Traditional)
[skip ci] Update translations (Chinese Traditional) cli.pot

* New translations cli.pot (English)
[skip ci] Update translations (English) cli.pot

* New translations web.pot (Chinese Simplified)
[skip ci] Update translations (Chinese Simplified) web.pot

* New translations web.pot (Chinese Traditional)
[skip ci] Update translations (Chinese Traditional) web.pot

* New translations web.pot (English)
[skip ci] Update translations (English) web.pot

* New translations backend.pot (Chinese Simplified)
[skip ci] Update translations (Chinese Simplified) backend.pot

* New translations backend.pot (Chinese Traditional)
[skip ci] Update translations (Chinese Traditional) backend.pot

* New translations backend.pot (English)
[skip ci] Update translations (English) backend.pot
2025-04-12 06:40:16 +08:00
renovate[bot] 774d13980c chore(deps): Update dependency @types/node to v22.14.1 (#661)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-12 06:39:47 +08:00
耗子 ae76a0171d fix: lint 2025-04-12 06:13:56 +08:00
耗子 137665b77d fix: lint 2025-04-12 05:57:26 +08:00
耗子 3b7bf35bf3 fix: lint 2025-04-12 05:55:18 +08:00
耗子 0b0c4da3eb fix: crowdin配置目录错误 2025-04-12 05:39:15 +08:00
耗子 7dfcfb98d5 feat: 添加crowdin配置 2025-04-12 05:33:34 +08:00
devhaozi 00b9be6bc9 chore(l10n): update pot files 2025-04-11 21:26:07 +00:00
耗子 771a9d1f28 feat: 完成应用翻译 2025-04-12 05:25:25 +08:00
耗子 3021f466f0 feat: 初步支持l10n 2025-04-12 04:27:10 +08:00
耗子 486a9fb58b feat: 移动embed到pkg下 2025-04-12 03:19:55 +08:00
耗子 dbc3050f2b feat: 更新php拓展,close #658 2025-04-12 02:15:47 +08:00
耗子 dc584a9382 fix: 网站默认使用lets证书,close #659 2025-04-12 00:30:59 +08:00
renovate[bot] e1f6afe08f chore(deps): Update dependency vite to v6.2.6 (#656)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-10 12:34:11 +00:00
renovate[bot] a7c95d929f chore(deps): Update non-major dependencies (#655)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-09 23:29:20 +08:00
耗子 9ff5596aca feat: 临时屏蔽libdns更新 2025-04-09 23:03:32 +08:00
耗子 4369eedfe5 feat: 临时屏蔽libdns更新 2025-04-09 22:57:28 +08:00
renovate[bot] 5af46a5039 chore(deps): Lock file maintenance (#654)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 07:20:36 +00:00
renovate[bot] 4e854515c8 chore(deps): Update module golang.org/x/crypto to v0.37.0 (#653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-06 22:56:10 +00:00
renovate[bot] 552487b9d3 chore(deps): Update non-major dependencies (#652)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-05 22:48:16 +00:00
耗子 8cadfe3e53 Merge remote-tracking branch 'origin/main' 2025-04-05 20:18:50 +08:00
耗子 15c0f28252 fix: lint 2025-04-05 20:18:45 +08:00
renovate[bot] 8e4b73c695 chore(deps): Update non-major dependencies (#651)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-05 20:11:09 +08:00
耗子 cb8f2a0ee1 Merge remote-tracking branch 'origin/main' 2025-04-05 20:10:59 +08:00
耗子 e6b3858937 fix: lint 2025-04-05 20:10:53 +08:00
renovate[bot] 1bdcf0473e chore(deps): Update golangci/golangci-lint-action action to v7 (#644)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-05 19:57:18 +08:00
耗子 edb5e858e4 fix: lint 2025-04-05 19:56:57 +08:00
renovate[bot] cfedad8a1b chore(deps): Update non-major dependencies (#650)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-05 06:30:17 +00:00
renovate[bot] 08a5c66e62 chore(deps): Update non-major dependencies (#649)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-04 06:38:29 +00:00
renovate[bot] 1d59fad7ad chore(deps): Update non-major dependencies (#648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-03 13:46:17 +00:00
renovate[bot] e0145ee9e6 chore(deps): Update non-major dependencies (#647)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-03 05:14:09 +00:00
renovate[bot] 4703d8c3ca chore(deps): Update non-major dependencies (#646)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-02 14:31:13 +00:00
renovate[bot] 8b95095e55 chore(deps): Update non-major dependencies (#645)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-01 23:00:25 +00:00
耗子 709c345201 fix: 允许修改上传的证书 2025-04-01 17:18:30 +08:00
renovate[bot] 288968006a chore(deps): Update non-major dependencies (#643)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-31 16:12:01 +00:00
renovate[bot] 7f619f786d chore(deps): Lock file maintenance (#642)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-31 06:04:10 +00:00
耗子 c9c3de1348 feat: 添加反代说明 2025-03-30 20:02:21 +08:00
耗子 841b1d7d1b feat: 反代配置生成器 2025-03-30 17:51:41 +08:00
耗子 6f9d5370e0 feat: 移除阿里dns的日志输出 2025-03-30 15:41:52 +08:00
耗子 c8f12b3a89 feat: 优化dns处理 2025-03-30 15:27:53 +08:00
耗子 daa3407505 fix: dns 2025-03-30 05:44:43 +08:00
耗子 f5d2d2204f fix: 新建证书后dns账号错误设置0 2025-03-30 04:30:33 +08:00
耗子 04ef3ce442 feat: 更准确的证书续签判断 2025-03-30 04:19:30 +08:00
耗子 aff68bd1c1 chore: 只创建zip安装包 2025-03-30 03:49:55 +08:00
耗子 fca7065dca feat(证书): 支持西部数码 2025-03-30 03:40:08 +08:00
耗子 29330ec457 feat: 新增一批dns提供商 2025-03-30 02:08:40 +08:00
耗子 cc15852944 feat: 新增一批dns提供商 2025-03-30 01:48:07 +08:00
耗子 ecc44b2e92 fix: fuse文件系统没有inode 2025-03-29 17:03:05 +08:00
耗子 2b1e21ba4b fix: test 2025-03-29 15:04:00 +08:00
耗子 7a22469fb1 chore: update deps 2025-03-29 15:02:15 +08:00
耗子 31c063ba68 feat: acme日志记录 2025-03-29 14:57:35 +08:00
耗子 dad868b64c fix: 重写迁移 2025-03-29 14:44:56 +08:00
耗子 16de31c838 Merge remote-tracking branch 'origin/main' 2025-03-29 14:38:28 +08:00
耗子 3c1062a006 fix: 严格所有字段的默认值 2025-03-29 14:38:22 +08:00
renovate[bot] 7017aff841 chore(deps): Update module github.com/go-gormigrate/gormigrate/v2 to v2.1.4 (#640)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-28 23:01:02 +00:00
耗子 334c4bc4c1 chore: update renovate.json 2025-03-29 03:31:51 +08:00
耗子 e07a69ab67 fix: lint 2025-03-28 22:39:35 +08:00
renovate[bot] f4e31c4902 chore(deps): Lock file maintenance (#638)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-28 22:27:14 +08:00
renovate[bot] b2d382c2af chore(deps): Update module github.com/tnb-labs/acmez/v3 to v3.0.0-20250328123917-f204dcee9d09 (#639)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-28 22:25:48 +08:00
耗子 5d0ad333c9 Merge branch 'main' of https://github.com/tnb-labs/panel 2025-03-28 22:24:13 +08:00
耗子 ccf05b8c42 feat: 新增minio 2025-03-28 22:23:05 +08:00
renovate[bot] 684c1b7140 chore(deps): Update module golang.org/x/net to v0.38.0 (#637)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-28 01:52:42 +00:00
renovate[bot] 15e12d083d chore(deps): Update non-major dependencies (#636)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 15:10:51 +08:00
renovate[bot] f6937bb4cb chore(deps): Update dependency cronstrue to v2.57.0 (#635)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-27 04:01:53 +08:00
耗子 8683a85640 feat: 备份支持上传 2025-03-27 03:59:47 +08:00
耗子 7ee6dd4d0e Merge remote-tracking branch 'origin/main' 2025-03-27 03:20:49 +08:00
耗子 1c7c6074a1 feat: 优化备份与恢复体验 2025-03-27 03:20:40 +08:00
devhaozi af865b62a5 chore: update mocks 2025-03-26 18:56:30 +00:00
耗子 69b430d7cb feat(#108): 容器编排 2025-03-27 02:55:45 +08:00
devhaozi 0956385284 chore: update mocks 2025-03-26 18:23:27 +00:00
耗子 46cf1fa7da feat: 优化编排请求体 2025-03-27 02:22:43 +08:00
renovate[bot] ac9b42ca4d chore(deps): Update pnpm to v10.7.0 (#634)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 23:56:06 +08:00
renovate[bot] 264335429a chore(deps): Update dependency unplugin-auto-import to v19.1.2 (#633)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-26 23:52:51 +08:00
renovate[bot] 323e2bf95a chore(deps): Update non-major dependencies (#632)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-25 23:12:45 +00:00
renovate[bot] 6b413d2962 chore(deps): Update module github.com/mholt/acmez/v3 to v3.1.1 (#631)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-25 06:43:44 +00:00
renovate[bot] 83e0e33207 chore(deps): Update non-major dependencies (#630)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 22:08:51 +00:00
renovate[bot] 827df91241 chore(deps): Lock file maintenance (#629)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 06:32:21 +00:00
renovate[bot] f78f414c77 chore(deps): Update dependency md-editor-v3 to v5.4.4 (#628)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-22 14:55:33 +08:00
renovate[bot] dfb73d1730 chore(deps): Update non-major dependencies (#627)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-22 01:22:59 +00:00
renovate[bot] 92c1d136c2 chore(deps): Update non-major dependencies (#626)
* chore(deps): Update non-major dependencies

* chore: update mocks

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-21 16:05:14 +00:00
耗子 45428f3e0b feat: 支持更新编排文件 2025-03-21 03:48:51 +08:00
耗子 139679ed0f feat: 支持更新编排文件 2025-03-21 03:21:44 +08:00
耗子 eed834bd5f feat: 支持更新编排文件 2025-03-21 03:20:08 +08:00
耗子 422c92cca5 feat: 创建compose支持env 2025-03-21 03:18:35 +08:00
耗子 28d699c886 fix: compose中的错误 2025-03-21 02:14:48 +08:00
耗子 f4533c9662 feat: 网站日志备份后重载Nginx 2025-03-20 19:23:41 +08:00
devhaozi ad71a8075b chore: update mocks 2025-03-19 18:52:30 +00:00
耗子 7d39015d97 feat: compose接口 2025-03-20 02:49:18 +08:00
renovate[bot] c3498aa5c5 chore(deps): Update pnpm to v10.6.5 (#624)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-19 16:39:33 +00:00
renovate[bot] 2c3bb5bf91 chore(deps): Update non-major dependencies (#623)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-18 10:19:09 +00:00
renovate[bot] 3c2b6e6488 chore(deps): Update dependency sass to v1.86.0 (#622)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 23:10:52 +00:00
耗子 dff0a61182 feat(#528): 证书支持部署脚本 2025-03-18 03:18:39 +08:00
耗子 d081f1e312 feat(#570): 为部分应用添加运行日志 2025-03-18 02:55:28 +08:00
耗子 46809c8d5b fix: build 2025-03-18 02:45:42 +08:00
耗子 f1246ee779 fix(#612): 优化复制功能 2025-03-18 02:41:06 +08:00
renovate[bot] 03168a5652 chore(deps): Update dependency @vitejs/plugin-vue to v5.2.3 (#621)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 18:31:06 +00:00
renovate[bot] 0e43d25430 chore(deps): Update dependency md-editor-v3 to v5.4.2 (#620)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 11:14:40 +00:00
renovate[bot] 46a9446007 chore(deps): Lock file maintenance (#619)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:43:57 +00:00
renovate[bot] 3761223230 chore(deps): Update dependency @vitejs/plugin-vue to v5.2.2 (#618)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 03:43:36 +00:00
renovate[bot] 836b34e59d chore(deps): Update module github.com/expr-lang/expr to v1.17.0 (#617)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-16 10:20:11 +00:00
renovate[bot] 3dc1106012 chore(deps): Update dependency @guolao/vue-monaco-editor to v1.5.5 (#616)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-15 10:56:09 +00:00
renovate[bot] 3eb8bf793f chore(deps): Update dependency @iconify/json to v2.2.317 (#615)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 19:37:18 +00:00
renovate[bot] cd823c4506 chore(deps): Update module github.com/go-rat/utils to v1.1.4 (#614)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 12:05:54 +00:00
renovate[bot] 36d45ee66f chore(deps): Update non-major dependencies (#611)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 06:33:53 +00:00
renovate[bot] 068d36de3d chore(deps): Update module github.com/ncruces/go-sqlite3 to v0.24.1 (#609)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 01:44:22 +00:00
renovate[bot] e3607b9bc7 chore(deps): Update non-major dependencies (#608)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-12 22:43:43 +00:00
耗子 76a2ff8e43 Merge remote-tracking branch 'origin/main' 2025-03-12 23:08:57 +08:00
耗子 b3e3da6833 feat: 优化关闭数据库连接 2025-03-12 23:08:46 +08:00
dependabot[bot] fa6c574743 chore(deps): bump @babel/helpers (#607)
Bumps the npm_and_yarn group with 1 update in the /web directory: [@babel/helpers](https://github.com/babel/babel/tree/HEAD/packages/babel-helpers).


Updates `@babel/helpers` from 7.26.9 to 7.26.10
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.10/packages/babel-helpers)

---
updated-dependencies:
- dependency-name: "@babel/helpers"
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 22:01:00 +08:00
耗子 e7625a77a8 feat: 优化SQLite配置防止数据库损坏 2025-03-12 21:58:06 +08:00
耗子 e1b3c80771 Merge branch 'main' of https://github.com/tnb-labs/panel 2025-03-12 21:57:53 +08:00
耗子 3e9228cb65 feat: 优化SQLite配置防止数据库损坏 2025-03-12 21:57:06 +08:00
renovate[bot] e8b5bb266f chore(deps): Update non-major dependencies (#606)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-12 11:04:01 +00:00
耗子 a6d27f261b fix: 容器主机端口显示错误 2025-03-12 03:05:07 +08:00
renovate[bot] 1a85571fca chore(deps): Update dependency @rushstack/eslint-patch to v1.11.0 (#605)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-11 02:38:42 +00:00
renovate[bot] 9b4f308367 chore(deps): Update module github.com/go-rat/utils to v1.1.0 (#604)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 22:10:38 +00:00
renovate[bot] aabf49d041 chore(deps): Update module github.com/tnb-labs/acmez/v3 to v3.0.0-20250310074100-3f4cd473d1db (#603)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 09:56:11 +00:00
renovate[bot] 0d22b53b36 chore(deps): Update dependency @vueuse/core to v13 (#601)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 15:46:39 +08:00
renovate[bot] 8d9c25c195 chore(deps): Update dependency @iconify/json to v2.2.315 (#602)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 07:42:28 +00:00
耗子 bfd4fe41d3 ci: fix mockery 2025-03-10 15:38:17 +08:00
renovate[bot] 8400705053 chore(deps): Lock file maintenance (#600)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 03:11:47 +00:00
renovate[bot] b3e72acad8 chore(deps): Update dependency @types/node to v22.13.10 (#599)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-08 10:42:33 +00:00
renovate[bot] 33884c4c67 chore(deps): Update dependency eslint to v9.22.0 (#598)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-07 22:35:54 +00:00
renovate[bot] 45a939571d chore(deps): Update dependency alova to v3.2.10 (#597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-07 14:24:15 +00:00
renovate[bot] ac4f2a8531 chore(deps): Update non-major dependencies (#596)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-07 07:07:55 +00:00
renovate[bot] 7c09b017c9 chore(deps): Update non-major dependencies (#595)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-06 11:05:41 +00:00
renovate[bot] 02c207d0b8 chore(deps): Update module golang.org/x/net to v0.37.0 (#594)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 22:08:03 +00:00
renovate[bot] 6d23f59d6a chore(deps): Update module golang.org/x/crypto to v0.36.0 (#593)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 19:17:35 +00:00
耗子 4c55552dec fix(#592): 某些情况下计划任务创建失败 2025-03-06 01:21:48 +08:00
renovate[bot] 5ba24d11f6 chore(deps): Update dependency @vueuse/core to v12.8.2 (#591)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 11:56:15 +00:00
renovate[bot] bcf557c47e chore(deps): Update dependency eslint-plugin-vue to v10 (#590)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 15:57:39 +08:00
renovate[bot] cac55a4f79 chore(deps): Update dependency @vueuse/core to v12.8.0 (#589)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 06:58:44 +00:00
renovate[bot] 0d26edf6f0 chore(deps): Update dependency eslint-plugin-vue to v9.33.0 (#588)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-05 03:09:56 +00:00
renovate[bot] 7a39ef706c chore(deps): Update module golang.org/x/net to v0.36.0 (#587)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-04 23:17:00 +00:00
renovate[bot] f5c37c0402 chore(deps): Update dependency @iconify/json to v2.2.313 (#586)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-04 12:03:56 +00:00
renovate[bot] 16d571e4ea chore(deps): Update module github.com/go-rat/chix to v1.1.5 (#585)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 22:44:10 +00:00
renovate[bot] b4b75aa9c5 chore(deps): Update dependency @types/node to v22.13.9 (#584)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 19:33:49 +00:00
renovate[bot] 6ed7784a89 chore(deps): Update dependency cronstrue to v2.56.0 (#583)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 15:34:59 +00:00
renovate[bot] d2c7c1109d chore(deps): Lock file maintenance (#582)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 02:58:02 +00:00
renovate[bot] 079a7f7c07 chore(deps): Update dependency prettier to v3.5.3 (#581)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 02:57:19 +00:00
renovate[bot] 923c6a478c chore(deps): Update dependency vue-tsc to v2.2.8 (#580)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-02 12:45:26 +00:00
renovate[bot] 9e0f65cf52 chore(deps): Lock file maintenance (#579)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-02 12:45:07 +00:00
renovate[bot] eab8184b6b chore(deps): Update non-major dependencies (#578)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-02 09:42:37 +00:00
renovate[bot] 3972a17bd1 chore(deps): Update non-major dependencies (#577)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 10:27:29 +00:00
renovate[bot] 34b7b8a10a chore(deps): Update dependency @types/node to v22.13.7 (#576)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 01:42:19 +00:00
renovate[bot] a4da49d8ae chore(deps): Update dependency @types/node to v22.13.6 (#575)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 21:52:59 +00:00
renovate[bot] f8a989fbfc chore(deps): Update non-major dependencies (#574)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 19:37:18 +00:00
renovate[bot] 4497b8fa82 chore(deps): Update dependency vite-plugin-static-copy to v2.3.0 (#573)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 14:52:48 +00:00
耗子 0bb7ccd121 Merge remote-tracking branch 'origin/main' 2025-02-28 16:06:20 +08:00
耗子 b90227017c fix: ssh连接成功后即立即调整窗口大小 2025-02-28 16:04:20 +08:00
renovate[bot] 75d941060f chore(deps): Update dependency alova to v3.2.9 (#572)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 06:17:59 +00:00
renovate[bot] b37be8aae9 chore(deps): Update dependency @iconify/json to v2.2.311 (#571)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 07:17:35 +00:00
renovate[bot] f5a471768b chore(deps): Update non-major dependencies (#569)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 03:43:49 +00:00
renovate[bot] f6ad6b23cb chore(deps): Update github.com/tufanbarisyildirim/gonginx digest to c03497d (#568)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-25 22:47:05 +00:00
renovate[bot] f5b9d3e912 chore(deps): Update dependency vite to v6.2.0 (#567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-25 05:51:07 +00:00
renovate[bot] 9619c3f55b chore(deps): Update dependency sass to v1.85.1 (#566)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-25 02:38:56 +00:00
renovate[bot] 8c08ebb041 chore(deps): Update module golang.org/x/crypto to v0.35.0 (#565)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 18:52:49 +00:00
renovate[bot] 0567319671 chore(deps): Update module github.com/ncruces/go-sqlite3 to v0.23.3 (#564)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 15:34:06 +00:00
renovate[bot] 760c3be9c7 chore(deps): Update dependency @iconify/json to v2.2.310 (#563)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 09:25:24 +00:00
耗子 1084968e99 chore: update ubuntu 2025-02-24 17:21:04 +08:00
renovate[bot] 188c2ffce0 chore(deps): Lock file maintenance (#562)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 03:10:59 +00:00
renovate[bot] f6296e7704 chore(deps): Update dependency vue-tsc to v2.2.4 (#561)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-23 14:38:04 +00:00
renovate[bot] bd149f6524 chore(deps): Update dependency prettier to v3.5.2 (#560)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-22 06:51:11 +00:00
renovate[bot] 989beb3642 chore(deps): Update module golang.org/x/crypto to v0.34.0 (#559)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-22 01:33:27 +00:00
renovate[bot] 5d4d94a5d8 chore(deps): Update non-major dependencies (#558)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-21 22:06:00 +00:00
renovate[bot] 41eca93f12 chore(deps): Update dependency md-editor-v3 to v5.3.2 (#557)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-21 11:18:40 +00:00
renovate[bot] 946def76c5 chore(deps): Lock file maintenance (#556)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 16:09:27 +00:00
renovate[bot] 24768a4078 chore(deps): Update non-major dependencies (#555)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 16:08:52 +00:00
renovate[bot] 7751117dc6 chore(deps): Update dependency md-editor-v3 to v5.3.0 (#554)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 09:56:51 +00:00
renovate[bot] f9f286335d chore(deps): Update unocss monorepo to v66 (#550)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 15:47:23 +08:00
renovate[bot] 1d50da79bb chore(deps): Update dependency vite to v6.1.1 (#553)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-19 12:51:55 +00:00
renovate[bot] 7e78b6eaa3 chore(deps): Update dependency unplugin-vue-components to v28.4.0 (#552)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-19 06:18:58 +00:00
renovate[bot] a098c988a7 chore(deps): Update dependency tsx to v4.19.3 (#551)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-19 01:36:51 +00:00
renovate[bot] 0b04f32aed chore(deps): Update non-major dependencies (#549)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-18 12:18:21 +00:00
renovate[bot] e14c1db060 chore(deps): Update non-major dependencies (#548)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-18 06:41:47 +00:00
renovate[bot] e109e16610 chore(deps): Update dependency md-editor-v3 to v5.2.3 (#547)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 05:47:45 +00:00
renovate[bot] cd3f2b5951 chore(deps): Lock file maintenance (#546)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 02:39:56 +00:00
耗子 fe28e84bf7 Merge remote-tracking branch 'origin/main' 2025-02-16 16:08:51 +08:00
耗子 fe4e3850c9 feat: test new sqlite driver 2025-02-16 16:07:22 +08:00
renovate[bot] 2a070e14c0 chore(deps): Update dependency vue-tsc to v2.2.2 (#545)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-15 18:32:15 +00:00
renovate[bot] 3984f85d57 chore(deps): Update non-major dependencies (#544)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-15 10:36:05 +00:00
耗子 dcd4eb1e49 Merge remote-tracking branch 'origin/main' 2025-02-15 02:48:35 +08:00
耗子 bc62fbadbb fix: redis info 2025-02-15 02:47:50 +08:00
renovate[bot] d17ef0bfda chore(deps): Update non-major dependencies (#543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 18:07:07 +00:00
renovate[bot] 622d08f6f4 chore(deps): Update dependency @vueuse/core to v12.6.1 (#542)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 06:44:38 +00:00
renovate[bot] 2a2fbbf287 chore(deps): Update dependency sass to v1.85.0 (#541)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 03:27:12 +00:00
renovate[bot] 77fa6ec58f chore(deps): Update dependency @types/node to v22.13.4 (#540)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 23:44:36 +00:00
renovate[bot] fae4894cea chore(deps): Update dependency prettier to v3.5.1 (#539)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 16:12:53 +00:00
耗子 b4195459c5 Merge remote-tracking branch 'origin/main' 2025-02-13 17:22:38 +08:00
耗子 5a40cd3910 fix: 优化备份与清理命令输出 2025-02-13 17:21:37 +08:00
renovate[bot] c6971004f5 chore(deps): Update non-major dependencies (#538)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 06:27:58 +00:00
renovate[bot] e6ff46ccac chore(deps): Update dependency pinia to v3.0.1 (#537)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-12 18:36:52 +00:00
耗子 a91d0b79a7 fix: 优化命令参数格式化 2025-02-13 00:19:19 +08:00
耗子 f0a9aceb13 Merge remote-tracking branch 'origin/main' 2025-02-13 00:00:03 +08:00
耗子 c251f73895 feat: 提升go1.24 2025-02-12 23:59:56 +08:00
renovate[bot] 79b8e58412 chore(deps): Update dependency vite-plugin-vue-devtools to v7.7.2 (#536)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-12 15:56:51 +00:00
耗子 73be25dcf7 feat: 提升go1.24 2025-02-12 23:52:13 +08:00
耗子 fff4d3eae2 fix: MySQL不支持重载 2025-02-12 03:07:25 +08:00
耗子 e614e97312 fix: 修复编辑器无法加载文件内容 2025-02-12 03:02:17 +08:00
耗子 7ac448d2c4 feat: 远程文件下载后自动修改为www用户 2025-02-12 02:51:11 +08:00
耗子 08f5c88cee feat: 优化首页弹窗位置 2025-02-12 02:38:17 +08:00
renovate[bot] 779d734dad chore(deps): Update dependency eslint to v9.20.1 (#535)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-11 17:42:25 +00:00
renovate[bot] c3ca61a665 chore(deps): Update dependency pinia to v3 (#534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-12 01:37:59 +08:00
耗子 832367eb2a Merge remote-tracking branch 'origin/main' 2025-02-12 01:36:38 +08:00
耗子 929a799f68 feat: 优化session cookie 2025-02-12 01:36:31 +08:00
renovate[bot] f1cdb19a59 chore(deps): Update non-major dependencies (#533)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-11 12:18:25 +00:00
renovate[bot] 6f588efea7 chore(deps): Lock file maintenance (#532)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 00:30:22 +00:00
renovate[bot] 333e8ae39f chore(deps): Update dependency marked to v15.0.7 (#531)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-10 00:29:49 +00:00
renovate[bot] 12dc49968a chore(deps): Update dependency @vue/eslint-config-typescript to v14.4.0 (#530)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-09 23:53:25 +00:00
耗子 960fc78518 feat: 修正批量删除后的列表刷新 2025-02-10 00:15:20 +08:00
耗子 447c393b46 chore: 优化文案 2025-02-09 23:47:30 +08:00
耗子 a72eb2ba43 feat: 优化文案 2025-02-09 23:37:21 +08:00
耗子 6cff72de98 feat: 优化 2025-02-09 23:11:05 +08:00
耗子 89be5e077a feat: 证书支持关闭自动续签 2025-02-09 22:41:50 +08:00
耗子 3e1caa4f2e fix: lint 2025-02-09 22:19:14 +08:00
耗子 459b71e8cb feat: 全局去除圆角 2025-02-09 22:13:23 +08:00
耗子 8c8a957df1 feat: 前端样式优化 2025-02-09 22:06:07 +08:00
耗子 9648b24d58 Merge remote-tracking branch 'origin/main' 2025-02-09 21:27:10 +08:00
耗子 b5752ac9e5 feat: 路径选择器 close #332 2025-02-09 21:27:00 +08:00
renovate[bot] 6e8cb1d513 chore(deps): Update non-major dependencies (#529)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-09 12:23:49 +00:00
耗子 191c3d8b3f feat: 修正部分链接 2025-02-09 20:19:20 +08:00
耗子 657382f929 feat: 优化一键签发证书 2025-02-09 16:52:55 +08:00
耗子 a64ec36744 feat: 优化代码 2025-02-09 16:38:47 +08:00
耗子 c527af97ca feat: 优化已登录自动跳转仪表盘 2025-02-09 15:51:42 +08:00
耗子 2293646c4c feat: close #527 2025-02-09 04:48:58 +08:00
耗子 23e215b009 feat: alova.js替换axios 2025-02-09 04:42:44 +08:00
耗子 a07c50b3f5 feat: alova.js替换axios 2025-02-09 04:19:44 +08:00
耗子 9956824d54 feat: alova.js替换axios 2025-02-09 03:09:09 +08:00
耗子 6ac5e63684 feat: alova.js替换axios 2025-02-09 02:10:17 +08:00
耗子 23b13ff79d Merge remote-tracking branch 'origin/main' 2025-02-09 02:03:06 +08:00
耗子 4a7742b991 feat: alova.js替换axios 2025-02-09 02:02:55 +08:00
renovate[bot] 377b904459 chore(deps): Update dependency @iconify/json to v2.2.304 (#526)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-08 08:48:33 +00:00
renovate[bot] b30d476a59 chore(deps): Update module golang.org/x/crypto to v0.33.0 (#525)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-08 02:12:52 +00:00
renovate[bot] 3e7629b5b1 chore(deps): Update dependency eslint to v9.20.0 (#524)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-07 21:53:47 +00:00
renovate[bot] 32bda21196 chore(deps): Update dependency md-editor-v3 to v5.2.2 (#523)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-07 08:49:03 +00:00
耗子 d27a29151e feat: alova.js替换axios 2025-02-06 18:18:07 +08:00
renovate[bot] 2b74b35701 chore(deps): Update dependency sass to v1.84.0 (#522)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-06 01:42:03 +00:00
耗子 30b1cf28cb chore: update issue template 2025-02-06 01:58:53 +08:00
耗子 2d17de5266 feat: update goreleaser config 2025-02-06 01:35:18 +08:00
耗子 01eb10bda3 fix: ci 2025-02-06 01:29:58 +08:00
耗子 86376503ac feat: alova.js替换axios 2025-02-06 01:01:18 +08:00
耗子 39fd752825 feat: alova.js替换axios 2025-02-06 00:41:34 +08:00
耗子 0b6eed5fed feat: alova.js替换axios 2025-02-06 00:01:22 +08:00
耗子 9a81c77167 feat: alova.js替换axios 2025-02-05 23:18:36 +08:00
耗子 03c375e7d9 Merge remote-tracking branch 'origin/main' 2025-02-05 22:43:50 +08:00
耗子 645e6d663f feat: alova.js替换axios 2025-02-05 22:43:40 +08:00
renovate[bot] 2b112d735f chore(deps): Update dependency vite to v6.1.0 (#521)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-05 14:38:07 +00:00
耗子 4632cf7d58 Merge remote-tracking branch 'origin/main' 2025-02-05 21:50:31 +08:00
耗子 b15262b5ce feat: 统统变any 2025-02-05 21:49:48 +08:00
renovate[bot] b25456e2fa chore(deps): Update dependency @iconify/json to v2.2.303 (#520)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-05 10:10:15 +00:00
耗子 398bdce676 Merge remote-tracking branch 'origin/main' 2025-02-04 23:52:42 +08:00
耗子 d25c035fc0 feat: axios转alova.js 2025-02-04 23:52:36 +08:00
renovate[bot] 486cbe047b chore(deps): Update module github.com/go-chi/chi/v5 to v5.2.1 (#519)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 13:39:23 +00:00
耗子 06d620ac06 fix: lint 2025-02-04 20:40:47 +08:00
耗子 aa661972cc fix: lint 2025-02-04 20:34:44 +08:00
耗子 74261f4045 fix: 修复恢复数据库压缩备份pattern contains path separator 2025-02-04 20:28:27 +08:00
耗子 50ec26ef03 fix: 部分tips出错时未关闭 2025-02-04 18:47:55 +08:00
renovate[bot] dff6d27e04 chore(deps): Update dependency @types/node to v22.13.1 (#518)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 00:59:00 +00:00
renovate[bot] e3703975ee chore(deps): Lock file maintenance (#517)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-03 02:01:08 +00:00
renovate[bot] 0cd49a3b18 chore(deps): Update non-major dependencies (#516)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-02 08:58:45 +00:00
renovate[bot] 1ad51e0fa0 chore(deps): Update dependency @types/node to v22.13.0 (#515)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-31 20:51:44 +00:00
renovate[bot] 405d8390bb chore(deps): Update dependency vue-i18n to v11.1.0 (#514)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-31 05:43:15 +00:00
renovate[bot] c4727dca27 chore(deps): Update dependency cronstrue to v2.54.0 (#513)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-30 15:03:03 +00:00
renovate[bot] 19d1169491 chore(deps): Update dependency @iconify/json to v2.2.301 (#512)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-30 09:40:43 +00:00
renovate[bot] e95fb840f7 chore(deps): Update module github.com/samber/lo to v1.49.1 (#511)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-28 12:34:36 +00:00
renovate[bot] 2e0def62ba chore(deps): Update dependency @types/node to v22.12.0 (#510)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-28 10:29:48 +00:00
renovate[bot] a4d12f8590 chore(deps): Update dependency @iconify/json to v2.2.300 (#509)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 08:59:23 +00:00
renovate[bot] 8a2c2411af chore(deps): Lock file maintenance (#508)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 01:18:52 +00:00
renovate[bot] e79d4b43cc chore(deps): Update module github.com/samber/lo to v1.49.0 (#507)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 01:18:15 +00:00
renovate[bot] 169b8c74f7 chore(deps): Update non-major dependencies (#506)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-26 08:52:19 +00:00
renovate[bot] 68eb239619 chore(deps): Update dependency @types/node to v22.10.10 (#505)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-23 18:36:50 +00:00
renovate[bot] 93f4797d44 chore(deps): Update non-major dependencies (#504)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-23 09:23:21 +00:00
renovate[bot] 170453e5ec chore(deps): Update dependency @types/node to v22.10.8 (#503)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-23 05:56:05 +00:00
renovate[bot] 6e44504378 chore(deps): Update non-major dependencies (#502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 10:08:54 +00:00
renovate[bot] 27c851ba8e chore(deps): Update module github.com/go-rat/utils to v1.0.9 (#501)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-22 01:02:01 +00:00
renovate[bot] 03c71a3fe6 chore(deps): Update dependency vite to v6.0.11 (#500)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 12:26:49 +00:00
renovate[bot] 95d88cc9a8 chore(deps): Update non-major dependencies (#499)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 22:07:23 +00:00
renovate[bot] db909f1235 chore(deps): Update module github.com/go-rat/utils to v1.0.8 (#498)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 17:53:32 +00:00
renovate[bot] 27c0451d00 chore(deps): Update dependency vite to v6.0.9 (#497)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 14:29:12 +00:00
renovate[bot] 6b66552350 chore(deps): Update non-major dependencies (#496)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 09:08:55 +00:00
renovate[bot] c873f850bb chore(deps): Lock file maintenance (#495)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 01:37:08 +00:00
renovate[bot] 61b93c81e6 chore(deps): Update dependency @vue/eslint-config-prettier to v10.2.0 (#494)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-18 09:22:48 +00:00
耗子 7181390d94 Merge remote-tracking branch 'origin/main' 2025-01-18 01:39:34 +08:00
耗子 2a9a6301ac feat: 优化systemd服务文件权限 2025-01-18 01:39:18 +08:00
renovate[bot] 22222f0fd7 chore(deps): Update dependency @iconify/json to v2.2.297 (#493)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-17 16:54:08 +00:00
renovate[bot] 74380a5d37 chore(deps): Update dependency @iconify/json to v2.2.296 (#492)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-17 09:14:25 +00:00
耗子 43764cf364 Merge remote-tracking branch 'origin/main' 2025-01-17 03:45:25 +08:00
耗子 6cec736467 fix: lint 2025-01-17 03:45:19 +08:00
renovate[bot] d9138ddf89 chore(deps): Update non-major dependencies to v65.4.2 (#491)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 09:51:17 +00:00
renovate[bot] 73c2c31ce6 chore(deps): Update dependency @types/node to v22.10.7 (#490)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-16 05:27:09 +00:00
耗子 f6bd27a0cb feat: 添加新赞助商 2025-01-16 01:36:42 +08:00
耗子 47df0ab396 feat: 添加新赞助商 2025-01-16 01:35:35 +08:00
耗子 35d7049bc1 Merge remote-tracking branch 'origin/main' 2025-01-16 01:29:46 +08:00
耗子 438f7fd458 feat: 添加新赞助商 2025-01-16 01:29:40 +08:00
renovate[bot] 5cc2ae1a32 chore(deps): Update dependency cronstrue to v2.53.0 (#489)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-15 17:04:25 +00:00
耗子 f4af1319c9 Merge remote-tracking branch 'origin/main' 2025-01-16 01:00:26 +08:00
耗子 c966920e29 feat: rename slogGorm to sloggorm 2025-01-16 01:00:21 +08:00
renovate[bot] a80b0ff8d4 chore(deps): Lock file maintenance (#488)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 19:52:06 +00:00
renovate[bot] c1ed51767a chore(deps): Lock file maintenance (#487)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 19:40:55 +00:00
renovate[bot] 7b8575b6dd chore(deps): Update dependency @vue/eslint-config-typescript to v14.3.0 (#486)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 19:40:14 +00:00
renovate[bot] 48c41c7fd5 chore(deps): Update dependency unplugin-icons to v22 (#483)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 11:14:08 +00:00
renovate[bot] 16791f3acb chore(deps): Update unocss monorepo to v65 (#485)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 11:12:03 +00:00
renovate[bot] 34e00731b5 chore(deps): Update dependency unplugin-vue-components to v28 (#484)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 19:07:57 +08:00
renovate[bot] c54bff44cb chore(deps): Update dependency unplugin-auto-import to v19 (#482)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 19:07:33 +08:00
renovate[bot] 3b738406c6 chore(deps): Update non-major dependencies (#481)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-14 10:20:26 +00:00
耗子 3f83119b39 fix: 日志切割脚本没有单引号 2025-01-14 18:06:34 +08:00
耗子 0eae148737 fix: 重命名之后文件列表没有刷新 2025-01-07 15:04:40 +08:00
耗子 7a648097d2 fix: redis支持设置密码情况下查看负载 2025-01-06 04:57:05 +08:00
耗子 6de5e799ea fix: 文件重命名错误提示文件已存在 2025-01-05 20:33:19 +08:00
耗子 36d7575f0b fix: test 2025-01-05 19:56:58 +08:00
耗子 48a49db67b fix: 解压缩不支持.gz 2025-01-05 19:53:19 +08:00
耗子 a573a6e44f feat: 有任务运行则跳过自动更新 2025-01-02 21:52:31 +08:00
耗子 d6b016d5e8 feat: 移除面板证书续签并将证书有效期固定为10年 2025-01-02 21:33:53 +08:00
耗子 645a6d2b98 feat: 自动更新失败后尝试修复 2025-01-02 21:32:13 +08:00
耗子 7dc98096a5 fix: 面板任务 2025-01-02 21:21:27 +08:00
耗子 4f5961d7f7 refactor: 重构应用加载器 2025-01-02 21:11:06 +08:00
耗子 4beb040b3d feat: 初始化默认启用自动更新 2025-01-02 19:21:17 +08:00
耗子 092da898b8 feat: 优化证书参数 2025-01-02 19:20:32 +08:00
耗子 818c5b466b fix: ed25519貌似没法正常使用 2025-01-02 19:13:48 +08:00
耗子 5dc14ef1bb feat(#419): 面板自动更新 2025-01-02 19:00:19 +08:00
耗子 813a8f42cf fix: lint 2025-01-02 18:35:45 +08:00
耗子 47fbbe7134 feat: 修改自动任务日志描述 2025-01-02 18:11:18 +08:00
耗子 50d988a62b feat: 自动续签面板证书 2025-01-02 18:07:07 +08:00
耗子 8e3d929def feat: 使用ed25519生成自签名证书 2025-01-02 18:01:17 +08:00
耗子 3022c91eb3 fix: mysql包含特殊字符连接失败 2025-01-01 21:15:13 +08:00
1295 changed files with 213631 additions and 36314 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' ]
+3 -4
View File
@@ -1,7 +1,6 @@
name: 🐛 报告问题 (Bug Report)
name: ☢️ 报告问题 (Bug Report)
description: 创建一个报告以帮助我们改进 (Create a report to help us improve)
title: "🐛 [Bug] "
labels: [ "☢️ Bug", "bug" ]
type: Bug
body:
- type: markdown
@@ -42,7 +41,7 @@ body:
description: |
请提供面板的版本号。
Please provide the version number of the panel.
placeholder: "2.3.0"
placeholder: "2.4.0"
validations:
required: true
- type: textarea
+1 -2
View File
@@ -1,7 +1,6 @@
name: ✨ 功能请求 (Feature Request)
description: 为这个项目提出一个想法 (Suggest an idea for this project)
title: "✨ [Feature] "
labels: [ "✏️ Feature", "enhancement" ]
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: 42 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/taskqueue/`**: 任务队列运行器(基于 DB 轮询,实现 `types.TaskRunner` 接口)
- **`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 上的实际使用案例
+28 -29
View File
@@ -4,59 +4,62 @@ on:
branches:
- main
pull_request:
permissions:
contents: read
jobs:
frontend:
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Setup pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v6
with:
version: latest
run_install: true
run_install: |
- cwd: web
package_json_file: web/package.json
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: 22
node-version: 24
cache: 'pnpm'
cache-dependency-path: web/pnpm-lock.yaml
- name: Build frontend
working-directory: web
run: |
cp .env.production .env
cp settings/proxy-config.example.ts settings/proxy-config.ts
cp config/proxy-config.example.ts config/proxy-config.ts
pnpm run gettext:compile
pnpm build
- name: Upload frontend
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: frontend
path: web/dist/
backend:
needs: frontend
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
strategy:
matrix:
goarch: [ amd64, arm64 ]
fail-fast: true
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v7
with:
cache: true
go-version: 'stable'
- name: Install dependencies
run: go mod tidy
- name: Download frontend
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: frontend
path: internal/embed/frontend
path: pkg/embed/frontend
- name: Set build info
run: |
echo "VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo '0.0.0')" >> $GITHUB_ENV
@@ -73,23 +76,19 @@ jobs:
GOARCH: ${{ matrix.goarch }}
run: |
LDFLAGS="-s -w --extldflags '-static'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.Version=${VERSION}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.BuildTime=${BUILD_TIME}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.CommitHash=${COMMIT_HASH}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.GoVersion=${GO_VERSION}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.BuildID=${BUILD_ID}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/panel/internal/app.BuildUser=${BUILD_USER}'"
LDFLAGS="${LDFLAGS} -X 'github.com/tnb-labs/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/v3/internal/app.Version=${VERSION}'"
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/v3/internal/app.BuildTime=${BUILD_TIME}'"
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/v3/internal/app.CommitHash=${COMMIT_HASH}'"
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/v3/internal/app.GoVersion=${GO_VERSION}'"
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/v3/internal/app.BuildID=${BUILD_ID}'"
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/v3/internal/app.BuildUser=${BUILD_USER}'"
LDFLAGS="${LDFLAGS} -X 'github.com/acepanel/panel/v3/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@v7
with:
name: backend-${{ matrix.goarch }}
path: |
web-${{ matrix.goarch }}
ace-${{ matrix.goarch }}
cli-${{ matrix.goarch }}
+11 -10
View File
@@ -7,41 +7,42 @@ permissions:
contents: write
jobs:
goreleaser:
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v7
with:
cache: true
go-version: 'stable'
- name: Setup pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v6
with:
version: latest
run_install: true
run_install: |
- cwd: web
package_json_file: web/package.json
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: 22
node-version: 24
cache: 'pnpm'
cache-dependency-path: web/pnpm-lock.yaml
- name: Build frontend
working-directory: web
run: |
cp .env.production .env
cp settings/proxy-config.example.ts settings/proxy-config.ts
cp config/proxy-config.example.ts config/proxy-config.ts
pnpm run gettext:compile
pnpm build
- name: Set environment variables
run: |
echo "GOVERSION=$(go version | cut -d' ' -f3)" >> $GITHUB_ENV
echo "HOSTNAME=$(hostname)" >> $GITHUB_ENV
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
uses: goreleaser/goreleaser-action@v7
with:
version: latest
args: release --clean
-22
View File
@@ -1,22 +0,0 @@
name: Issue Auto Lock
on:
schedule:
- cron: "*/5 * * * *"
workflow_dispatch:
issues:
types: [ opened ]
permissions:
issues: write
contents: read
jobs:
issue-lock:
runs-on: ubuntu-latest
steps:
- name: Set GH_REPO
run: echo "GH_REPO=${{ github.repository }}" >> $GITHUB_ENV
- name: Auto Lock Issue
uses: devhaozi/issue-auto-lock@v1
with:
gh_repo: ${{ github.repository }}
gh_token: ${{ secrets.GITHUB_TOKEN }}
issue_labels: "⭐ No Star"
-27
View File
@@ -1,27 +0,0 @@
name: Issue Auto Reply
on:
issues:
types: [ labeled ]
permissions:
contents: read
jobs:
issue-reply:
permissions:
issues: write
pull-requests: write
runs-on: ubuntu-24.04
steps:
- name: ☢️ Bug
if: github.event.label.name == '☢️ Bug'
uses: actions-cool/issues-helper@v3
with:
actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hi @${{ github.event.issue.user.login }} 👋
感谢提交 Bug,请确保您的描述清晰,日志完整,最好能提供复现步骤,以便我们更快定位问题并解决。
Thanks for submitting a bug, please make sure your description is clear, log is complete, and it is best to provide a reproduction step so that we can locate and solve the problem faster.
![Log](https://github.com/user-attachments/assets/0ce7a32d-cc79-4bef-91fc-5bec1d0ed376)
+60
View File
@@ -0,0 +1,60 @@
name: L10n
on:
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@v7
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
run_install: |
- cwd: web
package_json_file: web/package.json
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: 24
cache: 'pnpm'
cache-dependency-path: web/pnpm-lock.yaml
- name: Setup Go
uses: actions/setup-go@v7
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/ace -out ./pkg/embed/locales
cd web && pnpm run gettext:extract
- 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 }}
+18 -14
View File
@@ -9,28 +9,32 @@ permissions:
jobs:
golangci:
name: golanci-lint
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v7
with:
cache: true
go-version: 'stable'
- name: Check Wire generated code
run: |
go generate ./cmd/ace ./cmd/cli
git diff --exit-code -- cmd/ace/wire_gen.go cmd/cli/wire_gen.go
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v9
with:
skip-cache: true
version: latest
args: --timeout=30m ./...
govulncheck:
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v7
with:
cache: true
go-version: 'stable'
@@ -39,20 +43,20 @@ jobs:
- name: Run Govulncheck
run: govulncheck ./...
frontend:
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Setup pnpm
uses: pnpm/action-setup@v4
uses: pnpm/action-setup@v6
with:
version: latest
run_install: true
run_install: |
- cwd: web
package_json_file: web/package.json
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: 22
node-version: 24
cache: 'pnpm'
cache-dependency-path: web/pnpm-lock.yaml
- name: Run pnpm lint
+10 -3
View File
@@ -3,20 +3,27 @@ on:
push:
branches:
- main
pull_request:
permissions:
contents: write
jobs:
mockery:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Setup Go
uses: actions/setup-go@v7
with:
cache: true
go-version: 'stable'
- name: Install Mockery
run: |
go install github.com/vektra/mockery/v2@latest
- name: Generate Mocks
run: |
~/go/bin/mockery
- uses: stefanzweifel/git-auto-commit-action@v5
git pull
- uses: stefanzweifel/git-auto-commit-action@v7
name: Commit changes
with:
commit_message: "chore: update mocks"
+12 -6
View File
@@ -4,26 +4,32 @@ on:
branches:
- main
pull_request:
permissions:
contents: read
jobs:
unit:
runs-on: ubuntu-24.04
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v7
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
- name: Run tests
run: sudo go test -v -coverprofile="coverage.out" ./...
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v7
with:
file: ./coverage.out
token: ${{ secrets.CODECOV }}
disable_search: true
files: coverage.out
token: ${{ secrets.CODECOV_TOKEN }}
+7
View File
@@ -11,6 +11,8 @@ _cgo_gotypes.go
_cgo_export.*
# 编译文件
/ace
/cli
*.com
*.class
*.dll
@@ -33,6 +35,8 @@ _cgo_export.*
*.log
*.sqlite
*.db
*.db-shm
*.db-wal
config.yml
# 临时文件
@@ -63,3 +67,6 @@ out/
*.sublime*
__debug_bin
.project
# 傻逼 node_modules
node_modules/
+26 -34
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/tnb-labs/panel/internal/app.Version={{ .Version }}'
- -X 'github.com/tnb-labs/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
- -X 'github.com/tnb-labs/panel/internal/app.CommitHash={{ .ShortCommit }}'
- -X 'github.com/tnb-labs/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
- -X 'github.com/tnb-labs/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
- -X 'github.com/tnb-labs/panel/internal/app.BuildUser={{ .Env.USER }}'
- -X 'github.com/tnb-labs/panel/internal/app.BuildHost={{ .Env.HOSTNAME }}'
- -X 'github.com/acepanel/panel/v3/internal/app.Version={{ .Version }}'
- -X 'github.com/acepanel/panel/v3/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
- -X 'github.com/acepanel/panel/v3/internal/app.CommitHash={{ .ShortCommit }}'
- -X 'github.com/acepanel/panel/v3/internal/app.GoVersion={{ .Env.GOVERSION }}'
- -X 'github.com/acepanel/panel/v3/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
- -X 'github.com/acepanel/panel/v3/internal/app.BuildUser={{ .Env.USER }}'
- -X 'github.com/acepanel/panel/v3/internal/app.BuildHost={{ .Env.HOSTNAME }}'
- id: cli
main: ./cmd/cli
binary: cli
@@ -31,36 +34,25 @@ builds:
goarch:
- amd64
- arm64
flags:
- -trimpath
- -buildvcs=false
ldflags:
- -s -w --extldflags "-static"
- -X 'github.com/tnb-labs/panel/internal/app.Version={{ .Version }}'
- -X 'github.com/tnb-labs/panel/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
- -X 'github.com/tnb-labs/panel/internal/app.CommitHash={{ .ShortCommit }}'
- -X 'github.com/tnb-labs/panel/internal/app.GoVersion={{ .Env.GOVERSION }}'
- -X 'github.com/tnb-labs/panel/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
- -X 'github.com/tnb-labs/panel/internal/app.BuildUser={{ .Env.USER }}'
- -X 'github.com/tnb-labs/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/v3/internal/app.Version={{ .Version }}'
- -X 'github.com/acepanel/panel/v3/internal/app.BuildTime={{ .Now.Format "2006-01-02 15:04:05 MST" }}'
- -X 'github.com/acepanel/panel/v3/internal/app.CommitHash={{ .ShortCommit }}'
- -X 'github.com/acepanel/panel/v3/internal/app.GoVersion={{ .Env.GOVERSION }}'
- -X 'github.com/acepanel/panel/v3/internal/app.BuildID={{ .Env.GITHUB_RUN_ID }}'
- -X 'github.com/acepanel/panel/v3/internal/app.BuildUser={{ .Env.USER }}'
- -X 'github.com/acepanel/panel/v3/internal/app.BuildHost={{ .Env.HOSTNAME }}'
archives:
- id: panel
builds:
- web
ids:
- ace
- cli
format: zip
formats: ["zip"]
wrap_in_directory: false
strip_binary_directory: true
files:
+1 -1
View File
@@ -6,6 +6,6 @@ outpkg: "{{.PackageName}}"
filename: "{{.InterfaceName}}.go"
all: True
packages:
github.com/tnb-labs/panel/internal/biz:
github.com/acepanel/panel/v3/internal/biz:
config:
recursive: True
+193
View File
@@ -0,0 +1,193 @@
## 项目概述
AcePanel 是基于 Go 语言开发的新一代 Linux 服务器运维管理面板。项目采用前后端分离架构:
- 后端:Go 1.26 + go-chi 路由 + GORM + Wire 依赖注入
- 前端:Vue 3 + Vite + Pinia + Naive UI + pnpm + xterm.js + Alova.js
## 核心原则
- **效率至上**:快速单元式开发,所有代码注释、文档和回复使用简体中文
- **不写文档**:只写代码,不创建 README、GUIDE 等各种文档
- **改完即退**:完成代码修改后立即退出,用户会手动测试
- **简洁执念**:消除边界情况永远优于增加条件判断,复杂性是万恶之源
- **实用主义**:解决实际问题,而不是假想的威胁
- **闭嘴**:非用户要求不输出任何内容,静默改完代码后直接退出
## 构建和测试
### 后端构建
构建主程序:
```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/taskqueue/`**: 任务队列运行器(基于 DB 轮询,实现 `types.TaskRunner` 接口)
- **`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` 生成依赖注入代码
## 技术栈特定注意事项
### 助手函数(service 层)
在 service 层使用以下助手函数:
- `Success(w, data)`: 返回成功响应
- `Error(w, statusCode, format, args...)`: 返回错误响应
- `ErrorSystem(w, format, args...)`: 返回系统严重错误(500
- `Bind[T](r)`: 绑定请求参数到泛型类型 T
- `Paginate[T](...)`: 构建分页响应
## 代码风格
- 复杂逻辑添加注释说明,简单逻辑不添加注释
- 使用 `github.com/samber/lo` 进行函数式编程辅助
- 后端对外返回的字符串需要尽可能使用 gotext 进行翻译处理
- 前端使用 gettext 进行国际化处理,所有用户可见的字符串都必须使用 gettext 包裹以支持翻译
- 禁止手动编辑前后端的翻译文件,项目由外部 Crowdin 自动化管理
- 前端 HTTP 请求使用 Alova.js 的 useRequest 等助手函数,无需添加 onError 错误处理
- 后端使用 Wire 依赖注入,添加新依赖时需要 `go generate ./...` 重新生成代码
- 无需关注命令注入,SQL注入,文件上传等安全问题(这是服务器面板,所有登录的用户都被视为管理员)
## 配置文件
后端开发配置:
```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 上的实际使用案例
Symlink
+1
View File
@@ -0,0 +1 @@
AGENTS.md
+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.
+38 -661
View File
@@ -1,661 +1,38 @@
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.
免责声明
本软件是按照现有技术和条件所能达到的现状提供的,用户出于自愿而使用本软件,树新峰(耗子科技)不作任何形式的保证,包括但不限于:
1. 树新峰不对于因使用或无法使用本软件而造成的任何直接、间接、偶然、附带、特殊及后续的「损害」承担责任,即使已被告知此类「损害」的可能性;
2. 树新峰无组织本软件的交流社区的义务与责任,不承担因技术交流导致某一方故障而产生的「损害」;
3. 树新峰不对本软件的无故障、适用性、可用性、准确性、质量满意度等做任何形式的保证;
4. 树新峰不保证本软件可以满足您的要求,也不保证其内容、服务或功能无任何错误或不会中断,不保证任何缺陷将得到更正;
5. 树新峰对本软件提供的任何信息或建议均不构成任何担保;
6. 用户因使用本软件违反国家法律法规的,树新峰不承担任何责任;
7. 本条款所指的「损害」包括但不限于经济损失、利润损失、收入损失、业务中断、数据丢失等。
+54 -43
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/tnb-labs/panel)](https://go.dev/)
[![Release](https://img.shields.io/github/release/tnb-labs/panel.svg)](https://github.com/tnb-labs/panel/releases)
[![Test](https://github.com/tnb-labs/panel/actions/workflows/test.yml/badge.svg)](https://github.com/tnb-labs/panel/actions)
[![Report Card](https://goreportcard.com/badge/github.com/tnb-labs/panel)](https://goreportcard.com/report/github.com/tnb-labs/panel)
[![Stars](https://img.shields.io/github/stars/tnb-labs/panel?style=flat)](https://github.com/tnb-labs/panel)
[![License](https://img.shields.io/github/license/tnb-labs/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/v3)](https://goreportcard.com/report/github.com/acepanel/panel/v3)
[![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>
## 优势
@@ -26,55 +30,62 @@
3. **追随时代:** 整体设计走在时代前沿,对新系统兼容性好,在同类产品中处于领先地位
4. **高效运维:** 功能完善,自定义能力强,既可快速部署小型网站,也可基于定制化需求部署复杂应用
5. **离线运行:** 支持离线模式,甚至可以在部署完成后停止面板进程,不会对已有服务造成任何影响
6. **安全稳定:** 面板采用业界多种技术保障本体安全性,已在我们的多个生环境中长期稳定运行
6. **安全稳定:** 面板采用业界多种技术保障本体安全性,已在我们的多个生环境中长期稳定运行
7. **全面开源:** 少有的全开源面板,您可以在遵守开源协议的前提下对面板自由修改、二次开发
8. **全面开源:** 承诺面板本体未来不会引入任何收费/授权功能,永久免费使用
8. **永久免费:** 承诺面板本体未来不会引入任何收费/授权功能,永久免费使用
## 快速安装
支持 `amd64` | `arm64` 架构下的干净的主流系统,具体支持的系统请参考[安装文档](https://acepanel.github.io/quickstart/install)。
```shell
bash <(curl -sSLm 10 https://dl.acepanel.net/helper.sh)
```
> [!NOTE]
> 耗子面板 2.x -> AcePanel 3.0 迁移说明:[https://tom.moe/t/topic/3540](https://tom.moe/t/topic/3540)
## UI 截图
![UI 截图](.github/assets/ui.png)
## 快速安装
[https://ratpanel.github.io/quickstart/install.html](https://ratpanel.github.io/quickstart/install.html)
## 合作伙伴
如果耗子面板对您有帮助,欢迎赞助我们,同时感谢以下支持者/赞助商的支持:
![支付宝](https://github.com/user-attachments/assets/d000b147-6da1-467a-9d80-9a3e8078602a) ![微信](https://github.com/user-attachments/assets/a53ff212-7076-487e-88bd-c93f6e98df1d)
<a href="https://www.weixiaoduo.com/">
<img height="80" src=".github/assets/wxd.png" alt="微晓朵">
</a>
<a href="https://www.dkdun.cn/aff/MQZZNVHQ">
<img height="80" src=".github/assets/dk.png" alt="林枫云">
</a>
<a href="https://su.sctes.com/register?code=8st689ujpmm2p">
<img height="80" src=".github/assets/sctes.png" alt="无畏云加速">
</a>
<a href="https://su.sctes.com/register?code=8st689ujpmm2p">
<img height="80" src=".github/assets/wafpro.png" alt="WAFPRO">
</a>
<a href="https://scdn.ddunyun.com/">
<img height="80" src=".github/assets/ddunyun.png" alt="盾云SCDN">
</a>
<a href="https://1ms.run">
<img height="80" src=".github/assets/1ms.svg" alt="毫秒镜像提供经过审核的 Docker 镜像加速服务">
</a>
如果 AcePanel 对您有帮助,欢迎[赞助我们](https://github.com/acepanel/panel/issues/90),同时感谢以下支持者/赞助商的支持:
<p align="center">
<a target="_blank" href="https://afdian.com/a/tnblabs">
<img alt="sponsors" src="https://github.com/tnb-labs/sponsor/blob/main/sponsors.svg?raw=true"/>
<a href="https://www.weixiaoduo.com/">
<img height="60" src=".github/assets/wxd.png" alt="微晓朵">
</a>
<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>
<a href="https://scdn.ddunyun.com/">
<img height="60" src=".github/assets/ddunyun.png" alt="盾云SCDN">
</a>
<a href="https://1ms.run/">
<img height="60" src=".github/assets/1ms.svg" alt="毫秒镜像">
</a>
</p>
<p align="center">
<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/#tnb-labs/panel&Date">
<a href="https://www.star-history.com/?repos=acepanel%2Fpanel&type=date&legend=top-left">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=tnb-labs/panel&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=tnb-labs/panel&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=tnb-labs/panel&type=Date" />
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=acepanel/panel&type=date&theme=dark&legend=top-left&sealed_token=A7PSvKwCQys65o30bBWFVq0j-xWQ7q0kkPMbHsRTelnAZTLIVnCIIS18tyeO0LeMqQ4fsyoN1oPeM9dPIHpa1x03AV47vz7Q880xdcO7CH-y6qzXLCMO6g" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=acepanel/panel&type=date&legend=top-left&sealed_token=A7PSvKwCQys65o30bBWFVq0j-xWQ7q0kkPMbHsRTelnAZTLIVnCIIS18tyeO0LeMqQ4fsyoN1oPeM9dPIHpa1x03AV47vz7Q880xdcO7CH-y6qzXLCMO6g" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=acepanel/panel&type=date&legend=top-left&sealed_token=A7PSvKwCQys65o30bBWFVq0j-xWQ7q0kkPMbHsRTelnAZTLIVnCIIS18tyeO0LeMqQ4fsyoN1oPeM9dPIHpa1x03AV47vz7Q880xdcO7CH-y6qzXLCMO6g" />
</picture>
</a>
+49 -41
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/tnb-labs/panel)](https://go.dev/)
[![Release](https://img.shields.io/github/release/tnb-labs/panel.svg)](https://github.com/tnb-labs/panel/releases)
[![Test](https://github.com/tnb-labs/panel/actions/workflows/test.yml/badge.svg)](https://github.com/tnb-labs/panel/actions)
[![Report Card](https://goreportcard.com/badge/github.com/tnb-labs/panel)](https://goreportcard.com/report/github.com/tnb-labs/panel)
[![Stars](https://img.shields.io/github/stars/tnb-labs/panel?style=flat)](https://github.com/tnb-labs/panel)
[![License](https://img.shields.io/github/license/tnb-labs/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>
Enterprise-level 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/v3)](https://goreportcard.com/report/github.com/acepanel/panel/v3)
[![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
@@ -30,51 +34,55 @@ Website: [panel.haozi.net](https://panel.haozi.net) | QQ group: [12370907](https
7. **Fully Open Source:** Few fully open source panels, you can freely modify and develop the panel on the premise of complying with the open source agreement
8. **Permanently free:** Commit to the panel body will not introduce any charging/authorization functions in the future, and will be permanently free to use
## Quick Install
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
bash <(curl -sSLm 10 https://dl.acepanel.net/helper.sh)
```
## UI Screenshots
![UI Screenshots](.github/assets/ui.png)
## Quick Install
[https://ratpanel.github.io/quickstart/install.html](https://ratpanel.github.io/quickstart/install.html)
## Partners
If the Rat Panel is helpful to you, welcome to sponsor us, also thanks to the following supporters/sponsors:
![Alipay](https://github.com/user-attachments/assets/d000b147-6da1-467a-9d80-9a3e8078602a) ![WeChat](https://github.com/user-attachments/assets/a53ff212-7076-487e-88bd-c93f6e98df1d)
<a href="https://www.weixiaoduo.com/">
<img height="80" src=".github/assets/wxd.png" alt="微晓朵">
</a>
<a href="https://www.dkdun.cn/aff/MQZZNVHQ">
<img height="80" src=".github/assets/dk.png" alt="林枫云">
</a>
<a href="https://su.sctes.com/register?code=8st689ujpmm2p">
<img height="80" src=".github/assets/sctes.png" alt="无畏云加速">
</a>
<a href="https://su.sctes.com/register?code=8st689ujpmm2p">
<img height="80" src=".github/assets/wafpro.png" alt="WAFPRO">
</a>
<a href="https://scdn.ddunyun.com/">
<img height="80" src=".github/assets/ddunyun.png" alt="盾云SCDN">
</a>
<a href="https://1ms.run">
<img height="80" src=".github/assets/1ms.svg" alt="毫秒镜像提供经过审核的 Docker 镜像加速服务">
</a>
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 target="_blank" href="https://afdian.com/a/tnblabs">
<img alt="sponsors" src="https://github.com/tnb-labs/sponsor/blob/main/sponsors.svg?raw=true"/>
<a href="https://www.weixiaoduo.com/">
<img height="60" src=".github/assets/wxd.png" alt="微晓朵">
</a>
<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>
<a href="https://scdn.ddunyun.com/">
<img height="60" src=".github/assets/ddunyun.png" alt="盾云SCDN">
</a>
<a href="https://1ms.run/">
<img height="60" src=".github/assets/1ms.svg" alt="毫秒镜像">
</a>
</p>
<p align="center">
<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/#tnb-labs/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=tnb-labs/panel&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=tnb-labs/panel&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=tnb-labs/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>
+22 -16
View File
@@ -1,25 +1,31 @@
## 安全说明
## Supported Versions
**我们不对面板的安全性做任何保证**,但安全性是我们最关心的问题之一,我们已在多个不同应用的生产环境广泛应用耗子面板,至今无任何安全事故。
如果您在面板中发现任何安全问题,请勿提交 Issue,可通过以下方式直接联系我们:
- 邮箱:[admin@haozi.net](mailto:admin@haozi.net)
- QQ826896000
提前感谢您的支持与帮助!
致某些安全初学者:通过面板 `session` / `access_token` 执行的任何操作(包括且不限于:获取 root 权限、读取/写入系统敏感文件、执行任意 shell 命令等)均不被认为是安全问题,请不要刷此类报告浪费彼此的时间,这类低水平的报告对你的简历也没有任何帮助。
| Version | Supported |
|---------|--------------------|
| v3.x | :white_check_mark: |
| v2.x | :white_check_mark: |
| v1.x | :x: |
## Security Policy
**We do not make any guarantees about the security of the panel**, but security is one of our top concerns. We have widely used the Rat panel in the production environment of multiple different applications, and there has been no security incidents to date.
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:
If you find any security issues while using AcePanel, please do not submit an Issue. You can contact us directly through the following methods:
- (Recommend) [GitHub Security Advisories](https://github.com/acepanel/panel/security/advisories/new)
- Email: [admin@haozi.net](mailto:admin@haozi.net)
- Telegram: @devhaozi
Thank you in advance for your support and help!
Please do not apply for CVE, CNVD, or similar vulnerability IDs without contacting us first. Once an issue has been confirmed and fixed, we will assist with the related disclosure process. Thank you in advance for your support and cooperation!
To some security beginners: Any operation performed through the panel `session` / `access_token` (including but not limited to: obtaining root permissions, reading/writing sensitive system files, executing arbitrary shell commands, etc.) is not considered a security issue. Please do not waste each other's time by submitting such reports. Such low-level reports are of no help to your resume.
**Special Note:** AcePanel is designed with high-privilege operational capabilities. Any action performed under a valid authenticated session (such as `session` or `access_token`), including but not limited to obtaining root privileges, reading or writing sensitive system files, or executing arbitrary shell commands, is considered within the intended design scope and does not constitute a security issue. Please do not submit reports of this nature, as they are effectively self-compromise scenarios and only waste time for both parties.
## 安全说明
如果您在 AcePanel 中发现任何安全问题,请勿提交 Issue,可通过以下方式直接联系我们:
- (推荐)[GitHub Security Advisories](https://github.com/acepanel/panel/security/advisories/new)
- 邮箱:[admin@haozi.net](mailto:admin@haozi.net)
- QQ826896000
请勿在未与我们沟通的情况下直接申请 CVE、CNVD 等漏洞编号,相关问题确认并修复后我们会协助处理。提前感谢您的支持与配合!
**特别说明:** AcePanel 本身具备高权限操作能力,基于有效登录态(如 `session``access_token`)所执行的任何操作(包括且不限于:获取 root 权限、读取/写入系统敏感文件、执行任意 shell 命令等)均属于预期设计范围,不视为安全问题。请勿提交此类我攻击我自己的报告浪费彼此时间。
-3
View File
@@ -1,3 +0,0 @@
# cmd
cmd 目录存放应用的入口文件。
+34
View File
@@ -0,0 +1,34 @@
package main
import (
"errors"
"fmt"
"os"
"runtime/debug"
_ "time/tzdata"
)
func main() {
if err := run(); err != nil {
_, _ = fmt.Fprintln(os.Stderr, "Error:", err)
os.Exit(1)
}
}
func run() error {
if os.Geteuid() != 0 {
return errors.New("panel must run as root")
}
debug.SetGCPercent(10)
ace, cleanup, err := initAce()
if err != nil {
return err
}
if cleanup != nil {
defer cleanup()
}
return ace.Run()
}
+29
View File
@@ -0,0 +1,29 @@
//go:build wireinject
package main
import (
"github.com/google/wire"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/apps"
"github.com/acepanel/panel/v3/internal/biz"
"github.com/acepanel/panel/v3/internal/bootstrap"
"github.com/acepanel/panel/v3/internal/data"
"github.com/acepanel/panel/v3/internal/job"
"github.com/acepanel/panel/v3/internal/route"
"github.com/acepanel/panel/v3/internal/service"
)
func initAce() (*app.Ace, func(), error) {
panic(wire.Build(
bootstrap.ProviderSet,
apps.ProviderSet,
biz.ProviderSet,
data.ProviderSet,
service.ProviderSet,
route.ProviderSet,
job.ProviderSet,
app.NewAce,
))
}
+945
View File
@@ -0,0 +1,945 @@
// 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/v3/internal/app"
"github.com/acepanel/panel/v3/internal/apps/acewaf"
"github.com/acepanel/panel/v3/internal/apps/apache"
"github.com/acepanel/panel/v3/internal/apps/clickhouse"
"github.com/acepanel/panel/v3/internal/apps/codeserver"
"github.com/acepanel/panel/v3/internal/apps/docker"
"github.com/acepanel/panel/v3/internal/apps/elasticsearch"
"github.com/acepanel/panel/v3/internal/apps/fail2ban"
"github.com/acepanel/panel/v3/internal/apps/frp"
"github.com/acepanel/panel/v3/internal/apps/gitea"
"github.com/acepanel/panel/v3/internal/apps/grafana"
"github.com/acepanel/panel/v3/internal/apps/kafka"
"github.com/acepanel/panel/v3/internal/apps/mariadb"
"github.com/acepanel/panel/v3/internal/apps/memcached"
"github.com/acepanel/panel/v3/internal/apps/minio"
"github.com/acepanel/panel/v3/internal/apps/mongodb"
"github.com/acepanel/panel/v3/internal/apps/mysql"
"github.com/acepanel/panel/v3/internal/apps/nginx"
"github.com/acepanel/panel/v3/internal/apps/openresty"
"github.com/acepanel/panel/v3/internal/apps/opensearch"
"github.com/acepanel/panel/v3/internal/apps/percona"
"github.com/acepanel/panel/v3/internal/apps/pgadmin"
"github.com/acepanel/panel/v3/internal/apps/phpmyadmin"
"github.com/acepanel/panel/v3/internal/apps/podman"
"github.com/acepanel/panel/v3/internal/apps/postgresql"
"github.com/acepanel/panel/v3/internal/apps/prometheus"
"github.com/acepanel/panel/v3/internal/apps/pureftpd"
"github.com/acepanel/panel/v3/internal/apps/redis"
"github.com/acepanel/panel/v3/internal/apps/rocketmq"
"github.com/acepanel/panel/v3/internal/apps/rsync"
"github.com/acepanel/panel/v3/internal/apps/s3fs"
"github.com/acepanel/panel/v3/internal/apps/supervisor"
"github.com/acepanel/panel/v3/internal/apps/valkey"
"github.com/acepanel/panel/v3/internal/biz"
"github.com/acepanel/panel/v3/internal/bootstrap"
"github.com/acepanel/panel/v3/internal/data"
"github.com/acepanel/panel/v3/internal/job"
"github.com/acepanel/panel/v3/internal/middleware"
"github.com/acepanel/panel/v3/internal/route"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/websitestat"
)
import (
_ "time/tzdata"
)
// Injectors from wire.go:
func initAce() (*app.Ace, func(), error) {
acewafApp, err := acewaf.NewApp()
if err != nil {
return nil, nil, err
}
config, err := bootstrap.NewConf()
if err != nil {
return nil, nil, err
}
locale, err := bootstrap.NewT(config)
if err != nil {
return nil, nil, err
}
apacheApp, err := apache.NewApp(locale)
if err != nil {
return nil, nil, err
}
db, err := bootstrap.NewDB(config)
if err != nil {
return nil, nil, err
}
databaseServerRepo, err := data.NewDatabaseServerRepo(db)
if err != nil {
return nil, nil, err
}
settingRepo, err := data.NewSettingRepo(config, db)
if err != nil {
return nil, nil, err
}
clickhouseApp, err := clickhouse.NewApp(locale, databaseServerRepo, settingRepo)
if err != nil {
return nil, nil, err
}
codeserverApp, err := codeserver.NewApp()
if err != nil {
return nil, nil, err
}
dockerApp, err := docker.NewApp()
if err != nil {
return nil, nil, err
}
elasticsearchApp, err := elasticsearch.NewApp(locale)
if err != nil {
return nil, nil, err
}
websiteRepo, err := data.NewWebsiteRepo(db, locale, settingRepo)
if err != nil {
return nil, nil, err
}
fail2banApp, err := fail2ban.NewApp(locale, websiteRepo)
if err != nil {
return nil, nil, err
}
frpApp, err := frp.NewApp()
if err != nil {
return nil, nil, err
}
giteaApp, err := gitea.NewApp()
if err != nil {
return nil, nil, err
}
grafanaApp, err := grafana.NewApp(locale)
if err != nil {
return nil, nil, err
}
kafkaApp, err := kafka.NewApp(locale)
if err != nil {
return nil, nil, err
}
mysqlApp, err := mysql.NewApp(locale, databaseServerRepo, settingRepo)
if err != nil {
return nil, nil, err
}
mariadbApp, err := mariadb.NewApp(mysqlApp)
if err != nil {
return nil, nil, err
}
memcachedApp, err := memcached.NewApp(locale)
if err != nil {
return nil, nil, err
}
minioApp, err := minio.NewApp()
if err != nil {
return nil, nil, err
}
mongodbApp, err := mongodb.NewApp(locale, databaseServerRepo, settingRepo)
if err != nil {
return nil, nil, err
}
nginxApp, err := nginx.NewApp(locale)
if err != nil {
return nil, nil, err
}
openrestyApp, err := openresty.NewApp(nginxApp)
if err != nil {
return nil, nil, err
}
opensearchApp, err := opensearch.NewApp(locale)
if err != nil {
return nil, nil, err
}
perconaApp, err := percona.NewApp(mysqlApp)
if err != nil {
return nil, nil, err
}
pgadminApp, err := pgadmin.NewApp(config, locale, databaseServerRepo)
if err != nil {
return nil, nil, err
}
phpmyadminApp, err := phpmyadmin.NewApp(config, locale, databaseServerRepo)
if err != nil {
return nil, nil, err
}
podmanApp, err := podman.NewApp()
if err != nil {
return nil, nil, err
}
postgresqlApp, err := postgresql.NewApp(locale, databaseServerRepo, settingRepo)
if err != nil {
return nil, nil, err
}
logger, cleanup, err := bootstrap.NewLogger(config)
if err != nil {
return nil, nil, err
}
slogLogger := bootstrap.NewSlog(logger)
notifyChannelRepo, err := data.NewNotifyChannelRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
notifyUsecase, err := biz.NewNotifyUsecase(locale, slogLogger, notifyChannelRepo, settingRepo)
if err != nil {
cleanup()
return nil, nil, err
}
taskRunner, err := bootstrap.NewRunner(notifyUsecase, db, locale, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
taskRepo, err := data.NewTaskRepo(db, locale, slogLogger, taskRunner)
if err != nil {
cleanup()
return nil, nil, err
}
prometheusApp, err := prometheus.NewApp(config, locale, taskRepo)
if err != nil {
cleanup()
return nil, nil, err
}
pureftpdApp, err := pureftpd.NewApp(locale)
if err != nil {
cleanup()
return nil, nil, err
}
redisApp, err := redis.NewApp(locale, databaseServerRepo)
if err != nil {
cleanup()
return nil, nil, err
}
rocketmqApp, err := rocketmq.NewApp(locale)
if err != nil {
cleanup()
return nil, nil, err
}
rsyncApp, err := rsync.NewApp(locale)
if err != nil {
cleanup()
return nil, nil, err
}
s3fsApp, err := s3fs.NewApp(locale)
if err != nil {
cleanup()
return nil, nil, err
}
supervisorApp, err := supervisor.NewApp(locale)
if err != nil {
cleanup()
return nil, nil, err
}
valkeyApp, err := valkey.NewApp(locale, databaseServerRepo)
if err != nil {
cleanup()
return nil, nil, err
}
loader, err := bootstrap.NewLoader(acewafApp, apacheApp, clickhouseApp, codeserverApp, dockerApp, elasticsearchApp, fail2banApp, frpApp, giteaApp, grafanaApp, kafkaApp, mariadbApp, memcachedApp, minioApp, mongodbApp, mysqlApp, nginxApp, openrestyApp, opensearchApp, perconaApp, pgadminApp, phpmyadminApp, podmanApp, postgresqlApp, prometheusApp, pureftpdApp, redisApp, rocketmqApp, rsyncApp, s3fsApp, supervisorApp, valkeyApp)
if err != nil {
cleanup()
return nil, nil, err
}
manager, err := bootstrap.NewSession(config, db, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
appRepo, err := data.NewAppRepo(config, db, locale, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
userTokenRepo, err := data.NewUserTokenRepo(config, db, locale)
if err != nil {
cleanup()
return nil, nil, err
}
middlewares, err := middleware.NewMiddlewares(config, locale, manager, appRepo, userTokenRepo)
if err != nil {
cleanup()
return nil, nil, err
}
validator, err := bootstrap.NewValidator(config, db)
if err != nil {
cleanup()
return nil, nil, err
}
alertRepo, err := data.NewAlertRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
containerRepo, err := data.NewContainerRepo()
if err != nil {
cleanup()
return nil, nil, err
}
alertUsecase, err := biz.NewAlertUsecase(notifyUsecase, loader, locale, slogLogger, alertRepo, appRepo, containerRepo, databaseServerRepo, settingRepo)
if err != nil {
cleanup()
return nil, nil, err
}
alertService, err := service.NewAlertService(alertUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
cacheRepo, err := data.NewCacheRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
appUsecase, err := biz.NewAppUsecase(locale, appRepo, cacheRepo, taskRepo)
if err != nil {
cleanup()
return nil, nil, err
}
cacheUsecase := biz.NewCacheUsecase(cacheRepo)
settingUsecase, err := biz.NewSettingUsecase(locale, slogLogger, settingRepo, taskRepo)
if err != nil {
cleanup()
return nil, nil, err
}
appService, err := service.NewAppService(loader, appUsecase, cacheUsecase, settingUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
backupRepo, err := data.NewBackupRepo(config, db, locale, slogLogger, settingRepo, websiteRepo)
if err != nil {
cleanup()
return nil, nil, err
}
backupUsecase, err := biz.NewBackupUsecase(notifyUsecase, locale, slogLogger, backupRepo)
if err != nil {
cleanup()
return nil, nil, err
}
taskUsecase := biz.NewTaskUsecase(taskRepo)
backupService, err := service.NewBackupService(backupUsecase, taskUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
backupAccountRepo, err := data.NewBackupAccountRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
backupAccountUsecase, err := biz.NewBackupAccountUsecase(locale, slogLogger, backupAccountRepo, settingRepo)
if err != nil {
cleanup()
return nil, nil, err
}
backupStorageService, err := service.NewBackupStorageService(backupAccountUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
certRepo, err := data.NewCertRepo(db, locale, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
certUsecase, err := biz.NewCertUsecase(locale, slogLogger, certRepo, settingRepo)
if err != nil {
cleanup()
return nil, nil, err
}
certService, err := service.NewCertService(certUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
certAccountRepo, err := data.NewCertAccountRepo(db, locale, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
userRepo, err := data.NewUserRepo(db, locale)
if err != nil {
cleanup()
return nil, nil, err
}
certAccountUsecase, err := biz.NewCertAccountUsecase(locale, slogLogger, certAccountRepo, userRepo)
if err != nil {
cleanup()
return nil, nil, err
}
certAccountService, err := service.NewCertAccountService(certAccountUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
certDNSRepo, err := data.NewCertDNSRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
certDNSUsecase := biz.NewCertDNSUsecase(certDNSRepo, slogLogger)
certDNSService, err := service.NewCertDNSService(certDNSUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
containerUsecase := biz.NewContainerUsecase(containerRepo, settingRepo)
containerService, err := service.NewContainerService(containerUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
containerComposeRepo, err := data.NewContainerComposeRepo()
if err != nil {
cleanup()
return nil, nil, err
}
containerComposeUsecase := biz.NewContainerComposeUsecase(containerComposeRepo)
containerComposeService, err := service.NewContainerComposeService(containerComposeUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
containerImageRepo, err := data.NewContainerImageRepo()
if err != nil {
cleanup()
return nil, nil, err
}
containerImageUsecase := biz.NewContainerImageUsecase(containerImageRepo, settingRepo)
containerImageService, err := service.NewContainerImageService(containerImageUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
containerNetworkRepo, err := data.NewContainerNetworkRepo()
if err != nil {
cleanup()
return nil, nil, err
}
containerNetworkUsecase := biz.NewContainerNetworkUsecase(containerNetworkRepo, settingRepo)
containerNetworkService, err := service.NewContainerNetworkService(containerNetworkUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
containerVolumeRepo, err := data.NewContainerVolumeRepo()
if err != nil {
cleanup()
return nil, nil, err
}
containerVolumeUsecase := biz.NewContainerVolumeUsecase(containerVolumeRepo, settingRepo)
containerVolumeService, err := service.NewContainerVolumeService(containerVolumeUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
cronRepo, err := data.NewCronRepo(db, locale)
if err != nil {
cleanup()
return nil, nil, err
}
cronUsecase := biz.NewCronUsecase(cronRepo, slogLogger)
cronService, err := service.NewCronService(cronUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
databaseUserRepo, err := data.NewDatabaseUserRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
databaseUserUsecase, err := biz.NewDatabaseUserUsecase(slogLogger, databaseServerRepo, databaseUserRepo)
if err != nil {
cleanup()
return nil, nil, err
}
databaseRepo, err := data.NewDatabaseRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
databaseUsecase, err := biz.NewDatabaseUsecase(databaseUserUsecase, locale, slogLogger, databaseRepo, databaseServerRepo)
if err != nil {
cleanup()
return nil, nil, err
}
databaseService, err := service.NewDatabaseService(databaseUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
databaseElasticsearchRepo, err := data.NewDatabaseElasticsearchRepo(db, locale, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
databaseElasticsearchUsecase := biz.NewDatabaseElasticsearchUsecase(databaseElasticsearchRepo)
databaseElasticsearchService, err := service.NewDatabaseElasticsearchService(databaseElasticsearchUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
databaseRedisRepo, err := data.NewDatabaseRedisRepo(db, locale, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
databaseRedisUsecase := biz.NewDatabaseRedisUsecase(databaseRedisRepo)
databaseRedisService, err := service.NewDatabaseRedisService(databaseRedisUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
databaseServerUsecase, err := biz.NewDatabaseServerUsecase(locale, slogLogger, databaseServerRepo)
if err != nil {
cleanup()
return nil, nil, err
}
databaseServerService, err := service.NewDatabaseServerService(databaseServerUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
databaseUserService, err := service.NewDatabaseUserService(databaseUserUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
environmentRepo, err := data.NewEnvironmentRepo(config, locale)
if err != nil {
cleanup()
return nil, nil, err
}
environmentUsecase, err := biz.NewEnvironmentUsecase(locale, cacheRepo, environmentRepo, taskRepo)
if err != nil {
cleanup()
return nil, nil, err
}
environmentService, err := service.NewEnvironmentService(environmentUsecase, taskUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
environmentDotnetService, err := service.NewEnvironmentDotnetService(environmentUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
environmentGoService, err := service.NewEnvironmentGoService(environmentUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
environmentJavaService, err := service.NewEnvironmentJavaService(environmentUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
environmentNodejsService, err := service.NewEnvironmentNodejsService(environmentUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
environmentPHPService, err := service.NewEnvironmentPHPService(environmentUsecase, taskUsecase, config, locale)
if err != nil {
cleanup()
return nil, nil, err
}
environmentPythonService, err := service.NewEnvironmentPythonService(environmentUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
tamperRepo, err := data.NewTamperRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
tamperUsecase, err := biz.NewTamperUsecase(notifyUsecase, settingUsecase, locale, slogLogger, tamperRepo)
if err != nil {
cleanup()
return nil, nil, err
}
fileService, err := service.NewFileService(containerUsecase, tamperUsecase, taskUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
fileShareRepo, err := data.NewFileShareRepo(db, locale)
if err != nil {
cleanup()
return nil, nil, err
}
fileShareUsecase, err := biz.NewFileShareUsecase(slogLogger, fileShareRepo)
if err != nil {
cleanup()
return nil, nil, err
}
fileShareService, err := service.NewFileShareService(fileShareUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
firewallService, err := service.NewFirewallService(locale)
if err != nil {
cleanup()
return nil, nil, err
}
scanEventRepo, err := data.NewScanEventRepo()
if err != nil {
cleanup()
return nil, nil, err
}
scanEventUsecase := biz.NewScanEventUsecase(scanEventRepo, settingRepo)
firewallScanService, err := service.NewFirewallScanService(scanEventUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
projectRepo, err := data.NewProjectRepo(db, locale)
if err != nil {
cleanup()
return nil, nil, err
}
projectUsecase, err := biz.NewProjectUsecase(locale, slogLogger, projectRepo)
if err != nil {
cleanup()
return nil, nil, err
}
websiteStatRepo, err := data.NewWebsiteStatRepo()
if err != nil {
cleanup()
return nil, nil, err
}
websiteStatUsecase := biz.NewWebsiteStatUsecase(websiteStatRepo)
websiteUsecase, err := biz.NewWebsiteUsecase(certAccountUsecase, certUsecase, databaseUsecase, databaseUserUsecase, tamperUsecase, websiteStatUsecase, locale, slogLogger, databaseServerRepo, websiteRepo)
if err != nil {
cleanup()
return nil, nil, err
}
homeService, err := service.NewHomeService(appUsecase, backupUsecase, containerUsecase, cronUsecase, databaseServerUsecase, environmentUsecase, projectUsecase, settingUsecase, taskUsecase, websiteUsecase, config, locale)
if err != nil {
cleanup()
return nil, nil, err
}
logRepo, err := data.NewLogRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
logUsecase := biz.NewLogUsecase(logRepo)
logService, err := service.NewLogService(logUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
monitorRepo, err := data.NewMonitorRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
monitorUsecase := biz.NewMonitorUsecase(monitorRepo, settingRepo)
monitorService, err := service.NewMonitorService(monitorUsecase, settingUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
notifyService, err := service.NewNotifyService(notifyUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
processService, err := service.NewProcessService()
if err != nil {
cleanup()
return nil, nil, err
}
projectService, err := service.NewProjectService(projectUsecase, settingUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
safeRepo, err := data.NewSafeRepo()
if err != nil {
cleanup()
return nil, nil, err
}
safeUsecase := biz.NewSafeUsecase(safeRepo, slogLogger)
safeService, err := service.NewSafeService(safeUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
settingService, err := service.NewSettingService(certAccountUsecase, certUsecase, settingUsecase, db, locale)
if err != nil {
cleanup()
return nil, nil, err
}
sshRepo, err := data.NewSSHRepo(db, locale)
if err != nil {
cleanup()
return nil, nil, err
}
sshUsecase := biz.NewSSHUsecase(sshRepo, slogLogger)
sshService, err := service.NewSSHService(sshUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
systemctlService, err := service.NewSystemctlService(locale)
if err != nil {
cleanup()
return nil, nil, err
}
tamperService, err := service.NewTamperService(tamperUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
taskService, err := service.NewTaskService(taskUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
templateRepo, err := data.NewTemplateRepo(slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
templateUsecase, err := biz.NewTemplateUsecase(locale, cacheRepo, templateRepo)
if err != nil {
cleanup()
return nil, nil, err
}
templateService, err := service.NewTemplateService(settingUsecase, templateUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
toolboxBenchmarkService, err := service.NewToolboxBenchmarkService(locale)
if err != nil {
cleanup()
return nil, nil, err
}
toolboxDiskService, err := service.NewToolboxDiskService(locale)
if err != nil {
cleanup()
return nil, nil, err
}
toolboxLogService, err := service.NewToolboxLogService(containerImageUsecase, settingUsecase, db, locale)
if err != nil {
cleanup()
return nil, nil, err
}
toolboxMigrationService, err := service.NewToolboxMigrationService(appUsecase, databaseServerUsecase, databaseUsecase, databaseUserUsecase, environmentUsecase, projectUsecase, settingUsecase, websiteUsecase, config, locale, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
toolboxNetworkService, err := service.NewToolboxNetworkService()
if err != nil {
cleanup()
return nil, nil, err
}
toolboxSSHService, err := service.NewToolboxSSHService(locale)
if err != nil {
cleanup()
return nil, nil, err
}
toolboxSystemService, err := service.NewToolboxSystemService(locale)
if err != nil {
cleanup()
return nil, nil, err
}
userUsecase, err := biz.NewUserUsecase(locale, slogLogger, userRepo)
if err != nil {
cleanup()
return nil, nil, err
}
userService, err := service.NewUserService(notifyUsecase, userUsecase, config, locale, manager)
if err != nil {
cleanup()
return nil, nil, err
}
userPasskeyRepo, err := data.NewUserPasskeyRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
userPasskeyUsecase := biz.NewUserPasskeyUsecase(userPasskeyRepo)
userPasskeyService, err := service.NewUserPasskeyService(notifyUsecase, userPasskeyUsecase, userUsecase, config, locale, manager)
if err != nil {
cleanup()
return nil, nil, err
}
userTokenUsecase := biz.NewUserTokenUsecase(userTokenRepo)
userTokenService, err := service.NewUserTokenService(userTokenUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
webHookRepo, err := data.NewWebHookRepo(db, locale)
if err != nil {
cleanup()
return nil, nil, err
}
webHookUsecase, err := biz.NewWebHookUsecase(locale, slogLogger, webHookRepo)
if err != nil {
cleanup()
return nil, nil, err
}
webHookService, err := service.NewWebHookService(webHookUsecase)
if err != nil {
cleanup()
return nil, nil, err
}
websiteService, err := service.NewWebsiteService(settingUsecase, websiteUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
aggregator := websitestat.NewAggregator()
websiteStatService, err := service.NewWebsiteStatService(settingUsecase, websiteStatUsecase, websiteUsecase, aggregator)
if err != nil {
cleanup()
return nil, nil, err
}
wafRepo, err := data.NewWafRepo(locale, db, slogLogger, settingRepo)
if err != nil {
cleanup()
return nil, nil, err
}
wafUsecase := biz.NewWafUsecase(wafRepo)
wafService, err := service.NewWafService(wafUsecase, locale)
if err != nil {
cleanup()
return nil, nil, err
}
wsService, err := service.NewWsService(backupUsecase, certUsecase, sshUsecase, settingUsecase, taskUsecase, config, locale, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
services := &route.Services{
Alert: alertService,
App: appService,
Backup: backupService,
BackupStorage: backupStorageService,
Cert: certService,
CertAccount: certAccountService,
CertDNS: certDNSService,
Container: containerService,
ContainerCompose: containerComposeService,
ContainerImage: containerImageService,
ContainerNetwork: containerNetworkService,
ContainerVolume: containerVolumeService,
Cron: cronService,
Database: databaseService,
DatabaseElasticsearch: databaseElasticsearchService,
DatabaseRedis: databaseRedisService,
DatabaseServer: databaseServerService,
DatabaseUser: databaseUserService,
Environment: environmentService,
EnvironmentDotnet: environmentDotnetService,
EnvironmentGo: environmentGoService,
EnvironmentJava: environmentJavaService,
EnvironmentNodejs: environmentNodejsService,
EnvironmentPHP: environmentPHPService,
EnvironmentPython: environmentPythonService,
File: fileService,
FileShare: fileShareService,
Firewall: firewallService,
FirewallScan: firewallScanService,
Home: homeService,
Log: logService,
Monitor: monitorService,
Notify: notifyService,
Process: processService,
Project: projectService,
Safe: safeService,
Setting: settingService,
SSH: sshService,
Systemctl: systemctlService,
Tamper: tamperService,
Task: taskService,
Template: templateService,
ToolboxBenchmark: toolboxBenchmarkService,
ToolboxDisk: toolboxDiskService,
ToolboxLog: toolboxLogService,
ToolboxMigration: toolboxMigrationService,
ToolboxNetwork: toolboxNetworkService,
ToolboxSSH: toolboxSSHService,
ToolboxSystem: toolboxSystemService,
User: userService,
UserPasskey: userPasskeyService,
UserToken: userTokenService,
WebHook: webHookService,
Website: websiteService,
WebsiteStat: websiteStatService,
Waf: wafService,
Ws: wsService,
}
v := route.NewEndpoints(services)
mux, err := bootstrap.NewRouter(loader, config, locale, middlewares, validator, v)
if err != nil {
cleanup()
return nil, nil, err
}
dependencies := &job.Dependencies{
Alert: alertUsecase,
Backup: backupUsecase,
Cache: cacheUsecase,
Cert: certUsecase,
CertAccount: certAccountUsecase,
FileShare: fileShareUsecase,
Notify: notifyUsecase,
ScanEvent: scanEventUsecase,
Setting: settingUsecase,
Tamper: tamperUsecase,
Task: taskUsecase,
Website: websiteUsecase,
WebsiteStat: websiteStatUsecase,
Conf: config,
DB: db,
T: locale,
Log: slogLogger,
Aggregator: aggregator,
}
v2 := job.NewJobs(dependencies)
cron, err := bootstrap.NewCron(slogLogger, v2)
if err != nil {
cleanup()
return nil, nil, err
}
gormigrate, err := bootstrap.NewMigrate(db)
if err != nil {
cleanup()
return nil, nil, err
}
reloader, err := bootstrap.NewTLSReloader(config)
if err != nil {
cleanup()
return nil, nil, err
}
server, err := bootstrap.NewHttp(mux, config, reloader)
if err != nil {
cleanup()
return nil, nil, err
}
ace, err := app.NewAce(mux, config, cron, gormigrate, server, reloader, taskRunner)
if err != nil {
cleanup()
return nil, nil, err
}
return ace, func() {
cleanup()
}, nil
}
+22 -27
View File
@@ -1,37 +1,32 @@
/*
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 (
"errors"
"os"
_ "time/tzdata"
"github.com/gookit/color"
)
func main() {
if os.Geteuid() != 0 {
panic("panel must run as root")
}
cli, err := initCli()
if err != nil {
panic(err)
}
if err = cli.Run(); err != nil {
panic(err)
if err := run(); err != nil {
color.Errorf("|-%v\n", err)
os.Exit(1)
}
}
func run() error {
if os.Geteuid() != 0 {
return errors.New("panel must run as root")
}
cli, cleanup, err := initCli()
if err != nil {
return err
}
if cleanup != nil {
defer cleanup()
}
return cli.Run()
}
+15 -8
View File
@@ -5,14 +5,21 @@ package main
import (
"github.com/google/wire"
"github.com/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/bootstrap"
"github.com/tnb-labs/panel/internal/data"
"github.com/tnb-labs/panel/internal/route"
"github.com/tnb-labs/panel/internal/service"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/biz"
"github.com/acepanel/panel/v3/internal/bootstrap"
"github.com/acepanel/panel/v3/internal/command"
"github.com/acepanel/panel/v3/internal/data"
"github.com/acepanel/panel/v3/internal/service"
)
// initCli init command line.
func initCli() (*app.Cli, error) {
panic(wire.Build(bootstrap.ProviderSet, route.ProviderSet, service.ProviderSet, data.ProviderSet, app.NewCli))
func initCli() (*app.Cli, func(), error) {
panic(wire.Build(
bootstrap.ProviderSet,
biz.ProviderSet,
data.ProviderSet,
service.ProviderSet,
command.ProviderSet,
app.NewCli,
))
}
+198 -31
View File
@@ -7,11 +7,12 @@
package main
import (
"github.com/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/bootstrap"
"github.com/tnb-labs/panel/internal/data"
"github.com/tnb-labs/panel/internal/route"
"github.com/tnb-labs/panel/internal/service"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/biz"
"github.com/acepanel/panel/v3/internal/bootstrap"
"github.com/acepanel/panel/v3/internal/command"
"github.com/acepanel/panel/v3/internal/data"
"github.com/acepanel/panel/v3/internal/service"
)
import (
@@ -20,34 +21,200 @@ import (
// Injectors from wire.go:
// initCli init command line.
func initCli() (*app.Cli, error) {
koanf, err := bootstrap.NewConf()
func initCli() (*app.Cli, func(), error) {
config, err := bootstrap.NewConf()
if err != nil {
return nil, err
return nil, nil, err
}
logger := bootstrap.NewLog(koanf)
db, err := bootstrap.NewDB(koanf, logger)
locale, err := bootstrap.NewT(config)
if err != nil {
return nil, err
return nil, nil, err
}
cacheRepo := data.NewCacheRepo(db)
queue := bootstrap.NewQueue()
taskRepo := data.NewTaskRepo(db, logger, queue)
appRepo := data.NewAppRepo(db, cacheRepo, taskRepo)
userRepo := data.NewUserRepo(db)
settingRepo := data.NewSettingRepo(db, koanf, taskRepo)
databaseServerRepo := data.NewDatabaseServerRepo(db, logger)
databaseUserRepo := data.NewDatabaseUserRepo(db, databaseServerRepo)
databaseRepo := data.NewDatabaseRepo(db, databaseServerRepo, databaseUserRepo)
certRepo := data.NewCertRepo(db)
certAccountRepo := data.NewCertAccountRepo(db, userRepo)
websiteRepo := data.NewWebsiteRepo(db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo)
backupRepo := data.NewBackupRepo(db, settingRepo, websiteRepo)
cliService := service.NewCliService(koanf, db, appRepo, cacheRepo, userRepo, settingRepo, backupRepo, websiteRepo, databaseServerRepo)
cli := route.NewCli(cliService)
command := bootstrap.NewCli(cli)
gormigrate := bootstrap.NewMigrate(db)
appCli := app.NewCli(command, gormigrate)
return appCli, nil
db, err := bootstrap.NewDB(config)
if err != nil {
return nil, nil, err
}
logger, cleanup, err := bootstrap.NewLogger(config)
if err != nil {
return nil, nil, err
}
slogLogger := bootstrap.NewSlog(logger)
appRepo, err := data.NewAppRepo(config, db, locale, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
cacheRepo, err := data.NewCacheRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
notifyChannelRepo, err := data.NewNotifyChannelRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
settingRepo, err := data.NewSettingRepo(config, db)
if err != nil {
cleanup()
return nil, nil, err
}
notifyUsecase, err := biz.NewNotifyUsecase(locale, slogLogger, notifyChannelRepo, settingRepo)
if err != nil {
cleanup()
return nil, nil, err
}
taskRunner, err := bootstrap.NewRunner(notifyUsecase, db, locale, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
taskRepo, err := data.NewTaskRepo(db, locale, slogLogger, taskRunner)
if err != nil {
cleanup()
return nil, nil, err
}
appUsecase, err := biz.NewAppUsecase(locale, appRepo, cacheRepo, taskRepo)
if err != nil {
cleanup()
return nil, nil, err
}
websiteRepo, err := data.NewWebsiteRepo(db, locale, settingRepo)
if err != nil {
cleanup()
return nil, nil, err
}
backupRepo, err := data.NewBackupRepo(config, db, locale, slogLogger, settingRepo, websiteRepo)
if err != nil {
cleanup()
return nil, nil, err
}
backupUsecase, err := biz.NewBackupUsecase(notifyUsecase, locale, slogLogger, backupRepo)
if err != nil {
cleanup()
return nil, nil, err
}
cacheUsecase := biz.NewCacheUsecase(cacheRepo)
certAccountRepo, err := data.NewCertAccountRepo(db, locale, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
userRepo, err := data.NewUserRepo(db, locale)
if err != nil {
cleanup()
return nil, nil, err
}
certAccountUsecase, err := biz.NewCertAccountUsecase(locale, slogLogger, certAccountRepo, userRepo)
if err != nil {
cleanup()
return nil, nil, err
}
certRepo, err := data.NewCertRepo(db, locale, slogLogger)
if err != nil {
cleanup()
return nil, nil, err
}
certUsecase, err := biz.NewCertUsecase(locale, slogLogger, certRepo, settingRepo)
if err != nil {
cleanup()
return nil, nil, err
}
cronRepo, err := data.NewCronRepo(db, locale)
if err != nil {
cleanup()
return nil, nil, err
}
cronUsecase := biz.NewCronUsecase(cronRepo, slogLogger)
databaseServerRepo, err := data.NewDatabaseServerRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
databaseServerUsecase, err := biz.NewDatabaseServerUsecase(locale, slogLogger, databaseServerRepo)
if err != nil {
cleanup()
return nil, nil, err
}
settingUsecase, err := biz.NewSettingUsecase(locale, slogLogger, settingRepo, taskRepo)
if err != nil {
cleanup()
return nil, nil, err
}
userPasskeyRepo, err := data.NewUserPasskeyRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
userPasskeyUsecase := biz.NewUserPasskeyUsecase(userPasskeyRepo)
userUsecase, err := biz.NewUserUsecase(locale, slogLogger, userRepo)
if err != nil {
cleanup()
return nil, nil, err
}
databaseUserRepo, err := data.NewDatabaseUserRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
databaseUserUsecase, err := biz.NewDatabaseUserUsecase(slogLogger, databaseServerRepo, databaseUserRepo)
if err != nil {
cleanup()
return nil, nil, err
}
databaseRepo, err := data.NewDatabaseRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
databaseUsecase, err := biz.NewDatabaseUsecase(databaseUserUsecase, locale, slogLogger, databaseRepo, databaseServerRepo)
if err != nil {
cleanup()
return nil, nil, err
}
tamperRepo, err := data.NewTamperRepo(db)
if err != nil {
cleanup()
return nil, nil, err
}
tamperUsecase, err := biz.NewTamperUsecase(notifyUsecase, settingUsecase, locale, slogLogger, tamperRepo)
if err != nil {
cleanup()
return nil, nil, err
}
websiteStatRepo, err := data.NewWebsiteStatRepo()
if err != nil {
cleanup()
return nil, nil, err
}
websiteStatUsecase := biz.NewWebsiteStatUsecase(websiteStatRepo)
websiteUsecase, err := biz.NewWebsiteUsecase(certAccountUsecase, certUsecase, databaseUsecase, databaseUserUsecase, tamperUsecase, websiteStatUsecase, locale, slogLogger, databaseServerRepo, websiteRepo)
if err != nil {
cleanup()
return nil, nil, err
}
cliService, err := service.NewCliService(appUsecase, backupUsecase, cacheUsecase, certAccountUsecase, certUsecase, cronUsecase, databaseServerUsecase, notifyUsecase, settingUsecase, userPasskeyUsecase, userUsecase, websiteUsecase, config, db, locale)
if err != nil {
cleanup()
return nil, nil, err
}
v := command.Commands(locale, cliService)
cliCommand, err := bootstrap.NewCli(locale, v)
if err != nil {
cleanup()
return nil, nil, err
}
gormigrate, err := bootstrap.NewMigrate(db)
if err != nil {
cleanup()
return nil, nil, err
}
cli, err := app.NewCli(cliCommand, gormigrate)
if err != nil {
cleanup()
return nil, nil, err
}
return cli, func() {
cleanup()
}, 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/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/apps"
"github.com/tnb-labs/panel/internal/bootstrap"
"github.com/tnb-labs/panel/internal/data"
"github.com/tnb-labs/panel/internal/http/middleware"
"github.com/tnb-labs/panel/internal/job"
"github.com/tnb-labs/panel/internal/route"
"github.com/tnb-labs/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))
}
-146
View File
@@ -1,146 +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/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/apps"
"github.com/tnb-labs/panel/internal/apps/benchmark"
"github.com/tnb-labs/panel/internal/apps/docker"
"github.com/tnb-labs/panel/internal/apps/fail2ban"
"github.com/tnb-labs/panel/internal/apps/frp"
"github.com/tnb-labs/panel/internal/apps/gitea"
"github.com/tnb-labs/panel/internal/apps/memcached"
"github.com/tnb-labs/panel/internal/apps/mysql"
"github.com/tnb-labs/panel/internal/apps/nginx"
"github.com/tnb-labs/panel/internal/apps/php"
"github.com/tnb-labs/panel/internal/apps/phpmyadmin"
"github.com/tnb-labs/panel/internal/apps/podman"
"github.com/tnb-labs/panel/internal/apps/postgresql"
"github.com/tnb-labs/panel/internal/apps/pureftpd"
"github.com/tnb-labs/panel/internal/apps/redis"
"github.com/tnb-labs/panel/internal/apps/rsync"
"github.com/tnb-labs/panel/internal/apps/s3fs"
"github.com/tnb-labs/panel/internal/apps/supervisor"
"github.com/tnb-labs/panel/internal/apps/toolbox"
"github.com/tnb-labs/panel/internal/bootstrap"
"github.com/tnb-labs/panel/internal/data"
"github.com/tnb-labs/panel/internal/http/middleware"
"github.com/tnb-labs/panel/internal/job"
"github.com/tnb-labs/panel/internal/route"
"github.com/tnb-labs/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
}
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(db, logger, queue)
appRepo := data.NewAppRepo(db, cacheRepo, taskRepo)
middlewares := middleware.NewMiddlewares(koanf, logger, manager, appRepo)
userRepo := data.NewUserRepo(db)
userService := service.NewUserService(koanf, manager, userRepo)
databaseServerRepo := data.NewDatabaseServerRepo(db, logger)
databaseUserRepo := data.NewDatabaseUserRepo(db, databaseServerRepo)
databaseRepo := data.NewDatabaseRepo(db, databaseServerRepo, databaseUserRepo)
certRepo := data.NewCertRepo(db)
certAccountRepo := data.NewCertAccountRepo(db, userRepo)
websiteRepo := data.NewWebsiteRepo(db, cacheRepo, databaseRepo, databaseServerRepo, databaseUserRepo, certRepo, certAccountRepo)
settingRepo := data.NewSettingRepo(db, koanf, taskRepo)
cronRepo := data.NewCronRepo(db)
backupRepo := data.NewBackupRepo(db, settingRepo, websiteRepo)
dashboardService := service.NewDashboardService(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(backupRepo)
certService := service.NewCertService(certRepo)
certDNSRepo := data.NewCertDNSRepo(db)
certDNSService := service.NewCertDNSService(certDNSRepo)
certAccountService := service.NewCertAccountService(certAccountRepo)
appService := service.NewAppService(appRepo, cacheRepo, settingRepo)
cronService := service.NewCronService(cronRepo)
processService := service.NewProcessService()
safeRepo := data.NewSafeRepo()
safeService := service.NewSafeService(safeRepo)
firewallService := service.NewFirewallService()
sshRepo := data.NewSSHRepo(db)
sshService := service.NewSSHService(sshRepo)
containerRepo := data.NewContainerRepo()
containerService := service.NewContainerService(containerRepo)
containerNetworkRepo := data.NewContainerNetworkRepo()
containerNetworkService := service.NewContainerNetworkService(containerNetworkRepo)
containerImageRepo := data.NewContainerImageRepo()
containerImageService := service.NewContainerImageService(containerImageRepo)
containerVolumeRepo := data.NewContainerVolumeRepo()
containerVolumeService := service.NewContainerVolumeService(containerVolumeRepo)
fileService := service.NewFileService(taskRepo)
monitorRepo := data.NewMonitorRepo(db, settingRepo)
monitorService := service.NewMonitorService(settingRepo, monitorRepo)
settingService := service.NewSettingService(settingRepo)
systemctlService := service.NewSystemctlService()
benchmarkApp := benchmark.NewApp()
dockerApp := docker.NewApp()
fail2banApp := fail2ban.NewApp(websiteRepo)
frpApp := frp.NewApp()
giteaApp := gitea.NewApp()
memcachedApp := memcached.NewApp()
mysqlApp := mysql.NewApp(settingRepo)
nginxApp := nginx.NewApp()
phpApp := php.NewApp(taskRepo)
phpmyadminApp := phpmyadmin.NewApp()
podmanApp := podman.NewApp()
postgresqlApp := postgresql.NewApp()
pureftpdApp := pureftpd.NewApp()
redisApp := redis.NewApp()
rsyncApp := rsync.NewApp()
s3fsApp := s3fs.NewApp(settingRepo)
supervisorApp := supervisor.NewApp()
toolboxApp := toolbox.NewApp()
loader := apps.NewLoader(benchmarkApp, dockerApp, fail2banApp, frpApp, giteaApp, memcachedApp, mysqlApp, nginxApp, phpApp, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp, toolboxApp)
http := route.NewHttp(userService, dashboardService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, cronService, processService, safeService, firewallService, sshService, containerService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, loader)
wsService := service.NewWsService(koanf, sshRepo)
ws := route.NewWs(wsService)
mux, err := bootstrap.NewRouter(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)
cron, err := bootstrap.NewCron(koanf, logger, jobs)
if err != nil {
return nil, err
}
validation := bootstrap.NewValidator(db)
web := app.NewWeb(koanf, mux, server, gormigrate, cron, queue, validation)
return web, nil
}
+12 -1
View File
@@ -3,11 +3,22 @@ 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:
lifetime: 120
+7
View File
@@ -0,0 +1,7 @@
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
- source: /web/src/locales/*.pot
translation: /web/src/locales/%locale_with_underscore%.po
+132 -75
View File
@@ -1,94 +1,151 @@
module github.com/tnb-labs/panel
module github.com/acepanel/panel/v3
go 1.23
go 1.26
require (
github.com/bddjr/hlfhr v1.3.6
github.com/beevik/ntp v1.4.3
github.com/andybalholm/brotli v1.2.2
github.com/bddjr/hlfhr v1.6.1
github.com/beevik/ntp v1.5.0
github.com/cilium/ebpf v0.22.0
github.com/coder/websocket v1.8.15
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.16.9
github.com/glebarez/sqlite v1.11.0
github.com/go-chi/chi/v5 v5.2.0
github.com/go-gormigrate/gormigrate/v2 v2.1.3
github.com/go-rat/chix v1.1.4
github.com/go-rat/gormstore v1.0.6
github.com/go-rat/sessions v1.0.11
github.com/go-rat/utils v1.0.7
github.com/go-resty/resty/v2 v2.16.2
github.com/go-sql-driver/mysql v1.8.1
github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a
github.com/gomodule/redigo v1.9.2
github.com/google/wire v0.6.0
github.com/gookit/validate v1.5.4
github.com/gorilla/websocket v1.5.3
github.com/hashicorp/go-version v1.7.0
github.com/knadh/koanf/parsers/yaml v0.1.0
github.com/knadh/koanf/providers/file v1.1.2
github.com/knadh/koanf/v2 v2.1.2
github.com/lib/pq v1.10.9
github.com/libdns/alidns v1.0.3
github.com/libdns/cloudflare v0.1.1
github.com/libdns/huaweicloud v0.3.1
github.com/libdns/libdns v0.2.2
github.com/libdns/tencentcloud v1.1.0
github.com/mholt/acmez/v3 v3.0.0
github.com/dchest/captcha v1.1.0
github.com/expr-lang/expr v1.17.8
github.com/fsnotify/fsnotify v1.10.1
github.com/go-chi/chi/v5 v5.3.1
github.com/go-chi/httplog/v3 v3.4.0
github.com/go-gormigrate/gormigrate/v2 v2.1.6
github.com/go-sql-driver/mysql v1.10.0
github.com/go-webauthn/webauthn v0.17.4
github.com/gomodule/redigo v1.9.3
github.com/google/wire v0.7.0
github.com/gookit/color v1.6.1
github.com/hashicorp/go-version v1.9.0
github.com/leonelquinteros/gotext v1.7.2
github.com/lib/pq v1.12.3
github.com/libdns/alidns v1.0.7
github.com/libdns/cloudflare v0.2.2
github.com/libdns/cloudns v1.2.0
github.com/libdns/gcore v0.0.0-20250427050847-9964da923833
github.com/libdns/huaweicloud v1.0.1
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.3
github.com/libdns/westcn v1.0.2
github.com/libtnb/chix/v2 v2.1.1
github.com/libtnb/cron v0.5.2
github.com/libtnb/gormstore v1.3.0
github.com/libtnb/logrotate v0.1.2
github.com/libtnb/sessions v1.5.0
github.com/libtnb/sqlite v1.2.1
github.com/libtnb/utils v1.2.2
github.com/libtnb/validator v0.4.1
github.com/libtnb/validator/contrib/openapi v0.2.0
github.com/medama-io/go-useragent v1.2.4
github.com/mholt/acmez/v3 v3.1.6
github.com/moby/moby/api v1.55.0
github.com/moby/moby/client v0.5.1
github.com/orandin/slog-gorm v1.4.0
github.com/robfig/cron/v3 v3.0.1
github.com/samber/lo v1.47.0
github.com/sethvargo/go-limiter v1.0.0
github.com/shirou/gopsutil v2.21.11+incompatible
github.com/spf13/cast v1.7.1
github.com/stretchr/testify v1.10.0
github.com/tufanbarisyildirim/gonginx v0.0.0-20241225173012-5cc15aab65bd
github.com/urfave/cli/v3 v3.0.0-beta1
golang.org/x/crypto v0.31.0
golang.org/x/net v0.33.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
gorm.io/gorm v1.25.12
github.com/pkg/sftp v1.13.11
github.com/pquerna/otp v1.5.0
github.com/samber/lo v1.53.0
github.com/sethvargo/go-limiter v1.2.0
github.com/shirou/gopsutil/v4 v4.26.6
github.com/spf13/cast v1.10.0
github.com/stretchr/testify v1.11.1
github.com/studio-b12/gowebdav v0.13.0
github.com/tufanbarisyildirim/gonginx v0.0.0-20260220081509-8e17ce617db3
github.com/urfave/cli/v3 v3.10.1
github.com/valyala/fastjson v1.6.10
github.com/wneessen/go-mail v0.8.1
github.com/xuri/excelize/v2 v2.11.0
go.yaml.in/yaml/v4 v4.0.0-rc.6
golang.org/x/crypto v0.54.0
golang.org/x/net v0.57.0
golang.org/x/sync v0.22.0
golang.org/x/sys v0.47.0
gorm.io/gorm v1.31.2
modernc.org/sqlite v1.55.0
resty.dev/v3 v3.0.0-rc.3
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
filippo.io/edwards25519 v1.2.0 // indirect
github.com/G-Core/gcore-dns-sdk-go v0.3.3 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/bddjr/shuttingdown v0.1.0 // indirect
github.com/boombuler/barcode v1.1.0 // indirect
github.com/boyter/go-string v1.0.5 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.7.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-rat/securecookie v1.0.1 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/gofiber/schema v1.2.0 // indirect
github.com/ebitengine/purego v0.10.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.9.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/go-webauthn/x v0.2.6 // indirect
github.com/gofiber/schema v1.8.2 // indirect
github.com/gofiber/utils/v2 v2.2.0 // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/google/go-tpm v0.9.8 // indirect
github.com/google/subcommands v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gookit/filter v1.2.2 // indirect
github.com/gookit/goutil v0.6.18 // 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.1 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/libtnb/securecookie v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
github.com/mattn/go-isatty v0.0.21 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1051 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect
github.com/richardlehane/mscfb v1.0.7 // indirect
github.com/richardlehane/msoleps v1.0.6 // indirect
github.com/stretchr/objx v0.5.3 // indirect
github.com/tiendc/go-deepcopy v1.7.2 // indirect
github.com/timtadh/data-structures v0.6.2 // indirect
github.com/timtadh/lexmachine v0.2.3 // indirect
github.com/tinylib/msgp v1.6.4 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.11.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/xuri/efp v0.0.1 // indirect
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
modernc.org/libc v1.60.1 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.32.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
golang.org/x/exp v0.0.0-20260603202125-055de637280b // indirect
golang.org/x/mod v0.37.0 // indirect
golang.org/x/text v0.40.0 // indirect
golang.org/x/tools v0.47.0 // indirect
modernc.org/libc v1.74.1 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
)
replace (
github.com/libdns/tencentcloud => github.com/devhaozi/tencentcloud v0.0.0-20241129095955-7d4e898ce504
github.com/mholt/acmez/v3 => github.com/tnb-labs/acmez/v3 v3.0.0-20241217024017-a2f2c48efdcc
github.com/mholt/acmez/v3 => github.com/libtnb/acmez/v3 v3.0.0-20260406165834-a264acd02292
github.com/stretchr/testify => github.com/libtnb/testify v0.0.0-20260406170114-25da2dad39e7
)
tool github.com/google/wire/cmd/wire
+602 -219
View File
@@ -1,264 +1,647 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/bddjr/hlfhr v1.3.6 h1:XautNR6Vomud8SGh1V1Yjoj1L4YblsNM0ZUwZ7r3q4U=
github.com/bddjr/hlfhr v1.3.6/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=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE=
code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
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.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/andybalholm/brotli v1.2.2 h1:HzTuoo2ErYQqf5qvcJInB8uvqSVxRttzkFexPWtnceM=
github.com/andybalholm/brotli v1.2.2/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/bddjr/hlfhr v1.6.1 h1:bzl0zsL2pl7Gvlbx6l8jZjjv9GbBTeOaX4/+RaXyNXc=
github.com/bddjr/hlfhr v1.6.1/go.mod h1:cHRve//dit7d8ZCPUUFZAYW5WCm3LdMoiqX9i9GJdms=
github.com/bddjr/shuttingdown v0.1.0 h1:1thUjnzTXNbzexEFkh638ebjln8F4KlqXfLIlBXfdbs=
github.com/bddjr/shuttingdown v0.1.0/go.mod h1:+vG4Zp8uhFLT40DBABiA/XxJDHbzybe8RR72i29DFI4=
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=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo=
github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boyter/go-string v1.0.5 h1:/xcOlWdgelLYLVkUU0xBLfioGjZ9KIMUMI/RXG138YY=
github.com/boyter/go-string v1.0.5/go.mod h1:Mww9cDld2S2cdJ0tQffBhsZFMQRA2OJdcjWYZXvZ4Ss=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cilium/ebpf v0.22.0 h1:v2ktp0roffpMOj2MMf3idtCQZOsAoC4BJbAJN+ke2bY=
github.com/cilium/ebpf v0.22.0/go.mod h1:CDzZbe2hC5JjlDC+CY3KFCzlYwN4gbxppYM+Z10bQt4=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNUA=
github.com/coder/websocket v1.8.15/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/devhaozi/tencentcloud v0.0.0-20241129095955-7d4e898ce504 h1:Wk7RQyamGDfAz+5hZIT/GIm6ay5cORztgYVXOJTobKY=
github.com/devhaozi/tencentcloud v0.0.0-20241129095955-7d4e898ce504/go.mod h1:o0+WCxQ7LGLtyjnjYU4HbGW9uVjN44SdUDhxdUYLGPw=
github.com/dchest/captcha v1.1.0 h1:2kt47EoYUUkaISobUdTbqwx55xvKOJxyScVfw25xzhQ=
github.com/dchest/captcha v1.1.0/go.mod h1:7zoElIawLp7GUMLcj54K9kbw+jEyvz2K0FDdRRYhvWo=
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/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.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/expr-lang/expr v1.17.8 h1:W1loDTT+0PQf5YteHSTpju2qfUfNoBt4yw9+wOEU9VM=
github.com/expr-lang/expr v1.17.8/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
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.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-gormigrate/gormigrate/v2 v2.1.3 h1:ei3Vq/rpPI/jCJY9mRHJAKg5vU+EhZyWhBAkaAomQuw=
github.com/go-gormigrate/gormigrate/v2 v2.1.3/go.mod h1:VJ9FIOBAur+NmQ8c4tDVwOuiJcgupTG105FexPFrXzA=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78=
github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-chi/chi/v5 v5.3.1 h1:3j4HZLGZQ3JpMCrPJF/Jl3mYJfWLKBfNJ6quurUGCf8=
github.com/go-chi/chi/v5 v5.3.1/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto=
github.com/go-chi/httplog/v3 v3.4.0 h1:gO4fvt8HEtFwHq926HoKe1aV2DymfPJuZy4+U4zwT3I=
github.com/go-chi/httplog/v3 v3.4.0/go.mod h1:tDhJo9G+F4mioDgX4pKbyA0uVZwCtHejoSsDkvJkFkU=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gormigrate/gormigrate/v2 v2.1.6 h1:VtX+l1Stj2v5RGubVQk0LS/8EPGXR+ldcOyCmlmKoyg=
github.com/go-gormigrate/gormigrate/v2 v2.1.6/go.mod h1:PZpedQc4tWaxn6kvXicwhinh3L0seLpMc5ReKRX5id4=
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-rat/chix v1.1.4 h1:YVDwF4vrlkm5hmlDGWKoiWPgP98pJooBvhsto8UWq+o=
github.com/go-rat/chix v1.1.4/go.mod h1:eKUTG7GXqMS278PH0QZi8yMWZddvMfxqWu7mverM6kQ=
github.com/go-rat/gormstore v1.0.6 h1:YxmaY6n3JoTk6o9RXoZ1+cQ6HQL+ayf/6d8D76ydCAc=
github.com/go-rat/gormstore v1.0.6/go.mod h1:xG/n+Du8baWf7ptiafiHLWBZWVXrYIPC42CsoEbfr4M=
github.com/go-rat/securecookie v1.0.1 h1:HW0fpKmB+FjJzXTw8ABOwBJ+XrPmRBSZqHhmrv86lBo=
github.com/go-rat/securecookie v1.0.1/go.mod h1:tP/ObWYyjmcpabQ7WTon/i2lBSip/Aolliw2llXuPDU=
github.com/go-rat/sessions v1.0.11 h1:2eTUvhEBtymrAtwv71LlGHNylz+Ad2SgY3jg/sSUboA=
github.com/go-rat/sessions v1.0.11/go.mod h1:Ray/GCbuhm4U9xpjFFSCfOTCEn91puEhAXX5creHE9g=
github.com/go-rat/utils v1.0.7 h1:hZUe+n1W/tvF3j+oP/TeAHX3AF98SjVB1FuF11zkCAI=
github.com/go-rat/utils v1.0.7/go.mod h1:b0S/vd2bXWvPUQpw8z6RmJrUG7G7BkMuNSrsz6JRBHs=
github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg=
github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gofiber/schema v1.2.0 h1:j+ZRrNnUa/0ZuWrn/6kAtAufEr4jCJ+JuTURAMxNSZg=
github.com/gofiber/schema v1.2.0/go.mod h1:YYwj01w3hVfaNjhtJzaqetymL56VW642YS3qZPhuE6c=
github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a h1:BAyyIK6rc6Tq9vT2KVlPJXD8EVv/LpaFwn4fgfLwtHg=
github.com/golang-cz/httplog v0.0.0-20241002114323-98e09d6f537a/go.mod h1:bgk4Ij/0OQ89UeoFFAQrSNhbbr4rKJ0fwWfo7wc+TCc=
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/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-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s=
github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI=
github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw=
github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-webauthn/webauthn v0.17.4 h1:KFTSz3R2RYDiUn/0cDi3XTJgFenSG74eKTTHlqWhlxk=
github.com/go-webauthn/webauthn v0.17.4/go.mod h1:pZk63EE/BdztlmyS4Yc+9H5g4a8blNlbtGmdHQHbZX8=
github.com/go-webauthn/x v0.2.6 h1:TEyDuQAIiEgYpx60nKiBJIX/5nSUC8LxNbH+uf5U9uk=
github.com/go-webauthn/x v0.2.6/go.mod h1:45bA7YEqyQhRcQJ/TiBb46Ww8yqHBGvgEhQ3WWF0aDo=
github.com/gofiber/schema v1.8.2 h1:wq+LO2xEGlsqma/8Akp9PUebQ6vcsYmF0xYQ4F2ijvU=
github.com/gofiber/schema v1.8.2/go.mod h1:iyAMJztdyky7Pk2U7bwUP3EHjzMqUNjZ/hglE0nYO/g=
github.com/gofiber/utils/v2 v2.2.0 h1:YSSmCzQponq/f9uSOg2HtXC5qK1Dmor0o6DqaQVz8GE=
github.com/gofiber/utils/v2 v2.2.0/go.mod h1:Ieopk6sQh7rbhQ12aBNCJtJuG0gxAg0nz63sFCrrOmE=
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-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
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=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
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.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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
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/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc=
github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc=
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/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
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.6.18 h1:MUVj0G16flubWT8zYVicIuisUiHdgirPAkmnfD2kKgw=
github.com/gookit/goutil v0.6.18/go.mod h1:AY/5sAwKe7Xck+mEbuxj0n/bc3qwrGNe3Oeulln7zBA=
github.com/gookit/validate v1.5.4 h1:nwBo6vULnVUeNFCOde6RKFRbOCKJXVMnWR0ghedacLg=
github.com/gookit/validate v1.5.4/go.mod h1:p9sRPfpvYB4vXICBpEPzv8FoAky+XhUOhWQghgmmat4=
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/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
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/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.1 h1:KoTnDxJPRgrL0SoX0f8rCFg2zI0t4E3GZZBMo2nN8LU=
github.com/gookit/color v1.6.1/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
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/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=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
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.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA=
github.com/hashicorp/go-version v1.9.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 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/imega/luaformatter v0.0.0-20211025140405-86b0a68d6bef h1:RC993DdTIHNItsyLj79fgZNLzrf9tBN0GR6W5ZPms6s=
github.com/imega/luaformatter v0.0.0-20211025140405-86b0a68d6bef/go.mod h1:i2XCfvmO94HrEOQWllihhtPrkvNfuB2R2p/o6+OVnRU=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jaevor/go-nanoid v1.4.0 h1:mPz0oi3CrQyEtRxeRq927HHtZCJAAtZ7zdy7vOkrvWs=
github.com/jaevor/go-nanoid v1.4.0/go.mod h1:GIpPtsvl3eSBsjjIEFQdzzgpi50+Bo1Luk+aYlbJzlc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w=
github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY=
github.com/knadh/koanf/providers/file v1.1.2 h1:aCC36YGOgV5lTtAFz2qkgtWdeQsgfxUkxDOe+2nQY3w=
github.com/knadh/koanf/providers/file v1.1.2/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI=
github.com/knadh/koanf/v2 v2.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ=
github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=
github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
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/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=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
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.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ=
github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE=
github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054=
github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU=
github.com/libdns/huaweicloud v0.3.1 h1:wro0zpG86JKL3QVEaV/xfHW/59rmqwMYhcbl16w9EYg=
github.com/libdns/huaweicloud v0.3.1/go.mod h1:6s5ZIwLUr2qKsQz2SRNo7uh8X5uJohxoBR95rtSpNcI=
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/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/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
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/letsencrypt/challtestsrv v1.4.2 h1:0ON3ldMhZyWlfVNYYpFuWRTmZNnyfiL9Hh5YzC3JVwU=
github.com/letsencrypt/challtestsrv v1.4.2/go.mod h1:GhqMqcSoeGpYd5zX5TgwA6er/1MbWzx/o7yuuVya+Wk=
github.com/letsencrypt/pebble/v2 v2.10.0 h1:Wq6gYXlsY6ubqI3hhxsTzdyotvfdjFBxuwYqCLCnj/U=
github.com/letsencrypt/pebble/v2 v2.10.0/go.mod h1:Sk8cmUIPcIdv2nINo+9PB4L+ZBhzY+F9A1a/h/xmWiQ=
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
github.com/libdns/alidns v1.0.7 h1:0BhTUFlWIUyb5U7wujGqsyoeZkleUR/hMCtdZDS8Oc8=
github.com/libdns/alidns v1.0.7/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.2.0 h1:V299bWj6HHTsXSzoEkRsKNBb9V/HGj3zp4OYzJ31JX8=
github.com/libdns/cloudns v1.2.0/go.mod h1:zKx/cXy9W3f4bGjbEtOx+B435flBrz1uibNr3h27+aU=
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/huaweicloud v1.0.1 h1:TdfpPY//3/QPhEGCyvR8u2DTlGxzpyaIsqofH5kYFyA=
github.com/libdns/huaweicloud v1.0.1/go.mod h1:lMHNSEqLZeuE4N28ytaqLZia3NPuaaz1ldAH/73bOQE=
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.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-20260406165834-a264acd02292 h1:FF1ZlwFFjpwT6CtBodr/NV4OMJPMC7plTmfzqhXrlSk=
github.com/libtnb/acmez/v3 v3.0.0-20260406165834-a264acd02292/go.mod h1:5nTPosTGosLxF3+LU4ygbgMRFDhbAVpqMI4+a4aHLBY=
github.com/libtnb/chix/v2 v2.1.1 h1:QQwHXUyPaUUp/KOwzXf2NB0IRKb9v2pqIdOIItzBPiE=
github.com/libtnb/chix/v2 v2.1.1/go.mod h1:OEY9v0976U/IJzP6n8VdhgXH17VL2muo3EQWsIUyxm8=
github.com/libtnb/cron v0.5.2 h1:Yy9a/S82bAS4KzE1SSMjNLtigPkejgMPUp9BHGeb1pw=
github.com/libtnb/cron v0.5.2/go.mod h1:nrOHxS4Ernkn1E//vs64xzC58hGfoL0+lYSW2zsROGI=
github.com/libtnb/gormstore v1.3.0 h1:OvVTYHZ9MCHI3Anf2ITtJLunAefyBQWG96EmHCE0eLM=
github.com/libtnb/gormstore v1.3.0/go.mod h1:Z/n6VlVdLPmvunSM8sYGNHVebTTN5yUSTZsyNxW2IZc=
github.com/libtnb/logrotate v0.1.2 h1:q9FLtYQYtzAlOB9bs91nPppqt3GNTUOonrBOivrtiOc=
github.com/libtnb/logrotate v0.1.2/go.mod h1:yqC9N3C5mg0fBlquK+cWLB7xG5plQrP0ma84hMRj3ic=
github.com/libtnb/securecookie v1.4.0 h1:SkKHO7T5I4aRGV7/6fnYYsleQDnnDzeAmTDA0GMPD98=
github.com/libtnb/securecookie v1.4.0/go.mod h1:mg1i9HfstsYBGwCfQdU+3Z1GuieyZRAxbkFUnrzchJU=
github.com/libtnb/sessions v1.5.0 h1:gvTRkSR8lpp1PB0Nms/kc+gh1BwJ411fxLjnZLYItLs=
github.com/libtnb/sessions v1.5.0/go.mod h1:/Q/+lO7DcnJJEU1nL59ScPV/V8ct34F8BjoEMwwxjRw=
github.com/libtnb/sqlite v1.2.1 h1:lrhN3yG49pj1gmtMMP6Es0lhKIkzDeqBul2EWOLyjpY=
github.com/libtnb/sqlite v1.2.1/go.mod h1:pGq13BirCjkM6ihkWWxs0PGFhBiwUyA0PqdSmgDWfmM=
github.com/libtnb/testify v0.0.0-20260406170114-25da2dad39e7 h1:ZzONsNhw69uC/wJQlMj66n1zfsRgKAElfJ//E9G380U=
github.com/libtnb/testify v0.0.0-20260406170114-25da2dad39e7/go.mod h1:HeQeTfKU6tj2Lx1z79UacwYeDioo6M4ZD7BDDI6+rrg=
github.com/libtnb/utils v1.2.2 h1:HUqgAIb+TIrZPXRytYB5XqzkEXG2U4a4/ZGpprNtL5A=
github.com/libtnb/utils v1.2.2/go.mod h1:KUCx2+Phw3cLXOwuQDy6XwFOliaYuU1rpAuJQuHf+lQ=
github.com/libtnb/validator v0.4.1 h1:TYDC1C1yp93d02huzCqEc1aU/u5PAtC3jqGPtEdZchU=
github.com/libtnb/validator v0.4.1/go.mod h1:j08Lydxwy7ModkoKeYIHxvZvAtnzzdE8xi2J+SGVrgc=
github.com/libtnb/validator/contrib/openapi v0.2.0 h1:y47A16Lqtkc9/uifRIJ2XC+Rkgghj2fmHPrclcPXPtA=
github.com/libtnb/validator/contrib/openapi v0.2.0/go.mod h1:2vCdRniTmXWBtG4gro9B7s6ko4v3WdoSKjvhsWdTn3g=
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/mattn/go-isatty v0.0.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs=
github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/medama-io/go-useragent v1.2.4 h1:kDux3lCjZvBpccKus/goaZl6YMnrl9D8Tx/wb4HqRMI=
github.com/medama-io/go-useragent v1.2.4/go.mod h1:H9GYWth4IN8vAFZh5LeARza7VwM4jK9uk7Tb9huVzLw=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
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=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
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/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.55.0 h1:2/sexvQyqIWS8pRSCFddBfpW2qE7vR7FCL+vN8pxwMc=
github.com/moby/moby/api v1.55.0/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
github.com/moby/moby/client v0.5.1 h1:tYNaJno4c0HXz12y5BiqEDy0rVTYkWzI26lGvnTMiJw=
github.com/moby/moby/client v0.5.1/go.mod h1:odLstlZ6uSnfvAgVxMpvgmb8SUdd+siH2T0GBuxVAlM=
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-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/sftp v1.13.11 h1:0N92SLTB8JqASJB14ZLHHzFnBV8mG9zw4K7jghEFWuE=
github.com/pkg/sftp v1.13.11/go.mod h1:uNkH9roSXglNJqM+glJJi+TQXQUm0fXFWqCFmT8hsN0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/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=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
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/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/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
github.com/sethvargo/go-limiter v1.0.0 h1:JqW13eWEMn0VFv86OKn8wiYJY/m250WoXdrjRV0kLe4=
github.com/sethvargo/go-limiter v1.0.0/go.mod h1:01b6tW25Ap+MeLYBuD4aHunMrJoNO5PVUFdS9rac3II=
github.com/shirou/gopsutil v2.21.11+incompatible h1:lOGOyCG67a5dv2hq5Z1BLDUqqKp3HkbjPcz5j6XMS0U=
github.com/shirou/gopsutil v2.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1051 h1:3mg0L9vv9eO8UN4Oa7vNawe6yUIuXf9D0Q79rUmnblo=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1051/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
github.com/tnb-labs/acmez/v3 v3.0.0-20241217024017-a2f2c48efdcc h1:DjqfrS/10NQOqx8yFtPcNfbAN5wrjnYgaub+ZQXPHEY=
github.com/tnb-labs/acmez/v3 v3.0.0-20241217024017-a2f2c48efdcc/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
github.com/tufanbarisyildirim/gonginx v0.0.0-20241225173012-5cc15aab65bd h1:PYNV7LTzEzZ4lousHUM2yaDO2zI3FNt9NA0iuGrbK7w=
github.com/tufanbarisyildirim/gonginx v0.0.0-20241225173012-5cc15aab65bd/go.mod h1:hdMWBc1+TyB6G5ZZBBgPWQ8cjRZ6zpYdhal0uu6E9QM=
github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjcw9Zg=
github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y=
github.com/richardlehane/mscfb v1.0.7 h1:oeoiM0WE79vHwE8RpIYYvIAc8ajTH2mb6UZm55/+EB0=
github.com/richardlehane/mscfb v1.0.7/go.mod h1:pe0+IUIc0AHh0+teNzBlJCtSyZdFOGgV4ZK9bsoV+Jo=
github.com/richardlehane/msoleps v1.0.6 h1:9BvkpjvD+iUBalUY4esMwv6uBkfOip/Lzvd93jvR9gg=
github.com/richardlehane/msoleps v1.0.6/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
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.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.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=
github.com/samber/lo v1.53.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.2.0 h1:XKL1vsaQ2zztVJrnZSzpRWCq/aLQqMllJ/3D/0bt/cw=
github.com/sethvargo/go-limiter v1.2.0/go.mod h1:RC+qY2R7PAK81mBCrZEJlUlKnXSIqqQ8B7G44UgZ/1E=
github.com/shamaton/msgpack/v3 v3.2.0 h1:1q2Ms+MWmuRju+PuDMSFDB7p7621npeX4zprJN5Zck8=
github.com/shamaton/msgpack/v3 v3.2.0/go.mod h1:sgBYvEiyz8JR1NC3yGRoPVME9xXovpnh3l/plW1nfRo=
github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs=
github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
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=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
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.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.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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.13.0 h1:OcwSg6IQHOFNdYHn3bPOHwSE8looG8N56Y5xTT1asqQ=
github.com/studio-b12/gowebdav v0.13.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrIj44=
github.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
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=
github.com/timtadh/data-structures v0.6.2/go.mod h1:uYUnI1cQi/5yMCc7s23I+x8Mn8BCMf4WgK+7/4QSEk4=
github.com/timtadh/getopt v1.0.0/go.mod h1:L3EL6YN2G0eIAhYBo9b7SB9d/kEQmdnwthIlMJfj210=
github.com/timtadh/lexmachine v0.2.2/go.mod h1:GBJvD5OAfRn/gnp92zb9KTgHLB7akKyxmVivoYCcjQI=
github.com/timtadh/lexmachine v0.2.3 h1:ZqlfHnfMcAygtbNM5Gv7jQf8hmM8LfVzDjfCrq235NQ=
github.com/timtadh/lexmachine v0.2.3/go.mod h1:oK1NW+93fQSIF6s+J6sXBFWsCPCFbNmrwKV1i0aqvW0=
github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ=
github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
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-20260220081509-8e17ce617db3 h1:ClWHRIz18WRVT3RD9y9uTwEWU9eEi+VYFEIumg05FLk=
github.com/tufanbarisyildirim/gonginx v0.0.0-20260220081509-8e17ce617db3/go.mod h1:ALbEe81QPWOZjDKCKNWodG2iqCMtregG8+ebQgjx2+4=
github.com/urfave/cli/v3 v3.10.1 h1:7Kx9H50hrHbRbyxgO1KP6/BcbiGRz0uYh5YyQ30JEEY=
github.com/urfave/cli/v3 v3.10.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4=
github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE=
github.com/wneessen/go-mail v0.8.1 h1:tVcncj02/QySVFw3zr/kXOzZcuFQqBNT6K+Rbgm/pcM=
github.com/wneessen/go-mail v0.8.1/go.mod h1:dWZ61zadzCIyvB4y1/YzC5O7MrbbzBfPkARmbosdf8w=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.11.0 h1:HxaEFl6sRN2+8J5a8HaKq+0M4FsjBGMnWWtjOCPSG88=
github.com/xuri/excelize/v2 v2.11.0/go.mod h1:jxFLbzaIwGQ5ufFNvYfUOHqXhfPaNmP14KWfmNz2Uak=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
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/v4 v4.0.0-rc.6 h1:1h7H1ohdUh93/FyE4YaDa1Zh64K6VVbjF4K6WUxMtH4=
go.yaml.in/yaml/v4 v4.0.0-rc.6/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-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.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
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/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
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.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
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-20260603202125-055de637280b h1:v1uXiEBHo8QA0LiGCo7UgHMzHT4Kdfpl2zmtH5vaP1Q=
golang.org/x/exp v0.0.0-20260603202125-055de637280b/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw=
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/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE=
golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
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.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
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=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
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=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
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=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.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.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
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.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
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/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
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/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-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/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
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.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
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=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
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-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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/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.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4=
modernc.org/ccgo/v4 v4.21.0/go.mod h1:h6kt6H/A2+ew/3MW/p6KEoQmrq/i3pr0J/SiwiaF/g0=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/libc v1.60.1 h1:at373l8IFRTkJIkAU85BIuUoBM4T1b51ds0E1ovPG2s=
modernc.org/libc v1.60.1/go.mod h1:xJuobKuNxKH3RUatS7GjR+suWj+5c2K7bi4m/S5arOY=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s=
modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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/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.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-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.31.2 h1:3o8FXNo9v9S858gil+3LlZA1LkCOzgb4g5BL64FgaCo=
gorm.io/gorm v1.31.2/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=
modernc.org/cc/v4 v4.29.0 h1:CXgwL8cvxmyzBQZzbSl/6xFtMCryb6u8IOqDci39cgc=
modernc.org/cc/v4 v4.29.0/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU=
modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk=
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI=
modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.74.1 h1:bdR4VTKFMC4966QSNZ05XLGI/VwzVa2kTUX51Dm0riQ=
modernc.org/libc v1.74.1/go.mod h1:uH4t5bOx3G3g9Xcmj10YKlTcVISlRDwv8VoQJG9n8Os=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.55.0 h1:hIFh0MCH0rGinQ/4KYb5/UbCkRkb+UP+OkLCVWa5MTM=
modernc.org/sqlite v1.55.0/go.mod h1:4ntCLuNmnH8+GNqjka1wNg7KJd5/Hi5FYp8K+XQ7GZw=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
resty.dev/v3 v3.0.0-rc.3 h1:k24LZ03Cb4Ue5e6O/Pfxu5TQRBBYGES6wm2wceia+Io=
resty.dev/v3 v3.0.0-rc.3/go.mod h1:NTOerrC/4T7/FE6tXIZGIysXXBdgNqwMZuKtxpea9NM=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+127
View File
@@ -0,0 +1,127 @@
package app
import (
"context"
"errors"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/bddjr/hlfhr"
"github.com/go-chi/chi/v5"
"github.com/go-gormigrate/gormigrate/v2"
"github.com/libtnb/cron"
"github.com/acepanel/panel/v3/pkg/config"
"github.com/acepanel/panel/v3/pkg/tlscert"
"github.com/acepanel/panel/v3/pkg/types"
)
type Ace struct {
conf *config.Config
router *chi.Mux
server *hlfhr.Server
reloader *tlscert.Reloader
migrator *gormigrate.Gormigrate
cron *cron.Cron
runner types.TaskRunner
}
func NewAce(router *chi.Mux, conf *config.Config, cron *cron.Cron, migrator *gormigrate.Gormigrate, server *hlfhr.Server, reloader *tlscert.Reloader, runner types.TaskRunner) (*Ace, error) {
return &Ace{
conf: conf,
router: router,
server: server,
reloader: reloader,
migrator: migrator,
cron: cron,
runner: runner,
}, nil
}
func (r *Ace) Run() error {
// migrate database
if err := r.migrator.Migrate(); err != nil {
return err
}
fmt.Println("[DB] database migrated")
// start cron scheduler
if err := r.cron.Start(); err != nil {
return err
}
fmt.Println("[CRON] cron scheduler started")
// create context for runner
runnerCtx, runnerCancel := context.WithCancel(context.Background())
defer runnerCancel()
// start task runner
r.runner.Run(runnerCtx)
// 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() {
fmt.Println("[HTTP] listening and serving on port", r.conf.HTTP.Port)
if r.conf.HTTP.IsHTTPS() {
if err := r.server.ListenAndServeTLS("", ""); !errors.Is(err, http.ErrServerClosed) {
serverErr <- err
}
} else {
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("[APP] received signal:", sig)
}
// graceful shutdown
fmt.Println("[APP] shutting down gracefully...")
// stop cron scheduler
cronCtx, cronCancel := context.WithTimeout(context.Background(), 30*time.Second)
_ = r.cron.Stop(cronCtx)
cronCancel()
fmt.Println("[CRON] cron scheduler stopped")
// stop task runner
runnerCancel()
fmt.Println("[QUEUE] task runner stopped")
// close certificate reloader
if r.reloader != nil {
if err := r.reloader.Close(); err != nil {
fmt.Println("[TLS] certificate reloader close error:", err)
}
}
// 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
}
+4 -8
View File
@@ -2,7 +2,6 @@ package app
import (
"context"
"fmt"
"os"
"github.com/go-gormigrate/gormigrate/v2"
@@ -14,12 +13,12 @@ type Cli struct {
migrator *gormigrate.Gormigrate
}
func NewCli(cmd *cli.Command, migrator *gormigrate.Gormigrate) *Cli {
func NewCli(cmd *cli.Command, migrator *gormigrate.Gormigrate) (*Cli, error) {
IsCli = true
return &Cli{
cmd: cmd,
migrator: migrator,
}
}, nil
}
func (r *Cli) Run() error {
@@ -27,9 +26,6 @@ func (r *Cli) Run() error {
// 这里不处理错误,这么做是为了在异常时用户可以用 fix 命令尝试修复
_ = r.migrator.Migrate()
if err := r.cmd.Run(context.TODO(), os.Args); err != nil {
fmt.Printf("|-%v\n", err)
}
return nil
// 错误必须向上返回,调用方据此以非零码退出,后台任务才能正确判定失败
return r.cmd.Run(context.TODO(), os.Args)
}
+10 -5
View File
@@ -1,5 +1,10 @@
package app
import "time"
// StartTime 面板启动时间
var StartTime = time.Now()
// 面板状态常量
const (
StatusNormal = iota
@@ -11,11 +16,11 @@ const (
// 面板全局变量
var (
Key string
Root string
Locale string
IsCli bool
Status = StatusNormal
Key string // 密钥
Root string // 根目录
Locale string // 语言
IsCli bool // 是否命令行
Status = StatusNormal // 面板状态
)
// 自动注入
+73
View File
@@ -0,0 +1,73 @@
package app
import (
"sort"
"sync"
"time"
)
// HealthLevelError 需要用户立即处理
const HealthLevelError = "error"
// HealthLevelWarning 提示性问题,通常已被系统自动降级处理
const HealthLevelWarning = "warning"
// HealthIssue 单条健康问题
// Key 为稳定标识符(如 database:stat),前端据此选择翻译文案
// Message 为原始错误详情,供诊断参考
type HealthIssue struct {
Key string `json:"key"`
Level string `json:"level"`
Message string `json:"message"`
Since time.Time `json:"since"`
}
type healthRegistry struct {
mu sync.RWMutex
issues map[string]HealthIssue
}
// Health 全局健康状态注册表,供各后台任务上报/清除故障
var Health = &healthRegistry{issues: make(map[string]HealthIssue)}
// Report 上报或更新一条健康问题
// 同 key 重复上报时保留首次上报时间,仅更新 message 和 level
func (h *healthRegistry) Report(key, level, message string) {
h.mu.Lock()
defer h.mu.Unlock()
if existing, ok := h.issues[key]; ok {
existing.Level = level
existing.Message = message
h.issues[key] = existing
return
}
h.issues[key] = HealthIssue{
Key: key,
Level: level,
Message: message,
Since: time.Now(),
}
}
// Clear 清除指定 key 的健康问题
func (h *healthRegistry) Clear(key string) {
h.mu.Lock()
defer h.mu.Unlock()
delete(h.issues, key)
}
// Snapshot 返回当前所有健康问题,按 Since 升序(越早发生越靠前)
func (h *healthRegistry) Snapshot() []HealthIssue {
h.mu.RLock()
defer h.mu.RUnlock()
result := make([]HealthIssue, 0, len(h.issues))
for _, issue := range h.issues {
result = append(result, issue)
}
sort.Slice(result, func(i, j int) bool {
return result[i].Since.Before(result[j].Since)
})
return result
}
-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/tnb-labs/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
}
+23
View File
@@ -0,0 +1,23 @@
package acewaf
import (
"github.com/go-chi/chi/v5"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct{}
func NewApp() (*App, error) {
return &App{}, nil
}
func (s *App) Route(_ chi.Router) {
// WAF 管理路由由 route 贡献(route/waf.go)统一提供
}
func (s *App) Status() string {
ok, _ := systemctl.Status("acewaf")
return types.AggregateAppStatus(ok)
}
+162
View File
@@ -0,0 +1,162 @@
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/v3/internal/app"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/shell"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/tools"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
t *gotext.Locale
}
func NewApp(t *gotext.Locale) (*App, error) {
return &App{
t: t,
}, nil
}
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) Status() string {
ok, _ := systemctl.Status("apache")
return types.AggregateAppStatus(ok)
}
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"`
}
+43 -161
View File
@@ -1,170 +1,52 @@
package apps
import (
"reflect"
"slices"
"strings"
"github.com/go-chi/chi/v5"
"github.com/google/wire"
"github.com/tnb-labs/panel/internal/apps/benchmark"
"github.com/tnb-labs/panel/internal/apps/docker"
"github.com/tnb-labs/panel/internal/apps/fail2ban"
"github.com/tnb-labs/panel/internal/apps/frp"
"github.com/tnb-labs/panel/internal/apps/gitea"
"github.com/tnb-labs/panel/internal/apps/memcached"
"github.com/tnb-labs/panel/internal/apps/mysql"
"github.com/tnb-labs/panel/internal/apps/nginx"
"github.com/tnb-labs/panel/internal/apps/php"
"github.com/tnb-labs/panel/internal/apps/phpmyadmin"
"github.com/tnb-labs/panel/internal/apps/podman"
"github.com/tnb-labs/panel/internal/apps/postgresql"
"github.com/tnb-labs/panel/internal/apps/pureftpd"
"github.com/tnb-labs/panel/internal/apps/redis"
"github.com/tnb-labs/panel/internal/apps/rsync"
"github.com/tnb-labs/panel/internal/apps/s3fs"
"github.com/tnb-labs/panel/internal/apps/supervisor"
"github.com/tnb-labs/panel/internal/apps/toolbox"
"github.com/acepanel/panel/v3/internal/apps/acewaf"
"github.com/acepanel/panel/v3/internal/apps/apache"
"github.com/acepanel/panel/v3/internal/apps/clickhouse"
"github.com/acepanel/panel/v3/internal/apps/codeserver"
"github.com/acepanel/panel/v3/internal/apps/docker"
"github.com/acepanel/panel/v3/internal/apps/elasticsearch"
"github.com/acepanel/panel/v3/internal/apps/fail2ban"
"github.com/acepanel/panel/v3/internal/apps/frp"
"github.com/acepanel/panel/v3/internal/apps/gitea"
"github.com/acepanel/panel/v3/internal/apps/grafana"
"github.com/acepanel/panel/v3/internal/apps/kafka"
"github.com/acepanel/panel/v3/internal/apps/mariadb"
"github.com/acepanel/panel/v3/internal/apps/memcached"
"github.com/acepanel/panel/v3/internal/apps/minio"
"github.com/acepanel/panel/v3/internal/apps/mongodb"
"github.com/acepanel/panel/v3/internal/apps/mysql"
"github.com/acepanel/panel/v3/internal/apps/nginx"
"github.com/acepanel/panel/v3/internal/apps/openresty"
"github.com/acepanel/panel/v3/internal/apps/opensearch"
"github.com/acepanel/panel/v3/internal/apps/percona"
"github.com/acepanel/panel/v3/internal/apps/pgadmin"
"github.com/acepanel/panel/v3/internal/apps/phpmyadmin"
"github.com/acepanel/panel/v3/internal/apps/podman"
"github.com/acepanel/panel/v3/internal/apps/postgresql"
"github.com/acepanel/panel/v3/internal/apps/prometheus"
"github.com/acepanel/panel/v3/internal/apps/pureftpd"
"github.com/acepanel/panel/v3/internal/apps/redis"
"github.com/acepanel/panel/v3/internal/apps/rocketmq"
"github.com/acepanel/panel/v3/internal/apps/rsync"
"github.com/acepanel/panel/v3/internal/apps/s3fs"
"github.com/acepanel/panel/v3/internal/apps/supervisor"
"github.com/acepanel/panel/v3/internal/apps/valkey"
)
var ProviderSet = wire.NewSet(
NewLoader,
benchmark.NewApp,
docker.NewApp,
fail2ban.NewApp,
frp.NewApp,
gitea.NewApp,
memcached.NewApp,
mysql.NewApp,
nginx.NewApp,
php.NewApp,
phpmyadmin.NewApp,
podman.NewApp,
postgresql.NewApp,
pureftpd.NewApp,
redis.NewApp,
rsync.NewApp,
s3fs.NewApp,
supervisor.NewApp,
toolbox.NewApp,
apache.NewApp, clickhouse.NewApp, codeserver.NewApp,
docker.NewApp, elasticsearch.NewApp, fail2ban.NewApp,
frp.NewApp, gitea.NewApp, grafana.NewApp,
kafka.NewApp, mariadb.NewApp, memcached.NewApp,
minio.NewApp, mongodb.NewApp, mysql.NewApp,
nginx.NewApp, openresty.NewApp, opensearch.NewApp,
percona.NewApp, pgadmin.NewApp, phpmyadmin.NewApp, podman.NewApp,
postgresql.NewApp, prometheus.NewApp, pureftpd.NewApp,
redis.NewApp, rocketmq.NewApp, rsync.NewApp,
s3fs.NewApp, supervisor.NewApp, valkey.NewApp,
acewaf.NewApp,
)
var slugs []string
type Loader struct {
benchmark *benchmark.App
docker *docker.App
fail2ban *fail2ban.App
frp *frp.App
gitea *gitea.App
memcached *memcached.App
mysql *mysql.App
nginx *nginx.App
php *php.App
phpmyadmin *phpmyadmin.App
podman *podman.App
postgresql *postgresql.App
pureftpd *pureftpd.App
redis *redis.App
rsync *rsync.App
s3fs *s3fs.App
supervisor *supervisor.App
toolbox *toolbox.App
}
func NewLoader(
benchmark *benchmark.App,
docker *docker.App,
fail2ban *fail2ban.App,
frp *frp.App,
gitea *gitea.App,
memcached *memcached.App,
mysql *mysql.App,
nginx *nginx.App,
php *php.App,
phpmyadmin *phpmyadmin.App,
podman *podman.App,
postgresql *postgresql.App,
pureftpd *pureftpd.App,
redis *redis.App,
rsync *rsync.App,
s3fs *s3fs.App,
supervisor *supervisor.App,
toolbox *toolbox.App,
) *Loader {
loader := &Loader{
benchmark: benchmark,
docker: docker,
fail2ban: fail2ban,
frp: frp,
gitea: gitea,
memcached: memcached,
mysql: mysql,
nginx: nginx,
php: php,
phpmyadmin: phpmyadmin,
podman: podman,
postgresql: postgresql,
pureftpd: pureftpd,
redis: redis,
rsync: rsync,
s3fs: s3fs,
supervisor: supervisor,
toolbox: toolbox,
}
loader.initSlugs()
return loader
}
func (r *Loader) Register(mux chi.Router) {
mux.Route("/benchmark", r.benchmark.Route)
mux.Route("/docker", r.docker.Route)
mux.Route("/fail2ban", r.fail2ban.Route)
mux.Route("/frp", r.frp.Route)
mux.Route("/gitea", r.gitea.Route)
mux.Route("/memcached", r.memcached.Route)
mux.Route("/mysql", r.mysql.Route)
mux.Route("/nginx", r.nginx.Route)
mux.Route("/php74", r.php.Route(74))
mux.Route("/php80", r.php.Route(80))
mux.Route("/php81", r.php.Route(81))
mux.Route("/php82", r.php.Route(82))
mux.Route("/php83", r.php.Route(83))
mux.Route("/php84", r.php.Route(84))
mux.Route("/phpmyadmin", r.phpmyadmin.Route)
mux.Route("/podman", r.podman.Route)
mux.Route("/postgresql", r.postgresql.Route)
mux.Route("/pureftpd", r.pureftpd.Route)
mux.Route("/redis", r.redis.Route)
mux.Route("/rsync", r.rsync.Route)
mux.Route("/s3fs", r.s3fs.Route)
mux.Route("/supervisor", r.supervisor.Route)
mux.Route("/toolbox", r.toolbox.Route)
}
func (r *Loader) initSlugs() []string {
if len(slugs) == 0 {
v := reflect.Indirect(reflect.ValueOf(r))
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
slug := strings.ToLower(v.Type().Field(i).Name)
if !field.IsNil() {
slugs = append(slugs, slug)
}
}
// 处理php
slugs = slices.DeleteFunc(slugs, func(slug string) bool {
return slug == "php"
})
slugs = append(slugs, "php74", "php80", "php81", "php82", "php83", "php84")
}
return slugs
}
func Slugs() []string {
return slugs
}
-691
View File
@@ -1,691 +0,0 @@
package benchmark
import (
"bytes"
"compress/gzip"
"crypto/aes"
"crypto/cipher"
cryptorand "crypto/rand"
"encoding/json"
"fmt"
"image"
"image/color"
"io"
"math"
"math/big"
"math/rand/v2"
"net/http"
"os"
"runtime"
"strings"
"sync"
"time"
"github.com/go-chi/chi/v5"
"github.com/tnb-labs/panel/internal/service"
)
type App struct {
}
func NewApp() *App {
return &App{}
}
func (s *App) Route(r chi.Router) {
r.Post("/test", s.Test)
}
// Test 运行测试
func (s *App) Test(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Test](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
switch req.Name {
case "image":
result := s.imageProcessing(req.Multi)
service.Success(w, result)
case "machine":
result := s.machineLearning(req.Multi)
service.Success(w, result)
case "compile":
result := s.compileSimulationSingle(req.Multi)
service.Success(w, result)
case "encryption":
result := s.encryptionTest(req.Multi)
service.Success(w, result)
case "compression":
result := s.compressionTest(req.Multi)
service.Success(w, result)
case "physics":
result := s.physicsSimulation(req.Multi)
service.Success(w, result)
case "json":
result := s.jsonProcessing(req.Multi)
service.Success(w, result)
case "disk":
result := s.diskTestTask()
service.Success(w, result)
case "memory":
result := s.memoryTestTask()
service.Success(w, result)
default:
service.Error(w, http.StatusUnprocessableEntity, "未知测试类型")
}
}
// calculateCpuScore 计算CPU成绩
func (s *App) calculateCpuScore(duration time.Duration) int {
score := int((10 / duration.Seconds()) * float64(3000))
if score < 0 {
score = 0
}
return score
}
// calculateScore 计算内存/硬盘成绩
func (s *App) calculateScore(duration time.Duration) int {
score := int((20 / duration.Seconds()) * float64(30000))
if score < 0 {
score = 0
}
return score
}
// 图像处理
func (s *App) imageProcessing(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
}
start := time.Now()
if err := s.imageProcessingTask(n); err != nil {
return 0
}
duration := time.Since(start)
return s.calculateCpuScore(duration)
}
func (s *App) imageProcessingTask(numThreads int) error {
img := image.NewRGBA(image.Rect(0, 0, 4000, 4000))
for x := 0; x < 4000; x++ {
for y := 0; y < 4000; y++ {
img.Set(x, y, color.RGBA{R: uint8(x % 256), G: uint8(y % 256), A: 255})
}
}
var wg sync.WaitGroup
dx := img.Bounds().Dx()
dy := img.Bounds().Dy()
chunkSize := dy / numThreads
for i := 0; i < numThreads; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
startY := i * chunkSize
endY := startY + chunkSize
if i == numThreads-1 {
endY = dy
}
for x := 1; x < dx-1; x++ {
for y := startY + 1; y < endY-1; y++ {
// 卷积操作(模糊)
rTotal, gTotal, bTotal := 0, 0, 0
for k := -1; k <= 1; k++ {
for l := -1; l <= 1; l++ {
r, g, b, _ := img.At(x+k, y+l).RGBA()
rTotal += int(r)
gTotal += int(g)
bTotal += int(b)
}
}
rAvg := uint8(rTotal / 9 / 256)
gAvg := uint8(gTotal / 9 / 256)
bAvg := uint8(bTotal / 9 / 256)
img.Set(x, y, color.RGBA{R: rAvg, G: gAvg, B: bAvg, A: 255})
}
}
}(i)
}
wg.Wait()
return nil
}
// 机器学习(矩阵乘法)
func (s *App) machineLearning(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
}
start := time.Now()
s.machineLearningTask(n)
duration := time.Since(start)
return s.calculateCpuScore(duration)
}
func (s *App) machineLearningTask(numThreads int) {
size := 900
a := make([][]float64, size)
b := make([][]float64, size)
for i := 0; i < size; i++ {
a[i] = make([]float64, size)
b[i] = make([]float64, size)
for j := 0; j < size; j++ {
a[i][j] = rand.Float64()
b[i][j] = rand.Float64()
}
}
c := make([][]float64, size)
for i := 0; i < size; i++ {
c[i] = make([]float64, size)
}
var wg sync.WaitGroup
chunkSize := size / numThreads
for k := 0; k < numThreads; k++ {
wg.Add(1)
go func(k int) {
defer wg.Done()
start := k * chunkSize
end := start + chunkSize
if k == numThreads-1 {
end = size
}
for i := start; i < end; i++ {
for j := 0; j < size; j++ {
sum := 0.0
for l := 0; l < size; l++ {
sum += a[i][l] * b[l][j]
}
c[i][j] = sum
}
}
}(k)
}
wg.Wait()
}
// 数学问题(计算斐波那契数)
func (s *App) compileSimulationSingle(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
}
start := time.Now()
totalCalculations := 1000
fibNumber := 20000
calculationsPerThread := totalCalculations / n
remainder := totalCalculations % n
var wg sync.WaitGroup
for i := 0; i < n; i++ {
tasks := calculationsPerThread
if i < remainder {
tasks++ // 处理无法均分的剩余任务
}
wg.Add(1)
go func(tasks int) {
defer wg.Done()
for j := 0; j < tasks; j++ {
s.fib(fibNumber)
}
}(tasks)
}
wg.Wait()
duration := time.Since(start)
return s.calculateCpuScore(duration)
}
// 斐波那契函数
func (s *App) fib(n int) *big.Int {
if n < 2 {
return big.NewInt(int64(n))
}
a := big.NewInt(0)
b := big.NewInt(1)
temp := big.NewInt(0)
for i := 2; i <= n; i++ {
temp.Add(a, b)
a.Set(b)
b.Set(temp)
}
return b
}
// AES加密
func (s *App) encryptionTest(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
}
start := time.Now()
if err := s.encryptionTestTask(n); err != nil {
return 0
}
duration := time.Since(start)
return s.calculateCpuScore(duration)
}
func (s *App) encryptionTestTask(numThreads int) error {
key := []byte("abcdefghijklmnopqrstuvwxyz123456")
dataSize := 1024 * 1024 * 512 // 512 MB
plaintext := []byte(strings.Repeat("A", dataSize))
block, err := aes.NewCipher(key)
if err != nil {
return err
}
aesGCM, err := cipher.NewGCM(block)
if err != nil {
return err
}
chunkSize := dataSize / numThreads
var wg sync.WaitGroup
for i := 0; i < numThreads; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
start := i * chunkSize
end := start + chunkSize
if i == numThreads-1 {
end = dataSize
}
nonce := make([]byte, aesGCM.NonceSize())
if _, err = cryptorand.Read(nonce); err != nil {
return
}
aesGCM.Seal(nil, nonce, plaintext[start:end], nil)
}(i)
}
wg.Wait()
return nil
}
// 压缩/解压缩
func (s *App) compressionTest(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
}
start := time.Now()
s.compressionTestTask(n)
duration := time.Since(start)
return s.calculateCpuScore(duration)
}
func (s *App) compressionTestTask(numThreads int) {
data := []byte(strings.Repeat("耗子面板", 50000000))
chunkSize := len(data) / numThreads
var wg sync.WaitGroup
compressedChunks := make([]bytes.Buffer, numThreads)
// 压缩
for i := 0; i < numThreads; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
start := i * chunkSize
end := start + chunkSize
if i == numThreads-1 {
end = len(data)
}
var buf bytes.Buffer
w := gzip.NewWriter(&buf)
_, _ = w.Write(data[start:end])
_ = w.Close()
compressedChunks[i] = buf
}(i)
}
wg.Wait()
// 解压缩
for i := 0; i < numThreads; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
r, err := gzip.NewReader(&compressedChunks[i])
if err != nil {
return
}
_, err = io.Copy(io.Discard, r)
if err != nil {
return
}
_ = r.Close()
}(i)
}
wg.Wait()
}
// 物理仿真(N体问题)
func (s *App) physicsSimulation(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
}
start := time.Now()
s.physicsSimulationTask(n)
duration := time.Since(start)
return s.calculateCpuScore(duration)
}
func (s *App) physicsSimulationTask(numThreads int) {
const (
numBodies = 4000
steps = 30
)
type Body struct {
x, y, z, vx, vy, vz float64
}
bodies := make([]Body, numBodies)
for i := 0; i < numBodies; i++ {
bodies[i] = Body{
x: rand.Float64(),
y: rand.Float64(),
z: rand.Float64(),
vx: rand.Float64(),
vy: rand.Float64(),
vz: rand.Float64(),
}
}
chunkSize := numBodies / numThreads
for step := 0; step < steps; step++ {
var wg sync.WaitGroup
// 更新速度
for k := 0; k < numThreads; k++ {
wg.Add(1)
go func(k int) {
defer wg.Done()
start := k * chunkSize
end := start + chunkSize
if k == numThreads-1 {
end = numBodies
}
for i := start; i < end; i++ {
bi := &bodies[i]
for j := 0; j < numBodies; j++ {
if i == j {
continue
}
bj := &bodies[j]
dx := bj.x - bi.x
dy := bj.y - bi.y
dz := bj.z - bi.z
dist := math.Sqrt(dx*dx + dy*dy + dz*dz)
if dist == 0 {
continue
}
force := 1 / (dist * dist)
bi.vx += force * dx / dist
bi.vy += force * dy / dist
bi.vz += force * dz / dist
}
}
}(k)
}
wg.Wait()
// 更新位置
for k := 0; k < numThreads; k++ {
wg.Add(1)
go func(k int) {
defer wg.Done()
start := k * chunkSize
end := start + chunkSize
if k == numThreads-1 {
end = numBodies
}
for i := start; i < end; i++ {
bi := &bodies[i]
bi.x += bi.vx
bi.y += bi.vy
bi.z += bi.vz
}
}(k)
}
wg.Wait()
}
}
// JSON解析
func (s *App) jsonProcessing(multi bool) int {
n := 1
if multi {
n = runtime.NumCPU()
}
start := time.Now()
s.jsonProcessingTask(n)
duration := time.Since(start)
return s.calculateCpuScore(duration)
}
func (s *App) jsonProcessingTask(numThreads int) {
numElements := 1000000
elementsPerThread := numElements / numThreads
var wg sync.WaitGroup
for i := 0; i < numThreads; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
start := i * elementsPerThread
end := start + elementsPerThread
if i == numThreads-1 {
end = numElements
}
elements := make([]map[string]any, 0, end-start)
for j := start; j < end; j++ {
elements = append(elements, map[string]any{
"id": j,
"value": fmt.Sprintf("Value%d", j),
})
}
encoded, err := json.Marshal(elements)
if err != nil {
return
}
var parsed []map[string]any
err = json.Unmarshal(encoded, &parsed)
if err != nil {
return
}
}(i)
}
wg.Wait()
}
// 内存性能
func (s *App) memoryTestTask() map[string]any {
results := make(map[string]any)
dataSize := 500 * 1024 * 1024 // 500 MB
data := make([]byte, dataSize)
_, _ = cryptorand.Read(data)
start := time.Now()
// 内存读写速度
results["bandwidth"] = s.memoryBandwidthTest(data)
// 内存访问延迟
data = data[:100*1024*1024] // 100 MB
results["latency"] = s.memoryLatencyTest(data)
duration := time.Since(start)
results["score"] = s.calculateScore(duration)
return results
}
func (s *App) memoryBandwidthTest(data []byte) string {
dataSize := len(data)
startTime := time.Now()
for i := 0; i < dataSize; i++ {
data[i] ^= 0xFF
}
duration := time.Since(startTime).Seconds()
if duration == 0 {
return "N/A"
}
speed := float64(dataSize) / duration / (1024 * 1024)
return fmt.Sprintf("%.2f MB/s", speed)
}
func (s *App) memoryLatencyTest(data []byte) string {
dataSize := len(data)
indices := rand.Perm(dataSize)
startTime := time.Now()
sum := byte(0)
for _, idx := range indices {
sum ^= data[idx]
}
duration := time.Since(startTime).Seconds()
if duration == 0 {
return "N/A"
}
avgLatency := duration * 1e9 / float64(dataSize)
return fmt.Sprintf("%.2f ns", avgLatency)
}
// 硬盘IO
func (s *App) diskTestTask() map[string]any {
results := make(map[string]any)
blockSizes := []int64{4 * 1024, 64 * 1024, 512 * 1024, 1 * 1024 * 1024} // 4K, 64K, 512K, 1M
fileSize := int64(100 * 1024 * 1024) // 100MB 文件
start := time.Now()
for _, blockSize := range blockSizes {
result := s.diskIOTest(blockSize, fileSize)
results[fmt.Sprintf("%d", blockSize/1024)] = result
}
duration := time.Since(start)
results["score"] = s.calculateScore(duration)
return results
}
func (s *App) diskIOTest(blockSize int64, fileSize int64) map[string]any {
result := make(map[string]any)
tempFile := fmt.Sprintf("tempfile_%d", blockSize)
defer os.Remove(tempFile)
// 写测试
writeSpeed, writeIOPS := s.diskWriteTest(tempFile, blockSize, fileSize)
// 读测试
readSpeed, readIOPS := s.diskReadTest(tempFile, blockSize, fileSize)
result["write_speed"] = fmt.Sprintf("%.2f MB/s", writeSpeed)
result["write_iops"] = fmt.Sprintf("%.2f IOPS", writeIOPS)
result["read_speed"] = fmt.Sprintf("%.2f MB/s", readSpeed)
result["read_iops"] = fmt.Sprintf("%.2f IOPS", readIOPS)
return result
}
func (s *App) diskWriteTest(fileName string, blockSize int64, fileSize int64) (float64, float64) {
totalBlocks := fileSize / blockSize
data := make([]byte, blockSize)
_, _ = cryptorand.Read(data)
file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_SYNC, 0644)
if err != nil {
return 0, 0
}
defer file.Close()
start := time.Now()
for i := int64(0); i < totalBlocks; i++ {
// 生成随机偏移
offset := rand.Int64N(fileSize - blockSize + 1)
_, err := file.WriteAt(data, offset)
if err != nil {
return 0, 0
}
}
_ = file.Sync()
duration := time.Since(start).Seconds()
if duration == 0 {
duration = 1
}
speed := float64(totalBlocks*blockSize) / duration / (1024 * 1024)
iops := float64(totalBlocks) / duration
return speed, iops
}
func (s *App) diskReadTest(fileName string, blockSize int64, fileSize int64) (float64, float64) {
totalBlocks := fileSize / blockSize
data := make([]byte, blockSize)
file, err := os.OpenFile(fileName, os.O_RDONLY|os.O_SYNC, 0644)
if err != nil {
return 0, 0
}
defer file.Close()
start := time.Now()
for i := int64(0); i < totalBlocks; i++ {
// 生成随机偏移
offset := rand.Int64N(fileSize - blockSize + 1)
_, err := file.ReadAt(data, offset)
if err != nil && err != io.EOF {
return 0, 0
}
}
duration := time.Since(start).Seconds()
if duration == 0 {
duration = 1
}
speed := float64(totalBlocks*blockSize) / duration / (1024 * 1024)
iops := float64(totalBlocks) / duration
return speed, iops
}
-6
View File
@@ -1,6 +0,0 @@
package benchmark
type Test struct {
Name string `json:"name" validate:"required|in:image,machine,compile,encryption,compression,physics,json,memory,disk"`
Multi bool `json:"multi"`
}
+332
View File
@@ -0,0 +1,332 @@
package clickhouse
import (
"crypto/sha256"
"fmt"
"net/http"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/spf13/cast"
"go.yaml.in/yaml/v4"
"resty.dev/v3"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/biz"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
t *gotext.Locale
settingRepo biz.SettingRepo
databaseServerRepo biz.DatabaseServerRepo
}
func NewApp(t *gotext.Locale, databaseServerRepo biz.DatabaseServerRepo, settingRepo biz.SettingRepo) (*App, error) {
return &App{
t: t,
settingRepo: settingRepo,
databaseServerRepo: databaseServerRepo,
}, nil
}
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)
r.Get("/default_password", s.GetDefaultPassword)
r.Post("/default_password", s.SetDefaultPassword)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("clickhouse-server")
return types.AggregateAppStatus(ok)
}
// Load 获取 ClickHouse 运行状态
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
status, _ := systemctl.Status("clickhouse-server")
if !status {
service.Success(w, []types.NV{})
return
}
password, _ := s.settingRepo.Get(biz.SettingKeyClickHouseDefaultPassword)
port := s.getPort()
client := resty.New().SetTimeout(10 * time.Second)
defer func(client *resty.Client) { _ = client.Close() }(client)
// 获取版本
versionResp, err := client.R().Get(fmt.Sprintf("http://127.0.0.1:%s/?query=SELECT+version()&user=default&password=%s", port, password))
if err != nil || !versionResp.IsStatusSuccess() {
service.Success(w, []types.NV{})
return
}
version := strings.TrimSpace(string(versionResp.Bytes()))
// 获取运行时间
uptimeResp, _ := client.R().Get(fmt.Sprintf("http://127.0.0.1:%s/?query=SELECT+uptime()&user=default&password=%s", port, password))
uptime := strings.TrimSpace(string(uptimeResp.Bytes()))
// 获取当前查询数
queriesResp, _ := client.R().Get(fmt.Sprintf("http://127.0.0.1:%s/?query=SELECT+value+FROM+system.metrics+WHERE+metric='Query'&user=default&password=%s", port, password))
queries := strings.TrimSpace(string(queriesResp.Bytes()))
// 获取内存使用
memResp, _ := client.R().Get(fmt.Sprintf("http://127.0.0.1:%s/?query=SELECT+value+FROM+system.metrics+WHERE+metric='MemoryTracking'&user=default&password=%s", port, password))
memUsage := strings.TrimSpace(string(memResp.Bytes()))
// 获取数据库数量
dbCountResp, _ := client.R().Get(fmt.Sprintf("http://127.0.0.1:%s/?query=SELECT+count()+FROM+system.databases&user=default&password=%s", port, password))
dbCount := strings.TrimSpace(string(dbCountResp.Bytes()))
// 获取表数量
tableCountResp, _ := client.R().Get(fmt.Sprintf("http://127.0.0.1:%s/?query=SELECT+count()+FROM+system.tables&user=default&password=%s", port, password))
tableCount := strings.TrimSpace(string(tableCountResp.Bytes()))
data := []types.NV{
{Name: s.t.Get("Version"), Value: version},
{Name: s.t.Get("Uptime (seconds)"), Value: uptime},
{Name: s.t.Get("Active Queries"), Value: queries},
{Name: s.t.Get("Memory Usage (bytes)"), Value: memUsage},
{Name: s.t.Get("Databases"), Value: dbCount},
{Name: s.t.Get("Tables"), Value: tableCount},
}
service.Success(w, data)
}
// GetConfig 获取配置
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
conf, _ := io.Read(s.configPath())
service.Success(w, conf)
}
// UpdateConfig 更新配置
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(s.configPath(), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("clickhouse-server"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetConfigTune 获取配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
raw, _ := io.Read(s.configPath())
var cfg map[string]any
_ = yaml.Unmarshal([]byte(raw), &cfg)
if cfg == nil {
cfg = make(map[string]any)
}
tune := ConfigTune{
ListenHost: s.getYAMLValue(cfg, "listen_host"),
HTTPPort: s.getYAMLValue(cfg, "http_port"),
TCPPort: s.getYAMLValue(cfg, "tcp_port"),
MaxMemoryUsage: s.getYAMLValue(cfg, "max_memory_usage"),
MaxThreads: s.getYAMLValue(cfg, "max_threads"),
Path: s.getYAMLValue(cfg, "path"),
TmpPath: s.getYAMLValue(cfg, "tmp_path"),
LogLevel: s.getYAMLValue(cfg, "logger.level"),
}
service.Success(w, tune)
}
// UpdateConfigTune 更新配置调整参数
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
}
raw, _ := io.Read(s.configPath())
var cfg map[string]any
if err = yaml.Unmarshal([]byte(raw), &cfg); err != nil {
cfg = make(map[string]any)
}
// ClickHouse 顶层键直接用平铺方式
s.setYAMLValue(cfg, "listen_host", req.ListenHost)
s.setYAMLValue(cfg, "http_port", req.HTTPPort)
s.setYAMLValue(cfg, "tcp_port", req.TCPPort)
s.setYAMLValue(cfg, "max_memory_usage", req.MaxMemoryUsage)
s.setYAMLValue(cfg, "max_threads", req.MaxThreads)
s.setYAMLValue(cfg, "path", req.Path)
s.setYAMLValue(cfg, "tmp_path", req.TmpPath)
// logger.level 是嵌套的
s.setNestedYAMLValue(cfg, "logger.level", req.LogLevel)
data, err := yaml.Marshal(cfg)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = io.Write(s.configPath(), string(data), 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("clickhouse-server"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetDefaultPassword 获取 default 用户密码
func (s *App) GetDefaultPassword(w http.ResponseWriter, r *http.Request) {
password, err := s.settingRepo.Get(biz.SettingKeyClickHouseDefaultPassword)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get ClickHouse default password: %v", err))
return
}
service.Success(w, password)
}
// SetDefaultPassword 设置 default 用户密码
func (s *App) SetDefaultPassword(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[SetDefaultPassword](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
// 计算 SHA256 哈希
hash := sha256.Sum256([]byte(req.Password))
hexHash := fmt.Sprintf("%x", hash)
// 读取 users.d/default.yaml 并更新密码
raw, _ := io.Read(s.usersConfigPath())
var cfg map[string]any
if err = yaml.Unmarshal([]byte(raw), &cfg); err != nil {
cfg = make(map[string]any)
}
users, _ := cfg["users"].(map[string]any)
if users == nil {
users = make(map[string]any)
cfg["users"] = users
}
def, _ := users["default"].(map[string]any)
if def == nil {
def = make(map[string]any)
users["default"] = def
}
def["password_sha256_hex"] = hexHash
out, _ := yaml.Marshal(cfg)
if err = io.Write(s.usersConfigPath(), string(out), 0644); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to write ClickHouse user config: %v", err))
return
}
// 重启服务使密码生效
if err = systemctl.Restart("clickhouse-server"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
// 保存明文到面板数据库
if err = s.settingRepo.Set(biz.SettingKeyClickHouseDefaultPassword, req.Password); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to save ClickHouse default password: %v", err))
return
}
_ = s.databaseServerRepo.UpdatePassword("local_clickhouse", req.Password)
service.Success(w, nil)
}
// configPath 返回主配置文件路径
func (s *App) configPath() string {
return fmt.Sprintf("%s/server/clickhouse/config/config.yaml", app.Root)
}
// usersConfigPath 返回用户密码配置文件路径(users.d/ 由 ConfigProcessor 自动合并到 users.yaml
func (s *App) usersConfigPath() string {
return fmt.Sprintf("%s/server/clickhouse/config/users.d/default.yaml", app.Root)
}
// getPort 从配置中获取 HTTP 端口
func (s *App) getPort() string {
raw, _ := io.Read(s.configPath())
var cfg map[string]any
_ = yaml.Unmarshal([]byte(raw), &cfg)
if cfg != nil {
if v := s.getYAMLValue(cfg, "http_port"); v != "" {
return v
}
}
return "8123"
}
// getYAMLValue 获取 YAML 值,支持嵌套键
func (s *App) getYAMLValue(cfg map[string]any, key string) string {
// 先尝试平铺键
if val, ok := cfg[key]; ok {
return cast.ToString(val)
}
// 回退到嵌套键
parts := strings.SplitN(key, ".", 2)
val, ok := cfg[parts[0]]
if !ok {
return ""
}
if len(parts) == 1 {
return cast.ToString(val)
}
nested, ok := val.(map[string]any)
if !ok {
return ""
}
return s.getYAMLValue(nested, parts[1])
}
// setYAMLValue 设置平铺 YAML 值
func (s *App) setYAMLValue(cfg map[string]any, key string, value string) {
if value == "" {
return
}
cfg[key] = value
}
// setNestedYAMLValue 设置嵌套 YAML 值
func (s *App) setNestedYAMLValue(cfg map[string]any, key string, value string) {
if value == "" {
return
}
parts := strings.SplitN(key, ".", 2)
if len(parts) == 1 {
cfg[parts[0]] = value
return
}
nested, ok := cfg[parts[0]].(map[string]any)
if !ok {
nested = make(map[string]any)
cfg[parts[0]] = nested
}
s.setNestedYAMLValue(nested, parts[1], value)
}
+27
View File
@@ -0,0 +1,27 @@
package clickhouse
// UpdateConfig 更新配置
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
// SetDefaultPassword 设置 default 用户密码
type SetDefaultPassword struct {
Password string `form:"password" json:"password" validate:"required && password"`
}
// ConfigTune ClickHouse 配置调整
type ConfigTune struct {
// 网络
ListenHost string `form:"listen_host" json:"listen_host"`
HTTPPort string `form:"http_port" json:"http_port" validate:"number && min:1 && max:65535"`
TCPPort string `form:"tcp_port" json:"tcp_port" validate:"number && min:1 && max:65535"`
// 性能
MaxMemoryUsage string `form:"max_memory_usage" json:"max_memory_usage"`
MaxThreads string `form:"max_threads" json:"max_threads"`
// 路径
Path string `form:"path" json:"path" validate:"unix_path"`
TmpPath string `form:"tmp_path" json:"tmp_path" validate:"unix_path"`
// 日志
LogLevel string `form:"log_level" json:"log_level" validate:"in:trace,debug,information,warning,error"`
}
+53
View File
@@ -0,0 +1,53 @@
package codeserver
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct{}
func NewApp() (*App, error) {
return &App{}, nil
}
func (s *App) Route(r chi.Router) {
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("code-server")
return types.AggregateAppStatus(ok)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, _ := io.Read("/root/.config/code-server/config.yaml")
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("/root/.config/code-server/config.yaml", req.Config, 0600); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("code-server"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
+5
View File
@@ -0,0 +1,5 @@
package codeserver
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
+222 -5
View File
@@ -1,24 +1,35 @@
package docker
import (
"encoding/json"
"net/http"
"os"
"strings"
"github.com/go-chi/chi/v5"
"github.com/tnb-labs/panel/internal/service"
"github.com/tnb-labs/panel/pkg/io"
"github.com/tnb-labs/panel/pkg/systemctl"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct{}
func NewApp() *App {
return &App{}
func NewApp() (*App, error) {
return &App{}, nil
}
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) Status() string {
ok, _ := systemctl.Status("docker")
return types.AggregateAppStatus(ok)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
@@ -50,3 +61,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 after, ok := strings.CutPrefix(opt, "native.cgroupdriver="); ok {
settings.CgroupDriver = after
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"` // 日志配置选项
CgroupDriver string `json:"cgroup-driver,omitempty" validate:"in:systemd,cgroupfs"` // 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" validate:"in:iptables,nftables"` // 防火墙后端 (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:"-"`
}
+303
View File
@@ -0,0 +1,303 @@
package elasticsearch
import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/spf13/cast"
"go.yaml.in/yaml/v4"
"resty.dev/v3"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
t *gotext.Locale
}
func NewApp(t *gotext.Locale) (*App, error) {
return &App{t: t}, nil
}
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) Status() string {
ok, _ := systemctl.Status("elasticsearch")
return types.AggregateAppStatus(ok)
}
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
status, err := systemctl.Status("elasticsearch")
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get elasticsearch status: %v", err))
return
}
if !status {
service.Success(w, []types.NV{})
return
}
port := s.getPort()
client := resty.New().SetTimeout(10 * time.Second)
defer func(client *resty.Client) { _ = client.Close() }(client)
resp, err := client.R().Get(fmt.Sprintf("http://127.0.0.1:%s/_cluster/health", port))
if err != nil || !resp.IsStatusSuccess() {
service.Success(w, []types.NV{})
return
}
var health struct {
ClusterName string `json:"cluster_name"`
Status string `json:"status"`
NumberOfNodes int `json:"number_of_nodes"`
NumberOfDataNodes int `json:"number_of_data_nodes"`
ActiveShards int `json:"active_shards"`
ActivePrimaryShards int `json:"active_primary_shards"`
RelocatingShards int `json:"relocating_shards"`
UnassignedShards int `json:"unassigned_shards"`
}
if err = json.Unmarshal(resp.Bytes(), &health); err != nil {
service.Success(w, []types.NV{})
return
}
data := []types.NV{
{Name: s.t.Get("Cluster Name"), Value: health.ClusterName},
{Name: s.t.Get("Cluster Status"), Value: health.Status},
{Name: s.t.Get("Number of Nodes"), Value: cast.ToString(health.NumberOfNodes)},
{Name: s.t.Get("Number of Data Nodes"), Value: cast.ToString(health.NumberOfDataNodes)},
{Name: s.t.Get("Active Shards"), Value: cast.ToString(health.ActiveShards)},
{Name: s.t.Get("Active Primary Shards"), Value: cast.ToString(health.ActivePrimaryShards)},
{Name: s.t.Get("Relocating Shards"), Value: cast.ToString(health.RelocatingShards)},
{Name: s.t.Get("Unassigned Shards"), Value: cast.ToString(health.UnassignedShards)},
}
service.Success(w, data)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
conf, _ := io.Read(s.configPath())
service.Success(w, conf)
}
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(s.configPath(), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("elasticsearch"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetConfigTune 获取 ElasticSearch 配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
raw, _ := io.Read(s.configPath())
var cfg map[string]any
_ = yaml.Unmarshal([]byte(raw), &cfg)
if cfg == nil {
cfg = make(map[string]any)
}
// ES 9.x 不再在 jvm.options 中设置堆内存,改为 jvm.options.d/heap.options
heapRaw, _ := io.Read(s.jvmHeapOptionsPath())
heapInit, heapMax := s.parseJVMHeap(heapRaw)
// 兼容旧版本:如果 heap.options 没有,尝试从 jvm.options 读取
if heapInit == "" && heapMax == "" {
jvmRaw, _ := io.Read(s.jvmOptionsPath())
heapInit, heapMax = s.parseJVMHeap(jvmRaw)
}
tune := ConfigTune{
ClusterName: s.getYAMLValue(cfg, "cluster.name"),
NodeName: s.getYAMLValue(cfg, "node.name"),
NetworkHost: s.getYAMLValue(cfg, "network.host"),
HTTPPort: s.getYAMLValue(cfg, "http.port"),
DiscoveryType: s.getYAMLValue(cfg, "discovery.type"),
PathData: s.getYAMLValue(cfg, "path.data"),
PathLogs: s.getYAMLValue(cfg, "path.logs"),
HeapInitSize: heapInit,
HeapMaxSize: heapMax,
}
service.Success(w, tune)
}
// UpdateConfigTune 更新 ElasticSearch 配置调整参数
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
}
// 更新 YAML 配置
raw, _ := io.Read(s.configPath())
var cfg map[string]any
if err = yaml.Unmarshal([]byte(raw), &cfg); err != nil {
cfg = make(map[string]any)
}
s.setYAMLValue(cfg, "cluster.name", req.ClusterName)
s.setYAMLValue(cfg, "node.name", req.NodeName)
s.setYAMLValue(cfg, "network.host", req.NetworkHost)
s.setYAMLValue(cfg, "http.port", req.HTTPPort)
s.setYAMLValue(cfg, "discovery.type", req.DiscoveryType)
s.setYAMLValue(cfg, "path.data", req.PathData)
s.setYAMLValue(cfg, "path.logs", req.PathLogs)
data, err := yaml.Marshal(cfg)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = io.Write(s.configPath(), string(data), 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
// 更新 JVM 堆内存(写入 jvm.options.d/heap.options
if req.HeapInitSize != "" || req.HeapMaxSize != "" {
heapRaw, _ := io.Read(s.jvmHeapOptionsPath())
heapRaw = s.setJVMHeap(heapRaw, req.HeapInitSize, req.HeapMaxSize)
if err = io.Write(s.jvmHeapOptionsPath(), heapRaw, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
}
if err = systemctl.Restart("elasticsearch"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// configPath 返回配置文件路径
func (s *App) configPath() string {
return fmt.Sprintf("%s/server/elasticsearch/config/elasticsearch.yml", app.Root)
}
// jvmOptionsPath 返回 JVM 选项文件路径
func (s *App) jvmOptionsPath() string {
return fmt.Sprintf("%s/server/elasticsearch/config/jvm.options", app.Root)
}
// jvmHeapOptionsPath 返回 JVM 堆内存配置文件路径(ES 9.x 推荐方式)
func (s *App) jvmHeapOptionsPath() string {
return fmt.Sprintf("%s/server/elasticsearch/config/jvm.options.d/heap.options", app.Root)
}
// getPort 从配置中获取 HTTP 端口
func (s *App) getPort() string {
raw, _ := io.Read(s.configPath())
var cfg map[string]any
_ = yaml.Unmarshal([]byte(raw), &cfg)
if cfg != nil {
if v := s.getYAMLValue(cfg, "http.port"); v != "" {
return v
}
}
return "9200"
}
// getYAMLValue 获取 YAML 值,优先匹配平铺键(如 "path.data"),回退到嵌套键(如 path -> data
func (s *App) getYAMLValue(cfg map[string]any, key string) string {
// 优先匹配平铺键(安装脚本用 sed 生成的格式)
if val, ok := cfg[key]; ok {
return cast.ToString(val)
}
// 回退到嵌套键
parts := strings.SplitN(key, ".", 2)
val, ok := cfg[parts[0]]
if !ok {
return ""
}
if len(parts) == 1 {
return cast.ToString(val)
}
nested, ok := val.(map[string]any)
if !ok {
return ""
}
return s.getYAMLValue(nested, parts[1])
}
// setYAMLValue 设置 YAML 值
func (s *App) setYAMLValue(cfg map[string]any, key string, value string) {
if value == "" {
return
}
// 使用平铺键,同时清理可能存在的嵌套键
cfg[key] = value
parts := strings.SplitN(key, ".", 2)
if len(parts) == 2 {
if nested, ok := cfg[parts[0]].(map[string]any); ok {
delete(nested, parts[1])
if len(nested) == 0 {
delete(cfg, parts[0])
}
}
}
}
// parseJVMHeap 从 jvm.options 中提取堆内存配置
func (s *App) parseJVMHeap(content string) (initSize, maxSize string) {
reInit := regexp.MustCompile(`(?m)^-Xms(\S+)`)
reMax := regexp.MustCompile(`(?m)^-Xmx(\S+)`)
if m := reInit.FindStringSubmatch(content); len(m) == 2 {
initSize = m[1]
}
if m := reMax.FindStringSubmatch(content); len(m) == 2 {
maxSize = m[1]
}
return
}
// setJVMHeap 替换 jvm.options 中的堆内存配置
func (s *App) setJVMHeap(content string, initSize, maxSize string) string {
if initSize != "" {
re := regexp.MustCompile(`(?m)^-Xms\S+`)
if re.MatchString(content) {
content = re.ReplaceAllString(content, "-Xms"+initSize)
} else {
content += "\n-Xms" + initSize
}
}
if maxSize != "" {
re := regexp.MustCompile(`(?m)^-Xmx\S+`)
if re.MatchString(content) {
content = re.ReplaceAllString(content, "-Xmx"+maxSize)
} else {
content += "\n-Xmx" + maxSize
}
}
return content
}
+21
View File
@@ -0,0 +1,21 @@
package elasticsearch
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
// ConfigTune ElasticSearch 配置调整
type ConfigTune struct {
// 集群
ClusterName string `form:"cluster_name" json:"cluster_name"`
NodeName string `form:"node_name" json:"node_name"`
NetworkHost string `form:"network_host" json:"network_host"`
HTTPPort string `form:"http_port" json:"http_port"`
DiscoveryType string `form:"discovery_type" json:"discovery_type"`
// 路径
PathData string `form:"path_data" json:"path_data"`
PathLogs string `form:"path_logs" json:"path_logs"`
// JVM
HeapInitSize string `form:"heap_init_size" json:"heap_init_size"`
HeapMaxSize string `form:"heap_max_size" json:"heap_max_size"`
}
+55 -51
View File
@@ -7,25 +7,31 @@ import (
"strings"
"github.com/go-chi/chi/v5"
"github.com/go-rat/chix"
"github.com/go-rat/utils/str"
"github.com/leonelquinteros/gotext"
"github.com/libtnb/chix/v2"
"github.com/libtnb/utils/str"
"github.com/samber/lo"
"github.com/spf13/cast"
"github.com/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/biz"
"github.com/tnb-labs/panel/internal/service"
"github.com/tnb-labs/panel/pkg/io"
"github.com/tnb-labs/panel/pkg/shell"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/biz"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/shell"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
t *gotext.Locale
websiteRepo biz.WebsiteRepo
}
func NewApp(website biz.WebsiteRepo) *App {
func NewApp(t *gotext.Locale, websiteRepo biz.WebsiteRepo) (*App, error) {
return &App{
websiteRepo: website,
}
t: t,
websiteRepo: websiteRepo,
}, nil
}
func (s *App) Route(r chi.Router) {
@@ -34,8 +40,13 @@ func (s *App) Route(r chi.Router) {
r.Delete("/jails", s.Delete)
r.Get("/jails/{name}", s.BanList)
r.Post("/unban", s.Unban)
r.Post("/whiteList", s.SetWhiteList)
r.Get("/whiteList", s.GetWhiteList)
r.Post("/white_list", s.SetWhiteList)
r.Get("/white_list", s.GetWhiteList)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("fail2ban")
return types.AggregateAppStatus(ok)
}
// List 所有规则
@@ -47,12 +58,8 @@ func (s *App) List(w http.ResponseWriter, r *http.Request) {
}
jailList := regexp.MustCompile(`\[(.*?)]`).FindAllStringSubmatch(raw, -1)
if len(jailList) == 0 {
service.Error(w, http.StatusUnprocessableEntity, "Fail2ban 规则为空")
return
}
var jails []Jail
jails := make([]Jail, 0)
for i, jail := range jailList {
if i == 0 {
continue
@@ -107,7 +114,7 @@ func (s *App) Create(w http.ResponseWriter, r *http.Request) {
return
}
if (strings.Contains(raw, "["+jailName+"]") && jailType == "service") || (strings.Contains(raw, "["+jailWebsiteName+"]"+"-cc") && jailType == "website" && jailWebsiteMode == "cc") || (strings.Contains(raw, "["+jailWebsiteName+"]"+"-path") && jailType == "website" && jailWebsiteMode == "path") {
service.Error(w, http.StatusUnprocessableEntity, "规则已存在")
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("rule already exists"))
return
}
@@ -115,7 +122,7 @@ func (s *App) Create(w http.ResponseWriter, r *http.Request) {
case "website":
website, err := s.websiteRepo.GetByName(jailWebsiteName)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "获取网站配置失败:%v", err)
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
var ports string
@@ -135,12 +142,12 @@ 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
if err = io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "写入Fail2ban规则失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -159,7 +166,7 @@ ignoreregex =
`
}
if err = io.Write("/etc/fail2ban/filter.d/haozi-"+jailWebsiteName+"-"+jailWebsiteMode+".conf", filter, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "写入Fail2ban规则失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -170,7 +177,7 @@ ignoreregex =
switch jailName {
case "ssh":
filter = "sshd"
port, err = shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}'")
port, err = shell.Execf("cat /etc/ssh/sshd_config | grep 'Port ' | awk '{print $2}' | paste -sd ','")
case "mysql":
filter = "mysqld-auth"
port, err = shell.Execf("cat %s/server/mysql/conf/my.cnf | grep 'port' | head -n 1 | awk '{print $3}'", app.Root)
@@ -178,11 +185,11 @@ ignoreregex =
filter = "pure-ftpd"
port, err = shell.Execf(`cat %s/server/pure-ftpd/etc/pure-ftpd.conf | grep "Bind" | awk '{print $2}' | awk -F "," '{print $2}'`, app.Root)
default:
service.Error(w, http.StatusUnprocessableEntity, "未知服务")
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("unknown service"))
return
}
if len(port) == 0 || err != nil {
service.Error(w, http.StatusUnprocessableEntity, "获取服务端口失败,请检查是否安装")
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("get service port failed, please check if it is installed"))
return
}
@@ -199,13 +206,13 @@ bantime = ` + jailBanTime + `
`
raw += rule
if err := io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "写入Fail2ban规则失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
}
if _, err = shell.Execf("fail2ban-client reload"); err != nil {
service.Error(w, http.StatusInternalServerError, "重载配置失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -226,20 +233,20 @@ func (s *App) Delete(w http.ResponseWriter, r *http.Request) {
return
}
if !strings.Contains(raw, "["+req.Name+"]") {
service.Error(w, http.StatusUnprocessableEntity, "规则不存在")
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("rule not found"))
return
}
rule := str.Cut(raw, "# "+req.Name+"-START", "# "+req.Name+"-END")
raw = strings.Replace(raw, "\n# "+req.Name+"-START"+rule+"# "+req.Name+"-END", "", -1)
raw = strings.ReplaceAll(raw, "\n# "+req.Name+"-START"+rule+"# "+req.Name+"-END", "")
raw = strings.TrimSpace(raw)
if err := io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "写入Fail2ban规则失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if _, err := shell.Execf("fail2ban-client reload"); err != nil {
service.Error(w, http.StatusInternalServerError, "重载配置失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -256,33 +263,30 @@ func (s *App) BanList(w http.ResponseWriter, r *http.Request) {
currentlyBan, err := shell.Execf(`fail2ban-client status %s | grep "Currently banned" | awk '{print $4}'`, req.Name)
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取封禁列表失败")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get current banned list"))
return
}
totalBan, err := shell.Execf(`fail2ban-client status %s | grep "Total banned" | awk '{print $4}'`, req.Name)
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取封禁列表失败")
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, "获取封禁列表失败")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get banned ip list"))
return
}
bannedIpList := strings.Split(bannedIp, " ")
var list []map[string]string
for _, ip := range bannedIpList {
if len(ip) > 0 {
list = append(list, map[string]string{
"name": req.Name,
"ip": ip,
})
list := lo.FilterMap(bannedIpList, func(ip string, _ int) (map[string]string, bool) {
if len(ip) == 0 {
return nil, false
}
}
if list == nil {
list = []map[string]string{}
}
return map[string]string{
"name": req.Name,
"ip": ip,
}, true
})
service.Success(w, chix.M{
"currently_ban": currentlyBan,
@@ -300,7 +304,7 @@ func (s *App) Unban(w http.ResponseWriter, r *http.Request) {
}
if _, err = shell.Execf("fail2ban-client set %s unbanip %s", req.Name, req.IP); err != nil {
service.Error(w, http.StatusInternalServerError, "解封失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -325,17 +329,17 @@ func (s *App) SetWhiteList(w http.ResponseWriter, r *http.Request) {
if reg.MatchString(raw) {
raw = reg.ReplaceAllString(raw, "ignoreip = "+req.IP+"\n")
} else {
service.Error(w, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to parse the ignoreip of fail2ban"))
return
}
if err = io.Write("/etc/fail2ban/jail.local", raw, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "写入Fail2ban规则失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if _, err = shell.Execf("fail2ban-client reload"); err != nil {
service.Error(w, http.StatusInternalServerError, "重载配置失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
@@ -353,7 +357,7 @@ func (s *App) GetWhiteList(w http.ResponseWriter, r *http.Request) {
ignoreIp := reg.FindStringSubmatch(raw)[1]
service.Success(w, ignoreIp)
} else {
service.Error(w, http.StatusInternalServerError, "解析Fail2ban规则失败,Fail2ban可能已损坏")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to parse the ignoreip of fail2ban"))
return
}
}
+7 -7
View File
@@ -2,12 +2,12 @@ package fail2ban
type Add struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
MaxRetry int `json:"maxretry" validate:"required"`
FindTime int `json:"findtime" validate:"required"`
BanTime int `json:"bantime" validate:"required"`
WebsiteName string `json:"website_name"`
WebsiteMode string `json:"website_mode"`
Type string `json:"type" validate:"required && in:service,website"`
MaxRetry int `json:"maxretry" validate:"required && min:1"`
FindTime int `json:"findtime" validate:"required && min:1"`
BanTime int `json:"bantime" validate:"required && min:1"`
WebsiteName string `json:"website_name" validate:"required_if:Type,website"`
WebsiteMode string `json:"website_mode" validate:"required_if:Type,website && in:cc,path"`
WebsitePath string `json:"website_path"`
}
@@ -21,7 +21,7 @@ type BanList struct {
type Unban struct {
Name string `json:"name" validate:"required"`
IP string `json:"ip" validate:"required"`
IP string `json:"ip" validate:"required && ip"`
}
type SetWhiteList struct {
+94 -6
View File
@@ -6,21 +6,30 @@ import (
"github.com/go-chi/chi/v5"
"github.com/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/service"
"github.com/tnb-labs/panel/pkg/io"
"github.com/tnb-labs/panel/pkg/systemctl"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct{}
func NewApp() *App {
return &App{}
func NewApp() (*App, error) {
return &App{}, nil
}
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) Status() string {
frps, _ := systemctl.Status("frps")
frpc, _ := systemctl.Status("frpc")
return types.AggregateAppStatus(frps, frpc)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
@@ -58,3 +67,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"`
}
+12 -6
View File
@@ -6,16 +6,17 @@ import (
"github.com/go-chi/chi/v5"
"github.com/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/service"
"github.com/tnb-labs/panel/pkg/io"
"github.com/tnb-labs/panel/pkg/systemctl"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct{}
func NewApp() *App {
return &App{}
func NewApp() (*App, error) {
return &App{}, nil
}
func (s *App) Route(r chi.Router) {
@@ -23,6 +24,11 @@ func (s *App) Route(r chi.Router) {
r.Post("/config", s.UpdateConfig)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("gitea")
return types.AggregateAppStatus(ok)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, _ := io.Read(fmt.Sprintf("%s/server/gitea/app.ini", app.Root))
service.Success(w, config)
+524
View File
@@ -0,0 +1,524 @@
package grafana
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/samber/lo"
"go.yaml.in/yaml/v4"
"resty.dev/v3"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
t *gotext.Locale
}
func NewApp(t *gotext.Locale) (*App, error) {
return &App{t: t}, nil
}
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)
// 数据源管理
r.Get("/datasources", s.DataSourceList)
r.Post("/datasources", s.CreateDataSource)
r.Post("/datasources/{name}", s.UpdateDataSource)
r.Delete("/datasources/{name}", s.DeleteDataSource)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("grafana")
return types.AggregateAppStatus(ok)
}
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
status, err := systemctl.Status("grafana")
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get grafana status: %v", err))
return
}
if !status {
service.Success(w, []types.NV{})
return
}
// 从 defaults.ini 获取端口
config, _ := io.Read(s.configPath())
port := s.getINIValue(config, "server", "http_port")
if port == "" {
port = "3000"
}
client := resty.New().SetTimeout(10 * time.Second)
defer func(client *resty.Client) { _ = client.Close() }(client)
resp, err := client.R().Get(fmt.Sprintf("http://127.0.0.1:%s/api/health", port))
if err != nil || !resp.IsStatusSuccess() {
service.Success(w, []types.NV{})
return
}
var health struct {
Commit string `json:"commit"`
Database string `json:"database"`
Version string `json:"version"`
}
if err = json.Unmarshal(resp.Bytes(), &health); err != nil {
service.Success(w, []types.NV{})
return
}
data := []types.NV{
{Name: s.t.Get("Version"), Value: health.Version},
{Name: "Commit", Value: health.Commit},
{Name: s.t.Get("Database"), Value: health.Database},
}
service.Success(w, data)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
config, _ := io.Read(s.configPath())
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(s.configPath(), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("grafana"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetConfigTune 获取 Grafana 配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
config, _ := io.Read(s.configPath())
get := func(section, key string) string {
return s.getINIValue(config, section, key)
}
tune := ConfigTune{
// [server]
HTTPPort: get("server", "http_port"),
Domain: get("server", "domain"),
RootURL: get("server", "root_url"),
Protocol: get("server", "protocol"),
// [database]
DBType: get("database", "type"),
DBHost: get("database", "host"),
DBName: get("database", "name"),
DBUser: get("database", "user"),
DBPassword: get("database", "password"),
// [security]
AdminUser: get("security", "admin_user"),
AdminPassword: get("security", "admin_password"),
// [users]
AllowSignUp: get("users", "allow_sign_up"),
AutoAssignOrgRole: get("users", "auto_assign_org_role"),
// [smtp]
SMTPEnabled: get("smtp", "enabled"),
SMTPHost: get("smtp", "host"),
SMTPUser: get("smtp", "user"),
SMTPPassword: get("smtp", "password"),
SMTPFromAddress: get("smtp", "from_address"),
// [log]
LogMode: get("log", "mode"),
LogLevel: get("log", "level"),
}
service.Success(w, tune)
}
// UpdateConfigTune 更新 Grafana 配置调整参数
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, _ := io.Read(s.configPath())
// [server]
config = s.setINIValue(config, "server", "http_port", req.HTTPPort)
config = s.setINIValue(config, "server", "domain", req.Domain)
config = s.setINIValue(config, "server", "root_url", req.RootURL)
config = s.setINIValue(config, "server", "protocol", req.Protocol)
// [database]
config = s.setINIValue(config, "database", "type", req.DBType)
config = s.setINIValue(config, "database", "host", req.DBHost)
config = s.setINIValue(config, "database", "name", req.DBName)
config = s.setINIValue(config, "database", "user", req.DBUser)
config = s.setINIValue(config, "database", "password", req.DBPassword)
// [security]
config = s.setINIValue(config, "security", "admin_user", req.AdminUser)
config = s.setINIValue(config, "security", "admin_password", req.AdminPassword)
// [users]
config = s.setINIValue(config, "users", "allow_sign_up", req.AllowSignUp)
config = s.setINIValue(config, "users", "auto_assign_org_role", req.AutoAssignOrgRole)
// [smtp]
config = s.setINIValue(config, "smtp", "enabled", req.SMTPEnabled)
config = s.setINIValue(config, "smtp", "host", req.SMTPHost)
config = s.setINIValue(config, "smtp", "user", req.SMTPUser)
config = s.setINIValue(config, "smtp", "password", req.SMTPPassword)
config = s.setINIValue(config, "smtp", "from_address", req.SMTPFromAddress)
// [log]
config = s.setINIValue(config, "log", "mode", req.LogMode)
config = s.setINIValue(config, "log", "level", req.LogLevel)
if err = io.Write(s.configPath(), config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("grafana"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// DataSourceList 获取数据源列表
func (s *App) DataSourceList(w http.ResponseWriter, r *http.Request) {
service.Success(w, s.getDatasourceList(s.readDatasources()))
}
// CreateDataSource 创建数据源
func (s *App) CreateDataSource(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[DataSource](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
cfg := s.readDatasources()
if lo.ContainsBy(s.getDatasourceList(cfg), func(item any) bool {
ds, ok := item.(map[string]any)
return ok && ds["name"] == req.Name
}) {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("datasource %s already exists", req.Name))
return
}
if req.IsDefault {
s.clearDefault(cfg)
}
list := s.getDatasourceList(cfg)
list = append(list, s.buildDatasourceMap(req))
cfg["datasources"] = list
if err = s.writeDatasources(cfg); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// UpdateDataSource 更新数据源
func (s *App) UpdateDataSource(w http.ResponseWriter, r *http.Request) {
oldName := chi.URLParam(r, "name")
req, err := service.Bind[DataSource](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
cfg := s.readDatasources()
list := s.getDatasourceList(cfg)
_, idx, found := lo.FindIndexOf(list, func(item any) bool {
ds, ok := item.(map[string]any)
return ok && ds["name"] == oldName
})
if !found {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("datasource %s not found", oldName))
return
}
ds, _ := list[idx].(map[string]any)
newDs := s.buildDatasourceMap(req)
// 密码为空时保留原有 secureJsonData
if req.Password == "" {
if sec, exists := ds["secureJsonData"]; exists {
newDs["secureJsonData"] = sec
}
}
list[idx] = newDs
if req.IsDefault {
s.clearDefault(cfg)
lo.ForEach(list, func(item any, _ int) {
if ds, ok := item.(map[string]any); ok && ds["name"] == req.Name {
ds["isDefault"] = true
}
})
}
if oldName != req.Name {
s.addDeleteEntry(cfg, oldName)
}
cfg["datasources"] = list
if err = s.writeDatasources(cfg); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// DeleteDataSource 删除数据源
func (s *App) DeleteDataSource(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
cfg := s.readDatasources()
list := s.getDatasourceList(cfg)
newList := lo.Filter(list, func(item any, _ int) bool {
ds, ok := item.(map[string]any)
return !ok || ds["name"] != name
})
found := len(newList) < len(list)
if !found {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("datasource %s not found", name))
return
}
cfg["datasources"] = newList
s.addDeleteEntry(cfg, name)
if err := s.writeDatasources(cfg); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// configPath 返回 Grafana 主配置文件路径
func (s *App) configPath() string {
return fmt.Sprintf("%s/server/grafana/conf/defaults.ini", app.Root)
}
// getINIValue 从 INI 配置中获取指定 section 下的 key 值
func (s *App) getINIValue(content string, section string, key string) string {
currentSection := ""
for line := range strings.SplitSeq(content, "\n") {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "#") || strings.HasPrefix(trimmed, ";") {
continue
}
if strings.HasPrefix(trimmed, "[") && strings.HasSuffix(trimmed, "]") {
currentSection = strings.TrimSpace(trimmed[1 : len(trimmed)-1])
continue
}
if currentSection != section {
continue
}
parts := strings.SplitN(trimmed, "=", 2)
if len(parts) == 2 && strings.TrimSpace(parts[0]) == key {
return strings.TrimSpace(parts[1])
}
}
return ""
}
// setINIValue 在 INI 配置中设置指定 section 下的 key 值
func (s *App) setINIValue(content string, section string, key string, value string) string {
lines := strings.Split(content, "\n")
result := make([]string, 0, len(lines))
currentSection := ""
found := false
lastSectionLine := -1 // 目标 section 的最后一行索引
for i, line := range lines {
trimmed := strings.TrimSpace(line)
// 检测 section 头
if strings.HasPrefix(trimmed, "[") && strings.HasSuffix(trimmed, "]") {
// 如果离开目标 section 且未找到 key,在 section 末尾插入
if currentSection == section && !found && lastSectionLine >= 0 {
found = true
if value != "" {
// 在 section 末尾插入新行
insertIdx := lastSectionLine + 1
newLine := key + " = " + value
result = append(result[:insertIdx+1], append([]string{newLine}, result[insertIdx+1:]...)...)
}
}
currentSection = strings.TrimSpace(trimmed[1 : len(trimmed)-1])
}
if currentSection == section {
lastSectionLine = len(result)
}
// 在目标 section 内匹配 key
if currentSection == section && !found {
checkLine := trimmed
commented := false
if strings.HasPrefix(checkLine, ";") {
checkLine = strings.TrimSpace(checkLine[1:])
commented = true
} else if strings.HasPrefix(checkLine, "#") {
checkLine = strings.TrimSpace(checkLine[1:])
commented = true
}
parts := strings.SplitN(checkLine, "=", 2)
if len(parts) == 2 && strings.TrimSpace(parts[0]) == key {
found = true
if value == "" {
// 值为空时注释掉
if !commented {
result = append(result, ";"+line)
} else {
result = append(result, line)
}
} else {
result = append(result, key+" = "+value)
}
_ = i
continue
}
}
result = append(result, line)
}
// 如果在最后一个 section 中未找到 key
if currentSection == section && !found {
found = true
if value != "" {
result = append(result, key+" = "+value)
}
}
// section 不存在,在文件末尾追加
if !found && value != "" {
result = append(result, "")
result = append(result, "["+section+"]")
result = append(result, key+" = "+value)
}
return strings.Join(result, "\n")
}
// datasourcePath 返回 provisioning 数据源文件路径
func (s *App) datasourcePath() string {
return fmt.Sprintf("%s/server/grafana/conf/provisioning/datasources/panel.yml", app.Root)
}
// readDatasources 读取 provisioning 文件
func (s *App) readDatasources() map[string]any {
raw, _ := io.Read(s.datasourcePath())
if raw == "" {
return map[string]any{
"apiVersion": 1,
"datasources": []any{},
}
}
var cfg map[string]any
if err := yaml.Unmarshal([]byte(raw), &cfg); err != nil {
return map[string]any{
"apiVersion": 1,
"datasources": []any{},
}
}
return cfg
}
// writeDatasources 写入 provisioning 文件并重启 Grafana
func (s *App) writeDatasources(cfg map[string]any) error {
data, err := yaml.Marshal(cfg)
if err != nil {
return err
}
if err = io.Write(s.datasourcePath(), string(data), 0644); err != nil {
return err
}
return systemctl.Restart("grafana")
}
// getDatasourceList 从 cfg 中提取 datasources 切片
func (s *App) getDatasourceList(cfg map[string]any) []any {
ds, ok := cfg["datasources"].([]any)
if !ok {
return []any{}
}
return ds
}
// buildDatasourceMap 从请求构建单条 datasource map
func (s *App) buildDatasourceMap(req *DataSource) map[string]any {
ds := map[string]any{
"name": req.Name,
"type": req.Type,
"access": req.Access,
"url": req.URL,
"isDefault": req.IsDefault,
"editable": true,
}
if req.Access == "" {
ds["access"] = "proxy"
}
switch req.Type {
case "mysql", "postgres", "influxdb", "mssql":
if req.Database != "" {
ds["database"] = req.Database
}
if req.User != "" {
ds["user"] = req.User
}
if req.Password != "" {
ds["secureJsonData"] = map[string]any{"password": req.Password}
}
}
return ds
}
// clearDefault 清除所有数据源的默认标记
func (s *App) clearDefault(cfg map[string]any) {
lo.ForEach(s.getDatasourceList(cfg), func(item any, _ int) {
if ds, ok := item.(map[string]any); ok {
ds["isDefault"] = false
}
})
}
// addDeleteEntry 向 deleteDatasources 添加条目
func (s *App) addDeleteEntry(cfg map[string]any, name string) {
delList, _ := cfg["deleteDatasources"].([]any)
delList = append(delList, map[string]any{"name": name, "orgId": 1})
cfg["deleteDatasources"] = delList
}
+48
View File
@@ -0,0 +1,48 @@
package grafana
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
// ConfigTune Grafana 配置调整
type ConfigTune struct {
// [server]
HTTPPort string `form:"http_port" json:"http_port"`
Domain string `form:"domain" json:"domain"`
RootURL string `form:"root_url" json:"root_url"`
Protocol string `form:"protocol" json:"protocol"`
// [database]
DBType string `form:"db_type" json:"db_type"`
DBHost string `form:"db_host" json:"db_host"`
DBName string `form:"db_name" json:"db_name"`
DBUser string `form:"db_user" json:"db_user"`
DBPassword string `form:"db_password" json:"db_password"`
// [security]
AdminUser string `form:"admin_user" json:"admin_user"`
AdminPassword string `form:"admin_password" json:"admin_password"`
// [users]
AllowSignUp string `form:"allow_sign_up" json:"allow_sign_up"`
AutoAssignOrgRole string `form:"auto_assign_org_role" json:"auto_assign_org_role"`
// [smtp]
SMTPEnabled string `form:"smtp_enabled" json:"smtp_enabled"`
SMTPHost string `form:"smtp_host" json:"smtp_host"`
SMTPUser string `form:"smtp_user" json:"smtp_user"`
SMTPPassword string `form:"smtp_password" json:"smtp_password"`
SMTPFromAddress string `form:"smtp_from_address" json:"smtp_from_address"`
// [log]
LogMode string `form:"log_mode" json:"log_mode"`
LogLevel string `form:"log_level" json:"log_level"`
}
// DataSource 数据源
type DataSource struct {
Name string `form:"name" json:"name" validate:"required"`
Type string `form:"type" json:"type" validate:"required && in:prometheus,mysql,postgres,influxdb,loki,elasticsearch,tempo,jaeger,zipkin,graphite,alertmanager,opentsdb,mssql,testdata,grafana-pyroscope-datasource,grafana-opensearch-datasource"`
URL string `form:"url" json:"url" validate:"required"`
Access string `form:"access" json:"access" validate:"in:proxy,direct"`
IsDefault bool `form:"is_default" json:"is_default"`
// 数据库类型专用
Database string `form:"database" json:"database"`
User string `form:"user" json:"user"`
Password string `form:"password" json:"password"`
}
+253
View File
@@ -0,0 +1,253 @@
package kafka
import (
"fmt"
"net/http"
"regexp"
"strings"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
t *gotext.Locale
}
func NewApp(t *gotext.Locale) (*App, error) {
return &App{t: t}, nil
}
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) Status() string {
ok, _ := systemctl.Status("kafka")
return types.AggregateAppStatus(ok)
}
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
status, err := systemctl.Status("kafka")
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get kafka status: %v", err))
return
}
if !status {
service.Success(w, []types.NV{})
return
}
config, _ := io.Read(s.configPath())
data := []types.NV{
{Name: s.t.Get("Node ID"), Value: s.getPropertiesValue(config, "node.id")},
{Name: s.t.Get("Listeners"), Value: s.getPropertiesValue(config, "listeners")},
{Name: s.t.Get("Log Dirs"), Value: s.getPropertiesValue(config, "log.dirs")},
{Name: s.t.Get("Num Partitions"), Value: s.getPropertiesValue(config, "num.partitions")},
{Name: s.t.Get("Log Retention Hours"), Value: s.getPropertiesValue(config, "log.retention.hours")},
{Name: s.t.Get("Log Segment Bytes"), Value: s.getPropertiesValue(config, "log.segment.bytes")},
}
service.Success(w, data)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
conf, _ := io.Read(s.configPath())
service.Success(w, conf)
}
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(s.configPath(), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("kafka"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetConfigTune 获取 Kafka 配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
config, _ := io.Read(s.configPath())
heapRaw, _ := io.Read(s.heapEnvPath())
heapInit, heapMax := s.parseHeapEnv(heapRaw)
tune := ConfigTune{
NodeID: s.getPropertiesValue(config, "node.id"),
Listeners: s.getPropertiesValue(config, "listeners"),
LogDirs: s.getPropertiesValue(config, "log.dirs"),
NumPartitions: s.getPropertiesValue(config, "num.partitions"),
RetentionHours: s.getPropertiesValue(config, "log.retention.hours"),
LogSegmentBytes: s.getPropertiesValue(config, "log.segment.bytes"),
HeapInitSize: heapInit,
HeapMaxSize: heapMax,
}
service.Success(w, tune)
}
// UpdateConfigTune 更新 Kafka 配置调整参数
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, _ := io.Read(s.configPath())
config = s.setPropertiesValue(config, "node.id", req.NodeID)
config = s.setPropertiesValue(config, "listeners", req.Listeners)
config = s.setPropertiesValue(config, "log.dirs", req.LogDirs)
config = s.setPropertiesValue(config, "num.partitions", req.NumPartitions)
config = s.setPropertiesValue(config, "log.retention.hours", req.RetentionHours)
config = s.setPropertiesValue(config, "log.segment.bytes", req.LogSegmentBytes)
if err = io.Write(s.configPath(), config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
// 更新 JVM 堆内存
if req.HeapInitSize != "" || req.HeapMaxSize != "" {
heapRaw, _ := io.Read(s.heapEnvPath())
heapRaw = s.setHeapEnv(heapRaw, req.HeapInitSize, req.HeapMaxSize)
if err = io.Write(s.heapEnvPath(), heapRaw, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
}
if err = systemctl.Restart("kafka"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// configPath 返回配置文件路径
func (s *App) configPath() string {
return fmt.Sprintf("%s/server/kafka/config/server.properties", app.Root)
}
// heapEnvPath 返回 JVM 堆内存配置文件路径
func (s *App) heapEnvPath() string {
return fmt.Sprintf("%s/server/kafka/config/heap.env", app.Root)
}
// getPropertiesValue 从 properties 内容中获取指定键的值
func (s *App) getPropertiesValue(content string, key string) string {
for line := range strings.SplitSeq(content, "\n") {
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
continue
}
k, v, ok := strings.Cut(trimmed, "=")
if ok && strings.TrimSpace(k) == key {
return strings.TrimSpace(v)
}
}
return ""
}
// setPropertiesValue 在 properties 内容中设置指定键的值
func (s *App) setPropertiesValue(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:])
}
k, _, ok := strings.Cut(checkLine, "=")
if ok && strings.TrimSpace(k) == 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")
}
// parseHeapEnv 从 heap.env 中提取堆内存配置
func (s *App) parseHeapEnv(content string) (initSize, maxSize string) {
re := regexp.MustCompile(`KAFKA_HEAP_OPTS=(.+)`)
m := re.FindStringSubmatch(content)
if len(m) != 2 {
return
}
opts := m[1]
if mi := regexp.MustCompile(`-Xms(\S+)`).FindStringSubmatch(opts); len(mi) == 2 {
initSize = mi[1]
}
if mx := regexp.MustCompile(`-Xmx(\S+)`).FindStringSubmatch(opts); len(mx) == 2 {
maxSize = mx[1]
}
return
}
// setHeapEnv 设置 heap.env 中的堆内存配置
func (s *App) setHeapEnv(content string, initSize, maxSize string) string {
// 读取已有值作为默认
oldInit, oldMax := s.parseHeapEnv(content)
if initSize == "" {
initSize = oldInit
}
if maxSize == "" {
maxSize = oldMax
}
if initSize == "" {
initSize = "1g"
}
if maxSize == "" {
maxSize = "1g"
}
return fmt.Sprintf("KAFKA_HEAP_OPTS=-Xms%s -Xmx%s\n", initSize, maxSize)
}
+20
View File
@@ -0,0 +1,20 @@
package kafka
// UpdateConfig Kafka 配置更新
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
// ConfigTune Kafka 配置调整
type ConfigTune struct {
// Broker
NodeID string `form:"node_id" json:"node_id"` // node.id
Listeners string `form:"listeners" json:"listeners"` // listeners
LogDirs string `form:"log_dirs" json:"log_dirs"` // log.dirs
NumPartitions string `form:"num_partitions" json:"num_partitions"` // num.partitions
RetentionHours string `form:"retention_hours" json:"retention_hours"` // log.retention.hours
LogSegmentBytes string `form:"log_segment_bytes" json:"log_segment_bytes"` // log.segment.bytes
// JVM
HeapInitSize string `form:"heap_init_size" json:"heap_init_size"` // -Xms
HeapMaxSize string `form:"heap_max_size" json:"heap_max_size"` // -Xmx
}
+25
View File
@@ -0,0 +1,25 @@
package mariadb
import (
"github.com/go-chi/chi/v5"
"github.com/acepanel/panel/v3/internal/apps/mysql"
)
type App struct {
mysql *mysql.App
}
func NewApp(mysqlApp *mysql.App) (*App, error) {
return &App{
mysql: mysqlApp,
}, nil
}
func (s *App) Route(r chi.Router) {
s.mysql.Route(r)
}
func (s *App) Status() string {
return s.mysql.Status()
}
+134 -11
View File
@@ -5,31 +5,44 @@ import (
"net"
"net/http"
"regexp"
"strings"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/tnb-labs/panel/internal/service"
"github.com/tnb-labs/panel/pkg/io"
"github.com/tnb-labs/panel/pkg/systemctl"
"github.com/tnb-labs/panel/pkg/types"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct{}
type App struct {
t *gotext.Locale
}
func NewApp() *App {
return &App{}
func NewApp(t *gotext.Locale) (*App, error) {
return &App{
t: t,
}, nil
}
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) Status() string {
ok, _ := systemctl.Status("memcached")
return types.AggregateAppStatus(ok)
}
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
status, err := systemctl.Status("memcached")
if err != nil {
service.Error(w, http.StatusInternalServerError, "failed to get Memcached status: %v", err)
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get Memcached status: %v", err))
return
}
if !status {
@@ -42,11 +55,13 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
service.Success(w, []types.NV{})
return
}
defer conn.Close()
defer func(conn net.Conn) {
_ = conn.Close()
}(conn)
_, err = conn.Write([]byte("stats\nquit\n"))
if err != nil {
service.Error(w, http.StatusInternalServerError, "failed to write to Memcached: %v", err)
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to write to Memcached: %v", err))
return
}
@@ -67,7 +82,7 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
}
if err = scanner.Err(); err != nil {
service.Error(w, http.StatusInternalServerError, "failed to read from Memcached: %v", err)
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to read from Memcached: %v", err))
return
}
@@ -103,3 +118,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.SplitSeq(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" validate:"number && min:1 && max:65535"`
UDPPort string `form:"udp_port" json:"udp_port" validate:"number && min:1 && max:65535"`
ListenAddress string `form:"listen_address" json:"listen_address"`
Memory string `form:"memory" json:"memory" validate:"number && min:1"`
MaxConnections string `form:"max_connections" json:"max_connections" validate:"number && min:1"`
Threads string `form:"threads" json:"threads" validate:"number && min:1"`
}
+53
View File
@@ -0,0 +1,53 @@
package minio
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct{}
func NewApp() (*App, error) {
return &App{}, nil
}
func (s *App) Route(r chi.Router) {
r.Get("/env", s.GetEnv)
r.Post("/env", s.UpdateEnv)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("minio")
return types.AggregateAppStatus(ok)
}
func (s *App) GetEnv(w http.ResponseWriter, r *http.Request) {
env, _ := io.Read("/etc/default/minio")
service.Success(w, env)
}
func (s *App) UpdateEnv(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateEnv](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if err = io.Write("/etc/default/minio", req.Env, 0600); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("minio"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
+5
View File
@@ -0,0 +1,5 @@
package minio
type UpdateEnv struct {
Env string `form:"env" json:"env" validate:"required"`
}
+279
View File
@@ -0,0 +1,279 @@
package mongodb
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/spf13/cast"
"go.yaml.in/yaml/v4"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/biz"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/shell"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
t *gotext.Locale
settingRepo biz.SettingRepo
databaseServerRepo biz.DatabaseServerRepo
}
func NewApp(t *gotext.Locale, databaseServerRepo biz.DatabaseServerRepo, settingRepo biz.SettingRepo) (*App, error) {
return &App{
t: t,
settingRepo: settingRepo,
databaseServerRepo: databaseServerRepo,
}, nil
}
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)
r.Get("/admin_password", s.GetAdminPassword)
r.Post("/admin_password", s.SetAdminPassword)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("mongod")
return types.AggregateAppStatus(ok)
}
// Load 获取 MongoDB 运行状态
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
status, _ := systemctl.Status("mongod")
if !status {
service.Success(w, []types.NV{})
return
}
password, _ := s.settingRepo.Get(biz.SettingKeyMongoDBAdminPassword)
raw, err := shell.Execf(`mongosh --quiet --eval "JSON.stringify(db.serverStatus())" -u admin -p '%s' --authenticationDatabase admin 2>/dev/null`, password)
if err != nil {
service.Success(w, []types.NV{})
return
}
var status2 struct {
Uptime int `json:"uptime"`
Connections struct {
Current int `json:"current"`
TotalCreated int `json:"totalCreated"`
} `json:"connections"`
OpCounters struct {
Query int `json:"query"`
Insert int `json:"insert"`
Update int `json:"update"`
Delete int `json:"delete"`
} `json:"opcounters"`
Mem struct {
Resident int `json:"resident"`
} `json:"mem"`
StorageEngine struct {
Name string `json:"name"`
} `json:"storageEngine"`
Version string `json:"version"`
}
if err = json.Unmarshal([]byte(raw), &status2); err != nil {
service.Success(w, []types.NV{})
return
}
data := []types.NV{
{Name: s.t.Get("Version"), Value: status2.Version},
{Name: s.t.Get("Uptime (seconds)"), Value: cast.ToString(status2.Uptime)},
{Name: s.t.Get("Current Connections"), Value: cast.ToString(status2.Connections.Current)},
{Name: s.t.Get("Total Connections Created"), Value: cast.ToString(status2.Connections.TotalCreated)},
{Name: s.t.Get("Query Operations"), Value: cast.ToString(status2.OpCounters.Query)},
{Name: s.t.Get("Insert Operations"), Value: cast.ToString(status2.OpCounters.Insert)},
{Name: s.t.Get("Update Operations"), Value: cast.ToString(status2.OpCounters.Update)},
{Name: s.t.Get("Delete Operations"), Value: cast.ToString(status2.OpCounters.Delete)},
{Name: s.t.Get("Resident Memory (MB)"), Value: cast.ToString(status2.Mem.Resident)},
{Name: s.t.Get("Storage Engine"), Value: status2.StorageEngine.Name},
}
service.Success(w, data)
}
// GetConfig 获取配置
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
conf, _ := io.Read(s.configPath())
service.Success(w, conf)
}
// UpdateConfig 更新配置
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(s.configPath(), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("mongod"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetConfigTune 获取配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
raw, _ := io.Read(s.configPath())
var cfg map[string]any
_ = yaml.Unmarshal([]byte(raw), &cfg)
if cfg == nil {
cfg = make(map[string]any)
}
tune := ConfigTune{
DbPath: s.getYAMLValue(cfg, "storage.dbPath"),
CacheSizeGB: s.getYAMLValue(cfg, "storage.wiredTiger.engineConfig.cacheSizeGB"),
Port: s.getYAMLValue(cfg, "net.port"),
BindIp: s.getYAMLValue(cfg, "net.bindIp"),
SystemLogPath: s.getYAMLValue(cfg, "systemLog.path"),
Authorization: s.getYAMLValue(cfg, "security.authorization"),
}
service.Success(w, tune)
}
// UpdateConfigTune 更新配置调整参数
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
}
raw, _ := io.Read(s.configPath())
var cfg map[string]any
if err = yaml.Unmarshal([]byte(raw), &cfg); err != nil {
cfg = make(map[string]any)
}
s.setNestedYAMLValue(cfg, "storage.dbPath", req.DbPath)
s.setNestedYAMLValue(cfg, "storage.wiredTiger.engineConfig.cacheSizeGB", req.CacheSizeGB)
s.setNestedYAMLValue(cfg, "net.port", req.Port)
s.setNestedYAMLValue(cfg, "net.bindIp", req.BindIp)
s.setNestedYAMLValue(cfg, "systemLog.path", req.SystemLogPath)
s.setNestedYAMLValue(cfg, "security.authorization", req.Authorization)
data, err := yaml.Marshal(cfg)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = io.Write(s.configPath(), string(data), 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("mongod"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetAdminPassword 获取 admin 密码
func (s *App) GetAdminPassword(w http.ResponseWriter, r *http.Request) {
password, err := s.settingRepo.Get(biz.SettingKeyMongoDBAdminPassword)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get MongoDB admin password: %v", err))
return
}
service.Success(w, password)
}
// SetAdminPassword 设置 admin 密码
func (s *App) SetAdminPassword(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[SetAdminPassword](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
oldPassword, _ := s.settingRepo.Get(biz.SettingKeyMongoDBAdminPassword)
// 尝试用旧密码连接修改
_, err = shell.Execf(`mongosh --quiet -u admin -p '%s' --authenticationDatabase admin --eval "db.changeUserPassword('admin', '%s')"`, oldPassword, req.Password)
if err != nil {
// 回退:停止服务,无认证模式修改
_ = systemctl.Stop("mongod")
_, _ = shell.Execf(`su -s /bin/bash mongod -c "mongod --config %s --noauth --fork --logpath /tmp/mongod_reset.log"`, s.configPath())
_, resetErr := shell.Execf(`mongosh --quiet --eval "db.getSiblingDB('admin').changeUserPassword('admin', '%s')"`, req.Password)
_, _ = shell.Execf(`su -s /bin/bash mongod -c "mongod --config %s --shutdown" 2>/dev/null; pkill -f 'mongod --config.*--noauth' 2>/dev/null`, s.configPath())
_ = systemctl.Start("mongod")
if resetErr != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set MongoDB admin password: %v", resetErr))
return
}
}
if err = s.settingRepo.Set(biz.SettingKeyMongoDBAdminPassword, req.Password); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to save MongoDB admin password: %v", err))
return
}
_ = s.databaseServerRepo.UpdatePassword("local_mongodb", req.Password)
service.Success(w, nil)
}
// configPath 返回配置文件路径
func (s *App) configPath() string {
return fmt.Sprintf("%s/server/mongodb/mongod.conf", app.Root)
}
// getYAMLValue 获取嵌套 YAML 值,支持 dot notation
func (s *App) getYAMLValue(cfg map[string]any, key string) string {
parts := strings.SplitN(key, ".", 2)
val, ok := cfg[parts[0]]
if !ok {
return ""
}
if len(parts) == 1 {
return cast.ToString(val)
}
nested, ok := val.(map[string]any)
if !ok {
return ""
}
return s.getYAMLValue(nested, parts[1])
}
// setNestedYAMLValue 设置嵌套 YAML 值,逐层创建 map
func (s *App) setNestedYAMLValue(cfg map[string]any, key string, value string) {
if value == "" {
return
}
parts := strings.SplitN(key, ".", 2)
if len(parts) == 1 {
cfg[parts[0]] = value
return
}
nested, ok := cfg[parts[0]].(map[string]any)
if !ok {
nested = make(map[string]any)
cfg[parts[0]] = nested
}
s.setNestedYAMLValue(nested, parts[1], value)
}
+25
View File
@@ -0,0 +1,25 @@
package mongodb
// UpdateConfig 更新配置
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
// SetAdminPassword 设置 admin 密码
type SetAdminPassword struct {
Password string `form:"password" json:"password" validate:"required && password"`
}
// ConfigTune MongoDB 配置调整
type ConfigTune struct {
// 存储
DbPath string `form:"db_path" json:"db_path" validate:"unix_path"`
CacheSizeGB string `form:"cache_size_gb" json:"cache_size_gb"`
// 网络
Port string `form:"port" json:"port" validate:"number && min:1 && max:65535"`
BindIp string `form:"bind_ip" json:"bind_ip"`
// 日志
SystemLogPath string `form:"system_log_path" json:"system_log_path" validate:"unix_path"`
// 安全
Authorization string `form:"authorization" json:"authorization" validate:"in:enabled,disabled"`
}
+277 -99
View File
@@ -5,47 +5,58 @@ import (
"net/http"
"os"
"regexp"
"strings"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/spf13/cast"
"github.com/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/biz"
"github.com/tnb-labs/panel/internal/service"
"github.com/tnb-labs/panel/pkg/db"
"github.com/tnb-labs/panel/pkg/io"
"github.com/tnb-labs/panel/pkg/shell"
"github.com/tnb-labs/panel/pkg/systemctl"
"github.com/tnb-labs/panel/pkg/tools"
"github.com/tnb-labs/panel/pkg/types"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/biz"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/db"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/shell"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/tools"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
settingRepo biz.SettingRepo
t *gotext.Locale
settingRepo biz.SettingRepo
databaseServerRepo biz.DatabaseServerRepo
}
func NewApp(setting biz.SettingRepo) *App {
func NewApp(t *gotext.Locale, databaseServerRepo biz.DatabaseServerRepo, settingRepo biz.SettingRepo) (*App, error) {
return &App{
settingRepo: setting,
}
t: t,
settingRepo: settingRepo,
databaseServerRepo: databaseServerRepo,
}, nil
}
func (s *App) Route(r chi.Router) {
r.Get("/load", s.Load)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
r.Post("/clearErrorLog", s.ClearErrorLog)
r.Get("/slowLog", s.SlowLog)
r.Post("/clearSlowLog", s.ClearSlowLog)
r.Get("/rootPassword", s.GetRootPassword)
r.Post("/rootPassword", s.SetRootPassword)
r.Get("/slow_log", s.SlowLog)
r.Get("/root_password", s.GetRootPassword)
r.Post("/root_password", s.SetRootPassword)
r.Get("/config_tune", s.GetConfigTune)
r.Post("/config_tune", s.UpdateConfigTune)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("mysqld")
return types.AggregateAppStatus(ok)
}
// GetConfig 获取配置
func (s *App) GetConfig(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, "获取配置失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -61,12 +72,12 @@ func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
}
if err = io.Write(app.Root+"/server/mysql/conf/my.cnf", req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "写入配置失败:%v", err)
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("mysqld"); err != nil {
service.Error(w, http.StatusInternalServerError, "重启失败:%v", err)
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to restart MySQL: %v", err))
return
}
@@ -75,57 +86,55 @@ 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, "获取root密码失败")
return
}
if len(rootPassword) == 0 {
service.Error(w, http.StatusUnprocessableEntity, "root密码为空")
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, "设置环境变量失败")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set MYSQL_PWD env: %v", err))
return
}
raw, err := shell.Execf(`mysqladmin -u root extended-status`)
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取负载失败")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get MySQL status: %v", err))
return
}
if err = os.Unsetenv("MYSQL_PWD"); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to unset MYSQL_PWD env: %v", err))
return
}
_ = os.Unsetenv("MYSQL_PWD")
var load []map[string]string
expressions := []struct {
regex string
name string
}{
{`Uptime\s+\|\s+(\d+)\s+\|`, "运行时间"},
{`Queries\s+\|\s+(\d+)\s+\|`, "总查询次数"},
{`Connections\s+\|\s+(\d+)\s+\|`, "总连接次数"},
{`Com_commit\s+\|\s+(\d+)\s+\|`, "每秒事务"},
{`Com_rollback\s+\|\s+(\d+)\s+\|`, "每秒回滚"},
{`Bytes_sent\s+\|\s+(\d+)\s+\|`, "发送"},
{`Bytes_received\s+\|\s+(\d+)\s+\|`, "接收"},
{`Threads_connected\s+\|\s+(\d+)\s+\|`, "活动连接数"},
{`Max_used_connections\s+\|\s+(\d+)\s+\|`, "峰值连接数"},
{`Key_read_requests\s+\|\s+(\d+)\s+\|`, "索引命中率"},
{`Innodb_buffer_pool_reads\s+\|\s+(\d+)\s+\|`, "Innodb索引命中率"},
{`Created_tmp_disk_tables\s+\|\s+(\d+)\s+\|`, "创建临时表到硬盘"},
{`Open_tables\s+\|\s+(\d+)\s+\|`, "已打开的表"},
{`Select_full_join\s+\|\s+(\d+)\s+\|`, "没有使用索引的量"},
{`Select_full_range_join\s+\|\s+(\d+)\s+\|`, "没有索引的JOIN量"},
{`Select_range_check\s+\|\s+(\d+)\s+\|`, "没有索引的子查询量"},
{`Sort_merge_passes\s+\|\s+(\d+)\s+\|`, "排序后的合并次数"},
{`Table_locks_waited\s+\|\s+(\d+)\s+\|`, "锁表次数"},
{`Uptime\s+\|\s+(\d+)\s+\|`, s.t.Get("Uptime")},
{`Queries\s+\|\s+(\d+)\s+\|`, s.t.Get("Total Queries")},
{`Connections\s+\|\s+(\d+)\s+\|`, s.t.Get("Total Connections")},
{`Com_commit\s+\|\s+(\d+)\s+\|`, s.t.Get("Transactions per Second")},
{`Com_rollback\s+\|\s+(\d+)\s+\|`, s.t.Get("Rollbacks per Second")},
{`Bytes_sent\s+\|\s+(\d+)\s+\|`, s.t.Get("Bytes Sent")},
{`Bytes_received\s+\|\s+(\d+)\s+\|`, s.t.Get("Bytes Received")},
{`Threads_connected\s+\|\s+(\d+)\s+\|`, s.t.Get("Active Connections")},
{`Max_used_connections\s+\|\s+(\d+)\s+\|`, s.t.Get("Peak Connections")},
{`Key_read_requests\s+\|\s+(\d+)\s+\|`, s.t.Get("Index Hit Rate")},
{`Innodb_buffer_pool_reads\s+\|\s+(\d+)\s+\|`, s.t.Get("Innodb Index Hit Rate")},
{`Created_tmp_disk_tables\s+\|\s+(\d+)\s+\|`, s.t.Get("Temporary Tables Created on Disk")},
{`Open_tables\s+\|\s+(\d+)\s+\|`, s.t.Get("Open Tables")},
{`Select_full_join\s+\|\s+(\d+)\s+\|`, s.t.Get("Full Joins without Index")},
{`Select_full_range_join\s+\|\s+(\d+)\s+\|`, s.t.Get("Full Range Joins without Index")},
{`Select_range_check\s+\|\s+(\d+)\s+\|`, s.t.Get("Subqueries without Index")},
{`Sort_merge_passes\s+\|\s+(\d+)\s+\|`, s.t.Get("Sort Merge Passes")},
{`Table_locks_waited\s+\|\s+(\d+)\s+\|`, s.t.Get("Table Locks Waited")},
}
for _, expression := range expressions {
@@ -133,7 +142,7 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
matches := re.FindStringSubmatch(raw)
if len(matches) > 1 {
d := map[string]string{"name": expression.name, "value": matches[1]}
if expression.name == "发送" || expression.name == "接收" {
if expression.name == s.t.Get("Bytes Sent") || expression.name == s.t.Get("Bytes Received") {
d["value"] = tools.FormatBytes(cast.ToFloat64(matches[1]))
}
@@ -150,17 +159,42 @@ 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)
service.Success(w, load)
}
// ClearErrorLog 清空错误日志
func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
if err := systemctl.LogsClear("mysqld"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
// 查询缓存命中率
// 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, nil)
service.Success(w, load)
}
// SlowLog 获取慢查询日志
@@ -168,25 +202,11 @@ func (s *App) SlowLog(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/server/mysql/mysql-slow.log", app.Root))
}
// ClearSlowLog 清空慢查询日志
func (s *App) ClearSlowLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("echo '' > %s/server/mysql/mysql-slow.log", app.Root); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetRootPassword 获取root密码
func (s *App) GetRootPassword(w http.ResponseWriter, r *http.Request) {
rootPassword, err := s.settingRepo.Get(biz.SettingKeyMySQLRootPassword)
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取root密码失败")
return
}
if len(rootPassword) == 0 {
service.Error(w, http.StatusUnprocessableEntity, "root密码为空")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to load MySQL root password: %v", err))
return
}
@@ -202,7 +222,7 @@ func (s *App) SetRootPassword(w http.ResponseWriter, r *http.Request) {
}
oldRootPassword, _ := s.settingRepo.Get(biz.SettingKeyMySQLRootPassword)
mysql, err := db.NewMySQL("root", oldRootPassword, s.getSock(), "unix")
mysql, err := db.NewMySQL(r.Context(), "root", oldRootPassword, s.getSock(), "unix")
if err != nil {
// 尝试安全模式直接改密
if err = db.MySQLResetRootPassword(req.Password); err != nil {
@@ -216,8 +236,109 @@ func (s *App) SetRootPassword(w http.ResponseWriter, r *http.Request) {
return
}
}
if err = s.settingRepo.Set(biz.SettingKeyMySQLRootPassword, req.Password); err != nil {
service.Error(w, http.StatusInternalServerError, "设置保存失败:%v", err)
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
_ = s.databaseServerRepo.UpdatePassword("local_mysql", req.Password)
service.Success(w, nil)
}
// 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
}
@@ -225,25 +346,82 @@ func (s *App) SetRootPassword(w http.ResponseWriter, r *http.Request) {
}
func (s *App) getSock() string {
if io.Exists("/tmp/mysql.sock") {
return "/tmp/mysql.sock"
if sock := db.MySQLSocket(app.Root+"/server/mysql/config/my.cnf", "/etc/my.cnf"); sock != "" {
return sock
}
if io.Exists(app.Root + "/server/mysql/config/my.cnf") {
config, _ := io.Read(app.Root + "/server/mysql/config/my.cnf")
re := regexp.MustCompile(`socket\s*=\s*(['"]?)([^'"]+)`)
matches := re.FindStringSubmatch(config)
if len(matches) > 2 {
return matches[2]
}
}
if io.Exists("/etc/my.cnf") {
config, _ := io.Read("/etc/my.cnf")
re := regexp.MustCompile(`socket\s*=\s*(['"]?)([^'"]+)`)
matches := re.FindStringSubmatch(config)
if len(matches) > 2 {
return matches[2]
}
}
return "/tmp/mysql.sock"
}
// getINIValue 从 INI 格式内容中获取指定键的值
func (s *App) getINIValue(content string, key string) string {
lines := strings.SplitSeq(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")
}
+35 -1
View File
@@ -5,5 +5,39 @@ type UpdateConfig struct {
}
type SetRootPassword struct {
Password string `form:"password" json:"password" validate:"required|password"`
Password string `form:"password" json:"password" validate:"required && password"`
}
// ConfigTune MySQL 配置调整
type ConfigTune struct {
// 常规设置
Port string `form:"port" json:"port" validate:"number && min:1 && max:65535"`
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" validate:"in:0,1,2"`
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"`
}
+231 -31
View File
@@ -4,41 +4,62 @@ import (
"fmt"
"net/http"
"regexp"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-resty/resty/v2"
"github.com/leonelquinteros/gotext"
"github.com/spf13/cast"
"resty.dev/v3"
"github.com/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/service"
"github.com/tnb-labs/panel/pkg/io"
"github.com/tnb-labs/panel/pkg/shell"
"github.com/tnb-labs/panel/pkg/systemctl"
"github.com/tnb-labs/panel/pkg/tools"
"github.com/tnb-labs/panel/pkg/types"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/shell"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/tools"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
// Dependent services
t *gotext.Locale
}
func NewApp() *App {
return &App{}
func NewApp(t *gotext.Locale) (*App, error) {
return &App{
t: t,
}, nil
}
func (s *App) Route(r chi.Router) {
r.Get("/load", s.Load)
r.Get("/config", s.GetConfig)
r.Post("/config", s.SaveConfig)
r.Get("/errorLog", s.ErrorLog)
r.Post("/clearErrorLog", s.ClearErrorLog)
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) Status() string {
ok, _ := systemctl.Status("nginx")
return types.AggregateAppStatus(ok)
}
func (s *App) GetConfig(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, "获取配置失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -52,14 +73,14 @@ 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 {
service.Error(w, http.StatusInternalServerError, "保存配置失败")
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
}
if err = systemctl.Reload("nginx"); err != nil {
_, err = shell.Execf("nginx -t")
service.Error(w, http.StatusInternalServerError, "重载服务失败:%v", err)
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
return
}
@@ -67,11 +88,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("echo '' > %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
}
@@ -81,9 +102,11 @@ func (s *App) ClearErrorLog(w http.ResponseWriter, r *http.Request) {
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
client := resty.New().SetTimeout(10 * time.Second)
defer func(client *resty.Client) { _ = client.Close() }(client)
resp, err := client.R().Get("http://127.0.0.1/nginx_status")
if err != nil || !resp.IsSuccess() {
if err != nil || !resp.IsStatusSuccess() {
service.Success(w, []types.NV{})
return
}
raw := resp.String()
@@ -91,29 +114,29 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
workers, err := shell.Execf("ps aux | grep nginx | grep 'worker process' | wc -l")
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取负载失败")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get nginx workers: %v", err))
return
}
data = append(data, types.NV{
Name: "工作进程",
Name: s.t.Get("Workers"),
Value: workers,
})
out, err := shell.Execf("ps aux | grep nginx | grep 'worker process' | awk '{memsum+=$6};END {print memsum}'")
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取负载失败")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get nginx workers: %v", err))
return
}
mem := tools.FormatBytes(cast.ToFloat64(out))
data = append(data, types.NV{
Name: "内存占用",
Name: s.t.Get("Memory"),
Value: mem,
})
match := regexp.MustCompile(`Active connections:\s+(\d+)`).FindStringSubmatch(raw)
if len(match) == 2 {
data = append(data, types.NV{
Name: "活跃连接数",
Name: s.t.Get("Active connections"),
Value: match[1],
})
}
@@ -121,15 +144,15 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
match = regexp.MustCompile(`server accepts handled requests\s+(\d+)\s+(\d+)\s+(\d+)`).FindStringSubmatch(raw)
if len(match) == 4 {
data = append(data, types.NV{
Name: "总连接次数",
Name: s.t.Get("Total connections"),
Value: match[1],
})
data = append(data, types.NV{
Name: "总握手次数",
Name: s.t.Get("Total handshakes"),
Value: match[2],
})
data = append(data, types.NV{
Name: "总请求次数",
Name: s.t.Get("Total requests"),
Value: match[3],
})
}
@@ -137,18 +160,195 @@ func (s *App) Load(w http.ResponseWriter, r *http.Request) {
match = regexp.MustCompile(`Reading:\s+(\d+)\s+Writing:\s+(\d+)\s+Waiting:\s+(\d+)`).FindStringSubmatch(raw)
if len(match) == 4 {
data = append(data, types.NV{
Name: "请求数",
Name: s.t.Get("Reading"),
Value: match[1],
})
data = append(data, types.NV{
Name: "响应数",
Name: s.t.Get("Writing"),
Value: match[2],
})
data = append(data, types.NV{
Name: "驻留进程",
Name: s.t.Get("Waiting"),
Value: match[3],
})
}
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.SplitSeq(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:"required_if:SSL,true"` // SSL 证书路径
SSLCertificateKey string `form:"ssl_certificate_key" json:"ssl_certificate_key" validate:"required_if: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 解析超时时间
}
+640
View File
@@ -0,0 +1,640 @@
package nginx
import (
"fmt"
"net/http"
"os"
"path/filepath"
"sort"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/samber/lo"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/systemctl"
webserverNginx "github.com/acepanel/panel/v3/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
_, _ = fmt.Fprintf(&sb, "upstream %s {\n", upstream.Name)
// 负载均衡算法
if upstream.Algo != "" {
_, _ = fmt.Fprintf(&sb, " %s;\n", upstream.Algo)
}
// resolver 配置
if len(upstream.Resolver) > 0 {
_, _ = fmt.Fprintf(&sb, " resolver %s;\n", strings.Join(upstream.Resolver, " "))
if upstream.ResolverTimeout > 0 {
_, _ = fmt.Fprintf(&sb, " resolver_timeout %s;\n", formatNginxDuration(upstream.ResolverTimeout))
}
}
// 服务器列表
addrs := lo.Keys(upstream.Servers)
sort.Strings(addrs)
for _, addr := range addrs {
options := upstream.Servers[addr]
if options != "" {
_, _ = fmt.Fprintf(&sb, " server %s %s;\n", addr, options)
} else {
_, _ = fmt.Fprintf(&sb, " 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")
}
+28
View File
@@ -0,0 +1,28 @@
package openresty
import (
"github.com/go-chi/chi/v5"
"github.com/acepanel/panel/v3/internal/apps/nginx"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
nginx *nginx.App
}
func NewApp(nginxApp *nginx.App) (*App, error) {
return &App{
nginx: nginxApp,
}, nil
}
func (s *App) Route(r chi.Router) {
s.nginx.Route(r)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("nginx")
return types.AggregateAppStatus(ok)
}
+286
View File
@@ -0,0 +1,286 @@
package opensearch
import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/spf13/cast"
"go.yaml.in/yaml/v4"
"resty.dev/v3"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
t *gotext.Locale
}
func NewApp(t *gotext.Locale) (*App, error) {
return &App{t: t}, nil
}
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) Status() string {
ok, _ := systemctl.Status("opensearch")
return types.AggregateAppStatus(ok)
}
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
status, err := systemctl.Status("opensearch")
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get opensearch status: %v", err))
return
}
if !status {
service.Success(w, []types.NV{})
return
}
port := s.getPort()
client := resty.New().SetTimeout(10 * time.Second)
defer func(client *resty.Client) { _ = client.Close() }(client)
resp, err := client.R().Get(fmt.Sprintf("http://127.0.0.1:%s/_cluster/health", port))
if err != nil || !resp.IsStatusSuccess() {
service.Success(w, []types.NV{})
return
}
var health struct {
ClusterName string `json:"cluster_name"`
Status string `json:"status"`
NumberOfNodes int `json:"number_of_nodes"`
NumberOfDataNodes int `json:"number_of_data_nodes"`
ActiveShards int `json:"active_shards"`
ActivePrimaryShards int `json:"active_primary_shards"`
RelocatingShards int `json:"relocating_shards"`
UnassignedShards int `json:"unassigned_shards"`
}
if err = json.Unmarshal(resp.Bytes(), &health); err != nil {
service.Success(w, []types.NV{})
return
}
data := []types.NV{
{Name: s.t.Get("Cluster Name"), Value: health.ClusterName},
{Name: s.t.Get("Cluster Status"), Value: health.Status},
{Name: s.t.Get("Number of Nodes"), Value: cast.ToString(health.NumberOfNodes)},
{Name: s.t.Get("Number of Data Nodes"), Value: cast.ToString(health.NumberOfDataNodes)},
{Name: s.t.Get("Active Shards"), Value: cast.ToString(health.ActiveShards)},
{Name: s.t.Get("Active Primary Shards"), Value: cast.ToString(health.ActivePrimaryShards)},
{Name: s.t.Get("Relocating Shards"), Value: cast.ToString(health.RelocatingShards)},
{Name: s.t.Get("Unassigned Shards"), Value: cast.ToString(health.UnassignedShards)},
}
service.Success(w, data)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
conf, _ := io.Read(s.configPath())
service.Success(w, conf)
}
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(s.configPath(), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("opensearch"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetConfigTune 获取 OpenSearch 配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
raw, _ := io.Read(s.configPath())
var cfg map[string]any
_ = yaml.Unmarshal([]byte(raw), &cfg)
if cfg == nil {
cfg = make(map[string]any)
}
jvmRaw, _ := io.Read(s.jvmOptionsPath())
heapInit, heapMax := s.parseJVMHeap(jvmRaw)
tune := ConfigTune{
ClusterName: s.getYAMLValue(cfg, "cluster.name"),
NodeName: s.getYAMLValue(cfg, "node.name"),
NetworkHost: s.getYAMLValue(cfg, "network.host"),
HTTPPort: s.getYAMLValue(cfg, "http.port"),
DiscoveryType: s.getYAMLValue(cfg, "discovery.type"),
PathData: s.getYAMLValue(cfg, "path.data"),
PathLogs: s.getYAMLValue(cfg, "path.logs"),
HeapInitSize: heapInit,
HeapMaxSize: heapMax,
}
service.Success(w, tune)
}
// UpdateConfigTune 更新 OpenSearch 配置调整参数
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
}
raw, _ := io.Read(s.configPath())
var cfg map[string]any
if err = yaml.Unmarshal([]byte(raw), &cfg); err != nil {
cfg = make(map[string]any)
}
s.setYAMLValue(cfg, "cluster.name", req.ClusterName)
s.setYAMLValue(cfg, "node.name", req.NodeName)
s.setYAMLValue(cfg, "network.host", req.NetworkHost)
s.setYAMLValue(cfg, "http.port", req.HTTPPort)
s.setYAMLValue(cfg, "discovery.type", req.DiscoveryType)
s.setYAMLValue(cfg, "path.data", req.PathData)
s.setYAMLValue(cfg, "path.logs", req.PathLogs)
data, err := yaml.Marshal(cfg)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = io.Write(s.configPath(), string(data), 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if req.HeapInitSize != "" || req.HeapMaxSize != "" {
jvmRaw, _ := io.Read(s.jvmOptionsPath())
jvmRaw = s.setJVMHeap(jvmRaw, req.HeapInitSize, req.HeapMaxSize)
if err = io.Write(s.jvmOptionsPath(), jvmRaw, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
}
if err = systemctl.Restart("opensearch"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
func (s *App) configPath() string {
return fmt.Sprintf("%s/server/opensearch/config/opensearch.yml", app.Root)
}
func (s *App) jvmOptionsPath() string {
return fmt.Sprintf("%s/server/opensearch/config/jvm.options", app.Root)
}
func (s *App) getPort() string {
raw, _ := io.Read(s.configPath())
var cfg map[string]any
_ = yaml.Unmarshal([]byte(raw), &cfg)
if cfg != nil {
if v := s.getYAMLValue(cfg, "http.port"); v != "" {
return v
}
}
return "9200"
}
// getYAMLValue 获取 YAML 值,优先匹配平铺键(如 "path.data"),回退到嵌套键(如 path -> data
func (s *App) getYAMLValue(cfg map[string]any, key string) string {
// 优先匹配平铺键(安装脚本用 sed 生成的格式)
if val, ok := cfg[key]; ok {
return cast.ToString(val)
}
// 回退到嵌套键
parts := strings.SplitN(key, ".", 2)
val, ok := cfg[parts[0]]
if !ok {
return ""
}
if len(parts) == 1 {
return cast.ToString(val)
}
nested, ok := val.(map[string]any)
if !ok {
return ""
}
return s.getYAMLValue(nested, parts[1])
}
// setYAMLValue 设置 YAML 值
func (s *App) setYAMLValue(cfg map[string]any, key string, value string) {
if value == "" {
return
}
// 使用平铺键,同时清理可能存在的嵌套键
cfg[key] = value
parts := strings.SplitN(key, ".", 2)
if len(parts) == 2 {
if nested, ok := cfg[parts[0]].(map[string]any); ok {
delete(nested, parts[1])
if len(nested) == 0 {
delete(cfg, parts[0])
}
}
}
}
func (s *App) parseJVMHeap(content string) (initSize, maxSize string) {
reInit := regexp.MustCompile(`(?m)^-Xms(\S+)`)
reMax := regexp.MustCompile(`(?m)^-Xmx(\S+)`)
if m := reInit.FindStringSubmatch(content); len(m) == 2 {
initSize = m[1]
}
if m := reMax.FindStringSubmatch(content); len(m) == 2 {
maxSize = m[1]
}
return
}
func (s *App) setJVMHeap(content string, initSize, maxSize string) string {
if initSize != "" {
re := regexp.MustCompile(`(?m)^-Xms\S+`)
if re.MatchString(content) {
content = re.ReplaceAllString(content, "-Xms"+initSize)
} else {
content += "\n-Xms" + initSize
}
}
if maxSize != "" {
re := regexp.MustCompile(`(?m)^-Xmx\S+`)
if re.MatchString(content) {
content = re.ReplaceAllString(content, "-Xmx"+maxSize)
} else {
content += "\n-Xmx" + maxSize
}
}
return content
}
+21
View File
@@ -0,0 +1,21 @@
package opensearch
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
// ConfigTune OpenSearch 配置调整
type ConfigTune struct {
// 集群
ClusterName string `form:"cluster_name" json:"cluster_name"`
NodeName string `form:"node_name" json:"node_name"`
NetworkHost string `form:"network_host" json:"network_host"`
HTTPPort string `form:"http_port" json:"http_port"`
DiscoveryType string `form:"discovery_type" json:"discovery_type"`
// 路径
PathData string `form:"path_data" json:"path_data"`
PathLogs string `form:"path_logs" json:"path_logs"`
// JVM
HeapInitSize string `form:"heap_init_size" json:"heap_init_size"`
HeapMaxSize string `form:"heap_max_size" json:"heap_max_size"`
}
+25
View File
@@ -0,0 +1,25 @@
package percona
import (
"github.com/go-chi/chi/v5"
"github.com/acepanel/panel/v3/internal/apps/mysql"
)
type App struct {
mysql *mysql.App
}
func NewApp(mysqlApp *mysql.App) (*App, error) {
return &App{
mysql: mysqlApp,
}, nil
}
func (s *App) Route(r chi.Router) {
s.mysql.Route(r)
}
func (s *App) Status() string {
return s.mysql.Status()
}
+548
View File
@@ -0,0 +1,548 @@
package pgadmin
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
stdio "io"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/libtnb/chix/v2"
"github.com/spf13/cast"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/biz"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/config"
"github.com/acepanel/panel/v3/pkg/firewall"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/shell"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
t *gotext.Locale
conf *config.Config
databaseServerRepo biz.DatabaseServerRepo
}
func NewApp(conf *config.Config, t *gotext.Locale, databaseServerRepo biz.DatabaseServerRepo) (*App, error) {
return &App{
t: t,
conf: conf,
databaseServerRepo: databaseServerRepo,
}, nil
}
// pgadminLanguage 面板语言映射为 pgAdmin 语言代码
func (s *App) pgadminLanguage() string {
switch s.conf.App.Locale {
case "":
return ""
case "zh_CN":
return "zh_Hans_CN"
case "zh_TW":
return "zh_Hant_TW"
default:
return s.conf.App.Locale
}
}
// clientIP 获取请求来源 IP,面板位于反代之后时优先取配置的 IP 头
func (s *App) clientIP(r *http.Request) string {
ip := r.RemoteAddr
if header := s.conf.HTTP.IPHeader; header != "" && r.Header.Get(header) != "" {
ip = strings.TrimSpace(strings.Split(r.Header.Get(header), ",")[0])
}
if host, _, err := net.SplitHostPort(ip); err == nil {
ip = host
}
return ip
}
func (s *App) Route(r chi.Router) {
r.Get("/info", s.Info)
r.Post("/port", s.UpdatePort)
r.Post("/login", s.Login)
r.Post("/update_username", s.UpdateUsername)
r.Post("/reset_password", s.ResetPassword)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("pgadmin")
return types.AggregateAppStatus(ok)
}
func (s *App) path() string {
return fmt.Sprintf("%s/server/pgadmin", app.Root)
}
// port 从 systemd 环境文件中解析监听端口
func (s *App) port() (uint, error) {
conf, err := io.Read(fmt.Sprintf("%s/pgadmin.conf", s.path()))
if err != nil {
return 0, err
}
match := regexp.MustCompile(`PGADMIN_LISTEN=.+:(\d+)`).FindStringSubmatch(conf)
if len(match) < 2 {
return 0, errors.New(s.t.Get("pgAdmin port not found"))
}
return cast.ToUint(match[1]), nil
}
// credential 读取安装时生成的初始凭据(邮箱与密码)
func (s *App) credential() (string, string) {
raw, err := io.Read(fmt.Sprintf("%s/credential", s.path()))
if err != nil {
return "", ""
}
lines := strings.Split(strings.TrimSpace(raw), "\n")
if len(lines) < 2 {
return strings.TrimSpace(lines[0]), ""
}
return strings.TrimSpace(lines[0]), strings.TrimSpace(lines[1])
}
func (s *App) Info(w http.ResponseWriter, r *http.Request) {
port, err := s.port()
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
email, password := s.credential()
service.Success(w, chix.M{
"port": port,
"email": email,
"password": password,
})
}
func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdatePort](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
conf := fmt.Sprintf("%s/pgadmin.conf", s.path())
content, err := io.Read(conf)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
content = regexp.MustCompile(`PGADMIN_LISTEN=(.+):\d+`).ReplaceAllString(content, "PGADMIN_LISTEN=${1}:"+cast.ToString(req.Port))
if err = io.Write(conf, content, 0600); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
fw := firewall.NewFirewall()
err = fw.Port(firewall.FireInfo{
Type: firewall.TypeNormal,
PortStart: req.Port,
PortEnd: req.Port,
Strategy: firewall.StrategyAccept,
Direction: firewall.DirectionIn,
}, firewall.OperationAdd)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("pgadmin"); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to restart pgAdmin: %v", err))
return
}
service.Success(w, nil)
}
// serversFile pgAdmin dump-servers/load-servers 的 JSON 结构
type serversFile struct {
Servers map[string]serverEntry `json:"Servers"`
}
type serverEntry struct {
Name string `json:"Name"`
Group string `json:"Group"`
Host string `json:"Host"`
Port int `json:"Port"`
MaintenanceDB string `json:"MaintenanceDB"`
Username string `json:"Username"`
SSLMode string `json:"SSLMode,omitempty"`
PassFile string `json:"PassFile,omitempty"`
}
// escapePgpass 转义 pgpass 字段中的反斜杠与冒号
func escapePgpass(s string) string {
return strings.NewReplacer(`\`, `\\`, `:`, `\:`).Replace(s)
}
// existingServers 只读 pgAdmin 配置库查询 Servers 组内已注册的服务器
// CLI 每次调用都要冷启动整个 pgAdmin 应用,直读库快数个量级
func (s *App) existingServers(email string) (map[string]struct{}, error) {
db, err := sql.Open("sqlite", fmt.Sprintf("file:%s/data/pgadmin.db?mode=ro", s.path()))
if err != nil {
return nil, err
}
defer func() { _ = db.Close() }()
rows, err := db.Query(`SELECT s.host, s.port, s.username FROM server s
JOIN servergroup g ON s.servergroup_id = g.id
JOIN "user" u ON s.user_id = u.id
WHERE u.email = ? AND g.name = 'Servers'`, email)
if err != nil {
return nil, err
}
defer func() { _ = rows.Close() }()
existing := make(map[string]struct{})
for rows.Next() {
var host, username sql.NullString
var port sql.NullInt64
if err = rows.Scan(&host, &port, &username); err != nil {
return nil, err
}
existing[fmt.Sprintf("%s:%d:%s", host.String, port.Int64, username.String)] = struct{}{}
}
return existing, rows.Err()
}
// dumpExistingServers 通过 CLI 导出查询已注册服务器,直读配置库失败时的回退路径
func (s *App) dumpExistingServers(email string) map[string]struct{} {
dump := filepath.Join(os.TempDir(), "pgadmin-servers.json")
defer func() { _ = io.Remove(dump) }()
_, _ = shell.Execf("%s/cli dump-servers '%s' --user '%s'", s.path(), dump, email)
existing := make(map[string]struct{})
if raw, err := io.Read(dump); err == nil {
var dumped serversFile
if err = json.Unmarshal([]byte(raw), &dumped); err == nil {
for _, item := range dumped.Servers {
// 只认默认 Servers 组内的条目,历史版本同步进其他分组的会在此组补齐
if item.Group != "Servers" {
continue
}
existing[fmt.Sprintf("%s:%d:%s", item.Host, item.Port, item.Username)] = struct{}{}
}
}
}
return existing
}
// syncServers 将面板中全部 PostgreSQL 服务器合并注册到 pgAdmin,凭据写入 pgpass 实现免密
// 仅追加 pgAdmin 中缺失的服务器,不影响用户在 pgAdmin 中手动添加的内容
func (s *App) syncServers(ctx context.Context, email string) error {
servers, _, err := s.databaseServerRepo.List(ctx, 1, 10000, string(biz.DatabaseTypePostgresql))
if err != nil {
return err
}
if len(servers) == 0 {
return nil
}
// pgAdmin server 模式下 PassFile 以用户 storage 目录为根,目录名为邮箱 @ 转 _
storageDir := fmt.Sprintf("%s/data/storage/%s", s.path(), strings.ReplaceAll(email, "@", "_"))
pgpass := filepath.Join(storageDir, "pgpass")
// 重写 pgpass 中面板服务器的凭据行,保留其他行
prefixes := make([]string, 0, len(servers))
entries := make([]string, 0, len(servers))
for _, server := range servers {
prefix := fmt.Sprintf("%s:%d:*:%s:", escapePgpass(server.Host), server.Port, escapePgpass(server.Username))
prefixes = append(prefixes, prefix)
entries = append(entries, prefix+escapePgpass(server.Password))
}
var lines []string
if raw, err := io.Read(pgpass); err == nil {
for line := range strings.SplitSeq(strings.TrimSpace(raw), "\n") {
if line == "" {
continue
}
panelOwned := false
for _, prefix := range prefixes {
if strings.HasPrefix(line, prefix) {
panelOwned = true
break
}
}
if !panelOwned {
lines = append(lines, line)
}
}
}
lines = append(lines, entries...)
if err = os.MkdirAll(storageDir, 0700); err != nil {
return err
}
if err = io.Write(pgpass, strings.Join(lines, "\n")+"\n", 0600); err != nil {
return err
}
// 查询 pgAdmin 已有服务器用于查缺,直读配置库,异常时回退 CLI 导出
existing, err := s.existingServers(email)
if err != nil {
existing = s.dumpExistingServers(email)
}
// 一次性合并导入缺失的服务器
missing := make(map[string]serverEntry)
for i, server := range servers {
if _, ok := existing[fmt.Sprintf("%s:%d:%s", server.Host, server.Port, server.Username)]; ok {
continue
}
missing[cast.ToString(i+1)] = serverEntry{
Name: server.Name,
Group: "Servers",
Host: server.Host,
Port: int(server.Port),
MaintenanceDB: "postgres",
Username: server.Username,
SSLMode: "prefer",
PassFile: "/pgpass",
}
}
if len(missing) > 0 {
load := filepath.Join(os.TempDir(), "pgadmin-servers-add.json")
defer func() { _ = io.Remove(load) }()
payload, err := json.Marshal(serversFile{Servers: missing})
if err != nil {
return err
}
if err = io.Write(load, string(payload), 0600); err != nil {
return err
}
if out, err := shell.Execf("%s/cli load-servers '%s' --user '%s'", s.path(), load, email); err != nil {
return errors.Join(err, errors.New(out))
}
// CLI 以 root 运行,修正数据目录属主避免服务写入失败
if _, err = shell.Execf("chown -R www:www %s/data", s.path()); err != nil {
return err
}
return nil
}
// 常态路径仅写入了 pgpass,精确修正属主即可
if err = io.Chown(storageDir, "www", "www"); err != nil {
return err
}
return io.Chown(pgpass, "www", "www")
}
// Login 同步面板全部 PostgreSQL 服务器后代理登录 pgAdmin 并将会话 Cookie 转发给浏览器
// 面板与 pgAdmin 同主机不同端口,Cookie 按主机共享,浏览器凭转发的 Cookie 即为已登录态
func (s *App) Login(w http.ResponseWriter, r *http.Request) {
port, err := s.port()
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
email, password := s.credential()
if email == "" || password == "" {
service.Error(w, http.StatusInternalServerError, s.t.Get("pgAdmin credential file not found"))
return
}
if err = s.syncServers(r.Context(), email); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to sync servers to pgAdmin: %v", err))
return
}
client := &http.Client{
Timeout: 30 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
// 透传浏览器 IP 与 UA,pgAdmin 增强 Cookie 保护将会话绑定到 sha256(IP|UA),
// 伪装成浏览器身份登录后浏览器直连即可通过校验,无需关闭该保护
clientIP := s.clientIP(r)
clientUA := r.UserAgent()
// 获取登录页以取得会话 Cookie 与 CSRF token
loginURL := fmt.Sprintf("http://127.0.0.1:%d/login", port)
pageReq, err := http.NewRequestWithContext(r.Context(), http.MethodGet, loginURL, nil)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
pageReq.Header.Set("User-Agent", clientUA)
pageReq.Header.Set("X-Forwarded-For", clientIP)
pageResp, err := client.Do(pageReq)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to request pgAdmin: %v", err))
return
}
defer func() { _ = pageResp.Body.Close() }()
page, err := stdio.ReadAll(stdio.LimitReader(pageResp.Body, 4<<20))
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to request pgAdmin: %v", err))
return
}
// 登录页为 React 渲染,CSRF token 在内嵌 JSON 中,保留 input 形态兼容旧版本
csrf := regexp.MustCompile(`"csrfToken":\s*"([^"]+)"`).FindStringSubmatch(string(page))
if len(csrf) < 2 {
csrf = regexp.MustCompile(`name="csrf_token"[^>]*value="([^"]+)"`).FindStringSubmatch(string(page))
}
if len(csrf) < 2 {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to parse pgAdmin login page"))
return
}
form := url.Values{}
form.Set("csrf_token", csrf[1])
form.Set("email", email)
form.Set("password", password)
// 语言跟随面板设置,pgAdmin 会将 language 字段固化进会话
if lang := s.pgadminLanguage(); lang != "" {
form.Set("language", lang)
}
loginReq, err := http.NewRequestWithContext(r.Context(), http.MethodPost, fmt.Sprintf("http://127.0.0.1:%d/authenticate/login", port), strings.NewReader(form.Encode()))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginReq.Header.Set("User-Agent", clientUA)
loginReq.Header.Set("X-Forwarded-For", clientIP)
for _, cookie := range pageResp.Cookies() {
loginReq.AddCookie(cookie)
}
loginResp, err := client.Do(loginReq)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to request pgAdmin: %v", err))
return
}
defer func() { _ = loginResp.Body.Close() }()
_, _ = stdio.Copy(stdio.Discard, stdio.LimitReader(loginResp.Body, 4<<20))
// 登录成功时 pgAdmin 返回 302 且跳转目标不是登录页
location := loginResp.Header.Get("Location")
if loginResp.StatusCode != http.StatusFound || strings.Contains(location, "/login") {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to login pgAdmin, please check the credential of pgAdmin"))
return
}
// 改写 SameSite 以兼容面板 https 跳转 http 的场景
for _, cookie := range append(pageResp.Cookies(), loginResp.Cookies()...) {
cookie.SameSite = http.SameSiteLaxMode
cookie.Secure = false
http.SetCookie(w, cookie)
}
service.Success(w, chix.M{
"port": port,
})
}
// ResetPassword 通过 CLI 重置管理员密码并同步凭据文件
// UpdateUsername 修改管理员账号,迁移服务器配置与用户存储目录
func (s *App) UpdateUsername(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[UpdateUsername](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
oldEmail, password := s.credential()
if oldEmail == "" || password == "" {
service.Error(w, http.StatusInternalServerError, s.t.Get("pgAdmin credential file not found"))
return
}
if req.Username == oldEmail {
service.Success(w, nil)
return
}
// 以当前密码创建新管理员账号
if out, err := shell.Execf("%s/cli add-user '%s' '%s' --admin", s.path(), req.Username, password); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to create new account: %v", errors.Join(err, errors.New(out))))
return
}
// 迁移服务器连接配置到新账号
dump := filepath.Join(os.TempDir(), "pgadmin-servers-migrate.json")
defer func() { _ = io.Remove(dump) }()
_, _ = shell.Execf("%s/cli dump-servers '%s' --user '%s'", s.path(), dump, oldEmail)
if io.Exists(dump) {
_, _ = shell.Execf("%s/cli load-servers '%s' --user '%s'", s.path(), dump, req.Username)
}
// 删除旧账号
if out, err := shell.Execf("%s/cli delete-user '%s' --yes", s.path(), oldEmail); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to delete old account: %v", errors.Join(err, errors.New(out))))
return
}
// 迁移用户存储目录(pgpass 等),目录名为邮箱 @ 转 _
oldDir := fmt.Sprintf("%s/data/storage/%s", s.path(), strings.ReplaceAll(oldEmail, "@", "_"))
newDir := fmt.Sprintf("%s/data/storage/%s", s.path(), strings.ReplaceAll(req.Username, "@", "_"))
if io.Exists(oldDir) && !io.Exists(newDir) {
_ = os.Rename(oldDir, newDir)
}
// CLI 以 root 运行,修正数据目录属主避免服务写入失败
if _, err = shell.Execf("chown -R www:www %s/data", s.path()); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
// 更新凭据文件,下次登录按新账号重新同步
if err = io.Write(fmt.Sprintf("%s/credential", s.path()), req.Username+"\n"+password+"\n", 0600); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
func (s *App) ResetPassword(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ResetPassword](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
email, _ := s.credential()
if email == "" {
service.Error(w, http.StatusInternalServerError, s.t.Get("pgAdmin credential file not found"))
return
}
// cli 为安装脚本生成的稳定入口,屏蔽上游命令名随大版本变化
if out, err := shell.Execf("%s/cli update-user '%s' --password '%s'", s.path(), email, req.Password); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reset password: %v", errors.Join(err, errors.New(out))))
return
}
// CLI 以 root 运行,修正数据目录属主避免服务写入失败
if _, err = shell.Execf("chown -R www:www %s/data", s.path()); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = io.Write(fmt.Sprintf("%s/credential", s.path()), email+"\n"+req.Password+"\n", 0600); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
+13
View File
@@ -0,0 +1,13 @@
package pgadmin
type UpdatePort struct {
Port uint `form:"port" json:"port" validate:"required && number && min:1 && max:65535"`
}
type ResetPassword struct {
Password string `form:"password" json:"password" validate:"required && password"`
}
type UpdateUsername struct {
Username string `form:"username" json:"username" validate:"required && email"`
}
-434
View File
@@ -1,434 +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/spf13/cast"
"github.com/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/biz"
"github.com/tnb-labs/panel/internal/service"
"github.com/tnb-labs/panel/pkg/io"
"github.com/tnb-labs/panel/pkg/shell"
"github.com/tnb-labs/panel/pkg/types"
)
type App struct {
version uint
taskRepo biz.TaskRepo
}
func NewApp(task biz.TaskRepo) *App {
return &App{
taskRepo: task,
}
}
func (s *App) Route(version uint) func(r chi.Router) {
return func(r chi.Router) {
php := new(App)
php.version = version
php.taskRepo = s.taskRepo
r.Post("/setCli", php.SetCli)
r.Get("/config", php.GetConfig)
r.Post("/config", php.UpdateConfig)
r.Get("/fpmConfig", php.GetFPMConfig)
r.Post("/fpmConfig", php.UpdateFPMConfig)
r.Get("/load", php.Load)
r.Get("/errorLog", php.ErrorLog)
r.Get("/slowLog", php.SlowLog)
r.Post("/clearErrorLog", php.ClearErrorLog)
r.Post("/clearSlowLog", 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{"应用池", "工作模式", "启动时间", "接受连接", "监听队列", "最大监听队列", "监听队列长度", "空闲进程数量", "活动进程数量", "总进程数量", "最大活跃进程数量", "达到进程上限次数", "慢请求"}
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("echo '' > %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("echo '' > %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, "扩展不存在")
return
}
cmd := fmt.Sprintf(`curl -fsLm 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 -fsLm 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 = fmt.Sprintf("安装PHP-%d扩展 %s", 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, "扩展不存在")
return
}
cmd := fmt.Sprintf(`curl -fsLm 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 -fsLm 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 = fmt.Sprintf("卸载PHP-%d扩展 %s", 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: "Fileinfo 是一个用于识别文件类型的库",
},
{
Name: "OPcache",
Slug: "Zend OPcache",
Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销",
},
{
Name: "Redis",
Slug: "redis",
Description: "PhpRedis 是一个用 C 语言编写的 PHP 模块,用来连接并操作 Redis 数据库上的数据(需先安装 igbinary 拓展)",
},
{
Name: "Memcached",
Slug: "memcached",
Description: "Memcached 使用 libmemcached 库连接 Memcached 服务器",
},
{
Name: "ImageMagick",
Slug: "imagick",
Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件",
},
{
Name: "exif",
Slug: "exif",
Description: "通过 exif 扩展,您可以操作图像元数据",
},
{
Name: "pgsql",
Slug: "pgsql",
Description: "pgsql 是一个驱动程序,它启用从 PHP 到 PostgreSQL 数据库的访问(需先安装 PostgreSQL 应用)",
},
{
Name: "pdo_pgsql",
Slug: "pdo_pgsql",
Description: "pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问(需先安装 PostgreSQL 应用)",
},
{
Name: "imap",
Slug: "imap",
Description: "IMAP 扩展允许 PHP 读取、搜索、删除、下载和管理邮件",
},
{
Name: "zip",
Slug: "zip",
Description: "Zip 是一个用于处理 ZIP 文件的库",
},
{
Name: "bz2",
Slug: "bz2",
Description: "Bzip2 是一个用于压缩和解压缩文件的库",
},
{
Name: "readline",
Slug: "readline",
Description: "Readline 是一个库,它提供了一种用于处理文本的接口",
},
{
Name: "snmp",
Slug: "snmp",
Description: "SNMP 是一种用于网络管理的协议",
},
{
Name: "ldap",
Slug: "ldap",
Description: "LDAP 是一种用于访问目录服务的协议",
},
{
Name: "enchant",
Slug: "enchant",
Description: "Enchant 是一个拼写检查库",
},
{
Name: "pspell",
Slug: "pspell",
Description: "Pspell 是一个拼写检查库",
},
{
Name: "calendar",
Slug: "calendar",
Description: "Calendar 是一个用于处理日期的库",
},
{
Name: "gmp",
Slug: "gmp",
Description: "GMP 是一个用于处理大整数的库",
},
{
Name: "sysvmsg",
Slug: "sysvmsg",
Description: "Sysvmsg 是一个用于处理 System V 消息队列的库",
},
{
Name: "sysvsem",
Slug: "sysvsem",
Description: "Sysvsem 是一个用于处理 System V 信号量的库",
},
{
Name: "sysvshm",
Slug: "sysvshm",
Description: "Sysvshm 是一个用于处理 System V 共享内存的库",
},
{
Name: "xsl",
Slug: "xsl",
Description: "XSL 是一个用于处理 XML 文档的库",
},
{
Name: "intl",
Slug: "intl",
Description: "Intl 是一个用于处理国际化和本地化的库",
},
{
Name: "gettext",
Slug: "gettext",
Description: "Gettext 是一个用于处理多语言的库",
},
{
Name: "igbinary",
Slug: "igbinary",
Description: "Igbinary 是一个用于序列化和反序列化数据的库",
},
}
// ionCube Swoole 不支持 PHP 8.4
if cast.ToUint(s.version) < 84 {
extensions = append(extensions, Extension{
Name: "ionCube",
Slug: "ionCube Loader",
Description: "ionCube 是一个专业级的 PHP 加密解密工具(需在 OPcache 之后安装)",
})
extensions = append(extensions, Extension{
Name: "Swoole",
Slug: "swoole",
Description: "Swoole 是一个用于构建高性能的异步并发服务器的 PHP 扩展",
})
}
// 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: "Swow 是一个用于构建高性能的异步并发服务器的 PHP 扩展",
})
}
// 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"`
}
+205 -30
View File
@@ -1,41 +1,67 @@
package phpmyadmin
import (
"errors"
"fmt"
"html"
stdio "io"
"net/http"
"net/url"
"os"
"regexp"
"strings"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-rat/chix"
"github.com/leonelquinteros/gotext"
"github.com/libtnb/chix/v2"
"github.com/libtnb/utils/str"
"github.com/spf13/cast"
"github.com/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/service"
"github.com/tnb-labs/panel/pkg/firewall"
"github.com/tnb-labs/panel/pkg/io"
"github.com/tnb-labs/panel/pkg/shell"
"github.com/tnb-labs/panel/pkg/systemctl"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/biz"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/config"
"github.com/acepanel/panel/v3/pkg/firewall"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/shell"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct{}
type App struct {
t *gotext.Locale
conf *config.Config
databaseServerRepo biz.DatabaseServerRepo
}
func NewApp() *App {
return &App{}
func NewApp(conf *config.Config, t *gotext.Locale, databaseServerRepo biz.DatabaseServerRepo) (*App, error) {
return &App{
t: t,
conf: conf,
databaseServerRepo: databaseServerRepo,
}, nil
}
func (s *App) Route(r chi.Router) {
r.Get("/info", s.Info)
r.Post("/port", s.UpdatePort)
r.Post("/login", s.Login)
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
}
func (s *App) Info(w http.ResponseWriter, r *http.Request) {
files, err := io.ReadDir(fmt.Sprintf("%s/server/phpmyadmin", app.Root))
// Status phpMyAdmin 由 nginx 站点承载,运行状态与 nginx 一致
func (s *App) Status() string {
ok, _ := systemctl.Status("nginx")
return types.AggregateAppStatus(ok)
}
// info 获取 phpMyAdmin 的访问目录与端口
func (s *App) info() (string, int, error) {
files, err := os.ReadDir(fmt.Sprintf("%s/server/phpmyadmin", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "找不到 phpMyAdmin 目录")
return
return "", 0, errors.New(s.t.Get("phpMyAdmin directory not found"))
}
var phpmyadmin string
@@ -45,24 +71,173 @@ func (s *App) Info(w http.ResponseWriter, r *http.Request) {
}
}
if len(phpmyadmin) == 0 {
service.Error(w, http.StatusInternalServerError, "找不到 phpMyAdmin 目录")
return
return "", 0, errors.New(s.t.Get("phpMyAdmin directory not found"))
}
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 {
return "", 0, err
}
match := regexp.MustCompile(`listen\s+(\d+);`).FindStringSubmatch(conf)
if len(match) == 0 {
return "", 0, errors.New(s.t.Get("phpMyAdmin port not found"))
}
return phpmyadmin, cast.ToInt(match[1]), nil
}
func (s *App) Info(w http.ResponseWriter, r *http.Request) {
path, port, err := s.info()
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
match := regexp.MustCompile(`listen\s+(\d+);`).FindStringSubmatch(conf)
if len(match) == 0 {
service.Error(w, http.StatusInternalServerError, "找不到 phpMyAdmin 端口")
service.Success(w, chix.M{
"path": path,
"port": port,
})
}
// ensureConfig 为存量安装补写 config.inc.php,允许登录到任意 MySQL 服务器
func (s *App) ensureConfig(path string) error {
config := fmt.Sprintf("%s/server/phpmyadmin/%s/config.inc.php", app.Root, path)
if io.Exists(config) {
return nil
}
content := fmt.Sprintf(`<?php
declare(strict_types=1);
$cfg['blowfish_secret'] = '%s';
$cfg['AllowArbitraryServer'] = true;
`, str.Random(32))
return io.Write(config, content, 0644)
}
// Login 代理登录 phpMyAdmin 并将会话 Cookie 转发给浏览器
// 面板与 phpMyAdmin 同主机不同端口,Cookie 按主机共享,浏览器凭转发的 Cookie 即为已登录态
func (s *App) Login(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[Login](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
server, err := s.databaseServerRepo.Get(r.Context(), req.ServerID)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if server.Type != biz.DatabaseTypeMysql {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("server %s is not a MySQL server", server.Name))
return
}
path, port, err := s.info()
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = s.ensureConfig(path); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
loginURL := fmt.Sprintf("http://127.0.0.1:%d/%s/index.php?route=/", port, path)
client := &http.Client{
Timeout: 30 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
// 获取登录页以取得会话 Cookie 与 CSRF token
// 转发浏览器语言,避免语言协商落空后回落英文
pageReq, err := http.NewRequestWithContext(r.Context(), http.MethodGet, loginURL, nil)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
pageReq.Header.Set("Accept-Language", r.Header.Get("Accept-Language"))
pageResp, err := client.Do(pageReq)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to request phpMyAdmin: %v", err))
return
}
defer func() { _ = pageResp.Body.Close() }()
page, err := stdio.ReadAll(stdio.LimitReader(pageResp.Body, 4<<20))
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to request phpMyAdmin: %v", err))
return
}
token := regexp.MustCompile(`name="token" value="([^"]+)"`).FindStringSubmatch(string(page))
session := regexp.MustCompile(`name="set_session" value="([^"]+)"`).FindStringSubmatch(string(page))
if len(token) < 2 {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to parse phpMyAdmin login page"))
return
}
form := url.Values{}
form.Set("route", "/")
// 语言跟随面板设置,登录后固化进 phpMyAdmin 会话
if s.conf.App.Locale != "" {
form.Set("lang", s.conf.App.Locale)
}
form.Set("token", html.UnescapeString(token[1]))
if len(session) >= 2 {
form.Set("set_session", html.UnescapeString(session[1]))
}
form.Set("pma_username", server.Username)
form.Set("pma_password", server.Password)
form.Set("server", "1")
// 本地默认端口走 phpMyAdmin 默认配置(socket 连接),其余场景显式指定目标服务器
// host 为 localhost 但端口非默认时须用 127.0.0.1 强制走 TCP,否则 mysqli 会忽略端口走 socket
isLocal := server.Host == "localhost" || server.Host == "127.0.0.1"
if !isLocal || server.Port != 3306 {
host := server.Host
if host == "localhost" {
host = "127.0.0.1"
}
form.Set("pma_servername", fmt.Sprintf("%s %d", host, server.Port))
}
loginReq, err := http.NewRequestWithContext(r.Context(), http.MethodPost, loginURL, strings.NewReader(form.Encode()))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
loginReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
loginReq.Header.Set("Accept-Language", r.Header.Get("Accept-Language"))
for _, cookie := range pageResp.Cookies() {
loginReq.AddCookie(cookie)
}
loginResp, err := client.Do(loginReq)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to request phpMyAdmin: %v", err))
return
}
defer func() { _ = loginResp.Body.Close() }()
_, _ = stdio.Copy(stdio.Discard, stdio.LimitReader(loginResp.Body, 4<<20))
// 登录成功时 phpMyAdmin 返回 302 并携带会话 Cookie
if loginResp.StatusCode != http.StatusFound {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to login phpMyAdmin, please check the credentials and status of server %s", server.Name))
return
}
// 改写 SameSite 以兼容面板 https 跳转 http 的场景
for _, cookie := range append(pageResp.Cookies(), loginResp.Cookies()...) {
cookie.SameSite = http.SameSiteLaxMode
cookie.Secure = false
http.SetCookie(w, cookie)
}
service.Success(w, chix.M{
"path": phpmyadmin,
"port": cast.ToInt(match[1]),
"path": path,
"port": port,
})
}
@@ -73,14 +248,14 @@ 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 {
service.ErrorSystem(w)
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
}
@@ -89,8 +264,8 @@ func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
Type: firewall.TypeNormal,
PortStart: req.Port,
PortEnd: req.Port,
Direction: firewall.DirectionIn,
Strategy: firewall.StrategyAccept,
Direction: firewall.DirectionIn,
}, firewall.OperationAdd)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -99,7 +274,7 @@ func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
if err = systemctl.Reload("nginx"); err != nil {
_, err = shell.Execf("nginx -t")
service.Error(w, http.StatusInternalServerError, "重载OpenResty失败:%v", err)
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
return
}
@@ -107,7 +282,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
@@ -123,14 +298,14 @@ 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
}
if err = systemctl.Reload("nginx"); err != nil {
_, err = shell.Execf("nginx -t")
service.Error(w, http.StatusInternalServerError, "重载OpenResty失败:%v", err)
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload nginx: %v", err))
return
}
+5 -1
View File
@@ -5,5 +5,9 @@ type UpdateConfig struct {
}
type UpdatePort struct {
Port uint `form:"port" json:"port" validate:"required|number|min:1|max:65535"`
Port uint `form:"port" json:"port" validate:"required && number && min:1 && max:65535"`
}
type Login struct {
ServerID uint `form:"server_id" json:"server_id" validate:"required"`
}
+15 -9
View File
@@ -5,22 +5,28 @@ import (
"github.com/go-chi/chi/v5"
"github.com/tnb-labs/panel/internal/service"
"github.com/tnb-labs/panel/pkg/io"
"github.com/tnb-labs/panel/pkg/systemctl"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct{}
func NewApp() *App {
return &App{}
func NewApp() (*App, error) {
return &App{}, nil
}
func (s *App) Route(r chi.Router) {
r.Get("/registryConfig", s.GetRegistryConfig)
r.Post("/registryConfig", s.UpdateRegistryConfig)
r.Get("/storageConfig", s.GetStorageConfig)
r.Post("/storageConfig", s.UpdateStorageConfig)
r.Get("/registry_config", s.GetRegistryConfig)
r.Post("/registry_config", s.UpdateRegistryConfig)
r.Get("/storage_config", s.GetStorageConfig)
r.Post("/storage_config", s.UpdateStorageConfig)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("podman")
return types.AggregateAppStatus(ok)
}
func (s *App) GetRegistryConfig(w http.ResponseWriter, r *http.Request) {
+332 -43
View File
@@ -3,40 +3,65 @@ package postgresql
import (
"fmt"
"net/http"
"time"
"os"
"path/filepath"
"strings"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/spf13/cast"
"github.com/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/service"
"github.com/tnb-labs/panel/pkg/io"
"github.com/tnb-labs/panel/pkg/shell"
"github.com/tnb-labs/panel/pkg/systemctl"
"github.com/tnb-labs/panel/pkg/types"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/biz"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/db"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/shell"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct{}
type App struct {
t *gotext.Locale
settingRepo biz.SettingRepo
databaseServerRepo biz.DatabaseServerRepo
}
func NewApp() *App {
return &App{}
func NewApp(t *gotext.Locale, databaseServerRepo biz.DatabaseServerRepo, settingRepo biz.SettingRepo) (*App, error) {
setting := settingRepo
databaseServer := databaseServerRepo
return &App{
t: t,
settingRepo: setting,
databaseServerRepo: databaseServer,
}, nil
}
func (s *App) Route(r chi.Router) {
r.Get("/config", s.GetConfig)
r.Post("/config", s.UpdateConfig)
r.Get("/userConfig", s.GetUserConfig)
r.Post("/userConfig", s.UpdateUserConfig)
r.Get("/user_config", s.GetUserConfig)
r.Post("/user_config", s.UpdateUserConfig)
r.Get("/load", s.Load)
r.Get("/log", s.Log)
r.Post("/clearLog", 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)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("postgresql")
return types.AggregateAppStatus(ok)
}
// GetConfig 获取配置
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
// 获取配置
config, err := io.Read(fmt.Sprintf("%s/server/postgresql/data/postgresql.conf", app.Root))
config, err := io.Read(s.configPath())
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL配置失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -51,13 +76,14 @@ func (s *App) UpdateConfig(w http.ResponseWriter, r *http.Request) {
return
}
if err := io.Write(fmt.Sprintf("%s/server/postgresql/data/postgresql.conf", app.Root), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "写入PostgreSQL配置失败")
oldPort := s.getPort()
if err = io.Write(s.configPath(), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err := systemctl.Reload("postgresql"); err != nil {
service.Error(w, http.StatusInternalServerError, "重载服务失败")
if err = s.applyConfig(req.Config, oldPort); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to apply PostgreSQL config: %v", err))
return
}
@@ -69,7 +95,7 @@ func (s *App) GetUserConfig(w http.ResponseWriter, r *http.Request) {
// 获取配置
config, err := io.Read(fmt.Sprintf("%s/server/postgresql/data/pg_hba.conf", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL配置失败")
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
@@ -84,13 +110,13 @@ func (s *App) UpdateUserConfig(w http.ResponseWriter, r *http.Request) {
return
}
if err := io.Write(fmt.Sprintf("%s/server/postgresql/data/pg_hba.conf", app.Root), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "写入PostgreSQL配置失败")
if err = io.Write(fmt.Sprintf("%s/server/postgresql/data/pg_hba.conf", app.Root), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err := systemctl.Reload("postgresql"); err != nil {
service.Error(w, http.StatusInternalServerError, "重载服务失败")
if err = systemctl.Reload("postgresql"); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to reload PostgreSQL: %v", err))
return
}
@@ -105,54 +131,317 @@ 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, "获取PostgreSQL启动时间失败")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to load PostgreSQL postgres password: %v", err))
return
}
pid, err := shell.Execf(`echo "select pg_backend_pid();" | su - postgres -c "psql" | sed -n 3p`)
if err = os.Setenv("PGPASSWORD", postgresPassword); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to set PGPASSWORD env: %v", err))
return
}
defer func() { _ = os.Unsetenv("PGPASSWORD") }()
port := s.getPort()
start, err := shell.Execf(`psql -h 127.0.0.1 -p %d -U postgres -t -c "select pg_postmaster_start_time();" | head -1 | cut -d'.' -f1`, port)
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL进程PID失败")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL start time: %v", err))
return
}
pid, err := shell.Execf(`psql -h 127.0.0.1 -p %d -U postgres -t -c "select pg_backend_pid();"`, port)
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL backend pid: %v", err))
return
}
process, err := shell.Execf(`ps aux | grep postgres | grep -v grep | wc -l`)
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL进程数失败")
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 -p %d -U postgres -t -c "SELECT count(*) FROM pg_stat_activity WHERE NOT pid=pg_backend_pid();"`, port)
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL连接数失败")
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 -p %d -U postgres -t -c "select pg_size_pretty(pg_database_size('postgres'));"`, port)
if err != nil {
service.Error(w, http.StatusInternalServerError, "获取PostgreSQL空间占用失败")
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get PostgreSQL database size: %v", err))
return
}
data := []types.NV{
{Name: "启动时间", Value: start},
{Name: "进程 PID", Value: pid},
{Name: "进程数", Value: process},
{Name: "总连接数", Value: connections},
{Name: "空间占用", Value: storage},
{Name: s.t.Get("Start Time"), Value: start},
{Name: s.t.Get("Process PID"), Value: pid},
{Name: s.t.Get("Process Count"), Value: process},
{Name: s.t.Get("Total Connections"), Value: connections},
{Name: s.t.Get("Storage Usage"), Value: storage},
}
service.Success(w, data)
}
// Log 获取日志
// Log 获取应用日志路径列表
func (s *App) Log(w http.ResponseWriter, r *http.Request) {
service.Success(w, fmt.Sprintf("%s/server/postgresql/logs/postgresql-%s.log", app.Root, time.Now().Format(time.DateOnly)))
paths, err := filepath.Glob(fmt.Sprintf("%s/server/postgresql/logs/postgresql-*.log", app.Root))
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, paths)
}
// ClearLog 清空日志
func (s *App) ClearLog(w http.ResponseWriter, r *http.Request) {
if _, err := shell.Execf("rm -rf %s/server/postgresql/logs/postgresql-*.log", app.Root); err != 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)
port := s.getPort()
postgres, err := db.NewPostgres(r.Context(), "postgres", oldPassword, "127.0.0.1", port)
if err != nil {
// 直接修改密码
if _, err = shell.Execf(`su - postgres -c "psql -p %d -c \"ALTER USER postgres WITH PASSWORD '%s';\""`, port, 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(s.configPath())
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 := s.configPath()
oldPort := s.getPort()
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 = s.applyConfig(config, oldPort); err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to apply PostgreSQL config: %v", err))
return
}
service.Success(w, nil)
}
func (s *App) configPath() string {
return fmt.Sprintf("%s/server/postgresql/data/postgresql.conf", app.Root)
}
// getPort 读取 PostgreSQL 端口
func (s *App) getPort() uint {
config, err := io.Read(s.configPath())
if err != nil {
return 5432
}
return s.parsePort(config)
}
// parsePort 从 config 内容解析端口,未配置时返回默认值
func (s *App) parsePort(config string) uint {
port := cast.ToUint(s.getPGValue(config, "port"))
if port == 0 {
return 5432
}
return port
}
// applyConfig 让 PostgreSQL 配置生效
func (s *App) applyConfig(newConfig string, oldPort uint) error {
newPort := s.parsePort(newConfig)
if oldPort == newPort {
return systemctl.Reload("postgresql")
}
if err := systemctl.Restart("postgresql"); err != nil {
return err
}
return s.databaseServerRepo.UpdatePort("local_postgresql", newPort)
}
// getPGValue 从 PostgreSQL 配置内容中获取指定键的值
func (s *App) getPGValue(content string, key string) string {
lines := strings.SplitSeq(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" validate:"number && min:1 && max:65535"`
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"`
}
+436
View File
@@ -0,0 +1,436 @@
package prometheus
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"time"
"github.com/go-chi/chi/v5"
"github.com/leonelquinteros/gotext"
"github.com/samber/lo"
"github.com/spf13/cast"
"go.yaml.in/yaml/v4"
"resty.dev/v3"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/biz"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/config"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct {
t *gotext.Locale
conf *config.Config
taskRepo biz.TaskRepo
}
func NewApp(conf *config.Config, t *gotext.Locale, taskRepo biz.TaskRepo) (*App, error) {
return &App{t: t, conf: conf, taskRepo: taskRepo}, nil
}
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)
// Alertmanager 配置
r.Get("/alertmanager_config", s.GetAlertmanagerConfig)
r.Post("/alertmanager_config", s.UpdateAlertmanagerConfig)
// Exporters 管理
r.Get("/exporters", s.ExporterList)
r.Post("/exporters", s.InstallExporter)
r.Delete("/exporters", s.UninstallExporter)
r.Post("/exporters/{slug}/start", s.StartExporter)
r.Post("/exporters/{slug}/stop", s.StopExporter)
r.Post("/exporters/{slug}/restart", s.RestartExporter)
r.Get("/exporters/{slug}/config", s.GetExporterConfig)
r.Post("/exporters/{slug}/config", s.UpdateExporterConfig)
}
func (s *App) Status() string {
prom, _ := systemctl.Status("prometheus")
alert, _ := systemctl.Status("alertmanager")
return types.AggregateAppStatus(prom, alert)
}
func (s *App) Load(w http.ResponseWriter, r *http.Request) {
status, err := systemctl.Status("prometheus")
if err != nil {
service.Error(w, http.StatusInternalServerError, s.t.Get("failed to get prometheus status: %v", err))
return
}
if !status {
service.Success(w, []types.NV{})
return
}
client := resty.New().SetTimeout(10 * time.Second)
defer func(client *resty.Client) { _ = client.Close() }(client)
resp, err := client.R().Get("http://127.0.0.1:9090/api/v1/status/runtimeinfo")
if err != nil || !resp.IsStatusSuccess() {
service.Success(w, []types.NV{})
return
}
var result struct {
Data struct {
StartTime string `json:"startTime"`
GoroutineCount int `json:"goroutineCount"`
GOMAXPROCS int `json:"GOMAXPROCS"`
StorageRetention string `json:"storageRetention"`
ReloadConfigSuccess bool `json:"reloadConfigSuccess"`
} `json:"data"`
}
if err = json.Unmarshal(resp.Bytes(), &result); err != nil {
service.Success(w, []types.NV{})
return
}
data := []types.NV{
{Name: s.t.Get("Start Time"), Value: result.Data.StartTime},
{Name: s.t.Get("Storage Retention"), Value: result.Data.StorageRetention},
{Name: s.t.Get("Goroutine Count"), Value: cast.ToString(result.Data.GoroutineCount)},
{Name: "GOMAXPROCS", Value: cast.ToString(result.Data.GOMAXPROCS)},
{Name: s.t.Get("Config Reload Success"), Value: cast.ToString(result.Data.ReloadConfigSuccess)},
}
service.Success(w, data)
}
func (s *App) GetConfig(w http.ResponseWriter, r *http.Request) {
conf, _ := io.Read(fmt.Sprintf("%s/server/prometheus/prometheus.yml", app.Root))
service.Success(w, conf)
}
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/prometheus/prometheus.yml", app.Root), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("prometheus"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetConfigTune 获取 Prometheus 全局配置调整参数
func (s *App) GetConfigTune(w http.ResponseWriter, r *http.Request) {
conf, _ := io.Read(fmt.Sprintf("%s/server/prometheus/prometheus.yml", app.Root))
var cfg struct {
Global struct {
ScrapeInterval string `yaml:"scrape_interval"`
EvaluationInterval string `yaml:"evaluation_interval"`
ScrapeTimeout string `yaml:"scrape_timeout"`
} `yaml:"global"`
}
_ = yaml.Unmarshal([]byte(conf), &cfg)
tune := ConfigTune{
ScrapeInterval: cfg.Global.ScrapeInterval,
EvaluationInterval: cfg.Global.EvaluationInterval,
ScrapeTimeout: cfg.Global.ScrapeTimeout,
}
service.Success(w, tune)
}
// UpdateConfigTune 更新 Prometheus 全局配置调整参数
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/prometheus/prometheus.yml", app.Root)
raw, _ := io.Read(confPath)
var cfg map[string]any
if err = yaml.Unmarshal([]byte(raw), &cfg); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
global, ok := cfg["global"].(map[string]any)
if !ok {
global = make(map[string]any)
}
if req.ScrapeInterval != "" {
global["scrape_interval"] = req.ScrapeInterval
}
if req.EvaluationInterval != "" {
global["evaluation_interval"] = req.EvaluationInterval
}
if req.ScrapeTimeout != "" {
global["scrape_timeout"] = req.ScrapeTimeout
}
cfg["global"] = global
data, err := yaml.Marshal(cfg)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = io.Write(confPath, string(data), 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("prometheus"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetAlertmanagerConfig 获取 Alertmanager 配置
func (s *App) GetAlertmanagerConfig(w http.ResponseWriter, r *http.Request) {
conf, _ := io.Read(fmt.Sprintf("%s/server/prometheus/alertmanager/alertmanager.yml", app.Root))
service.Success(w, conf)
}
// UpdateAlertmanagerConfig 更新 Alertmanager 配置
func (s *App) UpdateAlertmanagerConfig(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/prometheus/alertmanager/alertmanager.yml", app.Root), req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("alertmanager"); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// ExporterList 获取 Exporter 列表及状态
func (s *App) ExporterList(w http.ResponseWriter, r *http.Request) {
exporters := s.getExporters()
for i := range exporters {
exporters[i].Installed = io.Exists(fmt.Sprintf("%s/server/prometheus/exporters/%s", app.Root, exporters[i].Slug))
if exporters[i].Installed {
running, _ := systemctl.Status("prometheus-" + exporters[i].Slug)
exporters[i].Running = running
}
}
service.Success(w, exporters)
}
// InstallExporter 安装 Exporter(异步任务)
func (s *App) InstallExporter(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ExporterSlug](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if !s.checkExporter(req.Slug) {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("exporter %s does not exist", req.Slug))
return
}
cmd := fmt.Sprintf(`curl -sSLm 10 --retry 3 'https://%s/prometheus/exporters/exporter.sh' | bash -s -- 'install' '%s'`, s.conf.App.DownloadEndpoint, url.PathEscape(req.Slug))
task := new(biz.Task)
task.Key = "prometheus:exporter:" + req.Slug
task.Name = s.t.Get("Install Prometheus exporter %s", req.Slug)
task.Status = biz.TaskStatusWaiting
task.Shell = cmd
if err = s.taskRepo.Push(task); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// UninstallExporter 卸载 Exporter(异步任务)
func (s *App) UninstallExporter(w http.ResponseWriter, r *http.Request) {
req, err := service.Bind[ExporterSlug](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if !s.checkExporter(req.Slug) {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("exporter %s does not exist", req.Slug))
return
}
cmd := fmt.Sprintf(`curl -sSLm 10 --retry 3 'https://%s/prometheus/exporters/exporter.sh' | bash -s -- 'uninstall' '%s'`, s.conf.App.DownloadEndpoint, url.PathEscape(req.Slug))
task := new(biz.Task)
task.Key = "prometheus:exporter:" + req.Slug
task.Name = s.t.Get("Uninstall Prometheus exporter %s", req.Slug)
task.Status = biz.TaskStatusWaiting
task.Shell = cmd
if err = s.taskRepo.Push(task); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// StartExporter 启动 Exporter
func (s *App) StartExporter(w http.ResponseWriter, r *http.Request) {
slug := chi.URLParam(r, "slug")
if !s.checkExporter(slug) {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("exporter %s does not exist", slug))
return
}
if err := systemctl.Start("prometheus-" + slug); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// StopExporter 停止 Exporter
func (s *App) StopExporter(w http.ResponseWriter, r *http.Request) {
slug := chi.URLParam(r, "slug")
if !s.checkExporter(slug) {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("exporter %s does not exist", slug))
return
}
if err := systemctl.Stop("prometheus-" + slug); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// RestartExporter 重启 Exporter
func (s *App) RestartExporter(w http.ResponseWriter, r *http.Request) {
slug := chi.URLParam(r, "slug")
if !s.checkExporter(slug) {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("exporter %s does not exist", slug))
return
}
if err := systemctl.Restart("prometheus-" + slug); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// GetExporterConfig 获取 Exporter 配置
func (s *App) GetExporterConfig(w http.ResponseWriter, r *http.Request) {
slug := chi.URLParam(r, "slug")
if !s.checkExporter(slug) {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("exporter %s does not exist", slug))
return
}
confPath := s.getExporterConfigPath(slug)
if confPath == "" {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("exporter %s has no configuration file", slug))
return
}
conf, _ := io.Read(confPath)
service.Success(w, conf)
}
// UpdateExporterConfig 更新 Exporter 配置
func (s *App) UpdateExporterConfig(w http.ResponseWriter, r *http.Request) {
slug := chi.URLParam(r, "slug")
if !s.checkExporter(slug) {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("exporter %s does not exist", slug))
return
}
confPath := s.getExporterConfigPath(slug)
if confPath == "" {
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("exporter %s has no configuration file", slug))
return
}
req, err := service.Bind[ExporterConfig](r)
if err != nil {
service.Error(w, http.StatusUnprocessableEntity, "%v", err)
return
}
if err = io.Write(confPath, req.Config, 0644); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
if err = systemctl.Restart("prometheus-" + slug); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
}
service.Success(w, nil)
}
// getExporters 返回所有 exporter 定义
func (s *App) getExporters() []Exporter {
return []Exporter{
{Name: "Node Exporter", Slug: "node_exporter", Description: s.t.Get("Hardware and OS metrics")},
{Name: "Nginx Exporter", Slug: "nginx_exporter", Description: s.t.Get("Nginx metrics")},
{Name: "MySQL Exporter", Slug: "mysqld_exporter", Description: s.t.Get("MySQL database metrics"), HasConfig: true},
{Name: "PostgreSQL Exporter", Slug: "postgres_exporter", Description: s.t.Get("PostgreSQL database metrics"), HasConfig: true},
{Name: "MongoDB Exporter", Slug: "mongodb_exporter", Description: s.t.Get("MongoDB metrics"), HasConfig: true},
{Name: "Elasticsearch Exporter", Slug: "elasticsearch_exporter", Description: s.t.Get("Elasticsearch metrics"), HasConfig: true},
{Name: "Redis Exporter", Slug: "redis_exporter", Description: s.t.Get("Redis metrics"), HasConfig: true},
{Name: "Memcached Exporter", Slug: "memcached_exporter", Description: s.t.Get("Memcached metrics")},
{Name: "Kafka Exporter", Slug: "kafka_exporter", Description: s.t.Get("Kafka metrics"), HasConfig: true},
}
}
// getExporterConfigPath 获取 exporter 配置文件路径
func (s *App) getExporterConfigPath(slug string) string {
base := fmt.Sprintf("%s/server/prometheus/exporters/%s", app.Root, slug)
switch slug {
case "redis_exporter", "postgres_exporter", "elasticsearch_exporter", "mongodb_exporter", "kafka_exporter":
return base + "/env"
case "mysqld_exporter":
return base + "/.my.cnf"
default:
return ""
}
}
// checkExporter 检查 slug 是否有效
func (s *App) checkExporter(slug string) bool {
return lo.ContainsBy(s.getExporters(), func(e Exporter) bool {
return e.Slug == slug
})
}
+32
View File
@@ -0,0 +1,32 @@
package prometheus
type UpdateConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
// ConfigTune Prometheus 全局配置调整
type ConfigTune struct {
ScrapeInterval string `form:"scrape_interval" json:"scrape_interval"`
EvaluationInterval string `form:"evaluation_interval" json:"evaluation_interval"`
ScrapeTimeout string `form:"scrape_timeout" json:"scrape_timeout"`
}
// Exporter Prometheus Exporter 信息
type Exporter struct {
Name string `json:"name"`
Slug string `json:"slug"`
Description string `json:"description"`
Installed bool `json:"installed"`
Running bool `json:"running"`
HasConfig bool `json:"has_config"`
}
// ExporterSlug Exporter 操作请求
type ExporterSlug struct {
Slug string `form:"slug" json:"slug" validate:"required"`
}
// ExporterConfig Exporter 配置更新请求
type ExporterConfig struct {
Config string `form:"config" json:"config" validate:"required"`
}
+173 -33
View File
@@ -1,26 +1,35 @@
package pureftpd
import (
"fmt"
"net/http"
"regexp"
"strings"
"github.com/go-chi/chi/v5"
"github.com/go-rat/chix"
"github.com/leonelquinteros/gotext"
"github.com/libtnb/chix/v2"
"github.com/samber/lo"
"github.com/spf13/cast"
"github.com/tnb-labs/panel/internal/app"
"github.com/tnb-labs/panel/internal/service"
"github.com/tnb-labs/panel/pkg/firewall"
"github.com/tnb-labs/panel/pkg/io"
"github.com/tnb-labs/panel/pkg/shell"
"github.com/tnb-labs/panel/pkg/systemctl"
"github.com/acepanel/panel/v3/internal/app"
"github.com/acepanel/panel/v3/internal/service"
"github.com/acepanel/panel/v3/pkg/firewall"
"github.com/acepanel/panel/v3/pkg/io"
"github.com/acepanel/panel/v3/pkg/shell"
"github.com/acepanel/panel/v3/pkg/systemctl"
"github.com/acepanel/panel/v3/pkg/types"
)
type App struct{}
type App struct {
t *gotext.Locale
}
func NewApp() *App {
return &App{}
func NewApp(t *gotext.Locale) (*App, error) {
return &App{
t: t,
}, nil
}
func (s *App) Route(r chi.Router) {
@@ -30,6 +39,13 @@ 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)
}
func (s *App) Status() string {
ok, _ := systemctl.Status("pure-ftpd")
return types.AggregateAppStatus(ok)
}
// List 获取用户列表
@@ -42,19 +58,17 @@ func (s *App) List(w http.ResponseWriter, r *http.Request) {
})
}
listArr := strings.Split(listRaw, "\n")
var users []User
for _, v := range listArr {
userRe := regexp.MustCompile(`(\S+)\s+(\S+)`)
users := lo.FilterMap(strings.Split(listRaw, "\n"), func(v string, _ int) (User, bool) {
if len(v) == 0 {
continue
return User{}, false
}
match := regexp.MustCompile(`(\S+)\s+(\S+)`).FindStringSubmatch(v)
users = append(users, User{
match := userRe.FindStringSubmatch(v)
return User{
Username: match[1],
Path: strings.Replace(match[2], "/./", "/", 1),
})
}
}, true
})
paged, total := service.Paginate(r, users)
@@ -76,18 +90,10 @@ func (s *App) Create(w http.ResponseWriter, r *http.Request) {
req.Path = "/" + req.Path
}
if !io.Exists(req.Path) {
service.Error(w, http.StatusUnprocessableEntity, "目录不存在")
service.Error(w, http.StatusUnprocessableEntity, s.t.Get("directory %s does not exist", req.Path))
return
}
if err = io.Chmod(req.Path, 0755); err != nil {
service.Error(w, http.StatusUnprocessableEntity, "修改目录权限失败")
return
}
if err = io.Chown(req.Path, "www", "www"); err != nil {
service.Error(w, http.StatusUnprocessableEntity, "修改目录权限失败")
return
}
if _, err = shell.Execf(`yes '%s' | pure-pw useradd '%s' -u www -g www -d '%s'`, req.Password, req.Username, req.Path); err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
return
@@ -142,13 +148,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, "获取PureFtpd端口失败")
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 设置端口
@@ -159,7 +171,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
}
@@ -169,8 +188,8 @@ func (s *App) UpdatePort(w http.ResponseWriter, r *http.Request) {
Type: firewall.TypeNormal,
PortStart: req.Port,
PortEnd: req.Port,
Direction: firewall.DirectionIn,
Strategy: firewall.StrategyAccept,
Direction: firewall.DirectionIn,
}, firewall.OperationAdd)
if err != nil {
service.Error(w, http.StatusInternalServerError, "%v", err)
@@ -184,3 +203,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.SplitSeq(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")
}

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