Compare commits

...
368 Commits
Author SHA1 Message Date
zhengkunwang223 174d60e3d6 fix: 解决网站配置文件生成错误问题 2024-06-26 15:52:00 +08:00
ssongliu 88f19b792c fix: 解决依赖升级导致的引用问题 2024-06-25 15:45:43 +08:00
ssongliu 790e9252e7 fix: 增加系统主题变化监听事件 (#5555) 2024-06-25 14:43:42 +08:00
zhengkunwang223 3d8d18db4d feat: 优化应用升级逻辑,拉取镜像失败无需回滚 2024-06-25 14:13:08 +08:00
zhengkunwang223 297e630753 feat: 优化 WAF 配置文件生成 2024-06-24 14:54:17 +08:00
ssongliu 7583196c80 fix: 解决面板主题跟随系统失效的问题 (#5547)
Refs #5527
2024-06-24 14:44:50 +08:00
zhengkunwang a06e4060a2 feat: 优化文件上传逻辑,减少临时空间占用 (#5508)
Refs https://github.com/1Panel-dev/1Panel/issues/5184
2024-06-24 14:44:40 +08:00
ssongliu 60d484bce4 fix: 解决升级导致的计划任务未执行问题 (#5483)
Refs #5414
2024-06-24 14:42:25 +08:00
ssongliu ebc6a1bd6d fix: 解决界面设置主题色不统一的问题 (#5449) 2024-06-24 14:40:42 +08:00
zhengkunwang 9bcfce14cb feat: demo 环境适配网站监控 (#5446) 2024-06-24 14:39:20 +08:00
zhengkunwang dfd84d414e fix: 解决应用安装 services 为空报系统错误的问题 (#5445) 2024-06-24 14:39:16 +08:00
zhengkunwang 1b07314c23 feat: 优化非80端口网站开启 https 端口配置 (#5443)
Refs https://github.com/1Panel-dev/1Panel/issues/5364
2024-06-24 14:39:13 +08:00
ssongliu f7e2f2dbb8 fix: 修改终端全屏按钮样式 (#5442)
Refs #5435
2024-06-24 14:38:41 +08:00
ssongliu 1a617e71bd fix: 解决浏览器切换时部分设置未同步的问题 (#5436)
Refs #5422
2024-06-24 14:38:38 +08:00
zhengkunwang 794ebfab2b fix: 解决部分情况下打开文件中文乱码的问题 (#5425)
Refs https://github.com/1Panel-dev/1Panel/issues/5374
2024-06-24 14:37:43 +08:00
zhengkunwang d33b836d3e fix: 解决升级页面 链接点击不跳转的问题 (#5413)
Refs https://github.com/1Panel-dev/1Panel/issues/5371
2024-06-24 14:37:40 +08:00
ssongliu 408997e725 fix: 解决计划任务执行周期错误的问题 (#5412)
Refs #5404
2024-06-24 14:37:13 +08:00
Frost-ZX 3ca3949b1e fix: 修正未认证设置表单初始值 (#5383) 2024-06-24 14:37:01 +08:00
ssongliu 2e05af650a fix: 解决深色模式下版本显示问题 (#5402)
Refs #5370
2024-06-24 14:36:56 +08:00
zhengkunwang 4285fedc5d style: 黑金主题样式优化 (#5360) 2024-06-08 04:20:25 +00:00
ssongliu 05c8370f0d pref: 优化暗黑主题样式 (#5355) 2024-06-07 16:24:24 +00:00
Anye b866816800 添加Flarum的PHP扩展模板 (#5354) 2024-06-07 23:56:46 +08:00
ssongliu e0f8cd96f8 fix: 修复应用升级文件对比显示问题 (#5352) 2024-06-07 15:02:23 +00:00
ssongliu c81f8c1bc4 fix: 解决登录跳概览页时样式错误的问题 (#5351) 2024-06-07 14:48:23 +00:00
ssongliu 45d628838f fix: 解决获取 SSL 信息错误的问题 (#5347)
Refs #5339
2024-06-07 14:38:24 +00:00
ssongliu f433895c97 fix: 修改部分黑金样式 (#5345) 2024-06-07 14:36:22 +00:00
zhengkunwang 1fcfbaf0a9 style: 黑金主题样式优化 (#5337) 2024-06-07 07:44:22 +00:00
zhengkunwang 63305e7316 fix: 解决自签证书开启自动续签报错的问题 (#5335) 2024-06-07 11:02:03 +08:00
ssongliu c394efa2ec fix: 解决 PostgreSQL 页面切换的样式问题 (#5332) 2024-06-06 14:41:38 +00:00
zhengkunwang 859ace71df fix: 解决限制应用安装之后安装按钮还能点的问题 (#5331) 2024-06-06 14:15:38 +00:00
zhengkunwang a7cf69e5a2 fix: 解决应用已安装时间显示错误的问题 (#5327) 2024-06-06 11:01:38 +00:00
John Bro d0105c7863 fix: 快照恢复弹框增加压缩密码输入框 (#5326) 2024-06-06 10:49:38 +00:00
ssongliu e18102dfc9 pref: 加密备份代码优化 (#5325) 2024-06-06 10:43:38 +00:00
ssongliu 135ac91160 fix: 增加系统请求超时时间 (#5324) 2024-06-06 09:37:38 +00:00
ssongliu f670095c08 fix: 修改登录页弹框判断 (#5323) 2024-06-06 08:29:37 +00:00
ssongliu ac384628c9 fix: 解决压缩备份恢复失败的问题 (#5322) 2024-06-06 08:03:39 +00:00
John Bro 60df7786d8 fix: 文件返回前进功能优化 (#5320) 2024-06-06 07:13:37 +00:00
ssongliu 0e74acbec0 fix: 解决应用安装失败的问题 (#5319) 2024-06-06 06:16:14 +00:00
ssongliu 37de6aae66 fix: 调整 Redis 空状态样式 (#5315) 2024-06-06 03:18:12 +00:00
John Bro f03051ce43 fix: 备份恢复弹框中增加输入压缩密码输入框 (#5305) 2024-06-05 15:54:10 +00:00
ssongliu d83402b819 fix: 解决 HTTP 类型代理失败的问题 (#5306) 2024-06-05 15:46:11 +00:00
ssongliu 97e800ee15 fix: 解决删除网站没删除备份文件的问题 (#5309) 2024-06-05 23:45:24 +08:00
zhengkunwang 7f583e5678 feat: 限制 PHP 运行环境制作镜像中被删除 (#5304) 2024-06-05 10:08:09 +00:00
吴小白 80b7bb44f7 fix: 修正 goreleaser v2 参数 (#5303)
#### What this PR does / why we need it?

DEPRECATED: --skip-publish was deprecated in favor of --skip=publish, check https://goreleaser.com/deprecations#-skip for more details

#### 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-06-05 08:58:09 +00:00
ssongliu 752e2ec17e fix: 面板代理地址自动增加 http 或者 https (#5302) 2024-06-05 08:52:09 +00:00
zhengkunwang 0118fbb944 fix: 修复编辑证书,秘钥算法没有回显的问题 (#5301)
Refs https://github.com/1Panel-dev/1Panel/issues/5299
2024-06-05 08:48:10 +00:00
ssongliu dba5302d68 fix: 解决代理选项为空的问题 (#5300) 2024-06-05 08:18:09 +00:00
ssongliu 546bc53f28 fix: 解决开启代理时同步应用失败的问题 (#5298) 2024-06-05 08:04:10 +00:00
zhengkunwang 4883c9dd49 feat: 升级增加 docker-compose 文件对比功能 (#5297) 2024-06-05 07:54:09 +00:00
ssongliuandssonglius11 428743d600 fix: 解决不带密码压缩文件失败的问题 (#5296)
Co-authored-by: ssonglius11 <ssonglius11@163.com>
2024-06-05 14:31:26 +08:00
zhengkunwang 33c4b8bba9 fix: 解决某些情况下安装应用错误没有正确显示的问题 (#5295)
Refs https://github.com/1Panel-dev/1Panel/issues/5289
2024-06-05 06:18:08 +00:00
ssongliu 296ae1c2e1 fix: 解决主题色切换报错的问题 (#5287) 2024-06-05 02:29:08 +00:00
ssongliu 7fb41f43cc fix: 解决登录页跳转空白的问题 (#5285) 2024-06-04 14:29:05 +00:00
ssongliu 0afc6242a2 fix: 优化 FTP 列表分页样式 (#5283) 2024-06-04 14:03:06 +00:00
zhengkunwang 62beecf337 fix: 解决自签证书关闭页面之后没有刷新的问题 (#5282) 2024-06-04 14:01:06 +00:00
zhengkunwang b08c7ff89d style: 统一修改名称样式 (#5281) 2024-06-04 13:59:05 +00:00
zhengkunwang daab425c78 fix: 解决打包报错问题 (#5278) 2024-06-04 09:02:31 +00:00
ssongliu 8ec9f727bd fix: 解决表格超出隐藏显示不正确的问题 (#5277) 2024-06-04 08:52:32 +00:00
zhengkunwang 8b781d466a feat: 应用升级支持对比 docker-compose 文件 (#5274)
Refs https://github.com/1Panel-dev/1Panel/issues/2826
2024-06-04 08:50:31 +00:00
John Bro 0182586869 feat: 压缩和解压缩 tar.gz 文件时支持设置压缩密码 (#5258) 2024-06-04 08:48:31 +00:00
John Bro dab5434125 fix: 容器垃圾清理不展示细节下拉清除 (#5271) 2024-06-04 06:38:48 +00:00
ssongliu e638a416ff fix: 修改面板设置证书时间显示不一致的问题 (#5267) 2024-06-04 03:41:50 +00:00
zhengkunwang ceca7fade0 fix: 解决部分情况下升级版本不显示的问题 (#5261)
Refs https://github.com/1Panel-dev/1Panel/issues/5256
2024-06-03 09:55:46 +00:00
zhengkunwang bdf8ba45ac fix: 解决应用安装升级状态错误的问题 (#5260) 2024-06-03 07:41:45 +00:00
ssongliu e45f43da9a pref: 优化登录页确认框样式 (#5259) 2024-06-03 15:41:35 +08:00
ssongliu 32fb7feb44 fix: 修改远程数据库排序问题 (#5257) 2024-06-03 06:07:44 +00:00
zhengkunwang caee2159a8 feat: 网站 access_log 使用主配置文件参数 (#5255) 2024-06-03 03:23:43 +00:00
zhengkunwang 6011f35f59 style: 增加 Chrome OS 图标 (#5243) 2024-05-31 10:27:19 +00:00
ssongliu 869d0c73bd fix: 服务器代理修改默认值 (#5242) 2024-05-31 10:07:19 +00:00
zhengkunwang 9af00cc80c perf: 优化应用同步查询接口 (#5241) 2024-05-31 09:57:20 +00:00
ssongliu 400dd79b9f fix: Redis 数据库连接信息移除密码限制 (#5239) 2024-05-31 09:35:20 +00:00
zhengkunwang 8c929004e9 feat: 修改 xpack 加载顺序 (#5238) 2024-05-31 09:33:21 +00:00
ssongliu e044d92075 fix: 快照删除增加备份文件删除选项 (#5237)
Refs #4962
2024-05-31 09:31:19 +00:00
ssongliu a02f160adf fix: demo 环境禁用终端工具 (#5235) 2024-05-31 06:51:18 +00:00
ssongliu ac33f5461d fix: 优化 Redis 数据库样式 (#5229) 2024-05-31 14:12:42 +08:00
ssongliu bf7a4f3254 fix: 解决计划任务时区显示不一致的问题 (#5230)
Refs  #5211
2024-05-31 05:47:19 +00:00
zhengkunwang ea40e32991 feat: 删除无用依赖 (#5227) 2024-05-30 11:01:15 +00:00
zhengkunwang d38d2c617d feat: 增加定时清理缓存 (#5226) 2024-05-30 10:09:15 +00:00
zhengkunwang 90bcf464d3 feat: Node.js 增加 pnpm 支持 (#5225) 2024-05-30 09:39:15 +00:00
ssongliu bd91c88357 fix: 解决 FTP 状态同步失败的问题 (#5224) 2024-05-30 09:11:16 +00:00
ssongliu 60dbd75ce2 fix: 远程数据库增加名称校验 (#5222) 2024-05-30 09:09:15 +00:00
ssongliu d5abd7ec3f fix: 解决远程数据库备份失败的问题 (#5221)
Refs #5166
2024-05-30 07:25:15 +00:00
ssongliu c133f39b5e fix: 解决主题颜色切换失败的问题 (#5218) 2024-05-30 07:17:15 +00:00
zhengkunwang 9df8f13fa7 feat: 升级 lego 版本 (#5216) 2024-05-30 07:15:14 +00:00
ssongliu e5bcb07cf6 fix: 处理数据库遮罩问题 (#5215) 2024-05-30 15:13:21 +08:00
ssongliu c62232e648 fix: 修复 Redis 数据库快速命令部分问题 (#5206) 2024-05-29 14:13:11 +00:00
zhengkunwang a4a66e1e18 fix: 解决应用安装切换版本校验条件没变的问题 (#5205) 2024-05-29 10:49:11 +00:00
zhengkunwang 86d021cbfc fix: 解决打开非文本文件报错的问题 (#5203) 2024-05-29 10:43:13 +00:00
zhengkunwang 9f3573bcb8 perf: 优化应用查询接口的执行速度 (#5202)
Refs https://github.com/1Panel-dev/1Panel/issues/5008
2024-05-29 10:41:13 +00:00
ssongliu 28f2dddcf6 fix: 解决黑金模式下概览页异常闪动的问题 (#5201) 2024-05-29 10:39:12 +00:00
zhengkunwang 3aa62f91bf fix: 解决使用本地 PHP 环境停止启动网站之后配置错误的问题 (#5198)
Refs https://github.com/1Panel-dev/1Panel/issues/5163
2024-05-29 07:49:10 +00:00
ssongliu fe00cb8015 style: 固定登录页样式 (#5197) 2024-05-29 07:15:10 +00:00
ssongliu 312c19e3cc fix: 概览页磁盘显示过滤 /snap (#5196) 2024-05-29 06:27:11 +00:00
Takagi 409e90d2c5 fix: the issue where the auto theme is invalid due to an incorrect class setting (#5194) 2024-05-29 14:26:20 +08:00
ssongliu 8fc708be97 feat: 增加服务器代理设置 (#5189) 2024-05-29 03:05:09 +00:00
ssongliu 2c7dcbfe17 fix: 解决概览页磁盘显示不全的问题 (#5190)
Refs #5174
2024-05-29 02:39:09 +00:00
zhengkunwang c189c48d51 fix: 解决清空伪静态配置报错的问题 (#5179)
Refs https://github.com/1Panel-dev/1Panel/issues/5086
2024-05-28 08:49:06 +00:00
wanghe a08612e0a0 docs: 增加安装包安装说明 2024-05-28 15:42:20 +08:00
ssongliu acda63d2f7 fix: 登录页跳转增加 404 (#5164) 2024-05-27 10:42:56 +00:00
ssongliu c39f029808 feat: 修改安全入口页特征样式 (#5159) 2024-05-27 03:32:54 +00:00
zhengkunwang 41bc8b634b feat: 增加华为 Icon (#5143) 2024-05-24 23:11:25 +08:00
Node b80b67b060 feat: 提升分段上传文件的体验 (#5123)
* feat: 提升分段上传文件的体验

* 原新建文件 mode 为755,修正为644
2024-05-24 11:15:24 +08:00
ssongliu 9c357f1ea4 fix: 解决升级过程中备份错误问题 (#5120)
Refs #5103
2024-05-23 15:16:50 +00:00
ssongliu 99ed9be1dd fix: 解决计划任务执行周期解析错误的问题 (#5119) 2024-05-23 09:50:49 +00:00
Node 3df8863904 fix: 修复分块上传文件资源未释放问题 (#5109) 2024-05-23 16:41:19 +08:00
Node f211b6b5ae fix: 修复几处日志文件未释放问题 (#5110) 2024-05-23 16:41:05 +08:00
ssongliu b59c9ee9ad fix: 解决升级过程中备份问题 (#5107) 2024-05-23 03:26:49 +00:00
ssongliu 8798babe07 pref: 1pctl 引用路径使用源文件代替软链地址 (#5105)
Refs #4930
2024-05-23 02:14:49 +00:00
Takagi 63c330054e refactor: refactor theme and logo switching functionality (#5015) 2024-05-22 18:38:03 +08:00
zhengkunwang f30b05a495 feat: 增加一些 icon (#5098) 2024-05-22 10:36:47 +00:00
zhengkunwang 260fbb6e19 feat: 文件夹名称增加完整路径展示 (#5097)
Refs https://github.com/1Panel-dev/1Panel/issues/5094
2024-05-22 10:34:47 +00:00
ssongliu 5cd59c7f39 fix: 解决在线文本编辑器打开 GBK 编码文件乱码问题 (#5096)
Refs #3804
2024-05-22 10:32:47 +00:00
zhengkunwang ede664be18 feat: 文件列表行内权限支持修改用户和用户组 (#5093)
Refs https://github.com/1Panel-dev/1Panel/issues/4981
2024-05-22 06:30:46 +00:00
zhengkunwang 0a7144f625 fix: 解决网站排序错误的问题 (#5092)
Refs https://github.com/1Panel-dev/1Panel/issues/5021
2024-05-22 06:26:47 +00:00
ssongliu 4dc722b83c feat: 进程守护增加运行目录跳转 (#5088)
Refs #4953
2024-05-22 03:10:45 +00:00
ssongliu 6131a99db0 fix: 解决部分 ssh 登录日志解析失败的问题 (#5081) 2024-05-21 08:44:45 +00:00
ssongliu d9f9f9a629 feat: 网站创建增加 FTP 选项 (#5076) 2024-05-21 08:42:46 +00:00
zhengkunwang 5e951f028e fix: 解决部分应用图标不显示的问题 (#5080) 2024-05-21 08:40:45 +00:00
ssongliu 65f92bf0c3 feat: FTP 增加状态及日志 (#5065) 2024-05-20 10:48:42 +00:00
zhengkunwang 201600ea06 feat: 增加网站监控的说明 (#5053) 2024-05-20 09:54:41 +00:00
ssongliu 64ed07e4fa feat: 工具箱支持 FTP (#5039) 2024-05-17 14:36:38 +00:00
zhengkunwang 35c4f4edfd feat: 增加网站监控页面 (#5038) 2024-05-17 09:54:38 +00:00
John Bro c31b3e9ddc feat: 工具箱的缓存清理添加容器垃圾清理功能 (#5024) 2024-05-17 06:02:37 +00:00
zhengkunwang 2e47afef3b feat: 优化网站目录跳转 (#5025) 2024-05-17 04:12:37 +00:00
ssongliu 54e81b2de5 fix: 修改数据库状态异常样式 (#5020) 2024-05-15 15:04:35 +00:00
ssongliu 8870b92ceb feat: Redis 数据库支持快速命令 (#5018)
Refs #3220
2024-05-15 15:02:35 +00:00
Anye cce60f4712 添加WordPress的PHP扩展模板 (#5016) 2024-05-15 13:48:49 +08:00
ssongliu 94cebfd8cc feat: Redis 支持远程数据库 (#5014) 2024-05-15 11:56:40 +08:00
ssongliu c56435970a feat: 数据库支持多 Redis 切换 (#5001)
Refs #3927
2024-05-13 14:40:31 +00:00
Zolo 188d9dd4c7 feat: 首页监控中上下行流量数据单位自动格式化 (#4207) (#4989) 2024-05-13 22:37:52 +08:00
zhengkunwang 8b9d1d9052 fix: 解决上传证书状态异常的问题 (#4999)
Refs https://github.com/1Panel-dev/1Panel/issues/4976
2024-05-13 05:54:30 +00:00
zhengkunwang 5a8869e832 feat: 优化网站域名跳转 (#4998)
Refs https://github.com/1Panel-dev/1Panel/issues/4964
2024-05-13 05:52:29 +00:00
lewisay 81835ef9a7 fix: 解决编辑证书时,其他域名格式不正确问题 (#4991)
#### 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-05-13 03:52:30 +00:00
zhengkunwang b3ce35d516 fix: 解决进程守护命令不能输入空格的问题 (#4997) 2024-05-13 03:34:30 +00:00
ssongliu 5979e8b5d4 feat: 存储卷清理增加清除所有标签 (#4994) 2024-05-13 03:32:29 +00:00
zhengkunwang 79c2ba2125 fix: 解决应用名称存在大写导致无法升级的问题 (#4987)
Refs https://github.com/1Panel-dev/1Panel/issues/4986
2024-05-12 14:16:28 +00:00
ssongliu 95bbf4ee52 feat: 右侧抽屉取消 esc 关闭 (#4979) 2024-05-11 09:52:28 +00:00
John Broandzhoujunhong d43df6941b feat: 运行环境及容器菜单增加清理构建缓存功能 (#4978)
Co-authored-by: zhoujunhong <1298308460@qq.com>
2024-05-11 17:50:24 +08:00
ssongliu df53193f3f fix: 解决安装包下载路径错误的问题 (#4954) 2024-05-10 06:56:24 +00:00
ssongliu d5c75f4eee fix: 解决容器镜像推送失败的问题 (#4944) 2024-05-09 14:00:24 +00:00
zhengkunwang 76356eaf5b feat: WAF 增加 cdn 配置文件 (#4942) 2024-05-09 13:52:24 +00:00
ssongliu 5d9c09f69e fix: 解决数据库备份错误的问题 (#4941) 2024-05-09 13:50:24 +00:00
ssongliu e799d1cc49 fix: 解决关闭菜单选项时刷新跳转到概览页的问题 (#4939) 2024-05-09 07:44:23 +00:00
ssongliu 1090df3703 fix: 修改版本升级显示信息 (#4938) 2024-05-09 07:28:23 +00:00
zhengkunwang f3267bf137 style: 去掉多余的字符 (#4936) 2024-05-09 03:32:23 +00:00
zhengkunwang 0a426f3cd8 style: 调整证书编辑页面样式 (#4934) 2024-05-09 03:04:23 +00:00
zhengkunwang c6e1bb5245 fix: 解决自签证书火狐浏览器报错的问题 (#4926)
Refs https://github.com/1Panel-dev/1Panel/issues/4927
2024-05-08 10:48:23 +00:00
ssongliu 0fa027d01d fix: 解决升级信息获取失败的问题 (#4925) 2024-05-08 10:46:23 +00:00
zhengkunwang 395bc63946 fix: 解决网站备份恢复导致网站列表丢失的问题 (#4924) 2024-05-08 10:44:24 +00:00
John Bro 7a6d0c5380 fix: 文件列表后退、前进按钮,在根目录时无法点击问题修复 (#4923)
Refs #4723
2024-05-08 10:42:22 +00:00
ssongliu 1e192c18e7 fix: 解决容器执行 exit 退出后服务 panic 的问题 (#4922) 2024-05-08 09:38:24 +00:00
ssongliu d7df881da9 fix: 解决编排目录跳转失败的问题 (#4921)
Refs #4895
2024-05-08 09:36:23 +00:00
zhengkunwang a6f354fed9 style: 调整反向代理的源文页面大小 (#4917)
#### 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-05-08 06:38:23 +00:00
ssongliu be12c2397d feat: 切割网站日志计划任务增加默认周期 (#4916) 2024-05-08 06:36:23 +00:00
zhengkunwang bddb488baf fix: 解决中文域名创建 PHP 类型网站失败的问题 (#4915) 2024-05-08 06:34:23 +00:00
ssongliu 75c23271df fix: 解决获取版本升级信息失败的问题 (#4914) 2024-05-08 06:32:23 +00:00
zhengkunwang 34eb20a679 style: 解决英文状态下删除文件提示字符超出的问题 (#4913) 2024-05-08 06:30:24 +00:00
zhengkunwang b01d8949cb feat: 修改 supervisor 名称校验规则 (#4910) 2024-05-08 06:28:23 +00:00
zhengkunwang b489f5cf2f feat: 证书申请增加配置,提高申请成功率 (#4903) 2024-05-07 18:26:34 +08:00
zhengkunwang 43ebf6eef9 feat: 证书增加编辑功能 (#4901) 2024-05-07 18:01:00 +08:00
zhengkunwang 736914f443 fix: 解决进程守护启动命令带空格导致报错的问题 (#4894) 2024-05-07 18:00:48 +08:00
ssongliu 4243ccb5c2 feat: 增加许可证错误信息显示 (#4877) 2024-05-06 06:25:55 +00:00
zhengkunwang 539b600d6c feat: 网站默认配置增加支持 CDN 真实 IP (#4874) 2024-05-06 05:57:56 +00:00
John Bro e676a632b9 fix: 运行时运行目录选择器支持手动输入 (#4870)
Refs #2909
2024-05-06 05:55:56 +00:00
zhengkunwang 4f6a443b13 feat: 限制创建网站使用 default 代号 (#4869)
Refs https://github.com/1Panel-dev/1Panel/issues/4868
2024-05-06 05:53:56 +00:00
zhengkunwang 7684066689 fix: 解决计算文件夹大小线程没有回收的问题 (#4866)
Refs https://github.com/1Panel-dev/1Panel/issues/4867
2024-05-06 05:51:55 +00:00
maninhill 9719ef2edc chore: Update README.md 2024-05-03 12:23:50 +08:00
wanghe-fit2cloud a5b6121411 docs: Update README.md 2024-04-30 18:51:04 +08:00
ssongliu e8fb727676 feat: 计划任务应用、网站备份增加排除规则 (#4810) 2024-04-30 18:23:36 +08:00
ssongliu 164a48c99f pref: 优化计划任务错误显示 (#4803) 2024-04-30 17:33:15 +08:00
John Broandzhoujunhong e47d449b1b fix: 删除 PHP 和 Node.js 运行环境的提示信息优化 (#4790)
* fix: 删除 PHP 和 Node.js 运行环境的提示信息优化

* fix: 代码优化

* fix: 代码优化

---------

Co-authored-by: zhoujunhong <1298308460@qq.com>
2024-04-30 11:58:49 +08:00
zhengkunwang 5c46c26dd5 fix: 解决反向代理匹配规则为空之后无法删除的问题 (#4777)
Refs https://github.com/1Panel-dev/1Panel/issues/4771
2024-04-28 14:12:44 +00:00
zhengkunwang a40116d991 fix: 解决应用商店同步图标丢失的问题 (#4776) 2024-04-28 14:10:44 +00:00
zhengkunwang b0a3ecd3d2 fix: 解决第二次上传同名文件不显示的问题 (#4737) 2024-04-26 18:20:31 +08:00
ssongliu 29fd941d55 feat: 失败状态应用允许恢复 (#4732) 2024-04-26 18:20:19 +08:00
zhengkunwang 9851b31013 feat: 增加一些提示信息 (#4730) 2024-04-26 18:20:09 +08:00
ssongliu 6e635c691d style: 优化网站列表描述信息显示 (#4726) 2024-04-26 16:13:53 +08:00
ssongliu 5532636e02 style: 修改面板设置描述信息 (#4725) 2024-04-26 16:13:44 +08:00
John Broandzhoujunhong e47e2d63d1 feat: 文件管理页面增加回退功能 (#4723)
Co-authored-by: zhoujunhong <1298308460@qq.com>
2024-04-26 16:13:34 +08:00
ssongliu b9dde4ebf0 style: 优化概览页 CPU 显示 (#4721) 2024-04-26 16:13:12 +08:00
zhengkunwang 6a30ccd5dc perf: 优化错误日志超长下的显示 (#4711) 2024-04-25 11:09:13 +00:00
ssongliu ad6a1ac465 fix: Docker 配置自动过滤空行 (#4707)
Refs #4701
2024-04-25 11:07:12 +00:00
zhengkunwang 35f311c452 fix: 修复 WAF 地图英文模式下的显示错误 (#4704) 2024-04-25 17:48:54 +08:00
ssongliu c3dd6f2e0a pref: 释放容器 client 连接 (#4700) 2024-04-25 08:45:12 +00:00
ssongliu 94405d877f fix: 解决计划任务本地保存份数错误的问题 (#4692)
Refs #4671
2024-04-25 02:31:10 +00:00
ssongliu 9cd37e8ec5 feat: 增加内测模式 (#4685) 2024-04-24 15:12:12 +00:00
zhengkunwang 1381ef9e8e style: 优化中国地图显示 (#4681) 2024-04-24 15:10:14 +00:00
John Bro 42dffe8078 fix: 快速命令管理页面中增加关键词筛选 (#4679)
Refs #3525
2024-04-24 15:08:11 +00:00
zhengkunwang 92f019cf7c feat: 伪静态增加提示 (#4678) 2024-04-24 09:36:11 +00:00
zhengkunwang c23dfc374f fix: 解决应用升级失败没有回滚的问题 (#4677) 2024-04-24 09:34:10 +00:00
wanghe-fit2cloud 61f7374d71 docs: update CONTRIBUTING.md 2024-04-23 22:52:33 +08:00
John Bro 3fcb20d24c fix: 创建网站可以使用相同域名问题修复 (#4669)
Refs #4624
2024-04-23 14:10:09 +00:00
ssongliu d4b561e42d feat: 升级 golang.org/x/net 依赖 (#4662) 2024-04-23 10:06:09 +00:00
zhengkunwang b6b4bfe195 fix: 解决低版本 OpenResty 创建网站报错的问题 (#4661) 2024-04-23 10:04:09 +00:00
ssongliu dcc0264b67 fix: 解决容器内进程异常结束的问题 (#4660)
Refs #4653
2024-04-23 10:02:10 +00:00
ssongliu 15b1f28fb9 fix: 解决概览页折线图选择失败的问题 (#4656)
Refs #4647
2024-04-23 10:00:10 +00:00
ssongliuandssonglius11 4596f9c2c2 feat: 数据库支持 Mariadb 11.x (#4665)
Co-authored-by: ssonglius11 <ssonglius11@163.com>
2024-04-23 17:59:00 +08:00
ssongliu 3156c84b19 feat: 增加应用备份跳转目录 (#4648) 2024-04-23 03:20:08 +00:00
John Bro ac400c7188 fix: SSH 连接列表显示优化 (#4650)
Refs #4546
2024-04-23 03:18:06 +00:00
ssongliu 7fd672c1c9 feat: 升级回滚增加相关数据库文件 (#4638) 2024-04-22 14:22:27 +00:00
ssongliu 884a169a6f fix: 增加特殊字符命令过滤 (#4599) 2024-04-19 08:58:08 +00:00
ssongliu e79a5f9ae2 fix: 修改安全入口长度校验 (#4594) 2024-04-19 07:24:08 +00:00
ssongliu 25608e7378 fix: 修改许可证状态获取接口 (#4593)
Refs #4589
2024-04-19 07:22:08 +00:00
zhengkunwang dad8bac715 fix: 解决伪静态开启报错的问题 (#4591) 2024-04-19 07:20:08 +00:00
ssongliu a4ee16cf1f fix: 解决容器日志文件被删除但是进程未结束的问题 (#4588)
Refs #4582
2024-04-19 07:18:10 +00:00
dependabot[bot] 7f7465b383 chore(deps): bump github.com/docker/docker (#4586)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 26.0.0+incompatible to 26.0.2+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v26.0.0...v26.0.2)

---
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-04-19 15:17:29 +08:00
zhengkunwang b7c3dc0d2a fix: 解决创建 PHP 网站首页无法访问的 BUG (#4577) 2024-04-18 14:40:07 +00:00
ssongliu bb02991b05 fix: 解决监控数据迁移失败的问题 (#4569)
Refs #4568
2024-04-18 14:38:06 +00:00
writegr a5fdff61ff chore: fix some typos (#4564)
Signed-off-by: writegr <wellweek@outlook.com>
2024-04-18 12:24:40 +08:00
zhengkunwang 4832081b82 fix: 解决文件上传权限错误的问题 (#4563) 2024-04-18 03:18:07 +00:00
ssongliu ef5a13f87f fix: 解决本地备份账号路径获取错误的问题 (#4560) 2024-04-18 02:36:06 +00:00
老广 e2bc8e800a docs: add projects to other readme (#4561) 2024-04-18 10:26:00 +08:00
老广 85a397949e Update README.md
Add our other projects
2024-04-17 17:08:35 +08:00
ssongliu 5cfdcedf9a fix: 解决容器日志缺失的问题 (#4549) 2024-04-17 08:52:07 +00:00
ssongliu c5bb8dab8c fix: 解决复制导致的快照失败问题 (#4547) 2024-04-17 07:28:07 +00:00
zhengkunwang f1421ba7f9 style: 修改文件编辑器配置样式 (#4535) 2024-04-16 18:59:30 +08:00
ssongliu 9e44db53a7 pref: 调整部分界面的高度问题 (#4534) 2024-04-16 18:59:14 +08:00
zhengkunwang 4814a76c47 style: 修改 openresty 的端口提示 (#4532) 2024-04-16 15:58:51 +08:00
ssongliu 2ec4b2561c feat: 修改服务初始化操作逻辑 (#4530) 2024-04-16 14:44:07 +08:00
wanghe 4efe7240ad Update tyops-check.yml 2024-04-16 13:57:03 +08:00
ssongliu ed13136221 feat: 增加许可证解绑功能 (#4522) 2024-04-15 18:37:05 +08:00
ssongliu 735fc75e35 feat: 登陆时加载许可证状态 (#4520) 2024-04-15 18:06:38 +08:00
xuwei-fit2cloud b8a52ae707 chore: 添加拼写检查Action 2024-04-15 17:58:36 +08:00
John Bro 2501a9f78a fix: 修复容器未指定 IP 编辑显示报错问题 (#4519) 2024-04-15 07:12:10 +00:00
Langel 5a8d6db43e feat: 增加菜单标签页功能 (#3267) (#4510) 2024-04-15 15:10:24 +08:00
ssongliu a3312331d2 feat: 解决登录页授权 IP 跳转失败的问题 (#4517) 2024-04-15 07:10:10 +00:00
zhengkunwang fbd0e836d7 fix:解决新建文件夹部分权限无法创建的问题 (#4515) 2024-04-15 07:08:10 +00:00
Craftsman 4935d88301 typos: Modify wrong words (#4518) 2024-04-15 15:05:00 +08:00
zhengkunwang 8625979cc3 feat: 修改 supervisor 名称限制 (#4513) 2024-04-15 03:56:10 +00:00
zhengkunwang c77742e72e fix: 解决安装应用时候,原始 docker-compose.yml 的 deploy 字段下内容会被自动覆写的问题 (#4512)
Refs https://github.com/1Panel-dev/1Panel/issues/4340
2024-04-15 03:54:09 +00:00
John Bro 43a6b4b735 feat: 容器创建支持指定 IP 地址 (#4489)
#### 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-04-13 11:56:07 +00:00
mobeicanyue f4ee534101 fix typos (#4503) 2024-04-13 19:49:17 +08:00
ssongliu c055a07e57 fix: 解决概览页 CPU 核心过多导致的显示问题 (#4490)
Refs #3966
2024-04-13 11:44:07 +00:00
ssongliu 62bead5c50 fix: 修改登录页跳转问题 (#4487) 2024-04-12 08:20:07 +00:00
zhengkunwang 9edafc13a3 feat: 修改中国地区名称 (#4485) 2024-04-12 04:30:07 +00:00
zhengkunwang e6ccf8be1f fix: 解决文件搜索目录包含回收站目录能进入的问题 (#4484) 2024-04-12 04:08:06 +00:00
ssongliu 222089d56a fix: 修改系统版本比较方法 (#4483) 2024-04-12 03:04:06 +00:00
zhengkunwang 96e48847d0 fix: 解决特殊字符下文件放入回收站失败的问题 (#4480) 2024-04-11 11:08:11 +00:00
zhengkunwang 38b0c42555 feat: 增加回收站文件夹限制 (#4479) 2024-04-11 11:06:10 +00:00
zhengkunwang 2eae8274f6 fix: 创建文件增加非法字符校验 (#4478) 2024-04-11 10:32:11 +00:00
ssongliu 0de79e537e fix: 修改高级功能菜单 LOGO (#4477) 2024-04-11 10:30:11 +00:00
zhengkunwang e37b87d63e fix: 解决安装应用时 deploy 字段被覆盖的问题 (#4476) 2024-04-11 10:28:11 +00:00
ssongliu 37a0329d4a fix: 解决登录页跳转错误的问题 (#4474) 2024-04-11 09:16:10 +00:00
ssongliu e037b69f52 fix: 解决登录页跳转错误的问题 (#4470) 2024-04-11 15:53:22 +08:00
zhengkunwang 34498392dc style(waf): 修改网站图标位置 (#4469) 2024-04-11 07:00:13 +00:00
ssongliu 1687b99526 feat: 增加 GPU 监控 (#4468) 2024-04-11 06:16:10 +00:00
ssongliu 0bf6db1cf8 fix: 解决僵尸进程的问题 (#4464) 2024-04-10 14:18:08 +00:00
ssongliu 0cd06803e2 fix: 监控数据迁移增加数据长度判断 (#4454) 2024-04-10 06:06:08 +00:00
guangwu 45f6a415de fix: close resource (#4456) 2024-04-10 14:05:07 +08:00
John Bro 3e3f0d9452 feat: 未认证设置状态下系统跳转页面功能实现 (#4444)
#### 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-04-09 14:46:09 +00:00
zhengkunwang 35785f1e40 feat: 增加文件修改权限的超时时间 (#4448)
Refs https://github.com/1Panel-dev/1Panel/issues/4291
2024-04-09 09:34:09 +00:00
zhengkunwang 437d6eb0fd feat: 网站名称支持带端口 (#4446)
Refs https://github.com/1Panel-dev/1Panel/issues/3726
2024-04-09 09:32:10 +00:00
zhengkunwang 1e2c85e771 feat: 文本编辑器记住上次设置 (#4445)
Refs https://github.com/1Panel-dev/1Panel/issues/4397
Refs https://github.com/1Panel-dev/1Panel/issues/4266
2024-04-09 09:30:09 +00:00
zhengkunwang ef075df3fe feat: 进程守护名称支持-_等字符 (#4442)
Refs https://github.com/1Panel-dev/1Panel/issues/4303
2024-04-09 08:38:09 +00:00
zhengkunwang 1e22769c8b feat: 文件上传权限优化 (#4441)
Refs https://github.com/1Panel-dev/1Panel/issues/4362
2024-04-09 08:36:09 +00:00
zhengkunwang 0751326b46 feat: 优化文件创建 (#4440) 2024-04-09 07:34:08 +00:00
zhengkunwang db4973cfde fix: 解决读取 block 文件报错的问题 (#4439) 2024-04-09 06:28:09 +00:00
zhengkunwang 0b47d692b8 fix: 解决 upstream 配置报错的问题 (#4438)
Refs https://github.com/1Panel-dev/1Panel/issues/4258
2024-04-09 06:16:09 +00:00
zhengkunwang 672ef66be8 fix: 解决 docker-compose 文件错误导致的异常 (#4431) 2024-04-08 09:54:07 +00:00
ssongliu 7ab005540b fix: 切换菜单设置接口 (#4429) 2024-04-08 09:48:08 +00:00
clonefetch 7c928735cd chore: remove repetitive word (#4430)
Signed-off-by: clonefetch <c0217@outlook.com>
2024-04-08 17:47:17 +08:00
zhengkunwang 9ec1610f34 feat: 增加一些翻译 (#4428) 2024-04-08 09:06:08 +00:00
ssongliu 4f2a340212 fix: 修改监控相关接口 (#4427) 2024-04-08 08:52:07 +00:00
ssongliu e69fd3b388 pref: 优化 OSS 连接检测错误信息 (#4426) 2024-04-08 06:44:07 +00:00
ssongliu 6991c773da feat: 监控数据使用独立数据库 (#4424) 2024-04-08 05:48:07 +00:00
ssongliu 9b88979b69 fix: 解决替换根目录失败的问题 (#4420) 2024-04-07 23:21:15 +08:00
ssongliu 7c9608727d fix: 解决升级失败的问题 (#4419) 2024-04-07 14:09:46 +00:00
ssongliu 62828c464e fix: 解决文件保存失败一直处于加载中的问题 (#4418) 2024-04-07 21:38:59 +08:00
ssongliu f1ed051135 feat: 增加概览页跳转方式 (#4417) 2024-04-07 09:29:46 +00:00
zhengkunwang 33a0c2ba30 feat: 删除网站适配防篡改 (#4415) 2024-04-07 09:25:46 +00:00
ssongliu 494da3c209 fix: 修改 xpack 依赖 (#4414) 2024-04-07 08:29:46 +00:00
wanghe 646fc4e678 Update .gitattributes 2024-04-07 11:59:42 +08:00
ssongliu cdbe24a523 fix: 修改升级等过程中复制方式 (#4402) 2024-04-06 14:32:10 +00:00
maninhill 10012c6148 chore:更新 README 2024-04-04 08:28:43 +08:00
zhengkunwang bb4fd69f3a fix: 解决没有 xpack 打包报错的问题 (#4400) 2024-04-03 09:40:07 +00:00
John Broandzhoujunhong 6ff0f14841 fix: 专业版菜单选择显示前端优化 (#4399)
Co-authored-by: zhoujunhong <1298308460@qq.com>
2024-04-03 17:27:55 +08:00
ssongliu 1088034a5c feat: 计划任务容器中执行增加 Command 选项 (#4398)
Refs #4363
2024-04-03 08:50:07 +00:00
zhengkunwang 8a011b1cf9 feat: 优化文件上传体验 (#4396) 2024-04-03 08:40:07 +00:00
Node e3eebc938a 提升文件上传体验 (#4393)
* 上传文件支持覆盖被占用的文件且上传失败时不影响原文件,增加强制覆盖选项

* 原文件存在时,保持同样的文件权限

* 制表符更新
2024-04-03 14:37:22 +08:00
John Bro 08da7802c6 feat: 专业版菜单隐藏控制功能实现 (#4392) 2024-04-03 14:11:12 +08:00
ssongliu 79ca1b4d6b pref: 专业版界面增加图标显示 (#4391) 2024-04-02 09:16:46 +00:00
ssongliu 47119e508b fix: 解决容器挂载卷回显失败的问题 (#4389)
Refs #4387
2024-04-02 09:14:45 +00:00
ssongliu 13bb5e0d30 pref: 优化界面设置提示信息 (#4382) 2024-04-01 10:52:06 +00:00
zhengkunwang 218c165c26 feat: 删除部分 .gitignore (#4380) 2024-04-01 09:52:07 +00:00
zhengkunwang d6006d34c0 feat: 修改 WAF 专业版提示 (#4377) 2024-04-01 08:38:08 +00:00
ssongliu 810ea03ac1 pref: 优化 Xpack 错误信息 (#4376) 2024-04-01 08:28:07 +00:00
ssongliu e046315616 pref: 修改描述信息 (#4375) 2024-04-01 08:00:08 +00:00
zhengkunwang 0a7f1eb286 fix: 处理时序攻击漏洞 (#4373) 2024-04-01 07:52:07 +00:00
wanghe-fit2cloud b0411b2df6 feat: 增加构建标签 2024-04-01 15:22:51 +08:00
ssongliu 471b7d9657 style: 调整 Logo 样式 (#4357) 2024-03-29 09:24:07 +00:00
ssongliu 1527c63d37 feat: 同步数据库中界面设置数据 (#4355) 2024-03-29 07:50:06 +00:00
zhengkunwang 0ebeeaebd6 fix: 解决 WAF 首次安装创建网站没有生效的问题 (#4354) 2024-03-29 07:04:06 +00:00
zhengkunwang 1ae5bef40d feat: 调整 waf 配置文件生成顺序 (#4339) 2024-03-28 08:10:07 +00:00
zhengkunwang 9b9fe5444d feat: 创建网站增加 waf 关联 (#4333) 2024-03-27 10:36:08 +00:00
ssongliu a5d853c61a fix: 解决界面设置无法上传大图片的问题 (#4332) 2024-03-27 10:34:07 +00:00
zhengkunwang 6610a3debe fix: 解决前端打包报错问题 (#4331) 2024-03-27 10:26:07 +00:00
zhengkunwang 6b244b2b03 feat: 删除旧版本 WAF 配置 (#4327) 2024-03-27 07:42:07 +00:00
刘瑞斌 0b26d70927 chore: 增加 JetBrains Toolbox 图标 (#4325) 2024-03-27 13:47:05 +08:00
ssongliu 18faed79f3 style: 调整 Xpack 界面样式与菜单位置 (#4323) 2024-03-27 03:32:07 +00:00
zhengkunwang b0cc1248f0 feat: 删除 xpack 的翻译 (#4322) 2024-03-27 03:24:06 +00:00
ssongliu 8013c7fd96 fix: 修改 Xpack 路由路径 (#4317) 2024-03-26 09:40:07 +00:00
ssongliu 306082d625 feat: 完善界面设置功能 (#4316) 2024-03-26 08:44:07 +00:00
刘瑞斌 821b8dfe63 chore: 去掉某些字体 (#4315) 2024-03-26 16:15:58 +08:00
ssongliu 8d88ed812c fix: 修改 License 状态判断 (#4309) 2024-03-26 06:08:07 +00:00
zhengkunwang aa93a6d373 feat: 删除 plugins 目录 (#4311) 2024-03-26 06:00:07 +00:00
ssongliu b4e8c8503c feat: 调整 License 导入样式 (#4305) 2024-03-25 22:49:39 +08:00
zhengkunwang 4de733e084 feat: 优化网站目录显示 (#4301)
Refs https://github.com/1Panel-dev/1Panel/issues/4280
2024-03-25 09:58:07 +00:00
ssongliu 6c67dc6ed4 feat: 完善 License 导入错误显示 (#4297) 2024-03-25 07:12:07 +00:00
ssongliu ba6991bfb6 feat: 增加 License 导入界面 (#4295) 2024-03-25 04:20:07 +00:00
zhengkunwang 33309d2d93 feat(waf): 优化配置文件读取方式 (#4288) 2024-03-23 14:04:06 +00:00
zhengkunwang 1b2208a4d0 feat(waf): 增加 IP 组功能 (#4277) 2024-03-22 10:58:08 +00:00
刘瑞斌 e3fb97fc8c ci: 替换过时的组件 (#4273) 2024-03-22 14:33:48 +08:00
an4er 1ff5bf85e9 fix: 解决命令注入waf被绕过的问题 (#4268) 2024-03-22 14:18:47 +08:00
wanghe-fit2cloud 062b2f2ae6 chore: 修改同步仓库地址 2024-03-22 14:17:40 +08:00
wanghe-fit2cloud 44445aca93 chore: 修改使用手册显示信息 2024-03-21 22:36:32 +08:00
dependabot[bot] 94de830637 chore(deps): bump github.com/docker/docker (#4256)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 25.0.3+incompatible to 25.0.5+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v25.0.3...v25.0.5)

---
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-03-21 18:54:22 +08:00
Shang Yuanchun 9160726127 解决部分操作系统上 platformVersion 为空导致的显示问题 (#4259)
* fix: 解决部分操作系统上 platformVersion 为空导致的显示问题

例如archlinux,显示的是:arch-

* fix: 修改表达式 (#4259)
2024-03-21 18:54:13 +08:00
wanghe 3cd0c89c4c Update CONTRIBUTING.md 2024-03-20 22:36:07 +08:00
ssongliu c27fcf18fc feat: 升级依赖 fit2cloud-ui-plus (#4250) 2024-03-20 10:50:07 +00:00
zhengkunwang b1f20d125e fix: 解决 i18n 导致页面错误的问题 (#4247) 2024-03-20 08:22:07 +00:00
zhengkunwang cd43bff44d fix: 解决文件选择框关闭再打开依然选中的问题 (#4243) 2024-03-20 03:32:07 +00:00
ssongliu 8cf8375911 feat: 镜像拉取日志 Unicode 转义 (#4239) 2024-03-19 14:40:06 +00:00
zhengkunwang ebc3195df4 feat: ACL 规则增加匹配方式 (#4237) 2024-03-19 12:58:06 +00:00
zhengkunwang ce69f6a142 fix: 解决应用安装快速跳转失败的问题 (#4236) 2024-03-19 09:12:07 +00:00
zhengkunwang f6777d51b5 fix: 解决应用安装快速跳转失败的问题 (#4235) 2024-03-19 07:36:06 +00:00
zhengkunwang d916f2ba6f fix: 解决网站配置文件更新之后参数丢失的问题 (#4231)
Refs https://github.com/1Panel-dev/1Panel/issues/4171
2024-03-19 05:50:06 +00:00
zhengkunwang 0e159f944a feat: 修改应用已安装和可升级页面的标签选择 (#4229) 2024-03-18 14:34:10 +00:00
Anye 329bb94d3c 增加PHP模板 (#4227)
* PHP模板增加“苹果CMS-V10”

* PHP增加“SeaCMS”扩展模板
2024-03-18 22:32:06 +08:00
zhengkunwang 7f2f7fa195 feat(waf): 优化配置更新接口 (#4226) 2024-03-18 13:16:10 +00:00
ssongliu 67c79bcde4 fix: 还原 Makefile 内容 (#4225) 2024-03-18 09:50:08 +00:00
zhengkunwang d7d7c7fd73 feat(waf): 增加内部接口请求安全性 (#4224) 2024-03-18 09:36:08 +00:00
ssongliu 94028d97cf style: 修改重启面板及服务器按钮位置 (#4221) 2024-03-18 09:34:08 +00:00
zhengkunwang cbfedb5d5e feat(waf): 优化请求次数记录方式 (#4210) 2024-03-16 14:44:06 +00:00
ssongliu 7784ee9575 fix: 解决快照恢复时校验 daemon.json 路径失败的问题 (#4205)
Refs #4206
2024-03-16 02:12:06 +00:00
ssongliu 29d5d82fa5 feat: 增加 xpack Makefile (#4204) 2024-03-15 09:26:08 +00:00
zhengkunwang 46f01032b8 fix: 解决回收站文件放置错误问题 (#4203)
Refs https://github.com/1Panel-dev/1Panel/issues/4199
2024-03-15 08:40:07 +00:00
zhengkunwang 341f45b4ef feat: 修改网站导入证书的描述 (#4202)
Refs https://github.com/1Panel-dev/1Panel/issues/4184
2024-03-15 08:08:07 +00:00
ssongliu a0907592b2 fix: 解决未登录情况下的页面跳转问题 (#4195) 2024-03-15 08:00:08 +00:00
zhengkunwang 825ebd19fb feat: 守护进程增加加载状态 (#4201)
Refs https://github.com/1Panel-dev/1Panel/issues/4129
2024-03-15 07:40:08 +00:00
zhengkunwang 8f24432a01 feat: 网站搜索增加网站关联域名匹配 (#4198)
Refs https://github.com/1Panel-dev/1Panel/pull/4198
2024-03-15 07:38:08 +00:00
zhengkunwang 6ce35b723b fix: 解决 Node 运行环境状态异常问题 (#4197) 2024-03-15 07:36:07 +00:00
ssongliu a33eabb4f0 fix: 移除计划任务报告中的 ANSI 转义 (#4193)
Refs #4097
2024-03-15 03:56:07 +00:00
ssongliu 34a6587ebc fix: 升级依赖 jackc/pgx/v5 (#4190) 2024-03-15 02:30:07 +00:00
ssongliu 17020ffe9f fix: 解决计划任务列表备份账号显示问题 (#4187)
Refs #4096
2024-03-14 10:24:07 +00:00
ssongliu 0f9b0d5d82 feat: 同步数据库时标记已删除数据库 (#4185)
Refs #4165
2024-03-14 10:22:07 +00:00
zhengkunwang be36103475 feat: 增加应用商店显示的过滤数量 (#4183) 2024-03-14 08:02:06 +00:00
ssongliu a3b5869c2b fix: 移除未使用依赖 2024-03-14 15:19:39 +08:00
maninhill 0807da41a5 Update README.md 2024-03-14 15:19:04 +08:00
maninhill ea6870e3c9 Update README.md 2024-03-14 15:07:13 +08:00
ssongliu 70b7daf23a fix: 升级依赖 google.golang.org/protobuf (#4176) 2024-03-14 03:24:06 +00:00
建国 1d01fa1e14 fix: 解决计划任务报告刷新异常的问题 (#4177)
Refs #4175
2024-03-14 03:14:06 +00:00
Akirami b643fa2ca3 fix: 修复应用商店详情页面部分图标比例错误 (#4168)
* fix: 修复应用商店详情页面部分图标比例错误

* fix: 替换为内联样式
2024-03-13 22:28:03 +08:00
zhengkunwang 77f4f64f0d feat(waf): 优化规则 (#4166) 2024-03-12 12:34:07 +00:00
ssongliu 3b697c7520 feat: 快照支持设置排除目录 (#4154)
Refs #4076
2024-03-12 03:04:08 +00:00
刘瑞斌 8ca6b9b6fa chore: add pnpm-lock.yaml to .gitignore (#4162) 2024-03-12 10:40:37 +08:00
zhengkunwang 0b66be7a3a fix: 解决安装应用过程中出现的状态异常问题 (#4156) 2024-03-11 12:34:08 +00:00
ssongliu f9f68a975c feat: 增加界面设置功能 (#4128) 2024-03-11 09:12:08 +00:00
L1nyz-tel ba63907a65 fix: 解决命令注入waf被绕过的问题 (#4131) 2024-03-08 22:43:56 +08:00
zhengkunwang 2687c91e65 feat: 升级 github.com/go-jose/go-jose/v3 版本到 v3.0.3 (#4126) 2024-03-08 08:50:06 +00:00
zhengkunwang eb82c168c5 feat: 升级 github.com/go-jose/go-jose/v3 版本到 v3.0.3 (#4121) 2024-03-08 08:14:07 +00:00
ssongliu 21b1242a94 fix: 解决部分 OneDrive 上传大文件失败的问题 (#4117)
Refs #4125
2024-03-08 08:12:06 +00:00
zhengkunwang a6784772b9 feat: 增加一个外部访问地址校验规则 (#4109)
Refs https://github.com/1Panel-dev/1Panel/issues/4091
2024-03-07 10:19:06 +00:00
igophper cf4fcaa07f fix: 解决编辑ssh端口同步到终端本地服务器问题 (#4043) (#4093) 2024-03-07 11:48:00 +08:00
zhengkunwang 62b1bb3191 feat: 升级 element-plus 到 2.6.0 (#4092) 2024-03-07 03:07:06 +00:00
ssongliu 8ce066a9d4 fix: 修改升级过程中的架构判断 (#4102) 2024-03-07 03:05:05 +00:00
wanghe-fit2cloud 7d8da406cf feat: 增加论坛链接 2024-03-07 10:52:00 +08:00
mobeicanyue b3d6b2b0de Issue 跳转 GitHub discussions (#4090) 2024-03-07 10:47:53 +08:00
zhengkunwang 21f24f2e10 feat(waf): 增加全局设置 (#4088) 2024-03-06 10:16:08 +00:00
611 changed files with 18438 additions and 32909 deletions
+5 -1
View File
@@ -1 +1,5 @@
*.ts linguist-language=Go
*.ts linguist-language=Go
*.js linguist-language=Go
*.css linguist-language=Go
*.scss linguist-language=Go
*.html linguist-language=Go
+5 -5
View File
@@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- 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 项目有其他问题
url: https://1panel.cn
about: 如果想要进一步了解 1Panel 项目,欢迎发送邮件到 support@fit2cloud.com 进行提问。
- name: 反馈安全问题 (Report Security Bug)
url: mailto://support@fit2cloud.com
about: 请通过邮箱 support@fit2cloud.com 反馈安全问题 (Please report security vulnerabilities to support@fit2cloud.com)
url: https://bbs.fit2cloud.com/c/1p/7
about: 如果你对 1Panel 有其他想要提问的,我们欢迎到我们的官方社区进行提问。
-12
View File
@@ -1,12 +0,0 @@
name: 问题咨询
description: 提出针对本项目安装部署、使用及其他方面的相关问题
title: "[QUESTION]"
labels: question
assignees: wanghe-fit2cloud
body:
- type: textarea
id: description
attributes:
label: "请描述您的问题"
validations:
required: true
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
name: 为 PR 添加标签
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: actions-ecosystem/action-add-labels@v1
with:
github_token: ${{ secrets.GITHUBTOKEN }}
+6 -6
View File
@@ -13,11 +13,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: '18.14'
node-version: '20.2'
- name: Build Web
id: build_frontend
run: |
@@ -25,10 +25,10 @@ jobs:
env:
NODE_OPTIONS: --max-old-space-size=8192
- name: Setup Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: '1.20.x'
go-version: '1.22'
- name: Build Server
uses: goreleaser/goreleaser-action@v4
uses: goreleaser/goreleaser-action@v6
with:
args: release --snapshot --clean
+6 -6
View File
@@ -11,9 +11,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: '20.2'
- name: Build Web
@@ -22,15 +22,15 @@ jobs:
env:
NODE_OPTIONS: --max-old-space-size=8192
- name: Setup Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: '1.21.x'
go-version: '1.22'
- name: Build Release
uses: goreleaser/goreleaser-action@v4
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: latest
args: release --skip-publish --clean
args: release --skip=publish --clean
- name: Upload Assets
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
uses: Yikun/hub-mirror-action@master
with:
src: 'github/1Panel-dev'
dst: 'gitee/fit2cloud-xlab'
dst: 'gitee/fit2cloud-feizhiyun'
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
dst_token: ${{ secrets.GITEE_TOKEN }}
static_list: "1Panel"
+13
View File
@@ -0,0 +1,13 @@
name: Typos Check
on: pull_request
jobs:
run:
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v2
- name: Check spelling
uses: crate-ci/typos@master
+4 -6
View File
@@ -16,7 +16,8 @@ build/1panel
*.factorypath
# IntelliJ IDEA
.idea
.idea/*
!.idea/icon.png
*.iws
*.iml
*.ipr
@@ -41,11 +42,7 @@ frontend/src/xpack
backend/xpack
backend/router/entry_xpack.go
backend/server/init_xpack.go
plugins/openresty/waf/db
plugins/openresty/waf/data
plugins/openresty/waf/sites
plugins/openresty/waf/conf/websites.json
backend/utils/xpack/xpack_xpack.go
.history/
dist/
@@ -55,3 +52,4 @@ install.sh
quick_start.sh
cmd/server/fileList.txt
.fileList.txt
1Panel.code-workspace
+1
View File
@@ -13,6 +13,7 @@ builds:
- main: ./cmd/server/main.go
binary: 1panel
flags:
- -tags=xpack
- -trimpath
ldflags:
- -w -s

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

+3 -3
View File
@@ -3,9 +3,9 @@
## Create pull request
PR are always welcome, even if they only contain small fixes like typos or a few lines of code. If there will be a significant effort, please document it as an issue and get a discussion going before starting to work on it.
Please submit a PR broken down into small changes bit by bit. A PR consisting of a lot features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion.
Please submit a PR broken down into small changes bit by bit. A PR consisting of a lot of features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion.
This [development guideline](https://kubeoperator.io/docs/dev/dev_manual/) contains information about repository structure, how to setup development environment, how to run it, and more.
This [development guideline](https://1panel.cn/docs/dev_manual/dev_manual/) contains information about repository structure, how to set up development environment, how to run it, and more.
Note: If you split your pull request to small changes, please make sure any of the changes goes to master will not break anything. Otherwise, it can not be merged until this feature complete.
@@ -22,4 +22,4 @@ When reporting issues, always include:
* Snapshots or log files if needed
Because the issues are open to the public, when submitting files, be sure to remove any sensitive information, e.g. user name, password, IP address, and company name. You can
replace those parts with "REDACTED" or other strings like "****".
replace those parts with "REDACTED" or other strings like "****".
+11 -17
View File
@@ -19,18 +19,10 @@
- **安全可靠**:基于容器管理并部署应用,实现最小的漏洞暴露面,同时提供防火墙和日志审计等功能;
- **一键备份**:支持一键备份和恢复,用户可以将数据备份到各类云端存储介质,永不丢失。
## UI 展示
![UI展示](https://resource.fit2cloud.com/1panel/img/overview.png)
1Panel 三分钟速览:https://www.bilibili.com/video/BV1Mt421n7LZ/
## 快速开始
**在线体验**
- 环境地址:<https://demo.1panel.cn/>
- 用户名:demo
- 密码:1panel
**一键安装**
执行如下命令一键安装 1Panel:
@@ -39,27 +31,29 @@
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
```
如果是用于离线环境,推荐使用 [安装包方式](https://1panel.cn/docs/installation/package_installation/) 进行安装部署。
**学习资料**
- [在线文档](https://1panel.cn/docs/)
- [教学视频](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
- [社区论坛](https://bbs.fit2cloud.com/c/1p/7)
- [如何加入微信交流群?](https://bbs.fit2cloud.com/t/topic/2147)
## 安全说明
## UI 展示
如果您在使用过程中发现任何安全问题,请通过以下方式直接联系我们:
- 邮箱:support@fit2cloud.com
- 电话:400-052-0755
![UI展示](https://resource.fit2cloud.com/1panel/img/overview.png)
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)
## FOSSA Status
## 我们的其他明星项目
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel?ref=badge_large)
- [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/) - 开源自动化测试平台
## License
-6
View File
@@ -3,9 +3,6 @@
如果您发现安全问题,请直接联系我们:
- wanghe@fit2cloud.com
- zhengkun@fit2cloud.com
- support@fit2cloud.com
- 400-052-0755
感谢您的支持!
@@ -14,8 +11,5 @@
All security bugs should be reported to the contact as below:
- wanghe@fit2cloud.com
- zhengkun@fit2cloud.com
- support@fit2cloud.com
- +86 400-052-0755
Thanks for your support!
+1 -1
View File
@@ -137,7 +137,7 @@ func (b *BaseApi) GetAppDetailByID(c *gin.Context) {
// @Accept json
// @Success 200 {object} response.IgnoredApp
// @Security ApiKeyAuth
// @Router /apps/ingored [get]
// @Router /apps/ignored [get]
func (b *BaseApi) GetIgnoredApp(c *gin.Context) {
res, err := appService.GetIgnoredApp()
if err != nil {
+4 -5
View File
@@ -205,14 +205,13 @@ func (b *BaseApi) GetServices(c *gin.Context) {
// @Param appInstallId path integer true "request"
// @Success 200 {array} dto.AppVersion
// @Security ApiKeyAuth
// @Router /apps/installed/:appInstallId/versions [get]
// @Router /apps/installed/update/versions [post]
func (b *BaseApi) GetUpdateVersions(c *gin.Context) {
appInstallId, err := helper.GetIntParamByKey(c, "appInstallId")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
var req request.AppUpdateVersion
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
versions, err := appInstallService.GetUpdateVersions(appInstallId)
versions, err := appInstallService.GetUpdateVersions(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
+23 -1
View File
@@ -8,6 +8,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/middleware"
"github.com/1Panel-dev/1Panel/backend/utils/captcha"
"github.com/1Panel-dev/1Panel/backend/utils/qqwry"
"github.com/gin-gonic/gin"
@@ -118,7 +119,28 @@ func (b *BaseApi) CheckIsSafety(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, status)
if status == "disable" && len(code) != 0 {
helper.ErrorWithDetail(c, constant.CodeErrNotFound, constant.ErrTypeInternalServer, err)
return
}
if status == "unpass" {
if middleware.LoadErrCode("err-entrance") != 200 {
helper.ErrResponse(c, middleware.LoadErrCode("err-entrance"))
return
}
helper.ErrorWithDetail(c, constant.CodeErrEntrance, constant.ErrTypeInternalServer, nil)
return
}
helper.SuccessWithOutData(c)
}
func (b *BaseApi) GetResponsePage(c *gin.Context) {
pageCode, err := authService.GetResponsePage()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, pageCode)
}
// @Tags Auth
+4 -4
View File
@@ -118,11 +118,11 @@ func (b *BaseApi) LoadOneDriveInfo(c *gin.Context) {
// @Summary Delete backup account
// @Description 删除备份账号
// @Accept json
// @Param request body dto.BatchDeleteReq true "request"
// @Param request body dto.OperateByID true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/backup/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":true,"db":"backup_accounts","output_column":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"backup_accounts","output_column":"type","output_value":"types"}],"formatZH":"删除备份账号 [types]","formatEN":"delete backup account [types]"}
func (b *BaseApi) DeleteBackup(c *gin.Context) {
var req dto.OperateByID
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -323,7 +323,7 @@ func (b *BaseApi) Backup(c *gin.Context) {
switch req.Type {
case "app":
if err := backupService.AppBackup(req); err != nil {
if _, err := backupService.AppBackup(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
@@ -343,7 +343,7 @@ func (b *BaseApi) Backup(c *gin.Context) {
return
}
case "redis":
if err := backupService.RedisBackup(); err != nil {
if err := backupService.RedisBackup(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
+86
View File
@@ -29,6 +29,28 @@ func (b *BaseApi) CreateCommand(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Redis Command
// @Summary Save redis command
// @Description 保存 Redis 快速命令
// @Accept json
// @Param request body dto.RedisCommand true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /hosts/command/redis [post]
// @x-panel-log {"bodyKeys":["name","command"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"保存 redis 快捷命令 [name][command]","formatEN":"save quick command for redis [name][command]"}
func (b *BaseApi) SaveRedisCommand(c *gin.Context) {
var req dto.RedisCommand
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := commandService.SaveRedisCommand(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Command
// @Summary Page commands
// @Description 获取快速命令列表分页
@@ -55,6 +77,32 @@ func (b *BaseApi) SearchCommand(c *gin.Context) {
})
}
// @Tags Redis Command
// @Summary Page redis commands
// @Description 获取 redis 快速命令列表分页
// @Accept json
// @Param request body dto.SearchWithPage true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /hosts/command/redis/search [post]
func (b *BaseApi) SearchRedisCommand(c *gin.Context) {
var req dto.SearchWithPage
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := commandService.SearchRedisCommandWithPage(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
// @Tags Command
// @Summary Tree commands
// @Description 获取快速命令树
@@ -72,6 +120,22 @@ func (b *BaseApi) SearchCommandTree(c *gin.Context) {
helper.SuccessWithData(c, list)
}
// @Tags Redis Command
// @Summary List redis commands
// @Description 获取 redis 快速命令列表
// @Success 200 {Array} dto.RedisCommand
// @Security ApiKeyAuth
// @Router /hosts/command/redis [get]
func (b *BaseApi) ListRedisCommand(c *gin.Context) {
list, err := commandService.ListRedisCommand()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Command
// @Summary List commands
// @Description 获取快速命令列表
@@ -110,6 +174,28 @@ func (b *BaseApi) DeleteCommand(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Redis Command
// @Summary Delete redis command
// @Description 删除 redis 快速命令
// @Accept json
// @Param request body dto.BatchDeleteReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /hosts/command/redis/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"redis_commands","output_column":"name","output_value":"names"}],"formatZH":"删除 redis 快捷命令 [names]","formatEN":"delete quick command of redis [names]"}
func (b *BaseApi) DeleteRedisCommand(c *gin.Context) {
var req dto.BatchDeleteReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := commandService.DeleteRedisCommand(req.Ids); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Command
// @Summary Update command
// @Description 更新快速命令
+43 -32
View File
@@ -12,11 +12,17 @@ import (
// @Tags Database Redis
// @Summary Load redis status info
// @Description 获取 redis 状态信息
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Success 200 {object} dto.RedisStatus
// @Security ApiKeyAuth
// @Router /databases/redis/status [get]
// @Router /databases/redis/status [post]
func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
data, err := redisService.LoadStatus()
var req dto.OperationWithName
if err := helper.CheckBind(&req, c); err != nil {
return
}
data, err := redisService.LoadStatus(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -28,11 +34,17 @@ func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
// @Tags Database Redis
// @Summary Load redis conf
// @Description 获取 redis 配置信息
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Success 200 {object} dto.RedisConf
// @Security ApiKeyAuth
// @Router /databases/redis/conf [get]
// @Router /databases/redis/conf [post]
func (b *BaseApi) LoadRedisConf(c *gin.Context) {
data, err := redisService.LoadConf()
var req dto.OperationWithName
if err := helper.CheckBind(&req, c); err != nil {
return
}
data, err := redisService.LoadConf(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -44,11 +56,17 @@ func (b *BaseApi) LoadRedisConf(c *gin.Context) {
// @Tags Database Redis
// @Summary Load redis persistence conf
// @Description 获取 redis 持久化配置
// @Accept json
// @Param request body dto.OperationWithName true "request"
// @Success 200 {object} dto.RedisPersistence
// @Security ApiKeyAuth
// @Router /databases/redis/persistence/conf [get]
// @Router /databases/redis/persistence/conf [post]
func (b *BaseApi) LoadPersistenceConf(c *gin.Context) {
data, err := redisService.LoadPersistenceConf()
var req dto.OperationWithName
if err := helper.CheckBind(&req, c); err != nil {
return
}
data, err := redisService.LoadPersistenceConf(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -57,6 +75,25 @@ func (b *BaseApi) LoadPersistenceConf(c *gin.Context) {
helper.SuccessWithData(c, data)
}
func (b *BaseApi) CheckHasCli(c *gin.Context) {
helper.SuccessWithData(c, redisService.CheckHasCli())
}
// @Tags Database Redis
// @Summary Install redis-cli
// @Description 安装 redis cli
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/redis/install/cli [post]
func (b *BaseApi) InstallCli(c *gin.Context) {
if err := redisService.InstallCli(); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags Database Redis
// @Summary Update redis conf
// @Description 更新 redis 配置信息
@@ -131,29 +168,3 @@ func (b *BaseApi) UpdateRedisPersistenceConf(c *gin.Context) {
}
helper.SuccessWithData(c, nil)
}
// @Tags Database Redis
// @Summary Page redis backups
// @Description 获取 redis 备份记录分页
// @Accept json
// @Param request body dto.PageInfo true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /databases/redis/backup/search [post]
func (b *BaseApi) RedisBackupList(c *gin.Context) {
var req dto.PageInfo
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := redisService.SearchBackupListWithPage(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
+4 -4
View File
@@ -61,7 +61,7 @@ func (b *BaseApi) LoadDaemonJson(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/daemonjson/update [post]
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 配置 [key]=>[value]","formatEN":"Updated the docker daemon.json configuration [key]=>[value]"}
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新配置 [key]","formatEN":"Updated configuration [key]"}
func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -84,7 +84,7 @@ func (b *BaseApi) UpdateDaemonJson(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/logoption/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 日志配置","formatEN":"Updated the docker daemon.json log option"}
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新日志配置","formatEN":"Updated the log option"}
func (b *BaseApi) UpdateLogOption(c *gin.Context) {
var req dto.LogOption
if err := helper.CheckBind(&req, c); err != nil {
@@ -107,7 +107,7 @@ func (b *BaseApi) UpdateLogOption(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/ipv6option/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json ipv6 配置","formatEN":"Updated the docker daemon.json ipv6 option"}
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 ipv6 配置","formatEN":"Updated the ipv6 option"}
func (b *BaseApi) UpdateIpv6Option(c *gin.Context) {
var req dto.Ipv6Option
if err := helper.CheckBind(&req, c); err != nil {
@@ -130,7 +130,7 @@ func (b *BaseApi) UpdateIpv6Option(c *gin.Context) {
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/daemonjson/update/byfile [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新 docker daemon.json 配置","formatEN":"Updated the docker daemon.json configuration"}
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"更新配置文件","formatEN":"Updated configuration file"}
func (b *BaseApi) UpdateDaemonJsonByFile(c *gin.Context) {
var req dto.DaemonJsonUpdateByFile
if err := helper.CheckBindAndValidate(&req, c); err != nil {
+1
View File
@@ -37,6 +37,7 @@ var (
deviceService = service.NewIDeviceService()
fail2banService = service.NewIFail2BanService()
ftpService = service.NewIFtpService()
settingService = service.NewISettingService()
backupService = service.NewIBackupService()
+90 -11
View File
@@ -301,28 +301,83 @@ func (b *BaseApi) UploadFiles(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
files := form.File["file"]
uploadFiles := form.File["file"]
paths := form.Value["path"]
overwrite := true
if ow, ok := form.Value["overwrite"]; ok {
if len(ow) != 0 {
parseBool, _ := strconv.ParseBool(ow[0])
overwrite = parseBool
}
}
if len(paths) == 0 || !strings.Contains(paths[0], "/") {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error paths in request"))
return
}
dir := path.Dir(paths[0])
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
_, err = os.Stat(dir)
if err != nil && os.IsNotExist(err) {
mode, err := files.GetParentMode(dir)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
if err = os.MkdirAll(dir, mode); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("mkdir %s failed, err: %v", dir, err))
return
}
}
info, err := os.Stat(dir)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
mode := info.Mode()
fileOp := files.NewFileOp()
success := 0
failures := make(buserr.MultiErr)
for _, file := range files {
if err := c.SaveUploadedFile(file, path.Join(paths[0], file.Filename)); err != nil {
for _, file := range uploadFiles {
dstFilename := path.Join(paths[0], file.Filename)
dstDir := path.Dir(dstFilename)
if !fileOp.Stat(dstDir) {
if err = fileOp.CreateDir(dstDir, mode); err != nil {
e := fmt.Errorf("create dir [%s] failed, err: %v", path.Dir(dstFilename), err)
failures[file.Filename] = e
global.LOG.Error(e)
continue
}
}
tmpFilename := dstFilename + ".tmp"
if err := c.SaveUploadedFile(file, tmpFilename); err != nil {
_ = os.Remove(tmpFilename)
e := fmt.Errorf("upload [%s] file failed, err: %v", file.Filename, err)
failures[file.Filename] = e
global.LOG.Error(e)
continue
}
dstInfo, statErr := os.Stat(dstFilename)
if overwrite {
_ = os.Remove(dstFilename)
}
err = os.Rename(tmpFilename, dstFilename)
if err != nil {
_ = os.Remove(tmpFilename)
e := fmt.Errorf("upload [%s] file failed, err: %v", file.Filename, err)
failures[file.Filename] = e
global.LOG.Error(e)
continue
}
if statErr == nil {
_ = os.Chmod(dstFilename, dstInfo.Mode())
} else {
_ = os.Chmod(dstFilename, mode)
}
success++
}
if success == 0 {
@@ -431,6 +486,7 @@ func (b *BaseApi) Download(c *gin.Context) {
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
}
defer file.Close()
info, _ := file.Stat()
c.Header("Content-Length", strconv.FormatInt(info.Size(), 10))
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(info.Name()))
@@ -536,20 +592,37 @@ func (b *BaseApi) Size(c *gin.Context) {
helper.SuccessWithData(c, res)
}
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int, overwrite bool) error {
defer func() {
_ = os.RemoveAll(fileDir)
}()
op := files.NewFileOp()
dstDir = strings.TrimSpace(dstDir)
mode, _ := files.GetParentMode(dstDir)
if mode == 0 {
mode = 0755
}
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
if err = op.CreateDir(dstDir, os.ModePerm); err != nil {
if err = op.CreateDir(dstDir, mode); err != nil {
return err
}
}
targetFile, err := os.Create(filepath.Join(dstDir, fileName))
dstFileName := filepath.Join(dstDir, fileName)
dstInfo, statErr := os.Stat(dstFileName)
if statErr == nil {
mode = dstInfo.Mode()
} else {
mode = 0644
}
if overwrite {
_ = os.Remove(dstFileName)
}
targetFile, err := os.OpenFile(dstFileName, os.O_RDWR|os.O_CREATE, mode)
if err != nil {
return err
}
defer targetFile.Close()
for i := 0; i < chunkCount; i++ {
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", fileName, i))
chunkData, err := os.ReadFile(chunkPath)
@@ -560,9 +633,10 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int)
if err != nil {
return err
}
_ = os.Remove(chunkPath)
}
return files.NewFileOp().DeleteDir(fileDir)
return nil
}
// @Tags File
@@ -584,6 +658,7 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
defer uploadFile.Close()
chunkIndex, err := strconv.Atoi(c.PostForm("chunkIndex"))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
@@ -643,7 +718,11 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
}
if chunkIndex+1 == chunkCount {
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount)
overwrite := true
if ow := c.PostForm("overwrite"); ow != "" {
overwrite, _ = strconv.ParseBool(ow)
}
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount, overwrite)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, buserr.WithMap(constant.ErrFileUpload, map[string]interface{}{"name": filename, "detail": err.Error()}, err))
return
+199
View File
@@ -0,0 +1,199 @@
package v1
import (
"encoding/base64"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
// @Tags FTP
// @Summary Load FTP base info
// @Description 获取 FTP 基础信息
// @Success 200 {object} dto.FtpBaseInfo
// @Security ApiKeyAuth
// @Router /toolbox/ftp/base [get]
func (b *BaseApi) LoadFtpBaseInfo(c *gin.Context) {
data, err := ftpService.LoadBaseInfo()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags FTP
// @Summary Load FTP operation log
// @Description 获取 FTP 操作日志
// @Accept json
// @Param request body dto.FtpLogSearch true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /toolbox/ftp/log/search [post]
func (b *BaseApi) LoadFtpLogInfo(c *gin.Context) {
var req dto.FtpLogSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := ftpService.LoadLog(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
// @Tags FTP
// @Summary Operate FTP
// @Description 修改 FTP 状态
// @Accept json
// @Param request body dto.Operate true "request"
// @Security ApiKeyAuth
// @Router /toolbox/ftp/operate [post]
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] FTP","formatEN":"[operation] FTP"}
func (b *BaseApi) OperateFtp(c *gin.Context) {
var req dto.Operate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := ftpService.Operate(req.Operation); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags FTP
// @Summary Page FTP user
// @Description 获取 FTP 账户列表分页
// @Accept json
// @Param request body dto.SearchWithPage true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /toolbox/ftp/search [post]
func (b *BaseApi) SearchFtp(c *gin.Context) {
var req dto.SearchWithPage
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := ftpService.SearchWithPage(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
// @Tags FTP
// @Summary Create FTP user
// @Description 创建 FTP 账户
// @Accept json
// @Param request body dto.FtpCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/ftp [post]
// @x-panel-log {"bodyKeys":["user", "path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 FTP 账户 [user][path]","formatEN":"create FTP [user][path]"}
func (b *BaseApi) CreateFtp(c *gin.Context) {
var req dto.FtpCreate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.Password) != 0 {
pass, err := base64.StdEncoding.DecodeString(req.Password)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Password = string(pass)
}
if _, err := ftpService.Create(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags FTP
// @Summary Delete FTP user
// @Description 删除 FTP 账户
// @Accept json
// @Param request body dto.BatchDeleteReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/ftp/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"ftps","output_column":"user","output_value":"users"}],"formatZH":"删除 FTP 账户 [users]","formatEN":"delete FTP users [users]"}
func (b *BaseApi) DeleteFtp(c *gin.Context) {
var req dto.BatchDeleteReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := ftpService.Delete(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags FTP
// @Summary Sync FTP user
// @Description 同步 FTP 账户
// @Accept json
// @Param request body dto.BatchDeleteReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/ftp/sync [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"同步 FTP 账户","formatEN":"sync FTP users"}
func (b *BaseApi) SyncFtp(c *gin.Context) {
if err := ftpService.Sync(); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
// @Tags FTP
// @Summary Update FTP user
// @Description 修改 FTP 账户
// @Accept json
// @Param request body dto.FtpUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /toolbox/ftp/update [post]
// @x-panel-log {"bodyKeys":["user", "path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 FTP 账户 [user][path]","formatEN":"update FTP [user][path]"}
func (b *BaseApi) UpdateFtp(c *gin.Context) {
var req dto.FtpUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.Password) != 0 {
pass, err := base64.StdEncoding.DecodeString(req.Password)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Password = string(pass)
}
if err := ftpService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
+5
View File
@@ -133,3 +133,8 @@ func CheckBind(req interface{}, c *gin.Context) error {
}
return nil
}
func ErrResponse(ctx *gin.Context, code int) {
ctx.JSON(code, nil)
ctx.Abort()
}
+34 -3
View File
@@ -15,6 +15,13 @@ import (
"github.com/shirou/gopsutil/v3/net"
)
// @Tags Monitor
// @Summary Load monitor datas
// @Description 获取监控数据
// @Param request body dto.MonitorSearch true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /hosts/monitor/search [post]
func (b *BaseApi) LoadMonitor(c *gin.Context) {
var req dto.MonitorSearch
if err := helper.CheckBindAndValidate(&req, c); err != nil {
@@ -28,7 +35,7 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
var backdatas []dto.MonitorData
if req.Param == "all" || req.Param == "cpu" || req.Param == "memory" || req.Param == "load" {
var bases []model.MonitorBase
if err := global.DB.
if err := global.MonitorDB.
Where("created_at > ? AND created_at < ?", req.StartTime, req.EndTime).
Find(&bases).Error; err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -45,7 +52,7 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
}
if req.Param == "all" || req.Param == "io" {
var bases []model.MonitorIO
if err := global.DB.
if err := global.MonitorDB.
Where("created_at > ? AND created_at < ?", req.StartTime, req.EndTime).
Find(&bases).Error; err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -62,7 +69,7 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
}
if req.Param == "all" || req.Param == "network" {
var bases []model.MonitorNetwork
if err := global.DB.
if err := global.MonitorDB.
Where("name = ? AND created_at > ? AND created_at < ?", req.Info, req.StartTime, req.EndTime).
Find(&bases).Error; err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -80,6 +87,30 @@ func (b *BaseApi) LoadMonitor(c *gin.Context) {
helper.SuccessWithData(c, backdatas)
}
// @Tags Monitor
// @Summary Clean monitor datas
// @Description 清空监控数据
// @Success 200
// @Security ApiKeyAuth
// @Router /hosts/monitor/clean [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空监控数据","formatEN":"clean monitor datas"}
func (b *BaseApi) CleanMonitor(c *gin.Context) {
if err := global.MonitorDB.Exec("DELETE FROM monitor_bases").Error; err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
if err := global.MonitorDB.Exec("DELETE FROM monitor_ios").Error; err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
if err := global.MonitorDB.Exec("DELETE FROM monitor_networks").Error; err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) GetNetworkOptions(c *gin.Context) {
netStat, _ := net.IOCounters(true)
var options []string
+30
View File
@@ -76,6 +76,20 @@ func (b *BaseApi) DeleteRuntime(c *gin.Context) {
helper.SuccessWithOutData(c)
}
func (b *BaseApi) DeleteRuntimeCheck(c *gin.Context) {
runTimeId, err := helper.GetIntParamByKey(c, "runTimeId")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
return
}
checkData, err := runtimeService.DeleteCheck(runTimeId)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, checkData)
}
// @Tags Runtime
// @Summary Update runtime
// @Description 更新运行环境
@@ -203,3 +217,19 @@ func (b *BaseApi) OperateNodeModules(c *gin.Context) {
}
helper.SuccessWithOutData(c)
}
// @Tags Runtime
// @Summary Sync runtime status
// @Description 同步运行环境状态
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Router /runtimes/sync [post]
func (b *BaseApi) SyncStatus(c *gin.Context) {
err := runtimeService.SyncRuntimeStatus()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithOutData(c)
}
+54 -24
View File
@@ -1,6 +1,7 @@
package v1
import (
"encoding/base64"
"errors"
"os"
"path"
@@ -60,6 +61,59 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Update proxy setting
// @Description 服务器代理配置
// @Accept json
// @Param request body dto.ProxyUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/proxy/update [post]
// @x-panel-log {"bodyKeys":["proxyUrl","proxyPort"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"服务器代理配置 [proxyPort]:[proxyPort]","formatEN":"set proxy [proxyPort]:[proxyPort]."}
func (b *BaseApi) UpdateProxy(c *gin.Context) {
var req dto.ProxyUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.ProxyPasswd) != 0 && len(req.ProxyType) != 0 {
pass, err := base64.StdEncoding.DecodeString(req.ProxyPasswd)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.ProxyPasswd = string(pass)
}
if err := settingService.UpdateProxy(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Update system setting
// @Description 隐藏高级功能菜单
// @Accept json
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/menu/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"隐藏高级功能菜单","formatEN":"Hide advanced feature menu."}
func (b *BaseApi) UpdateMenu(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := settingService.Update(req.Key, req.Value); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Update system password
// @Description 更新系统登录密码
@@ -227,30 +281,6 @@ func (b *BaseApi) LoadBaseDir(c *gin.Context) {
helper.SuccessWithData(c, global.CONF.System.DataDir)
}
// @Tags System Setting
// @Summary Clean monitor datas
// @Description 清空监控数据
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/monitor/clean [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空监控数据","formatEN":"clean monitor datas"}
func (b *BaseApi) CleanMonitor(c *gin.Context) {
if err := global.DB.Exec("DELETE FROM monitor_bases").Error; err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
if err := global.DB.Exec("DELETE FROM monitor_ios").Error; err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
if err := global.DB.Exec("DELETE FROM monitor_networks").Error; err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Load mfa info
// @Description 获取 mfa 信息
+2 -2
View File
@@ -168,13 +168,13 @@ func (b *BaseApi) RollbackSnapshot(c *gin.Context) {
// @Summary Delete system backup
// @Description 删除系统快照
// @Accept json
// @Param request body dto.BatchDeleteReq true "request"
// @Param request body dto.SnapshotBatchDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/snapshot/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"snapshots","output_column":"name","output_value":"name"}],"formatZH":"删除系统快照 [name]","formatEN":"Delete system backup [name]"}
func (b *BaseApi) DeleteSnapshot(c *gin.Context) {
var req dto.BatchDeleteReq
var req dto.SnapshotBatchDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
+44 -14
View File
@@ -9,6 +9,7 @@ import (
"strings"
"time"
"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"
@@ -78,6 +79,13 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
global.LOG.Errorf("gin context http handler failed, err: %v", err)
return
}
defer wsConn.Close()
if global.CONF.System.IsDemo {
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
return
}
}
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
@@ -87,22 +95,38 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
return
}
redisConf, err := redisService.LoadConf()
if wshandleError(wsConn, errors.WithMessage(err, "load redis container failed")) {
name := c.Query("name")
from := c.Query("from")
commands := []string{"redis-cli"}
database, err := databaseService.Get(name)
if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) {
return
}
defer wsConn.Close()
commands := "redis-cli"
if len(redisConf.Requirepass) != 0 {
commands = fmt.Sprintf("redis-cli -a %s --no-auth-warning", redisConf.Requirepass)
if from == "local" {
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"})
if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) {
return
}
name = redisInfo.ContainerName
if len(database.Password) != 0 {
commands = []string{"redis-cli", "-a", database.Password, "--no-auth-warning"}
}
} else {
itemPort := fmt.Sprintf("%v", database.Port)
commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort}
if len(database.Password) != 0 {
commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort, "-a", database.Password, "--no-auth-warning"}
}
name = "1Panel-redis-cli-tools"
}
pidMap := loadMapFromDockerTop(redisConf.ContainerName)
slave, err := terminal.NewCommand(fmt.Sprintf("docker exec -it %s %s", redisConf.ContainerName, commands))
pidMap := loadMapFromDockerTop(name)
itemCmds := append([]string{"exec", "-it", name}, commands...)
slave, err := terminal.NewCommand(itemCmds)
if wshandleError(wsConn, err) {
return
}
defer killBash(redisConf.ContainerName, commands, pidMap)
defer killBash(name, strings.Join(commands, " "), pidMap)
defer slave.Close()
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
@@ -130,6 +154,12 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
}
defer wsConn.Close()
if global.CONF.System.IsDemo {
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
return
}
}
containerID := c.Query("containerid")
command := c.Query("command")
user := c.Query("user")
@@ -161,9 +191,9 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
return
}
commands := fmt.Sprintf("docker exec -it %s %s", containerID, command)
commands := []string{"exec", "-it", containerID, command}
if len(user) != 0 {
commands = fmt.Sprintf("docker exec -it -u %s %s %s", user, containerID, command)
commands = []string{"exec", "-it", "-u", user, containerID, command}
}
pidMap := loadMapFromDockerTop(containerID)
slave, err := terminal.NewCommand(commands)
@@ -221,10 +251,10 @@ func loadMapFromDockerTop(containerID string) map[string]string {
lines := strings.Split(stdout, "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) != 2 {
if len(parts) < 2 {
continue
}
pidMap[parts[0]] = parts[1]
pidMap[parts[0]] = strings.Join(parts[1:], " ")
}
return pidMap
}
+11 -64
View File
@@ -1,6 +1,8 @@
package v1
import (
"encoding/base64"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
@@ -76,6 +78,14 @@ func (b *BaseApi) CreateWebsite(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if len(req.FtpPassword) != 0 {
pass, err := base64.StdEncoding.DecodeString(req.FtpPassword)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.FtpPassword = string(pass)
}
err := websiteService.CreateWebsite(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -305,69 +315,6 @@ func (b *BaseApi) CreateWebsiteCheck(c *gin.Context) {
helper.SuccessWithData(c, data)
}
// @Tags Website WAF
// @Summary Load websit waf conf
// @Description 获取网站 waf 配置
// @Accept json
// @Param request body request.WebsiteWafReq true "request"
// @Success 200 {object} response.WebsiteWafConfig
// @Security ApiKeyAuth
// @Router /websites/waf/config [post]
func (b *BaseApi) GetWebsiteWafConfig(c *gin.Context) {
var req request.WebsiteWafReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
data, err := websiteService.GetWafConfig(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags Website WAF
// @Summary Update website waf conf
// @Description 更新 网站 waf 配置
// @Accept json
// @Param request body request.WebsiteWafUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/waf/update [post]
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"WAF 配置修改 [domain]","formatEN":"WAF conf update [domain]"}
func (b *BaseApi) UpdateWebsiteWafConfig(c *gin.Context) {
var req request.WebsiteWafUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.UpdateWafConfig(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Website WAF
// @Summary Update website waf file
// @Description 更新 网站 waf 配置文件
// @Accept json
// @Param request body request.WebsiteWafUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/waf/file/update [post]
// @x-panel-log {"bodyKeys":["websiteId"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"websiteId","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"WAF 配置文件修改 [domain]","formatEN":"WAF conf file update [domain]"}
func (b *BaseApi) UpdateWebsiteWafFile(c *gin.Context) {
var req request.WebsiteWafFileUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := websiteService.UpdateWafFile(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Website Nginx
// @Summary Update website nginx conf
// @Description 更新 网站 nginx 配置
@@ -433,7 +380,7 @@ func (b *BaseApi) ChangeDefaultServer(c *gin.Context) {
}
// @Tags Website
// @Summary Load websit php conf
// @Summary Load website php conf
// @Description 获取网站 php 配置
// @Accept json
// @Param id path integer true "request"
+3 -2
View File
@@ -37,8 +37,9 @@ type AppOssConfig struct {
}
type AppVersion struct {
Version string `json:"version"`
DetailId uint `json:"detailId"`
Version string `json:"version"`
DetailId uint `json:"detailId"`
DockerCompose string `json:"dockerCompose"`
}
type AppList struct {
+2
View File
@@ -35,6 +35,7 @@ type CommonBackup struct {
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website postgresql"`
Name string `json:"name"`
DetailName string `json:"detailName"`
Secret string `json:"secret"`
}
type CommonRecover struct {
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive WebDAV"`
@@ -42,6 +43,7 @@ type CommonRecover struct {
Name string `json:"name"`
DetailName string `json:"detailName"`
File string `json:"file"`
Secret string `json:"secret"`
}
type RecordSearch struct {
+7
View File
@@ -6,6 +6,7 @@ type SearchCommandWithPage struct {
Order string `json:"order"`
GroupID uint `json:"groupID"`
Info string `json:"info"`
Name string `json:"name"`
}
type CommandOperate struct {
@@ -29,3 +30,9 @@ type CommandTree struct {
Label string `json:"label"`
Children []CommandInfo `json:"children"`
}
type RedisCommand struct {
ID uint `json:"id"`
Name string `json:"name"`
Command string `json:"command"`
}
+4 -1
View File
@@ -48,6 +48,8 @@ type ContainerOperate struct {
Name string `json:"name" validate:"required"`
Image string `json:"image" validate:"required"`
Network string `json:"network"`
Ipv4 string `json:"ipv4"`
Ipv6 string `json:"ipv6"`
PublishAllPorts bool `json:"publishAllPorts"`
ExposedPorts []PortHelper `json:"exposedPorts"`
Tty bool `json:"tty"`
@@ -98,6 +100,7 @@ type ContainerStats struct {
}
type VolumeHelper struct {
Type string `json:"type"`
SourceDir string `json:"sourceDir"`
ContainerDir string `json:"containerDir"`
Mode string `json:"mode"`
@@ -120,7 +123,7 @@ type ContainerRename struct {
}
type ContainerPrune struct {
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network"`
PruneType string `json:"pruneType" validate:"required,oneof=container image volume network buildcache"`
WithTagAll bool `json:"withTagAll"`
}
+17 -9
View File
@@ -1,6 +1,8 @@
package dto
import "time"
import (
"time"
)
type CronjobCreate struct {
Name string `json:"name" validate:"required"`
@@ -8,6 +10,7 @@ type CronjobCreate struct {
Spec string `json:"spec" validate:"required"`
Script string `json:"script"`
Command string `json:"command"`
ContainerName string `json:"containerName"`
AppID string `json:"appID"`
Website string `json:"website"`
@@ -20,6 +23,7 @@ type CronjobCreate struct {
BackupAccounts string `json:"backupAccounts"`
DefaultDownload string `json:"defaultDownload"`
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
Secret string `json:"secret"`
}
type CronjobUpdate struct {
@@ -28,6 +32,7 @@ type CronjobUpdate struct {
Spec string `json:"spec" validate:"required"`
Script string `json:"script"`
Command string `json:"command"`
ContainerName string `json:"containerName"`
AppID string `json:"appID"`
Website string `json:"website"`
@@ -40,6 +45,7 @@ type CronjobUpdate struct {
BackupAccounts string `json:"backupAccounts"`
DefaultDownload string `json:"defaultDownload"`
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
Secret string `json:"secret"`
}
type CronjobUpdateStatus struct {
@@ -70,6 +76,7 @@ type CronjobInfo struct {
Spec string `json:"spec"`
Script string `json:"script"`
Command string `json:"command"`
ContainerName string `json:"containerName"`
AppID string `json:"appID"`
Website string `json:"website"`
@@ -84,6 +91,7 @@ type CronjobInfo struct {
LastRecordTime string `json:"lastRecordTime"`
Status string `json:"status"`
Secret string `json:"secret"`
}
type SearchRecord struct {
@@ -95,12 +103,12 @@ type SearchRecord struct {
}
type Record struct {
ID uint `json:"id"`
StartTime time.Time `json:"startTime"`
Records string `json:"records"`
Status string `json:"status"`
Message string `json:"message"`
TargetPath string `json:"targetPath"`
Interval int `json:"interval"`
File string `json:"file"`
ID uint `json:"id"`
StartTime string `json:"startTime"`
Records string `json:"records"`
Status string `json:"status"`
Message string `json:"message"`
TargetPath string `json:"targetPath"`
Interval int `json:"interval"`
File string `json:"file"`
}
+17
View File
@@ -69,6 +69,8 @@ type DashboardCurrent struct {
NetBytesSent uint64 `json:"netBytesSent"`
NetBytesRecv uint64 `json:"netBytesRecv"`
GPUData []GPUInfo `json:"gpuData"`
ShotTime time.Time `json:"shotTime"`
}
@@ -86,3 +88,18 @@ type DiskInfo struct {
InodesFree uint64 `json:"inodesFree"`
InodesUsedPercent float64 `json:"inodesUsedPercent"`
}
type GPUInfo struct {
Index uint `json:"index"`
ProductName string `json:"productName"`
GPUUtil string `json:"gpuUtil"`
Temperature string `json:"temperature"`
PerformanceState string `json:"performanceState"`
PowerUsage string `json:"powerUsage"`
PowerDraw string `json:"powerDraw"`
MaxPowerLimit string `json:"maxPowerLimit"`
MemoryUsage string `json:"memoryUsage"`
MemUsed string `json:"memUsed"`
MemTotal string `json:"memTotal"`
FanSpeed string `json:"fanSpeed"`
}
+13 -9
View File
@@ -41,7 +41,7 @@ type MysqlDBInfo struct {
Username string `json:"username"`
Password string `json:"password"`
Permission string `json:"permission"`
BackupCount int `json:"backupCount"`
IsDelete bool `json:"isDelete"`
Description string `json:"description"`
}
@@ -131,7 +131,7 @@ type MysqlStatus struct {
}
type MysqlVariables struct {
BinlogCachSize string `json:"binlog_cache_size"`
BinlogCacheSize string `json:"binlog_cache_size"`
InnodbBufferPoolSize string `json:"innodb_buffer_pool_size"`
InnodbLogBufferSize string `json:"innodb_log_buffer_size"`
JoinBufferSize string `json:"join_buffer_size"`
@@ -165,26 +165,26 @@ type MysqlVariablesUpdateHelper struct {
// redis
type ChangeRedisPass struct {
Value string `json:"value" validate:"required"`
Database string `json:"database" validate:"required"`
Value string `json:"value"`
}
type RedisConfUpdate struct {
Database string `json:"database" validate:"required"`
Timeout string `json:"timeout"`
Maxclients string `json:"maxclients"`
Maxmemory string `json:"maxmemory"`
}
type RedisConfPersistenceUpdate struct {
Database string `json:"database" validate:"required"`
Type string `json:"type" validate:"required,oneof=aof rbd"`
Appendonly string `json:"appendonly"`
Appendfsync string `json:"appendfsync"`
Save string `json:"save"`
}
type RedisConfUpdateByFile struct {
File string `json:"file" validate:"required"`
RestartNow bool `json:"restartNow"`
}
type RedisConf struct {
Database string `json:"database" validate:"required"`
Name string `json:"name"`
Port int64 `json:"port"`
ContainerName string `json:"containerName"`
@@ -195,12 +195,14 @@ type RedisConf struct {
}
type RedisPersistence struct {
Database string `json:"database" validate:"required"`
Appendonly string `json:"appendonly"`
Appendfsync string `json:"appendfsync"`
Save string `json:"save"`
}
type RedisStatus struct {
Database string `json:"database" validate:"required"`
TcpPort string `json:"tcp_port"`
UptimeInDays string `json:"uptime_in_days"`
ConnectedClients string `json:"connected_clients"`
@@ -217,12 +219,14 @@ type RedisStatus struct {
}
type DatabaseFileRecords struct {
Database string `json:"database" validate:"required"`
FileName string `json:"fileName"`
FileDir string `json:"fileDir"`
CreatedAt string `json:"createdAt"`
Size int `json:"size"`
}
type RedisBackupRecover struct {
Database string `json:"database" validate:"required"`
FileName string `json:"fileName"`
FileDir string `json:"fileDir"`
}
@@ -281,7 +285,7 @@ type DatabaseCreate struct {
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Password string `json:"password"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
@@ -299,7 +303,7 @@ type DatabaseUpdate struct {
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Password string `json:"password"`
SSL bool `json:"ssl"`
RootCert string `json:"rootCert"`
+1 -1
View File
@@ -20,7 +20,7 @@ type PostgresqlDBInfo struct {
Username string `json:"username"`
Password string `json:"password"`
SuperUser bool `json:"superUser"`
BackupCount int `json:"backupCount"`
IsDelete bool `json:"isDelete"`
Description string `json:"description"`
}
+43
View File
@@ -0,0 +1,43 @@
package dto
import (
"time"
)
type FtpInfo struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
User string `json:"user"`
Password string `json:"password"`
Path string `json:"path"`
Status string `json:"status"`
Description string `json:"description"`
}
type FtpBaseInfo struct {
IsActive bool `json:"isActive"`
IsExist bool `json:"isExist"`
}
type FtpLogSearch struct {
PageInfo
User string `json:"user"`
Operation string `json:"operation"`
}
type FtpCreate struct {
User string `json:"user" validate:"required"`
Password string `json:"password" validate:"required"`
Path string `json:"path" validate:"required"`
Description string `json:"description"`
}
type FtpUpdate struct {
ID uint `json:"id"`
Password string `json:"password" validate:"required"`
Path string `json:"path" validate:"required"`
Status string `json:"status"`
Description string `json:"description"`
}
+24 -9
View File
@@ -43,6 +43,7 @@ type AppInstalledSearch struct {
Update bool `json:"update"`
Unused bool `json:"unused"`
All bool `json:"all"`
Sync bool `json:"sync"`
}
type AppInstalledInfo struct {
@@ -60,15 +61,24 @@ type AppBackupDelete struct {
}
type AppInstalledOperate struct {
InstallId uint `json:"installId" validate:"required"`
BackupId uint `json:"backupId"`
DetailId uint `json:"detailId"`
Operate constant.AppOperate `json:"operate" validate:"required"`
ForceDelete bool `json:"forceDelete"`
DeleteBackup bool `json:"deleteBackup"`
DeleteDB bool `json:"deleteDB"`
Backup bool `json:"backup"`
PullImage bool `json:"pullImage"`
InstallId uint `json:"installId" validate:"required"`
BackupId uint `json:"backupId"`
DetailId uint `json:"detailId"`
Operate constant.AppOperate `json:"operate" validate:"required"`
ForceDelete bool `json:"forceDelete"`
DeleteBackup bool `json:"deleteBackup"`
DeleteDB bool `json:"deleteDB"`
Backup bool `json:"backup"`
PullImage bool `json:"pullImage"`
DockerCompose string `json:"dockerCompose"`
}
type AppInstallUpgrade struct {
InstallID uint `json:"installId"`
DetailID uint `json:"detailId"`
Backup bool `json:"backup"`
PullImage bool `json:"pullImage"`
DockerCompose string `json:"dockerCompose"`
}
type AppInstalledUpdate struct {
@@ -87,3 +97,8 @@ type PortUpdate struct {
Name string `json:"name"`
Port int64 `json:"port"`
}
type AppUpdateVersion struct {
AppInstallID uint `json:"appInstallID" validate:"required"`
UpdateVersion string `json:"updateVersion"`
}
+6 -4
View File
@@ -22,7 +22,7 @@ type FileCreate struct {
Path string `json:"path" validate:"required"`
Content string `json:"content"`
IsDir bool `json:"isDir"`
Mode int64 `json:"mode" validate:"required"`
Mode int64 `json:"mode"`
IsLink bool `json:"isLink"`
IsSymlink bool `json:"isSymlink"`
LinkPath string `json:"linkPath"`
@@ -54,12 +54,14 @@ type FileCompress struct {
Type string `json:"type" validate:"required"`
Name string `json:"name" validate:"required"`
Replace bool `json:"replace"`
Secret string `json:"secret"`
}
type FileDeCompress struct {
Dst string `json:"dst" validate:"required"`
Type string `json:"type" validate:"required"`
Path string `json:"path" validate:"required"`
Dst string `json:"dst" validate:"required"`
Type string `json:"type" validate:"required"`
Path string `json:"path" validate:"required"`
Secret string `json:"secret"`
}
type FileEdit struct {
+1 -1
View File
@@ -27,7 +27,7 @@ type NginxRewriteReq struct {
type NginxRewriteUpdate struct {
WebsiteID uint `json:"websiteId" validate:"required"`
Name string `json:"name" validate:"required"`
Content string `json:"content" validate:"required"`
Content string `json:"content"`
}
type NginxProxyUpdate struct {
+10 -19
View File
@@ -27,6 +27,9 @@ type WebsiteCreate struct {
AppID uint `json:"appID"`
AppInstallID uint `json:"appInstallID"`
FtpUser string `json:"ftpUser"`
FtpPassword string `json:"ftpPassword"`
RuntimeID uint `json:"runtimeID"`
RuntimeConfig
}
@@ -69,24 +72,12 @@ type WebsiteOp struct {
Operate string `json:"operate"`
}
type WebsiteWafReq struct {
WebsiteID uint `json:"websiteId" validate:"required"`
Key string `json:"key" validate:"required"`
Rule string `json:"rule" validate:"required"`
}
type WebsiteRedirectUpdate struct {
WebsiteID uint `json:"websiteId" validate:"required"`
Key string `json:"key" validate:"required"`
Enable bool `json:"enable"`
}
type WebsiteWafUpdate struct {
WebsiteID uint `json:"websiteId" validate:"required"`
Key string `json:"key" validate:"required"`
Enable bool `json:"enable"`
}
type WebsiteRecover struct {
WebsiteName string `json:"websiteName" validate:"required"`
Type string `json:"type" validate:"required"`
@@ -190,7 +181,7 @@ type WebsiteProxyConfig struct {
CacheTime int `json:"cacheTime" `
CacheUnit string `json:"cacheUnit"`
Name string `json:"name" validate:"required"`
Modifier string `json:"modifier" validate:"required"`
Modifier string `json:"modifier"`
Match string `json:"match" validate:"required"`
ProxyPass string `json:"proxyPass" validate:"required"`
ProxyHost string `json:"proxyHost" validate:"required"`
@@ -207,12 +198,12 @@ type WebsiteRedirectReq struct {
WebsiteID uint `json:"websiteId" validate:"required"`
}
type WebsiteWafFileUpdate struct {
WebsiteID uint `json:"websiteID" validate:"required"`
Content string `json:"content" validate:"required"`
Type string `json:"type" validate:"required,oneof=cc ip_white ip_block url_white url_block cookie_block args_check post_check ua_check file_ext_block user_agent"`
}
type WebsiteCommonReq struct {
ID uint `json:"id" validate:"required"`
}
type WafWebsite struct {
Key string `json:"key"`
Domains []string `json:"domains"`
Host []string `json:"host"`
}
+23 -5
View File
@@ -20,6 +20,10 @@ type WebsiteSSLCreate struct {
Dir string `json:"dir"`
ID uint `json:"id"`
Description string `json:"description"`
DisableCNAME bool `json:"disableCNAME"`
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
}
type WebsiteDNSReq struct {
@@ -32,8 +36,9 @@ type WebsiteSSLRenew struct {
}
type WebsiteSSLApply struct {
ID uint `json:"ID" validate:"required"`
SkipDNSCheck bool `json:"SkipDNSCheck"`
ID uint `json:"ID" validate:"required"`
SkipDNSCheck bool `json:"skipDNSCheck"`
Nameservers []string `json:"nameservers"`
}
type WebsiteAcmeAccountCreate struct {
@@ -66,9 +71,22 @@ type WebsiteBatchDelReq struct {
}
type WebsiteSSLUpdate struct {
ID uint `json:"id" validate:"required"`
AutoRenew bool `json:"autoRenew"`
Description string `json:"description"`
ID uint `json:"id" validate:"required"`
AutoRenew bool `json:"autoRenew"`
Description string `json:"description"`
PrimaryDomain string `json:"primaryDomain" validate:"required"`
OtherDomains string `json:"otherDomains"`
Provider string `json:"provider" validate:"required"`
AcmeAccountID uint `json:"acmeAccountId"`
DnsAccountID uint `json:"dnsAccountId"`
KeyType string `json:"keyType"`
Apply bool `json:"apply"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
DisableCNAME bool `json:"disableCNAME"`
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
}
type WebsiteSSLUpload struct {
+48 -1
View File
@@ -10,7 +10,7 @@ import (
)
type AppRes struct {
Items []*AppDTO `json:"items"`
Items []*AppDto `json:"items"`
Total int64 `json:"total"`
}
@@ -28,6 +28,22 @@ type AppDTO struct {
Tags []model.Tag `json:"tags"`
}
type AppDto struct {
Name string `json:"name"`
Key string `json:"key"`
ID uint `json:"id"`
ShortDescZh string `json:"shortDescZh"`
ShortDescEn string `json:"shortDescEn"`
Icon string `json:"icon"`
Type string `json:"type"`
Status string `json:"status"`
Resource string `json:"resource"`
Installed bool `json:"installed"`
Versions []string `json:"versions"`
Limit int `json:"limit"`
Tags []model.Tag `json:"tags"`
}
type TagDTO struct {
model.Tag
}
@@ -72,7 +88,38 @@ type AppInstalledDTO struct {
Path string `json:"path"`
}
type AppDetail struct {
Website string `json:"website"`
Document string `json:"document"`
Github string `json:"github"`
}
type AppInstallDTO struct {
ID uint `json:"id"`
Name string `json:"name"`
AppID uint `json:"appID"`
AppDetailID uint `json:"appDetailID"`
Version string `json:"version"`
Status string `json:"status"`
Message string `json:"message"`
HttpPort int `json:"httpPort"`
HttpsPort int `json:"httpsPort"`
Path string `json:"path"`
CanUpdate bool `json:"canUpdate"`
Icon string `json:"icon"`
AppName string `json:"appName"`
Ready int `json:"ready"`
Total int `json:"total"`
AppKey string `json:"appKey"`
AppType string `json:"appType"`
AppStatus string `json:"appStatus"`
DockerCompose string `json:"dockerCompose"`
CreatedAt time.Time `json:"createdAt"`
App AppDetail `json:"app"`
}
type DatabaseConn struct {
Status string `json:"status"`
Username string `json:"username"`
Password string `json:"password"`
ContainerName string `json:"containerName"`
-5
View File
@@ -31,11 +31,6 @@ type WebsiteNginxConfig struct {
Params []NginxParam `json:"params"`
}
type WebsiteWafConfig struct {
Enable bool `json:"enable"`
Content string `json:"content"`
}
type WebsiteHTTPS struct {
Enable bool `json:"enable"`
HttpConfig string `json:"httpConfig"`
+42 -3
View File
@@ -8,6 +8,7 @@ type SettingInfo struct {
SystemIP string `json:"systemIP"`
SystemVersion string `json:"systemVersion"`
DockerSockPath string `json:"dockerSockPath"`
DeveloperMode string `json:"developerMode"`
SessionTimeout string `json:"sessionTimeout"`
LocalTime string `json:"localTime"`
@@ -19,6 +20,7 @@ type SettingInfo struct {
BindAddress string `json:"bindAddress"`
PanelName string `json:"panelName"`
Theme string `json:"theme"`
MenuTabs string `json:"menuTabs"`
Language string `json:"language"`
DefaultNetwork string `json:"defaultNetwork"`
LastCleanTime string `json:"lastCleanTime"`
@@ -52,6 +54,17 @@ type SettingInfo struct {
AppStoreSyncStatus string `json:"appStoreSyncStatus"`
FileRecycleBin string `json:"fileRecycleBin"`
SnapshotIgnore string `json:"snapshotIgnore"`
XpackHideMenu string `json:"xpackHideMenu"`
NoAuthSetting string `json:"noAuthSetting"`
ProxyUrl string `json:"proxyUrl"`
ProxyType string `json:"proxyType"`
ProxyPort string `json:"proxyPort"`
ProxyUser string `json:"proxyUser"`
ProxyPasswd string `json:"proxyPasswd"`
ProxyPasswdKeep string `json:"proxyPasswdKeep"`
}
type SettingUpdate struct {
@@ -103,11 +116,17 @@ type SnapshotCreate struct {
From string `json:"from" validate:"required"`
DefaultDownload string `json:"defaultDownload" validate:"required"`
Description string `json:"description" validate:"max=256"`
Secret string `json:"secret"`
}
type SnapshotRecover struct {
IsNew bool `json:"isNew"`
ReDownload bool `json:"reDownload"`
ID uint `json:"id" validate:"required"`
IsNew bool `json:"isNew"`
ReDownload bool `json:"reDownload"`
ID uint `json:"id" validate:"required"`
Secret string `json:"secret"`
}
type SnapshotBatchDelete struct {
DeleteWithFile bool `json:"deleteWithFile"`
Ids []uint `json:"ids" validate:"required"`
}
type SnapshotImport struct {
From string `json:"from"`
@@ -135,6 +154,7 @@ type SnapshotInfo struct {
}
type UpgradeInfo struct {
TestVersion string `json:"testVersion"`
NewVersion string `json:"newVersion"`
LatestVersion string `json:"latestVersion"`
ReleaseNote string `json:"releaseNote"`
@@ -153,11 +173,21 @@ type Upgrade struct {
Version string `json:"version" validate:"required"`
}
type ProxyUpdate struct {
ProxyUrl string `json:"proxyUrl"`
ProxyType string `json:"proxyType"`
ProxyPort string `json:"proxyPort"`
ProxyUser string `json:"proxyUser"`
ProxyPasswd string `json:"proxyPasswd"`
ProxyPasswdKeep string `json:"proxyPasswdKeep"`
}
type CleanData struct {
SystemClean []CleanTree `json:"systemClean"`
UploadClean []CleanTree `json:"uploadClean"`
DownloadClean []CleanTree `json:"downloadClean"`
SystemLogClean []CleanTree `json:"systemLogClean"`
ContainerClean []CleanTree `json:"containerClean"`
}
type CleanTree struct {
@@ -178,3 +208,12 @@ type Clean struct {
Name string `json:"name"`
Size uint64 `json:"size"`
}
type XpackHideMenu struct {
ID string `json:"id"`
Label string `json:"label"`
IsCheck bool `json:"isCheck"`
Title string `json:"title"`
Path string `json:"path,omitempty"`
Children []XpackHideMenu `json:"children,omitempty"`
}
+1 -1
View File
@@ -16,7 +16,7 @@ type App struct {
Type string `json:"type" gorm:"type:varchar(64);not null"`
Status string `json:"status" gorm:"type:varchar(64);not null"`
Required string `json:"required" gorm:"type:varchar(64);"`
CrossVersionUpdate bool `json:"crossVersionUpdate"`
CrossVersionUpdate bool `json:"crossVersionUpdate" yaml:"crossVersionUpdate"`
Limit int `json:"limit" gorm:"type:Integer;not null"`
Website string `json:"website" gorm:"type:varchar(64);not null"`
Github string `json:"github" gorm:"type:varchar(64);not null"`
+6
View File
@@ -6,3 +6,9 @@ type Command struct {
GroupID uint `gorm:"type:decimal" json:"groupID"`
Command string `gorm:"type:varchar(256);not null" json:"command"`
}
type RedisCommand struct {
BaseModel
Name string `gorm:"type:varchar(64);unique;not null" json:"name"`
Command string `gorm:"type:varchar(256);not null" json:"command"`
}
+5 -1
View File
@@ -1,6 +1,8 @@
package model
import "time"
import (
"time"
)
type Cronjob struct {
BaseModel
@@ -9,6 +11,7 @@ type Cronjob struct {
Type string `gorm:"type:varchar(64);not null" json:"type"`
Spec string `gorm:"type:varchar(64);not null" json:"spec"`
Command string `gorm:"type:varchar(64)" json:"command"`
ContainerName string `gorm:"type:varchar(64)" json:"containerName"`
Script string `gorm:"longtext" json:"script"`
Website string `gorm:"type:varchar(64)" json:"website"`
@@ -30,6 +33,7 @@ type Cronjob struct {
Status string `gorm:"type:varchar(64)" json:"status"`
EntryIDs string `gorm:"type:varchar(64)" json:"entryIDs"`
Records []JobRecords `json:"records"`
Secret string `gorm:"type:varchar(64)" json:"secret"`
}
type JobRecords struct {
+1
View File
@@ -9,5 +9,6 @@ type DatabaseMysql struct {
Username string `json:"username" gorm:"type:varchar(256);not null"`
Password string `json:"password" gorm:"type:varchar(256);not null"`
Permission string `json:"permission" gorm:"type:varchar(256);not null"`
IsDelete bool `json:"isDelete" gorm:"type:varchar(64)"`
Description string `json:"description" gorm:"type:varchar(256);"`
}
+2 -1
View File
@@ -8,6 +8,7 @@ type DatabasePostgresql struct {
Format string `json:"format" gorm:"type:varchar(64);not null"`
Username string `json:"username" gorm:"type:varchar(256);not null"`
Password string `json:"password" gorm:"type:varchar(256);not null"`
SuperUser bool `json:"superUser" gorm:"type:varchar(64)" `
SuperUser bool `json:"superUser" gorm:"type:varchar(64)"`
IsDelete bool `json:"isDelete" gorm:"type:varchar(64)"`
Description string `json:"description" gorm:"type:varchar(256);"`
}
+11
View File
@@ -0,0 +1,11 @@
package model
type Ftp struct {
BaseModel
User string `gorm:"type:varchar(64);not null" json:"user"`
Password string `gorm:"type:varchar(64);not null" json:"password"`
Status string `gorm:"type:varchar(64);not null" json:"status"`
Path string `gorm:"type:varchar(64);not null" json:"path"`
Description string `gorm:"type:varchar(64);not null" json:"description"`
}
+1
View File
@@ -26,6 +26,7 @@ type Website struct {
WebsiteSSLID uint `gorm:"type:integer" json:"webSiteSSLId"`
RuntimeID uint `gorm:"type:integer" json:"runtimeID"`
AppInstallID uint `gorm:"type:integer" json:"appInstallId"`
FtpID uint `gorm:"type:integer" json:"ftpId"`
User string `gorm:"type:varchar;" json:"user"`
Group string `gorm:"type:varchar;" json:"group"`
+19 -15
View File
@@ -9,26 +9,30 @@ import (
type WebsiteSSL struct {
BaseModel
PrimaryDomain string `gorm:"type:varchar(256);not null" json:"primaryDomain"`
PrivateKey string `gorm:"type:longtext;not null" json:"privateKey"`
Pem string `gorm:"type:longtext;not null" json:"pem"`
Domains string `gorm:"type:varchar(256);not null" json:"domains"`
CertURL string `gorm:"type:varchar(256);not null" json:"certURL"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
Provider string `gorm:"type:varchar(64);not null" json:"provider"`
Organization string `gorm:"type:varchar(64);not null" json:"organization"`
DnsAccountID uint `gorm:"type:integer;not null" json:"dnsAccountId"`
AcmeAccountID uint `gorm:"type:integer;not null" json:"acmeAccountId"`
CaID uint `gorm:"type:integer;not null;default:0" json:"caId"`
AutoRenew bool `gorm:"type:varchar(64);not null" json:"autoRenew"`
PrimaryDomain string `json:"primaryDomain"`
PrivateKey string `json:"privateKey"`
Pem string `json:"pem"`
Domains string `json:"domains"`
CertURL string `json:"certURL"`
Type string `json:"type"`
Provider string `json:"provider"`
Organization string `json:"organization"`
DnsAccountID uint `json:"dnsAccountId"`
AcmeAccountID uint `gorm:"column:acme_account_id" json:"acmeAccountId" `
CaID uint `json:"caId"`
AutoRenew bool `json:"autoRenew"`
ExpireDate time.Time `json:"expireDate"`
StartDate time.Time `json:"startDate"`
Status string `gorm:"not null;default:ready" json:"status"`
Status string `json:"status"`
Message string `json:"message"`
KeyType string `gorm:"not null;default:2048" json:"keyType"`
PushDir bool `gorm:"not null;default:0" json:"pushDir"`
KeyType string `json:"keyType"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
Description string `json:"description"`
SkipDNS bool `json:"skipDNS"`
Nameserver1 string `json:"nameserver1"`
Nameserver2 string `json:"nameserver2"`
DisableCNAME bool `json:"disableCNAME"`
AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`
+3
View File
@@ -3,6 +3,7 @@ package repo
import (
"context"
"encoding/json"
"github.com/1Panel-dev/1Panel/backend/constant"
"gorm.io/gorm/clause"
@@ -160,6 +161,7 @@ func (a *AppInstallRepo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOp
type RootInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
Port int64 `json:"port"`
HttpsPort int64 `json:"httpsPort"`
UserName string `json:"userName"`
@@ -234,5 +236,6 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
info.Key = app.Key
appInstall.App = app
info.AppPath = appInstall.GetAppPath()
info.Status = appInstall.Status
return &info, nil
}
+60
View File
@@ -16,6 +16,13 @@ type ICommandRepo interface {
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
Get(opts ...DBOption) (model.Command, error)
WithLikeName(name string) DBOption
PageRedis(limit, offset int, opts ...DBOption) (int64, []model.RedisCommand, error)
GetRedis(opts ...DBOption) (model.RedisCommand, error)
GetRedisList(opts ...DBOption) ([]model.RedisCommand, error)
SaveRedis(command *model.RedisCommand) error
DeleteRedis(opts ...DBOption) error
}
func NewICommandRepo() ICommandRepo {
@@ -32,6 +39,16 @@ func (u *CommandRepo) Get(opts ...DBOption) (model.Command, error) {
return command, err
}
func (u *CommandRepo) GetRedis(opts ...DBOption) (model.RedisCommand, error) {
var command model.RedisCommand
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&command).Error
return command, err
}
func (u *CommandRepo) Page(page, size int, opts ...DBOption) (int64, []model.Command, error) {
var users []model.Command
db := global.DB.Model(&model.Command{})
@@ -44,6 +61,18 @@ func (u *CommandRepo) Page(page, size int, opts ...DBOption) (int64, []model.Com
return count, users, err
}
func (u *CommandRepo) PageRedis(page, size int, opts ...DBOption) (int64, []model.RedisCommand, error) {
var users []model.RedisCommand
db := global.DB.Model(&model.RedisCommand{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
return count, users, err
}
func (u *CommandRepo) GetList(opts ...DBOption) ([]model.Command, error) {
var commands []model.Command
db := global.DB.Model(&model.Command{})
@@ -54,6 +83,16 @@ func (u *CommandRepo) GetList(opts ...DBOption) ([]model.Command, error) {
return commands, err
}
func (u *CommandRepo) GetRedisList(opts ...DBOption) ([]model.RedisCommand, error) {
var commands []model.RedisCommand
db := global.DB.Model(&model.RedisCommand{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&commands).Error
return commands, err
}
func (c *CommandRepo) WithByInfo(info string) DBOption {
return func(g *gorm.DB) *gorm.DB {
if len(info) == 0 {
@@ -68,6 +107,10 @@ func (u *CommandRepo) Create(command *model.Command) error {
return global.DB.Create(command).Error
}
func (u *CommandRepo) SaveRedis(command *model.RedisCommand) error {
return global.DB.Save(command).Error
}
func (u *CommandRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.Command{}).Where("id = ?", id).Updates(vars).Error
}
@@ -79,3 +122,20 @@ func (u *CommandRepo) Delete(opts ...DBOption) error {
}
return db.Delete(&model.Command{}).Error
}
func (u *CommandRepo) DeleteRedis(opts ...DBOption) error {
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.RedisCommand{}).Error
}
func (a CommandRepo) WithLikeName(name string) DBOption {
return func(g *gorm.DB) *gorm.DB {
if len(name) == 0 {
return g
}
return g.Where("name like ? or command like ?", "%"+name+"%", "%"+name+"%")
}
}
+80
View File
@@ -0,0 +1,80 @@
package repo
import (
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"gorm.io/gorm"
)
type FtpRepo struct{}
type IFtpRepo interface {
Get(opts ...DBOption) (model.Ftp, error)
GetList(opts ...DBOption) ([]model.Ftp, error)
Page(limit, offset int, opts ...DBOption) (int64, []model.Ftp, error)
Create(ftp *model.Ftp) error
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
WithByUser(user string) DBOption
}
func NewIFtpRepo() IFtpRepo {
return &FtpRepo{}
}
func (u *FtpRepo) Get(opts ...DBOption) (model.Ftp, error) {
var ftp model.Ftp
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&ftp).Error
return ftp, err
}
func (h *FtpRepo) WithByUser(user string) DBOption {
return func(g *gorm.DB) *gorm.DB {
if len(user) == 0 {
return g
}
return g.Where("user like ?", "%"+user+"%")
}
}
func (u *FtpRepo) GetList(opts ...DBOption) ([]model.Ftp, error) {
var ftps []model.Ftp
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&ftps).Error
return ftps, err
}
func (h *FtpRepo) Page(page, size int, opts ...DBOption) (int64, []model.Ftp, error) {
var users []model.Ftp
db := global.DB.Model(&model.Ftp{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
return count, users, err
}
func (h *FtpRepo) Create(ftp *model.Ftp) error {
return global.DB.Create(ftp).Error
}
func (h *FtpRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.Ftp{}).Where("id = ?", id).Updates(vars).Error
}
func (h *FtpRepo) Delete(opts ...DBOption) error {
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.Ftp{}).Error
}
+6 -6
View File
@@ -68,20 +68,20 @@ func (u *SettingRepo) Update(key, value string) error {
}
func (u *SettingRepo) CreateMonitorBase(model model.MonitorBase) error {
return global.DB.Create(&model).Error
return global.MonitorDB.Create(&model).Error
}
func (u *SettingRepo) BatchCreateMonitorIO(ioList []model.MonitorIO) error {
return global.DB.CreateInBatches(ioList, len(ioList)).Error
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
}
func (u *SettingRepo) BatchCreateMonitorNet(ioList []model.MonitorNetwork) error {
return global.DB.CreateInBatches(ioList, len(ioList)).Error
return global.MonitorDB.CreateInBatches(ioList, len(ioList)).Error
}
func (u *SettingRepo) DelMonitorBase(timeForDelete time.Time) error {
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorBase{}).Error
}
func (u *SettingRepo) DelMonitorIO(timeForDelete time.Time) error {
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorIO{}).Error
}
func (u *SettingRepo) DelMonitorNet(timeForDelete time.Time) error {
return global.DB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
return global.MonitorDB.Where("created_at < ?", timeForDelete).Delete(&model.MonitorNetwork{}).Error
}
+9 -2
View File
@@ -18,6 +18,7 @@ type IWebsiteRepo interface {
WithDefaultServer() DBOption
WithDomainLike(domain string) DBOption
WithRuntimeID(runtimeID uint) DBOption
WithIDs(ids []uint) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.Website, error)
List(opts ...DBOption) ([]model.Website, error)
GetFirst(opts ...DBOption) (model.Website, error)
@@ -36,9 +37,15 @@ func NewIWebsiteRepo() IWebsiteRepo {
type WebsiteRepo struct {
}
func (w *WebsiteRepo) WithAppInstallId(appInstallId uint) DBOption {
func (w *WebsiteRepo) WithAppInstallId(appInstallID uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("app_install_id = ?", appInstallId)
return db.Where("app_install_id = ?", appInstallID)
}
}
func (w *WebsiteRepo) WithIDs(ids []uint) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("id in (?)", ids)
}
}
+6
View File
@@ -14,6 +14,7 @@ type IWebsiteDomainRepo interface {
WithWebsiteId(websiteId uint) DBOption
WithPort(port int) DBOption
WithDomain(domain string) DBOption
WithDomainLike(domain string) DBOption
Page(page, size int, opts ...DBOption) (int64, []model.WebsiteDomain, error)
GetFirst(opts ...DBOption) (model.WebsiteDomain, error)
GetBy(opts ...DBOption) ([]model.WebsiteDomain, error)
@@ -44,6 +45,11 @@ func (w WebsiteDomainRepo) WithDomain(domain string) DBOption {
return db.Where("domain = ?", domain)
}
}
func (w WebsiteDomainRepo) WithDomainLike(domain string) DBOption {
return func(db *gorm.DB) *gorm.DB {
return db.Where("domain like ?", "%"+domain+"%")
}
}
func (w WebsiteDomainRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteDomain, error) {
var domains []model.WebsiteDomain
db := getDb(opts...).Model(&model.WebsiteDomain{})
+10 -1
View File
@@ -21,6 +21,7 @@ type ISSLRepo interface {
Create(ctx context.Context, ssl *model.WebsiteSSL) error
Save(ssl *model.WebsiteSSL) error
DeleteBy(opts ...DBOption) error
SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error
}
type WebsiteSSLRepo struct {
@@ -82,7 +83,15 @@ func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error
}
func (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error {
return getDb().Save(&ssl).Error
return getDb().Model(&model.WebsiteSSL{BaseModel: model.BaseModel{
ID: ssl.ID,
}}).Save(&ssl).Error
}
func (w WebsiteSSLRepo) SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error {
return getDb().Model(&model.WebsiteSSL{BaseModel: model.BaseModel{
ID: ssl.ID,
}}).Updates(params).Error
}
func (w WebsiteSSLRepo) DeleteBy(opts ...DBOption) error {
+48 -35
View File
@@ -5,7 +5,6 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
@@ -26,6 +25,8 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/1Panel-dev/1Panel/backend/utils/files"
http2 "github.com/1Panel-dev/1Panel/backend/utils/http"
httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http"
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
"gopkg.in/yaml.v3"
)
@@ -88,14 +89,22 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) {
if err != nil {
return nil, err
}
var appDTOs []*response.AppDTO
var appDTOs []*response.AppDto
for _, ap := range apps {
ap.ReadMe = ""
ap.Website = ""
ap.Document = ""
ap.Github = ""
appDTO := &response.AppDTO{
App: ap,
appDTO := &response.AppDto{
ID: ap.ID,
Name: ap.Name,
Key: ap.Key,
Type: ap.Type,
Icon: ap.Icon,
ShortDescZh: ap.ShortDescZh,
ShortDescEn: ap.ShortDescEn,
Resource: ap.Resource,
Limit: ap.Limit,
}
appDTOs = append(appDTOs, appDTO)
appTags, err := appTagRepo.GetByAppId(ap.ID)
@@ -226,20 +235,16 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response.
if appDetailDTO.DockerCompose == "" {
filename := filepath.Base(appDetailDTO.DownloadUrl)
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(appDetailDTO.DownloadUrl, filename), "docker-compose.yml")
composeRes, err := http.Get(dockerComposeUrl)
statusCode, composeRes, err := httpUtil.HandleGet(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
if err != nil {
return appDetailDTO, buserr.WithDetail("ErrGetCompose", err.Error(), err)
}
bodyContent, err := io.ReadAll(composeRes.Body)
if err != nil {
return appDetailDTO, buserr.WithDetail("ErrGetCompose", err.Error(), err)
if statusCode > 200 {
return appDetailDTO, buserr.WithDetail("ErrGetCompose", string(composeRes), err)
}
if composeRes.StatusCode > 200 {
return appDetailDTO, buserr.WithDetail("ErrGetCompose", string(bodyContent), err)
}
detail.DockerCompose = string(bodyContent)
detail.DockerCompose = string(composeRes)
_ = appDetailRepo.Update(context.Background(), detail)
appDetailDTO.DockerCompose = string(bodyContent)
appDetailDTO.DockerCompose = string(composeRes)
}
appDetailDTO.HostMode = isHostModel(appDetailDTO.DockerCompose)
@@ -362,7 +367,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
}
value, ok := composeMap["services"]
if !ok {
if !ok || value == nil {
err = buserr.New(constant.ErrFileParse)
return
}
@@ -439,6 +444,14 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
return
}
appInstall.Env = string(paramByte)
containerNames, err := getContainerNames(*appInstall)
if err != nil {
return
}
if len(containerNames) > 0 {
appInstall.ContainerName = strings.Join(containerNames, ",")
}
if err = appInstallRepo.Create(ctx, appInstall); err != nil {
return
}
@@ -448,7 +461,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
go func() {
defer func() {
if err != nil {
appInstall.Status = constant.Error
appInstall.Status = constant.UpErr
appInstall.Message = err.Error()
_ = appInstallRepo.Save(context.Background(), appInstall)
}
@@ -698,16 +711,11 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
}
versionUrl := fmt.Sprintf("%s/%s/1panel.json.version.txt", global.CONF.System.AppRepo, global.CONF.System.Mode)
versionRes, err := http2.GetHttpRes(versionUrl)
_, versionRes, err := http2.HandleGet(versionUrl, http.MethodGet, constant.TimeOut20s)
if err != nil {
return nil, err
}
defer versionRes.Body.Close()
body, err := io.ReadAll(versionRes.Body)
if err != nil {
return nil, err
}
lastModifiedStr := string(body)
lastModifiedStr := string(versionRes)
lastModified, err := strconv.Atoi(lastModifiedStr)
if err != nil {
return nil, err
@@ -727,6 +735,14 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
res.CanUpdate = true
return res, err
}
apps, _ := appRepo.GetBy(appRepo.WithResource(constant.AppResourceRemote))
for _, app := range apps {
if app.Icon == "" {
res.CanUpdate = true
return res, err
}
}
list, err := getAppList()
if err != nil {
return res, err
@@ -744,10 +760,10 @@ func getAppFromRepo(downloadPath string) error {
global.LOG.Infof("[AppStore] download file from %s", downloadUrl)
fileOp := files.NewFileOp()
packagePath := filepath.Join(constant.ResourceDir, filepath.Base(downloadUrl))
if err := fileOp.DownloadFile(downloadUrl, packagePath); err != nil {
if err := fileOp.DownloadFileWithProxy(downloadUrl, packagePath); err != nil {
return err
}
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.SdkZip); err != nil {
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.SdkZip, ""); err != nil {
return err
}
defer func() {
@@ -825,21 +841,22 @@ func (a AppService) SyncAppListFromRemote() (err error) {
oldAppIds = append(oldAppIds, old.ID)
}
transport := xpack.LoadRequestTransport()
baseRemoteUrl := fmt.Sprintf("%s/%s/1panel", global.CONF.System.AppRepo, global.CONF.System.Mode)
appsMap := getApps(oldApps, list.Apps)
global.LOG.Infof("Starting synchronization of application details...")
for _, l := range list.Apps {
app := appsMap[l.AppProperty.Key]
iconRes, err := http.Get(l.Icon)
_, iconRes, err := httpUtil.HandleGetWithTransport(l.Icon, http.MethodGet, transport, constant.TimeOut20s)
if err != nil {
return err
}
body, err := io.ReadAll(iconRes.Body)
if err != nil {
return err
iconStr := ""
if !strings.Contains(string(iconRes), "<xml>") {
iconStr = base64.StdEncoding.EncodeToString(iconRes)
}
iconStr := base64.StdEncoding.EncodeToString(body)
app.Icon = iconStr
app.TagsKey = l.AppProperty.Tags
if l.AppProperty.Recommend > 0 {
@@ -858,15 +875,11 @@ func (a AppService) SyncAppListFromRemote() (err error) {
if _, ok := InitTypes[app.Type]; ok {
dockerComposeUrl := fmt.Sprintf("%s/%s", versionUrl, "docker-compose.yml")
composeRes, err := http.Get(dockerComposeUrl)
_, composeRes, err := httpUtil.HandleGetWithTransport(dockerComposeUrl, http.MethodGet, transport, constant.TimeOut20s)
if err != nil {
return err
}
bodyContent, err := io.ReadAll(composeRes.Body)
if err != nil {
return err
}
detail.DockerCompose = string(bodyContent)
detail.DockerCompose = string(composeRes)
} else {
detail.DockerCompose = ""
}
+61 -79
View File
@@ -5,14 +5,18 @@ import (
"encoding/json"
"fmt"
"math"
"net/http"
"os"
"path"
"path/filepath"
"reflect"
"sort"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/utils/files"
httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http"
"github.com/docker/docker/api/types"
"gopkg.in/yaml.v3"
"github.com/1Panel-dev/1Panel/backend/utils/env"
@@ -39,17 +43,17 @@ type AppInstallService struct {
}
type IAppInstallService interface {
Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error)
Page(req request.AppInstalledSearch) (int64, []response.AppInstallDTO, error)
CheckExist(req request.AppInstalledInfo) (*response.AppInstalledCheck, error)
LoadPort(req dto.OperationWithNameAndType) (int64, error)
LoadConnInfo(req dto.OperationWithNameAndType) (response.DatabaseConn, error)
SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error)
SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstallDTO, error)
Operate(req request.AppInstalledOperate) error
Update(req request.AppInstalledUpdate) error
IgnoreUpgrade(req request.AppInstalledIgnoreUpgrade) error
SyncAll(systemInit bool) error
GetServices(key string) ([]response.AppService, error)
GetUpdateVersions(installId uint) ([]dto.AppVersion, error)
GetUpdateVersions(req request.AppUpdateVersion) ([]dto.AppVersion, error)
GetParams(id uint) (*response.AppConfig, error)
ChangeAppPort(req request.PortUpdate) error
GetDefaultConfigByKey(key, name string) (string, error)
@@ -74,7 +78,7 @@ func (a *AppInstallService) GetInstallList() ([]dto.AppInstallInfo, error) {
return datas, nil
}
func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error) {
func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []response.AppInstallDTO, error) {
var (
opts []repo.DBOption
total int64
@@ -118,7 +122,7 @@ func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []respo
}
}
installDTOs, err := handleInstalled(installs, req.Update)
installDTOs, err := handleInstalled(installs, req.Update, req.Sync)
if err != nil {
return 0, nil, err
}
@@ -150,7 +154,7 @@ func (a *AppInstallService) CheckExist(req request.AppInstalledInfo) (*response.
if reflect.DeepEqual(appInstall, model.AppInstall{}) {
return res, nil
}
if err = syncAppInstallStatus(&appInstall); err != nil {
if err = syncAppInstallStatus(&appInstall, false); err != nil {
return nil, err
}
@@ -182,6 +186,7 @@ func (a *AppInstallService) LoadConnInfo(req dto.OperationWithNameAndType) (resp
if err != nil {
return data, nil
}
data.Status = app.Status
data.Username = app.UserName
data.Password = app.Password
data.ServiceName = app.ServiceName
@@ -190,7 +195,7 @@ func (a *AppInstallService) LoadConnInfo(req dto.OperationWithNameAndType) (resp
return data, nil
}
func (a *AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error) {
func (a *AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstallDTO, error) {
var (
installs []model.AppInstall
err error
@@ -220,7 +225,7 @@ func (a *AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]
}
}
return handleInstalled(installs, false)
return handleInstalled(installs, false, true)
}
func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
@@ -240,28 +245,35 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
if err != nil {
return handleErr(install, err, out)
}
return syncAppInstallStatus(&install)
return syncAppInstallStatus(&install, false)
case constant.Stop:
out, err := compose.Stop(dockerComposePath)
if err != nil {
return handleErr(install, err, out)
}
return syncAppInstallStatus(&install)
return syncAppInstallStatus(&install, false)
case constant.Restart:
out, err := compose.Restart(dockerComposePath)
if err != nil {
return handleErr(install, err, out)
}
return syncAppInstallStatus(&install)
return syncAppInstallStatus(&install, false)
case constant.Delete:
if err := deleteAppInstall(install, req.DeleteBackup, req.ForceDelete, req.DeleteDB); err != nil && !req.ForceDelete {
return err
}
return nil
case constant.Sync:
return syncAppInstallStatus(&install)
return syncAppInstallStatus(&install, true)
case constant.Upgrade:
return upgradeInstall(install.ID, req.DetailId, req.Backup, req.PullImage)
upgradeReq := request.AppInstallUpgrade{
InstallID: install.ID,
DetailID: req.DetailId,
Backup: req.Backup,
PullImage: req.PullImage,
DockerCompose: req.DockerCompose,
}
return upgradeInstall(upgradeReq)
case constant.Reload:
return opNginx(install.ContainerName, constant.NginxReload)
default:
@@ -420,7 +432,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
continue
}
if !systemInit {
if err = syncAppInstallStatus(&i); err != nil {
if err = syncAppInstallStatus(&i, false); err != nil {
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
}
}
@@ -483,8 +495,8 @@ func (a *AppInstallService) GetServices(key string) ([]response.AppService, erro
return res, nil
}
func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion, error) {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
func (a *AppInstallService) GetUpdateVersions(req request.AppUpdateVersion) ([]dto.AppVersion, error) {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.AppInstallID))
var versions []dto.AppVersion
if err != nil {
return versions, err
@@ -505,9 +517,28 @@ func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion,
continue
}
if common.CompareVersion(detail.Version, install.Version) {
var newCompose string
if req.UpdateVersion != "" && req.UpdateVersion == detail.Version && detail.DockerCompose == "" && !app.IsLocalApp() {
filename := filepath.Base(detail.DownloadUrl)
dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(detail.DownloadUrl, filename), "docker-compose.yml")
statusCode, composeRes, err := httpUtil.HandleGet(dockerComposeUrl, http.MethodGet, constant.TimeOut20s)
if err != nil {
return versions, err
}
if statusCode > 200 {
return versions, err
}
detail.DockerCompose = string(composeRes)
_ = appDetailRepo.Update(context.Background(), detail)
}
newCompose, err = getUpgradeCompose(install, detail)
if err != nil {
return versions, err
}
versions = append(versions, dto.AppVersion{
Version: detail.Version,
DetailId: detail.ID,
Version: detail.Version,
DetailId: detail.ID,
DockerCompose: newCompose,
})
}
}
@@ -700,79 +731,30 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
return &res, nil
}
func syncAppInstallStatus(appInstall *model.AppInstall) error {
func syncAppInstallStatus(appInstall *model.AppInstall, force bool) error {
if appInstall.Status == constant.Installing || appInstall.Status == constant.Rebuilding || appInstall.Status == constant.Upgrading {
return nil
}
containerNames, err := getContainerNames(*appInstall)
if err != nil {
return err
}
cli, err := docker.NewClient()
if err != nil {
return err
}
containers, err := cli.ListContainersByName(containerNames)
defer cli.Close()
var (
containers []types.Container
containersMap map[string]types.Container
containerNames = strings.Split(appInstall.ContainerName, ",")
)
containers, err = cli.ListContainersByName(containerNames)
if err != nil {
return err
}
var (
runningCount int
exitedCount int
pausedCount int
exitedContainerNames []string
total = len(containerNames)
count = len(containers)
)
foundNames := make(map[string]bool)
containersMap = make(map[string]types.Container)
for _, con := range containers {
foundNames[con.Names[0]] = true
switch con.State {
case "exited":
exitedContainerNames = append(exitedContainerNames, strings.TrimPrefix(con.Names[0], "/"))
exitedCount++
case "running":
runningCount++
case "paused":
pausedCount++
}
containersMap[con.Names[0]] = con
}
var notFoundNames []string
for _, name := range containerNames {
if !foundNames["/"+name] {
notFoundNames = append(notFoundNames, name)
}
}
switch {
case count == 0:
appInstall.Status = constant.Error
appInstall.Message = buserr.WithName("ErrContainerNotFound", strings.Join(containerNames, ",")).Error()
case exitedCount == total:
appInstall.Status = constant.Stopped
case runningCount == total:
appInstall.Status = constant.Running
case pausedCount == total:
appInstall.Status = constant.Paused
default:
var msg string
if exitedCount > 0 {
msg = buserr.WithName("ErrContainerMsg", strings.Join(exitedContainerNames, ",")).Error()
}
if len(notFoundNames) > 0 {
msg += buserr.WithName("ErrContainerNotFound", strings.Join(notFoundNames, ",")).Error()
}
if msg == "" {
msg = buserr.New("ErrAppWarn").Error()
}
appInstall.Message = msg
appInstall.Status = constant.UnHealthy
}
_ = appInstallRepo.Save(context.Background(), appInstall)
synAppInstall(containersMap, appInstall, force)
return nil
}
+289 -112
View File
@@ -14,6 +14,13 @@ import (
"regexp"
"strconv"
"strings"
"time"
"github.com/docker/docker/api/types"
httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http"
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
"github.com/docker/docker/api/types/container"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
@@ -35,9 +42,9 @@ import (
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
composeV2 "github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/1Panel-dev/1Panel/backend/utils/files"
dockerTypes "github.com/docker/docker/api/types"
"github.com/pkg/errors"
)
@@ -364,6 +371,7 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
}
_ = websiteRepo.DeleteAll(ctx)
_ = websiteDomainRepo.DeleteAll(ctx)
xpack.RemoveTamper("")
case constant.AppMysql, constant.AppMariaDB:
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
case constant.AppPostgresql:
@@ -433,12 +441,65 @@ func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, f
return appInstallResourceRepo.DeleteBy(ctx, appInstallResourceRepo.WithAppInstallId(install.ID))
}
func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installID))
func getUpgradeCompose(install model.AppInstall, detail model.AppDetail) (string, error) {
if detail.DockerCompose == "" {
return "", nil
}
composeMap := make(map[string]interface{})
if err := yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); err != nil {
return "", err
}
value, ok := composeMap["services"]
if !ok || value == nil {
return "", buserr.New(constant.ErrFileParse)
}
servicesMap := value.(map[string]interface{})
if len(servicesMap) == 1 {
index := 0
oldServiceName := ""
for k := range servicesMap {
oldServiceName = k
index++
if index > 0 {
break
}
}
servicesMap[install.ServiceName] = servicesMap[oldServiceName]
if install.ServiceName != oldServiceName {
delete(servicesMap, oldServiceName)
}
}
envs := make(map[string]interface{})
if err := json.Unmarshal([]byte(install.Env), &envs); err != nil {
return "", err
}
config := getAppCommonConfig(envs)
if config.ContainerName == "" {
config.ContainerName = install.ContainerName
envs[constant.ContainerName] = install.ContainerName
}
config.Advanced = true
if err := addDockerComposeCommonParam(composeMap, install.ServiceName, config, envs); err != nil {
return "", err
}
paramByte, err := json.Marshal(envs)
if err != nil {
return "", err
}
install.Env = string(paramByte)
composeByte, err := yaml.Marshal(composeMap)
if err != nil {
return "", err
}
return string(composeByte), nil
}
func upgradeInstall(req request.AppInstallUpgrade) error {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallID))
if err != nil {
return err
}
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(detailID))
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(req.DetailID))
if err != nil {
return err
}
@@ -448,43 +509,70 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
install.Status = constant.Upgrading
go func() {
if backup {
if err = NewIBackupService().AppBackup(dto.CommonBackup{Name: install.App.Key, DetailName: install.Name}); err != nil {
global.LOG.Errorf(i18n.GetMsgWithMap("ErrAppBackup", map[string]interface{}{"name": install.Name, "err": err.Error()}))
var (
upErr error
backupFile string
preErr 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})
if err == nil {
localDir, err := loadLocalDir()
if err == nil {
backupFile = path.Join(localDir, backupRecord.FileDir, backupRecord.FileName)
} else {
global.LOG.Errorf(i18n.GetMsgWithName("ErrAppBackup", install.Name, err))
}
} else {
global.LOG.Errorf(i18n.GetMsgWithName("ErrAppBackup", install.Name, err))
}
}
var upErr error
defer func() {
if upErr != nil {
if backup {
if err := NewIBackupService().AppRecover(dto.CommonRecover{Name: install.App.Key, DetailName: install.Name, Type: "app", Source: constant.ResourceLocal}); err != nil {
global.LOG.Infof(i18n.GetMsgWithName("ErrAppUpgrade", install.Name, upErr))
if req.Backup {
global.LOG.Infof(i18n.GetMsgWithName("AppRecover", install.Name, nil))
if err := NewIBackupService().AppRecover(dto.CommonRecover{Name: install.App.Key, DetailName: install.Name, Type: "app", Source: constant.ResourceLocal, File: backupFile}); err != nil {
global.LOG.Errorf("recover app [%s] [%s] failed %v", install.App.Key, install.Name, err)
}
}
install.Status = constant.UpgradeErr
install.Message = upErr.Error()
_ = appInstallRepo.Save(context.Background(), &install)
existInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallID))
if existInstall.ID > 0 {
existInstall.Status = constant.UpgradeErr
existInstall.Message = upErr.Error()
_ = appInstallRepo.Save(context.Background(), &existInstall)
}
}
if preErr != nil {
global.LOG.Infof(i18n.GetMsgWithName("ErrAppUpgrade", install.Name, preErr))
existInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallID))
if existInstall.ID > 0 {
existInstall.Status = constant.UpgradeErr
existInstall.Message = preErr.Error()
_ = appInstallRepo.Save(context.Background(), &existInstall)
}
}
}()
fileOp := files.NewFileOp()
detailDir := path.Join(constant.ResourceDir, "apps", install.App.Resource, install.App.Key, detail.Version)
if install.App.Resource == constant.AppResourceRemote {
if upErr = downloadApp(install.App, detail, &install); upErr != nil {
if preErr = downloadApp(install.App, detail, &install); preErr != nil {
return
}
if detail.DockerCompose == "" {
composeDetail, err := fileOp.GetContent(path.Join(detailDir, "docker-compose.yml"))
if err != nil {
upErr = err
preErr = err
return
}
detail.DockerCompose = string(composeDetail)
_ = appDetailRepo.Update(context.Background(), detail)
}
go func() {
_, _ = http.Get(detail.DownloadCallBackUrl)
_, _, _ = httpUtil.HandleGet(detail.DownloadCallBackUrl, http.MethodGet, constant.TimeOut5s)
}()
}
@@ -492,6 +580,32 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
}
content, err := fileOp.GetContent(install.GetEnvPath())
if err != nil {
preErr = err
return
}
if req.PullImage {
projectName := strings.ToLower(install.Name)
images, err := composeV2.GetDockerComposeImages(projectName, content, []byte(detail.DockerCompose))
if err != nil {
preErr = err
return
}
for _, image := range images {
global.LOG.Infof(i18n.GetMsgWithName("PullImageStart", image, nil))
if out, err := cmd.ExecWithTimeOut("docker pull "+image, 20*time.Minute); err != nil {
if out != "" {
err = errors.New(out)
}
preErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
return
} else {
global.LOG.Infof(i18n.GetMsgByKey("PullImageSuccess"))
}
}
}
command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
stdout, _ := command.CombinedOutput()
if stdout != nil {
@@ -506,100 +620,42 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
_, _ = scriptCmd.CombinedOutput()
}
composeMap := make(map[string]interface{})
if upErr = yaml.Unmarshal([]byte(detail.DockerCompose), &composeMap); upErr != nil {
return
}
value, ok := composeMap["services"]
if !ok {
upErr = buserr.New(constant.ErrFileParse)
return
}
servicesMap := value.(map[string]interface{})
if len(servicesMap) == 1 {
index := 0
oldServiceName := ""
for k := range servicesMap {
oldServiceName = k
index++
if index > 0 {
break
}
var newCompose string
if req.DockerCompose == "" {
newCompose, upErr = getUpgradeCompose(install, detail)
if upErr != nil {
return
}
servicesMap[install.ServiceName] = servicesMap[oldServiceName]
if install.ServiceName != oldServiceName {
delete(servicesMap, oldServiceName)
}
}
envs := make(map[string]interface{})
if upErr = json.Unmarshal([]byte(install.Env), &envs); upErr != nil {
return
}
config := getAppCommonConfig(envs)
if config.ContainerName == "" {
config.ContainerName = install.ContainerName
envs[constant.ContainerName] = install.ContainerName
}
config.Advanced = true
if upErr = addDockerComposeCommonParam(composeMap, install.ServiceName, config, envs); upErr != nil {
return
}
paramByte, upErr := json.Marshal(envs)
if upErr != nil {
return
}
install.Env = string(paramByte)
composeByte, upErr := yaml.Marshal(composeMap)
if upErr != nil {
return
} else {
newCompose = req.DockerCompose
}
install.DockerCompose = string(composeByte)
install.DockerCompose = newCompose
install.Version = detail.Version
install.AppDetailId = detailID
install.AppDetailId = req.DetailID
if out, err := compose.Down(install.GetComposePath()); err != nil {
if out != "" {
upErr = errors.New(out)
return
}
upErr = err
return
}
envs := make(map[string]interface{})
if upErr = json.Unmarshal([]byte(install.Env), &envs); upErr != nil {
return
}
envParams := make(map[string]string, len(envs))
handleMap(envs, envParams)
if err = env.Write(envParams, install.GetEnvPath()); err != nil {
if upErr = env.Write(envParams, install.GetEnvPath()); upErr != nil {
return
}
if err = runScript(&install, "upgrade"); err != nil {
if upErr = runScript(&install, "upgrade"); upErr != nil {
return
}
content, err := fileOp.GetContent(install.GetEnvPath())
if err != nil {
upErr = err
return
}
if pullImage {
images, err := composeV2.GetDockerComposeImages(install.Name, content, []byte(detail.DockerCompose))
if err != nil {
upErr = err
return
}
dockerCli, err := composeV2.NewClient()
if err != nil {
upErr = err
return
}
for _, image := range images {
if err = dockerCli.PullImage(image, true); err != nil {
upErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
return
}
}
}
if upErr = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); upErr != nil {
return
}
@@ -613,6 +669,7 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
}
install.Status = constant.Running
_ = appInstallRepo.Save(context.Background(), &install)
global.LOG.Infof(i18n.GetMsgWithName("UpgradeAppSuccess", install.Name, nil))
}()
return appInstallRepo.Save(context.Background(), &install)
@@ -693,8 +750,8 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
strArray[i] = strings.ToLower(fmt.Sprintf("%v", t[i]))
}
envParams[k] = strings.Join(strArray, ",")
default:
envParams[k] = t.(string)
case map[string]interface{}:
handleMap(t, envParams)
}
}
}
@@ -729,11 +786,11 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
}
}()
if err = fileOp.DownloadFile(appDetail.DownloadUrl, filePath); err != nil {
if err = fileOp.DownloadFileWithProxy(appDetail.DownloadUrl, filePath); err != nil {
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
return
}
if err = fileOp.Decompress(filePath, appResourceDir, files.SdkTarGz); err != nil {
if err = fileOp.Decompress(filePath, appResourceDir, files.SdkTarGz, ""); err != nil {
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
return
}
@@ -753,7 +810,7 @@ func copyData(app model.App, appDetail model.AppDetail, appInstall *model.AppIns
return
}
go func() {
_, _ = http.Get(appDetail.DownloadCallBackUrl)
_, _, _ = httpUtil.HandleGet(appDetail.DownloadCallBackUrl, http.MethodGet, constant.TimeOut5s)
}()
}
appKey := app.Key
@@ -827,7 +884,8 @@ func checkContainerNameIsExist(containerName, appDir string) (bool, error) {
if err != nil {
return false, err
}
var options dockerTypes.ContainerListOptions
defer client.Close()
var options container.ListOptions
list, err := client.ContainerList(context.Background(), options)
if err != nil {
return false, err
@@ -879,7 +937,7 @@ func upApp(appInstall *model.AppInstall, pullImages bool) {
return
}
if err := upProject(appInstall); err != nil {
appInstall.Status = constant.Error
appInstall.Status = constant.UpErr
} else {
appInstall.Status = constant.Running
}
@@ -904,6 +962,12 @@ func rebuildApp(appInstall model.AppInstall) error {
_ = handleErr(appInstall, err, out)
return
}
containerNames, err := getContainerNames(appInstall)
if err != nil {
_ = handleErr(appInstall, err, out)
return
}
appInstall.ContainerName = strings.Join(containerNames, ",")
appInstall.Status = constant.Running
_ = appInstallRepo.Save(context.Background(), &appInstall)
@@ -1081,24 +1145,131 @@ func handleErr(install model.AppInstall, err error, out string) error {
install.Message = out
reErr = errors.New(out)
}
install.Status = constant.Error
install.Status = constant.UpErr
_ = appInstallRepo.Save(context.Background(), &install)
return reErr
}
func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]response.AppInstalledDTO, error) {
var res []response.AppInstalledDTO
func doNotNeedSync(installed model.AppInstall) bool {
return installed.Status == constant.Installing || installed.Status == constant.Rebuilding || installed.Status == constant.Upgrading ||
installed.Status == constant.Syncing
}
func synAppInstall(containers map[string]types.Container, appInstall *model.AppInstall, force bool) {
containerNames := strings.Split(appInstall.ContainerName, ",")
if len(containers) == 0 {
if appInstall.Status == constant.UpErr && !force {
return
}
appInstall.Status = constant.Error
appInstall.Message = buserr.WithName("ErrContainerNotFound", strings.Join(containerNames, ",")).Error()
_ = appInstallRepo.Save(context.Background(), appInstall)
return
}
notFoundNames := make([]string, 0)
exitNames := make([]string, 0)
exitedCount := 0
pausedCount := 0
runningCount := 0
total := len(containerNames)
for _, name := range containerNames {
if con, ok := containers["/"+name]; ok {
switch con.State {
case "exited":
exitedCount++
exitNames = append(exitNames, name)
case "running":
runningCount++
case "paused":
pausedCount++
}
} else {
notFoundNames = append(notFoundNames, name)
}
}
switch {
case exitedCount == total:
appInstall.Status = constant.Stopped
case runningCount == total:
appInstall.Status = constant.Running
case pausedCount == total:
appInstall.Status = constant.Paused
case len(notFoundNames) == total:
if appInstall.Status == constant.UpErr && !force {
return
}
appInstall.Status = constant.Error
appInstall.Message = buserr.WithName("ErrContainerNotFound", strings.Join(notFoundNames, ",")).Error()
default:
var msg string
if exitedCount > 0 {
msg = buserr.WithName("ErrContainerMsg", strings.Join(exitNames, ",")).Error()
}
if len(notFoundNames) > 0 {
msg += buserr.WithName("ErrContainerNotFound", strings.Join(notFoundNames, ",")).Error()
}
if msg == "" {
msg = buserr.New("ErrAppWarn").Error()
}
appInstall.Message = msg
appInstall.Status = constant.UnHealthy
}
_ = appInstallRepo.Save(context.Background(), appInstall)
}
func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool) ([]response.AppInstallDTO, error) {
var (
res []response.AppInstallDTO
containersMap map[string]types.Container
)
if sync {
cli, err := docker.NewClient()
if err != nil {
return nil, err
}
defer cli.Close()
containers, err := cli.ListAllContainers()
if err != nil {
return nil, err
}
containersMap = make(map[string]types.Container, len(containers))
for _, contain := range containers {
containersMap[contain.Names[0]] = contain
}
}
for _, installed := range appInstallList {
if updated && (installed.App.Type == "php" || installed.Status == constant.Installing || (installed.App.Key == constant.AppMysql && installed.Version == "5.6.51")) {
continue
}
if err := syncAppInstallStatus(&installed); err != nil {
global.LOG.Error("sync app install status error : ", err)
if sync && !doNotNeedSync(installed) {
synAppInstall(containersMap, &installed, false)
}
installDTO := response.AppInstalledDTO{
AppInstall: installed,
Path: installed.GetPath(),
installDTO := response.AppInstallDTO{
ID: installed.ID,
Name: installed.Name,
AppID: installed.AppId,
AppDetailID: installed.AppDetailId,
Version: installed.Version,
Status: installed.Status,
Message: installed.Message,
HttpPort: installed.HttpPort,
HttpsPort: installed.HttpsPort,
Icon: installed.App.Icon,
AppName: installed.App.Name,
AppKey: installed.App.Key,
AppType: installed.App.Type,
Path: installed.GetPath(),
CreatedAt: installed.CreatedAt,
App: response.AppDetail{
Github: installed.App.Github,
Website: installed.App.Website,
Document: installed.App.Document,
},
}
if updated {
installDTO.DockerCompose = installed.DockerCompose
}
app, err := appRepo.GetFirst(commonRepo.WithByID(installed.AppId))
if err != nil {
@@ -1225,14 +1396,20 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
return buserr.New(constant.ErrFileParse)
}
serviceValue := service.(map[string]interface{})
deploy := map[string]interface{}{
"resources": map[string]interface{}{
"limits": map[string]interface{}{
"cpus": "${CPUS}",
"memory": "${MEMORY_LIMIT}",
},
},
deploy := map[string]interface{}{}
if de, ok := serviceValue["deploy"]; ok {
deploy = de.(map[string]interface{})
}
resource := map[string]interface{}{}
if res, ok := deploy["resources"]; ok {
resource = res.(map[string]interface{})
}
resource["limits"] = map[string]interface{}{
"cpus": "${CPUS}",
"memory": "${MEMORY_LIMIT}",
}
deploy["resources"] = resource
serviceValue["deploy"] = deploy
ports, ok := serviceValue["ports"].([]interface{})
+13 -3
View File
@@ -1,6 +1,7 @@
package service
import (
"crypto/hmac"
"strconv"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -19,6 +20,7 @@ type AuthService struct{}
type IAuthService interface {
CheckIsSafety(code string) (string, error)
GetResponsePage() (string, error)
VerifyCode(code string) (bool, error)
Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, error)
LogOut(c *gin.Context) error
@@ -42,7 +44,7 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
if err != nil {
return nil, constant.ErrAuth
}
if info.Password != pass || nameSetting.Value != info.Name {
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
return nil, constant.ErrAuth
}
entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
@@ -78,7 +80,7 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance strin
if err != nil {
return nil, err
}
if info.Password != pass || nameSetting.Value != info.Name {
if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name {
return nil, constant.ErrAuth
}
entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
@@ -168,7 +170,7 @@ func (u *AuthService) VerifyCode(code string) (bool, error) {
if err != nil {
return false, err
}
return setting.Value == code, nil
return hmac.Equal([]byte(setting.Value), []byte(code)), nil
}
func (u *AuthService) CheckIsSafety(code string) (string, error) {
@@ -184,3 +186,11 @@ func (u *AuthService) CheckIsSafety(code string) (string, error) {
}
return "unpass", nil
}
func (u *AuthService) GetResponsePage() (string, error) {
pageCode, err := settingRepo.Get(settingRepo.WithByKey("NoAuthSetting"))
if err != nil {
return "", err
}
return pageCode.Value, nil
}
+32 -2
View File
@@ -37,6 +37,7 @@ type IBackupService interface {
GetBuckets(backupDto dto.ForBuckets) ([]interface{}, error)
Update(ireq dto.BackupOperate) error
Delete(id uint) error
DeleteRecordByName(backupType, name, detailName string, withDeleteFile bool) error
BatchDeleteRecord(ids []uint) error
NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error)
@@ -49,13 +50,13 @@ type IBackupService interface {
MysqlRecoverByUpload(req dto.CommonRecover) error
PostgresqlRecoverByUpload(req dto.CommonRecover) error
RedisBackup() error
RedisBackup(db dto.CommonBackup) error
RedisRecover(db dto.CommonRecover) error
WebsiteBackup(db dto.CommonBackup) error
WebsiteRecover(req dto.CommonRecover) error
AppBackup(db dto.CommonBackup) error
AppBackup(db dto.CommonBackup) (*model.BackupRecord, error)
AppRecover(req dto.CommonRecover) error
Run()
@@ -260,6 +261,35 @@ func (u *BackupService) Delete(id uint) error {
return backupRepo.Delete(commonRepo.WithByID(id))
}
func (u *BackupService) DeleteRecordByName(backupType, name, detailName string, withDeleteFile bool) error {
if !withDeleteFile {
return backupRepo.DeleteRecord(context.Background(), commonRepo.WithByType(backupType), commonRepo.WithByName(name), backupRepo.WithByDetailName(detailName))
}
records, err := backupRepo.ListRecord(commonRepo.WithByType(backupType), commonRepo.WithByName(name), backupRepo.WithByDetailName(detailName))
if err != nil {
return err
}
for _, record := range records {
backupAccount, err := backupRepo.Get(commonRepo.WithByType(record.Source))
if err != nil {
global.LOG.Errorf("load backup account %s info from db failed, err: %v", record.Source, err)
continue
}
client, err := u.NewClient(&backupAccount)
if err != nil {
global.LOG.Errorf("new client for backup account %s failed, err: %v", record.Source, err)
continue
}
if _, err = client.Delete(path.Join(record.FileDir, record.FileName)); err != nil {
global.LOG.Errorf("remove file %s from %s failed, err: %v", path.Join(record.FileDir, record.FileName), record.Source, err)
}
_ = backupRepo.DeleteRecord(context.Background(), commonRepo.WithByID(record.ID))
}
return nil
}
func (u *BackupService) BatchDeleteRecord(ids []uint) error {
records, err := backupRepo.ListRecord(commonRepo.WithIdsIn(ids))
if err != nil {
+17 -17
View File
@@ -22,26 +22,26 @@ import (
"github.com/pkg/errors"
)
func (u *BackupService) AppBackup(req dto.CommonBackup) error {
func (u *BackupService) AppBackup(req dto.CommonBackup) (*model.BackupRecord, error) {
localDir, err := loadLocalDir()
if err != nil {
return err
return nil, err
}
app, err := appRepo.GetFirst(appRepo.WithKey(req.Name))
if err != nil {
return err
return nil, err
}
install, err := appInstallRepo.GetFirst(commonRepo.WithByName(req.DetailName), appInstallRepo.WithAppId(app.ID))
if err != nil {
return err
return nil, err
}
timeNow := time.Now().Format("20060102150405")
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))
if err := handleAppBackup(&install, backupDir, fileName); err != nil {
return err
if err := handleAppBackup(&install, backupDir, fileName, "", req.Secret); err != nil {
return nil, err
}
record := &model.BackupRecord{
@@ -56,9 +56,9 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) error {
if err := backupRepo.CreateRecord(record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
return nil, err
}
return nil
return record, nil
}
func (u *BackupService) AppRecover(req dto.CommonRecover) error {
@@ -78,13 +78,13 @@ func (u *BackupService) AppRecover(req dto.CommonRecover) error {
if _, err := compose.Down(install.GetComposePath()); err != nil {
return err
}
if err := handleAppRecover(&install, req.File, false); err != nil {
if err := handleAppRecover(&install, req.File, false, req.Secret); err != nil {
return err
}
return nil
}
func handleAppBackup(install *model.AppInstall, backupDir, fileName string) error {
func handleAppBackup(install *model.AppInstall, backupDir, fileName string, excludes string, secret string) error {
fileOp := files.NewFileOp()
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
if !fileOp.Stat(tmpDir) {
@@ -103,7 +103,7 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
}
appPath := install.GetPath()
if err := handleTar(appPath, tmpDir, "app.tar.gz", ""); err != nil {
if err := handleTar(appPath, tmpDir, "app.tar.gz", excludes, ""); err != nil {
return err
}
@@ -129,16 +129,16 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
}
}
if err := handleTar(tmpDir, backupDir, fileName, ""); err != nil {
if err := handleTar(tmpDir, backupDir, fileName, "", secret); err != nil {
return err
}
return nil
}
func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback bool) error {
func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback bool, secret string) error {
isOk := false
fileOp := files.NewFileOp()
if err := handleUnTar(recoverFile, path.Dir(recoverFile)); err != nil {
if err := handleUnTar(recoverFile, path.Dir(recoverFile), secret); err != nil {
return err
}
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
@@ -164,13 +164,13 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
if !isRollback {
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("app/%s_%s.tar.gz", install.Name, time.Now().Format("20060102150405")))
if err := handleAppBackup(install, path.Dir(rollbackFile), path.Base(rollbackFile)); err != nil {
if err := handleAppBackup(install, path.Dir(rollbackFile), path.Base(rollbackFile), "", ""); err != nil {
return fmt.Errorf("backup app %s for rollback before recover failed, err: %v", install.Name, err)
}
defer func() {
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := handleAppRecover(install, rollbackFile, true); err != nil {
if err := handleAppRecover(install, rollbackFile, true, secret); err != nil {
global.LOG.Errorf("rollback app %s from %s failed, err: %v", install.Name, rollbackFile, err)
return
}
@@ -251,7 +251,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
_ = fileOp.Rename(appDir, backPath)
_ = fileOp.CreateDir(appDir, 0755)
if err := handleUnTar(tmpPath+"/app.tar.gz", install.GetAppPath()); err != nil {
if err := handleUnTar(tmpPath+"/app.tar.gz", install.GetAppPath(), ""); err != nil {
global.LOG.Errorf("handle recover from app.tar.gz failed, err: %v", err)
_ = fileOp.DeleteDir(appDir)
_ = fileOp.Rename(backPath, appDir)
+1 -1
View File
@@ -66,7 +66,7 @@ func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
return fmt.Errorf("mkdir %s failed, err: %v", dstDir, err)
}
}
if err := handleUnTar(req.File, dstDir); err != nil {
if err := handleUnTar(req.File, dstDir, ""); err != nil {
_ = os.RemoveAll(dstDir)
return err
}
+1 -1
View File
@@ -66,7 +66,7 @@ func (u *BackupService) PostgresqlRecoverByUpload(req dto.CommonRecover) error {
return fmt.Errorf("mkdir %s failed, err: %v", dstDir, err)
}
}
if err := handleUnTar(req.File, dstDir); err != nil {
if err := handleUnTar(req.File, dstDir, ""); err != nil {
_ = os.RemoveAll(dstDir)
return err
}
+12 -11
View File
@@ -20,12 +20,12 @@ import (
"github.com/pkg/errors"
)
func (u *BackupService) RedisBackup() error {
func (u *BackupService) RedisBackup(db dto.CommonBackup) error {
localDir, err := loadLocalDir()
if err != nil {
return err
}
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", db.Name)
if err != nil {
return err
}
@@ -46,11 +46,12 @@ func (u *BackupService) RedisBackup() error {
}
itemDir := fmt.Sprintf("database/redis/%s", redisInfo.Name)
backupDir := path.Join(localDir, itemDir)
if err := handleRedisBackup(redisInfo, backupDir, fileName); err != nil {
if err := handleRedisBackup(redisInfo, backupDir, fileName, db.Secret); err != nil {
return err
}
record := &model.BackupRecord{
Type: "redis",
Name: db.Name,
Source: "LOCAL",
BackupType: "LOCAL",
FileDir: itemDir,
@@ -64,18 +65,18 @@ func (u *BackupService) RedisBackup() error {
}
func (u *BackupService) RedisRecover(req dto.CommonRecover) error {
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
if err != nil {
return err
}
global.LOG.Infof("recover redis from backup file %s", req.File)
if err := handleRedisRecover(redisInfo, req.File, false); err != nil {
if err := handleRedisRecover(redisInfo, req.File, false, req.Secret); err != nil {
return err
}
return nil
}
func handleRedisBackup(redisInfo *repo.RootInfo, backupDir, fileName string) error {
func handleRedisBackup(redisInfo *repo.RootInfo, backupDir, fileName string, secret string) error {
fileOp := files.NewFileOp()
if !fileOp.Stat(backupDir) {
if err := os.MkdirAll(backupDir, os.ModePerm); err != nil {
@@ -90,7 +91,7 @@ func handleRedisBackup(redisInfo *repo.RootInfo, backupDir, fileName string) err
if strings.HasSuffix(fileName, ".tar.gz") {
redisDataDir := fmt.Sprintf("%s/%s/%s/data/appendonlydir", constant.AppInstallDir, "redis", redisInfo.Name)
if err := handleTar(redisDataDir, backupDir, fileName, ""); err != nil {
if err := handleTar(redisDataDir, backupDir, fileName, "", secret); err != nil {
return err
}
return nil
@@ -110,7 +111,7 @@ func handleRedisBackup(redisInfo *repo.RootInfo, backupDir, fileName string) err
return nil
}
func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback bool) error {
func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback bool, secret string) error {
fileOp := files.NewFileOp()
if !fileOp.Stat(recoverFile) {
return buserr.WithName("ErrFileNotFound", recoverFile)
@@ -146,13 +147,13 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
}
}
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/redis/%s_%s.%s", redisInfo.Name, time.Now().Format("20060102150405"), suffix))
if err := handleRedisBackup(redisInfo, path.Dir(rollbackFile), path.Base(rollbackFile)); err != nil {
if err := handleRedisBackup(redisInfo, path.Dir(rollbackFile), path.Base(rollbackFile), secret); err != nil {
return fmt.Errorf("backup database %s for rollback before recover failed, err: %v", redisInfo.Name, err)
}
defer func() {
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := handleRedisRecover(redisInfo, rollbackFile, true); err != nil {
if err := handleRedisRecover(redisInfo, rollbackFile, true, secret); err != nil {
global.LOG.Errorf("rollback redis from %s failed, err: %v", rollbackFile, err)
return
}
@@ -169,7 +170,7 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
}
if appendonly == "yes" && strings.HasPrefix(redisInfo.Version, "7.") {
redisDataDir := fmt.Sprintf("%s/%s/%s/data", constant.AppInstallDir, "redis", redisInfo.Name)
if err := handleUnTar(recoverFile, redisDataDir); err != nil {
if err := handleUnTar(recoverFile, redisDataDir, secret); err != nil {
return err
}
} else {
+13 -12
View File
@@ -4,18 +4,19 @@ import (
"encoding/json"
"errors"
"fmt"
"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/files"
"io/fs"
"os"
"path"
"strings"
"time"
"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/files"
)
func handleRuntimeBackup(runtime *model.Runtime, backupDir, fileName string) error {
func handleRuntimeBackup(runtime *model.Runtime, backupDir, fileName string, excludes string, secret string) error {
fileOp := files.NewFileOp()
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
if !fileOp.Stat(tmpDir) {
@@ -34,19 +35,19 @@ func handleRuntimeBackup(runtime *model.Runtime, backupDir, fileName string) err
}
appPath := runtime.GetPath()
if err := handleTar(appPath, tmpDir, "runtime.tar.gz", ""); err != nil {
if err := handleTar(appPath, tmpDir, "runtime.tar.gz", excludes, secret); err != nil {
return err
}
if err := handleTar(tmpDir, backupDir, fileName, ""); err != nil {
if err := handleTar(tmpDir, backupDir, fileName, "", secret); err != nil {
return err
}
return nil
}
func handleRuntimeRecover(runtime *model.Runtime, recoverFile string, isRollback bool) error {
func handleRuntimeRecover(runtime *model.Runtime, recoverFile string, isRollback bool, secret string) error {
isOk := false
fileOp := files.NewFileOp()
if err := handleUnTar(recoverFile, path.Dir(recoverFile)); err != nil {
if err := handleUnTar(recoverFile, path.Dir(recoverFile), secret); err != nil {
return err
}
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
@@ -72,13 +73,13 @@ func handleRuntimeRecover(runtime *model.Runtime, recoverFile string, isRollback
if !isRollback {
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("runtime/%s_%s.tar.gz", runtime.Name, time.Now().Format("20060102150405")))
if err := handleRuntimeBackup(runtime, path.Dir(rollbackFile), path.Base(rollbackFile)); err != nil {
if err := handleRuntimeBackup(runtime, path.Dir(rollbackFile), path.Base(rollbackFile), "", secret); err != nil {
return fmt.Errorf("backup runtime %s for rollback before recover failed, err: %v", runtime.Name, err)
}
defer func() {
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := handleRuntimeRecover(runtime, rollbackFile, true); err != nil {
if err := handleRuntimeRecover(runtime, rollbackFile, true, secret); err != nil {
global.LOG.Errorf("rollback runtime %s from %s failed, err: %v", runtime.Name, rollbackFile, err)
return
}
@@ -99,7 +100,7 @@ func handleRuntimeRecover(runtime *model.Runtime, recoverFile string, isRollback
_ = fileOp.Rename(runtimeDir, backPath)
_ = fileOp.CreateDir(runtimeDir, 0755)
if err := handleUnTar(tmpPath+"/runtime.tar.gz", fmt.Sprintf("%s/%s", constant.RuntimeDir, runtime.Type)); err != nil {
if err := handleUnTar(tmpPath+"/runtime.tar.gz", fmt.Sprintf("%s/%s", constant.RuntimeDir, runtime.Type), secret); err != nil {
global.LOG.Errorf("handle recover from runtime.tar.gz failed, err: %v", err)
_ = fileOp.DeleteDir(runtimeDir)
_ = fileOp.Rename(backPath, runtimeDir)
+17 -17
View File
@@ -35,7 +35,7 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
itemDir := fmt.Sprintf("website/%s", req.Name)
backupDir := path.Join(localDir, itemDir)
fileName := fmt.Sprintf("%s_%s.tar.gz", website.PrimaryDomain, timeNow+common.RandStrAndNum(5))
if err := handleWebsiteBackup(&website, backupDir, fileName); err != nil {
if err := handleWebsiteBackup(&website, backupDir, fileName, "", req.Secret); err != nil {
return err
}
@@ -65,16 +65,16 @@ func (u *BackupService) WebsiteRecover(req dto.CommonRecover) error {
return err
}
global.LOG.Infof("recover website %s from backup file %s", req.Name, req.File)
if err := handleWebsiteRecover(&website, req.File, false); err != nil {
if err := handleWebsiteRecover(&website, req.File, false, req.Secret); err != nil {
return err
}
return nil
}
func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback bool) error {
func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback bool, secret string) error {
fileOp := files.NewFileOp()
tmpPath := strings.ReplaceAll(recoverFile, ".tar.gz", "")
if err := handleUnTar(recoverFile, path.Dir(recoverFile)); err != nil {
if err := handleUnTar(recoverFile, path.Dir(recoverFile), secret); err != nil {
return err
}
defer func() {
@@ -107,13 +107,13 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
isOk := false
if !isRollback {
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s_%s.tar.gz", website.Alias, time.Now().Format("20060102150405")))
if err := handleWebsiteBackup(website, path.Dir(rollbackFile), path.Base(rollbackFile)); err != nil {
if err := handleWebsiteBackup(website, path.Dir(rollbackFile), path.Base(rollbackFile), "", ""); err != nil {
return fmt.Errorf("backup website %s for rollback before recover failed, err: %v", website.Alias, err)
}
defer func() {
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := handleWebsiteRecover(website, rollbackFile, true); err != nil {
if err := handleWebsiteRecover(website, rollbackFile, true, ""); err != nil {
global.LOG.Errorf("rollback website %s from %s failed, err: %v", website.Alias, rollbackFile, err)
return
}
@@ -141,7 +141,7 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
if err != nil {
return err
}
if err := handleAppRecover(&app, fmt.Sprintf("%s/%s.app.tar.gz", tmpPath, website.Alias), true); err != nil {
if err := handleAppRecover(&app, fmt.Sprintf("%s/%s.app.tar.gz", tmpPath, website.Alias), true, ""); err != nil {
global.LOG.Errorf("handle recover from app.tar.gz failed, err: %v", err)
return err
}
@@ -155,7 +155,7 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
return err
}
if runtime.Type == constant.RuntimeNode {
if err := handleRuntimeRecover(runtime, fmt.Sprintf("%s/%s.runtime.tar.gz", tmpPath, website.Alias), true); err != nil {
if err := handleRuntimeRecover(runtime, fmt.Sprintf("%s/%s.runtime.tar.gz", tmpPath, website.Alias), true, ""); err != nil {
return err
}
global.LOG.Info("put runtime.tar.gz into tmp dir successful")
@@ -163,7 +163,7 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
}
siteDir := fmt.Sprintf("%s/openresty/%s/www/sites", constant.AppInstallDir, nginxInfo.Name)
if err := handleUnTar(fmt.Sprintf("%s/%s.web.tar.gz", tmpPath, website.Alias), siteDir); err != nil {
if err := handleUnTar(fmt.Sprintf("%s/%s.web.tar.gz", tmpPath, website.Alias), siteDir, ""); err != nil {
global.LOG.Errorf("handle recover from web.tar.gz failed, err: %v", err)
return err
}
@@ -182,7 +182,7 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
return nil
}
func handleWebsiteBackup(website *model.Website, backupDir, fileName string) error {
func handleWebsiteBackup(website *model.Website, backupDir, fileName string, excludes string, secret string) error {
fileOp := files.NewFileOp()
tmpDir := fmt.Sprintf("%s/%s", backupDir, strings.ReplaceAll(fileName, ".tar.gz", ""))
if !fileOp.Stat(tmpDir) {
@@ -216,7 +216,7 @@ func handleWebsiteBackup(website *model.Website, backupDir, fileName string) err
if err != nil {
return err
}
if err := handleAppBackup(&app, tmpDir, fmt.Sprintf("%s.app.tar.gz", website.Alias)); err != nil {
if err := handleAppBackup(&app, tmpDir, fmt.Sprintf("%s.app.tar.gz", website.Alias), excludes, ""); err != nil {
return err
}
global.LOG.Info("put app.tar.gz into tmp dir successful")
@@ -226,7 +226,7 @@ func handleWebsiteBackup(website *model.Website, backupDir, fileName string) err
return err
}
if runtime.Type == constant.RuntimeNode {
if err := handleRuntimeBackup(runtime, tmpDir, fmt.Sprintf("%s.runtime.tar.gz", website.Alias)); err != nil {
if err := handleRuntimeBackup(runtime, tmpDir, fmt.Sprintf("%s.runtime.tar.gz", website.Alias), excludes, ""); err != nil {
return err
}
global.LOG.Info("put runtime.tar.gz into tmp dir successful")
@@ -234,11 +234,11 @@ func handleWebsiteBackup(website *model.Website, backupDir, fileName string) err
}
websiteDir := fmt.Sprintf("%s/openresty/%s/www/sites/%s", constant.AppInstallDir, nginxInfo.Name, website.Alias)
if err := handleTar(websiteDir, tmpDir, fmt.Sprintf("%s.web.tar.gz", website.Alias), ""); err != nil {
if err := handleTar(websiteDir, tmpDir, fmt.Sprintf("%s.web.tar.gz", website.Alias), excludes, ""); err != nil {
return err
}
global.LOG.Info("put web.tar.gz into tmp dir successful, now start to tar tmp dir")
if err := handleTar(tmpDir, backupDir, fileName, ""); err != nil {
if err := handleTar(tmpDir, backupDir, fileName, "", secret); err != nil {
return err
}
@@ -250,18 +250,18 @@ func checkValidOfWebsite(oldWebsite, website *model.Website) error {
return buserr.WithDetail(constant.ErrBackupMatch, fmt.Sprintf("oldName: %s, oldType: %v", oldWebsite.Alias, oldWebsite.Type), nil)
}
if oldWebsite.AppInstallID != 0 {
_, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
_, err := appInstallRepo.GetFirst(commonRepo.WithByID(oldWebsite.AppInstallID))
if err != nil {
return buserr.WithDetail(constant.ErrBackupMatch, "app", nil)
}
}
if oldWebsite.RuntimeID != 0 {
if _, err := runtimeRepo.GetFirst(commonRepo.WithByID(website.RuntimeID)); err != nil {
if _, err := runtimeRepo.GetFirst(commonRepo.WithByID(oldWebsite.RuntimeID)); err != nil {
return buserr.WithDetail(constant.ErrBackupMatch, "runtime", nil)
}
}
if oldWebsite.WebsiteSSLID != 0 {
if _, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(website.WebsiteSSLID)); err != nil {
if _, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(oldWebsite.WebsiteSSLID)); err != nil {
return buserr.WithDetail(constant.ErrBackupMatch, "ssl", nil)
}
}
+67 -1
View File
@@ -2,6 +2,7 @@ package service
import (
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
@@ -16,6 +17,11 @@ type ICommandService interface {
Create(commandDto dto.CommandOperate) error
Update(id uint, upMap map[string]interface{}) error
Delete(ids []uint) error
SearchRedisCommandWithPage(search dto.SearchWithPage) (int64, interface{}, error)
ListRedisCommand() ([]dto.RedisCommand, error)
SaveRedisCommand(commandDto dto.RedisCommand) error
DeleteRedisCommand(ids []uint) error
}
func NewICommandService() ICommandService {
@@ -65,7 +71,7 @@ func (u *CommandService) SearchForTree() ([]dto.CommandTree, error) {
}
func (u *CommandService) SearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error) {
total, commands, err := commandRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info), commonRepo.WithByGroupID(search.GroupID), commonRepo.WithOrderRuleBy(search.OrderBy, search.Order))
total, commands, err := commandRepo.Page(search.Page, search.PageSize, commandRepo.WithLikeName(search.Name), commonRepo.WithLikeName(search.Info), commonRepo.WithByGroupID(search.GroupID), commonRepo.WithOrderRuleBy(search.OrderBy, search.Order))
if err != nil {
return 0, nil, err
}
@@ -115,3 +121,63 @@ func (u *CommandService) Delete(ids []uint) error {
func (u *CommandService) Update(id uint, upMap map[string]interface{}) error {
return commandRepo.Update(id, upMap)
}
func (u *CommandService) SearchRedisCommandWithPage(search dto.SearchWithPage) (int64, interface{}, error) {
total, commands, err := commandRepo.PageRedis(search.Page, search.PageSize, commandRepo.WithLikeName(search.Info))
if err != nil {
return 0, nil, err
}
var dtoCommands []dto.RedisCommand
for _, command := range commands {
var item dto.RedisCommand
if err := copier.Copy(&item, &command); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dtoCommands = append(dtoCommands, item)
}
return total, dtoCommands, err
}
func (u *CommandService) ListRedisCommand() ([]dto.RedisCommand, error) {
commands, err := commandRepo.GetRedisList()
if err != nil {
return nil, constant.ErrRecordNotFound
}
var dtoCommands []dto.RedisCommand
for _, command := range commands {
var item dto.RedisCommand
if err := copier.Copy(&item, &command); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dtoCommands = append(dtoCommands, item)
}
return dtoCommands, err
}
func (u *CommandService) SaveRedisCommand(req dto.RedisCommand) error {
if req.ID == 0 {
command, _ := commandRepo.GetRedis(commonRepo.WithByName(req.Name))
if command.ID != 0 {
return constant.ErrRecordExist
}
}
var command model.RedisCommand
if err := copier.Copy(&command, &req); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
if err := commandRepo.SaveRedis(&command); err != nil {
return err
}
return nil
}
func (u *CommandService) DeleteRedisCommand(ids []uint) error {
if len(ids) == 1 {
command, _ := commandRepo.GetRedis(commonRepo.WithByID(ids[0]))
if command.ID == 0 {
return constant.ErrRecordNotFound
}
return commandRepo.DeleteRedis(commonRepo.WithByID(ids[0]))
}
return commandRepo.DeleteRedis(commonRepo.WithIdsIn(ids))
}
+153 -99
View File
@@ -29,6 +29,8 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/client"
@@ -87,7 +89,10 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
if err != nil {
return 0, nil, err
}
options := types.ContainerListOptions{All: true}
defer client.Close()
options := container.ListOptions{
All: true,
}
if len(req.Filters) != 0 {
options.Filters = filters.NewArgs()
options.Filters.Add("label", req.Filters)
@@ -216,7 +221,8 @@ func (u *ContainerService) List() ([]string, error) {
if err != nil {
return nil, err
}
containers, err := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
defer client.Close()
containers, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
if err != nil {
return nil, err
}
@@ -237,7 +243,8 @@ func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error
if err != nil {
return nil, err
}
list, err := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
defer client.Close()
list, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
if err != nil {
return nil, err
}
@@ -259,6 +266,7 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
if err != nil {
return "", err
}
defer client.Close()
var inspectInfo interface{}
switch req.Type {
case "container":
@@ -286,6 +294,7 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneRepo
if err != nil {
return report, err
}
defer client.Close()
pruneFilters := filters.NewArgs()
if req.WithTagAll {
pruneFilters.Add("dangling", "false")
@@ -315,12 +324,28 @@ func (u *ContainerService) Prune(req dto.ContainerPrune) (dto.ContainerPruneRepo
}
report.DeletedNumber = len(rep.NetworksDeleted)
case "volume":
versions, err := client.ServerVersion(context.Background())
if err != nil {
return report, err
}
if common.ComparePanelVersion(versions.APIVersion, "1.42") {
pruneFilters.Add("all", "true")
}
rep, err := client.VolumesPrune(context.Background(), pruneFilters)
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.VolumesDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
case "buildcache":
opts := types.BuildCachePruneOptions{}
opts.All = true
rep, err := client.BuildCachePrune(context.Background(), opts)
if err != nil {
return report, err
}
report.DeletedNumber = len(rep.CachesDeleted)
report.SpaceReclaimed = int(rep.SpaceReclaimed)
}
return report, nil
}
@@ -347,6 +372,7 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error {
if err != nil {
return err
}
defer client.Close()
ctx := context.Background()
newContainer, _ := client.ContainerInspect(ctx, req.Name)
if newContainer.ContainerJSONBase != nil {
@@ -376,14 +402,14 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error {
return err
}
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
container, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
con, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
if err != nil {
_ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
return err
}
global.LOG.Infof("create container %s successful! now check if the container is started and delete the container information if it is not.", req.Name)
if err := client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}); err != nil {
_ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
if err := client.ContainerStart(ctx, con.ID, container.StartOptions{}); err != nil {
_ = client.ContainerRemove(ctx, req.Name, container.RemoveOptions{RemoveVolumes: true, Force: true})
return fmt.Errorf("create successful but start failed, err: %v", err)
}
return nil
@@ -394,6 +420,7 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
if err != nil {
return nil, err
}
defer client.Close()
ctx := context.Background()
oldContainer, err := client.ContainerInspect(ctx, req.Name)
if err != nil {
@@ -410,6 +437,18 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
break
}
}
networkSettings := oldContainer.NetworkSettings
bridgeNetworkSettings := networkSettings.Networks[data.Network]
if bridgeNetworkSettings.IPAMConfig != nil {
ipv4Address := bridgeNetworkSettings.IPAMConfig.IPv4Address
data.Ipv4 = ipv4Address
ipv6Address := bridgeNetworkSettings.IPAMConfig.IPv6Address
data.Ipv6 = ipv6Address
} else {
data.Ipv4 = bridgeNetworkSettings.IPAddress
}
data.Cmd = oldContainer.Config.Cmd
data.OpenStdin = oldContainer.Config.OpenStdin
data.Tty = oldContainer.Config.Tty
@@ -442,7 +481,7 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
if oldContainer.HostConfig.Memory != 0 {
data.Memory = float64(oldContainer.HostConfig.Memory) / 1024 / 1024
}
data.Volumes = loadVolumeBinds(oldContainer.HostConfig.Binds)
data.Volumes = loadVolumeBinds(oldContainer.Mounts)
return &data, nil
}
@@ -452,6 +491,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
if err != nil {
return err
}
defer client.Close()
ctx := context.Background()
newContainer, _ := client.ContainerInspect(ctx, req.Name)
if newContainer.ContainerJSONBase != nil && newContainer.ID != req.ContainerID {
@@ -471,7 +511,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
}
}
if err := client.ContainerRemove(ctx, req.ContainerID, types.ContainerRemoveOptions{Force: true}); err != nil {
if err := client.ContainerRemove(ctx, req.ContainerID, container.RemoveOptions{Force: true}); err != nil {
return err
}
@@ -482,13 +522,13 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
}
global.LOG.Infof("new container info %s has been update, now start to recreate", req.Name)
container, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
con, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
if err != nil {
reCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
return fmt.Errorf("update container failed, err: %v", err)
}
global.LOG.Infof("update container %s successful! now check if the container is started.", req.Name)
if err := client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}); err != nil {
if err := client.ContainerStart(ctx, con.ID, container.StartOptions{}); err != nil {
return fmt.Errorf("update successful but start failed, err: %v", err)
}
@@ -500,6 +540,7 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
if err != nil {
return err
}
defer client.Close()
ctx := context.Background()
oldContainer, err := client.ContainerInspect(ctx, req.Name)
if err != nil {
@@ -523,18 +564,18 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
break
}
}
if err := client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{Force: true}); err != nil {
if err := client.ContainerRemove(ctx, req.Name, container.RemoveOptions{Force: true}); err != nil {
return err
}
global.LOG.Infof("new container info %s has been update, now start to recreate", req.Name)
container, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, req.Name)
con, err := client.ContainerCreate(ctx, config, hostConf, &networkConf, &v1.Platform{}, req.Name)
if err != nil {
reCreateAfterUpdate(req.Name, client, oldContainer.Config, oldContainer.HostConfig, oldContainer.NetworkSettings)
return fmt.Errorf("upgrade container failed, err: %v", err)
}
global.LOG.Infof("upgrade container %s successful! now check if the container is started.", req.Name)
if err := client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}); err != nil {
if err := client.ContainerStart(ctx, con.ID, container.StartOptions{}); err != nil {
return fmt.Errorf("upgrade successful but start failed, err: %v", err)
}
@@ -547,6 +588,7 @@ func (u *ContainerService) ContainerRename(req dto.ContainerRename) error {
if err != nil {
return err
}
defer client.Close()
newContainer, _ := client.ContainerInspect(ctx, req.NewName)
if newContainer.ContainerJSONBase != nil {
@@ -562,11 +604,12 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
if err != nil {
return err
}
defer client.Close()
for _, item := range req.Names {
global.LOG.Infof("start container %s operation %s", item, req.Operation)
switch req.Operation {
case constant.ContainerOpStart:
err = client.ContainerStart(ctx, item, types.ContainerStartOptions{})
err = client.ContainerStart(ctx, item, container.StartOptions{})
case constant.ContainerOpStop:
err = client.ContainerStop(ctx, item, container.StopOptions{})
case constant.ContainerOpRestart:
@@ -578,7 +621,7 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
case constant.ContainerOpUnpause:
err = client.ContainerUnpause(ctx, item)
case constant.ContainerOpRemove:
err = client.ContainerRemove(ctx, item, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
err = client.ContainerRemove(ctx, item, container.RemoveOptions{RemoveVolumes: true, Force: true})
}
}
return err
@@ -589,6 +632,7 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
if err != nil {
return err
}
defer client.Close()
ctx := context.Background()
containerItem, err := client.ContainerInspect(ctx, req.Name)
if err != nil {
@@ -612,7 +656,7 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
_ = os.Remove(file)
}
if err := client.ContainerStart(ctx, containerItem.ID, types.ContainerStartOptions{}); err != nil {
if err := client.ContainerStart(ctx, containerItem.ID, container.StartOptions{}); err != nil {
return err
}
return nil
@@ -623,22 +667,26 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType,
if cmd.CheckIllegal(container, since, tail) {
return buserr.New(constant.ErrCmdIllegal)
}
command := fmt.Sprintf("docker logs %s", container)
commandName := "docker"
commandArg := []string{"logs", container}
if containerType == "compose" {
command = fmt.Sprintf("docker-compose -f %s logs", container)
commandName = "docker-compose"
commandArg = []string{"-f", container, "logs"}
}
if tail != "0" {
command += " --tail " + tail
commandArg = append(commandArg, "--tail")
commandArg = append(commandArg, tail)
}
if since != "all" {
command += " --since " + since
commandArg = append(commandArg, "--since")
commandArg = append(commandArg, since)
}
if follow {
command += " -f"
commandArg = append(commandArg, "-f")
}
command += " 2>&1"
cmd := exec.Command("bash", "-c", command)
if !follow {
cmd := exec.Command(commandName, commandArg...)
cmd.Stderr = cmd.Stdout
stdout, _ := cmd.CombinedOutput()
if !utf8.Valid(stdout) {
return errors.New("invalid utf8")
@@ -649,50 +697,53 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, containerType,
return nil
}
cmd := exec.Command(commandName, commandArg...)
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
if err := cmd.Start(); err != nil {
return err
}
defer func() {
_ = cmd.Process.Signal(syscall.SIGTERM)
_ = cmd.Wait()
}()
var exitCh chan struct{}
if follow {
go func() {
_, wsData, _ := wsConn.ReadMessage()
if string(wsData) == "close conn" {
exitCh <- struct{}{}
}
}()
return err
}
cmd.Stderr = cmd.Stdout
if err := cmd.Start(); err != nil {
_ = cmd.Process.Signal(syscall.SIGTERM)
return err
}
exitCh := make(chan struct{})
go func() {
_, wsData, _ := wsConn.ReadMessage()
if string(wsData) == "close conn" {
_ = cmd.Process.Signal(syscall.SIGTERM)
exitCh <- struct{}{}
}
}()
buffer := make([]byte, 1024)
for {
select {
case <-exitCh:
return nil
default:
n, err := stdout.Read(buffer)
if err != nil {
if err == io.EOF {
return err
go func() {
buffer := make([]byte, 1024)
for {
select {
case <-exitCh:
return
default:
n, err := stdout.Read(buffer)
if err != nil {
if err == io.EOF {
return
}
global.LOG.Errorf("read bytes from log failed, err: %v", err)
return
}
if !utf8.Valid(buffer[:n]) {
continue
}
if err = wsConn.WriteMessage(websocket.TextMessage, buffer[:n]); err != nil {
global.LOG.Errorf("send message with log to ws failed, err: %v", err)
return
}
global.LOG.Errorf("read bytes from log failed, err: %v", err)
continue
}
if !utf8.Valid(buffer[:n]) {
continue
}
if err = wsConn.WriteMessage(websocket.TextMessage, buffer[:n]); err != nil {
global.LOG.Errorf("send message with log to ws failed, err: %v", err)
return err
}
}
}
}()
_ = cmd.Wait()
return nil
}
func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error) {
@@ -700,6 +751,7 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
if err != nil {
return nil, err
}
defer client.Close()
res, err := client.ContainerStats(context.TODO(), id, false)
if err != nil {
return nil, err
@@ -735,7 +787,8 @@ func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) s
if err != nil {
return ""
}
options := types.ContainerListOptions{All: true}
defer cli.Close()
options := container.ListOptions{All: true}
options.Filters = filters.NewArgs()
options.Filters.Add("label", fmt.Sprintf("%s=%s", composeProjectLabel, req.Name))
containers, err := cli.ContainerList(context.Background(), options)
@@ -818,16 +871,15 @@ func calculateNetwork(network map[string]types.NetworkStats) (float64, float64)
return rx, tx
}
func checkImageExist(client *client.Client, image string) bool {
images, err := client.ImageList(context.Background(), types.ImageListOptions{})
func checkImageExist(client *client.Client, imageItem string) bool {
images, err := client.ImageList(context.Background(), image.ListOptions{})
if err != nil {
fmt.Println(err)
return false
}
for _, img := range images {
for _, tag := range img.RepoTags {
if tag == image || tag == image+":latest" {
if tag == imageItem || tag == imageItem+":latest" {
return true
}
}
@@ -875,12 +927,11 @@ func loadCpuAndMem(client *client.Client, container string) dto.ContainerListSta
return data
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
res.Body.Close()
return data
}
res.Body.Close()
var stats *types.StatsJSON
if err := json.Unmarshal(body, &stats); err != nil {
return data
@@ -976,8 +1027,21 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
case "host", "none", "bridge":
hostConf.NetworkMode = container.NetworkMode(req.Network)
}
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
if req.Ipv4 != "" || req.Ipv6 != "" {
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{
req.Network: {
IPAMConfig: &network.EndpointIPAMConfig{
IPv4Address: req.Ipv4,
IPv6Address: req.Ipv6,
},
}}
} else {
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
}
} else {
if req.Ipv4 != "" || req.Ipv6 != "" {
return nil, nil, nil, fmt.Errorf("Please set up the network")
}
networkConf = network.NetworkingConfig{}
}
@@ -994,10 +1058,19 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
hostConf.MemorySwap = 0
hostConf.PortBindings = portMap
hostConf.Binds = []string{}
hostConf.Mounts = []mount.Mount{}
config.Volumes = make(map[string]struct{})
for _, volume := range req.Volumes {
config.Volumes[volume.ContainerDir] = struct{}{}
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.SourceDir, volume.ContainerDir, volume.Mode))
if volume.Type == "volume" {
hostConf.Mounts = append(hostConf.Mounts, mount.Mount{
Type: mount.Type(volume.Type),
Source: volume.SourceDir,
Target: volume.ContainerDir,
})
config.Volumes[volume.ContainerDir] = struct{}{}
} else {
hostConf.Binds = append(hostConf.Binds, fmt.Sprintf("%s:%s:%s", volume.SourceDir, volume.ContainerDir, volume.Mode))
}
}
return &config, &hostConf, &networkConf, nil
}
@@ -1018,46 +1091,27 @@ func reCreateAfterUpdate(name string, client *client.Client, config *container.C
global.LOG.Errorf("recreate after container update failed, err: %v", err)
return
}
if err := client.ContainerStart(ctx, oldContainer.ID, types.ContainerStartOptions{}); err != nil {
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")
}
func loadVolumeBinds(binds []string) []dto.VolumeHelper {
func loadVolumeBinds(binds []types.MountPoint) []dto.VolumeHelper {
var datas []dto.VolumeHelper
for _, bind := range binds {
parts := strings.Split(bind, ":")
var volumeItem dto.VolumeHelper
if len(parts) > 3 {
continue
volumeItem.Type = string(bind.Type)
if bind.Type == "volume" {
volumeItem.SourceDir = bind.Name
} else {
volumeItem.SourceDir = bind.Source
}
volumeItem.SourceDir = parts[0]
if len(parts) == 1 {
volumeItem.ContainerDir = parts[0]
volumeItem.ContainerDir = bind.Destination
volumeItem.Mode = "ro"
if bind.RW {
volumeItem.Mode = "rw"
}
if len(parts) == 2 {
switch parts[1] {
case "r", "ro":
volumeItem.ContainerDir = parts[0]
volumeItem.Mode = "ro"
case "rw":
volumeItem.ContainerDir = parts[0]
volumeItem.Mode = "rw"
default:
volumeItem.ContainerDir = parts[1]
volumeItem.Mode = "rw"
}
}
if len(parts) == 3 {
volumeItem.ContainerDir = parts[1]
if parts[2] == "r" {
volumeItem.Mode = "ro"
} else {
volumeItem.Mode = parts[2]
}
}
datas = append(datas, volumeItem)
}
return datas
+4 -2
View File
@@ -12,6 +12,8 @@ import (
"strings"
"time"
"github.com/docker/docker/api/types/container"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/buserr"
@@ -20,7 +22,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"golang.org/x/net/context"
)
@@ -39,8 +40,9 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
if err != nil {
return 0, nil, err
}
defer client.Close()
options := types.ContainerListOptions{All: true}
options := container.ListOptions{All: true}
options.Filters = filters.NewArgs()
options.Filters.Add("label", composeProjectLabel)
+4
View File
@@ -19,6 +19,7 @@ func (u *ContainerService) PageNetwork(req dto.SearchWithPage) (int64, interface
if err != nil {
return 0, nil, err
}
defer client.Close()
list, err := client.NetworkList(context.TODO(), types.NetworkListOptions{})
if err != nil {
return 0, nil, err
@@ -81,6 +82,7 @@ func (u *ContainerService) ListNetwork() ([]dto.Options, error) {
if err != nil {
return nil, err
}
defer client.Close()
list, err := client.NetworkList(context.TODO(), types.NetworkListOptions{})
if err != nil {
return nil, err
@@ -100,6 +102,7 @@ func (u *ContainerService) DeleteNetwork(req dto.BatchDelete) error {
if err != nil {
return err
}
defer client.Close()
for _, id := range req.Names {
if err := client.NetworkRemove(context.TODO(), id); err != nil {
if strings.Contains(err.Error(), "has active endpoints") {
@@ -115,6 +118,7 @@ func (u *ContainerService) CreateNetwork(req dto.NetworkCreate) error {
if err != nil {
return err
}
defer client.Close()
var (
ipams []network.IPAMConfig
enableV6 bool
+3
View File
@@ -82,6 +82,7 @@ func (u *ContainerService) ListVolume() ([]dto.Options, error) {
if err != nil {
return nil, err
}
defer client.Close()
list, err := client.VolumeList(context.TODO(), volume.ListOptions{})
if err != nil {
return nil, err
@@ -102,6 +103,7 @@ func (u *ContainerService) DeleteVolume(req dto.BatchDelete) error {
if err != nil {
return err
}
defer client.Close()
for _, id := range req.Names {
if err := client.VolumeRemove(context.TODO(), id, true); err != nil {
if strings.Contains(err.Error(), "volume is in use") {
@@ -117,6 +119,7 @@ func (u *ContainerService) CreateVolume(req dto.VolumeCreate) error {
if err != nil {
return err
}
defer client.Close()
arg := filters.NewArgs()
arg.Add("name", req.Name)
vos, _ := client.VolumeList(context.TODO(), volume.ListOptions{Filters: arg})
+10 -6
View File
@@ -29,7 +29,7 @@ type ICronjobService interface {
UpdateStatus(id uint, status string) error
Delete(req dto.CronjobBatchDelete) error
Download(down dto.CronjobDownload) (string, error)
StartJob(cronjob *model.Cronjob) (string, error)
StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error)
CleanRecord(req dto.CronjobClean) error
LoadRecordLog(req dto.OperateByID) string
@@ -71,6 +71,7 @@ func (u *CronjobService) SearchRecords(search dto.SearchRecord) (int64, interfac
if err := copier.Copy(&item, &record); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
item.StartTime = record.StartTime.Format("2006-01-02 15:04:05")
dtoCronjobs = append(dtoCronjobs, item)
}
return total, dtoCronjobs, err
@@ -172,6 +173,7 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
if cronjob.ID != 0 {
return constant.ErrRecordExist
}
cronjob.Secret = cronjobDto.Secret
if err := copier.Copy(&cronjob, &cronjobDto); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
@@ -179,7 +181,7 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
global.LOG.Infof("create cronjob %s successful, spec: %s", cronjob.Name, cronjob.Spec)
spec := cronjob.Spec
entryIDs, err := u.StartJob(&cronjob)
entryIDs, err := u.StartJob(&cronjob, false)
if err != nil {
return err
}
@@ -191,8 +193,8 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
return nil
}
func (u *CronjobService) StartJob(cronjob *model.Cronjob) (string, error) {
if len(cronjob.EntryIDs) != 0 {
func (u *CronjobService) StartJob(cronjob *model.Cronjob, isUpdate bool) (string, error) {
if len(cronjob.EntryIDs) != 0 && isUpdate {
ids := strings.Split(cronjob.EntryIDs, ",")
for _, id := range ids {
idItem, _ := strconv.Atoi(id)
@@ -249,7 +251,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
cronjob.Type = cronModel.Type
spec := cronjob.Spec
if cronModel.Status == constant.StatusEnable {
newEntryIDs, err := u.StartJob(&cronjob)
newEntryIDs, err := u.StartJob(&cronjob, true)
if err != nil {
return err
}
@@ -265,6 +267,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
upMap["name"] = req.Name
upMap["spec"] = spec
upMap["script"] = req.Script
upMap["command"] = req.Command
upMap["container_name"] = req.ContainerName
upMap["app_id"] = req.AppID
upMap["website"] = req.Website
@@ -277,6 +280,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
upMap["backup_accounts"] = req.BackupAccounts
upMap["default_download"] = req.DefaultDownload
upMap["retain_copies"] = req.RetainCopies
upMap["secret"] = req.Secret
return cronjobRepo.Update(id, upMap)
}
@@ -290,7 +294,7 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
err error
)
if status == constant.StatusEnable {
entryIDs, err = u.StartJob(&cronjob)
entryIDs, err = u.StartJob(&cronjob, false)
if err != nil {
return err
}
+11 -11
View File
@@ -41,7 +41,7 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time) e
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format("20060102150405")+common.RandStrAndNum(5))
if err := handleAppBackup(&app, backupDir, record.FileName); err != nil {
if err := handleAppBackup(&app, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
return err
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
@@ -74,7 +74,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
record.Source, record.BackupType = loadRecordPath(cronjob, accountMap)
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("website/%s", web.PrimaryDomain))
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", web.PrimaryDomain, startTime.Format("20060102150405")+common.RandStrAndNum(5))
if err := handleWebsiteBackup(&web, backupDir, record.FileName); err != nil {
if err := handleWebsiteBackup(&web, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
return err
}
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
@@ -138,7 +138,7 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
}
fileName := fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405")+common.RandStrAndNum(5))
backupDir := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name))
if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules); err != nil {
if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
return err
}
var record model.BackupRecord
@@ -169,7 +169,7 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
nameItem := startTime.Format("20060102150405") + common.RandStrAndNum(5)
fileName := fmt.Sprintf("system_log_%s.tar.gz", nameItem)
backupDir := path.Join(global.CONF.System.TmpDir, "log", nameItem)
if err := handleBackupLogs(backupDir, fileName); err != nil {
if err := handleBackupLogs(backupDir, fileName, cronjob.Secret); err != nil {
return err
}
var record model.BackupRecord
@@ -210,7 +210,7 @@ func (u *CronjobService) handleSnapshot(cronjob model.Cronjob, startTime time.Ti
From: record.BackupType,
DefaultDownload: cronjob.DefaultDownload,
}
name, err := NewISnapshotService().HandleSnapshot(true, logPath, req, startTime.Format("20060102150405")+common.RandStrAndNum(5))
name, err := NewISnapshotService().HandleSnapshot(true, logPath, req, startTime.Format("20060102150405")+common.RandStrAndNum(5), cronjob.Secret)
if err != nil {
return err
}
@@ -303,7 +303,7 @@ func loadRecordPath(cronjob model.Cronjob, accountMap map[string]cronjobUploadHe
return source, backupType
}
func handleBackupLogs(targetDir, fileName string) error {
func handleBackupLogs(targetDir, fileName string, secret string) error {
websites, err := websiteRepo.List()
if err != nil {
return err
@@ -326,7 +326,7 @@ func handleBackupLogs(targetDir, fileName string) error {
if len(logFiles) != 0 {
for i := 0; i < len(logFiles); i++ {
if !logFiles[i].IsDir() {
_ = cpBinary([]string{path.Join(itemDir, logFiles[i].Name())}, dirItem)
_ = common.CopyFile(path.Join(itemDir, logFiles[i].Name()), dirItem)
}
}
}
@@ -335,7 +335,7 @@ func handleBackupLogs(targetDir, fileName string) error {
if len(logFiles2) != 0 {
for i := 0; i < len(logFiles2); i++ {
if !logFiles2[i].IsDir() {
_ = cpBinary([]string{path.Join(itemDir2, logFiles2[i].Name())}, dirItem)
_ = common.CopyFile(path.Join(itemDir2, logFiles2[i].Name()), dirItem)
}
}
}
@@ -354,7 +354,7 @@ func handleBackupLogs(targetDir, fileName string) error {
if len(systemLogFiles) != 0 {
for i := 0; i < len(systemLogFiles); i++ {
if !systemLogFiles[i].IsDir() {
_ = cpBinary([]string{path.Join(systemLogDir, systemLogFiles[i].Name())}, systemDir)
_ = common.CopyFile(path.Join(systemLogDir, systemLogFiles[i].Name()), systemDir)
}
}
}
@@ -370,13 +370,13 @@ func handleBackupLogs(targetDir, fileName string) error {
if len(loginLogFiles) != 0 {
for i := 0; i < len(loginLogFiles); i++ {
if !loginLogFiles[i].IsDir() && (strings.HasPrefix(loginLogFiles[i].Name(), "secure") || strings.HasPrefix(loginLogFiles[i].Name(), "auth.log")) {
_ = cpBinary([]string{path.Join("/var/log", loginLogFiles[i].Name())}, loginDir)
_ = common.CopyFile(path.Join("/var/log", loginLogFiles[i].Name()), loginDir)
}
}
}
global.LOG.Debug("backup ssh log successful!")
if err := handleTar(targetDir, path.Dir(targetDir), fileName, ""); err != nil {
if err := handleTar(targetDir, path.Dir(targetDir), fileName, "", secret); err != nil {
return err
}
defer func() {
+26 -7
View File
@@ -38,7 +38,11 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
_ = cronjobRepo.UpdateRecords(record.ID, map[string]interface{}{"records": record.Records})
script := cronjob.Script
if len(cronjob.ContainerName) != 0 {
script = fmt.Sprintf("docker exec %s %s", cronjob.ContainerName, cronjob.Script)
command := "sh"
if len(cronjob.Command) != 0 {
command = cronjob.Command
}
script = fmt.Sprintf("docker exec %s %s -c \"%s\"", cronjob.ContainerName, command, strings.ReplaceAll(cronjob.Script, "\"", "\\\""))
}
err = u.handleShell(cronjob.Type, cronjob.Name, script, record.Records)
u.removeExpiredLog(*cronjob)
@@ -126,7 +130,7 @@ func (u *CronjobService) handleNtpSync() error {
return nil
}
func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
func handleTar(sourceDir, targetDir, name, exclusionRules string, secret string) error {
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
return err
@@ -154,8 +158,16 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
path = sourceDir
}
commands := fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s %s", targetDir+"/"+name, excludeRules, path)
global.LOG.Debug(commands)
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", " -"+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)
global.LOG.Debug(commands)
}
stdout, err := cmd.ExecWithTimeOut(commands, 24*time.Hour)
if err != nil {
if len(stdout) != 0 {
@@ -166,15 +178,22 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
return nil
}
func handleUnTar(sourceFile, targetDir string) error {
func handleUnTar(sourceFile, targetDir string, secret string) error {
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
return err
}
}
commands := ""
if len(secret) != 0 {
extraCmd := "openssl enc -d -aes-256-cbc -k " + secret + " -in " + sourceFile + " | "
commands = fmt.Sprintf("%s tar -zxvf - -C %s", extraCmd, targetDir+" > /dev/null 2>&1")
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
} else {
commands = fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir)
global.LOG.Debug(commands)
}
commands := fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir)
global.LOG.Debug(commands)
stdout, err := cmd.ExecWithTimeOut(commands, 24*time.Hour)
if err != nil {
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
+26 -2
View File
@@ -11,6 +11,8 @@ import (
"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"
"github.com/1Panel-dev/1Panel/backend/utils/xpack"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host"
@@ -154,6 +156,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
currentInfo.SwapMemoryUsedPercent = swapInfo.UsedPercent
currentInfo.DiskData = loadDiskInfo()
currentInfo.GPUData = loadGPUInfo()
if ioOption == "all" {
diskInfo, _ := disk.IOCounters()
@@ -219,10 +222,13 @@ func loadDiskInfo() []dto.DiskInfo {
if len(fields) < 7 {
continue
}
if fields[1] == "tmpfs" {
if strings.HasPrefix(fields[6], "/snap") || len(strings.Split(fields[6], "/")) > 10 {
continue
}
if strings.Contains(fields[2], "M") || strings.Contains(fields[2], "K") {
if strings.TrimSpace(fields[1]) == "tmpfs" {
continue
}
if strings.Contains(fields[2], "K") {
continue
}
if strings.Contains(fields[6], "docker") {
@@ -289,3 +295,21 @@ func loadDiskInfo() []dto.DiskInfo {
})
return datas
}
func loadGPUInfo() []dto.GPUInfo {
list := xpack.LoadGpuInfo()
if len(list) == 0 {
return nil
}
var data []dto.GPUInfo
for _, gpu := range list {
var dataItem dto.GPUInfo
if err := copier.Copy(&dataItem, &gpu); err != nil {
continue
}
dataItem.PowerUsage = dataItem.PowerDraw + " / " + dataItem.MaxPowerLimit
dataItem.MemoryUsage = dataItem.MemUsed + " / " + dataItem.MemTotal
data = append(data, dataItem)
}
return data
}
+32 -4
View File
@@ -7,7 +7,8 @@ import (
"path"
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
client2 "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
pg_client "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
redis_client "github.com/1Panel-dev/1Panel/backend/utils/redis"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
@@ -42,6 +43,7 @@ func (u *DatabaseService) SearchWithPage(search dto.DatabaseSearch) (int64, inte
total, dbs, err := databaseRepo.Page(search.Page, search.PageSize,
databaseRepo.WithTypeList(search.Type),
commonRepo.WithLikeName(search.Info),
commonRepo.WithOrderRuleBy(search.OrderBy, search.Order),
databaseRepo.WithoutByFrom("local"),
)
var datas []dto.DatabaseInfo
@@ -69,6 +71,9 @@ func (u *DatabaseService) Get(name string) (dto.DatabaseInfo, error) {
func (u *DatabaseService) List(dbType string) ([]dto.DatabaseOption, error) {
dbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))
if err != nil {
return nil, err
}
var datas []dto.DatabaseOption
for _, db := range dbs {
var item dto.DatabaseOption
@@ -113,7 +118,7 @@ func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
switch req.Type {
case constant.AppPostgresql:
_, err := postgresql.NewPostgresqlClient(client2.DBInfo{
_, err := postgresql.NewPostgresqlClient(pg_client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
@@ -122,6 +127,13 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
Timeout: 6,
})
return err == nil
case constant.AppRedis:
_, err := redis_client.NewRedisClient(redis_client.DBInfo{
Address: req.Address,
Port: req.Port,
Password: req.Password,
})
return err == nil
case "mysql", "mariadb":
_, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
@@ -153,7 +165,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
}
switch req.Type {
case constant.AppPostgresql:
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
if _, err := postgresql.NewPostgresqlClient(pg_client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
@@ -163,6 +175,14 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
}); err != nil {
return err
}
case constant.AppRedis:
if _, err := redis_client.NewRedisClient(redis_client.DBInfo{
Address: req.Address,
Port: req.Port,
Password: req.Password,
}); err != nil {
return err
}
case "mysql", "mariadb":
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
@@ -249,7 +269,7 @@ func (u *DatabaseService) Delete(req dto.DatabaseDelete) error {
func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
switch req.Type {
case constant.AppPostgresql:
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
if _, err := postgresql.NewPostgresqlClient(pg_client.DBInfo{
From: "remote",
Address: req.Address,
Port: req.Port,
@@ -259,6 +279,14 @@ func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
}); err != nil {
return err
}
case constant.AppRedis:
if _, err := redis_client.NewRedisClient(redis_client.DBInfo{
Address: req.Address,
Port: req.Port,
Password: req.Password,
}); err != nil {
return err
}
case "mysql", "mariadb":
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
+28 -11
View File
@@ -190,11 +190,13 @@ func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
if err != nil {
return err
}
deleteList := databases
for _, data := range datas {
hasOld := false
for _, oldData := range databases {
if strings.EqualFold(oldData.Name, data.Name) && strings.EqualFold(oldData.MysqlName, data.MysqlName) {
for i := 0; i < len(databases); i++ {
if strings.EqualFold(databases[i].Name, data.Name) && strings.EqualFold(databases[i].MysqlName, data.MysqlName) {
hasOld = true
deleteList = append(deleteList[:i], deleteList[i+1:]...)
break
}
}
@@ -208,6 +210,9 @@ func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
}
}
}
for _, delItem := range deleteList {
_ = mysqlRepo.Update(delItem.ID, map[string]interface{}{"is_delete": true})
}
return nil
}
@@ -235,7 +240,7 @@ func (u *MysqlService) DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error)
}
}
} else {
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID))
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID), appRepo.WithKey(req.Type))
for _, app := range apps {
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
if appInstall.ID != 0 {
@@ -358,6 +363,17 @@ func (u *MysqlService) ChangePassword(req dto.ChangeDBInfo) error {
if err := updateInstallInfoInDB(req.Type, req.Database, "password", req.Value); err != nil {
return err
}
if req.From == "local" {
remote, err := databaseRepo.Get(commonRepo.WithByName(req.Database))
if err != nil {
return err
}
pass, err := encrypt.StringEncrypt(req.Value)
if err != nil {
return fmt.Errorf("decrypt database password failed, err: %v", err)
}
_ = databaseRepo.Update(remote.ID, map[string]interface{}{"password": pass})
}
return nil
}
@@ -451,7 +467,7 @@ func (u *MysqlService) LoadRemoteAccess(req dto.OperationWithNameAndType) (bool,
if err != nil {
return false, err
}
hosts, err := executeSqlForRows(app.ContainerName, app.Password, "select host from mysql.user where user='root';")
hosts, err := executeSqlForRows(app.ContainerName, app.Key, app.Password, "select host from mysql.user where user='root';")
if err != nil {
return false, err
}
@@ -469,7 +485,7 @@ func (u *MysqlService) LoadVariables(req dto.OperationWithNameAndType) (*dto.Mys
if err != nil {
return nil, err
}
variableMap, err := executeSqlForMaps(app.ContainerName, app.Password, "show global variables;")
variableMap, err := executeSqlForMaps(app.ContainerName, app.Key, app.Password, "show global variables;")
if err != nil {
return nil, err
}
@@ -488,7 +504,7 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
return nil, err
}
statusMap, err := executeSqlForMaps(app.ContainerName, app.Password, "show global status;")
statusMap, err := executeSqlForMaps(app.ContainerName, app.Key, app.Password, "show global status;")
if err != nil {
return nil, err
}
@@ -512,7 +528,7 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
info.File = "OFF"
info.Position = "OFF"
rows, err := executeSqlForRows(app.ContainerName, app.Password, "show master status;")
rows, err := executeSqlForRows(app.ContainerName, app.Key, app.Password, "show master status;")
if err != nil {
return nil, err
}
@@ -527,8 +543,8 @@ func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlS
return &info, nil
}
func executeSqlForMaps(containerName, password, command string) (map[string]string, error) {
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
func executeSqlForMaps(containerName, dbType, password, command string) (map[string]string, error) {
cmd := exec.Command("docker", "exec", containerName, dbType, "-uroot", "-p"+password, "-e", command)
stdout, err := cmd.CombinedOutput()
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
@@ -546,8 +562,8 @@ func executeSqlForMaps(containerName, password, command string) (map[string]stri
return rowMap, nil
}
func executeSqlForRows(containerName, password, command string) ([]string, error) {
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
func executeSqlForRows(containerName, dbType, password, command string) ([]string, error) {
cmd := exec.Command("docker", "exec", containerName, dbType, "-uroot", "-p"+password, "-e", command)
stdout, err := cmd.CombinedOutput()
stdStr := strings.ReplaceAll(string(stdout), "mysql: [Warning] Using a password on the command line interface can be insecure.\n", "")
if err != nil || strings.HasPrefix(string(stdStr), "ERROR ") {
@@ -609,6 +625,7 @@ func LoadMysqlClientByFrom(database string) (mysql.MysqlClient, string, error) {
if err != nil {
return nil, "", err
}
dbInfo.Type = databaseItem.Type
dbInfo.From = databaseItem.From
dbInfo.Database = database
if dbInfo.From != "local" {
+19 -3
View File
@@ -216,11 +216,13 @@ func (u *PostgresqlService) LoadFromRemote(database string) error {
if err != nil {
return err
}
deleteList := databases
for _, data := range datas {
hasOld := false
for _, oldData := range databases {
if strings.EqualFold(oldData.Name, data.Name) && strings.EqualFold(oldData.PostgresqlName, data.PostgresqlName) {
for i := 0; i < len(databases); i++ {
if strings.EqualFold(databases[i].Name, data.Name) && strings.EqualFold(databases[i].PostgresqlName, data.PostgresqlName) {
hasOld = true
deleteList = append(deleteList[:i], deleteList[i+1:]...)
break
}
}
@@ -234,6 +236,9 @@ func (u *PostgresqlService) LoadFromRemote(database string) error {
}
}
}
for _, delItem := range deleteList {
_ = postgresqlRepo.Update(delItem.ID, map[string]interface{}{"is_delete": true})
}
return nil
}
@@ -261,7 +266,7 @@ func (u *PostgresqlService) DeleteCheck(req dto.PostgresqlDBDeleteCheck) ([]stri
}
}
} else {
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID))
apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(db.ID), appRepo.WithKey(req.Type))
for _, app := range apps {
appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId))
if appInstall.ID != 0 {
@@ -409,5 +414,16 @@ func (u *PostgresqlService) ChangePassword(req dto.ChangeDBInfo) error {
if err := updateInstallInfoInDB(req.Type, req.Database, "password", req.Value); err != nil {
return err
}
if req.From == "local" {
remote, err := databaseRepo.Get(commonRepo.WithByName(req.Database))
if err != nil {
return err
}
pass, err := encrypt.StringEncrypt(req.Value)
if err != nil {
return fmt.Errorf("decrypt database password failed, err: %v", err)
}
_ = databaseRepo.Update(remote.ID, map[string]interface{}{"password": pass})
}
return nil
}
+54 -56
View File
@@ -1,18 +1,20 @@
package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"github.com/docker/docker/api/types/container"
_ "github.com/go-sql-driver/mysql"
)
@@ -23,11 +25,12 @@ type IRedisService interface {
UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error
ChangePassword(info dto.ChangeRedisPass) error
LoadStatus() (*dto.RedisStatus, error)
LoadConf() (*dto.RedisConf, error)
LoadPersistenceConf() (*dto.RedisPersistence, error)
LoadStatus(req dto.OperationWithName) (*dto.RedisStatus, error)
LoadConf(req dto.OperationWithName) (*dto.RedisConf, error)
LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error)
SearchBackupListWithPage(req dto.PageInfo) (int64, interface{}, error)
CheckHasCli() bool
InstallCli() error
}
func NewIRedisService() IRedisService {
@@ -35,7 +38,7 @@ func NewIRedisService() IRedisService {
}
func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Database)
if err != nil {
return err
}
@@ -54,19 +57,54 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
return nil
}
func (u *RedisService) CheckHasCli() bool {
client, err := docker.NewDockerClient()
if err != nil {
return false
}
defer client.Close()
containerLists, err := client.ContainerList(context.Background(), container.ListOptions{})
if err != nil {
return false
}
for _, item := range containerLists {
if strings.ReplaceAll(item.Names[0], "/", "") == "1Panel-redis-cli-tools" {
return true
}
}
return false
}
func (u *RedisService) InstallCli() error {
item := dto.ContainerOperate{
Name: "1Panel-redis-cli-tools",
Image: "redis:7.2.4",
Network: "1panel-network",
}
return NewIContainerService().ContainerCreate(item)
}
func (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error {
if err := updateInstallInfoInDB("redis", "", "password", req.Value); err != nil {
if err := updateInstallInfoInDB("redis", req.Database, "password", req.Value); err != nil {
return err
}
if err := updateInstallInfoInDB("redis-commander", "", "password", req.Value); err != nil {
remote, err := databaseRepo.Get(commonRepo.WithByName(req.Database))
if err != nil {
return err
}
if remote.From == "local" {
pass, err := encrypt.StringEncrypt(req.Value)
if err != nil {
return fmt.Errorf("decrypt database password failed, err: %v", err)
}
_ = databaseRepo.Update(remote.ID, map[string]interface{}{"password": pass})
}
return nil
}
func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error {
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Database)
if err != nil {
return err
}
@@ -88,8 +126,8 @@ func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate)
return nil
}
func (u *RedisService) LoadStatus() (*dto.RedisStatus, error) {
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
func (u *RedisService) LoadStatus(req dto.OperationWithName) (*dto.RedisStatus, error) {
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
if err != nil {
return nil, err
}
@@ -116,8 +154,8 @@ func (u *RedisService) LoadStatus() (*dto.RedisStatus, error) {
return &info, nil
}
func (u *RedisService) LoadConf() (*dto.RedisConf, error) {
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
func (u *RedisService) LoadConf(req dto.OperationWithName) (*dto.RedisConf, error) {
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
if err != nil {
return nil, err
}
@@ -133,8 +171,8 @@ func (u *RedisService) LoadConf() (*dto.RedisConf, error) {
return &item, nil
}
func (u *RedisService) LoadPersistenceConf() (*dto.RedisPersistence, error) {
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
func (u *RedisService) LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error) {
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name)
if err != nil {
return nil, err
}
@@ -151,46 +189,6 @@ func (u *RedisService) LoadPersistenceConf() (*dto.RedisPersistence, error) {
return &item, nil
}
func (u *RedisService) SearchBackupListWithPage(req dto.PageInfo) (int64, interface{}, error) {
var (
list []dto.DatabaseFileRecords
backDatas []dto.DatabaseFileRecords
)
redisInfo, err := appInstallRepo.LoadBaseInfo("redis", "")
if err != nil {
return 0, nil, err
}
localDir, err := loadLocalDir()
if err != nil {
return 0, nil, err
}
backupDir := path.Join(localDir, fmt.Sprintf("database/redis/%s", redisInfo.Name))
_ = filepath.Walk(backupDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
if !info.IsDir() {
list = append(list, dto.DatabaseFileRecords{
CreatedAt: info.ModTime().Format("2006-01-02 15:04:05"),
Size: int(info.Size()),
FileDir: backupDir,
FileName: info.Name(),
})
}
return nil
})
total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total {
backDatas = make([]dto.DatabaseFileRecords, 0)
} else {
if end >= total {
end = total
}
backDatas = list[start:end]
}
return int64(total), backDatas, nil
}
func configGetStr(containerName, password, param string) (string, error) {
commands := append(redisExec(containerName, password), []string{"config", "get", param}...)
cmd := exec.Command("docker", commands...)
+117
View File
@@ -1,6 +1,7 @@
package service
import (
"context"
"fmt"
"os"
"path"
@@ -8,6 +9,10 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
@@ -130,6 +135,9 @@ func (u *DeviceService) Scan() dto.CleanData {
logTree := loadLogTree(fileOp)
SystemClean.SystemLogClean = append(SystemClean.SystemLogClean, logTree...)
containerTree := loadContainerTree()
SystemClean.ContainerClean = append(SystemClean.ContainerClean, containerTree...)
return SystemClean
}
@@ -259,6 +267,14 @@ func (u *DeviceService) Clean(req []dto.Clean) {
} else {
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByRecordFile(pathItem))
}
case "images":
dropImages()
case "containers":
dropContainers()
case "volumes":
dropVolumes()
case "build_cache":
dropBuildCache()
}
}
@@ -496,6 +512,50 @@ func loadLogTree(fileOp fileUtils.FileOp) []dto.CleanTree {
return treeData
}
func loadContainerTree() []dto.CleanTree {
var treeData []dto.CleanTree
client, err := docker.NewDockerClient()
if err != nil {
return treeData
}
diskUsage, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{})
if err != nil {
return treeData
}
imageSize := uint64(0)
for _, file := range diskUsage.Images {
if file.Containers == 0 {
imageSize += uint64(file.Size)
}
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_images", Size: imageSize, Children: nil, Type: "images", IsRecommend: true})
containerSize := uint64(0)
for _, file := range diskUsage.Containers {
if file.State != "running" {
containerSize += uint64(file.SizeRw)
}
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_containers", Size: containerSize, Children: nil, Type: "containers", IsRecommend: true})
volumeSize := uint64(0)
for _, file := range diskUsage.Volumes {
if file.UsageData.RefCount <= 0 {
volumeSize += uint64(file.UsageData.Size)
}
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "container_volumes", Size: volumeSize, Children: nil, Type: "volumes", IsRecommend: true})
var buildCacheTotalSize int64
for _, cache := range diskUsage.BuildCache {
if cache.Type == "source.local" {
buildCacheTotalSize += cache.Size
}
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "build_cache", Size: uint64(buildCacheTotalSize), Type: "build_cache", IsRecommend: true})
return treeData
}
func loadTreeWithDir(isCheck bool, treeType, pathItem string, fileOp fileUtils.FileOp) []dto.CleanTree {
var lists []dto.CleanTree
files, err := os.ReadDir(pathItem)
@@ -586,6 +646,63 @@ func dropFileOrDir(itemPath string) {
}
}
func dropBuildCache() {
client, err := docker.NewDockerClient()
if err != nil {
global.LOG.Errorf("do not get docker client")
}
opts := types.BuildCachePruneOptions{}
opts.All = true
_, err = client.BuildCachePrune(context.Background(), opts)
if err != nil {
global.LOG.Errorf("drop build cache failed, err %v", err)
}
}
func dropImages() {
client, err := docker.NewDockerClient()
if err != nil {
global.LOG.Errorf("do not get docker client")
}
pruneFilters := filters.NewArgs()
pruneFilters.Add("dangling", "false")
_, err = client.ImagesPrune(context.Background(), pruneFilters)
if err != nil {
global.LOG.Errorf("drop images failed, err %v", err)
}
}
func dropContainers() {
client, err := docker.NewDockerClient()
if err != nil {
global.LOG.Errorf("do not get docker client")
}
pruneFilters := filters.NewArgs()
_, err = client.ContainersPrune(context.Background(), pruneFilters)
if err != nil {
global.LOG.Errorf("drop containers failed, err %v", err)
}
}
func dropVolumes() {
client, err := docker.NewDockerClient()
if err != nil {
global.LOG.Errorf("do not get docker client")
}
pruneFilters := filters.NewArgs()
versions, err := client.ServerVersion(context.Background())
if err != nil {
global.LOG.Errorf("do not get docker api versions")
}
if common.ComparePanelVersion(versions.APIVersion, "1.42") {
pruneFilters.Add("all", "true")
}
_, err = client.VolumesPrune(context.Background(), pruneFilters)
if err != nil {
global.LOG.Errorf("drop volumes failed, err %v", err)
}
}
func dropFileOrDirWithLog(itemPath string, log *string, size *int64, count *int) {
itemSize := int64(0)
itemCount := 0
+2 -2
View File
@@ -4,7 +4,6 @@ import (
"bufio"
"context"
"encoding/json"
"fmt"
"os"
"path"
"strings"
@@ -55,6 +54,7 @@ func (u *DockerService) LoadDockerStatus() string {
if err != nil {
return constant.Stopped
}
defer client.Close()
if _, err := client.Ping(context.Background()); err != nil {
return constant.Stopped
}
@@ -72,6 +72,7 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
if err != nil {
data.Status = constant.Stopped
} else {
defer client.Close()
if _, err := client.Ping(ctx); err != nil {
data.Status = constant.Stopped
}
@@ -102,7 +103,6 @@ func (u *DockerService) LoadDockerConf() *dto.DaemonJsonConf {
return &data
}
if err := json.Unmarshal(arr, &conf); err != nil {
fmt.Println(err)
return &data
}
if _, ok := daemonMap["iptables"]; !ok {
+1
View File
@@ -24,6 +24,7 @@ var (
hostRepo = repo.NewIHostRepo()
groupRepo = repo.NewIGroupRepo()
commandRepo = repo.NewICommandRepo()
ftpRepo = repo.NewIFtpRepo()
settingRepo = repo.NewISettingRepo()
backupRepo = repo.NewIBackupRepo()
+70 -14
View File
@@ -2,17 +2,23 @@ package service
import (
"fmt"
"io"
"io/fs"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"time"
"unicode/utf8"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"golang.org/x/net/html/charset"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
@@ -116,22 +122,32 @@ func (f *FileService) GetFileTree(op request.FileOption) ([]response.FileTree, e
}
func (f *FileService) Create(op request.FileCreate) error {
if files.IsInvalidChar(op.Path) {
return buserr.New("ErrInvalidChar")
}
fo := files.NewFileOp()
if fo.Stat(op.Path) {
return buserr.New(constant.ErrFileIsExit)
}
if op.IsDir {
return fo.CreateDir(op.Path, fs.FileMode(op.Mode))
} else {
if op.IsLink {
if !fo.Stat(op.LinkPath) {
return buserr.New(constant.ErrLinkPathNotFound)
}
return fo.LinkFile(op.LinkPath, op.Path, op.IsSymlink)
mode := op.Mode
if mode == 0 {
fileInfo, err := os.Stat(filepath.Dir(op.Path))
if err == nil {
mode = int64(fileInfo.Mode().Perm())
} else {
return fo.CreateFile(op.Path)
mode = 0755
}
}
if op.IsDir {
return fo.CreateDirWithMode(op.Path, fs.FileMode(mode))
}
if op.IsLink {
if !fo.Stat(op.LinkPath) {
return buserr.New(constant.ErrLinkPathNotFound)
}
return fo.LinkFile(op.LinkPath, op.Path, op.IsSymlink)
}
return fo.CreateFileWithMode(op.Path, fs.FileMode(mode))
}
func (f *FileService) Delete(op request.FileDelete) error {
@@ -203,12 +219,12 @@ func (f *FileService) Compress(c request.FileCompress) error {
if !c.Replace && fo.Stat(filepath.Join(c.Dst, c.Name)) {
return buserr.New(constant.ErrFileIsExit)
}
return fo.Compress(c.Files, c.Dst, c.Name, files.CompressType(c.Type))
return fo.Compress(c.Files, c.Dst, c.Name, files.CompressType(c.Type), c.Secret)
}
func (f *FileService) DeCompress(c request.FileDeCompress) error {
fo := files.NewFileOp()
return fo.Decompress(c.Path, c.Dst, files.CompressType(c.Type))
return fo.Decompress(c.Path, c.Dst, files.CompressType(c.Type), c.Secret)
}
func (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo, error) {
@@ -219,6 +235,23 @@ func (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo,
if err != nil {
return response.FileInfo{}, err
}
content := []byte(info.Content)
if len(content) > 1024 {
content = content[:1024]
}
if !utf8.Valid(content) {
_, decodeName, _ := charset.DetermineEncoding(content, "")
if decodeName == "windows-1252" {
reader := strings.NewReader(info.Content)
item := transform.NewReader(reader, simplifiedchinese.GBK.NewDecoder())
contents, err := io.ReadAll(item)
if err != nil {
return response.FileInfo{}, err
}
info.Content = string(contents)
}
}
return response.FileInfo{FileInfo: *info}, nil
}
@@ -236,6 +269,9 @@ func (f *FileService) SaveContent(edit request.FileEdit) error {
}
func (f *FileService) ChangeName(req request.FileRename) error {
if files.IsInvalidChar(req.NewName) {
return buserr.New("ErrInvalidChar")
}
fo := files.NewFileOp()
return fo.Rename(req.OldName, req.NewName)
}
@@ -290,7 +326,7 @@ func (f *FileService) FileDownload(d request.FileDownload) (string, error) {
return "", err
}
fo := files.NewFileOp()
if err := fo.Compress(d.Paths, tempPath, d.Name, files.CompressType(d.Type)); err != nil {
if err := fo.Compress(d.Paths, tempPath, d.Name, files.CompressType(d.Type), ""); err != nil {
return "", err
}
filePath = filepath.Join(tempPath, d.Name)
@@ -299,12 +335,32 @@ func (f *FileService) FileDownload(d request.FileDownload) (string, error) {
}
func (f *FileService) DirSize(req request.DirSizeReq) (response.DirSizeRes, error) {
var (
res response.DirSizeRes
)
if req.Path == "/proc" {
return res, nil
}
cmd := exec.Command("du", "-s", req.Path)
output, err := cmd.Output()
if err == nil {
fields := strings.Fields(string(output))
if len(fields) == 2 {
var cmdSize int64
_, err = fmt.Sscanf(fields[0], "%d", &cmdSize)
if err == nil {
res.Size = float64(cmdSize * 1024)
return res, nil
}
}
}
fo := files.NewFileOp()
size, err := fo.GetDirSize(req.Path)
if err != nil {
return response.DirSizeRes{}, err
return res, err
}
return response.DirSizeRes{Size: size}, nil
res.Size = size
return res, nil
}
func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error) {
+245
View File
@@ -0,0 +1,245 @@
package service
import (
"os"
"sort"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"github.com/1Panel-dev/1Panel/backend/utils/toolbox"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
type FtpService struct{}
type IFtpService interface {
LoadBaseInfo() (dto.FtpBaseInfo, error)
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
Operate(operation string) error
Create(req dto.FtpCreate) (uint, error)
Delete(req dto.BatchDeleteReq) error
Update(req dto.FtpUpdate) error
Sync() error
LoadLog(req dto.FtpLogSearch) (int64, interface{}, error)
}
func NewIFtpService() IFtpService {
return &FtpService{}
}
func (f *FtpService) LoadBaseInfo() (dto.FtpBaseInfo, error) {
var baseInfo dto.FtpBaseInfo
client, err := toolbox.NewFtpClient()
if err != nil {
return baseInfo, err
}
baseInfo.IsActive, baseInfo.IsExist = client.Status()
return baseInfo, nil
}
func (f *FtpService) LoadLog(req dto.FtpLogSearch) (int64, interface{}, error) {
client, err := toolbox.NewFtpClient()
if err != nil {
return 0, nil, err
}
logItem, err := client.LoadLogs(req.User, req.Operation)
if err != nil {
return 0, nil, err
}
sort.Slice(logItem, func(i, j int) bool {
return logItem[i].Time > logItem[j].Time
})
var logs []toolbox.FtpLog
total, start, end := len(logItem), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total {
logs = make([]toolbox.FtpLog, 0)
} else {
if end >= total {
end = total
}
logs = logItem[start:end]
}
return int64(total), logs, nil
}
func (u *FtpService) Operate(operation string) error {
client, err := toolbox.NewFtpClient()
if err != nil {
return err
}
return client.Operate(operation)
}
func (f *FtpService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) {
total, lists, err := ftpRepo.Page(req.Page, req.PageSize, ftpRepo.WithByUser(req.Info), commonRepo.WithOrderBy("created_at desc"))
if err != nil {
return 0, nil, err
}
var users []dto.FtpInfo
for _, user := range lists {
var item dto.FtpInfo
if err := copier.Copy(&item, &user); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
item.Password, _ = encrypt.StringDecrypt(item.Password)
users = append(users, item)
}
return total, users, err
}
func (f *FtpService) Sync() error {
client, err := toolbox.NewFtpClient()
if err != nil {
return err
}
lists, err := client.LoadList()
if err != nil {
return nil
}
listsInDB, err := ftpRepo.GetList()
if err != nil {
return err
}
sameData := make(map[string]struct{})
for _, item := range lists {
for _, itemInDB := range listsInDB {
if item.User == itemInDB.User {
sameData[item.User] = struct{}{}
if item.Path != itemInDB.Path || item.Status != itemInDB.Status {
_ = ftpRepo.Update(itemInDB.ID, map[string]interface{}{"path": item.Path, "status": item.Status})
}
break
}
}
}
for _, item := range lists {
if _, ok := sameData[item.User]; !ok {
_ = ftpRepo.Create(&model.Ftp{User: item.User, Path: item.Path, Status: item.Status})
}
}
for _, item := range listsInDB {
if _, ok := sameData[item.User]; !ok {
_ = ftpRepo.Update(item.ID, map[string]interface{}{"status": "deleted"})
}
}
return nil
}
func (f *FtpService) Create(req dto.FtpCreate) (uint, error) {
if _, err := os.Stat(req.Path); err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(req.Path, os.ModePerm); err != nil {
return 0, err
}
} else {
return 0, err
}
}
pass, err := encrypt.StringEncrypt(req.Password)
if err != nil {
return 0, err
}
userInDB, _ := ftpRepo.Get(hostRepo.WithByUser(req.User))
if userInDB.ID != 0 {
return 0, constant.ErrRecordExist
}
client, err := toolbox.NewFtpClient()
if err != nil {
return 0, err
}
if err := client.UserAdd(req.User, req.Password, req.Path); err != nil {
return 0, err
}
var ftp model.Ftp
if err := copier.Copy(&ftp, &req); err != nil {
return 0, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
ftp.Status = constant.StatusEnable
ftp.Password = pass
if err := ftpRepo.Create(&ftp); err != nil {
return 0, err
}
return ftp.ID, nil
}
func (f *FtpService) Delete(req dto.BatchDeleteReq) error {
client, err := toolbox.NewFtpClient()
if err != nil {
return err
}
for _, id := range req.Ids {
ftpItem, err := ftpRepo.Get(commonRepo.WithByID(id))
if err != nil {
return err
}
_ = client.UserDel(ftpItem.User)
_ = ftpRepo.Delete(commonRepo.WithByID(id))
}
return nil
}
func (f *FtpService) Update(req dto.FtpUpdate) error {
if _, err := os.Stat(req.Path); err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(req.Path, os.ModePerm); err != nil {
return err
}
} else {
return err
}
}
pass, err := encrypt.StringEncrypt(req.Password)
if err != nil {
return err
}
ftpItem, _ := ftpRepo.Get(commonRepo.WithByID(req.ID))
if ftpItem.ID == 0 {
return constant.ErrRecordNotFound
}
passItem, err := encrypt.StringDecrypt(ftpItem.Password)
if err != nil {
return err
}
client, err := toolbox.NewFtpClient()
if err != nil {
return err
}
needReload := false
updates := make(map[string]interface{})
if req.Password != passItem {
if err := client.SetPasswd(ftpItem.User, req.Password); err != nil {
return err
}
updates["password"] = pass
needReload = true
}
if req.Status != ftpItem.Status {
if err := client.SetStatus(ftpItem.User, req.Status); err != nil {
return err
}
updates["status"] = req.Status
needReload = true
}
if req.Path != ftpItem.Path {
if err := client.SetPath(ftpItem.User, req.Path); err != nil {
return err
}
updates["path"] = req.Path
needReload = true
}
if req.Description != ftpItem.Description {
updates["description"] = req.Description
}
if needReload {
_ = client.Reload()
}
if len(updates) != 0 {
return ftpRepo.Update(ftpItem.ID, updates)
}
return nil
}
+2 -2
View File
@@ -307,7 +307,7 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
if err != nil {
return err
}
_, _ = section.NewKey("command", req.Command)
_, _ = section.NewKey("command", strings.TrimSpace(req.Command))
_, _ = section.NewKey("directory", req.Dir)
_, _ = section.NewKey("autorestart", "true")
_, _ = section.NewKey("startsecs", "3")
@@ -338,7 +338,7 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
}
commandKey := section.Key("command")
commandKey.SetValue(req.Command)
commandKey.SetValue(strings.TrimSpace(req.Command))
directoryKey := section.Key("directory")
directoryKey.SetValue(req.Dir)
userKey := section.Key("user")
+32 -20
View File
@@ -13,6 +13,9 @@ import (
"strings"
"time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -43,7 +46,7 @@ func NewIImageService() IImageService {
}
func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error) {
var (
list []types.ImageSummary
list []image.Summary
records []dto.ImageInfo
backDatas []dto.ImageInfo
)
@@ -51,11 +54,12 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
if err != nil {
return 0, nil, err
}
list, err = client.ImageList(context.Background(), types.ImageListOptions{})
defer client.Close()
list, err = client.ImageList(context.Background(), image.ListOptions{})
if err != nil {
return 0, nil, err
}
containers, _ := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
containers, _ := client.ContainerList(context.Background(), container.ListOptions{All: true})
if len(req.Info) != 0 {
length, count := len(list), 0
for count < length {
@@ -104,11 +108,12 @@ func (u *ImageService) ListAll() ([]dto.ImageInfo, error) {
if err != nil {
return nil, err
}
list, err := client.ImageList(context.Background(), types.ImageListOptions{})
defer client.Close()
list, err := client.ImageList(context.Background(), image.ListOptions{})
if err != nil {
return nil, err
}
containers, _ := client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
containers, _ := client.ContainerList(context.Background(), container.ListOptions{All: true})
for _, image := range list {
size := formatFileSize(image.Size)
records = append(records, dto.ImageInfo{
@@ -124,14 +129,15 @@ func (u *ImageService) ListAll() ([]dto.ImageInfo, error) {
func (u *ImageService) List() ([]dto.Options, error) {
var (
list []types.ImageSummary
list []image.Summary
backDatas []dto.Options
)
client, err := docker.NewDockerClient()
if err != nil {
return nil, err
}
list, err = client.ImageList(context.Background(), types.ImageListOptions{})
defer client.Close()
list, err = client.ImageList(context.Background(), image.ListOptions{})
if err != nil {
return nil, err
}
@@ -150,6 +156,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
if err != nil {
return "", err
}
defer client.Close()
fileName := "Dockerfile"
if req.From == "edit" {
dir := fmt.Sprintf("%s/docker/build/%s", constant.DataDir, strings.ReplaceAll(req.Name, ":", "_"))
@@ -233,6 +240,7 @@ func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
if err != nil {
return "", err
}
defer client.Close()
dockerLogDir := path.Join(global.CONF.System.TmpDir, "docker_logs")
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
@@ -263,7 +271,7 @@ func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
if err != nil {
return "", err
}
options := types.ImagePullOptions{}
options := image.PullOptions{}
if repo.Auth {
authConfig := registry.AuthConfig{
Username: repo.Username,
@@ -303,10 +311,12 @@ func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
if err != nil {
return err
}
defer client.Close()
res, err := client.ImageLoad(context.TODO(), file, true)
if err != nil {
return err
}
defer res.Body.Close()
content, err := io.ReadAll(res.Body)
if err != nil {
return err
@@ -322,6 +332,7 @@ func (u *ImageService) ImageSave(req dto.ImageSave) error {
if err != nil {
return err
}
defer client.Close()
out, err := client.ImageSave(context.TODO(), []string{req.TagName})
if err != nil {
@@ -344,6 +355,7 @@ func (u *ImageService) ImageTag(req dto.ImageTag) error {
if err != nil {
return err
}
defer client.Close()
if err := client.ImageTag(context.TODO(), req.SourceID, req.TargetName); err != nil {
return err
@@ -356,23 +368,22 @@ func (u *ImageService) ImagePush(req dto.ImagePush) (string, error) {
if err != nil {
return "", err
}
defer client.Close()
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
if err != nil {
return "", err
}
options := types.ImagePushOptions{}
if repo.Auth {
authConfig := registry.AuthConfig{
Username: repo.Username,
Password: repo.Password,
}
encodedJSON, err := json.Marshal(authConfig)
if err != nil {
return "", err
}
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
options.RegistryAuth = authStr
options := image.PushOptions{All: true}
authConfig := registry.AuthConfig{
Username: repo.Username,
Password: repo.Password,
}
encodedJSON, err := json.Marshal(authConfig)
if err != nil {
return "", err
}
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
options.RegistryAuth = authStr
newName := fmt.Sprintf("%s/%s", repo.DownloadUrl, req.Name)
if newName != req.TagName {
if err := client.ImageTag(context.TODO(), req.TagName, newName); err != nil {
@@ -414,6 +425,7 @@ func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
if err != nil {
return err
}
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 strings.Contains(err.Error(), "image is being used") || strings.Contains(err.Error(), "is using") {
+3 -1
View File
@@ -2,7 +2,6 @@ package service
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
"io"
"net/http"
"os"
@@ -10,6 +9,8 @@ import (
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
@@ -76,6 +77,7 @@ func (n NginxService) GetStatus() (response.NginxStatus, error) {
if err != nil {
return response.NginxStatus{}, err
}
defer res.Body.Close()
content, err := io.ReadAll(res.Body)
if err != nil {
return response.NginxStatus{}, err
+12 -14
View File
@@ -153,22 +153,20 @@ func (r RecycleBinService) Clear() error {
}
func getClashDir(realPath string) (string, error) {
trimmedPath := strings.Trim(realPath, "/")
parts := strings.Split(trimmedPath, "/")
dir := ""
if len(parts) > 0 {
dir = parts[0]
partitions, err := disk.Partitions(false)
if err != nil {
return "", err
partitions, err := disk.Partitions(false)
if err != nil {
return "", err
}
for _, p := range partitions {
if p.Mountpoint == "/" {
continue
}
for _, p := range partitions {
if p.Mountpoint == dir {
if err = createClashDir(path.Join(p.Mountpoint, ".1panel_clash")); err != nil {
return "", err
}
return dir, nil
if strings.HasPrefix(realPath, p.Mountpoint) {
clashDir := path.Join(p.Mountpoint, ".1panel_clash")
if err = createClashDir(path.Join(p.Mountpoint, ".1panel_clash")); err != nil {
return "", err
}
return clashDir, nil
}
}
return constant.RecycleBinDir, createClashDir(constant.RecycleBinDir)
+37 -7
View File
@@ -4,6 +4,14 @@ import (
"context"
"encoding/json"
"fmt"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
@@ -19,13 +27,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/pkg/errors"
"github.com/subosito/gotenv"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
)
type RuntimeService struct {
@@ -42,6 +43,8 @@ type IRuntimeService interface {
GetNodeModules(req request.NodeModuleReq) ([]response.NodeModule, error)
OperateNodeModules(req request.NodeModuleOperateReq) error
SyncForRestart() error
SyncRuntimeStatus() error
DeleteCheck(installID uint) ([]dto.AppResource, error)
}
func NewRuntimeService() IRuntimeService {
@@ -175,6 +178,18 @@ func (r *RuntimeService) Page(req request.RuntimeSearch) (int64, []response.Runt
return total, res, nil
}
func (r *RuntimeService) DeleteCheck(runTimeId uint) ([]dto.AppResource, error) {
var res []dto.AppResource
websites, _ := websiteRepo.GetBy(websiteRepo.WithRuntimeID(runTimeId))
for _, website := range websites {
res = append(res, dto.AppResource{
Type: "website",
Name: website.PrimaryDomain,
})
}
return res, nil
}
func (r *RuntimeService) Delete(runtimeDelete request.RuntimeDelete) error {
runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(runtimeDelete.ID))
if err != nil {
@@ -192,6 +207,7 @@ func (r *RuntimeService) Delete(runtimeDelete request.RuntimeDelete) error {
if err != nil {
return err
}
defer client.Close()
imageID, err := client.GetImageIDByName(runtime.Image)
if err != nil {
return err
@@ -419,6 +435,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
if err != nil {
return err
}
defer client.Close()
imageID, err := client.GetImageIDByName(oldImage)
if err != nil {
return err
@@ -586,3 +603,16 @@ func (r *RuntimeService) SyncForRestart() error {
}
return nil
}
func (r *RuntimeService) SyncRuntimeStatus() error {
runtimes, err := runtimeRepo.List()
if err != nil {
return err
}
for _, runtime := range runtimes {
if runtime.Type == constant.RuntimeNode {
_ = SyncRuntimeContainerStatus(&runtime)
}
}
return nil
}
+32 -36
View File
@@ -3,16 +3,6 @@ package service
import (
"bytes"
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"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/docker"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/pkg/errors"
"github.com/subosito/gotenv"
"gopkg.in/yaml.v3"
"io"
"net/http"
"os"
@@ -21,6 +11,18 @@ import (
"path/filepath"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"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/docker"
"github.com/1Panel-dev/1Panel/backend/utils/files"
httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http"
"github.com/pkg/errors"
"github.com/subosito/gotenv"
"gopkg.in/yaml.v3"
)
func handleNode(create request.RuntimeCreate, runtime *model.Runtime, fileOp files.FileOp, appVersionDir string) (err error) {
@@ -53,7 +55,10 @@ func handleNode(create request.RuntimeCreate, runtime *model.Runtime, fileOp fil
}
go func() {
_, _ = http.Get(nodeDetail.DownloadCallBackUrl)
if _, _, err := httpUtil.HandleGet(nodeDetail.DownloadCallBackUrl, http.MethodGet, constant.TimeOut5s); err != nil {
global.LOG.Errorf("http request failed(handleNode), err: %v", err)
return
}
}()
go startRuntime(runtime)
@@ -144,6 +149,7 @@ func runComposeCmdWithLog(operate string, composePath string, logPath string) er
global.LOG.Errorf("Failed to open log file: %v", err)
return err
}
defer logFile.Close()
multiWriterStdout := io.MultiWriter(os.Stdout, logFile)
cmd.Stdout = multiWriterStdout
var stderrBuf bytes.Buffer
@@ -172,6 +178,7 @@ func SyncRuntimeContainerStatus(runtime *model.Runtime) error {
if err != nil {
return err
}
defer cli.Close()
containers, err := cli.ListContainersByName(containerNames)
if err != nil {
return err
@@ -181,33 +188,20 @@ func SyncRuntimeContainerStatus(runtime *model.Runtime) error {
}
container := containers[0]
interval := 10 * time.Second
retries := 60
for i := 0; i < retries; i++ {
resp, err := cli.InspectContainer(container.ID)
if err != nil {
time.Sleep(interval)
continue
switch container.State {
case "exited":
runtime.Status = constant.RuntimeError
case "running":
runtime.Status = constant.RuntimeRunning
case "paused":
runtime.Status = constant.RuntimeStopped
default:
if runtime.Status != constant.RuntimeBuildIng {
runtime.Status = constant.RuntimeStopped
}
if resp.State.Health != nil {
status := strings.ToLower(resp.State.Health.Status)
switch status {
case "starting":
runtime.Status = constant.RuntimeStarting
_ = runtimeRepo.Save(runtime)
case "healthy":
runtime.Status = constant.RuntimeRunning
_ = runtimeRepo.Save(runtime)
return nil
case "unhealthy":
runtime.Status = constant.RuntimeUnhealthy
_ = runtimeRepo.Save(runtime)
return nil
}
}
time.Sleep(interval)
}
return nil
return runtimeRepo.Save(runtime)
}
func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
@@ -241,6 +235,7 @@ func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
if oldImageID != "" {
client, err := docker.NewClient()
if err == nil {
defer client.Close()
newImageID, err := client.GetImageIDByName(runtime.Image)
if err == nil && newImageID != oldImageID {
global.LOG.Infof("delete imageID [%s] ", oldImageID)
@@ -403,6 +398,7 @@ func checkContainerName(name string) error {
if err != nil {
return err
}
defer dockerCli.Close()
names, err := dockerCli.ListContainersByName([]string{name})
if err != nil {
return err
+53 -11
View File
@@ -33,6 +33,7 @@ type ISettingService interface {
GetSettingInfo() (*dto.SettingInfo, error)
LoadInterfaceAddr() ([]string, error)
Update(key, value string) error
UpdateProxy(req dto.ProxyUpdate) error
UpdatePassword(c *gin.Context, old, new string) error
UpdatePort(port uint) error
UpdateBindInfo(req dto.BindInfo) error
@@ -62,6 +63,12 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
if err := json.Unmarshal(arr, &info); err != nil {
return nil, err
}
if info.ProxyPasswdKeep != constant.StatusEnable {
info.ProxyPasswd = ""
} else {
info.ProxyPasswd, _ = encrypt.StringDecrypt(info.ProxyPasswd)
}
info.LocalTime = time.Now().Format("2006-01-02 15:04:05 MST -0700")
return &info, err
}
@@ -162,6 +169,29 @@ func (u *SettingService) UpdateBindInfo(req dto.BindInfo) error {
return nil
}
func (u *SettingService) UpdateProxy(req dto.ProxyUpdate) error {
if err := settingRepo.Update("ProxyUrl", req.ProxyUrl); err != nil {
return err
}
if err := settingRepo.Update("ProxyType", req.ProxyType); err != nil {
return err
}
if err := settingRepo.Update("ProxyPort", req.ProxyPort); err != nil {
return err
}
if err := settingRepo.Update("ProxyUser", req.ProxyUser); err != nil {
return err
}
pass, _ := encrypt.StringEncrypt(req.ProxyPasswd)
if err := settingRepo.Update("ProxyPasswd", pass); err != nil {
return err
}
if err := settingRepo.Update("ProxyPasswdKeep", req.ProxyPasswdKeep); err != nil {
return err
}
return nil
}
func (u *SettingService) UpdatePort(port uint) error {
if common.ScanPort(int(port)) {
return buserr.WithDetail(constant.ErrPortInUsed, port, nil)
@@ -317,12 +347,18 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
if err != nil {
return nil, err
}
data, err := loadInfoFromCert()
if err != nil {
return nil, err
}
var data dto.SSLInfo
switch sslType.Value {
case "self":
data, err = loadInfoFromCert()
if err != nil {
return nil, err
}
case "import":
data, err = loadInfoFromCert()
if err != nil {
return nil, err
}
if _, err := os.Stat(path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt")); err != nil {
return nil, fmt.Errorf("load server.crt file failed, err: %v", err)
}
@@ -340,9 +376,15 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) {
return nil, err
}
id, _ := strconv.Atoi(sslID.Value)
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(uint(id)))
if err != nil {
return nil, err
}
data.Domain = ssl.Domains
data.SSLID = uint(id)
data.Timeout = ssl.ExpireDate.Format("2006-01-02 15:04:05")
}
return data, nil
return &data, nil
}
func (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string) error {
@@ -384,23 +426,23 @@ func (u *SettingService) UpdatePassword(c *gin.Context, old, new string) error {
return nil
}
func loadInfoFromCert() (*dto.SSLInfo, error) {
func loadInfoFromCert() (dto.SSLInfo, error) {
var info dto.SSLInfo
certFile := path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt")
if _, err := os.Stat(certFile); err != nil {
return &info, err
return info, err
}
certData, err := os.ReadFile(certFile)
if err != nil {
return &info, err
return info, err
}
certBlock, _ := pem.Decode(certData)
if certBlock == nil {
return &info, err
return info, err
}
certObj, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return &info, err
return info, err
}
var domains []string
if len(certObj.IPAddresses) != 0 {
@@ -411,7 +453,7 @@ func loadInfoFromCert() (*dto.SSLInfo, error) {
if len(certObj.DNSNames) != 0 {
domains = append(domains, certObj.DNSNames...)
}
return &dto.SSLInfo{
return dto.SSLInfo{
Domain: strings.Join(domains, ","),
Timeout: certObj.NotAfter.Format("2006-01-02 15:04:05"),
RootPath: path.Join(global.CONF.System.BaseDir, "1panel/secret/server.crt"),
+32 -35
View File
@@ -32,14 +32,14 @@ type ISnapshotService interface {
SnapshotRecover(req dto.SnapshotRecover) error
SnapshotRollback(req dto.SnapshotRecover) error
SnapshotImport(req dto.SnapshotImport) error
Delete(req dto.BatchDeleteReq) error
Delete(req dto.SnapshotBatchDelete) error
LoadSnapShotStatus(id uint) (*dto.SnapshotStatus, error)
UpdateDescription(req dto.UpdateDescription) error
readFromJson(path string) (SnapshotJson, error)
HandleSnapshot(isCronjob bool, logPath string, req dto.SnapshotCreate, timeNow string) (string, error)
HandleSnapshot(isCronjob bool, logPath string, req dto.SnapshotCreate, timeNow string, secret string) (string, error)
}
func NewISnapshotService() ISnapshotService {
@@ -123,7 +123,7 @@ type SnapshotJson struct {
}
func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
if _, err := u.HandleSnapshot(false, "", req, time.Now().Format("20060102150405")); err != nil {
if _, err := u.HandleSnapshot(false, "", req, time.Now().Format("20060102150405"), req.Secret); err != nil {
return err
}
return nil
@@ -180,7 +180,7 @@ func (u *SnapshotService) readFromJson(path string) (SnapshotJson, error) {
return snap, nil
}
func (u *SnapshotService) HandleSnapshot(isCronjob bool, logPath string, req dto.SnapshotCreate, timeNow string) (string, error) {
func (u *SnapshotService) HandleSnapshot(isCronjob bool, logPath string, req dto.SnapshotCreate, timeNow string, secret string) (string, error) {
localDir, err := loadLocalDir()
if err != nil {
return "", err
@@ -274,7 +274,7 @@ func (u *SnapshotService) HandleSnapshot(isCronjob bool, logPath string, req dto
return
}
if snapStatus.Compress != constant.StatusDone {
snapCompress(itemHelper, rootDir)
snapCompress(itemHelper, rootDir, secret)
}
if snapStatus.Compress != constant.StatusDone {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
@@ -305,7 +305,7 @@ func (u *SnapshotService) HandleSnapshot(isCronjob bool, logPath string, req dto
return snap.Name, fmt.Errorf("snapshot %s 1panel data failed", snap.Name)
}
loadLogByStatus(snapStatus, logPath)
snapCompress(itemHelper, rootDir)
snapCompress(itemHelper, rootDir, secret)
if snapStatus.Compress != constant.StatusDone {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
loadLogByStatus(snapStatus, logPath)
@@ -323,25 +323,25 @@ func (u *SnapshotService) HandleSnapshot(isCronjob bool, logPath string, req dto
return snap.Name, nil
}
func (u *SnapshotService) Delete(req dto.BatchDeleteReq) error {
backups, _ := snapshotRepo.GetList(commonRepo.WithIdsIn(req.Ids))
localDir, err := loadLocalDir()
if err != nil {
return err
}
for _, snap := range backups {
itemFile := path.Join(localDir, "system", snap.Name)
_ = os.RemoveAll(itemFile)
itemTarFile := path.Join(global.CONF.System.TmpDir, "system", snap.Name+".tar.gz")
_ = os.Remove(itemTarFile)
func (u *SnapshotService) Delete(req dto.SnapshotBatchDelete) error {
snaps, _ := snapshotRepo.GetList(commonRepo.WithIdsIn(req.Ids))
for _, snap := range snaps {
if req.DeleteWithFile {
targetAccounts, err := loadClientMap(snap.From)
if err != nil {
return err
}
for _, item := range targetAccounts {
global.LOG.Debugf("remove snapshot file %s.tar.gz from %s", snap.Name, item.backType)
_, _ = item.client.Delete(path.Join(item.backupPath, "system_snapshot", snap.Name+".tar.gz"))
}
}
_ = snapshotRepo.DeleteStatus(snap.ID)
if err := snapshotRepo.Delete(commonRepo.WithByID(snap.ID)); err != nil {
return err
}
}
if err := snapshotRepo.Delete(commonRepo.WithIdsIn(req.Ids)); err != nil {
return err
}
return nil
}
@@ -385,24 +385,21 @@ func updateRecoverStatus(id uint, isRecover bool, interruptStep, status, message
}
}
func cpBinary(src []string, dst string) error {
global.LOG.Debugf(fmt.Sprintf("\\cp -f %s %s", strings.Join(src, " "), dst))
stdout, err := cmd.Exec(fmt.Sprintf("\\cp -f %s %s", strings.Join(src, " "), dst))
if err != nil {
return fmt.Errorf("cp file failed, stdout: %v, err: %v", stdout, err)
}
return nil
}
func (u *SnapshotService) handleUnTar(sourceDir, targetDir string) error {
func (u *SnapshotService) handleUnTar(sourceDir, targetDir string, secret string) error {
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
return err
}
}
commands := fmt.Sprintf("tar -zxf %s -C %s .", sourceDir, targetDir)
global.LOG.Debug(commands)
commands := ""
if len(secret) != 0 {
extraCmd := "openssl enc -d -aes-256-cbc -k " + secret + " -in " + sourceDir + " | "
commands = fmt.Sprintf("%s tar -zxvf - -C %s", extraCmd, targetDir+" > /dev/null 2>&1")
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
} else {
commands = fmt.Sprintf("tar zxvfC %s %s", sourceDir, targetDir)
global.LOG.Debug(commands)
}
stdout, err := cmd.ExecWithTimeOut(commands, 30*time.Minute)
if err != nil {
if len(stdout) != 0 {

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