Compare 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
415 changed files with 10515 additions and 2094 deletions
+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/')
+2
View File
@@ -31,6 +31,8 @@
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/)
+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
+14 -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,19 @@ 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) {
+3 -3
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 {
@@ -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,
})
}
+1
View File
@@ -37,6 +37,7 @@ var (
deviceService = service.NewIDeviceService()
fail2banService = service.NewIFail2BanService()
ftpService = service.NewIFtpService()
settingService = service.NewISettingService()
backupService = service.NewIBackupService()
+24 -6
View File
@@ -592,7 +592,11 @@ 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)
@@ -604,12 +608,21 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int)
return err
}
}
targetFile, err := os.OpenFile(filepath.Join(dstDir, fileName), os.O_RDWR|os.O_CREATE, mode)
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)
@@ -620,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
@@ -704,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()
}
+32
View File
@@ -1,6 +1,7 @@
package v1
import (
"encoding/base64"
"errors"
"os"
"path"
@@ -60,6 +61,37 @@ 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 隐藏高级功能菜单
+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
}
+40 -10
View File
@@ -3,11 +3,13 @@ package v1
import (
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"strconv"
"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"
@@ -77,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")) {
@@ -86,23 +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 := []string{"redis-cli"}
if len(redisConf.Requirepass) != 0 {
commands = []string{"redis-cli", "-a", redisConf.Requirepass, "--no-auth-warning"}
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)
itemCmds := append([]string{"exec", "-it", 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, strings.Join(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")
+10
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)
+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 {
+6
View File
@@ -30,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"`
}
+1 -1
View File
@@ -123,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"`
}
+14 -9
View File
@@ -1,6 +1,8 @@
package dto
import "time"
import (
"time"
)
type CronjobCreate struct {
Name string `json:"name" validate:"required"`
@@ -21,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 {
@@ -42,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 {
@@ -87,6 +91,7 @@ type CronjobInfo struct {
LastRecordTime string `json:"lastRecordTime"`
Status string `json:"status"`
Secret string `json:"secret"`
}
type SearchRecord struct {
@@ -98,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"`
}
+12 -8
View File
@@ -131,7 +131,7 @@ type MysqlStatus struct {
}
type MysqlVariables struct {
BinlogCacheSize 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"`
+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"`
}
+5 -3
View File
@@ -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 {
+3
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
}
+1 -1
View File
@@ -77,7 +77,7 @@ type WebsiteSSLUpdate struct {
PrimaryDomain string `json:"primaryDomain" validate:"required"`
OtherDomains string `json:"otherDomains"`
Provider string `json:"provider" validate:"required"`
AcmeAccountID uint `json:"acmeAccountId" validate:"required"`
AcmeAccountID uint `json:"acmeAccountId"`
DnsAccountID uint `json:"dnsAccountId"`
KeyType string `json:"keyType"`
Apply bool `json:"apply"`
+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"`
+35 -3
View File
@@ -58,6 +58,13 @@ type SettingInfo struct {
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 {
@@ -109,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"`
@@ -160,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 {
@@ -185,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"`
}
+4 -1
View File
@@ -1,6 +1,8 @@
package model
import "time"
import (
"time"
)
type Cronjob struct {
BaseModel
@@ -31,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 {
+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"`
+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
}
+50
View File
@@ -17,6 +17,12 @@ type ICommandRepo interface {
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 {
@@ -33,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{})
@@ -45,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{})
@@ -55,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 {
@@ -69,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
}
@@ -81,6 +123,14 @@ 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 {
+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
}
+38 -40
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,21 +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)
}
defer composeRes.Body.Close()
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)
@@ -363,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
}
@@ -440,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
}
@@ -449,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)
}
@@ -699,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
@@ -753,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() {
@@ -834,25 +841,21 @@ 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)
if err != nil {
return err
}
body, err := io.ReadAll(iconRes.Body)
_, iconRes, err := httpUtil.HandleGetWithTransport(l.Icon, http.MethodGet, transport, constant.TimeOut20s)
if err != nil {
return err
}
iconStr := ""
if !strings.Contains(string(body), "xml") {
iconStr = base64.StdEncoding.EncodeToString(body)
if !strings.Contains(string(iconRes), "<xml>") {
iconStr = base64.StdEncoding.EncodeToString(iconRes)
}
_ = iconRes.Body.Close()
app.Icon = iconStr
app.TagsKey = l.AppProperty.Tags
@@ -872,16 +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
}
defer composeRes.Body.Close()
bodyContent, err := io.ReadAll(composeRes.Body)
if err != nil {
return err
}
detail.DockerCompose = string(bodyContent)
detail.DockerCompose = string(composeRes)
} else {
detail.DockerCompose = ""
}
+60 -81
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,82 +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
}
defer cli.Close()
containers, err := cli.ListContainersByName(containerNames)
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:
if appInstall.Status != constant.Error {
appInstall.Status = constant.SyncErr
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
}
+242 -100
View File
@@ -14,7 +14,11 @@ 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"
@@ -38,6 +42,7 @@ 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"
"github.com/pkg/errors"
@@ -436,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
}
@@ -454,9 +512,10 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
var (
upErr error
backupFile string
preErr error
)
global.LOG.Infof(i18n.GetMsgByKey("UpgradeAppStart"))
if backup {
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()
@@ -473,38 +532,47 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
defer func() {
if upErr != nil {
global.LOG.Infof(i18n.GetMsgWithName("ErrAppUpgrade", install.Name, upErr))
if backup {
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)
}
}
existInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(installID))
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)
}()
}
@@ -512,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 {
@@ -526,88 +620,19 @@ 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, err := json.Marshal(envs)
if err != nil {
upErr = err
return
}
install.Env = string(paramByte)
composeByte, err := yaml.Marshal(composeMap)
if err != nil {
upErr = err
return
} else {
newCompose = req.DockerCompose
}
install.DockerCompose = string(composeByte)
install.DockerCompose = newCompose
install.Version = detail.Version
install.AppDetailId = detailID
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
}
defer dockerCli.Close()
for _, image := range images {
global.LOG.Infof(i18n.GetMsgWithName("PullImageStart", image, nil))
if err = dockerCli.PullImage(image, true); err != nil {
upErr = buserr.WithNameAndErr("ErrDockerPullImage", "", err)
return
} else {
global.LOG.Infof(i18n.GetMsgByKey("PullImageSuccess"))
}
}
}
install.AppDetailId = req.DetailID
if out, err := compose.Down(install.GetComposePath()); err != nil {
if out != "" {
@@ -617,6 +642,10 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error
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 upErr = env.Write(envParams, install.GetEnvPath()); upErr != nil {
@@ -757,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
}
@@ -781,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
@@ -908,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
}
@@ -933,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)
@@ -1110,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 {
+31 -1
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,7 +50,7 @@ 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
@@ -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 {
+10 -10
View File
@@ -40,7 +40,7 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) (*model.BackupRecord, er
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 {
if err := handleAppBackup(&install, backupDir, fileName, "", req.Secret); err != nil {
return nil, err
}
@@ -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, excludes 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, excl
}
appPath := install.GetPath()
if err := handleTar(appPath, tmpDir, "app.tar.gz", excludes); 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, excl
}
}
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 {
+8 -8
View File
@@ -16,7 +16,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/files"
)
func handleRuntimeBackup(runtime *model.Runtime, backupDir, fileName string, excludes 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) {
@@ -35,19 +35,19 @@ func handleRuntimeBackup(runtime *model.Runtime, backupDir, fileName string, exc
}
appPath := runtime.GetPath()
if err := handleTar(appPath, tmpDir, "runtime.tar.gz", excludes); 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", "")
@@ -73,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
}
@@ -100,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)
+14 -14
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, excludes 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, exc
if err != nil {
return err
}
if err := handleAppBackup(&app, tmpDir, fmt.Sprintf("%s.app.tar.gz", website.Alias), excludes); 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, exc
return err
}
if runtime.Type == constant.RuntimeNode {
if err := handleRuntimeBackup(runtime, tmpDir, fmt.Sprintf("%s.runtime.tar.gz", website.Alias), excludes); 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, exc
}
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), excludes); 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
}
+66
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 {
@@ -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))
}
+20 -4
View File
@@ -29,6 +29,7 @@ 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"
@@ -323,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
}
@@ -854,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
}
}
+9 -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
}
@@ -278,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)
}
@@ -291,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
}
+7 -7
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, cronjob.ExclusionRules); 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, cronjob.ExclusionRules); 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
@@ -376,7 +376,7 @@ func handleBackupLogs(targetDir, fileName string) error {
}
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() {
+21 -6
View File
@@ -130,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
@@ -158,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 {
@@ -170,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)
+5 -2
View File
@@ -222,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") {
+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",
+12 -1
View File
@@ -240,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 {
@@ -363,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
}
+12 -1
View File
@@ -266,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 {
@@ -414,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
View File
@@ -4,7 +4,6 @@ import (
"bufio"
"context"
"encoding/json"
"fmt"
"os"
"path"
"strings"
@@ -104,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()
+25 -3
View File
@@ -2,6 +2,7 @@ package service
import (
"fmt"
"io"
"io/fs"
"os"
"os/exec"
@@ -9,11 +10,15 @@ import (
"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"
@@ -214,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) {
@@ -230,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
}
@@ -304,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)
+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")
+3 -3
View File
@@ -19,6 +19,7 @@ import (
"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"
@@ -54,12 +55,10 @@ func handleNode(create request.RuntimeCreate, runtime *model.Runtime, fileOp fil
}
go func() {
res, err := http.Get(nodeDetail.DownloadCallBackUrl)
if err != nil {
if _, _, err := httpUtil.HandleGet(nodeDetail.DownloadCallBackUrl, http.MethodGet, constant.TimeOut5s); err != nil {
global.LOG.Errorf("http request failed(handleNode), err: %v", err)
return
}
res.Body.Close()
}()
go startRuntime(runtime)
@@ -150,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
+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 -26
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,15 +385,21 @@ func updateRecoverStatus(id uint, isRecover bool, interruptStep, status, message
}
}
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 {
+25 -8
View File
@@ -131,7 +131,7 @@ func snapBackup(snap snapHelper, localDir, targetDir string) {
defer snap.Wg.Done()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"backup_data": constant.Running})
status := constant.StatusDone
if err := handleSnapTar(localDir, targetDir, "1panel_backup.tar.gz", "./system;./system_snapshot;"); err != nil {
if err := handleSnapTar(localDir, targetDir, "1panel_backup.tar.gz", "./system;./system_snapshot;", ""); err != nil {
status = err.Error()
}
snap.Status.BackupData = status
@@ -158,7 +158,7 @@ func snapPanelData(snap snapHelper, localDir, targetDir string) {
sysIP, _ := settingRepo.Get(settingRepo.WithByKey("SystemIP"))
_ = settingRepo.Update("SystemIP", "")
checkPointOfWal()
if err := handleSnapTar(dataDir, targetDir, "1panel_data.tar.gz", exclusionRules); err != nil {
if err := handleSnapTar(dataDir, targetDir, "1panel_data.tar.gz", exclusionRules, ""); err != nil {
status = err.Error()
}
_ = snapshotRepo.Update(snap.SnapID, map[string]interface{}{"status": constant.StatusWaiting})
@@ -168,11 +168,11 @@ func snapPanelData(snap snapHelper, localDir, targetDir string) {
_ = settingRepo.Update("SystemIP", sysIP.Value)
}
func snapCompress(snap snapHelper, rootDir string) {
func snapCompress(snap snapHelper, rootDir string, secret string) {
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"compress": constant.StatusRunning})
tmpDir := path.Join(global.CONF.System.TmpDir, "system")
fileName := fmt.Sprintf("%s.tar.gz", path.Base(rootDir))
if err := snap.FileOp.Compress([]string{rootDir}, tmpDir, fileName, files.TarGz); err != nil {
if err := handleSnapTar(rootDir, tmpDir, fileName, "", secret); err != nil {
snap.Status.Compress = err.Error()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"compress": err.Error()})
return
@@ -221,7 +221,7 @@ func snapUpload(snap snapHelper, accounts string, file string) {
_ = os.Remove(source)
}
func handleSnapTar(sourceDir, targetDir, name, exclusionRules string) error {
func handleSnapTar(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
@@ -243,9 +243,26 @@ func handleSnapTar(sourceDir, targetDir, name, exclusionRules string) error {
exStr += exclude
exMap[exclude] = struct{}{}
}
commands := fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s -C %s .", targetDir+"/"+name, exStr, sourceDir)
global.LOG.Debug(commands)
path := ""
if strings.Contains(sourceDir, "/") {
itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "")
aheadDir := sourceDir[:strings.LastIndex(sourceDir, "/")]
if len(aheadDir) == 0 {
aheadDir = "/"
}
path += fmt.Sprintf("-C %s %s", aheadDir, itemDir)
} else {
path = sourceDir
}
commands := ""
if len(secret) != 0 {
extraCmd := "| openssl enc -aes-256-cbc -salt -k " + secret + " -out"
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s %s %s", " -"+exStr, path, extraCmd, targetDir+"/"+name)
global.LOG.Debug(strings.ReplaceAll(commands, fmt.Sprintf(" %s ", secret), "******"))
} else {
commands = fmt.Sprintf("tar --warning=no-file-changed --ignore-failed-read -zcf %s %s -C %s .", targetDir+"/"+name, exStr, sourceDir)
global.LOG.Debug(commands)
}
stdout, err := cmd.ExecWithTimeOut(commands, 30*time.Minute)
if err != nil {
if len(stdout) != 0 {
+15 -12
View File
@@ -30,14 +30,6 @@ func (u *SnapshotService) HandleSnapshotRecover(snap model.Snapshot, isRecover b
if _, err := os.Stat(baseDir); err != nil && os.IsNotExist(err) {
_ = os.MkdirAll(baseDir, os.ModePerm)
}
if req.IsNew || snap.InterruptStep == "Backup" {
if err := backupBeforeRecover(snap); err != nil {
updateRecoverStatus(snap.ID, isRecover, "Backup", constant.StatusFailed, fmt.Sprintf("handle backup before recover failed, err: %v", err))
return
}
global.LOG.Debug("handle backup before recover successful!")
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "Download" || req.ReDownload {
if err := handleDownloadSnapshot(snap, baseDir); err != nil {
updateRecoverStatus(snap.ID, isRecover, "Backup", constant.StatusFailed, err.Error())
@@ -47,14 +39,25 @@ func (u *SnapshotService) HandleSnapshotRecover(snap model.Snapshot, isRecover b
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "Decompress" {
if err := handleUnTar(fmt.Sprintf("%s/%s.tar.gz", baseDir, snap.Name), baseDir); err != nil {
if err := handleUnTar(fmt.Sprintf("%s/%s.tar.gz", baseDir, snap.Name), baseDir, req.Secret); err != nil {
updateRecoverStatus(snap.ID, isRecover, "Decompress", constant.StatusFailed, fmt.Sprintf("decompress file failed, err: %v", err))
return
}
global.LOG.Debug("decompress snapshot file successful!", baseDir)
req.IsNew = true
}
if req.IsNew || snap.InterruptStep == "Backup" {
if err := backupBeforeRecover(snap, ""); err != nil {
updateRecoverStatus(snap.ID, isRecover, "Backup", constant.StatusFailed, fmt.Sprintf("handle backup before recover failed, err: %v", err))
return
}
global.LOG.Debug("handle backup before recover successful!")
req.IsNew = true
}
snapFileDir = fmt.Sprintf("%s/%s", baseDir, snap.Name)
if _, err := os.Stat(snapFileDir); err != nil {
snapFileDir = baseDir
}
} else {
snapFileDir = fmt.Sprintf("%s/1panel_original/original_%s", global.CONF.System.BaseDir, snap.Name)
if _, err := os.Stat(snapFileDir); err != nil {
@@ -114,7 +117,7 @@ func (u *SnapshotService) HandleSnapshotRecover(snap model.Snapshot, isRecover b
}
if req.IsNew || snap.InterruptStep == "1PanelBackups" {
if err := u.handleUnTar(path.Join(snapFileDir, "/1panel/1panel_backup.tar.gz"), snapJson.BackupDataDir); err != nil {
if err := u.handleUnTar(path.Join(snapFileDir, "/1panel/1panel_backup.tar.gz"), snapJson.BackupDataDir, ""); err != nil {
updateRecoverStatus(snap.ID, isRecover, "1PanelBackups", constant.StatusFailed, err.Error())
return
}
@@ -124,7 +127,7 @@ func (u *SnapshotService) HandleSnapshotRecover(snap model.Snapshot, isRecover b
if req.IsNew || snap.InterruptStep == "1PanelData" {
checkPointOfWal()
if err := u.handleUnTar(path.Join(snapFileDir, "/1panel/1panel_data.tar.gz"), path.Join(snapJson.BaseDir, "1panel")); err != nil {
if err := u.handleUnTar(path.Join(snapFileDir, "/1panel/1panel_data.tar.gz"), path.Join(snapJson.BaseDir, "1panel"), ""); err != nil {
updateRecoverStatus(snap.ID, isRecover, "1PanelData", constant.StatusFailed, err.Error())
return
}
@@ -146,7 +149,7 @@ func (u *SnapshotService) HandleSnapshotRecover(snap model.Snapshot, isRecover b
_, _ = cmd.Exec("systemctl daemon-reload && systemctl restart 1panel.service")
}
func backupBeforeRecover(snap model.Snapshot) error {
func backupBeforeRecover(snap model.Snapshot, secret string) error {
baseDir := fmt.Sprintf("%s/1panel_original/original_%s", global.CONF.System.BaseDir, snap.Name)
var wg sync.WaitGroup
var status model.SnapshotStatus
+17 -6
View File
@@ -479,9 +479,12 @@ func loadFailedAuthDatas(line string) dto.SSHHistory {
return data
}
data.DateStr = dataStr
if index == 2 {
switch index {
case 1:
data.User = parts[9]
case 2:
data.User = parts[10]
} else {
default:
data.User = parts[7]
}
data.AuthMode = parts[6+index]
@@ -543,14 +546,22 @@ func loadDate(currentYear int, DateStr string, nyc *time.Location) time.Time {
func analyzeDateStr(parts []string) (int, string) {
t, err := time.Parse("2006-01-02T15:04:05.999999-07:00", parts[0])
if err != nil {
if err == nil {
if len(parts) < 12 {
return 0, ""
}
return 0, t.Format("2006 Jan 2 15:04:05")
}
t, err = time.Parse("2006-01-02 15:04:05", fmt.Sprintf("%s %s", parts[0], parts[1]))
if err == nil {
if len(parts) < 14 {
return 0, ""
}
return 2, fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2])
return 1, t.Format("2006 Jan 2 15:04:05")
}
if len(parts) < 12 {
if len(parts) < 14 {
return 0, ""
}
return 0, t.Format("2006 Jan 2 15:04:05")
return 2, fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2])
}
+12 -18
View File
@@ -3,7 +3,6 @@ package service
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path"
@@ -12,10 +11,12 @@ import (
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http"
)
type UpgradeService struct{}
@@ -108,7 +109,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
defer func() {
global.Cron.Start()
}()
if err := fileOp.DownloadFile(downloadPath+"/"+fileName, rootDir+"/"+fileName); err != nil {
if err := fileOp.DownloadFileWithProxy(downloadPath+"/"+fileName, rootDir+"/"+fileName); err != nil {
global.LOG.Errorf("download service file failed, err: %v", err)
_ = settingRepo.Update("SystemStatus", "Free")
return
@@ -117,7 +118,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
defer func() {
_ = os.Remove(rootDir)
}()
if err := handleUnTar(rootDir+"/"+fileName, rootDir); err != nil {
if err := handleUnTar(rootDir+"/"+fileName, rootDir, ""); err != nil {
global.LOG.Errorf("decompress file failed, err: %v", err)
_ = settingRepo.Update("SystemStatus", "Free")
return
@@ -175,7 +176,7 @@ func (u *UpgradeService) handleBackup(fileOp files.FileOp, originalDir string) e
return err
}
checkPointOfWal()
if err := handleTar(path.Join(global.CONF.System.BaseDir, "1panel/db"), originalDir, "db.tar.gz", "./1Panel.db-*"); err != nil {
if err := handleTar(path.Join(global.CONF.System.BaseDir, "1panel/db"), originalDir, "db.tar.gz", "db/1Panel.db-*", ""); err != nil {
return err
}
return nil
@@ -191,7 +192,7 @@ func (u *UpgradeService) handleRollback(originalDir string, errStep int) {
}
}
if _, err := os.Stat(path.Join(originalDir, "db.tar.gz")); err == nil {
if err := handleUnTar(path.Join(originalDir, "db.tar.gz"), global.CONF.System.DbPath); err != nil {
if err := handleUnTar(path.Join(originalDir, "db.tar.gz"), global.CONF.System.DbPath, ""); err != nil {
global.LOG.Errorf("rollback 1panel db failed, err: %v", err)
}
}
@@ -258,15 +259,13 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion, mode string)
if !isLatest {
path = fmt.Sprintf("%s/%s/latest.current", global.CONF.System.RepoUrl, mode)
}
latestVersionRes, err := http.Get(path)
_, latestVersionRes, err := httpUtil.HandleGet(path, http.MethodGet, constant.TimeOut20s)
if err != nil {
global.LOG.Errorf("load latest version from oss failed, err: %v", err)
return ""
}
defer latestVersionRes.Body.Close()
versionByte, err := io.ReadAll(latestVersionRes.Body)
version := string(versionByte)
if err != nil || strings.Contains(version, "<") {
version := string(latestVersionRes)
if strings.Contains(version, "<") {
global.LOG.Errorf("load latest version from oss failed, err: %v", version)
return ""
}
@@ -275,7 +274,7 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion, mode string)
}
versionMap := make(map[string]string)
if err := json.Unmarshal(versionByte, &versionMap); err != nil {
if err := json.Unmarshal(latestVersionRes, &versionMap); err != nil {
global.LOG.Errorf("load latest version from oss failed (error unmarshal), err: %v", err)
return ""
}
@@ -321,16 +320,11 @@ func (u *UpgradeService) checkVersion(v2, v1 string) string {
}
func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
releaseNotes, err := http.Get(path)
_, releaseNotes, err := httpUtil.HandleGet(path, http.MethodGet, constant.TimeOut20s)
if err != nil {
return "", err
}
defer releaseNotes.Body.Close()
release, err := io.ReadAll(releaseNotes.Body)
if err != nil {
return "", err
}
return string(release), nil
return string(releaseNotes), nil
}
func loadArch() (string, error) {
+16 -13
View File
@@ -331,6 +331,14 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
return err
}
if len(create.FtpUser) != 0 && len(create.FtpPassword) != 0 {
itemID, err := NewIFtpService().Create(dto.FtpCreate{User: create.FtpUser, Password: create.FtpPassword, Path: path.Join(global.CONF.System.BaseDir, "1panel/apps/openresty/openresty/www/sites", website.Alias, "index")})
if err != nil {
global.LOG.Errorf("create ftp for website failed, err: %v", err)
}
website.FtpID = itemID
}
if err = createWafConfig(website, domains); err != nil {
return err
}
@@ -446,7 +454,8 @@ func (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error {
tx, ctx := helper.GetTxAndContext()
defer tx.Rollback()
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("website"), commonRepo.WithByName(website.Alias))
go NewIBackupService().DeleteRecordByName("website", website.PrimaryDomain, website.Alias, req.DeleteBackup)
if err := websiteRepo.DeleteBy(ctx, commonRepo.WithByID(req.ID)); err != nil {
return err
}
@@ -455,14 +464,6 @@ func (w WebsiteService) DeleteWebsite(req request.WebsiteDelete) error {
}
tx.Commit()
if req.DeleteBackup {
localDir, _ := loadLocalDir()
backupDir := path.Join(localDir, fmt.Sprintf("website/%s", website.Alias))
if _, err := os.Stat(backupDir); err == nil {
_ = os.RemoveAll(backupDir)
}
global.LOG.Infof("delete website %s backups successful", website.Alias)
}
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/website/%s", website.Alias))
if _, err := os.Stat(uploadDir); err == nil {
_ = os.RemoveAll(uploadDir)
@@ -525,7 +526,7 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate)
for _, domain := range domainModels {
wafSite.Domains = append(wafSite.Domains, domain.Domain)
if domain.Port != 80 && domain.Port != 443 {
wafSite.Host = append(wafSite.Host, domain.Domain+":"+string(rune(domain.Port)))
wafSite.Host = append(wafSite.Host, domain.Domain+":"+strconv.Itoa(domain.Port))
}
}
if len(wafSite.Host) == 0 {
@@ -618,7 +619,7 @@ func (w WebsiteService) DeleteWebsiteDomain(domainId uint) error {
oldHostArray := wafSite.Host
var newHostArray []string
for _, host := range oldHostArray {
if host == webSiteDomain.Domain+":"+string(rune(webSiteDomain.Port)) {
if host == webSiteDomain.Domain+":"+strconv.Itoa(webSiteDomain.Port) {
continue
}
newHostArray = append(newHostArray, host)
@@ -940,7 +941,7 @@ func (w WebsiteService) PreInstallCheck(req request.WebsiteInstallCheckReq) ([]r
if len(checkIds) > 0 {
installList, _ := appInstallRepo.ListBy(commonRepo.WithIdsIn(checkIds))
for _, install := range installList {
if err = syncAppInstallStatus(&install); err != nil {
if err = syncAppInstallStatus(&install, false); err != nil {
return nil, err
}
res = append(res, response.WebsitePreInstallCheck{
@@ -1036,14 +1037,16 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
case constant.EnableLog:
key := "access_log"
logPath := path.Join("/www", "sites", website.Alias, "log", req.LogType)
params := []string{logPath}
switch req.LogType {
case constant.AccessLog:
params = append(params, "main")
website.AccessLog = true
case constant.ErrorLog:
key = "error_log"
website.ErrorLog = true
}
if err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: key, Params: []string{logPath}}}, &website); err != nil {
if err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: key, Params: params}}, &website); err != nil {
return nil, err
}
if err := websiteRepo.Save(context.Background(), &website); err != nil {
+9 -6
View File
@@ -389,11 +389,13 @@ func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
updateParams["nameserver1"] = update.Nameserver1
updateParams["nameserver2"] = update.Nameserver2
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(update.AcmeAccountID))
if err != nil {
return err
if websiteSSL.Provider != constant.SelfSigned {
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(update.AcmeAccountID))
if err != nil {
return err
}
updateParams["acme_account_id"] = acmeAccount.ID
}
updateParams["acme_account_id"] = acmeAccount.ID
if update.PushDir {
if !files.NewFileOp().Stat(update.Dir) {
@@ -412,7 +414,7 @@ func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
}
}
updateParams["domains"] = strings.Join(domains, ",")
if update.Provider == constant.DNSAccount || update.Provider == constant.Http {
if update.Provider == constant.DNSAccount || update.Provider == constant.Http || update.Provider == constant.SelfSigned {
updateParams["auto_renew"] = update.AutoRenew
} else {
updateParams["auto_renew"] = false
@@ -431,6 +433,7 @@ func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {
websiteSSL := &model.WebsiteSSL{
Provider: constant.Manual,
Description: req.Description,
Status: constant.SSLReady,
}
var err error
if req.SSLID > 0 {
@@ -549,7 +552,7 @@ func (w WebsiteSSLService) DownloadFile(id uint) (*os.File, error) {
return nil, err
}
fileName := websiteSSL.PrimaryDomain + ".zip"
if err = fileOp.Compress([]string{path.Join(dir, "fullchain.pem"), path.Join(dir, "privkey.pem")}, dir, fileName, files.SdkZip); err != nil {
if err = fileOp.Compress([]string{path.Join(dir, "fullchain.pem"), path.Join(dir, "privkey.pem")}, dir, fileName, files.SdkZip, ""); err != nil {
return nil, err
}
return os.Open(path.Join(dir, fileName))
+28 -12
View File
@@ -235,7 +235,7 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
server.UpdateServerName(serverNames)
siteFolder := path.Join("/www", "sites", website.Alias)
server.UpdateDirective("access_log", []string{path.Join(siteFolder, "log", "access.log")})
server.UpdateDirective("access_log", []string{path.Join(siteFolder, "log", "access.log"), "main"})
server.UpdateDirective("error_log", []string{path.Join(siteFolder, "log", "error.log")})
rootIndex := path.Join("/www/sites", website.Alias, "index")
@@ -316,9 +316,7 @@ func createWafConfig(website *model.Website, domains []model.WebsiteDomain) erro
for _, domain := range domains {
wafWebsite.Domains = append(wafWebsite.Domains, domain.Domain)
if domain.Port != 80 && domain.Port != 443 {
wafWebsite.Host = append(wafWebsite.Host, domain.Domain+":"+string(rune(domain.Port)))
}
wafWebsite.Host = append(wafWebsite.Host, domain.Domain+":"+strconv.Itoa(domain.Port))
}
websitesArray = append(websitesArray, wafWebsite)
websitesContent, err := json.Marshal(websitesArray)
@@ -431,6 +429,10 @@ func delWafConfig(website model.Website, force bool) error {
if !fileOp.Stat(wafDataPath) {
return nil
}
monitorDir := path.Join(wafDataPath, "db", "sites", website.Alias)
if fileOp.Stat(monitorDir) {
_ = fileOp.DeleteDir(monitorDir)
}
websitesConfigPath := path.Join(wafDataPath, "conf", "sites.json")
content, err := fileOp.GetContent(websitesConfigPath)
if err != nil {
@@ -559,6 +561,16 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
if err != nil {
return nil
}
domains, err := websiteDomainRepo.GetBy(websiteDomainRepo.WithWebsiteId(website.ID))
if err != nil {
return nil
}
noDefaultPort := true
for _, domain := range domains {
if domain.Port == 80 {
noDefaultPort = false
}
}
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
@@ -578,13 +590,17 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
server.RemoveListenByBind(httpPortIPV6)
server.RemoveDirective("if", []string{"($scheme"})
case constant.HTTPToHTTPS:
server.UpdateListen(httpPort, website.DefaultServer)
if !noDefaultPort {
server.UpdateListen(httpPort, website.DefaultServer)
}
if website.IPV6 {
server.UpdateListen(httpPortIPV6, website.DefaultServer)
}
server.AddHTTP2HTTPS()
case constant.HTTPAlso:
server.UpdateListen(httpPort, website.DefaultServer)
if !noDefaultPort {
server.UpdateListen(httpPort, website.DefaultServer)
}
server.RemoveDirective("if", []string{"($scheme"})
if website.IPV6 {
server.UpdateListen(httpPortIPV6, website.DefaultServer)
@@ -722,15 +738,15 @@ func opWebsite(website *model.Website, operate string) error {
website.Status = constant.WebStopped
}
if operate == constant.StartWeb {
proxyInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
absoluteIncludeDir := path.Join(nginxInstall.Install.GetPath(), fmt.Sprintf("/www/sites/%s/proxy", website.Alias))
if files.NewFileOp().Stat(absoluteIncludeDir) {
fileOp := files.NewFileOp()
if fileOp.Stat(absoluteIncludeDir) && !files.IsEmptyDir(absoluteIncludeDir) {
proxyInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
server.UpdateDirective("include", []string{proxyInclude})
}
server.UpdateDirective("include", []string{proxyInclude})
rewriteInclude := fmt.Sprintf("/www/sites/%s/rewrite/%s.conf", website.Alias, website.Alias)
absoluteRewritePath := path.Join(nginxInstall.Install.GetPath(), rewriteInclude)
if files.NewFileOp().Stat(absoluteRewritePath) {
if fileOp.Stat(absoluteRewritePath) {
server.UpdateDirective("include", []string{rewriteInclude})
}
rootIndex := path.Join("/www/sites", website.Alias, "index")
@@ -759,7 +775,7 @@ func opWebsite(website *model.Website, operate string) error {
return err
}
if runtime.Type == constant.RuntimePHP {
if website.ProxyType == constant.RuntimeProxyUnix {
if website.ProxyType == constant.RuntimeProxyUnix || website.ProxyType == constant.RuntimeProxyTcp {
localPath = path.Join(nginxInstall.Install.GetPath(), rootIndex, "index.php")
}
server.UpdatePHPProxy([]string{website.Proxy}, localPath)
@@ -845,7 +861,7 @@ func changeServiceName(newComposeContent, newServiceName string) (composeByte []
return
}
value, ok := composeMap["services"]
if !ok {
if !ok || value == nil {
err = buserr.New(constant.ErrFileParse)
return
}
+1 -1
View File
@@ -13,7 +13,7 @@ const (
Syncing = "Syncing"
SyncSuccess = "SyncSuccess"
Paused = "Paused"
SyncErr = "SyncErr"
UpErr = "UpErr"
ContainerPrefix = "1Panel-"
+6
View File
@@ -12,3 +12,9 @@ const (
TypeSSL = "ssl"
TypeSystem = "system"
)
const (
TimeOut5s = 5
TimeOut20s = 20
TimeOut5m = 300
)
+5 -6
View File
@@ -8,16 +8,15 @@ const (
CodeSuccess = 200
CodeErrBadRequest = 400
CodeErrUnauthorized = 401
CodeErrUnSafety = 402
CodeErrForbidden = 403
CodeErrNotFound = 404
CodePasswordExpired = 405
CodeAuth = 406
CodeGlobalLoading = 407
CodeErrIP = 408
CodeErrDomain = 409
CodeErrInternalServer = 500
CodeErrHeader = 406
CodeErrIP = 310
CodeErrDomain = 311
CodeErrEntrance = 312
CodePasswordExpired = 313
CodeErrXpack = 410
)
+4 -1
View File
@@ -46,6 +46,9 @@ func Run() {
if _, err := global.Cron.AddJob(fmt.Sprintf("%v %v * * *", mathRand.Intn(60), mathRand.Intn(3)), job.NewAppStoreJob()); err != nil {
global.LOG.Errorf("can not add appstore corn job: %s", err.Error())
}
if _, err := global.Cron.AddJob("@daily", job.NewCacheJob()); err != nil {
global.LOG.Errorf("can not add cache corn job: %s", err.Error())
}
var backup model.BackupAccount
_ = global.DB.Where("type = ?", "OneDrive").Find(&backup).Error
@@ -68,7 +71,7 @@ func Run() {
global.LOG.Errorf("start my cronjob failed, err: %v", err)
}
for i := 0; i < len(cronJobs); i++ {
entryIDs, err := service.NewICronjobService().StartJob(&cronJobs[i])
entryIDs, err := service.NewICronjobService().StartJob(&cronJobs[i], false)
if err != nil {
global.LOG.Errorf("start %s job %s failed, err: %v", cronJobs[i].Type, cronJobs[i].Name, err)
}
+26
View File
@@ -0,0 +1,26 @@
package job
import (
"github.com/1Panel-dev/1Panel/backend/global"
"time"
)
type Cache struct{}
func NewCacheJob() *Cache {
return &Cache{}
}
func (c *Cache) Run() {
global.LOG.Info("run cache gc start ...")
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
again:
err := global.CacheDb.RunValueLogGC(0.7)
if err == nil {
goto again
}
}
global.LOG.Info("run cache gc end ...")
}
+2
View File
@@ -4,6 +4,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/configs"
"github.com/1Panel-dev/1Panel/backend/init/cache/badger_db"
"github.com/1Panel-dev/1Panel/backend/init/session/psession"
"github.com/dgraph-io/badger/v4"
"github.com/go-playground/validator/v10"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/robfig/cron/v3"
@@ -20,6 +21,7 @@ var (
VALID *validator.Validate
SESSION *psession.PSession
CACHE *badger_db.Cache
CacheDb *badger.DB
Viper *viper.Viper
Cron *cron.Cron
+2 -1
View File
@@ -49,7 +49,8 @@ func Init() {
if err != nil {
panic(err)
}
_ = cache.DropAll()
global.CacheDb = cache
global.CACHE = badger_db.NewCacheDB(cache)
global.LOG.Info("init cache successfully")
}
+5
View File
@@ -84,6 +84,11 @@ func Init() {
migrations.AddDeveloperSetting,
migrations.AddWebsiteSSLColumn,
migrations.AddRedisCommand,
migrations.AddMonitorMenu,
migrations.AddFtp,
migrations.AddProxy,
migrations.AddCronJobColumn,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
+1 -1
View File
@@ -85,7 +85,7 @@ var AddTableSetting = &gormigrate.Migration{
if err := tx.Create(&model.Setting{Key: "Language", Value: "zh"}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "Theme", Value: "auto"}).Error; err != nil {
if err := tx.Create(&model.Setting{Key: "Theme", Value: "light"}).Error; err != nil {
return err
}
@@ -1,6 +1,9 @@
package migrations
import (
"encoding/json"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/go-gormigrate/gormigrate/v2"
@@ -164,3 +167,84 @@ var AddWebsiteSSLColumn = &gormigrate.Migration{
return nil
},
}
var AddRedisCommand = &gormigrate.Migration{
ID: "20240515-add-redis-command",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.RedisCommand{}); err != nil {
return err
}
return nil
},
}
var AddMonitorMenu = &gormigrate.Migration{
ID: "20240517-update-xpack-hide-menu",
Migrate: func(tx *gorm.DB) error {
var (
setting model.Setting
menu dto.XpackHideMenu
)
tx.Model(&model.Setting{}).Where("key", "XpackHideMenu").First(&setting)
if err := json.Unmarshal([]byte(setting.Value), &menu); err != nil {
return err
}
menu.Children = append(menu.Children, dto.XpackHideMenu{
ID: "6",
Title: "xpack.monitor.name",
Path: "/xpack/monitor/dashboard",
Label: "MonitorDashboard",
IsCheck: true,
})
data, err := json.Marshal(menu)
if err != nil {
return err
}
return tx.Model(&model.Setting{}).Where("key", "XpackHideMenu").Updates(map[string]interface{}{"value": string(data)}).Error
},
}
var AddFtp = &gormigrate.Migration{
ID: "20240521-add-ftp",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Ftp{}, model.Website{}); err != nil {
return err
}
return nil
},
}
var AddProxy = &gormigrate.Migration{
ID: "20240528-add-proxy",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "ProxyType", Value: ""}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "ProxyUrl", Value: ""}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "ProxyPort", Value: ""}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "ProxyUser", Value: ""}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "ProxyPasswd", Value: ""}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "ProxyPasswdKeep", Value: ""}).Error; err != nil {
return err
}
return nil
},
}
var AddCronJobColumn = &gormigrate.Migration{
ID: "20240524-add-cronjob-command",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Cronjob{}); err != nil {
return err
}
return nil
},
}
@@ -124,6 +124,12 @@ var AddTablePHPExtensions = &gormigrate.Migration{
if err := tx.Create(&model.PHPExtensions{Name: "默认", Extensions: "bcmath,gd,gettext,intl,pcntl,shmop,soap,sockets,sysvsem,xmlrpc,zip"}).Error; err != nil {
return err
}
if err := tx.Create(&model.PHPExtensions{Name: "WordPress", Extensions: "exif,igbinary,imagick,intl,zip,apcu,memcached,opcache,redis,bc,image,shmop,mysqli,pdo_mysql"}).Error; err != nil {
return err
}
if err := tx.Create(&model.PHPExtensions{Name: "Flarum", Extensions: "curl,gd,pdo_mysql,mysqli,bz2,exif,yaf,imap"}).Error; err != nil {
return err
}
if err := tx.Create(&model.PHPExtensions{Name: "苹果CMS-V10", Extensions: "mysqli,pdo_mysql,zip,gd,redis,memcache,memcached"}).Error; err != nil {
return err
}
-3
View File
@@ -4,7 +4,6 @@ import (
"fmt"
"net/http"
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/middleware"
@@ -64,8 +63,6 @@ func Routers() *gin.Engine {
PublicGroup.GET("/health", func(c *gin.Context) {
c.JSON(200, "ok")
})
baseApi := v1.ApiGroupApp.BaseApi
PublicGroup.GET("/api/v1/respagecode", baseApi.GetResponsePage)
PublicGroup.Use(gzip.Gzip(gzip.DefaultCompression))
setWebStatic(PublicGroup)
}
+3 -3
View File
@@ -101,19 +101,19 @@ func Init() {
}
func loadParams(param string) string {
stdout, err := cmd.Execf("grep '^%s=' /usr/bin/1pctl | cut -d'=' -f2", param)
stdout, err := cmd.Execf("grep '^%s=' /usr/local/bin/1pctl | cut -d'=' -f2", param)
if err != nil {
panic(err)
}
info := strings.ReplaceAll(stdout, "\n", "")
if len(info) == 0 || info == `""` {
panic(fmt.Sprintf("error `%s` find in /usr/bin/1pctl", param))
panic(fmt.Sprintf("error `%s` find in /usr/local/bin/1pctl", param))
}
return info
}
func loadChangeInfo() string {
stdout, err := cmd.Exec("grep '^CHANGE_USER_INFO=' /usr/bin/1pctl | cut -d'=' -f2")
stdout, err := cmd.Exec("grep '^CHANGE_USER_INFO=' /usr/local/bin/1pctl | cut -d'=' -f2")
if err != nil {
return ""
}
+4
View File
@@ -29,6 +29,10 @@ func BindDomain() gin.HandlerFunc {
}
if domains != status.Value {
if LoadErrCode("err-domain") != 200 {
helper.ErrResponse(c, LoadErrCode("err-domain"))
return
}
helper.ErrorWithDetail(c, constant.CodeErrDomain, constant.ErrTypeInternalServer, errors.New("domain not allowed"))
return
}
+5
View File
@@ -30,6 +30,11 @@ var whiteUrlList = map[string]struct{}{
"/api/v1/waf/attack/stat": {},
"/api/v1/waf/config/website": {},
"/api/v1/monitor/stat": {},
"/api/v1/monitor/visitors": {},
"/api/v1/monitor/visitors/loc": {},
"/api/v1/monitor/qps": {},
}
func DemoHandle() gin.HandlerFunc {
+32
View File
@@ -0,0 +1,32 @@
package middleware
import (
"net/http"
"github.com/1Panel-dev/1Panel/backend/app/repo"
)
func LoadErrCode(errInfo string) int {
settingRepo := repo.NewISettingRepo()
codeVal, err := settingRepo.Get(settingRepo.WithByKey("NoAuthSetting"))
if err != nil {
return 500
}
switch codeVal.Value {
case "400":
return http.StatusBadRequest
case "401":
return http.StatusUnauthorized
case "403":
return http.StatusForbidden
case "404":
return http.StatusNotFound
case "408":
return http.StatusRequestTimeout
case "416":
return http.StatusRequestedRangeNotSatisfiable
default:
return http.StatusOK
}
}
+4
View File
@@ -35,6 +35,10 @@ func WhiteAllow() gin.HandlerFunc {
return
}
}
if LoadErrCode("err-ip") != 200 {
helper.ErrResponse(c, LoadErrCode("err-ip"))
return
}
helper.ErrorWithDetail(c, constant.CodeErrIP, constant.ErrTypeInternalServer, errors.New("IP address not allowed"))
}
}
+1 -1
View File
@@ -37,7 +37,7 @@ func PasswordExpired() gin.HandlerFunc {
return
}
if time.Now().After(expiredTime) {
helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, nil)
helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, err)
return
}
c.Next()
+1 -1
View File
@@ -23,7 +23,6 @@ func (a *AppRouter) InitRouter(Router *gin.RouterGroup) {
appRouter.GET("/details/:id", baseApi.GetAppDetailByID)
appRouter.POST("/install", baseApi.InstallApp)
appRouter.GET("/tags", baseApi.GetAppTags)
appRouter.GET("/installed/:appInstallId/versions", baseApi.GetUpdateVersions)
appRouter.POST("/installed/check", baseApi.CheckAppInstalled)
appRouter.POST("/installed/loadport", baseApi.LoadPort)
appRouter.POST("/installed/conninfo", baseApi.LoadConnInfo)
@@ -39,5 +38,6 @@ func (a *AppRouter) InitRouter(Router *gin.RouterGroup) {
appRouter.POST("/installed/params/update", baseApi.UpdateInstalled)
appRouter.POST("/installed/ignore", baseApi.IgnoreUpgrade)
appRouter.GET("/ignored/detail", baseApi.GetIgnoredApp)
appRouter.POST("/installed/update/versions", baseApi.GetUpdateVersions)
}
}
+5 -4
View File
@@ -35,12 +35,13 @@ func (s *DatabaseRouter) InitRouter(Router *gin.RouterGroup) {
cmdRouter.POST("/remote", baseApi.LoadRemoteAccess)
cmdRouter.GET("/options", baseApi.ListDBName)
cmdRouter.GET("/redis/persistence/conf", baseApi.LoadPersistenceConf)
cmdRouter.GET("/redis/status", baseApi.LoadRedisStatus)
cmdRouter.GET("/redis/conf", baseApi.LoadRedisConf)
cmdRouter.POST("/redis/persistence/conf", baseApi.LoadPersistenceConf)
cmdRouter.POST("/redis/status", baseApi.LoadRedisStatus)
cmdRouter.POST("/redis/conf", baseApi.LoadRedisConf)
cmdRouter.GET("/redis/exec", baseApi.RedisWsSsh)
cmdRouter.GET("/redis/check", baseApi.CheckHasCli)
cmdRouter.POST("/redis/install/cli", baseApi.InstallCli)
cmdRouter.POST("/redis/password", baseApi.ChangeRedisPassword)
cmdRouter.POST("/redis/backup/search", baseApi.RedisBackupList)
cmdRouter.POST("/redis/conf/update", baseApi.UpdateRedisConf)
cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf)
+5
View File
@@ -56,6 +56,11 @@ func (s *HostRouter) InitRouter(Router *gin.RouterGroup) {
hostRouter.GET("/command/tree", baseApi.SearchCommandTree)
hostRouter.POST("/command/update", baseApi.UpdateCommand)
hostRouter.GET("/command/redis", baseApi.ListRedisCommand)
hostRouter.POST("/command/redis", baseApi.SaveRedisCommand)
hostRouter.POST("/command/redis/search", baseApi.SearchRedisCommand)
hostRouter.POST("/command/redis/del", baseApi.DeleteRedisCommand)
hostRouter.POST("/tool", baseApi.GetToolStatus)
hostRouter.POST("/tool/init", baseApi.InitToolConfig)
hostRouter.POST("/tool/operate", baseApi.OperateTool)
+1
View File
@@ -24,6 +24,7 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
settingRouter.POST("/update", baseApi.UpdateSetting)
settingRouter.GET("/interface", baseApi.LoadInterfaceAddr)
settingRouter.POST("/menu/update", baseApi.UpdateMenu)
settingRouter.POST("/proxy/update", baseApi.UpdateProxy)
settingRouter.POST("/bind/update", baseApi.UpdateBindInfo)
settingRouter.POST("/port/update", baseApi.UpdatePort)
settingRouter.POST("/ssl/update", baseApi.UpdateSSL)
+9
View File
@@ -36,5 +36,14 @@ func (s *ToolboxRouter) InitRouter(Router *gin.RouterGroup) {
toolboxRouter.POST("/fail2ban/operate/sshd", baseApi.OperateSSHD)
toolboxRouter.POST("/fail2ban/update", baseApi.UpdateFail2BanConf)
toolboxRouter.POST("/fail2ban/update/byconf", baseApi.UpdateFail2BanConfByFile)
toolboxRouter.GET("/ftp/base", baseApi.LoadFtpBaseInfo)
toolboxRouter.POST("/ftp/log/search", baseApi.LoadFtpLogInfo)
toolboxRouter.POST("/ftp/operate", baseApi.OperateFtp)
toolboxRouter.POST("/ftp/search", baseApi.SearchFtp)
toolboxRouter.POST("/ftp", baseApi.CreateFtp)
toolboxRouter.POST("/ftp/update", baseApi.UpdateFtp)
toolboxRouter.POST("/ftp/del", baseApi.DeleteFtp)
toolboxRouter.POST("/ftp/sync", baseApi.SyncFtp)
}
}
+3 -2
View File
@@ -4,12 +4,13 @@ import (
"crypto/tls"
"encoding/gob"
"fmt"
"github.com/1Panel-dev/1Panel/backend/i18n"
"net"
"net/http"
"os"
"path"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/init/app"
"github.com/1Panel-dev/1Panel/backend/init/business"
@@ -43,9 +44,9 @@ func Start() {
session.Init()
gin.SetMode("debug")
cron.Run()
InitOthers()
business.Init()
hook.Init()
InitOthers()
rootRouter := router.Routers()
+4 -56
View File
@@ -2,19 +2,13 @@ package docker
import (
"context"
"github.com/compose-spec/compose-go/v2/loader"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/compose/v2/pkg/api"
"github.com/joho/godotenv"
"path"
"regexp"
"strings"
"time"
"github.com/compose-spec/compose-go/v2/loader"
"github.com/compose-spec/compose-go/v2/types"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/flags"
"github.com/docker/compose/v2/pkg/api"
"github.com/docker/compose/v2/pkg/compose"
"github.com/docker/docker/client"
"github.com/joho/godotenv"
)
type ComposeService struct {
@@ -22,47 +16,6 @@ type ComposeService struct {
project *types.Project
}
func UpComposeProject(project *types.Project) error {
for i, s := range project.Services {
s.CustomLabels = map[string]string{
api.ProjectLabel: project.Name,
api.ServiceLabel: s.Name,
api.VersionLabel: api.ComposeVersion,
api.WorkingDirLabel: project.WorkingDir,
api.ConfigFilesLabel: strings.Join(project.ComposeFiles, ","),
api.OneoffLabel: "False",
}
project.Services[i] = s
}
apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return err
}
var ops []command.CLIOption
ops = append(ops, command.WithAPIClient(apiClient), command.WithDefaultContextStoreConfig())
cli, err := command.NewDockerCli(ops...)
if err != nil {
return err
}
cliOp := flags.NewClientOptions()
if err = cli.Initialize(cliOp); err != nil {
return err
}
service := compose.NewComposeService(cli)
composeService := ComposeService{Service: service}
return composeService.Up(context.Background(), project, api.UpOptions{
Create: api.CreateOptions{
Timeout: getComposeTimeout(),
},
Start: api.StartOptions{
WaitTimeout: *getComposeTimeout(),
Wait: true,
},
})
}
func GetComposeProject(projectName, workDir string, yml []byte, env []byte, skipNormalization bool) (*types.Project, error) {
var configFiles []types.ConfigFile
configFiles = append(configFiles, types.ConfigFile{
@@ -93,11 +46,6 @@ func GetComposeProject(projectName, workDir string, yml []byte, env []byte, skip
return project, nil
}
func getComposeTimeout() *time.Duration {
timeout := time.Minute * time.Duration(10)
return &timeout
}
type ComposeProject struct {
Version string
Services map[string]Service `yaml:"services"`
+16 -4
View File
@@ -4,6 +4,7 @@ import (
"context"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
@@ -76,6 +77,17 @@ func (c Client) ListContainersByName(names []string) ([]types.Container, error)
}
return res, nil
}
func (c Client) ListAllContainers() ([]types.Container, error) {
var (
options container.ListOptions
)
options.All = true
containers, err := c.cli.ContainerList(context.Background(), options)
if err != nil {
return nil, err
}
return containers, nil
}
func (c Client) CreateNetwork(name string) error {
_, err := c.cli.NetworkCreate(context.Background(), name, types.NetworkCreate{
@@ -85,7 +97,7 @@ func (c Client) CreateNetwork(name string) error {
}
func (c Client) DeleteImage(imageID string) error {
if _, err := c.cli.ImageRemove(context.Background(), imageID, types.ImageRemoveOptions{Force: true}); err != nil {
if _, err := c.cli.ImageRemove(context.Background(), imageID, image.RemoveOptions{Force: true}); err != nil {
return err
}
return nil
@@ -105,7 +117,7 @@ func (c Client) PullImage(imageName string, force bool) error {
return nil
}
}
if _, err := c.cli.ImagePull(context.Background(), imageName, types.ImagePullOptions{}); err != nil {
if _, err := c.cli.ImagePull(context.Background(), imageName, image.PullOptions{}); err != nil {
return err
}
return nil
@@ -114,7 +126,7 @@ func (c Client) PullImage(imageName string, force bool) error {
func (c Client) GetImageIDByName(imageName string) (string, error) {
filter := filters.NewArgs()
filter.Add("reference", imageName)
list, err := c.cli.ImageList(context.Background(), types.ImageListOptions{
list, err := c.cli.ImageList(context.Background(), image.ListOptions{
Filters: filter,
})
if err != nil {
@@ -129,7 +141,7 @@ func (c Client) GetImageIDByName(imageName string) (string, error) {
func (c Client) CheckImageExist(imageName string) (bool, error) {
filter := filters.NewArgs()
filter.Add("reference", imageName)
list, err := c.cli.ImageList(context.Background(), types.ImageListOptions{
list, err := c.cli.ImageList(context.Background(), image.ListOptions{
Filters: filter,
})
if err != nil {

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