Compare commits

..
Author SHA1 Message Date
wanghe-fit2cloud aaae8a5d3b fix: Remove the build cache 2024-11-20 12:08:35 +08:00
wanghe-fit2cloud 5ca91157ca chore: modify question templates. 2024-11-19 18:12:11 +08:00
wanghe-fit2cloud bad1b0b105 perf: Update related workflows 2024-11-19 18:08:58 +08:00
wanghe-fit2cloud d14a149c57 Remove .vscode folder from the repository 2024-11-19 18:06:57 +08:00
吴小白 9c6c77615e build(deps): bump goreleaser from v2.4.5 to latest (#7122)
#### What this PR does / why we need it?
- https://github.com/goreleaser/goreleaser/issues/5292
- https://github.com/goreleaser/goreleaser/commit/700889269f740f659c9f3867f2b79fed677b74b0

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2024-11-19 03:56:05 +00:00
吴小白 913e64bb75 perf: remove go cache (#7114)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2024-11-18 08:02:03 +00:00
蘭 fd8ef545e8 style: 主题样式优化 (#7113) 2024-11-18 07:42:03 +00:00
ssongliu d068cc40f0 fix: 修改未激活状态下信息内容 (#7112) 2024-11-18 06:58:03 +00:00
ssongliu ee0e17a496 fix: 解决数据库切换版本不正确的问题 (#7111) 2024-11-18 06:16:04 +00:00
吴小白 f1afc0f4d9 build(deps): bump goreleaser from v2 to v2.4.5 (#7110)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2024-11-18 06:14:03 +00:00
吴小白 ea1afe7610 build(deps): bump golang from 1.22 to 1.23 (#7109) 2024-11-18 11:33:25 +08:00
ssongliu 4ade175a3d fix: 统一 xpack 失联接口返回内容 (#7106) 2024-11-18 02:30:03 +00:00
igophper c6ea06153a fix: 解决编辑端口规则不能保存的问题 (#7100) 2024-11-17 22:52:02 +08:00
ssongliu eb36a0f8cb fix: 优化许可证错误信息 (#7094) 2024-11-16 13:00:02 +00:00
ssongliu 3087c1a6e8 fix: 完善 license 同步失联逻辑 (#7090) 2024-11-15 15:22:01 +00:00
蘭 e5b0a1c591 feat: Echarts 组件适配主题颜色 (#7087) 2024-11-15 20:14:34 +08:00
igophper af41e8e429 feat: 增加DNS服务商CloudDNS (#7081)
Refs #7038
2024-11-15 16:46:45 +08:00
蘭 7e677ee9fa fix: 获取告警短信使用量 (#7077) 2024-11-15 08:06:02 +00:00
蘭 eea4cf68ad fix: 修复许可证失效主题切换错误问题 (#7075) 2024-11-15 07:24:01 +00:00
刘瑞斌 3a400dd15a feat: 增加火山引擎DNS (#7006) 2024-11-15 11:27:41 +08:00
maninhill fbbbd52d72 chore: Update README_EN.md 2024-11-15 06:58:19 +08:00
maninhill 27664c7dce chore: Update README.md 2024-11-14 22:56:36 +08:00
蘭 e1bdd215b1 fix: 编辑计划任务和病毒扫描告警问题处理 (#7058) 2024-11-14 21:52:37 +08:00
蘭 d04a35565e feat: 主题样式优化 (#7053) 2024-11-14 09:32:01 +00:00
蘭 7aad2cf4af fix: 客户端缓存导致界面设置 Logo 不能正常显示 (#7048)
Refs #7020
2024-11-14 08:18:02 +00:00
蘭 bfa667584a fix: 编辑计划任务和病毒扫描告警问题处理 (#7047)
Refs #7039
2024-11-14 16:15:47 +08:00
ssongliu 1858cb4df8 fix: 导入恢复限制文件数量 (#7045) 2024-11-14 06:20:01 +00:00
ssongliu dc30a3d1c3 fix: 解决 vue 组件未兼容繁体的问题 (#7044)
Refs #7033
2024-11-14 06:18:02 +00:00
蘭 6e690cc6f2 style: 主题样式优化 (#7037) 2024-11-13 14:16:01 +00:00
蘭 911b4076f5 feat: 主题色保存/恢复增加确认提示 (#7036) 2024-11-13 14:14:01 +00:00
蘭 4c01b1e542 fix: 删除目录校验 (#7035) 2024-11-13 14:12:01 +00:00
蘭 ec372dbb98 fix: 删除目录校验是否为禁止删除的目录 (#7031) 2024-11-13 07:10:00 +00:00
蘭 0b87025c52 feat: 面板目录禁止删除 (#7029)
Refs #7008
2024-11-13 06:52:02 +00:00
蘭 7d4230ecfb fix: 关闭代理同时更新 Docker 代理 (#7028) 2024-11-13 06:50:00 +00:00
ssongliu d06583773a fix: 优化概览页磁盘显示 (#7023) 2024-11-13 06:46:01 +00:00
ssongliu 67c1fbd07a fix: 优化登陆页面特征信息 (#7027)
Refs #7000
2024-11-13 06:44:00 +00:00
蘭 5c7fab8f01 style: 面板样式优化 (#7026) 2024-11-13 06:30:00 +00:00
ssongliu 06554375cb fix: 修改面板证书显示域名信息 (#7019) 2024-11-12 23:04:00 +08:00
蘭 e65329e7b3 perf: 优化 docker 代理 (#7016) 2024-11-12 11:18:00 +00:00
蘭 f5435c581b perf: 优化主题色及样式 (#7015) 2024-11-12 10:32:00 +00:00
ssongliu 5e8b23db6a fix: 快照恢复忽略应用恢复失败问题 (#7012) 2024-11-12 17:34:23 +08:00
蘭 f2849aa6ec perf: 优化主题色及样式 (#7009) 2024-11-12 13:48:49 +08:00
Xiao TanandBH1XAQ ee947b6468 fix: 修复 pure-ftpd 上传缓慢问题 (#6994)
* fix: 修复 pure-ftpd 上传缓慢问题

* fix: 修复 pure-ftpd 修改密码用户名的判断依据

---------

Co-authored-by: BH1XAQ <tanxiao@16iot.cn>
2024-11-12 09:27:21 +08:00
蘭 49c072b40d perf: 优化主题色及样式 (#7002) 2024-11-11 22:46:27 +08:00
蘭 204f9f1fbc feat: 面板代理服务器提供 docker 代理 (#6986) 2024-11-08 18:19:48 +08:00
zhengkunwang cccbaafd1b feat: 修改应用安装超时时间 (#6983) 2024-11-08 06:55:21 +00:00
BugKing 7bc06bb6b1 docs: Update README.md 2024-11-08 09:25:05 +08:00
BugKing c16d5190c6 docs: Update README.md
增加专业版简要说明。
2024-11-08 09:25:05 +08:00
蘭 ffdd9b9d79 feat: 增加自定义主题颜色,调整默认暗色主题配色 (#6964) 2024-11-06 09:39:20 +00:00
ssongliu 8d35c54672 fix: 优化防火墙端口转发错误信息 (#6967) 2024-11-06 08:31:19 +00:00
ssongliu eb0164907c fix: 解决面板证书信息获取失败的问题 (#6962)
Refs #6863
2024-11-06 06:37:19 +00:00
wanghe 478d44885c Update OWNERS (#6963) 2024-11-06 14:33:34 +08:00
dependabot[bot] 541bc456be build(deps): bump github.com/golang-jwt/jwt/v4 from 4.5.0 to 4.5.1 (#6959)
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.5.0...v4.5.1)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-06 10:22:52 +08:00
liqiang-fit2cloud 5ce75992a3 Update llm-code-review.yml 2024-11-05 14:57:12 +08:00
蘭 c8b3ba7f6c style: 组件样式调整 (#6943) 2024-11-04 10:50:53 +00:00
liqiang-fit2cloud d92ce86bb8 build: add code review action. 2024-11-04 17:59:28 +08:00
ssongliu f7b4c8cd28 fix: 优化容器创建提示信息 (#6925) 2024-11-01 14:27:10 +00:00
YaKun9 24c063b106 feat: 概览-状态,磁盘数量多过时默认收起 (#6918)
Refs https://github.com/1Panel-dev/1Panel/issues/6917
feat: 概览-状态,磁盘数量多过时默认收起
2024-11-01 14:25:10 +00:00
igophper e478d2e624 fix: 解决清空网站日志时的闪烁问题 (#6905)
Refs #6904
2024-10-31 10:19:36 +08:00
ssongliu dc0f726029 fix: 解决操作日志打印错误的问题 (#6899) 2024-10-30 17:02:19 +08:00
ssongliu 784b8da7af fix: 解决远程数据库无法切换的问题 (#6893)
Refs #6883
2024-10-30 08:14:41 +00:00
ssongliu 79e9dc9914 fix: 解决全屏切换直接关闭后菜单不显示的问题 (#6894)
Refs #6889
2024-10-30 08:12:39 +00:00
wanghe 4849c6b42d Update README.md 2024-10-30 13:51:35 +08:00
wanghe 024d142c8d Update README.md 2024-10-28 15:58:12 +08:00
ssongliu 126aea931c fix: 解决容器编排路径为空时无法删除的问题 (#6862) 2024-10-27 15:18:02 +00:00
ssongliu 2372281d5e fix: 解决容器镜像仓库编辑失败的问题 (#6845)
Refs #6839
2024-10-25 14:11:55 +00:00
ssongliu 376d6609f2 fix: 补全进程守护 watch 引用 (#6836) 2024-10-24 15:00:18 +08:00
ssongliu 96ab4c53af fix: 解决计划任务告警误报的问题 (#6825) 2024-10-23 14:08:19 +00:00
ssongliu 96cf46d834 fix: 解决低版本 dockerd 命令不兼容的问题 (#6824) 2024-10-23 10:34:19 +00:00
ssongliu 208414704d style: 容器样式调整 (#6822)
Refs #6819
2024-10-23 09:32:19 +00:00
zhengkunwang 7f453aa145 feat: 启动运行环境增加默认网络检测 (#6820) 2024-10-23 09:08:20 +00:00
蘭 123997fa13 feat: 面板重启导致计划任务失败时进行短信告警 (#6817) 2024-10-23 03:46:19 +00:00
zhengkunwang 7faef2f8a7 feat: 创建运行环境网站增加 Python 选项 (#6815) 2024-10-23 02:52:16 +00:00
ssongliu f93cf3b996 fix: 解决快照列表排序的问题 (#6816)
Refs #6810
2024-10-23 02:46:17 +00:00
zhengkunwang df5b69756c feat: 创建运行环境网站增加默认网络创建 (#6807) 2024-10-22 11:24:34 +00:00
蘭 745bb98abe fix: 完善短信告警国际化 (#6806) 2024-10-22 11:20:32 +00:00
ssongliu cf1d02771f fix: 解决 Docker 重启失败的问题 (#6805) 2024-10-22 09:42:28 +00:00
zhengkunwang 52377b0042 fix: 解决 Python 运行环境编辑报错的问题 (#6801) 2024-10-22 08:10:28 +00:00
蘭 270501a237 fix: 文件夹选择控件,选择文件时不默认选择当前目录 (#6799)
Refs #6012
2024-10-22 07:40:29 +00:00
zhengkunwang 2e5283b8d3 fix: 解决 Mysql 特定版本忽略升级失效的问题 (#6800)
Refs https://github.com/1Panel-dev/1Panel/issues/6796
2024-10-22 07:34:29 +00:00
ssongliu 8cad47b29e feat: 修改容器操作超时时间 (#6798)
Refs #6747
2024-10-22 07:20:28 +00:00
Ludovic Fernandez 2458d60d80 fix: otel dependencies (#6793) 2024-10-22 07:39:07 +08:00
ssongliu 9a79dc4cdd feat: SSL 配置增加重启选项 (#6792) 2024-10-21 22:47:14 +08:00
Ludovic Fernandez e531b509d6 fix: huawei cloud (#6791) 2024-10-21 22:15:32 +08:00
蘭 d287fc1186 fix: 解析病毒扫描内容格式处理 (#6790) 2024-10-21 14:06:20 +00:00
zhengkunwang f47f7c7361 fix: 解决创建运行环境报名称存在的问题 (#6785) 2024-10-21 08:50:22 +00:00
zhengkunwang 7a6c99d57d fix: 解决证书自动续签日志报错的问题 (#6784) 2024-10-21 07:10:20 +00:00
zhengkunwang 5105114321 fix: 解决网站开启 HTTPS 之后 404 配置丢失的问题 (#6775) 2024-10-18 09:17:38 +00:00
zhengkunwang 60beebcb4c style: 日志样式调整 (#6774) 2024-10-18 08:55:38 +00:00
ssongliu fa03b4226a fix: 解决容器编排跳转路径错误的问题 (#6773) 2024-10-18 08:43:37 +00:00
蘭 b047a053c5 fix: 解决移动端主机文件批量删除错误 (#6765) 2024-10-18 03:29:36 +00:00
ssongliu 42907cd228 feat: postgresql 远程数据库支持 17.x 版本 (#6759) 2024-10-17 13:37:32 +00:00
蘭 425892cfc4 refactor: 调整包引用 (#6758) 2024-10-17 13:35:32 +00:00
ssongliu 16fd804e93 feat: 优化数据库切换效果 (#6757)
Refs #6755
2024-10-17 10:05:32 +00:00
John Bro 2f147dde13 fix: 容器部分操作动态修改执行命令 (#6750)
Refs #3242
2024-10-17 10:03:31 +00:00
蘭 0d04f07f2a fix: Switch 组件 small 尺寸样式修复 (#6752) 2024-10-17 07:31:30 +00:00
蘭 96485218b7 feat: 病毒扫描定时扫描和短信告警合并提示许可证绑定 (#6751) 2024-10-17 07:29:31 +00:00
蘭 227cc57426 feat: 计划任务失败告警支持全类型告警 (#6749) 2024-10-17 07:27:31 +00:00
zhengkunwang b99061777a feat: PHP 镜像 build 增加超时时间 (#6744) 2024-10-17 04:13:30 +00:00
zhengkunwang 8749dc649a style: 优化一些样式 (#6740) 2024-10-16 09:37:27 +00:00
zhengkunwang 65da51a478 style: 优化应用升级文件对比样式 (#6733) 2024-10-16 04:07:26 +00:00
蘭 0c31ba1c5f feat: 文件夹选择控件,默认选中当前目录 (#6720)
Refs #6012
2024-10-15 10:21:22 +00:00
蘭 acf94b052b feat: 增加病毒扫描和计划任务告警 (#6709) 2024-10-14 09:51:17 +00:00
蘭 556441d811 style: 解决 Alert 提示组件标题存在链接时文字错位问题 (#6711) 2024-10-14 09:41:17 +00:00
zhengkunwang efcf17e47c style: 优化登录页软件许可协议提示语样式 (#6712) 2024-10-14 09:31:18 +00:00
zhengkunwang 02b7744995 pref: 优化网站日志读取 (#6710)
Refs https://github.com/1Panel-dev/1Panel/issues/6642
2024-10-14 09:29:17 +00:00
ssongliu c86947b69c style: 修改按钮组边框宽度不一致的问题 (#6706)
Refs #6704
2024-10-14 06:41:16 +00:00
ssongliu 839b8c2844 feat: 修改容器列表应用商店容器默认显示 (#6705) 2024-10-14 06:29:16 +00:00
John Bro e4a634e6e8 fix: 容器重启前增加校验操作 (#6696) 2024-10-14 10:36:55 +08:00
wild_pointer eb99c10ad0 fix: 修复“修改文件权限时,文件所属用户和用户组不显示”问题 (#6698) 2024-10-14 10:24:48 +08:00
zzzandzengzz01 175b7b89e4 feat:firewalld和ufw同时存在时,不进行操作 (#6688)
Co-authored-by: zengzz01 <zengzz01@mingyuanyun.com>
2024-10-11 18:03:33 +08:00
zhengkunwang 5374afb226 feat: 增加华为云申请证书 (#6687)
Refs https://github.com/1Panel-dev/1Panel/issues/6047
2024-10-11 06:41:05 +00:00
John Bro 1a44f48fad fix: 日志查看统一改成网页全屏以及样式调整 (#6682)
Refs #5873
2024-10-10 10:55:02 +00:00
zhengkunwang 50dbe7cfc0 feat: 增加 Python 运行环境 (#6681) 2024-10-10 18:49:13 +08:00
ssongliu c7e576c879 fix: 概览页磁盘显示排除 overlay 类型 (#6672)
Refs #6637
2024-10-10 03:07:00 +00:00
ssongliu a3e4847744 feat: 快照列表改为异步获取大小 (#6670)
Refs #6540
2024-10-10 03:05:00 +00:00
ssongliu 903f8ed5a7 feat: 容器列表缓存是否显示应用商店容器 (#6667)
Refs #5885
2024-10-10 02:38:59 +00:00
zhengkunwang b091c7f364 feat: 反向代理文本替换增加 sub_filter_types 参数 (#6661)
Refs https://github.com/1Panel-dev/1Panel/issues/6490
2024-10-09 07:02:56 +00:00
zhengkunwang e069db3c45 feat: 反代回源 SNI 支持设置 proxy_ssl_name (#6659)
Refs https://github.com/1Panel-dev/1Panel/issues/5960
2024-10-09 06:42:56 +00:00
see-more 047264dbbc chore;删除error_code组件的body标签 (#6656) 2024-10-09 10:24:47 +08:00
ssongliu 0c216ba87a feat: 支持离线版许可证 (#6651) 2024-10-08 13:56:55 +00:00
ssongliu 31cdc28143 fix: 修改上传恢复提示信息 (#6647) 2024-10-08 02:58:52 +00:00
zhengkunwang e03b728240 feat: 自签证书支持 ipv6 (#6627)
Refs https://github.com/1Panel-dev/1Panel/issues/6623
2024-09-29 10:25:47 +00:00
ssongliu 4c275ba52f style: 修改许可证即将过期样式 (#6616) 2024-09-29 06:37:45 +00:00
ssongliu 2ac9a888a8 feat: 移除编排删除后跳转 (#6613) 2024-09-29 02:15:45 +00:00
Anye f9bdcf8938 feat: 新增 BookStack 的 PHP 扩展模板 (#6611) 2024-09-29 10:01:55 +08:00
ssongliu c3bef26ae6 feat: 调整编排删除逻辑 (#6610) 2024-09-28 22:29:08 +08:00
ssongliu 634849c347 feat: Compose 环境变量编辑增加是否 1Panel 创建校验 (#6605) 2024-09-28 01:37:40 +00:00
John Bro c557b737f6 fix: 重构容器编排环境变量设置逻辑 (#6600) 2024-09-27 14:25:37 +00:00
zhengkunwang 1d326afa93 feat: 优化日志加载 (#6593) 2024-09-26 14:37:32 +00:00
wanghe-fit2cloud cb6c58cb5d Revert "style:优化概览界面的布局 (#6491)"
This reverts commit 7f06c7e92e.
2024-09-25 10:24:33 +08:00
John Bro ec4d708569 feat: 容器编排列表增加编排目录并支持跳转 (#6565)
Refs #3579
2024-09-24 14:25:23 +00:00
John Bro 05b19cefa5 feat: 容器日志下载只支持全量下载 (#6559)
Refs #2399
2024-09-24 14:23:24 +00:00
John Broandzhoujunhong fdd57d0801 feat: 容器编排及模板列表支持排序 (#6566)
Co-authored-by: zhoujunhong <1298308460@qq.com>
2024-09-24 22:21:28 +08:00
Kataick c16d6d1644 flx: 修复会默认出现水平滚动条的问题 (#6564) 2024-09-24 16:01:05 +08:00
John Bro 656b39c880 fix: 添加国际化及样式修改 (#6555) 2024-09-23 10:11:14 +00:00
John Bro 753c996291 fix: 编排模板列表日期格式化 (#6551) 2024-09-23 06:33:15 +00:00
ssongliu dcdd0cc2bb fix: 解决服务包含 .socket 时起停失败的问题 (#6553)
Refs #6524
2024-09-23 06:31:14 +00:00
ssongliu 2f32df16d9 fix: 解决部分浏览器无法打开文件菜单的问题 (#6537) 2024-09-19 10:56:55 +00:00
John Bro 9caa9c47b4 feat: 容器编排显示容器状态,编排模板样式优化 (#6534)
Refs #4404
2024-09-19 10:54:56 +00:00
蘭 55bec8a911 fix: 解决图片/视频预览时,重命名文件名导致浏览器缓存文件内容问题 (#6533)
Refs #6505
2024-09-19 06:00:54 +00:00
John Bro 43f95fd40e fix: 创建和编辑 compose 文件支持设置环境变量 (#6503) 2024-09-18 06:00:49 +00:00
Anye 641812e7c1 fix: 调整Logo长宽比,防止显示被拉伸 (#6510)
* fix: 调整Logo长宽比,防止显示被拉伸
2024-09-17 12:52:58 +08:00
zhengkunwang 95d3e75425 feat: demo 环境增加首页状态接口过滤 (#6498) 2024-09-14 10:12:32 +00:00
see-more 7f06c7e92e style:优化概览界面的布局 (#6491)
* fix: <body> cannot be child of <div>

* style:优化概览界面的布局
2024-09-14 13:43:52 +08:00
ssongliu 0e6fdffeec feat: S3 备份账号支持 Path Style (#6489)
Refs #4553
2024-09-13 10:42:27 +00:00
endymx f77681b9c9 feat(ssl证书): 实现华为云dns (#6407) 2024-09-13 10:53:06 +08:00
John Bro 535d4bbe1b fix: 删除容器后 compose 记录保存以便后续处理 (#6447)
Refs https://github.com/1Panel-dev/1Panel/issues/5099
2024-09-12 10:06:22 +00:00
zhengkunwang 1dfbca3d5b feat: 删除多余代码 (#6463) 2024-09-11 23:10:15 +08:00
zhengkunwang d2ca265cc2 perf: 优化首页加载速度 (#6461) 2024-09-11 14:12:16 +00:00
ssongliu fc70345fcd fix: 优化移动端运行时样式 (#6460) 2024-09-11 22:02:11 +08:00
ssongliu 6ae9b982a9 fix: 优化移动端样式 (#6455) 2024-09-11 10:22:15 +00:00
蘭 0122b4c6ce fix: 解决主机文件压缩解压特殊字符问题 (#6451) 2024-09-11 08:20:15 +00:00
ssongliu 08c0faa20e feat: 优化概览页内存单位 (#6450) 2024-09-11 07:38:15 +00:00
ssongliu ab7efea8c5 style: 优化移动端样式 (#6448) 2024-09-11 07:24:15 +00:00
zhengkunwang f4623664d5 feat: 日志页面增加加载状态 (#6446) 2024-09-11 03:48:14 +00:00
ssongliu df147435c5 fix: 移除重复样式 (#6445) 2024-09-11 03:16:14 +00:00
see-more 71c1c5ae83 style:优化设置移动端界面 (#6443)
* style:优化设置移动端界面

* style:设置-安全界面样式

* style:修复设置-备份账号服务器磁盘移动端无法剧中

* style:优化移动端设置界面
2024-09-11 10:30:46 +08:00
see-more 0ea142d6db style:优化日志审计页面样式 (#6441) 2024-09-11 10:16:02 +08:00
ssongliu f88a3059be fix: 修改移动端样式 (#6440) 2024-09-10 14:04:12 +00:00
蘭 48281ddf86 fix: 主机文件解压文件 mimetype 类型增加 (#6437) 2024-09-10 14:02:11 +00:00
zhengkunwang 7cdcf9b757 feat: 增加 Intel Arc 显卡 适配 (#6435) 2024-09-10 18:55:29 +08:00
ssongliu ef0bb12fbd fix: 处理代码格式问题 (#6433) 2024-09-10 07:40:09 +00:00
蘭 a0857f86b2 fix: 主机文件打开软链接优化 (#6432) 2024-09-10 07:26:10 +00:00
wangdan-fit2cloud 2219b3f47b perf: 优化工具栏样式 (#6431)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2024-09-10 07:18:10 +00:00
蘭 b6c873f22d fix: 主机文件的路径栏路径 Bug 解决 (#6428)
Refs #6310
2024-09-10 03:46:08 +00:00
蘭 64bc92a7fd fix: 限制二进制文件解压的类型 (#6425)
Refs #6113
2024-09-10 02:14:08 +00:00
ssongliu 6c4c0f7595 fix: 解决服务器重启按钮被遮住的样式问题 (#6424) 2024-09-09 22:56:14 +08:00
蘭 2d9c977805 fix: 解决文件编辑器行号问题 (#6423)
Refs #5918

升级了编辑器版本,调整了编辑器的主题,需要整体测试一下编辑器的样式!
2024-09-09 10:08:04 +00:00
ssongliu dff0375848 fix: 解决容器端口的显示问题 (#6417) 2024-09-09 07:26:04 +00:00
ssongliu ab825a1a70 feat: 防火墙操作支持 ipv6 网段 (#6415) 2024-09-09 11:19:29 +08:00
Anye 466617847f fix: 创建CA机构生成的应为证书而非证书签名请求 (#6404) 2024-09-07 21:56:38 +08:00
蘭 a53fff2416 fix: 解决软连接被识别为文件问题 (#6400)
Refs #5879
2024-09-06 09:37:52 +00:00
zhengkunwang dd1430dbbf feat: 反向代理配置文件统一 (#6396)
Refs https://github.com/1Panel-dev/1Panel/issues/6395
2024-09-06 06:33:50 +00:00
蘭 5aad16968b perf: 优化文件管理器终端命令 (#6393)
Refs #5891
2024-09-06 03:51:50 +00:00
see-more f2d3acb650 style:更新所有移动端container路径下样式 (#6387)
* style:更新所有移动端container路径下样式

* style:修复el-button的默认margin
2024-09-06 10:00:53 +08:00
see-more 666f8c813d style:移动端文件页面样式和防火墙页面样式 (#6386)
* style:移动端端文件页面样式

* style:修改防火墙页面样式

* style:手机端ssl界面和数据库界面调整
2024-09-06 10:00:39 +08:00
ssongliu e87fb7b50e fix: 备份账号 bucket 支持手动输入 (#6381)
Refs #6378
2024-09-05 13:53:48 +00:00
蘭 75612879c7 fix: 解决部分情况下缓存目录导致启动失败的问题 (#6382) 2024-09-05 09:23:47 +00:00
蘭 94e0c80d47 refactor: 调整主机监控任务时间为整分钟执行 (#6379) 2024-09-05 08:41:48 +00:00
蘭 feb6f4cbe0 fix: 概览页获取 CPU 精度问题 (#6380)
Refs #6309
2024-09-05 08:39:48 +00:00
ssongliu 1173bbf628 fix: 解决备份恢复弹窗打开后无法关闭的问题 (#6376)
Refs #6355
2024-09-05 07:11:48 +00:00
ssongliu 1cdc2e93ad fix: 解决 FreshClam 操作失败的问题 (#6374)
Refs #6372
2024-09-05 07:09:46 +00:00
see-more 3356bd8825 fix:移动端网站页面和数据库页面样式 (#6360)
* fix:移动端网站页面和数据库页面样式

* style:修改database页面样式

* style:修改databse页面手机端搜索框与按钮间距
2024-09-05 11:46:57 +08:00
ssongliu decc7a4a1e fix: 解决停止状态容器端口获取不正确的问题 (#6370)
Refs #6333
2024-09-05 03:45:46 +00:00
zhengkunwang a397cab61c fix: 解决部分情况下缓存目录导致启动失败的问题 (#6364)
Refs https://github.com/1Panel-dev/1Panel/issues/6362
2024-09-04 09:21:43 +00:00
trom 228e3d9ae3 style:优化container界面的布局 (#6348) 2024-09-03 13:46:37 +08:00
zhengkunwang 051f670453 fix: 修复域名删除导致网站访问被 WAF 拦截的问题 (#6337)
Refs https://github.com/1Panel-dev/1Panel/issues/6336
2024-09-02 10:23:37 +00:00
trom cf8aa86a67 fix:修复文件路径bug (#6313)
* fix:修复文件路径bug

* fix:修改文件路径逻辑
2024-09-02 10:35:32 +08:00
ssongliu 5b00286e65 fix: 修改 PostgreSQL 容器内部端口 (#6275)
Refs #6274
2024-08-28 08:45:22 +00:00
ssongliu 6597caaf05 fix: 解决快照套接字忽略失败的问题 (#6273) 2024-08-28 06:41:22 +00:00
ssongliu 5851124dee fix: 解决备份套接字忽略不完的问题 (#6272) 2024-08-28 05:45:22 +00:00
trom b18689a0d6 feat:显示当前ip,系统网络代理地址 (#6251) 2024-08-27 13:39:36 +08:00
蘭 a5e7f78a1f pref: 完善告警通知任务 (#6263) 2024-08-27 13:39:18 +08:00
ssongliu a1d394fbc2 fix: 解决容器端口范围太大时,编辑打开失败的问题 (#6249)
Refs #6245
2024-08-26 08:07:13 +00:00
wanghe-fit2cloud 5e82bda666 Revert commit 3d5580a: feat:显示当前ip,系统网络代理地址 2024-08-26 15:55:07 +08:00
trom 3d5580ad9d feat:显示当前ip,系统网络代理地址 (#6235) 2024-08-26 11:25:03 +08:00
ssongliu c83cfbe8b0 fix: 解决备份导入失败的问题 (#6228) 2024-08-23 14:58:59 +00:00
蘭 00cc1b62ef pref: 完善告警通知 (#6227) 2024-08-23 13:53:00 +00:00
ssongliu e782f75863 fix: 解决容器配置修改后未重启的问题 (#6225)
Refs #6226
2024-08-23 10:24:58 +00:00
zhengkunwang f5f379465f feat: 创建运行环境网站,校验 PHP 镜像是否存在 (#6220) 2024-08-23 09:37:00 +00:00
ssongliu 70a8fda449 feat: 优化数据库备份页面样式 (#6221) 2024-08-23 17:36:21 +08:00
ssongliu ca14302191 fix: 优化部分频繁打印的日志操作 (#6219) 2024-08-23 06:40:58 +00:00
ssongliu 7297c2626f fix: 优化防火墙启动时自动添加端口功能 (#6216)
Refs #6215
2024-08-23 03:54:57 +00:00
蘭 92a3982f21 feat: 告警通知功能完善 (#6211) 2024-08-22 14:22:56 +00:00
蘭 626f5efdcd style: 优化 switch 组件 small 尺寸样式 (#6210) 2024-08-22 14:20:55 +00:00
ssongliu 24e780b984 fix: 压缩增加 *.socket 排除规则 (#6196) 2024-08-21 07:12:50 +00:00
zhengkunwang 596d4aceb6 fix: 解决删除 PHP 运行环境之后,删除其他网站提示错误的问题 (#6192) 2024-08-21 02:50:50 +00:00
蘭 e770254b65 feat: 增加短信告警功能 (#6184) 2024-08-20 22:11:13 +08:00
wanghe-fit2cloud 7901c26d32 feat: 修改 snapshot 参数 2024-08-20 18:47:49 +08:00
zhengkunwang fc65f8f689 feat: 修复代码警告 (#6190) 2024-08-20 10:36:47 +00:00
zhengkunwang e0a5e14978 feat: 修改非 PHP 运行环境网站的删除提示 (#6189) 2024-08-20 09:42:47 +00:00
zhengkunwang 07562c203a feat: 优化已安装应用的参数显示 (#6188) 2024-08-20 09:40:48 +00:00
ssongliu 8187a19e6e fix: 修改版本号复制方式 (#6187) 2024-08-20 09:38:47 +00:00
zhengkunwang 32cd684307 fix: 解决路由菜单文字丢失的问题 (#6185) 2024-08-20 06:46:46 +00:00
ssongliu 3a72fc95d9 style: 处理代码警告 (#6183) 2024-08-20 03:38:47 +00:00
ssongliu 64405fba3d fix: 统一时区获取方法 (#6175) 2024-08-19 10:30:42 +00:00
zhengkunwang cc7981229c fix: 解决 redis 没有密码情况下安装应用没有关联的问题 (#6173)
Refs #6174
2024-08-19 10:06:42 +00:00
zhengkunwang 3ec208dc85 perf: 优化运行环境日志卡顿情况 (#6172) 2024-08-19 10:04:43 +00:00
ssongliu 1b92b6cbfa fix: mysql 绑定用户增加输入校验 (#6149) 2024-08-16 06:00:31 +00:00
trom 779addfcb4 fix:mysql升级到8.4后查询会出错,容器的command回显使用 text area (#6147)
* style:容器的command参数回显应该使用 text area而不是text input,且不应该过滤'\n'

* chore:prettier格式化

* fix:mysql升级到8.4后,数据库管理 - 当前状态 查询会出错

* chore:修改逻辑判断
2024-08-16 11:23:44 +08:00
ssongliu 7da5f7a884 fix: 修改镜像拉取校验规则 (#6145)
Refs #5740
2024-08-15 15:18:32 +00:00
ssongliu 34e14636d4 fix: 解决 ClamAV 卸载后定时任务无法停止的问题 (#6143)
Refs #6100
2024-08-15 15:16:30 +00:00
ssongliu aa222aac31 fix: 解决计划任务备份内容允许为空的校验问题 (#6142)
Refs #6099
2024-08-15 09:42:28 +00:00
zhengkunwang 5134caa103 fix: 解决部分应用参数数据库显示错误的问题 (#6136) 2024-08-15 07:12:28 +00:00
zhengkunwang dca3c92d72 fix: 解决 PHP 运行环境版本过期之后下载安装包失败的问题 (#6125) 2024-08-14 10:29:59 +00:00
zhengkunwang a85d5bad46 feat: 优化 mysql 版本升级 (#6119) 2024-08-13 10:48:56 +00:00
zhengkunwang 716613a4db feat: 应用升级备份保留 3 份 (#6116)
Refs https://github.com/1Panel-dev/1Panel/issues/6107
2024-08-13 07:28:55 +00:00
zhengkunwang 12eaa66bc5 style: 去掉应用列表中的更新应用列表按钮 (#6105) 2024-08-12 18:40:10 +08:00
zhengkunwang a6af97e3c1 style: 优化文件属性页面显示 (#6104)
Refs https://github.com/1Panel-dev/1Panel/issues/5855
2024-08-12 10:02:53 +00:00
zhengkunwang d561c86c68 feat: 优化网站日志在每行数据量大情况下的读取 (#6103)
Refs https://github.com/1Panel-dev/1Panel/issues/6102
2024-08-12 09:58:52 +00:00
dependabot[bot] f243d1c9b4 chore(deps): bump github.com/docker/docker (#6093) 2024-08-12 13:43:47 +08:00
trom 1cc2cc6400 feat:更新应用列表上移 #6035 (#6084) 2024-08-12 10:26:16 +08:00
trom 15f4c05772 chore:点击复制版本号(#5894) (#6077) 2024-08-09 18:25:53 +08:00
trom c9a204b7a7 fix: 搜索框加长(#6063) 回收站时间排序(#5957) hoer状态撑高界面(#5929) (#6070) 2024-08-09 10:22:29 +08:00
trom 3d0018c0bd chore:新建文件夹支持添加空格,登录页鼠标悬浮样式 (#6058) 2024-08-08 10:27:06 +08:00
zhengkunwang d639e46bc7 fix: 解决 PHP 运行环境网站删除之后没有删除应用的问题 (#6036) 2024-08-06 03:34:29 +00:00
dependabot[bot] fa633bf419 chore(deps): bump github.com/docker/docker (#5989)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 26.0.2+incompatible to 26.1.4+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v26.0.2...v26.1.4)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-31 19:05:05 +08:00
zhengkunwang 40f7ef90b3 feat: 修改反向代理默认 Connection 请求头 (#5951)
Refs https://github.com/1Panel-dev/1Panel/issues/5937
2024-07-26 07:01:40 +00:00
zhengkunwang 50efe607d4 feat: 优化泛域名申请证书手动解析值 (#5950)
Refs https://github.com/1Panel-dev/1Panel/issues/4609
2024-07-26 06:59:40 +00:00
igophper 3b057f2c27 feat: 2FA支持前一个验证码 (#5942)
Refs #5940
2024-07-26 11:15:53 +08:00
maninhill d3d018bd44 chore: Update README.md 2024-07-24 17:01:38 +08:00
maninhill 1378609aa2 chore: Update README.md 2024-07-24 16:58:59 +08:00
zhengkunwang 001c896eed fix: 解决上传文件夹权限没改的问题 (#5919) 2024-07-23 17:52:30 +08:00
ssongliu 1cf3a0143c fix: 解决系统日志解析错误的问题 (#5917) 2024-07-23 07:57:32 +00:00
zhengkunwang f04be521e5 feat: 回滚代码 (#5914) 2024-07-23 14:42:06 +08:00
ssongliu 2873632a40 pref: 移除部分废弃代码 (#5910) 2024-07-23 02:31:32 +00:00
zhengkunwang 075879b5b5 fix: 解决应用商店 Go 和 Java 安装跳转错误的问题 (#5909)
Refs https://github.com/1Panel-dev/1Panel/issues/5900
2024-07-23 02:29:32 +00:00
zhengkunwang 9b12930376 fix: 解决安装 gitea 跳转到 postgres 报错的问题 (#5908)
Refs https://github.com/1Panel-dev/1Panel/issues/5902
2024-07-23 02:27:31 +00:00
zhengkunwang 3339ba9bad fix: 解决潜在的 SQL 注入漏洞 (#5906)
影响范围:操作日志
2024-07-22 14:41:31 +00:00
zhengkunwang 68922676fc fix: 解决从网站上传的证书状态显示异常的问题 (#5869)
Refs https://github.com/1Panel-dev/1Panel/issues/5867
2024-07-19 07:41:28 +00:00
zhengkunwang dcf2afb4a6 feat: 上传文件使用父目录用户和用户组 (#5868)
Refs https://github.com/1Panel-dev/1Panel/issues/5866
2024-07-19 07:33:28 +00:00
ssongliu 16a35141dd fix: 解决病毒定时扫描校验国际化的问题 (#5856) 2024-07-18 03:27:28 +00:00
John Bro f6c334bdad feat: 计划任务备份应用/网站/数据库支持多选 (#5849) 2024-07-17 13:35:27 +00:00
蘭 cbb119117b fix: 多个文件同时计算大小,计算状态独自显示 (#5851) 2024-07-17 11:09:30 +00:00
ssongliu cae0a6118f pref: 优化远程数据库解绑提示信息 (#5850) 2024-07-17 09:53:28 +00:00
ssongliu 3c0dc7459c feat: 工具箱病毒扫描支持定时扫描 (#5847) 2024-07-17 08:55:28 +00:00
zhengkunwang ca0c96cb12 fix: 解决网站按过期时间排序之后导致的日期控件错误问题 (#5846) 2024-07-17 06:57:29 +00:00
蘭 ed599265e4 fix: 查看文件/文件夹属性详情 (#5844)
Refs #5842

查看文件/文件夹属性详情,不再获取文件内容。
2024-07-17 06:43:27 +00:00
wanghe-fit2cloud 864bff574a chore: 删除 FUNDING.yml 2024-07-17 12:10:43 +08:00
蘭 3b1df05ae0 fix: 关闭 pdf 预览 (#5838)
pdfjs 安全漏洞,后续版本增加预览 pdf
2024-07-16 11:17:27 +00:00
蘭 464392f7e1 fix: 文本管理计算文件夹大小请求超时 (#5836)
文本管理计算文件夹大小调整为局部加载,增加请求时长
2024-07-16 07:05:27 +00:00
zhengkunwang b562d9f8e8 feat: 网站过期时间增加排序 (#5833) 2024-07-16 04:03:28 +00:00
ssongliu 74bd997f07 pref: 优化远程数据库删除提示信息 (#5824)
Refs #5819 #5818
2024-07-15 10:43:27 +00:00
ssongliu 12c6fe6331 feat: 病毒扫描增加状态判断 (#5817)
Refs #5769
2024-07-15 04:05:26 +00:00
maoball eb4d550305 fix: 修复变量名拼错 (#5792)
* fix: 修复变量名拼错

* fix: 修复变量名拼错

* fix: 修复变量名拼错

* fix: 修复变量名拼错

* fix: 修复变量名拼错
2024-07-13 21:58:02 +08:00
zhengkunwang 9a635e99b2 feat: 增加 clamdscan 服务检测 (#5784) 2024-07-12 06:11:25 +00:00
蘭 03b04eea6b feat: 文本编辑器新增右侧缩略图开关 (#5781) 2024-07-12 02:19:25 +00:00
蘭 d2740308a1 fix: 关闭抽屉同时销毁全部子元素 (#5776) 2024-07-11 13:59:24 +00:00
Anye 6f86dda1aa Add port to vscodeUrl in SSH remote connection (#5775)
修改了 vscodeUrl 模板以在主机和路径之间包含来自 addForm.port 的端口,确保正确设置 SSH 远程连接。
2024-07-11 18:45:52 +08:00
zhengkunwang 0194362a35 feat: 增加 Go 运行环境跳转 (#5764) 2024-07-11 08:21:25 +00:00
315 changed files with 9426 additions and 4459 deletions
-1
View File
@@ -1 +0,0 @@
custom: ["https://afdian.net/a/1Panel"]
+50
View File
@@ -0,0 +1,50 @@
name: 'Bug Report'
description: 'Report an Bug'
title: '[Bug] '
assignees: wanghe-fit2cloud
body:
- type: markdown
attributes:
value: "## Contact Information"
- type: input
validations:
required: false
attributes:
label: "Contact Information"
description: "The ways to quickly contact you: WeChat group number and nickname, email, etc."
- type: markdown
attributes:
value: "## Environment Information"
- type: input
validations:
required: true
attributes:
label: "1Panel Version"
description: "Log in to the 1Panel Web console and check the current version at the bottom right of the page."
- type: markdown
attributes:
value: "## Detailed information"
- type: textarea
attributes:
label: "Problem Description"
description: "Briefly describe the issue you’ve encountered."
validations:
required: true
- type: textarea
attributes:
label: "Steps to Reproduce"
description: "How can this issue be reproduced."
validations:
required: true
- type: textarea
attributes:
label: "The expected correct result"
- type: textarea
attributes:
label: "Related log output"
description: "Please paste any relevant log output here. It will automatically be formatted as code, so no backticks are necessary."
render: shell
- type: textarea
attributes:
label: "Additional Information"
description: "If you have any additional information to provide, you can include it here (screenshots, videos, etc., are welcome)."
@@ -1,61 +1,50 @@
name: BUG 提交
description: 提交产品缺陷帮助我们更好的改进
title: "[BUG]"
labels: "类型: 缺陷"
name: '反馈缺陷'
description: '反馈一个缺陷'
title: '[Bug] '
assignees: wanghe-fit2cloud
body:
- type: markdown
id: contacts_title
attributes:
value: "## 联系方式"
- type: input
id: contacts
validations:
required: false
attributes:
label: "联系方式"
description: "可以快速联系到您的方式:交流群号及昵称、邮箱等"
- type: markdown
id: environment
attributes:
value: "## 环境信息"
- type: input
id: version
validations:
required: true
attributes:
label: "1Panel 版本"
description: "登录 1Panel Web 控制台,在页面右下角查看当前版本。"
- type: markdown
id: details
attributes:
value: "## 详细信息"
- type: textarea
id: what-happened
attributes:
label: "问题描述"
description: "简要描述您碰到的问题"
validations:
required: true
- type: textarea
id: how-happened
attributes:
label: "重现步骤"
description: "如果操作可以重现该问题"
description: "如何操作可以重现该问题"
validations:
required: true
- type: textarea
id: expect
attributes:
label: "期待的正确结果"
- type: textarea
id: logs
attributes:
label: "相关日志输出"
description: "请复制并粘贴任何相关的日志输出。 这将自动格式化为代码,因此无需反引号。"
render: shell
- type: textarea
id: additional-information
attributes:
label: "附加信息"
description: "如果你还有其他需要提供的信息,可以在这里填写(可以提供截图、视频等)。"
@@ -0,0 +1,29 @@
name: 'Feature Request'
description: 'Suggest an idea'
title: '[Feature] '
assignees: wanghe-fit2cloud
body:
- type: markdown
attributes:
value: "## Environment Information"
- type: input
validations:
required: true
attributes:
label: "1Panel Version"
description: "Log in to the 1Panel Web console and check the current version at the bottom right of the page."
- type: markdown
attributes:
value: "## Detailed information"
- type: textarea
attributes:
label: "Please describe your needs or suggestions for improvements"
validations:
required: true
- type: textarea
attributes:
label: "Please describe the solution you suggest"
- type: textarea
attributes:
label: "Additional Information"
description: "If you have any additional information to provide, you can include it here (screenshots, videos, etc., are welcome)."
@@ -1,36 +1,29 @@
name: 需求建议
description: 提出针对本项目的想法和建议
title: "[FEATURE]"
labels: enhancement
name: '功能需求'
description: '提出需求或建议'
title: '[Feature] '
assignees: wanghe-fit2cloud
body:
- type: markdown
id: environment
attributes:
value: "## 环境信息"
- type: input
id: version
validations:
required: true
attributes:
label: "1Panel 版本"
description: "登录 1Panel Web 控制台,在页面右下角查看当前版本。"
- type: markdown
id: details
attributes:
value: "## 详细信息"
- type: textarea
id: description
attributes:
label: "请描述您的需求或者改进建议"
validations:
required: true
- type: textarea
id: solution
attributes:
label: "请描述你建议的实现方案"
- type: textarea
id: additional-information
attributes:
label: "附加信息"
description: "如果你还有其他需要提供的信息,可以在这里填写(可以提供截图、视频等)。"
+4 -4
View File
@@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 问题与讨论 / Questions & Discussions
- name: Questions & Discussions
url: https://github.com/1Panel-dev/1Panel/discussions
about: 提出针对本项目安装部署、使用及其他方面的相关问题 / Raise questions about the installation, deployment, use and other aspects of the project.
- name: 对 1Panel 项目有其他问题
about: Raise questions about the installation, deployment, use and other aspects of the project.
- name: 问题与讨论
url: https://bbs.fit2cloud.com/c/1p/7
about: 如果你对 1Panel 有其他想要提问的,我们欢迎到我们的官方社区进行提问。
about: 提出针对本项目安装部署、使用及其他方面的相关问题。
+28
View File
@@ -0,0 +1,28 @@
name: LLM Code Review
permissions:
contents: read
pull-requests: write
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
llm-code-review:
runs-on: ubuntu-latest
steps:
- uses: fit2cloud/LLM-CodeReview-Action@main
env:
GITHUB_TOKEN: ${{ secrets.FIT2CLOUDRD_LLM_CODE_REVIEW_TOKEN }}
OPENAI_API_KEY: ${{ secrets.ALIYUN_LLM_API_KEY }}
LANGUAGE: Chinese
OPENAI_API_ENDPOINT: https://dashscope.aliyuncs.com/compatible-mode/v1
MODEL: qwen2-1.5b-instruct
PROMPT: "请检查下面的代码差异是否有不规范、潜在的问题或者优化建议,用中文回答。"
top_p: 1
temperature: 1
# max_tokens: 10000
MAX_PATCH_LENGTH: 10000
IGNORE_PATTERNS: "/node_modules,*.md,/dist,/.github"
FILE_PATTERNS: "*.java,*.go,*.py,*.vue,*.ts,*.js,*.css,*.scss,*.html"
+4 -4
View File
@@ -24,12 +24,12 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
go-version: '1.23'
- name: Build Release
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: latest
version: '~> v2'
args: release --skip=publish --clean
- name: Upload Assets
uses: softprops/action-gh-release@v1
@@ -56,11 +56,11 @@ jobs:
dist/*.tar.gz
dist/checksums.txt
- name: Setup OSSUTIL
uses: yizhoumo/setup-ossutil@v1
uses: yizhoumo/setup-ossutil@v2
with:
endpoint: ${{ secrets.OSS_ENDPOINT }}
access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
ossutil-version: '1.7.14'
ossutil-version: '1.7.18'
- name: Upload Assets to OSS
run: ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir --force
+2 -3
View File
@@ -29,6 +29,7 @@ build/1panel
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories
/pkg/
backend/__debug_bin
@@ -40,9 +41,7 @@ frontend/auto-imports.d.ts
frontend/components.d.ts
frontend/src/xpack
backend/xpack
backend/router/entry_xpack.go
backend/server/init_xpack.go
backend/utils/xpack/xpack_xpack.go
*_xpack.go
.history/
dist/
+11 -16
View File
@@ -1,5 +1,7 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=jcroql
version: 2
before:
hooks:
# - export NODE_OPTIONS="--max-old-space-size=8192"
@@ -40,20 +42,13 @@ archives:
- install.sh
- README.md
- LICENSE
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
release:
draft: true
mode: append
extra_files:
- glob: dist/*.tar.gz
- glob: dist/checksums.txt
name_template: "Release {{.Tag}}"
# The lines beneath this are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
-3
View File
@@ -1,3 +0,0 @@
{
"ansible.python.interpreterPath": "/opt/homebrew/bin/python3"
}
+2 -4
View File
@@ -1,11 +1,9 @@
reviewers:
- wanghe-fit2cloud
- zhengkunwang223
- ssongliu
- wanghe-fit2cloud
- wangdan-fit2cloud
approvers:
- wanghe-fit2cloud
- zhengkunwang223
- ssongliu
- wanghe-fit2cloud
- wangdan-fit2cloud
+10 -4
View File
@@ -1,9 +1,10 @@
<p align="center"><a href="https://1panel.cn"><img src="http://1panel.oss-cn-hangzhou.aliyuncs.com/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Web-based linux server management control panel</b></p>
<p align="center"><b>#1 Rated Web-based Linux Server Management Tool</b></p>
<p align="center"><b>现代化、开源的 Linux 服务器运维管理面板</b></p>
<p align="center">
<a href="https://trendshift.io/repositories/2462" target="_blank"><img src="https://trendshift.io/api/badge/repositories/2462" alt="1Panel-dev%2F1Panel | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://hellogithub.com/repository/71791baf930149ac9b84e1acf186573f" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=71791baf930149ac9b84e1acf186573f&claim_uid=p8vB3kP5CMrthiL&theme=dark&theme=neutral" alt="Featured|HelloGitHub" style="width: 250px; height: 55px;" width="250" height="55" /></a>
<a href="https://trendshift.io/repositories/2462" target="_blank"><img src="https://trendshift.io/api/badge/repositories/2462" alt="1Panel-dev%2F1Panel | Trendshift" style="width: 180px; height: auto;" /></a>
<a href="https://hellogithub.com/repository/71791baf930149ac9b84e1acf186573f" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=71791baf930149ac9b84e1acf186573f&claim_uid=p8vB3kP5CMrthiL&theme=dark&theme=neutral" alt="Featured|HelloGitHub" style="width: 180px; height: auto;" /></a>
<a href="https://market.aliyun.com/products/53690006/cmjj00062740.html?userCode=kmemb8jp" target="_blank"><img src="https://img.alicdn.com/imgextra/i2/O1CN01H5JIwY1rZ0OobDjnJ_!!6000000005644-2-tps-1000-216.png" alt="1Panel-dev%2F1Panel | Aliyun" style="width: 180px; height: auto;" /></a>
</p>
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF" alt="License: GPL v3"></a>
@@ -45,13 +46,18 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
- [社区论坛](https://bbs.fit2cloud.com/c/1p/7)
- [如何加入微信交流群?](https://bbs.fit2cloud.com/t/topic/2147)
## 专业版
相比于社区版,1Panel 专业版为用户提供了大量增强功能及技术支持服务,增强功能包括 WAF 增强、网站防篡改、网站监控、GPU 监控、黑金主题及自定义界面等。 [点击查看专业版详细介绍](https://www.lxware.cn/1panel)。
## 飞致云的其他明星项目
- [MaxKB](https://github.com/1Panel-dev/MaxKB/) - 基于 LLM 大语言模型的开源知识库问答系统
- [JumpServer](https://github.com/jumpserver/jumpserver/) - 广受欢迎的开源堡垒机
- [Halo](https://github.com/halo-dev/halo/) - 强大易用的开源建站工具
- [DataEase](https://github.com/dataease/dataease/) - 人人可用的开源数据可视化分析工具
- [MeterSphere](https://github.com/metersphere/metersphere/) - 开源自动化测试平台
- [MeterSphere](https://github.com/metersphere/metersphere/) - 开源持续测试工具
- [KubePi](https://github.com/1Panel-dev/KubePi/) - 现代化、开源的 K8s 面板
## License
+1 -1
View File
@@ -155,7 +155,7 @@ func (b *BaseApi) GetIgnoredApp(c *gin.Context) {
// @Success 200 {object} model.AppInstall
// @Security ApiKeyAuth
// @Router /apps/install [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[{"input_column":"name","input_value":"name","isList":false,"db":"app_installs","output_column":"app_id","output_value":"appId"},{"info":"appId","isList":false,"db":"apps","output_column":"key","output_value":"appKey"}],"formatZH":"安装应用 [appKey]-[name]","formatEN":"Install app [appKey]-[name]"}
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"安装应用 [name]","formatEN":"Install app [name]"}
func (b *BaseApi) InstallApp(c *gin.Context) {
var req request.AppInstallCreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
+5 -3
View File
@@ -2,6 +2,7 @@ package v1
import (
"encoding/base64"
"net/http"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -120,12 +121,13 @@ func (b *BaseApi) CheckIsSafety(c *gin.Context) {
return
}
if status == "disable" && len(code) != 0 {
helper.ErrorWithDetail(c, constant.CodeErrNotFound, constant.ErrTypeInternalServer, err)
helper.ErrResponse(c, http.StatusNotFound)
return
}
if status == "unpass" {
if middleware.LoadErrCode("err-entrance") != 200 {
helper.ErrResponse(c, middleware.LoadErrCode("err-entrance"))
code := middleware.LoadErrCode()
if code != 200 {
helper.ErrResponse(c, code)
return
}
helper.ErrorWithDetail(c, constant.CodeErrEntrance, constant.ErrTypeInternalServer, nil)
+24 -2
View File
@@ -51,16 +51,38 @@ func (b *BaseApi) UpdateClam(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Clam
// @Summary Update clam status
// @Description 修改扫描规则状态
// @Accept json
// @Param request body dto.ClamUpdateStatus true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/clam/status/update [post]
// @x-panel-log {"bodyKeys":["id","status"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"clams","output_column":"name","output_value":"name"}],"formatZH":"修改扫描规则 [name] 状态为 [status]","formatEN":"change the status of clam [name] to [status]."}
func (b *BaseApi) UpdateClamStatus(c *gin.Context) {
var req dto.ClamUpdateStatus
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := clamService.UpdateStatus(req.ID, req.Status); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Clam
// @Summary Page clam
// @Description 获取扫描规则列表分页
// @Accept json
// @Param request body dto.SearchWithPage true "request"
// @Param request body dto.SearchClamWithPage true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /toolbox/clam/search [post]
func (b *BaseApi) SearchClam(c *gin.Context) {
var req dto.SearchWithPage
var req dto.SearchClamWithPage
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
+1 -1
View File
@@ -72,7 +72,7 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) {
return
}
loc, _ := time.LoadLocation(common.LoadTimeZone())
loc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
req.StartTime = req.StartTime.In(loc)
req.EndTime = req.EndTime.In(loc)
+7 -13
View File
@@ -3,6 +3,8 @@ package v1
import (
"errors"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
@@ -56,24 +58,16 @@ func (b *BaseApi) LoadDashboardBaseInfo(c *gin.Context) {
// @Summary Load dashboard current info
// @Description 获取首页实时数据
// @Accept json
// @Param ioOption path string true "request"
// @Param netOption path string true "request"
// @Param request body dto.DashboardReq true "request"
// @Success 200 {object} dto.DashboardCurrent
// @Security ApiKeyAuth
// @Router /dashboard/current/:ioOption/:netOption [get]
// @Router /dashboard/current [post]
func (b *BaseApi) LoadDashboardCurrentInfo(c *gin.Context) {
ioOption, ok := c.Params.Get("ioOption")
if !ok {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error ioOption in path"))
var req dto.DashboardReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
netOption, ok := c.Params.Get("netOption")
if !ok {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error netOption in path"))
return
}
data := dashboardService.LoadCurrentInfo(ioOption, netOption)
data := dashboardService.LoadCurrentInfo(req)
helper.SuccessWithData(c, data)
}
+25 -4
View File
@@ -11,6 +11,7 @@ import (
"path/filepath"
"strconv"
"strings"
"syscall"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -336,8 +337,12 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
return
}
mode := info.Mode()
fileOp := files.NewFileOp()
stat, ok := info.Sys().(*syscall.Stat_t)
uid, gid := -1, -1
if ok {
uid, gid = int(stat.Uid), int(stat.Gid)
}
success := 0
failures := make(buserr.MultiErr)
@@ -351,6 +356,7 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
global.LOG.Error(e)
continue
}
_ = os.Chown(dstDir, uid, gid)
}
tmpFilename := dstFilename + ".tmp"
if err := c.SaveUploadedFile(file, tmpFilename); err != nil {
@@ -378,6 +384,9 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
} else {
_ = os.Chmod(dstFilename, mode)
}
if uid != -1 && gid != -1 {
_ = os.Chown(dstFilename, uid, gid)
}
success++
}
if success == 0 {
@@ -603,9 +612,17 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int,
if mode == 0 {
mode = 0755
}
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
if err = op.CreateDir(dstDir, mode); err != nil {
return err
uid, gid := -1, -1
if info, err := os.Stat(dstDir); err != nil {
if os.IsNotExist(err) {
if err = op.CreateDir(dstDir, mode); err != nil {
return err
}
}
} else {
stat, ok := info.Sys().(*syscall.Stat_t)
if ok {
uid, gid = int(stat.Uid), int(stat.Gid)
}
}
dstFileName := filepath.Join(dstDir, fileName)
@@ -615,6 +632,7 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int,
} else {
mode = 0644
}
if overwrite {
_ = os.Remove(dstFileName)
}
@@ -635,6 +653,9 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int,
}
_ = os.Remove(chunkPath)
}
if uid != -1 && gid != -1 {
_ = os.Chown(dstFileName, uid, gid)
}
return nil
}
+1 -1
View File
@@ -28,7 +28,7 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
return
}
loc, _ := time.LoadLocation(common.LoadTimeZone())
loc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
req.StartTime = req.StartTime.In(loc)
req.EndTime = req.EndTime.In(loc)
+1 -1
View File
@@ -62,7 +62,7 @@ func (b *BaseApi) CreateRuntime(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除网站 [name]","formatEN":"Delete website [name]"}
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"删除运行环境 [name]","formatEN":"Delete runtime [name]"}
func (b *BaseApi) DeleteRuntime(c *gin.Context) {
var req request.RuntimeDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
+24 -2
View File
@@ -99,12 +99,12 @@ func (b *BaseApi) UpdateSnapDescription(c *gin.Context) {
// @Summary Page system snapshot
// @Description 获取系统快照列表分页
// @Accept json
// @Param request body dto.SearchWithPage true "request"
// @Param request body dto.PageSnapshot true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /settings/snapshot/search [post]
func (b *BaseApi) SearchSnapshot(c *gin.Context) {
var req dto.SearchWithPage
var req dto.PageSnapshot
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
@@ -120,6 +120,28 @@ func (b *BaseApi) SearchSnapshot(c *gin.Context) {
})
}
// @Tags System Setting
// @Summary Load system snapshot size
// @Description 获取系统快照大小
// @Accept json
// @Param request body dto.PageSnapshot true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/snapshot/size [post]
func (b *BaseApi) LoadSnapshotSize(c *gin.Context) {
var req dto.PageSnapshot
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := snapshotService.LoadSize(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags System Setting
// @Summary Recover system backup
// @Description 从系统快照恢复
+20
View File
@@ -0,0 +1,20 @@
package dto
type CreateOrUpdateAlert struct {
AlertTitle string `json:"alertTitle"`
AlertType string `json:"alertType"`
AlertCount uint `json:"alertCount"`
EntryID uint `json:"entryID"`
}
type AlertBase struct {
AlertType string `json:"alertType"`
EntryID uint `json:"entryID"`
}
type PushAlert struct {
TaskName string `json:"taskName"`
AlertType string `json:"alertType"`
EntryID uint `json:"entryID"`
Param string `json:"param"`
}
+1
View File
@@ -36,6 +36,7 @@ type CommonBackup struct {
Name string `json:"name"`
DetailName string `json:"detailName"`
Secret string `json:"secret"`
FileName string `json:"fileName"`
}
type CommonRecover struct {
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
+22
View File
@@ -4,6 +4,13 @@ import (
"time"
)
type SearchClamWithPage struct {
PageInfo
Info string `json:"info"`
OrderBy string `json:"orderBy" validate:"required,oneof=name status created_at"`
Order string `json:"order" validate:"required,oneof=null ascending descending"`
}
type ClamBaseInfo struct {
Version string `json:"version"`
IsActive bool `json:"isActive"`
@@ -19,11 +26,14 @@ type ClamInfo struct {
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name"`
Status string `json:"status"`
Path string `json:"path"`
InfectedStrategy string `json:"infectedStrategy"`
InfectedDir string `json:"infectedDir"`
LastHandleDate string `json:"lastHandleDate"`
Spec string `json:"spec"`
Description string `json:"description"`
AlertCount uint `json:"alertCount"`
}
type ClamLogSearch struct {
@@ -56,10 +66,14 @@ type ClamLog struct {
type ClamCreate struct {
Name string `json:"name"`
Status string `json:"status"`
Path string `json:"path"`
InfectedStrategy string `json:"infectedStrategy"`
InfectedDir string `json:"infectedDir"`
Spec string `json:"spec"`
Description string `json:"description"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
}
type ClamUpdate struct {
@@ -69,7 +83,15 @@ type ClamUpdate struct {
Path string `json:"path"`
InfectedStrategy string `json:"infectedStrategy"`
InfectedDir string `json:"infectedDir"`
Spec string `json:"spec"`
Description string `json:"description"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
}
type ClamUpdateStatus struct {
ID uint `json:"id"`
Status string `json:"status"`
}
type ClamDelete struct {
+14 -11
View File
@@ -116,7 +116,7 @@ type PortHelper struct {
type ContainerOperation struct {
Names []string `json:"names" validate:"required"`
Operation string `json:"operation" validate:"required,oneof=start stop restart kill pause unpause remove"`
Operation string `json:"operation" validate:"required,oneof=up start stop restart kill pause unpause remove"`
}
type ContainerRename struct {
@@ -200,6 +200,7 @@ type ComposeInfo struct {
Workdir string `json:"workdir"`
Path string `json:"path"`
Containers []ComposeContainer `json:"containers"`
Env []string `json:"env"`
}
type ComposeContainer struct {
ContainerID string `json:"containerID"`
@@ -208,22 +209,24 @@ type ComposeContainer struct {
State string `json:"state"`
}
type ComposeCreate struct {
Name string `json:"name"`
From string `json:"from" validate:"required,oneof=edit path template"`
File string `json:"file"`
Path string `json:"path"`
Template uint `json:"template"`
Name string `json:"name"`
From string `json:"from" validate:"required,oneof=edit path template"`
File string `json:"file"`
Path string `json:"path"`
Template uint `json:"template"`
Env []string `json:"env"`
}
type ComposeOperation struct {
Name string `json:"name" validate:"required"`
Path string `json:"path" validate:"required"`
Operation string `json:"operation" validate:"required,oneof=start stop down"`
Path string `json:"path"`
Operation string `json:"operation" validate:"required,oneof=up start stop down delete"`
WithFile bool `json:"withFile"`
}
type ComposeUpdate struct {
Name string `json:"name" validate:"required"`
Path string `json:"path" validate:"required"`
Content string `json:"content" validate:"required"`
Name string `json:"name" validate:"required"`
Path string `json:"path" validate:"required"`
Content string `json:"content" validate:"required"`
Env []string `json:"env"`
}
type ContainerLog struct {
+6
View File
@@ -31,10 +31,13 @@ type CronjobCreate struct {
DefaultDownload string `json:"defaultDownload"`
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
Secret string `json:"secret"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
}
type CronjobUpdate struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required"`
Name string `json:"name" validate:"required"`
Spec string `json:"spec" validate:"required"`
@@ -53,6 +56,8 @@ type CronjobUpdate struct {
DefaultDownload string `json:"defaultDownload"`
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
Secret string `json:"secret"`
AlertCount uint `json:"alertCount"`
AlertTitle string `json:"alertTitle"`
}
type CronjobUpdateStatus struct {
@@ -99,6 +104,7 @@ type CronjobInfo struct {
LastRecordTime string `json:"lastRecordTime"`
Status string `json:"status"`
Secret string `json:"secret"`
AlertCount uint `json:"alertCount"`
}
type SearchRecord struct {
+22 -4
View File
@@ -16,10 +16,11 @@ type DashboardBase struct {
KernelArch string `json:"kernelArch"`
KernelVersion string `json:"kernelVersion"`
VirtualizationSystem string `json:"virtualizationSystem"`
CPUCores int `json:"cpuCores"`
CPULogicalCores int `json:"cpuLogicalCores"`
CPUModelName string `json:"cpuModelName"`
IpV4Addr string `json:"ipv4Addr"`
SystemProxy string `json:"SystemProxy"`
CPUCores int `json:"cpuCores"`
CPULogicalCores int `json:"cpuLogicalCores"`
CPUModelName string `json:"cpuModelName"`
CurrentInfo DashboardCurrent `json:"currentInfo"`
}
@@ -34,6 +35,12 @@ type OsInfo struct {
DiskSize int64 `json:"diskSize"`
}
type DashboardReq struct {
Scope string `json:"scope"`
IoOption string `json:"ioOption"`
NetOption string `json:"netOption"`
}
type DashboardCurrent struct {
Uptime uint64 `json:"uptime"`
TimeSinceUptime string `json:"timeSinceUptime"`
@@ -72,6 +79,7 @@ type DashboardCurrent struct {
NetBytesRecv uint64 `json:"netBytesRecv"`
GPUData []GPUInfo `json:"gpuData"`
XPUData []XPUInfo `json:"xpuData"`
ShotTime time.Time `json:"shotTime"`
}
@@ -105,3 +113,13 @@ type GPUInfo struct {
MemTotal string `json:"memTotal"`
FanSpeed string `json:"fanSpeed"`
}
type XPUInfo struct {
DeviceID int `json:"deviceID"`
DeviceName string `json:"deviceName"`
Memory string `json:"memory"`
Temperature string `json:"temperature"`
MemoryUsed string `json:"memoryUsed"`
Power string `json:"power"`
MemoryUtil string `json:"memoryUtil"`
}
+18 -17
View File
@@ -7,7 +7,7 @@ import (
type WebsiteSearch struct {
dto.PageInfo
Name string `json:"name"`
OrderBy string `json:"orderBy" validate:"required,oneof=primary_domain type status created_at"`
OrderBy string `json:"orderBy" validate:"required,oneof=primary_domain type status created_at expire_date"`
Order string `json:"order" validate:"required,oneof=null ascending descending"`
WebsiteGroupID uint `json:"websiteGroupId"`
}
@@ -55,7 +55,7 @@ type WebsiteUpdate struct {
ID uint `json:"id" validate:"required"`
PrimaryDomain string `json:"primaryDomain" validate:"required"`
Remark string `json:"remark"`
WebsiteGroupID uint `json:"webSiteGroupID" validate:"required"`
WebsiteGroupID uint `json:"webSiteGroupID"`
ExpireDate string `json:"expireDate"`
IPV6 bool `json:"IPV6"`
}
@@ -175,21 +175,22 @@ type WebsiteUpdateDirPermission struct {
}
type WebsiteProxyConfig struct {
ID uint `json:"id" validate:"required"`
Operate string `json:"operate" validate:"required"`
Enable bool `json:"enable" `
Cache bool `json:"cache" `
CacheTime int `json:"cacheTime" `
CacheUnit string `json:"cacheUnit"`
Name string `json:"name" validate:"required"`
Modifier string `json:"modifier"`
Match string `json:"match" validate:"required"`
ProxyPass string `json:"proxyPass" validate:"required"`
ProxyHost string `json:"proxyHost" validate:"required"`
Content string `json:"content"`
FilePath string `json:"filePath"`
Replaces map[string]string `json:"replaces"`
SNI bool `json:"sni"`
ID uint `json:"id" validate:"required"`
Operate string `json:"operate" validate:"required"`
Enable bool `json:"enable" `
Cache bool `json:"cache" `
CacheTime int `json:"cacheTime" `
CacheUnit string `json:"cacheUnit"`
Name string `json:"name" validate:"required"`
Modifier string `json:"modifier"`
Match string `json:"match" validate:"required"`
ProxyPass string `json:"proxyPass" validate:"required"`
ProxyHost string `json:"proxyHost" validate:"required"`
Content string `json:"content"`
FilePath string `json:"filePath"`
Replaces map[string]string `json:"replaces"`
SNI bool `json:"sni"`
ProxySSLName string `json:"proxySSLName"`
}
type WebsiteProxyReq struct {
+1
View File
@@ -41,6 +41,7 @@ type WebsiteSSLApply struct {
ID uint `json:"ID" validate:"required"`
SkipDNSCheck bool `json:"skipDNSCheck"`
Nameservers []string `json:"nameservers"`
DisableLog bool `json:"disableLog"`
}
type WebsiteAcmeAccountCreate struct {
+5 -4
View File
@@ -36,10 +36,11 @@ type FileWgetRes struct {
}
type FileLineContent struct {
Content string `json:"content"`
End bool `json:"end"`
Path string `json:"path"`
Total int `json:"total"`
Content string `json:"content"`
End bool `json:"end"`
Path string `json:"path"`
Total int `json:"total"`
Lines []string `json:"lines"`
}
type FileExist struct {
+2
View File
@@ -30,6 +30,8 @@ type WebsiteRes struct {
RuntimeName string `json:"runtimeName"`
SSLExpireDate time.Time `json:"sslExpireDate"`
SSLStatus string `json:"sslStatus"`
AppInstallID uint `json:"appInstallId"`
RuntimeType string `json:"runtimeType"`
}
type WebsiteOption struct {
+21 -8
View File
@@ -30,6 +30,7 @@ type SettingInfo struct {
ServerPort string `json:"serverPort"`
SSL string `json:"ssl"`
SSLType string `json:"sslType"`
AutoRestart string `json:"autoRestart"`
BindDomain string `json:"bindDomain"`
AllowIPs string `json:"allowIPs"`
SecurityEntrance string `json:"securityEntrance"`
@@ -73,12 +74,13 @@ type SettingUpdate struct {
}
type SSLUpdate struct {
SSLType string `json:"sslType" validate:"required,oneof=self select import import-paste import-local"`
Domain string `json:"domain"`
SSL string `json:"ssl" validate:"required,oneof=enable disable"`
Cert string `json:"cert"`
Key string `json:"key"`
SSLID uint `json:"sslID"`
SSLType string `json:"sslType" validate:"required,oneof=self select import import-paste import-local"`
Domain string `json:"domain"`
SSL string `json:"ssl" validate:"required,oneof=enable disable"`
Cert string `json:"cert"`
Key string `json:"key"`
SSLID uint `json:"sslID"`
AutoRestart string `json:"autoRestart"`
}
type SSLInfo struct {
Domain string `json:"domain"`
@@ -98,6 +100,12 @@ type PortUpdate struct {
ServerPort uint `json:"serverPort" validate:"required,number,max=65535,min=1"`
}
type PageSnapshot struct {
PageInfo
Info string `json:"info"`
OrderBy string `json:"orderBy" validate:"required,oneof=name created_at"`
Order string `json:"order" validate:"required,oneof=null ascending descending"`
}
type SnapshotStatus struct {
Panel string `json:"panel"`
PanelInfo string `json:"panelInfo"`
@@ -141,9 +149,8 @@ type SnapshotInfo struct {
DefaultDownload string `json:"defaultDownload"`
Status string `json:"status"`
Message string `json:"message"`
CreatedAt time.Time `json:"createdAt"`
CreatedAt time.Time `json:"created_at"`
Version string `json:"version"`
Size int64 `json:"size"`
InterruptStep string `json:"interruptStep"`
RecoverStatus string `json:"recoverStatus"`
@@ -154,6 +161,12 @@ type SnapshotInfo struct {
LastRollbackedAt string `json:"lastRollbackedAt"`
}
type SnapshotFile struct {
ID uint `json:"id"`
Name string `json:"name"`
Size int64 `json:"size"`
}
type UpgradeInfo struct {
TestVersion string `json:"testVersion"`
NewVersion string `json:"newVersion"`
+3
View File
@@ -4,8 +4,11 @@ type Clam struct {
BaseModel
Name string `gorm:"type:varchar(64);not null" json:"name"`
Status string `gorm:"type:varchar(64)" json:"status"`
Path string `gorm:"type:varchar(64);not null" json:"path"`
InfectedStrategy string `gorm:"type:varchar(64)" json:"infectedStrategy"`
InfectedDir string `gorm:"type:varchar(64)" json:"infectedDir"`
Spec string `gorm:"type:varchar(64)" json:"spec"`
EntryID int `gorm:"type:varchar(64)" json:"entryID"`
Description string `gorm:"type:varchar(64)" json:"description"`
}
+1
View File
@@ -12,4 +12,5 @@ type Compose struct {
BaseModel
Name string `gorm:"type:varchar(256)" json:"name"`
Path string `gorm:"type:varchar(256)" json:"path"`
}
+7
View File
@@ -25,6 +25,7 @@ type IBackupRepo interface {
WithByFileName(fileName string) DBOption
WithByType(backupType string) DBOption
WithByCronID(cronjobID uint) DBOption
WithFileNameStartWith(filePrefix string) DBOption
}
func NewIBackupRepo() IBackupRepo {
@@ -81,6 +82,12 @@ func (u *BackupRepo) WithByFileName(fileName string) DBOption {
}
}
func (u *BackupRepo) WithFileNameStartWith(filePrefix string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("file_name LIKE ?", filePrefix+"%")
}
}
func (u *BackupRepo) WithByType(backupType string) DBOption {
return func(g *gorm.DB) *gorm.DB {
if len(backupType) == 0 {
+11
View File
@@ -13,6 +13,7 @@ type IClamRepo interface {
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
Get(opts ...DBOption) (model.Clam, error)
List(opts ...DBOption) ([]model.Clam, error)
}
func NewIClamRepo() IClamRepo {
@@ -29,6 +30,16 @@ func (u *ClamRepo) Get(opts ...DBOption) (model.Clam, error) {
return clam, err
}
func (u *ClamRepo) List(opts ...DBOption) ([]model.Clam, error) {
var clam []model.Clam
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&clam).Error
return clam, err
}
func (u *ClamRepo) Page(page, size int, opts ...DBOption) (int64, []model.Clam, error) {
var users []model.Clam
db := global.DB.Model(&model.Clam{})
+5
View File
@@ -19,6 +19,7 @@ type IComposeTemplateRepo interface {
CreateRecord(compose *model.Compose) error
DeleteRecord(opts ...DBOption) error
ListRecord() ([]model.Compose, error)
UpdateRecord(name string, vars map[string]interface{}) error
}
func NewIComposeTemplateRepo() IComposeTemplateRepo {
@@ -102,3 +103,7 @@ func (u *ComposeTemplateRepo) DeleteRecord(opts ...DBOption) error {
}
return db.Delete(&model.Compose{}).Error
}
func (u *ComposeTemplateRepo) UpdateRecord(name string, vars map[string]interface{}) error {
return global.DB.Model(&model.Compose{}).Where("name = ?", name).Updates(vars).Error
}
+1 -1
View File
@@ -96,7 +96,7 @@ func (w *WebsiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.Web
db := getDb(opts...).Model(&model.Website{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Preload("WebsiteSSL").Find(&websites).Error
err := db.Debug().Limit(size).Offset(size * (page - 1)).Preload("WebsiteSSL").Find(&websites).Error
return count, websites, err
}
-1
View File
@@ -50,7 +50,6 @@ func (w WebsiteSSLRepo) WithByCAID(caID uint) DBOption {
return db.Where("ca_id = ?", caID)
}
}
func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteSSL, error) {
var sslList []model.WebsiteSSL
db := getDb(opts...).Model(&model.WebsiteSSL{})
+8 -5
View File
@@ -91,10 +91,10 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
}
var appDTOs []*response.AppDto
for _, ap := range apps {
ap.ReadMe = ""
ap.Website = ""
ap.Document = ""
ap.Github = ""
// ap.ReadMe = ""
// ap.Website = ""
// ap.Document = ""
// ap.Github = ""
appDTO := &response.AppDto{
ID: ap.ID,
Name: ap.Name,
@@ -145,6 +145,9 @@ func (a AppService) GetAppTags() ([]response.TagDTO, error) {
func (a AppService) GetApp(key string) (*response.AppDTO, error) {
var appDTO response.AppDTO
if key == "postgres" {
key = "postgresql"
}
app, err := appRepo.GetFirst(appRepo.WithKey(key))
if err != nil {
return nil, err
@@ -185,7 +188,7 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
versionPath := filepath.Join(app.GetAppResourcePath(), detail.Version)
if !fileOp.Stat(versionPath) || detail.Update {
if err = downloadApp(app, detail, nil); err != nil {
if err = downloadApp(app, detail, nil); err != nil && !fileOp.Stat(versionPath) {
return appDetailDTO, err
}
}
+22
View File
@@ -535,6 +535,12 @@ func (a *AppInstallService) GetUpdateVersions(req request.AppUpdateVersion) ([]d
if err != nil {
return versions, err
}
if app.Key == constant.AppMysql {
majorVersion := getMajorVersion(install.Version)
if !strings.HasPrefix(detail.Version, majorVersion) {
continue
}
}
versions = append(versions, dto.AppVersion{
Version: detail.Version,
DetailId: detail.ID,
@@ -703,6 +709,22 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
}
}
appParam.Values = form.Values
} else if form.Type == "apps" {
if m, ok := form.Child.(map[string]interface{}); ok {
result := make(map[string]string)
for key, value := range m {
if strVal, ok := value.(string); ok {
result[key] = strVal
}
}
if envKey, ok := result["envKey"]; ok {
serviceName := envs[envKey]
if serviceName != nil {
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(serviceName.(string)))
appParam.ShowValue = appInstall.Name
}
}
}
}
params = append(params, appParam)
} else {
+85 -20
View File
@@ -186,17 +186,18 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
}
case constant.AppRedis:
if password, ok := params["PANEL_REDIS_ROOT_PASSWORD"]; ok {
if password != "" {
authParam := dto.RedisAuthParam{
RootPassword: password.(string),
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
authParam := dto.RedisAuthParam{
RootPassword: "",
}
database.Password = password.(string)
if password != "" {
authParam.RootPassword = password.(string)
database.Password = password.(string)
}
authByte, err := json.Marshal(authParam)
if err != nil {
return err
}
appInstall.Param = string(authByte)
}
}
if err := databaseRepo.Create(ctx, database); err != nil {
@@ -516,8 +517,19 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
)
global.LOG.Infof(i18n.GetMsgWithName("UpgradeAppStart", install.Name, nil))
if req.Backup {
backupRecord, err := NewIBackupService().AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name})
iBackUpService := NewIBackupService()
fileName := fmt.Sprintf("upgrade_backup_%s_%s.tar.gz", install.Name, time.Now().Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5))
backupRecord, err := iBackUpService.AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name, FileName: fileName})
if err == nil {
backups, _ := iBackUpService.ListAppRecords(install.App.Key, install.Name, "upgrade_backup")
if len(backups) > 3 {
backupsToDelete := backups[:len(backups)-3]
var deleteIDs []uint
for _, backup := range backupsToDelete {
deleteIDs = append(deleteIDs, backup.ID)
}
_ = iBackUpService.BatchDeleteRecord(deleteIDs)
}
localDir, err := loadLocalDir()
if err == nil {
backupFile = path.Join(localDir, backupRecord.FileDir, backupRecord.FileName)
@@ -912,18 +924,34 @@ func upApp(appInstall *model.AppInstall, pullImages bool) {
errMsg string
)
if pullImages && appInstall.App.Type != "php" {
out, err = compose.Pull(appInstall.GetComposePath())
projectName := strings.ToLower(appInstall.Name)
envByte, err := files.NewFileOp().GetContent(appInstall.GetEnvPath())
if err != nil {
if out != "" {
if strings.Contains(out, "no such host") {
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
}
if strings.Contains(out, "timeout") {
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
return err
}
images, err := composeV2.GetDockerComposeImages(projectName, envByte, []byte(appInstall.DockerCompose))
if err != nil {
return err
}
for _, image := range images {
if out, err = cmd.ExecWithTimeOut("docker pull "+image, 60*time.Minute); err != nil {
if out != "" {
if strings.Contains(out, "no such host") {
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
}
if strings.Contains(out, "timeout") {
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut") + ":"
}
} else {
if err.Error() == buserr.New(constant.ErrCmdTimeout).Error() {
errMsg = i18n.GetMsgByKey("ErrImagePullTimeOut")
} else {
errMsg = i18n.GetMsgWithMap("ErrImagePull", map[string]interface{}{"err": err.Error()})
}
}
appInstall.Message = errMsg + out
return err
}
return err
}
}
@@ -1224,6 +1252,31 @@ func synAppInstall(containers map[string]types.Container, appInstall *model.AppI
_ = appInstallRepo.Save(context.Background(), appInstall)
}
func getMajorVersion(version string) string {
parts := strings.Split(version, ".")
if len(parts) >= 2 {
return parts[0] + "." + parts[1]
}
return version
}
func ignoreUpdate(installed model.AppInstall) bool {
if installed.App.Type == "php" || installed.Status == constant.Installing {
return true
}
if installed.App.Key == constant.AppMysql {
majorVersion := getMajorVersion(installed.Version)
appDetails, _ := appDetailRepo.GetBy(appDetailRepo.WithAppId(installed.App.ID))
for _, appDetail := range appDetails {
if strings.HasPrefix(appDetail.Version, majorVersion) && common.CompareVersion(appDetail.Version, installed.Version) {
return false
}
}
return true
}
return false
}
func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool) ([]response.AppInstallDTO, error) {
var (
res []response.AppInstallDTO
@@ -1246,7 +1299,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
}
for _, installed := range appInstallList {
if updated && (installed.App.Type == "php" || installed.Status == constant.Installing || (installed.App.Key == constant.AppMysql && installed.Version == "5.6.51")) {
if updated && ignoreUpdate(installed) {
continue
}
if sync && !doNotNeedSync(installed) {
@@ -1305,6 +1358,18 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
continue
}
lastVersion := versions[0]
if app.Key == constant.AppMysql {
for _, version := range versions {
majorVersion := getMajorVersion(installed.Version)
if !strings.HasPrefix(version, majorVersion) {
continue
} else {
lastVersion = version
break
}
}
}
if common.IsCrossVersion(installed.Version, lastVersion) {
installDTO.CanUpdate = app.CrossVersionUpdate
} else {
+15
View File
@@ -40,6 +40,7 @@ type IBackupService interface {
DeleteRecordByName(backupType, name, detailName string, withDeleteFile bool) error
BatchDeleteRecord(ids []uint) error
NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error)
ListAppRecords(name, detailName, fileName string) ([]model.BackupRecord, error)
ListFiles(req dto.BackupSearchFile) []string
@@ -100,6 +101,20 @@ func (u *BackupService) SearchRecordsWithPage(search dto.RecordSearch) (int64, [
return total, datas, err
}
func (u *BackupService) ListAppRecords(name, detailName, fileName string) ([]model.BackupRecord, error) {
records, err := backupRepo.ListRecord(
commonRepo.WithOrderBy("created_at asc"),
commonRepo.WithByName(name),
commonRepo.WithByType("app"),
backupRepo.WithFileNameStartWith(fileName),
backupRepo.WithByDetailName(detailName),
)
if err != nil {
return nil, err
}
return records, err
}
func (u *BackupService) SearchRecordsByCronjobWithPage(search dto.RecordSearchByCronjob) (int64, []dto.BackupRecords, error) {
total, records, err := backupRepo.PageRecord(
search.Page, search.PageSize,
+4 -1
View File
@@ -39,7 +39,10 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) (*model.BackupRecord, er
itemDir := fmt.Sprintf("app/%s/%s", req.Name, req.DetailName)
backupDir := path.Join(localDir, itemDir)
fileName := fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
fileName := req.FileName
if req.FileName == "" {
fileName = fmt.Sprintf("%s_%s.tar.gz", req.DetailName, timeNow+common.RandStrAndNum(5))
}
if err := handleAppBackup(&install, backupDir, fileName, "", req.Secret); err != nil {
return nil, err
}
+202 -47
View File
@@ -8,17 +8,21 @@ import (
"path"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
"github.com/jinzhu/copier"
"github.com/robfig/cron/v3"
"github.com/pkg/errors"
)
@@ -37,9 +41,10 @@ type ClamService struct {
type IClamService interface {
LoadBaseInfo() (dto.ClamBaseInfo, error)
Operate(operate string) error
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
SearchWithPage(search dto.SearchClamWithPage) (int64, interface{}, error)
Create(req dto.ClamCreate) error
Update(req dto.ClamUpdate) error
UpdateStatus(id uint, status string) error
Delete(req dto.ClamDelete) error
HandleOnce(req dto.OperateByID) error
LoadFile(req dto.ClamFileReq) (string, error)
@@ -54,19 +59,19 @@ func NewIClamService() IClamService {
return &ClamService{}
}
func (f *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
func (c *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
var baseInfo dto.ClamBaseInfo
baseInfo.Version = "-"
baseInfo.FreshVersion = "-"
exist1, _ := systemctl.IsExist(clamServiceNameCentOs)
if exist1 {
f.serviceName = clamServiceNameCentOs
c.serviceName = clamServiceNameCentOs
baseInfo.IsExist = true
baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameCentOs)
}
exist2, _ := systemctl.IsExist(clamServiceNameUbuntu)
if exist2 {
f.serviceName = clamServiceNameUbuntu
c.serviceName = clamServiceNameUbuntu
baseInfo.IsExist = true
baseInfo.IsActive, _ = systemctl.IsActive(clamServiceNameUbuntu)
}
@@ -75,44 +80,47 @@ func (f *ClamService) LoadBaseInfo() (dto.ClamBaseInfo, error) {
baseInfo.FreshIsExist = true
baseInfo.FreshIsActive, _ = systemctl.IsActive(freshClamService)
}
if !cmd.Which("clamdscan") {
baseInfo.IsActive = false
}
if baseInfo.IsActive {
version, err := cmd.Exec("clamdscan --version")
if err != nil {
return baseInfo, nil
}
if strings.Contains(version, "/") {
baseInfo.Version = strings.TrimPrefix(strings.Split(version, "/")[0], "ClamAV ")
} else {
baseInfo.Version = strings.TrimPrefix(version, "ClamAV ")
if err == nil {
if strings.Contains(version, "/") {
baseInfo.Version = strings.TrimPrefix(strings.Split(version, "/")[0], "ClamAV ")
} else {
baseInfo.Version = strings.TrimPrefix(version, "ClamAV ")
}
}
} else {
_ = StopAllCronJob(false)
}
if baseInfo.FreshIsActive {
version, err := cmd.Exec("freshclam --version")
if err != nil {
return baseInfo, nil
}
if strings.Contains(version, "/") {
baseInfo.FreshVersion = strings.TrimPrefix(strings.Split(version, "/")[0], "ClamAV ")
} else {
baseInfo.FreshVersion = strings.TrimPrefix(version, "ClamAV ")
if err == nil {
if strings.Contains(version, "/") {
baseInfo.FreshVersion = strings.TrimPrefix(strings.Split(version, "/")[0], "ClamAV ")
} else {
baseInfo.FreshVersion = strings.TrimPrefix(version, "ClamAV ")
}
}
}
return baseInfo, nil
}
func (f *ClamService) Operate(operate string) error {
func (c *ClamService) Operate(operate string) error {
switch operate {
case "start", "restart", "stop":
stdout, err := cmd.Execf("systemctl %s %s", operate, f.serviceName)
stdout, err := cmd.Execf("systemctl %s %s", operate, c.serviceName)
if err != nil {
return fmt.Errorf("%s the %s failed, err: %s", operate, f.serviceName, stdout)
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, stdout)
}
return nil
case "fresh-start", "fresh-restart", "fresh-stop":
stdout, err := cmd.Execf("systemctl %s %s", strings.TrimPrefix(operate, "fresh-"), freshClamService)
if err != nil {
return fmt.Errorf("%s the %s failed, err: %s", operate, f.serviceName, stdout)
return fmt.Errorf("%s the %s failed, err: %s", operate, c.serviceName, stdout)
}
return nil
default:
@@ -120,8 +128,8 @@ func (f *ClamService) Operate(operate string) error {
}
}
func (f *ClamService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) {
total, commands, err := clamRepo.Page(req.Page, req.PageSize, commonRepo.WithLikeName(req.Info))
func (c *ClamService) SearchWithPage(req dto.SearchClamWithPage) (int64, interface{}, error) {
total, commands, err := clamRepo.Page(req.Page, req.PageSize, commonRepo.WithLikeName(req.Info), commonRepo.WithOrderRuleBy(req.OrderBy, req.Order))
if err != nil {
return 0, nil, err
}
@@ -134,7 +142,7 @@ func (f *ClamService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}
item.LastHandleDate = "-"
datas = append(datas, item)
}
nyc, _ := time.LoadLocation(common.LoadTimeZone())
nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
for i := 0; i < len(datas); i++ {
logPaths := loadFileByName(datas[i].Name)
sort.Slice(logPaths, func(i, j int) bool {
@@ -147,11 +155,21 @@ func (f *ClamService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}
}
datas[i].LastHandleDate = t1.Format(constant.DateTimeLayout)
}
alertBase := dto.AlertBase{
AlertType: "clams",
EntryID: datas[i].ID,
}
alertCount := xpack.GetAlert(alertBase)
if alertCount != 0 {
datas[i].AlertCount = alertCount
} else {
datas[i].AlertCount = 0
}
}
return total, datas, err
}
func (f *ClamService) Create(req dto.ClamCreate) error {
func (c *ClamService) Create(req dto.ClamCreate) error {
clam, _ := clamRepo.Get(commonRepo.WithByName(req.Name))
if clam.ID != 0 {
return constant.ErrRecordExist
@@ -162,13 +180,34 @@ func (f *ClamService) Create(req dto.ClamCreate) error {
if clam.InfectedStrategy == "none" || clam.InfectedStrategy == "remove" {
clam.InfectedDir = ""
}
if len(req.Spec) != 0 {
entryID, err := xpack.StartClam(clam, false)
if err != nil {
return err
}
clam.EntryID = entryID
clam.Status = constant.StatusEnable
}
if err := clamRepo.Create(&clam); err != nil {
return err
}
if req.AlertCount != 0 {
createAlert := dto.CreateOrUpdateAlert{
AlertTitle: req.AlertTitle,
AlertCount: req.AlertCount,
AlertType: "clams",
EntryID: clam.ID,
}
err := xpack.CreateAlert(createAlert)
if err != nil {
return err
}
}
return nil
}
func (f *ClamService) Update(req dto.ClamUpdate) error {
func (c *ClamService) Update(req dto.ClamUpdate) error {
clam, _ := clamRepo.Get(commonRepo.WithByName(req.Name))
if clam.ID == 0 {
return constant.ErrRecordNotFound
@@ -176,19 +215,76 @@ func (f *ClamService) Update(req dto.ClamUpdate) error {
if req.InfectedStrategy == "none" || req.InfectedStrategy == "remove" {
req.InfectedDir = ""
}
var clamItem model.Clam
if err := copier.Copy(&clamItem, &req); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
clamItem.EntryID = clam.EntryID
upMap := map[string]interface{}{}
if len(clam.Spec) != 0 && clam.EntryID != 0 {
global.Cron.Remove(cron.EntryID(clamItem.EntryID))
upMap["entry_id"] = 0
}
if len(req.Spec) == 0 {
upMap["status"] = ""
upMap["entry_id"] = 0
}
if len(req.Spec) != 0 && clam.Status != constant.StatusDisable {
newEntryID, err := xpack.StartClam(clamItem, true)
if err != nil {
return err
}
upMap["entry_id"] = newEntryID
}
if len(clam.Spec) == 0 && len(req.Spec) != 0 {
upMap["status"] = constant.StatusEnable
}
upMap["name"] = req.Name
upMap["path"] = req.Path
upMap["infected_dir"] = req.InfectedDir
upMap["infected_strategy"] = req.InfectedStrategy
upMap["spec"] = req.Spec
upMap["description"] = req.Description
if err := clamRepo.Update(req.ID, upMap); err != nil {
return err
}
updateAlert := dto.CreateOrUpdateAlert{
AlertTitle: req.AlertTitle,
AlertType: "clams",
AlertCount: req.AlertCount,
EntryID: clam.ID,
}
err := xpack.UpdateAlert(updateAlert)
if err != nil {
return err
}
return nil
}
func (u *ClamService) Delete(req dto.ClamDelete) error {
func (c *ClamService) UpdateStatus(id uint, status string) error {
clam, _ := clamRepo.Get(commonRepo.WithByID(id))
if clam.ID == 0 {
return constant.ErrRecordNotFound
}
var (
entryID int
err error
)
if status == constant.StatusEnable {
entryID, err = xpack.StartClam(clam, true)
if err != nil {
return err
}
} else {
global.Cron.Remove(cron.EntryID(clam.EntryID))
global.LOG.Infof("stop cronjob entryID: %v", clam.EntryID)
}
return clamRepo.Update(clam.ID, map[string]interface{}{"status": status, "entry_id": entryID})
}
func (c *ClamService) Delete(req dto.ClamDelete) error {
for _, id := range req.Ids {
clam, _ := clamRepo.Get(commonRepo.WithByID(id))
if clam.ID == 0 {
@@ -203,11 +299,22 @@ func (u *ClamService) Delete(req dto.ClamDelete) error {
if err := clamRepo.Delete(commonRepo.WithByID(id)); err != nil {
return err
}
alertBase := dto.AlertBase{
AlertType: "clams",
EntryID: clam.ID,
}
err := xpack.DeleteAlert(alertBase)
if err != nil {
return err
}
}
return nil
}
func (u *ClamService) HandleOnce(req dto.OperateByID) error {
func (c *ClamService) HandleOnce(req dto.OperateByID) error {
if cleaned := StopAllCronJob(true); cleaned {
return buserr.New("ErrClamdscanNotFound")
}
clam, _ := clamRepo.Get(commonRepo.WithByID(req.ID))
if clam.ID == 0 {
return constant.ErrRecordNotFound
@@ -240,14 +347,15 @@ func (u *ClamService) HandleOnce(req dto.OperateByID) error {
}
global.LOG.Debugf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
stdout, err := cmd.Execf("clamdscan --fdpass %s %s -l %s", strategy, clam.Path, logFile)
handleAlert(stdout, clam.Name, clam.ID)
if err != nil {
global.LOG.Errorf("clamdscan failed, stdout: %v, err: %v", string(stdout), err)
global.LOG.Errorf("clamdscan failed, stdout: %v, err: %v", stdout, err)
}
}()
return nil
}
func (u *ClamService) LoadRecords(req dto.ClamLogSearch) (int64, interface{}, error) {
func (c *ClamService) LoadRecords(req dto.ClamLogSearch) (int64, interface{}, error) {
clam, _ := clamRepo.Get(commonRepo.WithByID(req.ClamID))
if clam.ID == 0 {
return 0, nil, constant.ErrRecordNotFound
@@ -258,7 +366,7 @@ func (u *ClamService) LoadRecords(req dto.ClamLogSearch) (int64, interface{}, er
}
var filterFiles []string
nyc, _ := time.LoadLocation(common.LoadTimeZone())
nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
for _, item := range logPaths {
t1, err := time.ParseInLocation(constant.DateTimeSlimLayout, item, nyc)
if err != nil {
@@ -294,7 +402,7 @@ func (u *ClamService) LoadRecords(req dto.ClamLogSearch) (int64, interface{}, er
}
return int64(total), datas, nil
}
func (u *ClamService) LoadRecordLog(req dto.ClamLogReq) (string, error) {
func (c *ClamService) LoadRecordLog(req dto.ClamLogReq) (string, error) {
logPath := path.Join(global.CONF.System.DataDir, resultDir, req.ClamName, req.RecordName)
var tail string
if req.Tail != "0" {
@@ -310,7 +418,7 @@ func (u *ClamService) LoadRecordLog(req dto.ClamLogReq) (string, error) {
return string(stdout), nil
}
func (u *ClamService) CleanRecord(req dto.OperateByID) error {
func (c *ClamService) CleanRecord(req dto.OperateByID) error {
clam, _ := clamRepo.Get(commonRepo.WithByID(req.ID))
if clam.ID == 0 {
return constant.ErrRecordNotFound
@@ -320,37 +428,37 @@ func (u *ClamService) CleanRecord(req dto.OperateByID) error {
return nil
}
func (u *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {
func (c *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {
filePath := ""
switch req.Name {
case "clamd":
if u.serviceName == clamServiceNameUbuntu {
if c.serviceName == clamServiceNameUbuntu {
filePath = "/etc/clamav/clamd.conf"
} else {
filePath = "/etc/clamd.d/scan.conf"
}
case "clamd-log":
filePath = u.loadLogPath("clamd-log")
filePath = c.loadLogPath("clamd-log")
if len(filePath) != 0 {
break
}
if u.serviceName == clamServiceNameUbuntu {
if c.serviceName == clamServiceNameUbuntu {
filePath = "/var/log/clamav/clamav.log"
} else {
filePath = "/var/log/clamd.scan"
}
case "freshclam":
if u.serviceName == clamServiceNameUbuntu {
if c.serviceName == clamServiceNameUbuntu {
filePath = "/etc/clamav/freshclam.conf"
} else {
filePath = "/etc/freshclam.conf"
}
case "freshclam-log":
filePath = u.loadLogPath("freshclam-log")
filePath = c.loadLogPath("freshclam-log")
if len(filePath) != 0 {
break
}
if u.serviceName == clamServiceNameUbuntu {
if c.serviceName == clamServiceNameUbuntu {
filePath = "/var/log/clamav/freshclam.log"
} else {
filePath = "/var/log/freshclam.log"
@@ -375,12 +483,12 @@ func (u *ClamService) LoadFile(req dto.ClamFileReq) (string, error) {
return string(stdout), nil
}
func (u *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
func (c *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
filePath := ""
service := ""
switch req.Name {
case "clamd":
if u.serviceName == clamServiceNameUbuntu {
if c.serviceName == clamServiceNameUbuntu {
service = clamServiceNameUbuntu
filePath = "/etc/clamav/clamd.conf"
} else {
@@ -388,7 +496,7 @@ func (u *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
filePath = "/etc/clamd.d/scan.conf"
}
case "freshclam":
if u.serviceName == clamServiceNameUbuntu {
if c.serviceName == clamServiceNameUbuntu {
filePath = "/etc/clamav/freshclam.conf"
} else {
filePath = "/etc/freshclam.conf"
@@ -410,6 +518,29 @@ func (u *ClamService) UpdateFile(req dto.UpdateByNameAndFile) error {
return nil
}
func StopAllCronJob(withCheck bool) bool {
if withCheck {
isActive := false
exist1, _ := systemctl.IsExist(clamServiceNameCentOs)
if exist1 {
isActive, _ = systemctl.IsActive(clamServiceNameCentOs)
}
exist2, _ := systemctl.IsExist(clamServiceNameUbuntu)
if exist2 {
isActive, _ = systemctl.IsActive(clamServiceNameUbuntu)
}
if isActive {
return false
}
}
clams, _ := clamRepo.List(commonRepo.WithByStatus(constant.StatusEnable))
for i := 0; i < len(clams); i++ {
global.Cron.Remove(cron.EntryID(clams[i].EntryID))
_ = clamRepo.Update(clams[i].ID, map[string]interface{}{"status": constant.StatusDisable, "entry_id": 0})
}
return true
}
func loadFileByName(name string) []string {
var logPaths []string
pathItem := path.Join(global.CONF.System.DataDir, resultDir, name)
@@ -458,16 +589,16 @@ func loadResultFromLog(pathItem string) dto.ClamLog {
}
return data
}
func (u *ClamService) loadLogPath(name string) string {
func (c *ClamService) loadLogPath(name string) string {
confPath := ""
if name == "clamd-log" {
if u.serviceName == clamServiceNameUbuntu {
if c.serviceName == clamServiceNameUbuntu {
confPath = "/etc/clamav/clamd.conf"
} else {
confPath = "/etc/clamd.d/scan.conf"
}
} else {
if u.serviceName == clamServiceNameUbuntu {
if c.serviceName == clamServiceNameUbuntu {
confPath = "/etc/clamav/freshclam.conf"
} else {
confPath = "/etc/freshclam.conf"
@@ -497,3 +628,27 @@ func (u *ClamService) loadLogPath(name string) string {
return ""
}
func handleAlert(stdout, clamName string, clamId uint) {
if strings.Contains(stdout, "- SCAN SUMMARY -") {
lines := strings.Split(stdout, "\n")
for _, line := range lines {
if strings.HasPrefix(line, "Infected files: ") {
infectedFiles, _ := strconv.Atoi(strings.TrimPrefix(line, "Infected files: "))
if infectedFiles > 0 {
pushAlert := dto.PushAlert{
TaskName: clamName,
AlertType: "clams",
EntryID: clamId,
Param: strconv.Itoa(infectedFiles),
}
err := xpack.PushAlert(pushAlert)
if err != nil {
global.LOG.Errorf("clamdscan push failed, err: %v", err)
}
break
}
}
}
}
}
+69 -17
View File
@@ -187,7 +187,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
IsFromApp = true
}
ports := loadContainerPort(item.Ports)
exposePorts := transPortToStr(records[i].Ports)
info := dto.ContainerInfo{
ContainerID: item.ID,
CreateTime: time.Unix(item.Created, 0).Format(constant.DateTimeLayout),
@@ -196,7 +196,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
ImageName: item.Image,
State: item.State,
RunTime: item.Status,
Ports: ports,
Ports: exposePorts,
IsFromApp: IsFromApp,
IsFromCompose: IsFromCompose,
}
@@ -445,6 +445,8 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
}
}
exposePorts, _ := loadPortByInspect(oldContainer.ID, client)
data.ExposedPorts = loadContainerPortForInfo(exposePorts)
networkSettings := oldContainer.NetworkSettings
bridgeNetworkSettings := networkSettings.Networks[data.Network]
if bridgeNetworkSettings.IPAMConfig != nil {
@@ -465,19 +467,7 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
for key, val := range oldContainer.Config.Labels {
data.Labels = append(data.Labels, fmt.Sprintf("%s=%s", key, val))
}
for key, val := range oldContainer.HostConfig.PortBindings {
var itemPort dto.PortHelper
if !strings.Contains(string(key), "/") {
continue
}
itemPort.ContainerPort = strings.Split(string(key), "/")[0]
itemPort.Protocol = strings.Split(string(key), "/")[1]
for _, binds := range val {
itemPort.HostIP = binds.HostIP
itemPort.HostPort = binds.HostPort
data.ExposedPorts = append(data.ExposedPorts, itemPort)
}
}
data.AutoRemove = oldContainer.HostConfig.AutoRemove
data.Privileged = oldContainer.HostConfig.Privileged
data.PublishAllPorts = oldContainer.HostConfig.PublishAllPorts
@@ -908,6 +898,10 @@ func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) s
break
}
}
if len(containers) == 0 {
composeItem, _ := composeRepo.GetRecord(commonRepo.WithByName(req.Name))
filePath = composeItem.Path
}
}
if _, err := os.Stat(filePath); err != nil {
return ""
@@ -1201,7 +1195,7 @@ func reCreateAfterUpdate(name string, client *client.Client, config *container.C
if err := client.ContainerStart(ctx, oldContainer.ID, container.StartOptions{}); err != nil {
global.LOG.Errorf("restart after container update failed, err: %v", err)
}
global.LOG.Errorf("recreate after container update successful")
global.LOG.Info("recreate after container update successful")
}
func loadVolumeBinds(binds []types.MountPoint) []dto.VolumeHelper {
@@ -1224,7 +1218,65 @@ func loadVolumeBinds(binds []types.MountPoint) []dto.VolumeHelper {
return datas
}
func loadContainerPort(ports []types.Port) []string {
func loadPortByInspect(id string, client *client.Client) ([]types.Port, error) {
container, err := client.ContainerInspect(context.Background(), id)
if err != nil {
return nil, err
}
var itemPorts []types.Port
for key, val := range container.ContainerJSONBase.HostConfig.PortBindings {
if !strings.Contains(string(key), "/") {
continue
}
item := strings.Split(string(key), "/")
itemPort, _ := strconv.ParseUint(item[0], 10, 16)
for _, itemVal := range val {
publicPort, _ := strconv.ParseUint(itemVal.HostPort, 10, 16)
itemPorts = append(itemPorts, types.Port{PrivatePort: uint16(itemPort), Type: item[1], PublicPort: uint16(publicPort), IP: itemVal.HostIP})
}
}
return itemPorts, nil
}
func loadContainerPortForInfo(itemPorts []types.Port) []dto.PortHelper {
var exposedPorts []dto.PortHelper
samePortMap := make(map[string]dto.PortHelper)
ports := transPortToStr(itemPorts)
var itemPort dto.PortHelper
for _, item := range ports {
itemStr := strings.Split(item, "->")
if len(itemStr) < 2 {
continue
}
lastIndex := strings.LastIndex(itemStr[0], ":")
if lastIndex == -1 {
itemPort.HostPort = itemStr[0]
} else {
itemPort.HostIP = itemStr[0][0:lastIndex]
itemPort.HostPort = itemStr[0][lastIndex+1:]
}
itemContainer := strings.Split(itemStr[1], "/")
if len(itemContainer) != 2 {
continue
}
itemPort.ContainerPort = itemContainer[0]
itemPort.Protocol = itemContainer[1]
keyItem := fmt.Sprintf("%s->%s/%s", itemPort.HostPort, itemPort.ContainerPort, itemPort.Protocol)
if val, ok := samePortMap[keyItem]; ok {
val.HostIP = ""
samePortMap[keyItem] = val
} else {
samePortMap[keyItem] = itemPort
}
}
for _, val := range samePortMap {
exposedPorts = append(exposedPorts, val)
}
return exposedPorts
}
func transPortToStr(ports []types.Port) []string {
var (
ipv4Ports []types.Port
ipv6Ports []types.Port
+111 -13
View File
@@ -8,6 +8,7 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"sort"
"strings"
"time"
@@ -31,6 +32,12 @@ const composeConfigLabel = "com.docker.compose.project.config_files"
const composeWorkdirLabel = "com.docker.compose.project.working_dir"
const composeCreatedBy = "createdBy"
type DockerCompose struct {
Version string `yaml:"version"`
Services map[string]map[string]interface{} `yaml:"services"`
Networks map[string]interface{} `yaml:"networks"`
}
func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface{}, error) {
var (
records []dto.ComposeInfo
@@ -52,6 +59,20 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
}
composeCreatedByLocal, _ := composeRepo.ListRecord()
composeLocalMap := make(map[string]dto.ComposeInfo)
for _, localItem := range composeCreatedByLocal {
composeItemLocal := dto.ComposeInfo{
ContainerNumber: 0,
CreatedAt: localItem.CreatedAt.Format(constant.DateTimeLayout),
ConfigFile: localItem.Path,
Workdir: strings.TrimSuffix(localItem.Path, "/docker-compose.yml"),
}
composeItemLocal.CreatedBy = "1Panel"
composeItemLocal.Path = localItem.Path
composeLocalMap[localItem.Name] = composeItemLocal
}
composeMap := make(map[string]dto.ComposeInfo)
for _, container := range list {
if name, ok := container.Labels[composeProjectLabel]; ok {
@@ -95,12 +116,24 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
}
}
}
for _, item := range composeCreatedByLocal {
if err := composeRepo.DeleteRecord(commonRepo.WithByID(item.ID)); err != nil {
global.LOG.Error(err)
mergedMap := make(map[string]dto.ComposeInfo)
for key, localItem := range composeLocalMap {
mergedMap[key] = localItem
}
for key, item := range composeMap {
if existingItem, exists := mergedMap[key]; exists {
if item.ContainerNumber > 0 {
if existingItem.ContainerNumber <= 0 {
mergedMap[key] = item
}
}
} else {
mergedMap[key] = item
}
}
for key, value := range composeMap {
for key, value := range mergedMap {
value.Name = key
records = append(records, value)
}
@@ -127,7 +160,26 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
}
BackDatas = records[start:end]
}
return int64(total), BackDatas, nil
listItem := loadEnv(BackDatas)
return int64(total), listItem, nil
}
func loadEnv(list []dto.ComposeInfo) []dto.ComposeInfo {
for i := 0; i < len(list); i++ {
envFilePath := filepath.Join(path.Dir(list[i].Path), "1panel.env")
file, err := os.ReadFile(envFilePath)
if err != nil {
continue
}
lines := strings.Split(string(file), "\n")
for _, line := range lines {
lineItem := strings.TrimSpace(line)
if len(lineItem) != 0 && !strings.HasPrefix(lineItem, "#") {
list[i].Env = append(list[i].Env, lineItem)
}
}
}
return list
}
func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
@@ -141,6 +193,9 @@ func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
if err := u.loadPath(&req); err != nil {
return false, err
}
if err := newComposeEnv(req.Path, req.Env); err != nil {
return false, err
}
cmd := exec.Command("docker-compose", "-f", req.Path, "config")
stdout, err := cmd.CombinedOutput()
if err != nil {
@@ -173,6 +228,9 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
if err != nil {
return "", err
}
if err := newComposeEnv(req.Path, req.Env); err != nil {
return "", err
}
go func() {
defer file.Close()
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
@@ -186,7 +244,7 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
return
}
global.LOG.Infof("docker-compose up %s successful!", req.Name)
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name, Path: req.Path})
_, _ = file.WriteString("docker-compose up successful!")
}()
@@ -194,23 +252,36 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
}
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
if len(req.Path) == 0 && req.Operation == "delete" {
_ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name))
return nil
}
if cmd.CheckIllegal(req.Path, req.Operation) {
return buserr.New(constant.ErrCmdIllegal)
}
if _, err := os.Stat(req.Path); err != nil {
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
}
if stdout, err := compose.Operate(req.Path, req.Operation); err != nil {
return errors.New(string(stdout))
}
global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name)
if req.Operation == "down" {
_ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name))
if req.Operation == "delete" {
if stdout, err := compose.Operate(req.Path, "down"); err != nil {
return errors.New(string(stdout))
}
if req.WithFile {
_ = os.RemoveAll(path.Dir(req.Path))
}
_ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name))
return nil
}
if req.Operation == "up" {
if stdout, err := compose.Up(req.Path); err != nil {
return errors.New(string(stdout))
}
} else {
if stdout, err := compose.Operate(req.Path, req.Operation); err != nil {
return errors.New(string(stdout))
}
}
global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name)
return nil
}
@@ -232,6 +303,10 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
write.Flush()
global.LOG.Infof("docker-compose.yml %s has been replaced, now start to docker-compose restart", req.Path)
if err := newComposeEnv(req.Path, req.Env); err != nil {
return err
}
if stdout, err := compose.Up(req.Path); err != nil {
if err := recreateCompose(string(oldFile), req.Path); err != nil {
return fmt.Errorf("update failed when handle compose up, err: %s, recreate failed: %v", string(stdout), err)
@@ -280,3 +355,26 @@ func recreateCompose(content, path string) error {
}
return nil
}
func newComposeEnv(pathItem string, env []string) error {
if len(env) == 0 {
return nil
}
envFilePath := path.Join(path.Dir(pathItem), "1panel.env")
file, err := os.OpenFile(envFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
global.LOG.Errorf("failed to create env file: %v", err)
return err
}
defer file.Close()
for _, env := range env {
envItem := strings.TrimSpace(env)
if _, err := file.WriteString(fmt.Sprintf("%s\n", envItem)); err != nil {
global.LOG.Errorf("failed to write env to file: %v", err)
return err
}
}
global.LOG.Infof("1panel.env file successfully created or updated with env variables in %s", envFilePath)
return nil
}
+1 -1
View File
@@ -52,7 +52,7 @@ func (u *ContainerService) PageVolume(req dto.SearchWithPage) (int64, interface{
records = list.Volumes[start:end]
}
nyc, _ := time.LoadLocation(common.LoadTimeZone())
nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
for _, item := range records {
tag := make([]string, 0)
for _, val := range item.Labels {
+47 -1
View File
@@ -13,6 +13,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/robfig/cron/v3"
@@ -53,6 +54,16 @@ func (u *CronjobService) SearchWithPage(search dto.PageCronjob) (int64, interfac
} else {
item.LastRecordTime = "-"
}
alertBase := dto.AlertBase{
AlertType: cronjob.Type,
EntryID: cronjob.ID,
}
alertCount := xpack.GetAlert(alertBase)
if alertCount != 0 {
item.AlertCount = alertCount
} else {
item.AlertCount = 0
}
dtoCronjobs = append(dtoCronjobs, item)
}
return total, dtoCronjobs, err
@@ -190,6 +201,18 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
if err := cronjobRepo.Create(&cronjob); err != nil {
return err
}
if cronjobDto.AlertCount != 0 {
createAlert := dto.CreateOrUpdateAlert{
AlertTitle: cronjobDto.AlertTitle,
AlertCount: cronjobDto.AlertCount,
AlertType: cronjob.Type,
EntryID: cronjob.ID,
}
err := xpack.CreateAlert(createAlert)
if err != nil {
return err
}
}
return nil
}
@@ -232,6 +255,14 @@ func (u *CronjobService) Delete(req dto.CronjobBatchDelete) error {
if err := cronjobRepo.Delete(commonRepo.WithByID(id)); err != nil {
return err
}
alertBase := dto.AlertBase{
AlertType: cronjob.Type,
EntryID: cronjob.ID,
}
err := xpack.DeleteAlert(alertBase)
if err != nil {
return err
}
}
return nil
@@ -281,7 +312,21 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
upMap["default_download"] = req.DefaultDownload
upMap["retain_copies"] = req.RetainCopies
upMap["secret"] = req.Secret
return cronjobRepo.Update(id, upMap)
err = cronjobRepo.Update(id, upMap)
if err != nil {
return err
}
updateAlert := dto.CreateOrUpdateAlert{
AlertTitle: req.AlertTitle,
AlertType: cronModel.Type,
AlertCount: req.AlertCount,
EntryID: cronModel.ID,
}
err = xpack.UpdateAlert(updateAlert)
if err != nil {
return err
}
return nil
}
func (u *CronjobService) UpdateStatus(id uint, status string) error {
@@ -293,6 +338,7 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
entryIDs string
err error
)
if status == constant.StatusEnable {
entryIDs, err = u.StartJob(&cronjob, false)
if err != nil {
+46 -25
View File
@@ -1,6 +1,7 @@
package service
import (
"errors"
"fmt"
"os"
"path"
@@ -17,15 +18,23 @@ import (
func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) error {
var apps []model.AppInstall
if cronjob.AppID == "all" {
if strings.Contains(cronjob.AppID, "all") {
apps, _ = appInstallRepo.ListBy()
} else {
itemID, _ := strconv.Atoi(cronjob.AppID)
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
appIds := strings.Split(cronjob.AppID, ",")
var idItems []uint
for i := 0; i < len(appIds); i++ {
itemID, _ := strconv.Atoi(appIds[i])
idItems = append(idItems, uint(itemID))
}
appItems, err := appInstallRepo.ListBy(commonRepo.WithIdsIn(idItems))
if err != nil {
return err
}
apps = append(apps, app)
if len(appItems) == 0 {
return errors.New("no such app in database!")
}
apps = appItems
}
accountMap, err := loadClientMap(cronjob.BackupAccounts)
if err != nil {
@@ -60,6 +69,9 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) e
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Time) error {
webs := loadWebsForJob(cronjob)
if len(webs) == 0 {
return errors.New("no such website in database!")
}
accountMap, err := loadClientMap(cronjob.BackupAccounts)
if err != nil {
return err
@@ -93,6 +105,9 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Time) error {
dbs := loadDbsForJob(cronjob)
if len(dbs) == 0 {
return errors.New("no such db in database!")
}
accountMap, err := loadClientMap(cronjob.BackupAccounts)
if err != nil {
return err
@@ -232,7 +247,7 @@ type databaseHelper struct {
func loadDbsForJob(cronjob model.Cronjob) []databaseHelper {
var dbs []databaseHelper
if cronjob.DBName == "all" {
if strings.Contains(cronjob.DBName, "all") {
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
mysqlItems, _ := mysqlRepo.List()
for _, mysql := range mysqlItems {
@@ -254,36 +269,42 @@ func loadDbsForJob(cronjob model.Cronjob) []databaseHelper {
}
return dbs
}
itemID, _ := strconv.Atoi(cronjob.DBName)
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
mysqlItem, _ := mysqlRepo.Get(commonRepo.WithByID(uint(itemID)))
dbs = append(dbs, databaseHelper{
DBType: cronjob.DBType,
Database: mysqlItem.MysqlName,
Name: mysqlItem.Name,
})
} else {
pgItem, _ := postgresqlRepo.Get(commonRepo.WithByID(uint(itemID)))
dbs = append(dbs, databaseHelper{
DBType: cronjob.DBType,
Database: pgItem.PostgresqlName,
Name: pgItem.Name,
})
dbNames := strings.Split(cronjob.DBName, ",")
for _, name := range dbNames {
itemID, _ := strconv.Atoi(name)
if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" {
mysqlItem, _ := mysqlRepo.Get(commonRepo.WithByID(uint(itemID)))
dbs = append(dbs, databaseHelper{
DBType: cronjob.DBType,
Database: mysqlItem.MysqlName,
Name: mysqlItem.Name,
})
} else {
pgItem, _ := postgresqlRepo.Get(commonRepo.WithByID(uint(itemID)))
dbs = append(dbs, databaseHelper{
DBType: cronjob.DBType,
Database: pgItem.PostgresqlName,
Name: pgItem.Name,
})
}
}
return dbs
}
func loadWebsForJob(cronjob model.Cronjob) []model.Website {
var weblist []model.Website
if cronjob.Website == "all" {
if strings.Contains(cronjob.Website, "all") {
weblist, _ = websiteRepo.List()
return weblist
}
itemID, _ := strconv.Atoi(cronjob.Website)
webItem, _ := websiteRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
if webItem.ID != 0 {
weblist = append(weblist, webItem)
websites := strings.Split(cronjob.Website, ",")
var idItems []uint
for i := 0; i < len(websites); i++ {
itemID, _ := strconv.Atoi(websites[i])
idItems = append(idItems, uint(itemID))
}
weblist, _ = websiteRepo.List(commonRepo.WithIdsIn(idItems))
return weblist
}
+24 -7
View File
@@ -5,12 +5,14 @@ import (
"fmt"
"os"
"path"
pathUtils "path"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -19,6 +21,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
"github.com/pkg/errors"
)
@@ -84,12 +87,12 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
err = u.handleSnapshot(*cronjob, record.StartTime, record.Records)
}
if err != nil {
if len(message) != 0 {
record.Records, _ = mkdirAndWriteFile(cronjob, record.StartTime, message)
}
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), record.Records)
handleCronJobAlert(cronjob)
return
}
if len(message) != 0 {
@@ -139,7 +142,6 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string, secret string)
excludes := strings.Split(exclusionRules, ",")
excludeRules := ""
excludes = append(excludes, "*.sock")
for _, exclude := range excludes {
if len(exclude) == 0 {
continue
@@ -162,10 +164,14 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string, secret string)
if len(secret) != 0 {
extraCmd := "| openssl enc -aes-256-cbc -salt -k '" + secret + "' -out"
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s %s %s", " -"+excludeRules, path, extraCmd, targetDir+"/"+name)
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read --exclude-from=<(find %s -type s -print) -zcf %s %s %s %s", sourceDir, " -"+excludeRules, path, extraCmd, targetDir+"/"+name)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
} else {
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s %s", targetDir+"/"+name, excludeRules, path)
itemPrefix := pathUtils.Base(sourceDir)
if itemPrefix == "/" {
itemPrefix = ""
}
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read --exclude-from=<(find %s -type s -printf '%s' | sed 's|^|%s/|') -zcf %s %s %s", sourceDir, "%P\n", itemPrefix, targetDir+"/"+name, excludeRules, path)
global.LOG.Debug(commands)
}
stdout, err := cmd.ExecWithTimeOut(commands, 24*time.Hour)
@@ -239,7 +245,6 @@ func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime t
_ = fileOp.WriteFile(srcErrorLogPath, strings.NewReader(""), 0755)
}
msg := i18n.GetMsgWithMap("CutWebsiteLogSuccess", map[string]interface{}{"name": website.PrimaryDomain, "path": dstFilePath})
global.LOG.Infof(msg)
msgs = append(msgs, msg)
}
u.removeExpiredLog(*cronjob)
@@ -326,7 +331,6 @@ func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, accountMap
}
func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap map[string]cronjobUploadHelper, record model.BackupRecord) {
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
var opts []repo.DBOption
opts = append(opts, commonRepo.WithByFrom("cronjob"))
opts = append(opts, backupRepo.WithByCronID(cronjob.ID))
@@ -361,7 +365,6 @@ func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap m
}
func (u *CronjobService) removeExpiredLog(cronjob model.Cronjob) {
global.LOG.Infof("start to handle remove expired, retain copies: %d", cronjob.RetainCopies)
records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), commonRepo.WithOrderBy("created_at desc"))
if len(records) <= int(cronjob.RetainCopies) {
return
@@ -391,3 +394,17 @@ func (u *CronjobService) generateLogsPath(cronjob model.Cronjob, startTime time.
func hasBackup(cronjobType string) bool {
return cronjobType == "app" || cronjobType == "database" || cronjobType == "website" || cronjobType == "directory" || cronjobType == "snapshot" || cronjobType == "log"
}
func handleCronJobAlert(cronjob *model.Cronjob) {
pushAlert := dto.PushAlert{
TaskName: cronjob.Name,
AlertType: cronjob.Type,
EntryID: cronjob.ID,
Param: cronjob.Type,
}
err := xpack.PushAlert(pushAlert)
if err != nil {
global.LOG.Errorf("cronjob alert push failed, err: %v", err)
return
}
}
+120 -70
View File
@@ -3,13 +3,19 @@ package service
import (
"encoding/json"
"fmt"
network "net"
"os"
"sort"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/copier"
@@ -17,9 +23,6 @@ import (
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
)
type DashboardService struct{}
@@ -27,7 +30,7 @@ type DashboardService struct{}
type IDashboardService interface {
LoadOsInfo() (*dto.OsInfo, error)
LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error)
LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
LoadCurrentInfo(req dto.DashboardReq) *dto.DashboardCurrent
Restart(operation string) error
}
@@ -94,11 +97,20 @@ func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto
baseInfo.KernelVersion = hostInfo.KernelVersion
ss, _ := json.Marshal(hostInfo)
baseInfo.VirtualizationSystem = string(ss)
baseInfo.IpV4Addr = GetOutboundIP()
httpProxy := os.Getenv("http_proxy")
if httpProxy == "" {
httpProxy = os.Getenv("HTTP_PROXY")
}
if httpProxy != "" {
baseInfo.SystemProxy = httpProxy
}
baseInfo.SystemProxy = "noProxy"
appInstall, err := appInstallRepo.ListBy()
if err != nil {
return nil, err
}
baseInfo.AppInstalledNumber = len(appInstall)
postgresqlDbs, err := postgresqlRepo.List()
if err != nil {
@@ -128,78 +140,88 @@ func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto
baseInfo.CPUCores, _ = cpu.Counts(false)
baseInfo.CPULogicalCores, _ = cpu.Counts(true)
baseInfo.CurrentInfo = *u.LoadCurrentInfo(ioOption, netOption)
baseInfo.CurrentInfo = *u.LoadCurrentInfo(dto.DashboardReq{
Scope: "ioNet",
IoOption: ioOption,
NetOption: netOption,
})
return &baseInfo, nil
}
func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent {
func (u *DashboardService) LoadCurrentInfo(req dto.DashboardReq) *dto.DashboardCurrent {
var currentInfo dto.DashboardCurrent
if req.Scope == "gpu" {
currentInfo.GPUData = loadGPUInfo()
currentInfo.XPUData = loadXpuInfo()
}
hostInfo, _ := host.Info()
currentInfo.Uptime = hostInfo.Uptime
currentInfo.TimeSinceUptime = time.Now().Add(-time.Duration(hostInfo.Uptime) * time.Second).Format(constant.DateTimeLayout)
currentInfo.Procs = hostInfo.Procs
currentInfo.CPUTotal, _ = cpu.Counts(true)
totalPercent, _ := cpu.Percent(0, false)
if len(totalPercent) == 1 {
currentInfo.CPUUsedPercent = totalPercent[0]
currentInfo.CPUUsed = currentInfo.CPUUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
}
currentInfo.CPUPercent, _ = cpu.Percent(0, true)
loadInfo, _ := load.Avg()
currentInfo.Load1 = loadInfo.Load1
currentInfo.Load5 = loadInfo.Load5
currentInfo.Load15 = loadInfo.Load15
currentInfo.LoadUsagePercent = loadInfo.Load1 / (float64(currentInfo.CPUTotal*2) * 0.75) * 100
memoryInfo, _ := mem.VirtualMemory()
currentInfo.MemoryTotal = memoryInfo.Total
currentInfo.MemoryAvailable = memoryInfo.Available
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
swapInfo, _ := mem.SwapMemory()
currentInfo.SwapMemoryTotal = swapInfo.Total
currentInfo.SwapMemoryAvailable = swapInfo.Free
currentInfo.SwapMemoryUsed = swapInfo.Used
currentInfo.SwapMemoryUsedPercent = swapInfo.UsedPercent
currentInfo.DiskData = loadDiskInfo()
currentInfo.GPUData = loadGPUInfo()
if ioOption == "all" {
diskInfo, _ := disk.IOCounters()
for _, state := range diskInfo {
currentInfo.IOReadBytes += state.ReadBytes
currentInfo.IOWriteBytes += state.WriteBytes
currentInfo.IOCount += (state.ReadCount + state.WriteCount)
currentInfo.IOReadTime += state.ReadTime
currentInfo.IOWriteTime += state.WriteTime
}
} else {
diskInfo, _ := disk.IOCounters(ioOption)
for _, state := range diskInfo {
currentInfo.IOReadBytes += state.ReadBytes
currentInfo.IOWriteBytes += state.WriteBytes
currentInfo.IOCount += (state.ReadCount + state.WriteCount)
currentInfo.IOReadTime += state.ReadTime
currentInfo.IOWriteTime += state.WriteTime
if req.Scope == "basic" {
currentInfo.TimeSinceUptime = time.Now().Add(-time.Duration(hostInfo.Uptime) * time.Second).Format(constant.DateTimeLayout)
currentInfo.Procs = hostInfo.Procs
currentInfo.CPUTotal, _ = cpu.Counts(true)
totalPercent, _ := cpu.Percent(100*time.Millisecond, false)
if len(totalPercent) == 1 {
currentInfo.CPUUsedPercent = totalPercent[0]
currentInfo.CPUUsed = currentInfo.CPUUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
}
currentInfo.CPUPercent, _ = cpu.Percent(100*time.Millisecond, true)
loadInfo, _ := load.Avg()
currentInfo.Load1 = loadInfo.Load1
currentInfo.Load5 = loadInfo.Load5
currentInfo.Load15 = loadInfo.Load15
currentInfo.LoadUsagePercent = loadInfo.Load1 / (float64(currentInfo.CPUTotal*2) * 0.75) * 100
memoryInfo, _ := mem.VirtualMemory()
currentInfo.MemoryTotal = memoryInfo.Total
currentInfo.MemoryAvailable = memoryInfo.Available
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
swapInfo, _ := mem.SwapMemory()
currentInfo.SwapMemoryTotal = swapInfo.Total
currentInfo.SwapMemoryAvailable = swapInfo.Free
currentInfo.SwapMemoryUsed = swapInfo.Used
currentInfo.SwapMemoryUsedPercent = swapInfo.UsedPercent
currentInfo.DiskData = loadDiskInfo()
}
if netOption == "all" {
netInfo, _ := net.IOCounters(false)
if len(netInfo) != 0 {
currentInfo.NetBytesSent = netInfo[0].BytesSent
currentInfo.NetBytesRecv = netInfo[0].BytesRecv
if req.Scope == "ioNet" {
if req.IoOption == "all" {
diskInfo, _ := disk.IOCounters()
for _, state := range diskInfo {
currentInfo.IOReadBytes += state.ReadBytes
currentInfo.IOWriteBytes += state.WriteBytes
currentInfo.IOCount += (state.ReadCount + state.WriteCount)
currentInfo.IOReadTime += state.ReadTime
currentInfo.IOWriteTime += state.WriteTime
}
} else {
diskInfo, _ := disk.IOCounters(req.IoOption)
for _, state := range diskInfo {
currentInfo.IOReadBytes += state.ReadBytes
currentInfo.IOWriteBytes += state.WriteBytes
currentInfo.IOCount += (state.ReadCount + state.WriteCount)
currentInfo.IOReadTime += state.ReadTime
currentInfo.IOWriteTime += state.WriteTime
}
}
} else {
netInfo, _ := net.IOCounters(true)
for _, state := range netInfo {
if state.Name == netOption {
currentInfo.NetBytesSent = state.BytesSent
currentInfo.NetBytesRecv = state.BytesRecv
if req.NetOption == "all" {
netInfo, _ := net.IOCounters(false)
if len(netInfo) != 0 {
currentInfo.NetBytesSent = netInfo[0].BytesSent
currentInfo.NetBytesRecv = netInfo[0].BytesRecv
}
} else {
netInfo, _ := net.IOCounters(true)
for _, state := range netInfo {
if state.Name == req.NetOption {
currentInfo.NetBytesSent = state.BytesSent
currentInfo.NetBytesRecv = state.BytesRecv
}
}
}
}
@@ -208,6 +230,18 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
return &currentInfo
}
func GetOutboundIP() string {
conn, err := network.Dial("udp", "8.8.8.8:80")
if err != nil {
return "IPNotFound"
}
defer conn.Close()
localAddr := conn.LocalAddr().(*network.UDPAddr)
return localAddr.IP.String()
}
type diskInfo struct {
Type string
Mount string
@@ -235,13 +269,13 @@ func loadDiskInfo() []dto.DiskInfo {
if strings.HasPrefix(fields[6], "/snap") || len(strings.Split(fields[6], "/")) > 10 {
continue
}
if strings.TrimSpace(fields[1]) == "tmpfs" {
if strings.TrimSpace(fields[1]) == "tmpfs" || strings.TrimSpace(fields[1]) == "overlay" {
continue
}
if strings.Contains(fields[2], "K") {
continue
}
if strings.Contains(fields[6], "docker") {
if strings.Contains(fields[6], "docker") || strings.Contains(fields[6], "podman") || strings.Contains(fields[6], "containerd") || strings.HasPrefix(fields[6], "/var/lib/containers") {
continue
}
isExclude := false
@@ -323,3 +357,19 @@ func loadGPUInfo() []dto.GPUInfo {
}
return data
}
func loadXpuInfo() []dto.XPUInfo {
list := xpack.LoadXpuInfo()
if len(list) == 0 {
return nil
}
var data []dto.XPUInfo
for _, gpu := range list {
var dataItem dto.XPUInfo
if err := copier.Copy(&dataItem, &gpu); err != nil {
continue
}
data = append(data, dataItem)
}
return data
}
+8 -1
View File
@@ -141,6 +141,10 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
}
func (u *MysqlService) BindUser(req dto.BindUser) error {
if cmd.CheckIllegal(req.Username, req.Password, req.Permission) {
return buserr.New(constant.ErrCmdIllegal)
}
dbItem, err := mysqlRepo.Get(mysqlRepo.WithByMysqlName(req.Database), commonRepo.WithByName(req.DB))
if err != nil {
return err
@@ -533,7 +537,10 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
info.Position = "OFF"
rows, err := executeSqlForRows(app.ContainerName, app.Key, app.Password, "show master status;")
if err != nil {
return nil, err
rows, err = executeSqlForRows(app.ContainerName, app.Key, app.Password, "SHOW BINARY LOG STATUS;")
if err != nil {
return nil, err
}
}
if len(rows) > 2 {
itemValue := strings.Split(rows[1], "\t")
+110 -17
View File
@@ -4,14 +4,17 @@ import (
"bufio"
"context"
"encoding/json"
"fmt"
"os"
"path"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
"github.com/pkg/errors"
)
@@ -195,9 +198,29 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
daemonMap["exec-opts"] = []string{"native.cgroupdriver=systemd"}
}
}
case "http-proxy", "https-proxy":
delete(daemonMap, "proxies")
if len(req.Value) > 0 {
proxies := map[string]interface{}{
req.Key: req.Value,
}
daemonMap["proxies"] = proxies
}
case "socks5-proxy", "close-proxy":
delete(daemonMap, "proxies")
if len(req.Value) > 0 {
proxies := map[string]interface{}{
"http-proxy": req.Value,
"https-proxy": req.Value,
}
daemonMap["proxies"] = proxies
}
}
if len(daemonMap) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
if err := restartDocker(); err != nil {
return err
}
return nil
}
newJson, err := json.MarshalIndent(daemonMap, "", "\t")
@@ -207,10 +230,12 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
if err := os.WriteFile(constant.DaemonJsonPath, newJson, 0640); err != nil {
return err
}
if err := validateDockerConfig(); err != nil {
return err
}
stdout, err := cmd.Exec("systemctl restart docker")
if err != nil {
return errors.New(string(stdout))
if err := restartDocker(); err != nil {
return err
}
return nil
}
@@ -254,9 +279,12 @@ func (u *DockerService) UpdateLogOption(req dto.LogOption) error {
return err
}
stdout, err := cmd.Exec("systemctl restart docker")
if err != nil {
return errors.New(string(stdout))
if err := validateDockerConfig(); err != nil {
return err
}
if err := restartDocker(); err != nil {
return err
}
return nil
}
@@ -294,9 +322,12 @@ func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error {
return err
}
stdout, err := cmd.Exec("systemctl restart docker")
if err != nil {
return errors.New(string(stdout))
if err := validateDockerConfig(); err != nil {
return err
}
if err := restartDocker(); err != nil {
return err
}
return nil
}
@@ -304,6 +335,9 @@ func (u *DockerService) UpdateIpv6Option(req dto.Ipv6Option) error {
func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
if len(req.File) == 0 {
_ = os.Remove(constant.DaemonJsonPath)
if err := restartDocker(); err != nil {
return err
}
return nil
}
err := createIfNotExistDaemonJsonFile()
@@ -319,21 +353,42 @@ func (u *DockerService) UpdateConfByFile(req dto.DaemonJsonUpdateByFile) error {
_, _ = write.WriteString(req.File)
write.Flush()
stdout, err := cmd.Exec("systemctl restart docker")
if err != nil {
return errors.New(string(stdout))
if err := validateDockerConfig(); err != nil {
return err
}
if err := restartDocker(); err != nil {
return err
}
return nil
}
func (u *DockerService) OperateDocker(req dto.DockerOperation) error {
service := "docker"
if req.Operation == "stop" {
service = "docker.socket"
}
stdout, err := cmd.Execf("systemctl %s %s ", req.Operation, service)
sudo := cmd.SudoHandleCmd()
dockerCmd, err := getDockerRestartCommand()
if err != nil {
return errors.New(string(stdout))
return err
}
if req.Operation == "stop" {
isSocketActive, _ := systemctl.IsActive("docker.socket")
if isSocketActive {
std, err := cmd.Execf("%s systemctl stop docker.socket", sudo)
if err != nil {
global.LOG.Errorf("handle systemctl stop docker.socket failed, err: %v", std)
}
}
}
if req.Operation == "restart" {
if err := validateDockerConfig(); err != nil {
return err
}
}
stdout, err := cmd.Execf("%s %s %s", dockerCmd, req.Operation, service)
if err != nil {
return errors.New(stdout)
}
return nil
}
@@ -386,3 +441,41 @@ func changeLogOption(daemonMap map[string]interface{}, logMaxFile, logMaxSize st
}
}
}
func validateDockerConfig() error {
if !cmd.Which("dockerd") {
return nil
}
stdout, err := cmd.Exec("dockerd --validate")
if strings.Contains(stdout, "unknown flag: --validate") {
return nil
}
if err != nil || (stdout != "" && strings.TrimSpace(stdout) != "configuration OK") {
return fmt.Errorf("Docker configuration validation failed, err: %v", stdout)
}
return nil
}
func getDockerRestartCommand() (string, error) {
stdout, err := cmd.Exec("which docker")
if err != nil {
return "", fmt.Errorf("failed to find docker: %v", err)
}
dockerPath := stdout
if strings.Contains(dockerPath, "snap") {
return "snap", nil
}
return "systemctl", nil
}
func restartDocker() error {
restartCmd, err := getDockerRestartCommand()
if err != nil {
return err
}
stdout, err := cmd.Execf("%s restart docker", restartCmd)
if err != nil {
return fmt.Errorf("failed to restart Docker: %s", stdout)
}
return nil
}
+24 -5
View File
@@ -62,9 +62,13 @@ func NewIFileService() IFileService {
func (f *FileService) GetFileList(op request.FileOption) (response.FileInfo, error) {
var fileInfo response.FileInfo
if _, err := os.Stat(op.Path); err != nil && os.IsNotExist(err) {
data, err := os.Stat(op.Path)
if err != nil && os.IsNotExist(err) {
return fileInfo, nil
}
if !data.IsDir() {
op.FileOption.Path = filepath.Dir(op.FileOption.Path)
}
info, err := files.NewFileInfo(op.FileOption)
if err != nil {
return fileInfo, err
@@ -141,7 +145,7 @@ func shouldFilterPath(path string) bool {
func (f *FileService) buildFileTree(node *response.FileTree, items []*files.FileInfo, op request.FileOption, level int) error {
for _, v := range items {
if shouldFilterPath(v.Path) {
global.LOG.Info("File Tree: Skipping %s due to filter\n", v.Path)
global.LOG.Infof("File Tree: Skipping %s due to filter\n", v.Path)
continue
}
childNode := response.FileTree{
@@ -167,7 +171,7 @@ func (f *FileService) buildChildNode(childNode *response.FileTree, fileInfo *fil
subInfo, err := files.NewFileInfo(op.FileOption)
if err != nil {
if os.IsPermission(err) || errors.Is(err, unix.EACCES) {
global.LOG.Info("File Tree: Skipping %s due to permission denied\n", fileInfo.Path)
global.LOG.Infof("File Tree: Skipping %s due to permission denied\n", fileInfo.Path)
return nil
}
global.LOG.Errorf("File Tree: Skipping %s due to error: %s\n", fileInfo.Path, err.Error())
@@ -183,7 +187,7 @@ func (f *FileService) Create(op request.FileCreate) error {
}
fo := files.NewFileOp()
if fo.Stat(op.Path) {
return buserr.New(constant.ErrFileIsExit)
return buserr.New(constant.ErrFileIsExist)
}
mode := op.Mode
if mode == 0 {
@@ -207,6 +211,12 @@ func (f *FileService) Create(op request.FileCreate) error {
}
func (f *FileService) Delete(op request.FileDelete) error {
if op.IsDir {
excludeDir := global.CONF.System.DataDir
if filepath.Base(op.Path) == ".1panel_clash" || op.Path == excludeDir {
return buserr.New(constant.ErrPathNotDelete)
}
}
fo := files.NewFileOp()
recycleBinStatus, _ := settingRepo.Get(settingRepo.WithByKey("FileRecycleBin"))
if recycleBinStatus.Value == "disable" {
@@ -273,7 +283,7 @@ func (f *FileService) ChangeOwner(req request.FileRoleUpdate) error {
func (f *FileService) Compress(c request.FileCompress) error {
fo := files.NewFileOp()
if !c.Replace && fo.Stat(filepath.Join(c.Dst, c.Name)) {
return buserr.New(constant.ErrFileIsExit)
return buserr.New(constant.ErrFileIsExist)
}
return fo.Compress(c.Files, c.Dst, c.Name, files.CompressType(c.Type), c.Secret)
}
@@ -474,11 +484,20 @@ func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.Fi
if err != nil {
return nil, err
}
if req.Latest && req.Page == 1 && len(lines) < 1000 && total > 1 {
preLines, _, _, err := files.ReadFileByLine(logFilePath, total-1, req.PageSize, false)
if err != nil {
return nil, err
}
lines = append(preLines, lines...)
}
res := &response.FileLineContent{
Content: strings.Join(lines, "\n"),
End: isEndOfFile,
Path: logFilePath,
Total: total,
Lines: lines,
}
return res, nil
}
+76 -20
View File
@@ -10,6 +10,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
@@ -86,6 +87,11 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
case "port":
rules, err = client.ListPort()
case "forward":
isSupport, errSup := checkIsSupport()
if !isSupport {
return 0, nil, errSup
}
rules, err = client.ListForward()
case "address":
rules, err = client.ListAddress()
@@ -179,6 +185,7 @@ func (u *FirewallService) OperateFirewall(operation string) error {
if err != nil {
return err
}
needRestartDocker := false
switch operation {
case "start":
if err := client.Start(); err != nil {
@@ -188,26 +195,30 @@ func (u *FirewallService) OperateFirewall(operation string) error {
_ = client.Stop()
return err
}
_, _ = cmd.Exec("systemctl restart docker")
return nil
needRestartDocker = true
case "stop":
if err := client.Stop(); err != nil {
return err
}
_, _ = cmd.Exec("systemctl restart docker")
return nil
needRestartDocker = true
case "restart":
if err := client.Restart(); err != nil {
return err
}
_, _ = cmd.Exec("systemctl restart docker")
return nil
needRestartDocker = true
case "disablePing":
return u.updatePingStatus("0")
case "enablePing":
return u.updatePingStatus("1")
default:
return fmt.Errorf("not supported operation: %s", operation)
}
return fmt.Errorf("not support such operation: %s", operation)
if needRestartDocker {
if err := restartDocker(); err != nil {
return err
}
}
return nil
}
func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool) error {
@@ -301,27 +312,69 @@ func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool)
}
func (u *FirewallService) OperateForwardRule(req dto.ForwardRuleOperate) error {
isSupport, errSup := checkIsSupport()
if !isSupport {
return errSup
}
client, err := firewall.NewFirewallClient()
if err != nil {
return err
}
rules, _ := client.ListForward()
i := 0
for _, rule := range rules {
shouldKeep := true
for i := range req.Rules {
reqRule := &req.Rules[i]
if reqRule.TargetIP == "" {
reqRule.TargetIP = "127.0.0.1"
}
if reqRule.Operation == "remove" {
for _, proto := range strings.Split(reqRule.Protocol, "/") {
if reqRule.Port == rule.Port &&
reqRule.TargetPort == rule.TargetPort &&
reqRule.TargetIP == rule.TargetIP &&
proto == rule.Protocol {
shouldKeep = false
break
}
}
}
}
if shouldKeep {
rules[i] = rule
i++
}
}
rules = rules[:i]
for _, rule := range rules {
for _, reqRule := range req.Rules {
if reqRule.Operation == "remove" {
continue
}
if reqRule.TargetIP == "" {
reqRule.TargetIP = "127.0.0.1"
}
if reqRule.Port == rule.Port && reqRule.TargetPort == rule.TargetPort && reqRule.TargetIP == rule.TargetIP {
return constant.ErrRecordExist
for _, proto := range strings.Split(reqRule.Protocol, "/") {
if reqRule.Port == rule.Port &&
reqRule.TargetPort == rule.TargetPort &&
reqRule.TargetIP == rule.TargetIP &&
proto == rule.Protocol {
return constant.ErrRecordExist
}
}
}
}
sort.SliceStable(req.Rules, func(i, j int) bool {
if req.Rules[i].Operation == "remove" && req.Rules[j].Operation != "remove" {
return true
}
if req.Rules[i].Operation != "remove" && req.Rules[j].Operation == "remove" {
return false
}
n1, _ := strconv.Atoi(req.Rules[i].Num)
n2, _ := strconv.Atoi(req.Rules[j].Num)
return n1 > n2
@@ -588,14 +641,6 @@ func (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) er
if err := client.Port(fireClient.FireInfo{Port: "443", Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
return err
}
apps := u.loadPortByApp()
for _, app := range apps {
if len(app.HttpPort) != 0 && app.HttpPort != "0" {
if err := client.Port(fireClient.FireInfo{Port: app.HttpPort, Protocol: "tcp", Strategy: "accept"}, "add"); err != nil {
return err
}
}
}
return client.Reload()
}
@@ -692,3 +737,14 @@ func checkPortUsed(ports, proto string, apps []portOfApp) string {
}
return ""
}
func checkIsSupport() (bool, error) {
std, err := cmd.Exec("iptables --version")
if err != nil {
return false, fmt.Errorf("handle iptables --version failed, stdout: %s, err: %v", std, err)
}
if strings.Contains(std, "nf_tables") {
return false, buserr.New(constant.ErrNFTables)
}
return true, nil
}
+3 -3
View File
@@ -286,7 +286,7 @@ func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
if err != nil {
return "", err
}
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
authStr := base64.StdEncoding.EncodeToString(encodedJSON)
options.RegistryAuth = authStr
}
image := repo.DownloadUrl + "/" + req.ImageName
@@ -295,7 +295,7 @@ func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
out, err := client.ImagePull(context.TODO(), image, options)
if err != nil {
_, _ = file.WriteString("image pull failed!")
global.LOG.Errorf("image %s pull failed, err: %v", image, err)
_, _ = file.WriteString(fmt.Sprintf("image %s pull failed, err: %v", image, err))
return
}
defer out.Close()
@@ -432,7 +432,7 @@ func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
}
defer client.Close()
for _, id := range req.Names {
if _, err := client.ImageRemove(context.TODO(), id, types.ImageRemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
if _, err := client.ImageRemove(context.TODO(), id, image.RemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
if strings.Contains(err.Error(), "image is being used") || strings.Contains(err.Error(), "is using") {
if strings.Contains(id, "sha256:") {
return buserr.New(constant.ErrObjectInUsed)
+42 -27
View File
@@ -84,11 +84,16 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
if imageRepo.ID != 0 {
return constant.ErrRecordExist
}
if req.Protocol == "http" {
_ = u.handleRegistries(req.DownloadUrl, "", "create")
stdout, err := cmd.Exec("systemctl restart docker")
if err != nil {
return errors.New(string(stdout))
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
}
if err := validateDockerConfig(); err != nil {
return err
}
if err := restartDocker(); err != nil {
return err
}
ticker := time.NewTicker(3 * time.Second)
defer ticker.Stop()
@@ -112,23 +117,18 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
return err
}
}
if req.Auth {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
return err
}
}
if err := copier.Copy(&imageRepo, &req); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
imageRepo.Status = constant.StatusSuccess
if req.Auth {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
imageRepo.Status = constant.StatusFailed
imageRepo.Message = err.Error()
}
}
if err := imageRepoRepo.Create(&imageRepo); err != nil {
return err
}
return nil
return imageRepoRepo.Create(&imageRepo)
}
func (u *ImageRepoService) BatchDelete(req dto.ImageRepoDelete) error {
@@ -154,32 +154,47 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
if err != nil {
return err
}
if repo.DownloadUrl != req.DownloadUrl || (!repo.Auth && req.Auth) {
_ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update")
if repo.Protocol == "http" && req.Protocol == "https" {
if err := u.handleRegistries("", repo.DownloadUrl, "delete"); err != nil {
return fmt.Errorf("delete registry %s failed, err: %v", repo.DownloadUrl, err)
}
}
if repo.Protocol == "http" && req.Protocol == "http" {
if err := u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update"); err != nil {
return fmt.Errorf("update registry %s => %s failed, err: %v", repo.DownloadUrl, req.DownloadUrl, err)
}
}
if repo.Protocol == "https" && req.Protocol == "http" {
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
}
}
if repo.Auth != req.Auth || repo.DownloadUrl != req.DownloadUrl {
if repo.Auth {
_, _ = cmd.ExecWithCheck("docker", "logout", repo.DownloadUrl)
}
stdout, err := cmd.Exec("systemctl restart docker")
if err != nil {
return errors.New(string(stdout))
if req.Auth {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
return err
}
}
}
if err := validateDockerConfig(); err != nil {
return err
}
if err := restartDocker(); err != nil {
return err
}
upMap := make(map[string]interface{})
upMap["download_url"] = req.DownloadUrl
upMap["protocol"] = req.Protocol
upMap["username"] = req.Username
upMap["password"] = req.Password
upMap["auth"] = req.Auth
upMap["status"] = constant.StatusSuccess
upMap["message"] = ""
if req.Auth {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
upMap["status"] = constant.StatusFailed
upMap["message"] = err.Error()
}
}
return imageRepoRepo.Update(req.ID, upMap)
}
+9 -6
View File
@@ -201,16 +201,19 @@ func StartMonitor(removeBefore bool, interval string) error {
service := NewIMonitorService()
ctx, cancel := context.WithCancel(context.Background())
monitorCancel = cancel
monitorID, err := global.Cron.AddJob(fmt.Sprintf("@every %sm", interval), service)
if err != nil {
return err
}
now := time.Now()
nextMinute := now.Truncate(time.Minute).Add(time.Minute)
time.AfterFunc(time.Until(nextMinute), func() {
monitorID, err := global.Cron.AddJob(fmt.Sprintf("@every %sm", interval), service)
if err != nil {
return
}
global.MonitorCronID = monitorID
})
service.Run()
go service.saveIODataToDB(ctx, float64(intervalItem))
go service.saveNetDataToDB(ctx, float64(intervalItem))
global.MonitorCronID = monitorID
return nil
}
+11 -8
View File
@@ -56,7 +56,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
opts []repo.DBOption
)
if create.Name != "" {
opts = append(opts, commonRepo.WithLikeName(create.Name))
opts = append(opts, commonRepo.WithByName(create.Name))
}
if create.Type != "" {
opts = append(opts, commonRepo.WithByType(create.Type))
@@ -83,7 +83,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
if exist != nil {
return nil, buserr.New(constant.ErrImageExist)
}
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
if !fileOp.Stat(create.CodeDir) {
return nil, buserr.New(constant.ErrPathNotFound)
}
@@ -133,7 +133,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (*model.Runtime, e
if err = handlePHP(create, runtime, fileOp, appVersionDir); err != nil {
return nil, err
}
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
runtime.Port = create.Port
if err = handleNodeAndJava(create, runtime, fileOp, appVersionDir); err != nil {
return nil, err
@@ -217,7 +217,7 @@ func (r *RuntimeService) Delete(runtimeDelete request.RuntimeDelete) error {
global.LOG.Errorf("delete image id [%s] error %v", imageID, err)
}
}
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
if out, err := compose.Down(runtime.GetComposePath()); err != nil && !runtimeDelete.ForceDelete {
if out != "" {
return errors.New(out)
@@ -300,7 +300,7 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) {
}
}
res.AppParams = appParams
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
res.Params = make(map[string]interface{})
envs, err := gotenv.Unmarshal(runtime.Env)
if err != nil {
@@ -308,7 +308,7 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeDTO, error) {
}
for k, v := range envs {
switch k {
case "NODE_APP_PORT", "PANEL_APP_PORT_HTTP", "JAVA_APP_PORT", "GO_APP_PORT":
case "NODE_APP_PORT", "PANEL_APP_PORT_HTTP", "JAVA_APP_PORT", "GO_APP_PORT", "APP_PORT":
port, err := strconv.Atoi(v)
if err != nil {
return nil, err
@@ -361,7 +361,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
if exist != nil {
return buserr.New(constant.ErrImageExist)
}
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
if runtime.Port != req.Port {
if err = checkPortExist(req.Port); err != nil {
return err
@@ -441,7 +441,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
return err
}
go buildRuntime(runtime, imageID, req.Rebuild)
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
runtime.Version = req.Version
runtime.CodeDir = req.CodeDir
runtime.Port = req.Port
@@ -495,6 +495,9 @@ func (r *RuntimeService) OperateRuntime(req request.RuntimeOperate) error {
_ = runtimeRepo.Save(runtime)
}
}()
go func() {
_ = docker.CreateDefaultDockerNetwork()
}()
switch req.Operate {
case constant.RuntimeUp:
if err = runComposeCmdWithLog(req.Operate, runtime.GetComposePath(), runtime.GetLogPath()); err != nil {
+20 -3
View File
@@ -2,6 +2,7 @@ package service
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
@@ -218,7 +219,10 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
_ = logFile.Close()
}()
cmd := exec.Command("docker-compose", "-f", composePath, "build")
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Hour)
defer cancel()
cmd := exec.CommandContext(ctx, "docker-compose", "-f", composePath, "build")
multiWriterStdout := io.MultiWriter(os.Stdout, logFile)
cmd.Stdout = multiWriterStdout
var stderrBuf bytes.Buffer
@@ -228,7 +232,11 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
err = cmd.Run()
if err != nil {
runtime.Status = constant.RuntimeError
runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + stderrBuf.String()
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + buserr.New("ErrCmdTimeout").Error()
} else {
runtime.Message = buserr.New(constant.ErrImageBuildErr).Error() + ":" + stderrBuf.String()
}
} else {
runtime.Status = constant.RuntimeNormal
runtime.Message = ""
@@ -338,6 +346,14 @@ func handleParams(create request.RuntimeCreate, projectDir string) (composeConte
if err != nil {
return
}
case constant.RuntimePython:
create.Params["CODE_DIR"] = create.CodeDir
create.Params["PYTHON_VERSION"] = create.Version
create.Params["PANEL_APP_PORT_HTTP"] = create.Port
composeContent, err = handleCompose(env, composeContent, create, projectDir)
if err != nil {
return
}
}
newMap := make(map[string]string)
@@ -384,7 +400,8 @@ func handleCompose(env gotenv.Env, composeContent []byte, create request.Runtime
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${JAVA_APP_PORT}")
case constant.RuntimeGo:
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${GO_APP_PORT}")
case constant.RuntimePython:
ports = append(ports, "${HOST_IP}:${PANEL_APP_PORT_HTTP}:${APP_PORT}")
}
for i, port := range create.ExposedPorts {
+5 -2
View File
@@ -322,6 +322,9 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error {
if err := settingRepo.Update("SSL", req.SSL); err != nil {
return err
}
if err := settingRepo.Update("AutoRestart", req.AutoRestart); err != nil {
return err
}
sID, _ := c.Cookie(constant.SessionName)
c.SetCookie(constant.SessionName, sID, 0, "", "", true, true)
@@ -354,7 +357,7 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
if err != nil {
return nil, err
}
case "import":
case "import-paste", "import-local":
data, err = loadInfoFromCert()
if err != nil {
return nil, err
@@ -380,7 +383,7 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
if err != nil {
return nil, err
}
data.Domain = ssl.Domains
data.Domain = ssl.PrimaryDomain
data.SSLID = uint(id)
data.Timeout = ssl.ExpireDate.Format(constant.DateTimeLayout)
}
+29 -21
View File
@@ -27,7 +27,8 @@ type SnapshotService struct {
}
type ISnapshotService interface {
SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error)
SearchWithPage(req dto.PageSnapshot) (int64, interface{}, error)
LoadSize(req dto.PageSnapshot) ([]dto.SnapshotFile, error)
SnapshotCreate(req dto.SnapshotCreate) error
SnapshotRecover(req dto.SnapshotRecover) error
SnapshotRollback(req dto.SnapshotRecover) error
@@ -46,13 +47,32 @@ func NewISnapshotService() ISnapshotService {
return &SnapshotService{}
}
func (u *SnapshotService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) {
total, systemBackups, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithLikeName(req.Info))
dtoSnap, err := loadSnapSize(systemBackups)
func (u *SnapshotService) SearchWithPage(req dto.PageSnapshot) (int64, interface{}, error) {
total, records, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithLikeName(req.Info), commonRepo.WithOrderRuleBy(req.OrderBy, req.Order))
if err != nil {
return 0, nil, err
}
return total, dtoSnap, err
var data []dto.SnapshotInfo
for _, record := range records {
var item dto.SnapshotInfo
if err := copier.Copy(&item, &record); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
data = append(data, item)
}
return total, data, err
}
func (u *SnapshotService) LoadSize(req dto.PageSnapshot) ([]dto.SnapshotFile, error) {
_, records, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithLikeName(req.Info), commonRepo.WithOrderRuleBy(req.OrderBy, req.Order))
if err != nil {
return nil, err
}
data, err := loadSnapSize(records)
if err != nil {
return nil, err
}
return data, err
}
func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
@@ -421,16 +441,7 @@ func rebuildAllAppInstall() error {
go func(app model.AppInstall) {
defer wg.Done()
dockerComposePath := app.GetComposePath()
out, err := compose.Down(dockerComposePath)
if err != nil {
_ = handleErr(app, err, out)
return
}
out, err = compose.Up(dockerComposePath)
if err != nil {
_ = handleErr(app, err, out)
return
}
_, _ = compose.Up(dockerComposePath)
app.Status = constant.Running
_ = appInstallRepo.Save(context.Background(), &app)
}(appInstalls[i])
@@ -507,15 +518,12 @@ func loadOs() string {
}
}
func loadSnapSize(records []model.Snapshot) ([]dto.SnapshotInfo, error) {
var datas []dto.SnapshotInfo
func loadSnapSize(records []model.Snapshot) ([]dto.SnapshotFile, error) {
var datas []dto.SnapshotFile
clientMap := make(map[string]loadSizeHelper)
var wg sync.WaitGroup
for i := 0; i < len(records); i++ {
var item dto.SnapshotInfo
if err := copier.Copy(&item, &records[i]); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
item := dto.SnapshotFile{Name: records[i].Name, ID: records[i].ID}
itemPath := fmt.Sprintf("system_snapshot/%s.tar.gz", item.Name)
if _, ok := clientMap[records[i].DefaultDownload]; !ok {
backup, err := backupRepo.Get(commonRepo.WithByType(records[i].DefaultDownload))
+2 -3
View File
@@ -231,7 +231,6 @@ func handleSnapTar(sourceDir, targetDir, name, exclusionRules string, secret str
exMap := make(map[string]struct{})
exStr := ""
excludes := strings.Split(exclusionRules, ";")
excludes = append(excludes, "*.sock")
for _, exclude := range excludes {
if len(exclude) == 0 {
continue
@@ -257,10 +256,10 @@ func handleSnapTar(sourceDir, targetDir, name, exclusionRules string, secret str
commands := ""
if len(secret) != 0 {
extraCmd := "| openssl enc -aes-256-cbc -salt -k '" + secret + "' -out"
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s %s %s", " -"+exStr, path, extraCmd, targetDir+"/"+name)
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read --exclude-from=<(find %s -type s -print) -zcf %s %s %s %s", sourceDir, " -"+exStr, path, extraCmd, targetDir+"/"+name)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
} else {
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s -C %s .", targetDir+"/"+name, exStr, sourceDir)
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read --exclude-from=<(find %s -type s -printf '%s' | sed 's|^|./|') -zcf %s %s -C %s .", sourceDir, "%P\n", targetDir+"/"+name, exStr, sourceDir)
global.LOG.Debug(commands)
}
stdout, err := cmd.ExecWithTimeOut(commands, 30*time.Minute)
+5 -3
View File
@@ -47,7 +47,7 @@ func (u *SnapshotService) HandleSnapshotRecover(snap model.Snapshot, isRecover b
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "Backup" {
if err := backupBeforeRecover(snap, ""); err != nil {
if err := backupBeforeRecover(snap); err != nil {
updateRecoverStatus(snap.ID, isRecover, "Backup", constant.StatusFailed, fmt.Sprintf("handle backup before recover failed, err: %v", err))
return
}
@@ -149,7 +149,7 @@ func (u *SnapshotService) HandleSnapshotRecover(snap model.Snapshot, isRecover b
_, _ = cmd.Exec("systemctl daemon-reload && systemctl restart 1panel.service")
}
func backupBeforeRecover(snap model.Snapshot, secret string) error {
func backupBeforeRecover(snap model.Snapshot) error {
baseDir := fmt.Sprintf("%s/1panel_original/original_%s", global.CONF.System.BaseDir, snap.Name)
var wg sync.WaitGroup
var status model.SnapshotStatus
@@ -231,7 +231,9 @@ func recoverDaemonJson(src string, fileOp files.FileOp) error {
}
}
_, _ = cmd.Exec("systemctl restart docker")
if err := restartDocker(); err != nil {
return err
}
return nil
}
+11 -1
View File
@@ -124,6 +124,16 @@ func (u *SSHService) OperateSSH(operation string) error {
if operation == "enable" || operation == "disable" {
serviceName += ".service"
}
if operation == "stop" {
isSocketActive, _ := systemctl.IsActive(serviceName + ".socket")
if isSocketActive {
std, err := cmd.Execf("%s systemctl stop %s", sudo, serviceName+".socket")
if err != nil {
global.LOG.Errorf("handle systemctl stop %s.socket failed, err: %v", serviceName, std)
}
}
}
stdout, err := cmd.Execf("%s systemctl %s %s", sudo, operation, serviceName)
if err != nil {
if strings.Contains(stdout, "alias name or linked unit file") {
@@ -312,7 +322,7 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
showCountFrom := (req.Page - 1) * req.PageSize
showCountTo := req.Page * req.PageSize
nyc, _ := time.LoadLocation(common.LoadTimeZone())
nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
qqWry, err := qqwry.NewQQwry()
if err != nil {
global.LOG.Errorf("load qqwry datas failed: %s", err)
+32 -5
View File
@@ -9,6 +9,7 @@ import (
"encoding/pem"
"errors"
"fmt"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"os"
"path"
"reflect"
@@ -139,8 +140,10 @@ func (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []respons
}
for _, web := range websites {
var (
appName string
runtimeName string
appName string
runtimeName string
runtimeType string
appInstallID uint
)
switch web.Type {
case constant.Deployment:
@@ -149,12 +152,15 @@ func (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []respons
return 0, nil, err
}
appName = appInstall.Name
appInstallID = appInstall.ID
case constant.Runtime:
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(web.RuntimeID))
if err != nil {
return 0, nil, err
}
runtimeName = runtime.Name
runtimeType = runtime.Type
appInstallID = runtime.ID
}
sitePath := path.Join(constant.AppInstallDir, constant.AppOpenresty, nginxInstall.Name, "www", "sites", web.Alias)
@@ -173,6 +179,8 @@ func (w WebsiteService) PageWebsite(req request.WebsiteSearch) (int64, []respons
SSLStatus: checkSSLStatus(web.WebsiteSSL.ExpireDate),
RuntimeName: runtimeName,
SitePath: sitePath,
AppInstallID: appInstallID,
RuntimeType: runtimeType,
})
}
return total, websiteDTOs, nil
@@ -305,6 +313,14 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
switch runtime.Type {
case constant.RuntimePHP:
if runtime.Resource == constant.ResourceAppstore {
client, err := docker.NewDockerClient()
if err != nil {
return err
}
defer client.Close()
if !checkImageExist(client, runtime.Image) {
return buserr.WithName("ErrImageNotExist", runtime.Name)
}
var (
req request.AppInstallCreate
install *model.AppInstall
@@ -336,7 +352,7 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
}
website.Proxy = proxy
}
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
website.Proxy = fmt.Sprintf("127.0.0.1:%d", runtime.Port)
}
}
@@ -471,7 +487,9 @@ func (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error {
tx, ctx := helper.GetTxAndContext()
defer tx.Rollback()
go NewIBackupService().DeleteRecordByName("website", website.PrimaryDomain, website.Alias, req.DeleteBackup)
go func() {
_ = NewIBackupService().DeleteRecordByName("website", website.PrimaryDomain, website.Alias, req.DeleteBackup)
}()
if err := websiteRepo.DeleteBy(ctx, commonRepo.WithByID(req.ID)); err != nil {
return err
}
@@ -623,8 +641,10 @@ func (w WebsiteService) DeleteWebsiteDomain(domainId uint) error {
wafSite := wafWebsite
oldDomains := wafSite.Domains
var newDomains []string
removed := false
for _, domain := range oldDomains {
if domain == webSiteDomain.Domain {
if domain == webSiteDomain.Domain && !removed {
removed = true
continue
}
newDomains = append(newDomains, domain)
@@ -910,6 +930,7 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
websiteSSL.Provider = constant.Manual
websiteSSL.PrivateKey = privateKey
websiteSSL.Pem = certificate
websiteSSL.Status = constant.SSLReady
res.SSL = websiteSSL
}
@@ -1646,6 +1667,9 @@ func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error)
}
if req.SNI {
location.UpdateDirective("proxy_ssl_server_name", []string{"on"})
if req.ProxySSLName != "" {
location.UpdateDirective("proxy_ssl_name", []string{req.ProxySSLName})
}
} else {
location.UpdateDirective("proxy_ssl_server_name", []string{"off"})
}
@@ -1728,6 +1752,9 @@ func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, e
if directive.GetName() == "proxy_ssl_server_name" {
proxyConfig.SNI = directive.GetParameters()[0] == "on"
}
if directive.GetName() == "proxy_ssl_name" {
proxyConfig.ProxySSLName = directive.GetParameters()[0]
}
}
res = append(res, proxyConfig)
}
+10 -11
View File
@@ -217,15 +217,14 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website
if req.Domains != "" {
domainArray := strings.Split(req.Domains, "\n")
for _, domain := range domainArray {
if !common.IsValidDomain(domain) {
err = buserr.WithName("ErrDomainFormat", domain)
return nil, err
} else {
if ipAddress := net.ParseIP(domain); ipAddress == nil {
domains = append(domains, domain)
} else {
ips = append(ips, ipAddress)
if ipAddress := net.ParseIP(domain); ipAddress == nil {
if !common.IsValidDomain(domain) {
err = buserr.WithName("ErrDomainFormat", domain)
return nil, err
}
domains = append(domains, domain)
} else {
ips = append(ips, ipAddress)
}
}
if len(domains) > 0 {
@@ -432,14 +431,14 @@ func (w WebsiteCAService) DownloadFile(id uint) (*os.File, error) {
if err = fileOp.CreateDir(dir, 0666); err != nil {
return nil, err
}
if err = fileOp.WriteFile(path.Join(dir, "ca.csr"), strings.NewReader(ca.CSR), 0644); err != nil {
if err = fileOp.WriteFile(path.Join(dir, "ca.crt"), strings.NewReader(ca.CSR), 0644); err != nil {
return nil, err
}
if err = fileOp.WriteFile(path.Join(dir, "private.key"), strings.NewReader(ca.PrivateKey), 0644); err != nil {
if err = fileOp.WriteFile(path.Join(dir, "ca.key"), strings.NewReader(ca.PrivateKey), 0644); err != nil {
return nil, err
}
fileName := ca.Name + ".zip"
if err = fileOp.Compress([]string{path.Join(dir, "ca.csr"), path.Join(dir, "private.key")}, dir, fileName, files.SdkZip, ""); err != nil {
if err = fileOp.Compress([]string{path.Join(dir, "ca.crt"), path.Join(dir, "ca.key")}, dir, fileName, files.SdkZip, ""); err != nil {
return nil, err
}
return os.Open(path.Join(dir, fileName))
+21 -12
View File
@@ -181,6 +181,13 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
return create, nil
}
func printSSLLog(logger *log.Logger, msgKey string, params map[string]interface{}, disableLog bool) {
if disableLog {
return
}
logger.Println(i18n.GetMsgWithMap(msgKey, params))
}
func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
var (
err error
@@ -272,11 +279,13 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
defer logFile.Close()
logger := log.New(logFile, "", log.LstdFlags)
legoLogger.Logger = logger
startMsg := i18n.GetMsgWithMap("ApplySSLStart", map[string]interface{}{"domain": strings.Join(domains, ","), "type": i18n.GetMsgByKey(websiteSSL.Provider)})
if websiteSSL.Provider == constant.DNSAccount {
startMsg = startMsg + i18n.GetMsgWithMap("DNSAccountName", map[string]interface{}{"name": dnsAccount.Name, "type": dnsAccount.Type})
if !apply.DisableLog {
startMsg := i18n.GetMsgWithMap("ApplySSLStart", map[string]interface{}{"domain": strings.Join(domains, ","), "type": i18n.GetMsgByKey(websiteSSL.Provider)})
if websiteSSL.Provider == constant.DNSAccount {
startMsg = startMsg + i18n.GetMsgWithMap("DNSAccountName", map[string]interface{}{"name": dnsAccount.Name, "type": dnsAccount.Type})
}
legoLogger.Logger.Println(startMsg)
}
legoLogger.Logger.Println(startMsg)
resource, err := client.ObtainSSL(domains, privateKey)
if err != nil {
handleError(websiteSSL, err)
@@ -296,7 +305,7 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
websiteSSL.Type = cert.Issuer.CommonName
websiteSSL.Organization = cert.Issuer.Organization[0]
websiteSSL.Status = constant.SSLReady
legoLogger.Logger.Println(i18n.GetMsgWithMap("ApplySSLSuccess", map[string]interface{}{"domain": strings.Join(domains, ",")}))
printSSLLog(logger, "ApplySSLSuccess", map[string]interface{}{"domain": strings.Join(domains, ",")}, apply.DisableLog)
saveCertificateFile(websiteSSL, logger)
if websiteSSL.ExecShell {
@@ -304,11 +313,11 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
if websiteSSL.PushDir {
workDir = websiteSSL.Dir
}
legoLogger.Logger.Println(i18n.GetMsgByKey("ExecShellStart"))
printSSLLog(logger, "ExecShellStart", nil, apply.DisableLog)
if err = cmd.ExecShellWithTimeOut(websiteSSL.Shell, workDir, logger, 30*time.Minute); err != nil {
legoLogger.Logger.Println(i18n.GetMsgWithMap("ErrExecShell", map[string]interface{}{"err": err.Error()}))
printSSLLog(logger, "ErrExecShell", map[string]interface{}{"err": err.Error()}, apply.DisableLog)
} else {
legoLogger.Logger.Println(i18n.GetMsgByKey("ExecShellSuccess"))
printSSLLog(logger, "ExecShellSuccess", nil, apply.DisableLog)
}
}
@@ -320,9 +329,9 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID))
if len(websites) > 0 {
for _, website := range websites {
legoLogger.Logger.Println(i18n.GetMsgWithMap("ApplyWebSiteSSLLog", map[string]interface{}{"name": website.PrimaryDomain}))
printSSLLog(logger, "ApplyWebSiteSSLLog", map[string]interface{}{"name": website.PrimaryDomain}, apply.DisableLog)
if err := createPemFile(website, *websiteSSL); err != nil {
legoLogger.Logger.Println(i18n.GetMsgWithMap("ErrUpdateWebsiteSSL", map[string]interface{}{"name": website.PrimaryDomain, "err": err.Error()}))
printSSLLog(logger, "ErrUpdateWebsiteSSL", map[string]interface{}{"name": website.PrimaryDomain, "err": err.Error()}, apply.DisableLog)
}
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
@@ -330,10 +339,10 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
return
}
if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
legoLogger.Logger.Println(i18n.GetMsgByKey(constant.ErrSSLApply))
printSSLLog(logger, constant.ErrSSLApply, nil, apply.DisableLog)
return
}
legoLogger.Logger.Println(i18n.GetMsgByKey("ApplyWebSiteSSLSuccess"))
printSSLLog(logger, "ApplyWebSiteSSLSuccess", nil, apply.DisableLog)
}
}()
+15 -8
View File
@@ -3,7 +3,6 @@ package service
import (
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
"log"
"os"
"path"
@@ -13,6 +12,8 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/i18n"
@@ -276,7 +277,7 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
server.UpdateRoot(rootIndex)
server.UpdatePHPProxy([]string{website.Proxy}, "")
}
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo:
case constant.RuntimeNode, constant.RuntimeJava, constant.RuntimeGo, constant.RuntimePython:
proxy := fmt.Sprintf("http://127.0.0.1:%d", runtime.Port)
server.UpdateRootProxy([]string{proxy})
}
@@ -1001,20 +1002,23 @@ func saveCertificateFile(websiteSSL *model.WebsiteSSL, logger *log.Logger) {
}
}
func GetSystemSSL() (bool, uint) {
func GetSystemSSL() (bool, bool, uint) {
sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
if err != nil {
global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
return false, 0
return false, false, 0
}
if sslSetting.Value == "enable" {
sslID, _ := settingRepo.Get(settingRepo.WithByKey("SSLID"))
idValue, _ := strconv.Atoi(sslID.Value)
if idValue > 0 {
return true, uint(idValue)
if idValue <= 0 {
return false, false, 0
}
auto, _ := settingRepo.Get(settingRepo.WithByKey("AutoRestart"))
return true, auto.Value == "enable", uint(idValue)
}
return false, 0
return false, false, 0
}
func UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {
@@ -1033,7 +1037,7 @@ func UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {
return buserr.WithErr(constant.ErrSSLApply, err)
}
}
enable, sslID := GetSystemSSL()
enable, auto, sslID := GetSystemSSL()
if enable && sslID == websiteSSL.ID {
fileOp := files.NewFileOp()
secretDir := path.Join(global.CONF.System.BaseDir, "1panel/secret")
@@ -1045,6 +1049,9 @@ func UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {
global.LOG.Errorf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", websiteSSL.PrimaryDomain, err.Error())
return err
}
if auto {
_, _ = cmd.Exec("systemctl restart 1panel.service")
}
}
return nil
}
+11
View File
@@ -0,0 +1,11 @@
package constant
const (
AlertEnable = "Enable"
AlertDisable = "Disable"
AlertSuccess = "Success"
AlertError = "Error"
AlertSyncError = "SyncError"
AlertPushError = "PushError"
AlertPushSuccess = "PushSuccess"
)
+16 -2
View File
@@ -46,6 +46,8 @@ var (
ErrCmdIllegal = "ErrCmdIllegal"
ErrXpackNotFound = "ErrXpackNotFound"
ErrXpackNotActive = "ErrXpackNotActive"
ErrXpackLost = "ErrXpackLost"
ErrXpackTimeout = "ErrXpackTimeout"
ErrXpackOutOfDate = "ErrXpackOutOfDate"
)
@@ -95,11 +97,12 @@ var (
ErrPathNotFound = "ErrPathNotFound"
ErrMovePathFailed = "ErrMovePathFailed"
ErrLinkPathNotFound = "ErrLinkPathNotFound"
ErrFileIsExit = "ErrFileIsExit"
ErrFileIsExist = "ErrFileIsExist"
ErrFileUpload = "ErrFileUpload"
ErrFileDownloadDir = "ErrFileDownloadDir"
ErrCmdNotFound = "ErrCmdNotFound"
ErrFavoriteExist = "ErrFavoriteExist"
ErrPathNotDelete = "ErrPathNotDelete"
)
// mysql
@@ -143,7 +146,9 @@ var (
)
var (
ErrFirewall = "ErrFirewall"
ErrFirewallNone = "ErrFirewallNone"
ErrFirewallBoth = "ErrFirewallBoth"
ErrNFTables = "ErrNFTables"
)
// cronjob
@@ -162,3 +167,12 @@ var (
ErrLicenseSave = "ErrLicenseSave"
ErrLicenseSync = "ErrLicenseSync"
)
// alert
var (
ErrAlert = "ErrAlert"
ErrAlertPush = "ErrAlertPush"
ErrAlertSave = "ErrAlertSave"
ErrAlertSync = "ErrAlertSync"
ErrAlertRemote = "ErrAlertRemote"
)
+5 -4
View File
@@ -14,10 +14,11 @@ const (
RuntimeUnhealthy = "unhealthy"
RuntimeCreating = "creating"
RuntimePHP = "php"
RuntimeNode = "node"
RuntimeJava = "java"
RuntimeGo = "go"
RuntimePHP = "php"
RuntimeNode = "node"
RuntimeJava = "java"
RuntimeGo = "go"
RuntimePython = "python"
RuntimeProxyUnix = "unix"
RuntimeProxyTcp = "tcp"
+2
View File
@@ -10,6 +10,8 @@ const (
StatusEnable = "Enable"
StatusDisable = "Disable"
StatusNone = "None"
StatusOnRetry = "OnRetry"
StatusLost = "Lost"
OrderDesc = "descending"
OrderAsc = "ascending"
+2 -2
View File
@@ -17,14 +17,14 @@ import (
)
func Run() {
nyc, _ := time.LoadLocation(common.LoadTimeZone())
nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
global.Cron = cron.New(cron.WithLocation(nyc), cron.WithChain(cron.Recover(cron.DefaultLogger)), cron.WithChain(cron.DelayIfStillRunning(cron.DefaultLogger)))
var (
interval model.Setting
status model.Setting
)
syncBeforeStart()
go syncBeforeStart()
if err := global.DB.Where("key = ?", "MonitorStatus").Find(&status).Error; err != nil {
global.LOG.Errorf("load monitor status from db failed, err: %v", err)
}
+12 -6
View File
@@ -1,16 +1,18 @@
package job
import (
"path"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"path"
"strings"
"time"
)
type ssl struct {
@@ -21,11 +23,11 @@ func NewSSLJob() *ssl {
}
func (ssl *ssl) Run() {
systemSSLEnable, sslID := service.GetSystemSSL()
systemSSLEnable, auto, sslID := service.GetSystemSSL()
sslRepo := repo.NewISSLRepo()
sslService := service.NewIWebsiteSSLService()
sslList, _ := sslRepo.List()
nyc, _ := time.LoadLocation(common.LoadTimeZone())
nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
global.LOG.Info("The scheduled certificate update task is currently in progress ...")
now := time.Now().Add(10 * time.Second)
for _, s := range sslList {
@@ -50,7 +52,8 @@ func (ssl *ssl) Run() {
}
} else {
if err := sslService.ObtainSSL(request.WebsiteSSLApply{
ID: s.ID,
ID: s.ID,
DisableLog: true,
}); err != nil {
global.LOG.Errorf("Failed to update the SSL certificate for the [%s] domain , err:%s", s.PrimaryDomain, err.Error())
continue
@@ -68,6 +71,9 @@ func (ssl *ssl) Run() {
global.LOG.Errorf("Failed to update the SSL certificate for 1Panel System domain [%s] , err:%s", s.PrimaryDomain, err.Error())
continue
}
if auto {
_, _ = cmd.Exec("systemctl restart 1panel.service")
}
}
global.LOG.Infof("The SSL certificate for the [%s] domain has been successfully updated", s.PrimaryDomain)
}
+4 -3
View File
@@ -1,6 +1,9 @@
package job
import (
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
@@ -8,8 +11,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"sync"
"time"
)
type website struct{}
@@ -19,7 +20,7 @@ func NewWebsiteJob() *website {
}
func (w *website) Run() {
nyc, _ := time.LoadLocation(common.LoadTimeZone())
nyc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
websites, _ := repo.NewIWebsiteRepo().List()
global.LOG.Info("Website scheduled task in progress ...")
now := time.Now().Add(10 * time.Minute)
+18 -2
View File
@@ -74,13 +74,15 @@ ErrFileToLarge: "file is too large"
ErrPathNotFound: "Path is not found"
ErrMovePathFailed: "The target path cannot contain the original path!"
ErrLinkPathNotFound: "Target path does not exist!"
ErrFileIsExit: "File or directory already exists!"
ErrFileIsExist: "File or directory already exists!"
ErrFileUpload: "Failed to upload file {{.name}} {{.detail}}"
ErrFileDownloadDir: "Download folder not supported"
ErrCmdNotFound: "{{ .name}} command does not exist, please install this command on the host first"
ErrSourcePathNotFound: "Source directory does not exist"
ErrFavoriteExist: "This path has been collected"
ErrInvalidChar: "Illegal characters are prohibited"
ErrPathNotDelete: "The selected directory cannot be deleted"
#website
ErrDomainIsExist: "Domain is already exist"
@@ -94,6 +96,7 @@ ErrPathPermission: 'A folder with non-1000:1000 permissions was detected in the
ErrDomainIsUsed: "Domain is already used by website {{ .name }}"
ErrDomainFormat: "{{ .name }} domain format error"
ErrDefaultAlias: "default is a reserved code name, please use another code name"
ErrImageNotExist: "Running environment {{.name}} image does not exist, please re-edit the running environment"
#ssl
ErrSSLCannotDelete: "The certificate {{ .name }} is being used by the website and cannot be removed"
@@ -164,7 +167,9 @@ ErrConfigAlreadyExist: "A configuration file with the same name already exists"
ErrUserFindErr: "Failed to find user {{ .name }} {{ .err }}"
#ssh
ErrFirewall: "No firewalld or ufw service is detected. Please check and try again!"
ErrFirewallNone: "No firewalld or ufw service detected on the system. Please check and try again!"
ErrFirewallBoth: "Both firewalld and ufw services are detected on the system. To avoid conflicts, please uninstall one and try again!"
ErrNFTables: "Port forwarding functionality relies on the iptables service and is currently not compatible with nftables operations!"
#cronjob
ErrBashExecute: "Script execution error, please check the specific information in the task output text area."
@@ -174,6 +179,7 @@ CutWebsiteLogSuccess: "{{ .name }} website log cut successfully, backup path {{
#toolbox
ErrNotExistUser: "The current user does not exist. Please modify and retry!"
ErrBanAction: "Setting failed, the current {{ .name }} service is unavailable, please check and try again!"
ErrClamdscanNotFound: "The clamdscan command was not detected, please refer to the documentation to install it!"
#waf
ErrScope: "Modification of this configuration is not supported"
@@ -195,3 +201,13 @@ ErrLicenseSync: "Failed to sync license information, no license information dete
ErrXpackNotFound: "This section is a professional edition feature, please import the license first in Panel Settings-License interface"
ErrXpackNotActive: "This section is a professional edition feature, please synchronize the license status first in Panel Settings-License interface"
ErrXpackOutOfDate: "The current license has expired, please re-import the license in Panel Settings-License interface"
ErrXpackLost: "The license has reached the maximum number of retry attempts. Please go to the [Settings] [License] page and manually click the sync button to ensure that the professional version features are functioning properly."
ErrXpackTimeout: "Request timed out, the network connection may be unstable, please try again later!"
#license
ErrAlert: "Alert information format error, please check and try again!"
ErrAlertPush: "Alert push error, please check and try again!"
ErrAlertSave: "Alert save error, please check and try again!"
ErrAlertSync: "Alert sync error, please check and try again!"
ErrAlertRemote: "Remote alert error, please check and try again!"
+16 -2
View File
@@ -75,12 +75,13 @@ ErrFileToLarge: "文件超過10M,無法打開"
ErrPathNotFound: "目錄不存在"
ErrMovePathFailed: "目標路徑不能包含原路徑!"
ErrLinkPathNotFound: "目標路徑不存在!"
ErrFileIsExit: "文件或文件夾已存在!"
ErrFileIsExist: "文件或文件夾已存在!"
ErrFileUpload: "{{ .name }} 上傳文件失敗 {{ .detail}}"
ErrFileDownloadDir: "不支持下載文件夾"
ErrCmdNotFound: "{{ .name}} 命令不存在,請先在宿主機安裝此命令"
ErrSourcePathNotFound: "源目錄不存在"
ErrFavoriteExist: "已收藏此路徑"
ErrPathNotDelete: "所選目錄不可删除"
#website
ErrDomainIsExist: "域名已存在"
@@ -94,6 +95,7 @@ ErrPathPermission: 'index 目錄下偵測到非 1000:1000 權限資料夾,可
ErrDomainIsUsed: "域名已被網站【{{ .name }}】使用"
ErrDomainFormat: "{{ .name }} 域名格式不正確"
ErrDefaultAlias: "default 為保留代號,請使用其他代號"
ErrImageNotExist: "執行環境 {{.name}} 鏡像不存在,請重新編輯執行環境"
#ssl
ErrSSLCannotDelete: "{{ .name }} 證書正在被網站使用,無法刪除"
@@ -165,7 +167,9 @@ ErrConfigAlreadyExist: "已存在同名配置文件"
ErrUserFindErr: "用戶 {{ .name }} 查找失敗 {{ .err }}"
#ssh
ErrFirewall: "當前未檢測到系統 firewalld 或 ufw 服務,請檢查後重試!"
ErrFirewallNone: "未檢測到系統 firewalld 或 ufw 服務,請檢查後重試!"
ErrFirewallBoth: "檢測到系統同時存在 firewalld 或 ufw 服務,為避免衝突,請卸載後重試!"
ErrNFTables: "端口轉發功能依賴於 iptables 服務,暫不兼容 nftables 操作!"
#cronjob
ErrBashExecute: "腳本執行錯誤,請在任務輸出文本域中查看具體信息。"
@@ -175,6 +179,7 @@ CutWebsiteLogSuccess: "{{ .name }} 網站日誌切割成功,備份路徑 {{ .p
#toolbox
ErrNotExistUser: "當前使用者不存在,請修改後重試!"
ErrBanAction: "設置失敗,當前 {{ .name }} 服務不可用,請檢查後重試!"
ErrClamdscanNotFound: "未偵測到 clamdscan 指令,請參考文件安裝!"
#waf
ErrScope: "不支援修改此配置"
@@ -197,3 +202,12 @@ ErrLicenseSync: "許可證信息同步失敗,資料庫中未檢測到許可證
ErrXpackNotFound: "該部分為專業版功能,請先在 面板設置-許可證 界面導入許可證"
ErrXpackNotActive: "該部分為專業版功能,請先在 面板設置-許可證 界面同步許可證狀態"
ErrXpackOutOfDate: "當前許可證已過期,請重新在 面板設置-許可證 界面導入許可證"
ErrXpackLost: "許可證已達到最大重試次數,請進入【面板設定】【許可證】頁面手動點擊同步按鈕,以確保專業版功能正常使用"
ErrXpackTimeout: "請求超時,網絡連接可能不穩定,請稍後再試!"
#license
ErrAlert: "告警資訊格式錯誤,請檢查後重試!"
ErrAlertPush: "告警資訊推送錯誤,請檢查後重試!"
ErrAlertSave: "告警資訊保存錯誤,請檢查後重試!"
ErrAlertSync: "告警資訊同步錯誤,請檢查後重試!"
ErrAlertRemote: "告警資訊遠端錯誤,請檢查後重試!"
+17 -2
View File
@@ -74,13 +74,15 @@ ErrFileToLarge: "文件超过10M,无法打开"
ErrPathNotFound: "目录不存在"
ErrMovePathFailed: "目标路径不能包含原路径!"
ErrLinkPathNotFound: "目标路径不存在!"
ErrFileIsExit: "文件或文件夹已存在!"
ErrFileIsExist: "文件或文件夹已存在!"
ErrFileUpload: "{{ .name }} 上传文件失败 {{ .detail}}"
ErrFileDownloadDir: "不支持下载文件夹"
ErrCmdNotFound: "{{ .name}} 命令不存在,请先在宿主机安装此命令"
ErrSourcePathNotFound: "源目录不存在"
ErrFavoriteExist: "已收藏此路径"
ErrInvalidChar: "禁止使用非法字符"
ErrPathNotDelete: "所选目录不可删除"
#website
ErrDomainIsExist: "域名已存在"
@@ -94,6 +96,7 @@ ErrPathPermission: 'index 目录下检测到非 1000:1000 权限文件夹,可
ErrDomainIsUsed: "域名已被网站【{{ .name }}】使用"
ErrDomainFormat: "{{ .name }} 域名格式不正确"
ErrDefaultAlias: "default 为保留代号,请使用其他代号"
ErrImageNotExist: "运行环境 {{.name}} 镜像不存在,请重新编辑运行环境"
#ssl
ErrSSLCannotDelete: "{{ .name }} 证书正在被网站使用,无法删除"
@@ -167,7 +170,9 @@ ErrConfigAlreadyExist: "已存在同名配置文件"
ErrUserFindErr: "用户 {{ .name }} 查找失败 {{ .err }}"
#ssh
ErrFirewall: "当前未检测到系统 firewalld 或 ufw 服务,请检查后重试!"
ErrFirewallNone: "未检测到系统 firewalld 或 ufw 服务,请检查后重试!"
ErrFirewallBoth: "检测到系统同时存在 firewalld 或 ufw 服务,为避免冲突,请卸载后重试!"
ErrNFTables: "端口转发功能依赖于 iptables 服务,暂不兼容 nftables 操作!"
#cronjob
ErrBashExecute: "脚本执行错误,请在任务输出文本域中查看具体信息。"
@@ -177,6 +182,7 @@ CutWebsiteLogSuccess: "{{ .name }} 网站日志切割成功,备份路径 {{ .p
#toolbox
ErrNotExistUser: "当前用户不存在,请修改后重试!"
ErrBanAction: "设置失败,当前 {{ .name }} 服务不可用,请检查后重试!"
ErrClamdscanNotFound: "未检测到 clamdscan 命令,请参考文档安装!"
#waf
ErrScope: "不支持修改此配置"
@@ -198,4 +204,13 @@ ErrLicenseSync: "许可证信息同步失败,数据库中未检测到许可证
ErrXpackNotFound: "该部分为专业版功能,请先在 面板设置-许可证 界面导入许可证"
ErrXpackNotActive: "该部分为专业版功能,请先在 面板设置-许可证 界面同步许可证状态"
ErrXpackOutOfDate: "当前许可证已过期,请重新在 面板设置-许可证 界面导入许可证"
ErrXpackLost: "许可证已达到最大重试次数,请进入【面板设置】【许可证】页面手动点击同步按钮,以确保专业版功能正常使用"
ErrXpackTimeout: "请求超时,网络连接可能不稳定,请稍后再试!"
#license
ErrAlert: "告警信息格式错误,请检查后重试!"
ErrAlertPush: "告警信息推送错误,请检查后重试!"
ErrAlertSave: "告警信息保存错误,请检查后重试!"
ErrAlertSync: "告警信息同步错误,请检查后重试!"
ErrAlertRemote: "告警信息远端错误,请检查后重试!"
+3 -1
View File
@@ -1,6 +1,7 @@
package cache
import (
"os"
"time"
"github.com/1Panel-dev/1Panel/backend/global"
@@ -10,7 +11,8 @@ import (
func Init() {
c := global.CONF.System.Cache
_ = os.RemoveAll(c)
_ = os.Mkdir(c, 0755)
options := badger.Options{
Dir: c,
ValueDir: c,
+37 -5
View File
@@ -7,6 +7,7 @@ import (
"path"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -14,6 +15,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
)
func Init() {
@@ -126,11 +128,24 @@ func handleSnapStatus() {
}
func handleCronjobStatus() {
_ = global.DB.Model(&model.JobRecords{}).Where("status = ?", constant.StatusWaiting).
Updates(map[string]interface{}{
"status": constant.StatusFailed,
"message": "the task was interrupted due to the restart of the 1panel service",
}).Error
var jobRecords []model.JobRecords
_ = global.DB.Where("status = ?", constant.StatusWaiting).Find(&jobRecords).Error
for _, record := range jobRecords {
err := global.DB.Model(&model.JobRecords{}).Where("status = ?", constant.StatusWaiting).
Updates(map[string]interface{}{
"status": constant.StatusFailed,
"message": "the task was interrupted due to the restart of the 1panel service",
}).Error
if err != nil {
global.LOG.Errorf("Failed to update job ID: %v, Error:%v", record.ID, err)
continue
}
var cronjob *model.Cronjob
_ = global.DB.Where("id = ?", record.CronjobID).First(&cronjob).Error
handleCronJobAlert(cronjob)
}
}
func loadLocalDir() {
@@ -210,3 +225,20 @@ func initDir() {
}
}
}
func handleCronJobAlert(cronjob *model.Cronjob) {
if cronjob.Type == "snapshot" {
return
}
pushAlert := dto.PushAlert{
TaskName: cronjob.Name,
AlertType: cronjob.Type,
EntryID: cronjob.ID,
Param: cronjob.Type,
}
err := xpack.PushAlert(pushAlert)
if err != nil {
global.LOG.Errorf("cronjob alert push failed, err: %v", err)
return
}
}
+5
View File
@@ -92,6 +92,11 @@ func Init() {
migrations.AddForward,
migrations.AddShellColumn,
migrations.AddClam,
migrations.AddClamStatus,
migrations.AddAlertMenu,
migrations.AddComposeColumn,
migrations.AddAutoRestart,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
@@ -278,3 +278,59 @@ var AddClam = &gormigrate.Migration{
return nil
},
}
var AddClamStatus = &gormigrate.Migration{
ID: "20240716-add-clam-status",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Clam{}); err != nil {
return err
}
return nil
},
}
var AddAlertMenu = &gormigrate.Migration{
ID: "20240706-update-xpack-hide-menu",
Migrate: func(tx *gorm.DB) error {
var (
setting model.Setting
menu dto.XpackHideMenu
)
tx.Model(&model.Setting{}).Where("key", "XpackHideMenu").First(&setting)
if err := json.Unmarshal([]byte(setting.Value), &menu); err != nil {
return err
}
menu.Children = append(menu.Children, dto.XpackHideMenu{
ID: "7",
Title: "xpack.alert.alert",
Path: "/xpack/alert/dashboard",
Label: "XAlertDashboard",
IsCheck: true,
})
data, err := json.Marshal(menu)
if err != nil {
return err
}
return tx.Model(&model.Setting{}).Where("key", "XpackHideMenu").Updates(map[string]interface{}{"value": string(data)}).Error
},
}
var AddComposeColumn = &gormigrate.Migration{
ID: "20240906-add-compose-command",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Compose{}); err != nil {
return err
}
return nil
},
}
var AddAutoRestart = &gormigrate.Migration{
ID: "20241021-add-auto-restart",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "AutoRestart", Value: "enable"}).Error; err != nil {
return err
}
return nil
},
}
@@ -127,6 +127,9 @@ var AddTablePHPExtensions = &gormigrate.Migration{
if err := tx.Create(&model.PHPExtensions{Name: "WordPress", Extensions: "exif,igbinary,imagick,intl,zip,apcu,memcached,opcache,redis,bc,image,shmop,mysqli,pdo_mysql,gd"}).Error; err != nil {
return err
}
if err := tx.Create(&model.PHPExtensions{Name: "BookStack", Extensions: "gd,dom,iconv,mbstring,mysqlnd,openssl,pdo,pdo_mysql,tokenizer,xml"}).Error; err != nil {
return err
}
if err := tx.Create(&model.PHPExtensions{Name: "Flarum", Extensions: "curl,gd,pdo_mysql,mysqli,bz2,exif,yaf,imap"}).Error; err != nil {
return err
}
+3 -2
View File
@@ -29,8 +29,9 @@ func BindDomain() gin.HandlerFunc {
}
if domains != status.Value {
if LoadErrCode("err-domain") != 200 {
helper.ErrResponse(c, LoadErrCode("err-domain"))
code := LoadErrCode()
if code != 200 {
helper.ErrResponse(c, code)
return
}
helper.ErrorWithDetail(c, constant.CodeErrDomain, constant.ErrTypeInternalServer, errors.New("domain not allowed"))
+1
View File
@@ -19,6 +19,7 @@ var whiteUrlList = map[string]struct{}{
"/api/v1/logs/operation": {},
"/api/v1/logs/login": {},
"/api/v1/auth/logout": {},
"/api/v1/dashboard/current": {},
"/api/v1/apps/installed/loadport": {},
"/api/v1/apps/installed/check": {},
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/repo"
)
func LoadErrCode(errInfo string) int {
func LoadErrCode() int {
settingRepo := repo.NewISettingRepo()
codeVal, err := settingRepo.Get(settingRepo.WithByKey("NoAuthSetting"))
if err != nil {
+3 -2
View File
@@ -35,8 +35,9 @@ func WhiteAllow() gin.HandlerFunc {
return
}
}
if LoadErrCode("err-ip") != 200 {
helper.ErrResponse(c, LoadErrCode("err-ip"))
code := LoadErrCode()
if code != 200 {
helper.ErrResponse(c, code)
return
}
helper.ErrorWithDetail(c, constant.CodeErrIP, constant.ErrTypeInternalServer, errors.New("IP address not allowed"))
+18 -7
View File
@@ -7,6 +7,7 @@ import (
"fmt"
"io"
"net/http"
"reflect"
"strings"
"time"
@@ -96,10 +97,11 @@ func OperationLog() gin.HandlerFunc {
if funcs.InputValue == key {
var names []string
if funcs.IsList {
sql := fmt.Sprintf("SELECT %s FROM %s where %s in (?);", funcs.OutputColumn, funcs.DB, funcs.InputColumn)
_ = global.DB.Raw(sql, value).Scan(&names)
query := fmt.Sprintf("SELECT %s FROM %s WHERE %s in (?)", funcs.OutputColumn, funcs.DB, funcs.InputColumn)
_ = global.DB.Raw(query, value).Scan(&names)
} else {
_ = global.DB.Raw(fmt.Sprintf("select %s from %s where %s = ?;", funcs.OutputColumn, funcs.DB, funcs.InputColumn), value).Scan(&names)
query := fmt.Sprintf("SELECT %s FROM %s WHERE %s = ?", funcs.OutputColumn, funcs.DB, funcs.InputColumn)
_ = global.DB.Raw(query, value).Scan(&names)
}
formatMap[funcs.OutputValue] = strings.Join(names, ",")
break
@@ -109,12 +111,21 @@ func OperationLog() gin.HandlerFunc {
}
for key, value := range formatMap {
if strings.Contains(operationDic.FormatEN, "["+key+"]") {
if arrays, ok := value.([]string); ok {
operationDic.FormatZH = strings.ReplaceAll(operationDic.FormatZH, "["+key+"]", fmt.Sprintf("[%v]", strings.Join(arrays, ",")))
operationDic.FormatEN = strings.ReplaceAll(operationDic.FormatEN, "["+key+"]", fmt.Sprintf("[%v]", strings.Join(arrays, ",")))
} else {
t := reflect.TypeOf(value)
if t.Kind() != reflect.Array && t.Kind() != reflect.Slice {
operationDic.FormatZH = strings.ReplaceAll(operationDic.FormatZH, "["+key+"]", fmt.Sprintf("[%v]", value))
operationDic.FormatEN = strings.ReplaceAll(operationDic.FormatEN, "["+key+"]", fmt.Sprintf("[%v]", value))
} else {
val := reflect.ValueOf(value)
length := val.Len()
var elements []string
for i := 0; i < length; i++ {
element := val.Index(i).Interface().(string)
elements = append(elements, element)
}
operationDic.FormatZH = strings.ReplaceAll(operationDic.FormatZH, "["+key+"]", fmt.Sprintf("[%v]", strings.Join(elements, ",")))
operationDic.FormatEN = strings.ReplaceAll(operationDic.FormatEN, "["+key+"]", fmt.Sprintf("[%v]", strings.Join(elements, ",")))
}
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ func PasswordExpired() gin.HandlerFunc {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypePasswordExpired, err)
return
}
loc, _ := time.LoadLocation(common.LoadTimeZone())
loc, _ := time.LoadLocation(common.LoadTimeZoneByCmd())
expiredTime, err := time.ParseInLocation(constant.DateTimeLayout, extime.Value, loc)
if err != nil {
helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, err)
+1 -1
View File
@@ -18,7 +18,7 @@ func (s *DashboardRouter) InitRouter(Router *gin.RouterGroup) {
{
cmdRouter.GET("/base/os", baseApi.LoadDashboardOsInfo)
cmdRouter.GET("/base/:ioOption/:netOption", baseApi.LoadDashboardBaseInfo)
cmdRouter.GET("/current/:ioOption/:netOption", baseApi.LoadDashboardCurrentInfo)
cmdRouter.POST("/current", baseApi.LoadDashboardCurrentInfo)
cmdRouter.POST("/system/restart/:operation", baseApi.SystemRestart)
}
}
+1
View File
@@ -37,6 +37,7 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
settingRouter.POST("/snapshot", baseApi.CreateSnapshot)
settingRouter.POST("/snapshot/status", baseApi.LoadSnapShotStatus)
settingRouter.POST("/snapshot/search", baseApi.SearchSnapshot)
settingRouter.POST("/snapshot/size", baseApi.LoadSnapshotSize)
settingRouter.POST("/snapshot/import", baseApi.ImportSnapshot)
settingRouter.POST("/snapshot/del", baseApi.DeleteSnapshot)
settingRouter.POST("/snapshot/recover", baseApi.RecoverSnapshot)
+1
View File
@@ -56,6 +56,7 @@ func (s *ToolboxRouter) InitRouter(Router *gin.RouterGroup) {
toolboxRouter.POST("/clam/base", baseApi.LoadClamBaseInfo)
toolboxRouter.POST("/clam/operate", baseApi.OperateClam)
toolboxRouter.POST("/clam/update", baseApi.UpdateClam)
toolboxRouter.POST("/clam/status/update", baseApi.UpdateClamStatus)
toolboxRouter.POST("/clam/del", baseApi.DeleteClam)
toolboxRouter.POST("/clam/handle", baseApi.HandleClamScan)
}
+5 -1
View File
@@ -27,12 +27,16 @@ func NewS3Client(vars map[string]interface{}) (*s3Client, error) {
if len(scType) == 0 {
scType = "Standard"
}
mode := loadParamFromVars("mode", vars)
if len(mode) == 0 {
mode = "virtual hosted"
}
sess, err := session.NewSession(&aws.Config{
Credentials: credentials.NewStaticCredentials(accessKey, secretKey, ""),
Endpoint: aws.String(endpoint),
Region: aws.String(region),
DisableSSL: aws.Bool(true),
S3ForcePathStyle: aws.Bool(false),
S3ForcePathStyle: aws.Bool(mode == "path"),
})
if err != nil {
return nil, err
+5 -2
View File
@@ -203,8 +203,11 @@ func SudoHandleCmd() string {
}
func Which(name string) bool {
_, err := exec.LookPath(name)
return err == nil
stdout, err := Execf("which %s", name)
if err != nil || (len(strings.ReplaceAll(stdout, "\n", "")) == 0) {
return false
}
return true
}
func ExecShellWithTimeOut(cmdStr, workdir string, logger *log.Logger, timeout time.Duration) error {
+33 -12
View File
@@ -230,13 +230,17 @@ func RemoveRepeatElement(a interface{}) (ret []interface{}) {
}
func LoadSizeUnit(value float64) string {
if value > 1048576 {
return fmt.Sprintf("%vM", value/1048576)
val := int64(value)
if val%1024 != 0 {
return fmt.Sprintf("%v", val)
}
if value > 1024 {
return fmt.Sprintf("%vK", value/1024)
if val > 1048576 {
return fmt.Sprintf("%vM", val/1048576)
}
return fmt.Sprintf("%v", value)
if val > 1024 {
return fmt.Sprintf("%vK", val/1024)
}
return fmt.Sprintf("%v", val)
}
func LoadSizeUnit2F(value float64) string {
@@ -252,13 +256,6 @@ func LoadSizeUnit2F(value float64) string {
return fmt.Sprintf("%.2f", value)
}
func LoadTimeZone() string {
loc := time.Now().Location()
if _, err := time.LoadLocation(loc.String()); err != nil {
return "Asia/Shanghai"
}
return loc.String()
}
func LoadTimeZoneByCmd() string {
loc := time.Now().Location().String()
if _, err := time.LoadLocation(loc); err != nil {
@@ -321,3 +318,27 @@ func SplitStr(str string, spi ...string) []string {
func IsValidIP(ip string) bool {
return net.ParseIP(ip) != nil
}
const (
b = uint64(1)
kb = 1024 * b
mb = 1024 * kb
gb = 1024 * mb
)
func FormatBytes(bytes uint64) string {
switch {
case bytes < kb:
return fmt.Sprintf("%dB", bytes)
case bytes < mb:
return fmt.Sprintf("%.2fKB", float64(bytes)/float64(kb))
case bytes < gb:
return fmt.Sprintf("%.2fMB", float64(bytes)/float64(mb))
default:
return fmt.Sprintf("%.2fGB", float64(bytes)/float64(gb))
}
}
func FormatPercent(percent float64) string {
return fmt.Sprintf("%.2f%%", percent)
}
+3
View File
@@ -613,6 +613,9 @@ func (f FileOp) decompressWithSDK(srcFile string, dst string, cType CompressType
func (f FileOp) Decompress(srcFile string, dst string, cType CompressType, secret string) error {
if cType == Tar || cType == Zip || cType == TarGz {
shellArchiver, err := NewShellArchiver(cType)
if !f.Stat(dst) {
_ = f.CreateDir(dst, 0755)
}
if err == nil {
if err = shellArchiver.Extract(srcFile, dst, secret); err == nil {
return nil
+165 -73
View File
@@ -72,6 +72,9 @@ func NewFileInfo(op FileOption) (*FileInfo, error) {
info, err := appFs.Stat(op.Path)
if err != nil {
if os.IsNotExist(err) {
return nil, buserr.New(constant.ErrLinkPathNotFound)
}
return nil, err
}
@@ -101,23 +104,47 @@ func NewFileInfo(op FileOption) (*FileInfo, error) {
}
if file.IsSymlink {
file.LinkPath = GetSymlink(op.Path)
}
if op.Expand {
if file.IsDir {
if err := file.listChildren(op); err != nil {
return nil, err
}
return file, nil
} else {
if err := file.getContent(); err != nil {
linkPath := GetSymlink(op.Path)
if !filepath.IsAbs(linkPath) {
dir := filepath.Dir(op.Path)
var err error
linkPath, err = filepath.Abs(filepath.Join(dir, linkPath))
if err != nil {
return nil, err
}
}
file.LinkPath = linkPath
targetInfo, err := appFs.Stat(linkPath)
if err != nil {
file.IsDir = false
file.Mode = "-"
file.User = "-"
file.Group = "-"
} else {
file.IsDir = targetInfo.IsDir()
}
file.Extension = filepath.Ext(file.LinkPath)
}
if op.Expand {
if err := handleExpansion(file, op); err != nil {
return nil, err
}
}
return file, nil
}
func handleExpansion(file *FileInfo, op FileOption) error {
if file.IsDir {
return file.listChildren(op)
}
if !file.IsDetail {
return file.getContent()
}
return nil
}
func (f *FileInfo) search(search string, count int) (files []FileSearchInfo, total int, err error) {
cmd := exec.Command("find", f.Path, "-name", fmt.Sprintf("*%s*", search))
output, err := cmd.StdoutPipe()
@@ -203,63 +230,82 @@ func (f *FileInfo) listChildren(option FileOption) error {
return err
}
} else {
dirFiles, err := afs.ReadDir(f.Path)
files, err = f.getFiles(afs, option)
if err != nil {
return err
}
var (
dirs []FileSearchInfo
fileList []FileSearchInfo
)
for _, file := range dirFiles {
info := FileSearchInfo{
Path: f.Path,
FileInfo: file,
}
if file.IsDir() {
dirs = append(dirs, info)
} else {
fileList = append(fileList, info)
}
}
sortFileList(dirs, option.SortBy, option.SortOrder)
sortFileList(fileList, option.SortBy, option.SortOrder)
files = append(dirs, fileList...)
}
items, err := f.processFiles(files, option)
if err != nil {
return err
}
if option.ContainSub {
f.ItemTotal = total
}
start := (option.Page - 1) * option.PageSize
end := option.PageSize + start
var result []*FileInfo
if start < 0 || start > f.ItemTotal || end < 0 || start > end {
result = items
} else {
if end > f.ItemTotal {
result = items[start:]
} else {
result = items[start:end]
}
}
f.Items = result
return nil
}
func (f *FileInfo) getFiles(afs *afero.Afero, option FileOption) ([]FileSearchInfo, error) {
dirFiles, err := afs.ReadDir(f.Path)
if err != nil {
return nil, err
}
var (
dirs []FileSearchInfo
fileList []FileSearchInfo
)
for _, file := range dirFiles {
info := FileSearchInfo{
Path: f.Path,
FileInfo: file,
}
if file.IsDir() {
dirs = append(dirs, info)
} else {
fileList = append(fileList, info)
}
}
sortFileList(dirs, option.SortBy, option.SortOrder)
sortFileList(fileList, option.SortBy, option.SortOrder)
return append(dirs, fileList...), nil
}
func (f *FileInfo) processFiles(files []FileSearchInfo, option FileOption) ([]*FileInfo, error) {
var items []*FileInfo
for _, df := range files {
if option.Dir && !df.IsDir() {
if shouldSkipFile(df, option) {
continue
}
name := df.Name()
fPath := path.Join(df.Path, df.Name())
if option.Search != "" {
if option.ContainSub {
fPath = df.Path
name = strings.TrimPrefix(strings.TrimPrefix(fPath, f.Path), "/")
} else {
lowerName := strings.ToLower(name)
lowerSearch := strings.ToLower(option.Search)
if !strings.Contains(lowerName, lowerSearch) {
continue
}
}
}
name, fPath := f.getFilePathAndName(option, df)
if !option.ShowHidden && IsHidden(name) {
continue
}
f.ItemTotal++
isSymlink, isInvalidLink := false, false
if IsSymlink(df.Mode()) {
isSymlink = true
info, err := f.Fs.Stat(fPath)
if err == nil {
df.FileInfo = info
} else {
isInvalidLink = true
}
}
isSymlink, isInvalidLink := f.checkSymlink(df)
file := &FileInfo{
Fs: f.Fs,
@@ -284,7 +330,26 @@ func (f *FileInfo) listChildren(option FileOption) error {
file.FavoriteID = favorite.ID
}
if isSymlink {
file.LinkPath = GetSymlink(fPath)
linkPath := GetSymlink(fPath)
if !filepath.IsAbs(linkPath) {
dir := filepath.Dir(fPath)
var err error
linkPath, err = filepath.Abs(filepath.Join(dir, linkPath))
if err != nil {
return nil, err
}
}
file.LinkPath = linkPath
targetInfo, err := file.Fs.Stat(linkPath)
if err != nil {
file.IsDir = false
file.Mode = "-"
file.User = "-"
file.Group = "-"
} else {
file.IsDir = targetInfo.IsDir()
}
file.Extension = filepath.Ext(file.LinkPath)
}
if df.Size() > 0 {
file.MimeType = GetMimeType(fPath)
@@ -294,24 +359,53 @@ func (f *FileInfo) listChildren(option FileOption) error {
}
items = append(items, file)
}
if option.ContainSub {
f.ItemTotal = total
return items, nil
}
func shouldSkipFile(df FileSearchInfo, option FileOption) bool {
if option.Dir && !df.IsDir() {
return true
}
start := (option.Page - 1) * option.PageSize
end := option.PageSize + start
var result []*FileInfo
if start < 0 || start > f.ItemTotal || end < 0 || start > end {
result = items
} else {
if end > f.ItemTotal {
result = items[start:]
} else {
result = items[start:end]
if option.Search != "" && !option.ContainSub {
lowerName := strings.ToLower(df.Name())
lowerSearch := strings.ToLower(option.Search)
if !strings.Contains(lowerName, lowerSearch) {
return true
}
}
f.Items = result
return nil
return false
}
func (f *FileInfo) getFilePathAndName(option FileOption, df FileSearchInfo) (string, string) {
name := df.Name()
fPath := path.Join(df.Path, df.Name())
if option.Search != "" && option.ContainSub {
fPath = df.Path
name = strings.TrimPrefix(strings.TrimPrefix(fPath, f.Path), "/")
}
return name, fPath
}
func (f *FileInfo) checkSymlink(df FileSearchInfo) (bool, bool) {
isSymlink := false
isInvalidLink := false
if IsSymlink(df.Mode()) {
isSymlink = true
info, err := f.Fs.Stat(path.Join(df.Path, df.Name()))
if err == nil {
df.FileInfo = info
} else {
isInvalidLink = true
}
}
return isSymlink, isInvalidLink
}
func (f *FileInfo) getContent() error {
@@ -326,10 +420,8 @@ func (f *FileInfo) getContent() error {
if err != nil {
return nil
}
if !f.IsDetail {
if len(cByte) > 0 && DetectBinary(cByte) {
return buserr.New(constant.ErrFileCanNotRead)
}
if len(cByte) > 0 && DetectBinary(cByte) {
return buserr.New(constant.ErrFileCanNotRead)
}
f.Content = string(cByte)
return nil

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