Compare commits

...
143 Commits
Author SHA1 Message Date
zhengkunwang 04edce94f9 feat: PHP 运行环境增加扩展源选择 (#2340) 2023-09-27 10:44:04 +08:00
ssongliu ccb81365f2 fix: 优化部分日志打印 (#2349) 2023-09-26 23:31:08 +08:00
ssongliu acf608cb7e fix: 解决部分容器创建失败的问题 (#2348) 2023-09-26 23:31:01 +08:00
zhengkunwang 2724708539 fix: 解决 Supervisor 停止之后一直同步状态的问题 (#2346) 2023-09-26 23:30:56 +08:00
zhengkunwang 50589a8d8d fix: 解决网站设置 PHP 源文之后没有立即生效的问题 (#2341) 2023-09-26 23:30:48 +08:00
ssongliu f06f487e8a fix: 修改容器操作提示信息 (#2324) 2023-09-16 10:02:16 +00:00
ssongliu 42dd668e1e fix: 解决恢复应用导致数据库备份文件丢失的问题 (#2323) 2023-09-16 09:00:16 +00:00
ssongliu 6cd00a072d fix: 优化版本排序 (#2322) 2023-09-16 08:30:16 +00:00
ssongliu 8faa3b6e51 fix: 修改数据库设置跳转路由 (#2320) 2023-09-16 06:36:15 +00:00
ssongliu 52198e9c0c fix: 数据库部分逻辑调整 (#2316) 2023-09-16 05:16:15 +00:00
ssongliu 173e3e9cc1 fix: 修改数据库备份逻辑 (#2315) 2023-09-15 10:48:14 +00:00
ssongliu 40c751cde2 fix: 修改数据库删除逻辑 (#2314) 2023-09-15 10:38:15 +00:00
zhengkunwang a3bf4513e1 fix: 修复日志文件下载不全的问题 (#2313) 2023-09-15 10:02:16 +00:00
zhengkunwang 4e7350aaae feat: 修改应用安装删除数据库逻辑 (#2311) 2023-09-15 08:46:13 +00:00
ssongliu 9ae935d06a fix: 解决数据库性能调整导致服务重启的问题 (#2308)
Refs #2301
2023-09-15 07:28:15 +00:00
ssongliu b6055eb058 fix: 解决数据冲突导致的升级失败问题 (#2306)
Refs #2290
2023-09-15 07:26:19 +00:00
wangdan-fit2cloud 0e158ffa2b fix: 修复导航样式冲突 (#2307)
#### 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.
2023-09-15 07:24:20 +00:00
zhengkunwang cc7260e121 style: 容器界面提示文字和操作保持一致 (#2305) 2023-09-15 07:22:18 +00:00
ssongliu 43b3ef47f3 fix: 解决数据库列表样式错乱的问题 (#2296) 2023-09-14 14:50:13 +00:00
zhengkunwang 21755223e1 fix: 解决限制安装的应用程序在首页还能继续安装的问题 (#2295) 2023-09-14 14:48:14 +00:00
ssongliu 08f0147d81 fix: 解决防火墙偶发的修改备注信息失败的问题 (#2292) 2023-09-14 14:44:14 +00:00
ssongliu 3bcb5800a2 fix: 解决快照恢复时应用重建无法拉起的问题 (#2287) 2023-09-14 06:06:13 +00:00
ssongliu aa057772dc fix: 解决同步计划任务中快照失败的问题 (#2286) 2023-09-14 03:38:12 +00:00
ssongliu bf32424458 fix: 解决 MySQL 8.x 切换数据库访问权限导致创建数据库失败的问题 (#2283) 2023-09-14 02:58:14 +00:00
ssongliu 5c0fb405d0 fix: 修改计划任务下载判断 (#2284) 2023-09-14 02:54:17 +00:00
ssongliu 279f8f0e6c fix: 解决 MySQL 8.x 切换数据库访问权限导致创建数据库失败的问题 (#2281) 2023-09-13 10:30:14 +00:00
zhengkunwang 8bc6b409d9 fix: 解决 mariadb 删除没有同步删除数据库表的问题 (#2280) 2023-09-13 10:18:14 +00:00
zhengkunwang 5a315a773a fix: 解决 mariadb 删除没有同步删除数据库表的问题 (#2279) 2023-09-13 09:58:14 +00:00
ssongliu b9befc72a3 fix: 解决计划任务备份所有应用失败的问题 (#2278) 2023-09-13 09:32:12 +00:00
ssongliu cab0c7938d fix: 解决容器切换端口选项时,仍然进行端口校验的问题 (#2277) 2023-09-13 09:12:14 +00:00
zhengkunwang a241fb32d2 style: 修改 openresty 非 80 443 端口提示 (#2276) 2023-09-13 08:58:15 +00:00
ssongliu 7b4b0bddff feat: 升级时暂停计划任务 (#2274) 2023-09-13 07:54:12 +00:00
zhengkunwang 65662b2e84 feat: 取消 HTTP 模式下申请证书的 Openresty 端口校验 (#2272) 2023-09-13 07:50:12 +00:00
ssongliu 6f250e63db fix: 解决 redis 修改密码报错的问题 (#2271) 2023-09-13 06:10:11 +00:00
ssongliu 47cfa0c730 fix: 解决快照上传失败的问题 (#2270) 2023-09-13 06:08:16 +00:00
wanghe-fit2cloud 9837c60a88 refactor: update readme 2023-09-12 22:43:08 +08:00
ssongliu e991bbe799 fix: 修改防火墙操作超时时间 (#2268) 2023-09-12 14:40:12 +00:00
zhengkunwang 418863543c fix: 解决应用停止之后不能启动的问题 (#2267) 2023-09-12 09:52:11 +00:00
Ikko Eltociear Ashimine c027ea21ca docs: Add Japanese README (#2265) 2023-09-12 17:50:37 +08:00
ssongliu 327e160ea3 fix: 解决应用、网站备份恢复失败的问题 (#2266) 2023-09-12 09:48:11 +00:00
ssongliu 3a0b3e952b fix: 容器列表页显示端口映射对应 ip (#2262) 2023-09-12 09:16:10 +00:00
ssongliu 16446ecaf3 feat: 优化防火墙响应速度 (#2261) 2023-09-12 09:14:15 +00:00
zhengkunwang ecb7a2ebee fix: 解决应用安装选择 mariadb 安装失败的问题 (#2264) 2023-09-12 09:12:11 +00:00
ssongliu 49d217e990 fix: 解决数据库切换导致的错乱样式问题 (#2259) 2023-09-12 02:54:10 +00:00
ssongliu d1d0ca92ca feat: 支持自定义概览页安全入口提示是否显示 (#2257) 2023-09-11 10:32:11 +00:00
ssongliu ac91480987 fix: 解决防火墙搜索报错的问题 (#2256) 2023-09-11 10:30:14 +00:00
ssongliu a364dbffbf fix: 数据库、计划任务下拉框样式调整 (#2255) 2023-09-11 10:28:19 +00:00
ssongliu 9aaf3bf435 fix: 解决不同 mysql 应用无法同名的问题 (#2252) 2023-09-11 06:56:10 +00:00
ssongliu eff3e8fcc5 fix: 数据库选择项样式优化 (#2235) 2023-09-10 09:20:13 +00:00
ssongliu 9d11e56074 fix: 修改 mongodb 和 postgresql 的数据库用户名获取 (#2232) 2023-09-10 09:18:16 +00:00
ssongliu 645e6bbb79 fix: 快照增加文件大小显示 (#2227) 2023-09-08 15:12:13 +00:00
ssongliu 9e5a452245 fix: 修改数据库配置文件不存在提示信息 (#2226) 2023-09-08 15:10:17 +00:00
ssongliu 6b908d2553 fix: 解决数据库创建重命名用户的问题 (#2222) 2023-09-07 13:36:10 +00:00
ssongliu 6e8f22a4a6 fix: 修改数据库创建权限校验规则 (#2220) 2023-09-07 09:42:10 +00:00
zhengkunwang f812d9f7cd fix: 解决删除 mariadb 的时候没有删除数据库表信息的问题 (#2219) 2023-09-07 09:20:10 +00:00
ssongliu a61487de9f fix: 数据库删除检查样式调整 (#2218) 2023-09-07 09:18:17 +00:00
ssongliu 9a43b52c62 fix: 容器创建增加 entrypoint 参数 (#2217) 2023-09-07 08:24:10 +00:00
ssongliu ad8bf09034 feat: 解决数据库同步时存在特殊字符导致失败的问题 (#2214) 2023-09-07 07:16:11 +00:00
zhengkunwang e75c9a0304 feat: 安装数据库应用增加与远程数据库名称校验 (#2212) 2023-09-07 03:38:10 +00:00
ssongliu c485fe2bd5 fix: 解决获取默认配置失败的问题 (#2208) 2023-09-06 13:22:12 +00:00
ssongliu add4fab794 feat: 数据库设置或远程服务器跳转返回时跳转到原路由 (#2207) 2023-09-06 13:20:16 +00:00
zhengkunwang 02dce0e3fa style: 修改部分弹窗 提示文字样式 (#2202) 2023-09-06 09:16:12 +00:00
ssongliu ae515b079c feat: 容器批量操作增加详情显示 (#2201) 2023-09-06 09:14:16 +00:00
ssongliu df5a1b3e40 fix: 登陆接口增加安全入口校验 (#2199) 2023-09-06 07:00:12 +00:00
zhengkunwang 278fac6d80 fix: 解决文件名带空格导致的设置权限失败的问题 (#2197)
Refs https://github.com/1Panel-dev/1Panel/issues/2149
2023-09-06 03:32:11 +00:00
ssongliu 3e33a4b16c fix: 修改数据库重复校验判断 (#2195) 2023-09-06 03:02:12 +00:00
ali-pay f0eaee82b6 fix: 解决路由按钮边框显示异常的问题 (#2196) 2023-09-06 10:54:24 +08:00
zhengkunwang 4d8118d9ab feat:网站目录支持三级 增加网站目录权限校验 (#2190)
Refs https://github.com/1Panel-dev/1Panel/issues/2140
2023-09-05 14:28:11 +00:00
ssongliu 31cd6691c9 feat: 容器日志行数增加所有选项 (#2191)
Refs #2143
2023-09-05 14:04:11 +00:00
zhengkunwang e509fb0571 feat:文件列表增加 uid 和 gid 显示 (#2192)
Refs https://github.com/1Panel-dev/1Panel/issues/2066
2023-09-05 10:34:11 +00:00
ssongliu e6522ded62 feat: MySQL 数据库选择项增加跳转 (#2189) 2023-09-05 09:34:11 +00:00
ssongliu 0a9fc26714 fix: 容器列表端口显示优化 (#2188)
Refs #2181
2023-09-05 09:32:15 +00:00
zhengkunwang ed3d587046 fix: 解决守护进程停止之后状态读取异常的问题 (#2186)
Refs https://github.com/1Panel-dev/1Panel/issues/2167
2023-09-05 08:12:10 +00:00
zhengkunwang 38b462eece style: 修改文件和应用商店样式 (#2184) 2023-09-05 06:20:10 +00:00
ssongliu c588c34955 fix: 容器列表样式调整 (#2179) 2023-09-04 14:24:14 +00:00
zhengkunwang fb00ba812a feat: 解决快速安装应用跳转失败问题 (#2178) 2023-09-04 14:22:18 +00:00
ssongliu 0c2d971d72 fix: 解决数据库切换时,状态标签显示不正常的问题 (#2177) 2023-09-04 09:40:14 +00:00
ssongliu ea17c451a6 fix: 解决合并导致的代码格式问题 (#2174) 2023-09-04 08:56:13 +00:00
zhengkunwang c00d715b87 feat: 解决安装应用失败的问题 (#2172) 2023-09-04 08:26:13 +00:00
ssongliu db443a9f82 fix: 修改 migration,解决升级失败的问题 (#2171) 2023-09-04 08:24:18 +00:00
zhengkunwang 5b00bfd1c4 feat: 优化应用安装流程 (#2168) 2023-09-04 07:58:13 +00:00
ssongliu dbf349ee9d fix: 解决防火墙筛选问题 (#2170) 2023-09-04 07:56:12 +00:00
CyJaySong f75744e252 🎨 容器列表添加网络地址列 (#2169) 2023-09-04 15:51:10 +08:00
ssongliu ecc444db9f fix: 修改远程数据库列表查询规则 (#2166) 2023-09-04 06:20:13 +00:00
wangdan-fit2cloud ba38402a24 fix: 调整按钮样式 (#2163)
#### 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.
2023-09-04 06:18:17 +00:00
ali-pay 1a6d237d0f fix: 解决页面像素不对齐的问题 (#2165) 2023-09-04 12:53:09 +08:00
ssongliu 78999f97db fix: 统一状态栏样式 (#2162) 2023-09-04 04:04:13 +00:00
ssongliu 7dacac8846 feat: 数据库密码加密存储 (#2146) 2023-09-01 15:10:14 +00:00
ssongliu 0c6a065994 fix: 解决概览页流量突增问题 (#2142)
Refs #2073
2023-09-01 08:42:12 +00:00
ssongliu dba3c6f2c1 fix: 面板设置样式调整 (#2141)
Refs #2079
2023-09-01 07:54:12 +00:00
ssongliu 149d44dbbe feat: 远程数据库支持添加 mariadb (#2139) 2023-09-01 04:28:12 +00:00
ali-pay 66c824a841 fix: 解决侧边栏收起状态下菜单长度不一致的问题 (#2137) 2023-08-31 23:14:15 +08:00
ssongliu a948ece792 fix: 数据库样式调整 (#2136) 2023-08-31 14:28:13 +00:00
ssongliu 3da1b22a1b fix: 解决同步数据库失败的问题 (#2133) 2023-08-31 09:22:12 +00:00
zhengkunwang ff0ed46554 feat: 应用安装支持选择 mariadb 和其他 mysql (#2130) 2023-08-31 07:14:11 +00:00
ssongliu 2ed2a7ed8c fix: 解决 deepin 获取 ssh 服务状态失败的问题 (#2129)
Refs #1646
2023-08-31 06:16:12 +00:00
ssongliu f9d5a445c7 feat: 防火墙支持多 ip 及多 ip 段 (#2122)
Refs #2046
2023-08-30 14:28:12 +00:00
ssongliu 9cecd66f3d feat: 防火墙列表增加状态、策略筛选项 (#2121)
Refs #1859
2023-08-30 06:56:11 +00:00
ssongliu 529a7e6ea6 fix: 1pctl 增加回滚功能 (#2110)
Refs #1660
2023-08-30 03:06:10 +00:00
ssongliu d3cd087b91 feat: 防火墙支持删除系统占用端口 (#2116) 2023-08-30 02:30:09 +00:00
ssongliu 4d2ca1f3a0 fix: 1pctl 增加非 root 提示信息 (#2112)
Refs #2100
2023-08-30 02:28:14 +00:00
zhengkunwang 7b1566f4ac feat: 修改删除文件的提示 (#2111)
Refs https://github.com/1Panel-dev/1Panel/issues/2086
2023-08-29 14:24:13 +00:00
zhengkunwang 7891f9225b feat: 应用安装支持选择多数据库 (#2109) 2023-08-29 14:22:18 +00:00
ssongliu 03f53f7042 fix: 解决版本升级导致无法创建数据库的问题 (#2108) 2023-08-29 09:18:16 +00:00
ssongliu 9f38a78512 fix: 修改数据库 migration (#2105) 2023-08-29 06:36:16 +00:00
zhengkunwang 49e3d45912 fix: 解决 Openresty 获取状态异常的问题 (#2104) 2023-08-29 06:32:17 +00:00
zhengkunwang d1e7b36243 feat: 修改 user-info 指令的输出 (#2103) 2023-08-29 03:40:15 +00:00
wanghe 3adb200585 Update README.md (#2102) 2023-08-29 11:02:45 +08:00
ssongliu ff141b21e2 feat: 多数据库接口适配 (#2094) 2023-08-29 02:50:15 +00:00
Allyxandallyxmiko-server b54c79e196 fix: 修复压缩包解压时可能的由于父目录不存在导致的解压失败的问题 (#2099)
Co-authored-by: allyxmiko-server <allyxmiko@gmail.com>
2023-08-28 22:37:48 +08:00
zhengkunwang b55b930353 feat: PHP 运行环境更新增加重建 php 应用功能 (#2095) 2023-08-28 14:18:15 +00:00
zhengkunwang 8b1098fc9d feat: 应用重建改为响应式 (#2091) 2023-08-28 10:10:16 +00:00
zhengkunwang ac0f697111 fix: 解决多容器应用升级失败的问题 (#2090) 2023-08-28 08:18:13 +00:00
zhengkunwang 7ff9c75bdf feat: 修改应用商店版本升级判断条件 (#2089) 2023-08-28 07:32:12 +00:00
zhengkunwang 9f0d957145 feat: 网站日志支持读取 10M 以上文件 (#2072) 2023-08-25 15:42:14 +00:00
zhengkunwang 680e93dcd4 feat: 网站支持不同域名使用同一个端口 (#2071)
Refs https://github.com/1Panel-dev/1Panel/issues/1997
2023-08-25 15:40:17 +00:00
zhengkunwang f546ffee90 feat: 应用商店增加版本控制 (#2068) 2023-08-25 10:44:11 +00:00
ssongliu 6a8bd490bf feat: 计划任务支持系统快照 (#2061)
Refs #1503 #1480 #1291
2023-08-24 14:26:16 +00:00
zhengkunwang 684f20a5dc feat: Openresty 支持非 80 443 端口安装 (#2059) 2023-08-24 10:20:15 +00:00
zhengkunwang a3c07dd3d3 fix: 解决打开进程守护页面报错的问题 (#2057)
Refs https://github.com/1Panel-dev/1Panel/issues/2051
2023-08-24 08:16:45 +00:00
ssongliu ca0368c931 feat: 放开 1panel-network 按钮禁用 (#2052) 2023-08-24 04:02:13 +00:00
zhengkunwang 59eff6e27f feat: 应用安装、升级、卸载之前支持执行脚本 (#2042) 2023-08-23 14:48:14 +00:00
ssongliu 63ae17372d feat: 计划任务支持备份应用 (#2040)
Refs #1788 #1713
2023-08-23 14:44:14 +00:00
ssongliu f196d029cb feat: 重构快照功能,取消快照过程 loading (#2039) 2023-08-23 09:16:18 +00:00
zhengkunwang f9bf6b69fb feat: 增加强制删除 openresty 功能 (#2030) 2023-08-23 02:42:13 +00:00
ssongliu 5e47656542 fix: 解决数据库用户名显示错误的问题 (#2028) 2023-08-22 08:32:12 +00:00
zhengkunwang d72d2c1167 fix: 解决运行环境网站切换PHP版本导致已安装应用消失的问题 (#2026) 2023-08-22 07:38:11 +00:00
ssongliu 24c87905b1 fix: 解决清空容器日志导致的 CPU 占用异常问题 (#2019)
Refs #1999
2023-08-21 09:00:12 +00:00
ssongliu fe7fed02ee fix: 服务器地址支持设置为空 (#2016) 2023-08-21 08:14:12 +00:00
ssongliu f4c3aafced fix: 解决数据库用户名超长导致用户创建失败的问题 (#2011) 2023-08-21 03:32:11 +00:00
huailei c1db5886ff perf: 防止message重复弹出 (#1996) 2023-08-18 17:22:32 +08:00
ssongliu 5dac95c3de fix: 移除容器日志中的 ANSI 转义序列 (#1991)
Refs #1655
2023-08-17 12:24:09 +00:00
ssongliu f8f231253d fix: 解决防火墙多端口创建失败的问题 (#1988) 2023-08-17 08:58:10 +00:00
ssongliu da8a06909b fix: 延长进程状态获取等待时间 (#1986) 2023-08-17 08:10:09 +00:00
ssongliu e67d9055aa fix: 解决进程状态一直加载中的问题 (#1983)
Refs #1968
2023-08-17 06:46:09 +00:00
ssongliu c3134e4c2a fix: 解决网站反代编辑时回显错误的问题 (#1980)
Refs #1976
2023-08-17 03:32:09 +00:00
wangdan-fit2cloud bdd36b3fbc fix: 移动端样式优化 (#1979)
#### 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.
2023-08-17 03:02:09 +00:00
ssongliu d04c4bd425 fix: 解决暂停进程被重新拉起的问题 (#1977)
Refs #1964
2023-08-16 15:44:11 +00:00
ssongliu 8135f0f598 fix: 解决获取远程数据库失败的问题 (#1974)
Refs #1925
2023-08-16 15:36:11 +00:00
ssongliu 6be8bc0c50 fix: 解决容器编排创建日志加载慢的问题 (#1973)
Refs #1950
2023-08-16 15:34:15 +00:00
ssongliu 8433822021 fix: 修改网站重定向列表样式 (#1975)
Refs #1971
2023-08-16 15:32:12 +00:00
wangdan-fit2cloud 65194ae22b 优化移动端样式 (#1967)
#### 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.
2023-08-16 03:34:10 +00:00
ssongliu 89faf01391 feat: 防火墙支持设置备注信息 (#1962)
Refs #1930 #1121 #1642
2023-08-15 14:48:11 +00:00
222 changed files with 8568 additions and 3821 deletions
+3 -3
View File
@@ -1,12 +1,12 @@
[README_EN.md](README_EN.md)
<p align="center"><a href="https://1panel.cn"><img src="http://1panel.oss-cn-hangzhou.aliyuncs.com/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>现代化、开源的 Linux 服务器运维管理面板</b></p>
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://shields.io/github/license/1Panel-dev/1Panel" alt="License: GPL v3"></a>
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://shields.io/github/license/1Panel-dev/1Panel?color=%231890FF" alt="License: GPL v3"></a>
<a href="https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard"><img src="https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef" alt="Codacy"></a>
<a href="https://github.com/1Panel-dev/1Panel/releases"><img src="https://img.shields.io/github/v/release/1Panel-dev/1Panel" alt="GitHub release"></a>
<a href="https://github.com/1Panel-dev/1Panel"><img src="https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square" alt="Stars"></a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel.svg?type=shield" alt="FOSSA Status"></a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel?ref=badge_shield"><img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2F1Panel-dev%2F1Panel.svg?type=shield" alt="FOSSA Status"></a><br>
[<a href="README_EN.md">English</a>] | [<a href="README_JP.md">日本語</a>]
</p>
------------------------------
+2 -2
View File
@@ -1,11 +1,11 @@
[中文 README.md](README.md)
<p align="center"><a href="https://1panel.cn"><img src="http://1panel.oss-cn-hangzhou.aliyuncs.com/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>Modern and Open-Source Linux Server Operation and Management Panel</b></p>
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://shields.io/github/license/1Panel-dev/1Panel" alt="License: GPL v3"></a>
<a href="https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard"><img src="https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef" alt="Codacy"></a>
<a href="https://github.com/1Panel-dev/1Panel/releases"><img src="https://img.shields.io/github/v/release/1Panel-dev/1Panel" alt="GitHub release"></a>
<a href="https://github.com/1Panel-dev/1Panel"><img src="https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square" alt="Stars"></a>
<a href="https://github.com/1Panel-dev/1Panel"><img src="https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square" alt="Stars"></a><br>
[<a href="README.md">中文</a>] | [<a href="README_JP.md">日本語</a>]
</p>
------------------------------
+72
View File
@@ -0,0 +1,72 @@
<p align="center"><a href="https://1panel.cn"><img src="http://1panel.oss-cn-hangzhou.aliyuncs.com/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
<p align="center"><b>モダンでオープンソースの Linux サーバー運用管理パネル</b></p>
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://shields.io/github/license/1Panel-dev/1Panel" alt="License: GPL v3"></a>
<a href="https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard"><img src="https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef" alt="Codacy"></a>
<a href="https://github.com/1Panel-dev/1Panel/releases"><img src="https://img.shields.io/github/v/release/1Panel-dev/1Panel" alt="GitHub release"></a>
<a href="https://github.com/1Panel-dev/1Panel"><img src="https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square" alt="Stars"></a><br>
[<a href="README.md">中文</a>] | [<a href="README_EN.md">English</a>]
</p>
------------------------------
1Panel はモダンでオープンソースの Linux サーバー操作と管理パネルです:
- **素早いウェブサイト構築**: Wordpress および [Halo](https://github.com/halo-dev/halo/) と深く統合されており、ドメイン名のバインディング、SSL 証明書の設定など、ワンクリックで解決できます;
- **効率的なマネジメント**: アプリケーション管理、ホスト監視、ファイル管理、データベース管理、コンテナ管理など、web インターフェイスを通じて Linux サーバーを簡単に管理できます;
- **安全性と信頼性**: ファイアウォールとセキュリティ監査機能により、脆弱性の露出を最小限に抑えます;
- **ワンクリックバックアップ**: ワンクリックでのバックアップと復元をサポートし、バックアップデータはクラウドに保存され、失われることはありません。
## UI ディスプレイ
![UI Display](https://resource.fit2cloud.com/1panel/img/overview_en.png)
## クイックスタート
**オンラインでも**
- アドレス: <https://demo.1panel.cn/>
- ユーザネーム: demo
- パスワード: 1panel
**ワンクリックインストール**
ワンクリックで 1Panel をインストールするには、次のコマンドを実行します:
```sh
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
```
**学習教材**
- [オンラインドキュメント](https://1panel.cn/docs/)
- [ビデオ教材](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
## コミュニティ
ご質問やご提案がございましたら、GitHub Issue にご投稿いただくか、WeChat グループにご参加ください。
**WeChat グループ**
<img src="https://1panel.cn/img/wechat-group.jpg" width="156" height="156"/>
## セキュリティ情報
セキュリティ上の問題を発見された場合は、下記までご連絡ください:
- メール: support@fit2cloud.com
- 電話番号: 400-052-0755
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=1Panel-dev/1Panel&type=Date)](https://star-history.com/#1Panel-dev/1Panel&Date)
## License
Copyright (c) 2014-2023 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
<https://www.gnu.org/licenses/gpl-3.0.html>
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
+45 -26
View File
@@ -1,7 +1,6 @@
package v1
import (
"errors"
"reflect"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
@@ -14,8 +13,8 @@ import (
)
// @Tags App
// @Summary List app installed
// @Description 获取已安装应用列表
// @Summary Page app installed
// @Description 分页获取已安装应用列表
// @Accept json
// @Param request body request.AppInstalledSearch true "request"
// @Success 200
@@ -47,21 +46,41 @@ func (b *BaseApi) SearchAppInstalled(c *gin.Context) {
}
}
// @Tags App
// @Summary List app installed
// @Description 获取已安装应用列表
// @Accept json
// @Success 200 array dto.AppInstallInfo
// @Security ApiKeyAuth
// @Router /apps/installed/list [get]
func (b *BaseApi) ListAppInstalled(c *gin.Context) {
list, err := appInstallService.GetInstallList()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags App
// @Summary Check app installed
// @Description 检查应用安装情况
// @Accept json
// @Param key path string true "request"
// @Param request body request.AppInstalledInfo true "request"
// @Success 200 {object} response.AppInstalledCheck
// @Security ApiKeyAuth
// @Router /apps/installed/check/:key [get]
// @Router /apps/installed/check [post]
func (b *BaseApi) CheckAppInstalled(c *gin.Context) {
key, ok := c.Params.Get("key")
if !ok {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error key in path"))
var req request.AppInstalledInfo
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
checkData, err := appInstallService.CheckExist(key)
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
checkData, err := appInstallService.CheckExist(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -73,17 +92,17 @@ func (b *BaseApi) CheckAppInstalled(c *gin.Context) {
// @Summary Search app port by key
// @Description 获取应用端口
// @Accept json
// @Param key path string true "request"
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200 {integer} port
// @Security ApiKeyAuth
// @Router /apps/installed/loadport/:key [get]
// @Router /apps/installed/loadport [post]
func (b *BaseApi) LoadPort(c *gin.Context) {
key, ok := c.Params.Get("key")
if !ok {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error key in path"))
var req dto.OperationWithNameAndType
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
port, err := appInstallService.LoadPort(key)
port, err := appInstallService.LoadPort(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -95,17 +114,17 @@ func (b *BaseApi) LoadPort(c *gin.Context) {
// @Summary Search app password by key
// @Description 获取应用连接信息
// @Accept json
// @Param key path string true "request"
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200 {string} response.DatabaseConn
// @Security ApiKeyAuth
// @Router /apps/installed/conninfo/:key [get]
func (b *BaseApi) LoadConnInfo(c *gin.Context) {
key, ok := c.Params.Get("key")
if !ok {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error key in path"))
var req dto.OperationWithNameAndType
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
conn, err := appInstallService.LoadConnInfo(key)
conn, err := appInstallService.LoadConnInfo(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -243,17 +262,17 @@ func (b *BaseApi) ChangeAppPort(c *gin.Context) {
// @Summary Search default config by key
// @Description 通过 key 获取应用默认配置
// @Accept json
// @Param key path string true "request"
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200 {string} content
// @Security ApiKeyAuth
// @Router /apps/installed/conf/:key [get]
// @Router /apps/installed/conf [post]
func (b *BaseApi) GetDefaultConfig(c *gin.Context) {
key := c.Param("key")
if key == "" {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInternalServer, nil)
var req dto.OperationWithNameAndType
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
content, err := appInstallService.GetDefaultConfigByKey(key)
content, err := appInstallService.GetDefaultConfigByKey(req.Type, req.Name)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
+16 -2
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/model"
@@ -20,6 +22,7 @@ type BaseApi struct{}
// @Param request body dto.Login true "request"
// @Success 200 {object} dto.UserLoginInfo
// @Router /auth/login [post]
// @Header 200 {string} EntranceCode "安全入口"
func (b *BaseApi) Login(c *gin.Context) {
var req dto.Login
if err := c.ShouldBindJSON(&req); err != nil {
@@ -32,8 +35,13 @@ func (b *BaseApi) Login(c *gin.Context) {
return
}
}
entranceItem := c.Request.Header.Get("EntranceCode")
var entrance []byte
if len(entranceItem) != 0 {
entrance, _ = base64.StdEncoding.DecodeString(entranceItem)
}
user, err := authService.Login(c, req)
user, err := authService.Login(c, req, string(entrance))
go saveLoginLogs(c, err)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -49,6 +57,7 @@ func (b *BaseApi) Login(c *gin.Context) {
// @Param request body dto.MFALogin true "request"
// @Success 200 {object} dto.UserLoginInfo
// @Router /auth/mfalogin [post]
// @Header 200 {string} EntranceCode "安全入口"
func (b *BaseApi) MFALogin(c *gin.Context) {
var req dto.MFALogin
if err := c.ShouldBindJSON(&req); err != nil {
@@ -59,8 +68,13 @@ func (b *BaseApi) MFALogin(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
entranceItem := c.Request.Header.Get("EntranceCode")
var entrance []byte
if len(entranceItem) != 0 {
entrance, _ = base64.StdEncoding.DecodeString(entranceItem)
}
user, err := authService.MFALogin(c, req)
user, err := authService.MFALogin(c, req, string(entrance))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
+3 -3
View File
@@ -335,7 +335,7 @@ func (b *BaseApi) Backup(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
case "mysql":
case "mysql", "mariadb":
if err := backupService.MysqlBackup(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -383,7 +383,7 @@ func (b *BaseApi) Recover(c *gin.Context) {
req.File = downloadPath
}
switch req.Type {
case "mysql":
case "mysql", "mariadb":
if err := backupService.MysqlRecover(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -428,7 +428,7 @@ func (b *BaseApi) RecoverByUpload(c *gin.Context) {
}
switch req.Type {
case "mysql":
case "mysql", "mariadb":
if err := backupService.MysqlRecoverByUpload(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -9,16 +9,16 @@ import (
)
// @Tags Database
// @Summary Create remote database
// @Summary Create database
// @Description 创建远程数据库
// @Accept json
// @Param request body dto.RemoteDBCreate true "request"
// @Param request body dto.DatabaseCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/remote [post]
// @x-panel-log {"bodyKeys":["name", "type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建远程数据库 [name][type]","formatEN":"create remote database [name][type]"}
func (b *BaseApi) CreateRemoteDB(c *gin.Context) {
var req dto.RemoteDBCreate
// @Router /databases/db [post]
// @x-panel-log {"bodyKeys":["name", "type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建远程数据库 [name][type]","formatEN":"create database [name][type]"}
func (b *BaseApi) CreateDatabase(c *gin.Context) {
var req dto.DatabaseCreate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
@@ -27,7 +27,7 @@ func (b *BaseApi) CreateRemoteDB(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := remoteDBService.Create(req); err != nil {
if err := databaseService.Create(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
@@ -35,16 +35,16 @@ func (b *BaseApi) CreateRemoteDB(c *gin.Context) {
}
// @Tags Database
// @Summary Check remote database
// @Summary Check database
// @Description 检测远程数据库连接性
// @Accept json
// @Param request body dto.RemoteDBCreate true "request"
// @Param request body dto.DatabaseCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/remote/check [post]
// @x-panel-log {"bodyKeys":["name", "type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"检测远程数据库 [name][type] 连接性","formatEN":"check if remote database [name][type] is connectable"}
func (b *BaseApi) CheckeRemoteDB(c *gin.Context) {
var req dto.RemoteDBCreate
// @Router /databases/db/check [post]
// @x-panel-log {"bodyKeys":["name", "type"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"检测远程数据库 [name][type] 连接性","formatEN":"check if database [name][type] is connectable"}
func (b *BaseApi) CheckDatabase(c *gin.Context) {
var req dto.DatabaseCreate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
@@ -53,25 +53,25 @@ func (b *BaseApi) CheckeRemoteDB(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
helper.SuccessWithData(c, remoteDBService.CheckeRemoteDB(req))
helper.SuccessWithData(c, databaseService.CheckDatabase(req))
}
// @Tags Database
// @Summary Page remote databases
// @Summary Page databases
// @Description 获取远程数据库列表分页
// @Accept json
// @Param request body dto.RemoteDBSearch true "request"
// @Param request body dto.DatabaseSearch true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /databases/remote/search [post]
func (b *BaseApi) SearchRemoteDB(c *gin.Context) {
var req dto.RemoteDBSearch
// @Router /databases/db/search [post]
func (b *BaseApi) SearchDatabase(c *gin.Context) {
var req dto.DatabaseSearch
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
total, list, err := remoteDBService.SearchWithPage(req)
total, list, err := databaseService.SearchWithPage(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -84,18 +84,18 @@ func (b *BaseApi) SearchRemoteDB(c *gin.Context) {
}
// @Tags Database
// @Summary List remote databases
// @Summary List databases
// @Description 获取远程数据库列表
// @Success 200 {array} dto.RemoteDBOption
// @Success 200 {array} dto.DatabaseOption
// @Security ApiKeyAuth
// @Router /databases/remote/list/:type [get]
func (b *BaseApi) ListRemoteDB(c *gin.Context) {
// @Router /databases/db/list/:type [get]
func (b *BaseApi) ListDatabase(c *gin.Context) {
dbType, err := helper.GetStrParamByKey(c, "type")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
list, err := remoteDBService.List(dbType)
list, err := databaseService.List(dbType)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -105,18 +105,18 @@ func (b *BaseApi) ListRemoteDB(c *gin.Context) {
}
// @Tags Database
// @Summary Get remote databases
// @Summary Get databases
// @Description 获取远程数据库
// @Success 200 {object} dto.RemoteDBInfo
// @Success 200 {object} dto.DatabaseInfo
// @Security ApiKeyAuth
// @Router /databases/remote/:name [get]
func (b *BaseApi) GetRemoteDB(c *gin.Context) {
// @Router /databases/db/:name [get]
func (b *BaseApi) GetDatabase(c *gin.Context) {
name, err := helper.GetStrParamByKey(c, "name")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
data, err := remoteDBService.Get(name)
data, err := databaseService.Get(name)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -126,15 +126,15 @@ func (b *BaseApi) GetRemoteDB(c *gin.Context) {
}
// @Tags Database
// @Summary Delete remote database
// @Summary Delete database
// @Description 删除远程数据库
// @Accept json
// @Param request body dto.OperateByID true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/remote/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"databases","output_column":"name","output_value":"names"}],"formatZH":"删除远程数据库 [names]","formatEN":"delete remote database [names]"}
func (b *BaseApi) DeleteRemoteDB(c *gin.Context) {
// @Router /databases/db/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"ids","isList":true,"db":"databases","output_column":"name","output_value":"names"}],"formatZH":"删除远程数据库 [names]","formatEN":"delete database [names]"}
func (b *BaseApi) DeleteDatabase(c *gin.Context) {
var req dto.OperateByID
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
@@ -145,7 +145,7 @@ func (b *BaseApi) DeleteRemoteDB(c *gin.Context) {
return
}
if err := remoteDBService.Delete(req.ID); err != nil {
if err := databaseService.Delete(req.ID); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
@@ -153,16 +153,16 @@ func (b *BaseApi) DeleteRemoteDB(c *gin.Context) {
}
// @Tags Database
// @Summary Update remote database
// @Summary Update database
// @Description 更新远程数据库
// @Accept json
// @Param request body dto.RemoteDBUpdate true "request"
// @Param request body dto.DatabaseUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/remote/update [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新远程数据库 [name]","formatEN":"update remote database [name]"}
func (b *BaseApi) UpdateRemoteDB(c *gin.Context) {
var req dto.RemoteDBUpdate
// @Router /databases/db/update [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新远程数据库 [name]","formatEN":"update database [name]"}
func (b *BaseApi) UpdateDatabase(c *gin.Context) {
var req dto.DatabaseUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
@@ -172,7 +172,7 @@ func (b *BaseApi) UpdateRemoteDB(c *gin.Context) {
return
}
if err := remoteDBService.Update(req); err != nil {
if err := databaseService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
+50 -16
View File
@@ -143,7 +143,7 @@ func (b *BaseApi) ChangeMysqlAccess(c *gin.Context) {
// @Router /databases/variables/update [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"调整 mysql 数据库性能参数","formatEN":"adjust mysql database performance parameters"}
func (b *BaseApi) UpdateMysqlVariables(c *gin.Context) {
var req []dto.MysqlVariablesUpdate
var req dto.MysqlVariablesUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
@@ -232,15 +232,21 @@ func (b *BaseApi) ListDBName(c *gin.Context) {
// @Tags Database Mysql
// @Summary Load mysql database from remote
// @Description 从服务器获取
// @Accept json
// @Param request body dto.MysqlLoadDB true "request"
// @Security ApiKeyAuth
// @Router /databases/load/:from [get]
// @Router /databases/load [post]
func (b *BaseApi) LoadDBFromRemote(c *gin.Context) {
from, err := helper.GetStrParamByKey(c, "from")
if err != nil {
var req dto.MysqlLoadDB
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := mysqlService.LoadFromRemote(from); err != nil {
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := mysqlService.LoadFromRemote(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
@@ -252,12 +258,12 @@ func (b *BaseApi) LoadDBFromRemote(c *gin.Context) {
// @Summary Check before delete mysql database
// @Description Mysql 数据库删除前检查
// @Accept json
// @Param request body dto.OperateByID true "request"
// @Param request body dto.MysqlDBDeleteCheck true "request"
// @Success 200 {array} string
// @Security ApiKeyAuth
// @Router /databases/del/check [post]
func (b *BaseApi) DeleteCheckMysql(c *gin.Context) {
var req dto.OperateByID
var req dto.MysqlDBDeleteCheck
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
@@ -267,7 +273,7 @@ func (b *BaseApi) DeleteCheckMysql(c *gin.Context) {
return
}
apps, err := mysqlService.DeleteCheck(req.ID)
apps, err := mysqlService.DeleteCheck(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -308,11 +314,18 @@ func (b *BaseApi) DeleteMysql(c *gin.Context) {
// @Tags Database Mysql
// @Summary Load mysql base info
// @Description 获取 mysql 基础信息
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200 {object} dto.DBBaseInfo
// @Security ApiKeyAuth
// @Router /databases/baseinfo [get]
// @Router /databases/baseinfo [post]
func (b *BaseApi) LoadBaseinfo(c *gin.Context) {
data, err := mysqlService.LoadBaseInfo()
var req dto.OperationWithNameAndType
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
data, err := mysqlService.LoadBaseInfo(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -346,11 +359,18 @@ func (b *BaseApi) LoadDatabaseFile(c *gin.Context) {
// @Tags Database Mysql
// @Summary Load mysql remote access
// @Description 获取 mysql 远程访问权限
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200 {boolean} isRemote
// @Security ApiKeyAuth
// @Router /databases/remote [get]
// @Router /databases/remote [post]
func (b *BaseApi) LoadRemoteAccess(c *gin.Context) {
isRemote, err := mysqlService.LoadRemoteAccess()
var req dto.OperationWithNameAndType
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
isRemote, err := mysqlService.LoadRemoteAccess(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -362,11 +382,18 @@ func (b *BaseApi) LoadRemoteAccess(c *gin.Context) {
// @Tags Database Mysql
// @Summary Load mysql status info
// @Description 获取 mysql 状态信息
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200 {object} dto.MysqlStatus
// @Security ApiKeyAuth
// @Router /databases/status [get]
// @Router /databases/status [post]
func (b *BaseApi) LoadStatus(c *gin.Context) {
data, err := mysqlService.LoadStatus()
var req dto.OperationWithNameAndType
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
data, err := mysqlService.LoadStatus(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -378,11 +405,18 @@ func (b *BaseApi) LoadStatus(c *gin.Context) {
// @Tags Database Mysql
// @Summary Load mysql variables info
// @Description 获取 mysql 性能参数信息
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200 {object} dto.MysqlVariables
// @Security ApiKeyAuth
// @Router /databases/variables [get]
// @Router /databases/variables [post]
func (b *BaseApi) LoadVariables(c *gin.Context) {
data, err := mysqlService.LoadVariables()
var req dto.OperationWithNameAndType
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
data, err := mysqlService.LoadVariables(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
+2 -2
View File
@@ -92,13 +92,13 @@ func (b *BaseApi) UpdateRedisConf(c *gin.Context) {
// @Summary Change redis password
// @Description 更新 redis 密码
// @Accept json
// @Param request body dto.ChangeDBInfo true "request"
// @Param request body dto.ChangeRedisPass true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/redis/password [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"修改 redis 数据库密码","formatEN":"change the password of the redis database"}
func (b *BaseApi) ChangeRedisPassword(c *gin.Context) {
var req dto.ChangeDBInfo
var req dto.ChangeRedisPass
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
+1 -1
View File
@@ -22,7 +22,7 @@ var (
dockerService = service.NewIDockerService()
mysqlService = service.NewIMysqlService()
remoteDBService = service.NewIRemoteDBService()
databaseService = service.NewIDatabaseService()
redisService = service.NewIRedisService()
cronjobService = service.NewICronjobService()
+29
View File
@@ -38,6 +38,10 @@ func (b *BaseApi) SearchFirewallRule(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
total, list, err := firewallService.SearchWithPage(req)
if err != nil {
@@ -156,6 +160,31 @@ func (b *BaseApi) BatchOperateRule(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Firewall
// @Summary Update rule description
// @Description 更新防火墙描述
// @Accept json
// @Param request body dto.UpdateFirewallDescription true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /hosts/firewall/update/description [post]
func (b *BaseApi) UpdateFirewallDescription(c *gin.Context) {
var req dto.UpdateFirewallDescription
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := firewallService.UpdateDescription(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Firewall
// @Summary Create group
// @Description 更新端口防火墙规则
-10
View File
@@ -189,16 +189,6 @@ func (b *BaseApi) GetProcess(c *gin.Context) {
helper.SuccessWithData(c, configs)
}
// @Tags Host tool
// @Summary Load Supervisor process status
// @Description 获取 Supervisor 进程状态
// @Success 200 {array} response.ProcessStatus
// @Security ApiKeyAuth
// @Router /host/tool/supervisor/process/load [post]
func (b *BaseApi) LoadProcessStatus(c *gin.Context) {
helper.SuccessWithData(c, hostToolService.LoadProcessStatus())
}
// @Tags Host tool
// @Summary Get Supervisor process config
// @Description 操作 Supervisor 进程文件
+26
View File
@@ -60,6 +60,32 @@ func (b *BaseApi) ImportSnapshot(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags System Setting
// @Summary Load Snapshot status
// @Description 获取快照状态
// @Accept json
// @Param request body dto.OperateByID true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/snapshot/status [post]
func (b *BaseApi) LoadSnapShotStatus(c *gin.Context) {
var req dto.OperateByID
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
data, err := snapshotService.LoadSnapShotStatus(req.ID)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags System Setting
// @Summary Update snapshot description
// @Description 更新快照描述信息
+22
View File
@@ -912,3 +912,25 @@ func (b *BaseApi) UpdateRedirectConfigFile(c *gin.Context) {
}
helper.SuccessWithOutData(c)
}
// @Tags Website
// @Summary Get website dir
// @Description 获取网站目录配置
// @Accept json
// @Param request body request.WebsiteCommonReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/dir [post]
func (b *BaseApi) GetDirConfig(c *gin.Context) {
var req request.WebsiteCommonReq
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
res, err := websiteService.LoadWebsiteDirConfig(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
+8 -1
View File
@@ -67,7 +67,8 @@ type LocalAppInstallDefine struct {
}
type ExtraProperties struct {
Tags []Tag `json:"tags"`
Tags []Tag `json:"tags"`
Version string `json:"version"`
}
type AppProperty struct {
@@ -132,3 +133,9 @@ var AppToolMap = map[string]string{
"mysql": "phpmyadmin",
"redis": "redis-commander",
}
type AppInstallInfo struct {
ID uint `json:"id"`
Key string `json:"key"`
Name string `json:"name"`
}
+2 -9
View File
@@ -21,25 +21,18 @@ type BackupInfo struct {
Vars string `json:"vars"`
}
type BackupSearch struct {
PageInfo
Type string `json:"type" validate:"required,oneof=website mysql"`
Name string `json:"name" validate:"required"`
DetailName string `json:"detailName"`
}
type BackupSearchFile struct {
Type string `json:"type" validate:"required"`
}
type CommonBackup struct {
Type string `json:"type" validate:"required,oneof=app mysql redis website"`
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"`
Name string `json:"name"`
DetailName string `json:"detailName"`
}
type CommonRecover struct {
Source string `json:"source" validate:"required,oneof=OSS S3 SFTP MINIO LOCAL COS KODO OneDrive"`
Type string `json:"type" validate:"required,oneof=app mysql redis website"`
Type string `json:"type" validate:"required,oneof=app mysql mariadb redis website"`
Name string `json:"name"`
DetailName string `json:"detailName"`
File string `json:"file"`
+3 -1
View File
@@ -24,7 +24,8 @@ type ContainerInfo struct {
State string `json:"state"`
RunTime string `json:"runTime"`
Ports []string `json:"ports"`
Network []string `json:"network"`
Ports []string `json:"ports"`
IsFromApp bool `json:"isFromApp"`
IsFromCompose bool `json:"isFromCompose"`
@@ -44,6 +45,7 @@ type ContainerOperate struct {
PublishAllPorts bool `json:"publishAllPorts"`
ExposedPorts []PortHelper `json:"exposedPorts"`
Cmd []string `json:"cmd"`
Entrypoint []string `json:"entrypoint"`
CPUShares int64 `json:"cpuShares"`
NanoCPUs float64 `json:"nanoCPUs"`
Memory float64 `json:"memory"`
+3
View File
@@ -14,6 +14,7 @@ type CronjobCreate struct {
Script string `json:"script"`
ContainerName string `json:"containerName"`
AppID string `json:"appID"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBName string `json:"dbName"`
@@ -36,6 +37,7 @@ type CronjobUpdate struct {
Script string `json:"script"`
ContainerName string `json:"containerName"`
AppID string `json:"appID"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBName string `json:"dbName"`
@@ -79,6 +81,7 @@ type CronjobInfo struct {
Script string `json:"script"`
ContainerName string `json:"containerName"`
AppID string `json:"appID"`
Website string `json:"website"`
ExclusionRules string `json:"exclusionRules"`
DBName string `json:"dbName"`
+101 -39
View File
@@ -4,10 +4,10 @@ import "time"
type MysqlDBSearch struct {
PageInfo
Info string `json:"info"`
From string `json:"from"`
OrderBy string `json:"orderBy"`
Order string `json:"order"`
Info string `json:"info"`
Database string `json:"database" validate:"required"`
OrderBy string `json:"orderBy"`
Order string `json:"order"`
}
type MysqlDBInfo struct {
@@ -25,14 +25,17 @@ type MysqlDBInfo struct {
}
type MysqlOption struct {
ID uint `json:"id"`
Name string `json:"name"`
From string `json:"from"`
ID uint `json:"id"`
From string `json:"from"`
Type string `json:"type"`
Database string `json:"database"`
Name string `json:"name"`
}
type MysqlDBCreate struct {
Name string `json:"name" validate:"required"`
From string `json:"from" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Database string `json:"database" validate:"required"`
Format string `json:"format" validate:"required,oneof=utf8mb4 utf8 gbk big5"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
@@ -40,10 +43,24 @@ type MysqlDBCreate struct {
Description string `json:"description"`
}
type MysqlLoadDB struct {
From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
Database string `json:"database" validate:"required"`
}
type MysqlDBDeleteCheck struct {
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
Database string `json:"database" validate:"required"`
}
type MysqlDBDelete struct {
ID uint `json:"id" validate:"required"`
ForceDelete bool `json:"forceDelete"`
DeleteBackup bool `json:"deleteBackup"`
ID uint `json:"id" validate:"required"`
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
Database string `json:"database" validate:"required"`
ForceDelete bool `json:"forceDelete"`
DeleteBackup bool `json:"deleteBackup"`
}
type MysqlStatus struct {
@@ -107,18 +124,27 @@ type MysqlVariables struct {
}
type MysqlVariablesUpdate struct {
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
Database string `json:"database" validate:"required"`
Variables []MysqlVariablesUpdateHelper `json:"variables"`
}
type MysqlVariablesUpdateHelper struct {
Param string `json:"param"`
Value interface{} `json:"value"`
}
type MysqlConfUpdateByFile struct {
MysqlName string `json:"mysqlName" validate:"required"`
File string `json:"file"`
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
Database string `json:"database" validate:"required"`
File string `json:"file"`
}
type ChangeDBInfo struct {
ID uint `json:"id"`
From string `json:"from"`
Value string `json:"value" validate:"required"`
ID uint `json:"id"`
From string `json:"from" validate:"required,oneof=local remote"`
Type string `json:"type" validate:"required,oneof=mysql mariadb"`
Database string `json:"database" validate:"required"`
Value string `json:"value" validate:"required"`
}
type DBBaseInfo struct {
@@ -127,30 +153,11 @@ type DBBaseInfo struct {
Port int64 `json:"port"`
}
type SearchDBWithPage struct {
PageInfo
MysqlName string `json:"mysqlName" validate:"required"`
}
type BackupDB struct {
MysqlName string `json:"mysqlName" validate:"required"`
DBName string `json:"dbName" validate:"required"`
}
type RecoverDB struct {
MysqlName string `json:"mysqlName" validate:"required"`
DBName string `json:"dbName" validate:"required"`
BackupName string `json:"backupName" validate:"required"`
}
type UploadRecover struct {
MysqlName string `json:"mysqlName" validate:"required"`
DBName string `json:"dbName" validate:"required"`
FileName string `json:"fileName"`
FileDir string `json:"fileDir"`
}
// redis
type ChangeRedisPass struct {
Value string `json:"value" validate:"required"`
}
type RedisConfUpdate struct {
Timeout string `json:"timeout"`
Maxclients string `json:"maxclients"`
@@ -209,3 +216,58 @@ type RedisBackupRecover struct {
FileName string `json:"fileName"`
FileDir string `json:"fileDir"`
}
// database
type DatabaseSearch struct {
PageInfo
Info string `json:"info"`
Type string `json:"type"`
OrderBy string `json:"orderBy"`
Order string `json:"order"`
}
type DatabaseInfo struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name" validate:"max=256"`
From string `json:"from"`
Type string `json:"type"`
Version string `json:"version"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
Description string `json:"description"`
}
type DatabaseOption struct {
ID uint `json:"id"`
Type string `json:"type"`
From string `json:"from"`
Database string `json:"database"`
Version string `json:"version"`
Address string `json:"address"`
}
type DatabaseCreate struct {
Name string `json:"name" validate:"required,max=256"`
Type string `json:"type" validate:"required"`
From string `json:"from" validate:"required,oneof=local remote"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Description string `json:"description"`
}
type DatabaseUpdate struct {
ID uint `json:"id"`
Type string `json:"type" validate:"required"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Description string `json:"description"`
}
+18 -2
View File
@@ -9,8 +9,10 @@ type FirewallBaseInfo struct {
type RuleSearch struct {
PageInfo
Info string `json:"info"`
Type string `json:"type" validate:"required"`
Info string `json:"info"`
Status string `json:"status"`
Strategy string `json:"strategy"`
Type string `json:"type" validate:"required"`
}
type FirewallOperation struct {
@@ -23,12 +25,26 @@ type PortRuleOperate struct {
Port string `json:"port" validate:"required"`
Protocol string `json:"protocol" validate:"required,oneof=tcp udp tcp/udp"`
Strategy string `json:"strategy" validate:"required,oneof=accept drop"`
Description string `json:"description"`
}
type UpdateFirewallDescription struct {
Type string `json:"type"`
Address string `json:"address"`
Port string `json:"port"`
Protocol string `json:"protocol"`
Strategy string `json:"strategy" validate:"required,oneof=accept drop"`
Description string `json:"description"`
}
type AddrRuleOperate struct {
Operation string `json:"operation" validate:"required,oneof=add remove"`
Address string `json:"address" validate:"required"`
Strategy string `json:"strategy" validate:"required,oneof=accept drop"`
Description string `json:"description"`
}
type PortRuleUpdate struct {
-52
View File
@@ -1,52 +0,0 @@
package dto
import "time"
type RemoteDBSearch struct {
PageInfo
Info string `json:"info"`
Type string `json:"type"`
OrderBy string `json:"orderBy"`
Order string `json:"order"`
}
type RemoteDBInfo struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name" validate:"max=256"`
From string `json:"from"`
Version string `json:"version"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
Description string `json:"description"`
}
type RemoteDBOption struct {
ID uint `json:"id"`
Name string `json:"name"`
Address string `json:"address"`
}
type RemoteDBCreate struct {
Name string `json:"name" validate:"required,max=256"`
Type string `json:"type" validate:"required,oneof=mysql"`
From string `json:"from" validate:"required,oneof=local remote"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Description string `json:"description"`
}
type RemoteDBUpdate struct {
ID uint `json:"id"`
Version string `json:"version" validate:"required"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Description string `json:"description"`
}
+5
View File
@@ -41,6 +41,11 @@ type AppInstalledSearch struct {
Unused bool `json:"unused"`
}
type AppInstalledInfo struct {
Key string `json:"key" validate:"required"`
Name string `json:"name"`
}
type AppBackupSearch struct {
dto.PageInfo
AppInstallID uint `json:"appInstallID"`
+3
View File
@@ -17,6 +17,7 @@ type RuntimeCreate struct {
Image string `json:"image"`
Type string `json:"type"`
Version string `json:"version"`
Source string `json:"source"`
}
type RuntimeDelete struct {
@@ -29,4 +30,6 @@ type RuntimeUpdate struct {
Params map[string]interface{} `json:"params"`
Image string `json:"image"`
Version string `json:"version"`
Rebuild bool `json:"rebuild"`
Source string `json:"source"`
}
+9 -3
View File
@@ -141,9 +141,11 @@ type WebsiteNginxUpdate struct {
}
type WebsiteLogReq struct {
ID uint `json:"id" validate:"required"`
Operate string `json:"operate" validate:"required"`
LogType string `json:"logType" validate:"required"`
ID uint `json:"id" validate:"required"`
Operate string `json:"operate" validate:"required"`
LogType string `json:"logType" validate:"required"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
}
type WebsiteDefaultUpdate struct {
@@ -211,3 +213,7 @@ type WebsiteWafFileUpdate struct {
Content string `json:"content" validate:"required"`
Type string `json:"type" validate:"required,oneof=cc ip_white ip_block url_white url_block cookie_block args_check post_check ua_check file_ext_block"`
}
type WebsiteCommonReq struct {
ID uint `json:"id" validate:"required"`
}
+2
View File
@@ -39,6 +39,8 @@ type AppInstalledCheck struct {
AppInstallID uint `json:"appInstallId"`
ContainerName string `json:"containerName"`
InstallPath string `json:"installPath"`
HttpPort int `json:"httpPort"`
HttpsPort int `json:"httpsPort"`
}
type AppDetailDTO struct {
+1
View File
@@ -6,4 +6,5 @@ type RuntimeRes struct {
model.Runtime
AppParams []AppParam `json:"appParams"`
AppID uint `json:"appId"`
Source string `json:"source"`
}
+9
View File
@@ -41,6 +41,8 @@ type WebsiteHTTPS struct {
type WebsiteLog struct {
Enable bool `json:"enable"`
Content string `json:"content"`
End bool `json:"end"`
Path string `json:"path"`
}
type PHPConfig struct {
@@ -52,3 +54,10 @@ type PHPConfig struct {
type NginxRewriteRes struct {
Content string `json:"content"`
}
type WebsiteDirConfig struct {
Dirs []string `json:"dirs"`
User string `json:"user"`
UserGroup string `json:"userGroup"`
Msg string `json:"msg"`
}
+14
View File
@@ -75,7 +75,21 @@ type PortUpdate struct {
ServerPort uint `json:"serverPort" validate:"required,number,max=65535,min=1"`
}
type SnapshotStatus struct {
Panel string `json:"panel"`
PanelInfo string `json:"panelInfo"`
DaemonJson string `json:"daemonJson"`
AppData string `json:"appData"`
PanelData string `json:"panelData"`
BackupData string `json:"backupData"`
Compress string `json:"compress"`
Size string `json:"size"`
Upload string `json:"upload"`
}
type SnapshotCreate struct {
ID uint `json:"id"`
From string `json:"from" validate:"required,oneof=OSS S3 SFTP MINIO COS KODO OneDrive"`
Description string `json:"description" validate:"max=256"`
}
@@ -6,4 +6,5 @@ type AppInstallResource struct {
LinkId uint `json:"linkId" gorm:"type:integer;not null;"`
ResourceId uint `json:"resourceId" gorm:"type:integer;"`
Key string `json:"key" gorm:"type:varchar(64);not null"`
From string `json:"from" gorm:"type:varchar(64);not null;default:local"`
}
+1
View File
@@ -18,6 +18,7 @@ type Cronjob struct {
ContainerName string `gorm:"type:varchar(64)" json:"containerName"`
Script string `gorm:"longtext" json:"script"`
Website string `gorm:"type:varchar(64)" json:"website"`
AppID string `gorm:"type:varchar(64)" json:"appID"`
DBName string `gorm:"type:varchar(64)" json:"dbName"`
URL string `gorm:"type:varchar(256)" json:"url"`
SourceDir string `gorm:"type:varchar(256)" json:"sourceDir"`
+15
View File
@@ -0,0 +1,15 @@
package model
type Database struct {
BaseModel
AppInstallID uint `json:"appInstallID" gorm:"type:decimal"`
Name string `json:"name" gorm:"type:varchar(64);not null;unique"`
Type string `json:"type" gorm:"type:varchar(64);not null"`
Version string `json:"version" gorm:"type:varchar(64);not null"`
From string `json:"from" gorm:"type:varchar(64);not null"`
Address string `json:"address" gorm:"type:varchar(64);not null"`
Port uint `json:"port" gorm:"type:decimal;not null"`
Username string `json:"username" gorm:"type:varchar(64)"`
Password string `json:"password" gorm:"type:varchar(64)"`
Description string `json:"description" gorm:"type:varchar(256);"`
}
+12
View File
@@ -0,0 +1,12 @@
package model
type Firewall struct {
BaseModel
Type string `gorm:"type:varchar(64);not null" json:"type"`
Port string `gorm:"type:varchar(64);not null" json:"port"`
Protocol string `gorm:"type:varchar(64);not null" json:"protocol"`
Address string `gorm:"type:varchar(64);not null" json:"address"`
Strategy string `gorm:"type:varchar(64);not null" json:"strategy"`
Description string `gorm:"type:varchar(64);not null" json:"description"`
}
-14
View File
@@ -1,14 +0,0 @@
package model
type RemoteDB struct {
BaseModel
Name string `json:"name" gorm:"type:varchar(64);not null"`
Type string `json:"type" gorm:"type:varchar(64);not null"`
Version string `json:"version" gorm:"type:varchar(64);not null"`
From string `json:"from" gorm:"type:varchar(64);not null"`
Address string `json:"address" gorm:"type:varchar(64);not null"`
Port uint `json:"port" gorm:"type:decimal;not null"`
Username string `json:"username" gorm:"type:varchar(64)"`
Password string `json:"password" gorm:"type:varchar(64)"`
Description string `json:"description" gorm:"type:varchar(256);"`
}
+15
View File
@@ -17,3 +17,18 @@ type Snapshot struct {
RollbackMessage string `json:"rollbackMessage" gorm:"type:varchar(256)"`
LastRollbackedAt string `json:"lastRollbackedAt" gorm:"type:varchar(64)"`
}
type SnapshotStatus struct {
BaseModel
SnapID uint `gorm:"type:decimal" json:"snapID"`
Panel string `json:"panel" gorm:"type:varchar(64);default:Running"`
PanelInfo string `json:"panelInfo" gorm:"type:varchar(64);default:Running"`
DaemonJson string `json:"daemonJson" gorm:"type:varchar(64);default:Running"`
AppData string `json:"appData" gorm:"type:varchar(64);default:Running"`
PanelData string `json:"panelData" gorm:"type:varchar(64);default:Running"`
BackupData string `json:"backupData" gorm:"type:varchar(64);default:Running"`
Compress string `json:"compress" gorm:"type:varchar(64);default:Waiting"`
Size string `json:"size" gorm:"type:varchar(64)"`
Upload string `json:"upload" gorm:"type:varchar(64);default:Waiting"`
}
+1 -1
View File
@@ -66,7 +66,7 @@ func (a AppRepo) Page(page, size int, opts ...DBOption) (int64, []model.App, err
db := getDb(opts...).Model(&model.App{})
count := int64(0)
db = db.Count(&count)
err := db.Debug().Limit(size).Offset(size * (page - 1)).Preload("AppTags").Find(&apps).Error
err := db.Limit(size).Offset(size * (page - 1)).Preload("AppTags").Find(&apps).Error
return count, apps, err
}
+13 -1
View File
@@ -3,6 +3,7 @@ package repo
import (
"context"
"encoding/json"
"gorm.io/gorm/clause"
"github.com/1Panel-dev/1Panel/backend/app/model"
@@ -160,6 +161,7 @@ type RootInfo struct {
Name string `json:"name"`
Port int64 `json:"port"`
HttpsPort int64 `json:"httpsPort"`
UserName string `json:"userName"`
Password string `json:"password"`
UserPassword string `json:"userPassword"`
ContainerName string `json:"containerName"`
@@ -193,7 +195,7 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
return nil, err
}
switch app.Key {
case "mysql":
case "mysql", "mariadb":
password, ok := envMap["PANEL_DB_ROOT_PASSWORD"].(string)
if ok {
info.Password = password
@@ -203,6 +205,15 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
if ok {
info.Password = password
}
case "mongodb", "postgresql":
user, ok := envMap["PANEL_DB_ROOT_USER"].(string)
if ok {
info.UserName = user
}
password, ok := envMap["PANEL_DB_ROOT_PASSWORD"].(string)
if ok {
info.Password = password
}
}
userPassword, ok := envMap["PANEL_DB_USER_PASSWORD"].(string)
@@ -218,5 +229,6 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
info.Env = appInstall.Env
info.Param = appInstall.Param
info.Version = appInstall.Version
info.Key = app.Key
return &info, nil
}
+143
View File
@@ -0,0 +1,143 @@
package repo
import (
"context"
"fmt"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"gorm.io/gorm"
)
type DatabaseRepo struct{}
type IDatabaseRepo interface {
GetList(opts ...DBOption) ([]model.Database, error)
Page(limit, offset int, opts ...DBOption) (int64, []model.Database, error)
Create(ctx context.Context, database *model.Database) error
Update(id uint, vars map[string]interface{}) error
Delete(ctx context.Context, opts ...DBOption) error
Get(opts ...DBOption) (model.Database, error)
WithByFrom(from string) DBOption
WithoutByFrom(from string) DBOption
WithAppInstallID(appInstallID uint) DBOption
WithTypeList(dbType string) DBOption
}
func NewIDatabaseRepo() IDatabaseRepo {
return &DatabaseRepo{}
}
func (d *DatabaseRepo) Get(opts ...DBOption) (model.Database, error) {
var database model.Database
db := global.DB
for _, opt := range opts {
db = opt(db)
}
if err := db.First(&database).Error; err != nil {
return database, err
}
pass, err := encrypt.StringDecrypt(database.Password)
if err != nil {
global.LOG.Errorf("decrypt database %s password failed, err: %v", database.Name, err)
}
database.Password = pass
return database, nil
}
func (d *DatabaseRepo) Page(page, size int, opts ...DBOption) (int64, []model.Database, error) {
var databases []model.Database
db := global.DB.Model(&model.Database{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
if err := db.Limit(size).Offset(size * (page - 1)).Find(&databases).Error; err != nil {
return count, databases, err
}
for i := 0; i < len(databases); i++ {
pass, err := encrypt.StringDecrypt(databases[i].Password)
if err != nil {
global.LOG.Errorf("decrypt database db %s password failed, err: %v", databases[i].Name, err)
}
databases[i].Password = pass
}
return count, databases, nil
}
func (d *DatabaseRepo) GetList(opts ...DBOption) ([]model.Database, error) {
var databases []model.Database
db := global.DB.Model(&model.Database{})
for _, opt := range opts {
db = opt(db)
}
if err := db.Find(&databases).Error; err != nil {
return databases, err
}
for i := 0; i < len(databases); i++ {
pass, err := encrypt.StringDecrypt(databases[i].Password)
if err != nil {
global.LOG.Errorf("decrypt database db %s password failed, err: %v", databases[i].Name, err)
}
databases[i].Password = pass
}
return databases, nil
}
func (d *DatabaseRepo) WithByFrom(from string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("`from` = ?", from)
}
}
func (d *DatabaseRepo) WithoutByFrom(from string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("`from` != ?", from)
}
}
func (d *DatabaseRepo) WithTypeList(dbType string) DBOption {
return func(g *gorm.DB) *gorm.DB {
if !strings.Contains(dbType, ",") {
return g.Where("`type` = ?", dbType)
}
types := strings.Split(dbType, ",")
var (
rules []string
values []interface{}
)
for _, ty := range types {
if len(ty) != 0 {
rules = append(rules, "`type` = ?")
values = append(values, ty)
}
}
return g.Where(strings.Join(rules, " OR "), values...)
}
}
func (d *DatabaseRepo) WithAppInstallID(appInstallID uint) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("app_install_id = ?", appInstallID)
}
}
func (d *DatabaseRepo) Create(ctx context.Context, database *model.Database) error {
pass, err := encrypt.StringEncrypt(database.Password)
if err != nil {
return fmt.Errorf("decrypt database db %s password failed, err: %v", database.Name, err)
}
database.Password = pass
return getTx(ctx).Create(database).Error
}
func (d *DatabaseRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.Database{}).Where("id = ?", id).Updates(vars).Error
}
func (d *DatabaseRepo) Delete(ctx context.Context, opts ...DBOption) error {
return getTx(ctx, opts...).Delete(&model.Database{}).Error
}
+40 -15
View File
@@ -2,9 +2,11 @@ package repo
import (
"context"
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"gorm.io/gorm"
)
@@ -19,7 +21,6 @@ type IMysqlRepo interface {
Create(ctx context.Context, mysql *model.DatabaseMysql) error
Delete(ctx context.Context, opts ...DBOption) error
Update(id uint, vars map[string]interface{}) error
UpdateDatabaseInfo(id uint, vars map[string]interface{}) error
DeleteLocal(ctx context.Context) error
}
@@ -33,33 +34,64 @@ func (u *MysqlRepo) Get(opts ...DBOption) (model.DatabaseMysql, error) {
for _, opt := range opts {
db = opt(db)
}
err := db.First(&mysql).Error
if err := db.First(&mysql).Error; err != nil {
return mysql, err
}
pass, err := encrypt.StringDecrypt(mysql.Password)
if err != nil {
global.LOG.Errorf("decrypt database db %s password failed, err: %v", mysql.Name, err)
}
mysql.Password = pass
return mysql, err
}
func (u *MysqlRepo) List(opts ...DBOption) ([]model.DatabaseMysql, error) {
var users []model.DatabaseMysql
var mysqls []model.DatabaseMysql
db := global.DB.Model(&model.DatabaseMysql{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&users).Error
return users, err
if err := db.Find(&mysqls).Error; err != nil {
return mysqls, err
}
for i := 0; i < len(mysqls); i++ {
pass, err := encrypt.StringDecrypt(mysqls[i].Password)
if err != nil {
global.LOG.Errorf("decrypt database db %s password failed, err: %v", mysqls[i].Name, err)
}
mysqls[i].Password = pass
}
return mysqls, nil
}
func (u *MysqlRepo) Page(page, size int, opts ...DBOption) (int64, []model.DatabaseMysql, error) {
var users []model.DatabaseMysql
var mysqls []model.DatabaseMysql
db := global.DB.Model(&model.DatabaseMysql{})
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
if err := db.Limit(size).Offset(size * (page - 1)).Find(&mysqls).Error; err != nil {
return count, mysqls, err
}
for i := 0; i < len(mysqls); i++ {
pass, err := encrypt.StringDecrypt(mysqls[i].Password)
if err != nil {
global.LOG.Errorf("decrypt database db %s password failed, err: %v", mysqls[i].Name, err)
}
mysqls[i].Password = pass
}
return count, mysqls, nil
}
func (u *MysqlRepo) Create(ctx context.Context, mysql *model.DatabaseMysql) error {
pass, err := encrypt.StringEncrypt(mysql.Password)
if err != nil {
return fmt.Errorf("decrypt database db %s password failed, err: %v", mysql.Name, err)
}
mysql.Password = pass
return getTx(ctx).Create(mysql).Error
}
@@ -75,13 +107,6 @@ func (u *MysqlRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.DatabaseMysql{}).Where("id = ?", id).Updates(vars).Error
}
func (u *MysqlRepo) UpdateDatabaseInfo(id uint, vars map[string]interface{}) error {
if err := global.DB.Model(&model.AppInstall{}).Where("id = ?", id).Updates(vars).Error; err != nil {
return err
}
return nil
}
func (u *MysqlRepo) WithByMysqlName(mysqlName string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("mysql_name = ?", mysqlName)
+51
View File
@@ -19,6 +19,12 @@ type IHostRepo interface {
Create(host *model.Host) error
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
GetFirewallRecord(opts ...DBOption) (model.Firewall, error)
ListFirewallRecord() ([]model.Firewall, error)
SaveFirewallRecord(firewall *model.Firewall) error
DeleteFirewallRecordByID(id uint) error
DeleteFirewallRecord(fType, port, protocol, address, strategy string) error
}
func NewIHostRepo() IHostRepo {
@@ -106,3 +112,48 @@ func (h *HostRepo) Delete(opts ...DBOption) error {
}
return db.Delete(&model.Host{}).Error
}
func (h *HostRepo) GetFirewallRecord(opts ...DBOption) (model.Firewall, error) {
var firewall model.Firewall
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&firewall).Error
return firewall, err
}
func (h *HostRepo) ListFirewallRecord() ([]model.Firewall, error) {
var datas []model.Firewall
if err := global.DB.Find(&datas).Error; err != nil {
return datas, nil
}
return datas, nil
}
func (h *HostRepo) SaveFirewallRecord(firewall *model.Firewall) error {
if firewall.ID != 0 {
return global.DB.Save(firewall).Error
}
var data model.Firewall
if firewall.Type == "port" {
_ = global.DB.Where("type = ? AND port = ? AND protocol = ? AND address = ? AND strategy = ?", "port", firewall.Port, firewall.Protocol, firewall.Address, firewall.Strategy).First(&data)
if data.ID != 0 {
firewall.ID = data.ID
}
} else {
_ = global.DB.Where("type = ? AND address = ? AND strategy = ?", "address", firewall.Address, firewall.Strategy).First(&data)
if data.ID != 0 {
firewall.ID = data.ID
}
}
return global.DB.Save(firewall).Error
}
func (h *HostRepo) DeleteFirewallRecordByID(id uint) error {
return global.DB.Where("id = ?", id).Delete(&model.Firewall{}).Error
}
func (h *HostRepo) DeleteFirewallRecord(fType, port, protocol, address, strategy string) error {
return global.DB.Where("type = ? AND port = ? AND protocol = ? AND address = ? AND strategy = ?", fType, port, protocol, address, strategy).Delete(&model.Firewall{}).Error
}
-84
View File
@@ -1,84 +0,0 @@
package repo
import (
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"gorm.io/gorm"
)
type RemoteDBRepo struct{}
type IRemoteDBRepo interface {
GetList(opts ...DBOption) ([]model.RemoteDB, error)
Page(limit, offset int, opts ...DBOption) (int64, []model.RemoteDB, error)
Create(database *model.RemoteDB) error
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
Get(opts ...DBOption) (model.RemoteDB, error)
WithByFrom(from string) DBOption
WithoutByFrom(from string) DBOption
}
func NewIRemoteDBRepo() IRemoteDBRepo {
return &RemoteDBRepo{}
}
func (u *RemoteDBRepo) Get(opts ...DBOption) (model.RemoteDB, error) {
var database model.RemoteDB
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&database).Error
return database, err
}
func (u *RemoteDBRepo) Page(page, size int, opts ...DBOption) (int64, []model.RemoteDB, error) {
var users []model.RemoteDB
db := global.DB.Model(&model.RemoteDB{})
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 *RemoteDBRepo) GetList(opts ...DBOption) ([]model.RemoteDB, error) {
var databases []model.RemoteDB
db := global.DB.Model(&model.RemoteDB{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&databases).Error
return databases, err
}
func (c *RemoteDBRepo) WithByFrom(from string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("`from` == ?", from)
}
}
func (c *RemoteDBRepo) WithoutByFrom(from string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("`from` != ?", from)
}
}
func (u *RemoteDBRepo) Create(database *model.RemoteDB) error {
return global.DB.Create(database).Error
}
func (u *RemoteDBRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.RemoteDB{}).Where("id = ?", id).Updates(vars).Error
}
func (u *RemoteDBRepo) Delete(opts ...DBOption) error {
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.RemoteDB{}).Error
}
+13
View File
@@ -2,7 +2,9 @@ package repo
import (
"context"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"gorm.io/gorm"
)
@@ -20,6 +22,7 @@ type IRuntimeRepo interface {
Save(runtime *model.Runtime) error
DeleteBy(opts ...DBOption) error
GetFirst(opts ...DBOption) (*model.Runtime, error)
List(opts ...DBOption) ([]model.Runtime, error)
}
func NewIRunTimeRepo() IRuntimeRepo {
@@ -65,6 +68,16 @@ func (r *RuntimeRepo) Page(page, size int, opts ...DBOption) (int64, []model.Run
return count, runtimes, err
}
func (r *RuntimeRepo) List(opts ...DBOption) ([]model.Runtime, error) {
var runtimes []model.Runtime
db := global.DB.Model(&model.Runtime{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&runtimes).Error
return runtimes, err
}
func (r *RuntimeRepo) Create(ctx context.Context, runtime *model.Runtime) error {
db := getTx(ctx).Model(&model.Runtime{})
return db.Create(&runtime).Error
+36
View File
@@ -12,6 +12,12 @@ type ISnapshotRepo interface {
Update(id uint, vars map[string]interface{}) error
Page(limit, offset int, opts ...DBOption) (int64, []model.Snapshot, error)
Delete(opts ...DBOption) error
GetStatus(snapID uint) (model.SnapshotStatus, error)
GetStatusList(opts ...DBOption) ([]model.SnapshotStatus, error)
CreateStatus(snap *model.SnapshotStatus) error
DeleteStatus(snapID uint) error
UpdateStatus(id uint, vars map[string]interface{}) error
}
func NewISnapshotRepo() ISnapshotRepo {
@@ -67,3 +73,33 @@ func (u *SnapshotRepo) Delete(opts ...DBOption) error {
}
return db.Delete(&model.Snapshot{}).Error
}
func (u *SnapshotRepo) GetStatus(snapID uint) (model.SnapshotStatus, error) {
var data model.SnapshotStatus
if err := global.DB.Where("snap_id = ?", snapID).First(&data).Error; err != nil {
return data, err
}
return data, nil
}
func (u *SnapshotRepo) GetStatusList(opts ...DBOption) ([]model.SnapshotStatus, error) {
var status []model.SnapshotStatus
db := global.DB.Model(&model.SnapshotStatus{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&status).Error
return status, err
}
func (u *SnapshotRepo) CreateStatus(snap *model.SnapshotStatus) error {
return global.DB.Create(snap).Error
}
func (u *SnapshotRepo) DeleteStatus(snapID uint) error {
return global.DB.Where("snap_id = ?", snapID).Delete(&model.SnapshotStatus{}).Error
}
func (u *SnapshotRepo) UpdateStatus(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.SnapshotStatus{}).Where("id = ?", id).Updates(vars).Error
}
+5
View File
@@ -26,6 +26,7 @@ type IWebsiteRepo interface {
SaveWithoutCtx(app *model.Website) error
DeleteBy(ctx context.Context, opts ...DBOption) error
Create(ctx context.Context, app *model.Website) error
DeleteAll(ctx context.Context) error
}
func NewIWebsiteRepo() IWebsiteRepo {
@@ -131,3 +132,7 @@ func (w *WebsiteRepo) SaveWithoutCtx(website *model.Website) error {
func (w *WebsiteRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {
return getTx(ctx, opts...).Delete(&model.Website{}).Error
}
func (w *WebsiteRepo) DeleteAll(ctx context.Context) error {
return getTx(ctx).Where("1 = 1 ").Delete(&model.Website{}).Error
}
+5
View File
@@ -21,6 +21,7 @@ type IWebsiteDomainRepo interface {
Create(ctx context.Context, app *model.WebsiteDomain) error
Save(ctx context.Context, app *model.WebsiteDomain) error
DeleteBy(ctx context.Context, opts ...DBOption) error
DeleteAll(ctx context.Context) error
}
func NewIWebsiteDomainRepo() IWebsiteDomainRepo {
@@ -85,3 +86,7 @@ func (w WebsiteDomainRepo) Save(ctx context.Context, app *model.WebsiteDomain) e
func (w WebsiteDomainRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {
return getTx(ctx, opts...).Delete(&model.WebsiteDomain{}).Error
}
func (w WebsiteDomainRepo) DeleteAll(ctx context.Context) error {
return getTx(ctx).Where("1 = 1 ").Delete(&model.WebsiteDomain{}).Error
}
+62 -22
View File
@@ -5,6 +5,14 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path"
"reflect"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
@@ -19,12 +27,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/files"
http2 "github.com/1Panel-dev/1Panel/backend/utils/http"
"gopkg.in/yaml.v3"
"io"
"net/http"
"os"
"path"
"strconv"
"strings"
)
type AppService struct {
@@ -274,6 +276,20 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
appDetail model.AppDetail
app model.App
)
appDetail, err = appDetailRepo.GetFirst(commonRepo.WithByID(req.AppDetailId))
if err != nil {
return
}
app, err = appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId))
if err != nil {
return
}
if DatabaseKeys[app.Key] > 0 {
if existDatabases, _ := databaseRepo.GetList(commonRepo.WithByName(req.Name)); len(existDatabases) > 0 {
err = buserr.New(constant.ErrRemoteExist)
return
}
}
for key := range req.Params {
if !strings.Contains(key, "PANEL_APP_PORT") {
continue
@@ -291,14 +307,6 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
}
}
appDetail, err = appDetailRepo.GetFirst(commonRepo.WithByID(req.AppDetailId))
if err != nil {
return
}
app, err = appRepo.GetFirst(commonRepo.WithByID(appDetail.AppId))
if err != nil {
return
}
if err = checkRequiredAndLimit(app); err != nil {
return
}
@@ -352,13 +360,20 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
appInstall.ContainerName = containerName
index := 0
serviceName := ""
for k := range servicesMap {
appInstall.ServiceName = k
serviceName = k
if index > 0 {
continue
}
index++
}
if app.Limit == 0 && appInstall.Name != serviceName {
servicesMap[appInstall.Name] = servicesMap[serviceName]
delete(servicesMap, serviceName)
serviceName = appInstall.Name
}
appInstall.ServiceName = serviceName
if err = addDockerComposeCommonParam(composeMap, appInstall.ServiceName, req.AppContainerConfig, req.Params); err != nil {
return
@@ -382,6 +397,14 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
}
}
}()
if hostName, ok := req.Params["PANEL_DB_HOST"]; ok {
database, _ := databaseRepo.Get(commonRepo.WithByName(hostName.(string)))
if !reflect.DeepEqual(database, model.Database{}) {
req.Params["PANEL_DB_HOST"] = database.Address
req.Params["PANEL_DB_PORT"] = database.Port
req.Params["PANEL_DB_HOST_NAME"] = hostName
}
}
paramByte, err = json.Marshal(req.Params)
if err != nil {
return
@@ -394,15 +417,17 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
return
}
go func() {
if err = copyData(app, appDetail, appInstall, req); err != nil {
if appInstall.Status == constant.Installing {
defer func() {
if err != nil {
appInstall.Status = constant.Error
appInstall.Message = err.Error()
_ = appInstallRepo.Save(context.Background(), appInstall)
}
_ = appInstallRepo.Save(context.Background(), appInstall)
}()
if err = copyData(app, appDetail, appInstall, req); err != nil {
return
}
if err = upAppPre(app, appInstall); err != nil {
if err = runScript(appInstall, "init"); err != nil {
return
}
upApp(appInstall)
@@ -668,6 +693,21 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
res.CanUpdate = true
return res, err
}
if err = getAppFromRepo(fmt.Sprintf("%s/%s/1panel.json.zip", global.CONF.System.AppRepo, global.CONF.System.Mode)); err != nil {
return nil, err
}
listFile := path.Join(constant.ResourceDir, "1panel.json")
content, err := os.ReadFile(listFile)
if err != nil {
return nil, err
}
list := &dto.AppList{}
if err = json.Unmarshal(content, list); err != nil {
return nil, err
}
if list.Extra.Version != "" && setting.SystemVersion != list.Extra.Version && !common.CompareVersion(setting.SystemVersion, list.Extra.Version) {
return nil, buserr.New("ErrVersionTooLow")
}
return res, nil
}
@@ -765,8 +805,8 @@ func (a AppService) SyncAppListFromRemote() (err error) {
for _, v := range versions {
version := v.Name
detail := detailsMap[version]
dockerComposeUrl := fmt.Sprintf("%s/%s/%s/%s", baseRemoteUrl, app.Key, version, "docker-compose.yml")
versionUrl := fmt.Sprintf("%s/%s/%s", baseRemoteUrl, app.Key, version)
dockerComposeUrl := fmt.Sprintf("%s/%s", versionUrl, "docker-compose.yml")
composeRes, err := http.Get(dockerComposeUrl)
if err != nil {
return err
@@ -779,7 +819,7 @@ func (a AppService) SyncAppListFromRemote() (err error) {
paramByte, _ := json.Marshal(v.AppForm)
detail.Params = string(paramByte)
detail.DownloadUrl = v.DownloadUrl
detail.DownloadUrl = fmt.Sprintf("%s/%s", versionUrl, app.Key+"-"+version+".tar.gz")
detail.DownloadCallBackUrl = v.DownloadCallBackUrl
detail.Update = true
detail.LastModified = v.LastModified
+94 -45
View File
@@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/backend/i18n"
"math"
"os"
"path"
@@ -12,6 +11,8 @@ import (
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"gopkg.in/yaml.v3"
@@ -40,9 +41,9 @@ type AppInstallService struct {
type IAppInstallService interface {
Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error)
CheckExist(key string) (*response.AppInstalledCheck, error)
LoadPort(key string) (int64, error)
LoadConnInfo(key string) (response.DatabaseConn, 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)
Operate(req request.AppInstalledOperate) error
Update(req request.AppInstalledUpdate) error
@@ -52,14 +53,28 @@ type IAppInstallService interface {
GetUpdateVersions(installId uint) ([]dto.AppVersion, error)
GetParams(id uint) (*response.AppConfig, error)
ChangeAppPort(req request.PortUpdate) error
GetDefaultConfigByKey(key string) (string, error)
GetDefaultConfigByKey(key, name string) (string, error)
DeleteCheck(installId uint) ([]dto.AppResource, error)
GetInstallList() ([]dto.AppInstallInfo, error)
}
func NewIAppInstalledService() IAppInstallService {
return &AppInstallService{}
}
func (a *AppInstallService) GetInstallList() ([]dto.AppInstallInfo, error) {
var datas []dto.AppInstallInfo
appInstalls, err := appInstallRepo.ListBy()
if err != nil {
return nil, err
}
for _, install := range appInstalls {
datas = append(datas, dto.AppInstallInfo{ID: install.ID, Key: install.App.Key, Name: install.Name})
}
return datas, nil
}
func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error) {
var (
opts []repo.DBOption
@@ -115,23 +130,30 @@ func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []respo
return total, installDTOs, nil
}
func (a *AppInstallService) CheckExist(key string) (*response.AppInstalledCheck, error) {
func (a *AppInstallService) CheckExist(req request.AppInstalledInfo) (*response.AppInstalledCheck, error) {
res := &response.AppInstalledCheck{
IsExist: false,
}
app, err := appRepo.GetFirst(appRepo.WithKey(key))
app, err := appRepo.GetFirst(appRepo.WithKey(req.Key))
if err != nil {
return res, nil
}
res.App = app.Name
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithAppId(app.ID))
var appInstall model.AppInstall
if len(req.Name) == 0 {
appInstall, _ = appInstallRepo.GetFirst(appInstallRepo.WithAppId(app.ID))
} else {
appInstall, _ = appInstallRepo.GetFirst(appInstallRepo.WithAppId(app.ID), commonRepo.WithByName(req.Name))
}
if reflect.DeepEqual(appInstall, model.AppInstall{}) {
return res, nil
}
if err := syncById(appInstall.ID); err != nil {
if err = syncByID(appInstall.ID); err != nil {
return nil, err
}
appInstall, _ = appInstallRepo.GetFirst(commonRepo.WithByID(appInstall.ID))
res.ContainerName = appInstall.ContainerName
res.Name = appInstall.Name
@@ -140,22 +162,24 @@ func (a *AppInstallService) CheckExist(key string) (*response.AppInstalledCheck,
res.Status = appInstall.Status
res.AppInstallID = appInstall.ID
res.IsExist = true
res.InstallPath = path.Join(constant.AppInstallDir, app.Key, appInstall.Name)
res.InstallPath = path.Join(constant.AppInstallDir, appInstall.App.Key, appInstall.Name)
res.HttpPort = appInstall.HttpPort
res.HttpsPort = appInstall.HttpsPort
return res, nil
}
func (a *AppInstallService) LoadPort(key string) (int64, error) {
app, err := appInstallRepo.LoadBaseInfo(key, "")
func (a *AppInstallService) LoadPort(req dto.OperationWithNameAndType) (int64, error) {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
if err != nil {
return int64(0), nil
}
return app.Port, nil
}
func (a *AppInstallService) LoadConnInfo(key string) (response.DatabaseConn, error) {
func (a *AppInstallService) LoadConnInfo(req dto.OperationWithNameAndType) (response.DatabaseConn, error) {
var data response.DatabaseConn
app, err := appInstallRepo.LoadBaseInfo(key, "")
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
if err != nil {
return data, nil
}
@@ -215,26 +239,26 @@ func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
if err != nil {
return handleErr(install, err, out)
}
return syncById(install.ID)
return syncByID(install.ID)
case constant.Stop:
out, err := compose.Stop(dockerComposePath)
if err != nil {
return handleErr(install, err, out)
}
return syncById(install.ID)
return syncByID(install.ID)
case constant.Restart:
out, err := compose.Restart(dockerComposePath)
if err != nil {
return handleErr(install, err, out)
}
return syncById(install.ID)
return syncByID(install.ID)
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 syncById(install.ID)
return syncByID(install.ID)
case constant.Upgrade:
return upgradeInstall(install.ID, req.DetailId, req.Backup)
default:
@@ -393,7 +417,7 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
continue
}
if !systemInit {
if err := syncById(i.ID); err != nil {
if err := syncByID(i.ID); err != nil {
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
}
}
@@ -402,25 +426,50 @@ func (a *AppInstallService) SyncAll(systemInit bool) error {
}
func (a *AppInstallService) GetServices(key string) ([]response.AppService, error) {
app, err := appRepo.GetFirst(appRepo.WithKey(key))
if err != nil {
return nil, err
}
installs, err := appInstallRepo.ListBy(appInstallRepo.WithAppId(app.ID), appInstallRepo.WithStatus(constant.Running))
if err != nil {
return nil, err
}
var res []response.AppService
for _, install := range installs {
paramMap := make(map[string]string)
if install.Param != "" {
_ = json.Unmarshal([]byte(install.Param), &paramMap)
if DatabaseKeys[key] > 0 {
dbs, _ := databaseRepo.GetList(databaseRepo.WithByFrom("local"), commonRepo.WithByType(key))
if len(dbs) == 0 {
return res, nil
}
for _, db := range dbs {
service := response.AppService{
Label: db.Name,
Value: db.Name,
}
if db.AppInstallID > 0 {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(db.AppInstallID))
if err != nil {
return nil, err
}
paramMap := make(map[string]string)
if install.Param != "" {
_ = json.Unmarshal([]byte(install.Param), &paramMap)
}
service.Config = paramMap
}
res = append(res, service)
}
} else {
app, err := appRepo.GetFirst(appRepo.WithKey(key))
if err != nil {
return nil, err
}
installs, err := appInstallRepo.ListBy(appInstallRepo.WithAppId(app.ID), appInstallRepo.WithStatus(constant.Running))
if err != nil {
return nil, err
}
for _, install := range installs {
paramMap := make(map[string]string)
if install.Param != "" {
_ = json.Unmarshal([]byte(install.Param), &paramMap)
}
res = append(res, response.AppService{
Label: install.Name,
Value: install.ServiceName,
Config: paramMap,
})
}
res = append(res, response.AppService{
Label: install.Name,
Value: install.ServiceName,
Config: paramMap,
})
}
return res, nil
}
@@ -523,22 +572,22 @@ func (a *AppInstallService) DeleteCheck(installId uint) ([]dto.AppResource, erro
return res, nil
}
func (a *AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
appInstall, err := getAppInstallByKey(key)
func (a *AppInstallService) GetDefaultConfigByKey(key, name string) (string, error) {
baseInfo, err := appInstallRepo.LoadBaseInfo(key, name)
if err != nil {
return "", err
}
fileOp := files.NewFileOp()
filePath := path.Join(constant.AppResourceDir, "remote", appInstall.App.Key, appInstall.Version, "conf")
filePath := path.Join(constant.AppResourceDir, "remote", baseInfo.Key, baseInfo.Version, "conf")
if !fileOp.Stat(filePath) {
filePath = path.Join(constant.AppResourceDir, appInstall.App.Key, "versions", appInstall.Version, "conf")
filePath = path.Join(constant.AppResourceDir, baseInfo.Key, "versions", baseInfo.Version, "conf")
}
if !fileOp.Stat(filePath) {
return "", buserr.New(constant.ErrPathNotFound)
}
if key == constant.AppMysql {
if key == constant.AppMysql || key == constant.AppMariaDB {
filePath = path.Join(filePath, "my.cnf")
}
if key == constant.AppRedis {
@@ -626,8 +675,8 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
return &res, nil
}
func syncById(installId uint) error {
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
func syncByID(installID uint) error {
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installID))
if err != nil {
return err
}
@@ -731,7 +780,7 @@ func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value
envKey := ""
switch param {
case "password":
if appKey == "mysql" {
if appKey == "mysql" || appKey == "mariadb" {
envKey = "PANEL_DB_ROOT_PASSWORD="
} else {
envKey = "PANEL_REDIS_ROOT_PASSWORD="
+168 -51
View File
@@ -15,6 +15,8 @@ import (
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/i18n"
@@ -80,11 +82,48 @@ func checkPort(key string, params map[string]interface{}) (int, error) {
return 0, nil
}
var DatabaseKeys = map[string]uint{
"mysql": 3306,
"mariadb": 3306,
"postgresql": 5432,
"mongodb": 27017,
"redis": 6379,
"memcached": 11211,
}
func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall, params map[string]interface{}) error {
var dbConfig dto.AppDatabase
if app.Type == "runtime" {
if app.Type == "runtime" && DatabaseKeys[app.Key] > 0 {
database := &model.Database{
AppInstallID: appInstall.ID,
Name: appInstall.Name,
Type: app.Key,
Version: appInstall.Version,
From: "local",
Address: appInstall.ServiceName,
Port: DatabaseKeys[app.Key],
}
detail, err := appDetailRepo.GetFirst(commonRepo.WithByID(appInstall.AppDetailId))
if err != nil {
return err
}
formFields := &dto.AppForm{}
if err := json.Unmarshal([]byte(detail.Params), formFields); err != nil {
return err
}
for _, form := range formFields.FormFields {
if form.EnvKey == "PANEL_APP_PORT_HTTP" {
portFloat, ok := form.Default.(float64)
if ok {
database.Port = uint(int(portFloat))
}
break
}
}
switch app.Key {
case "mysql", "mariadb", "postgresql":
case "mysql", "mariadb", "postgresql", "mongodb":
if password, ok := params["PANEL_DB_ROOT_PASSWORD"]; ok {
if password != "" {
authParam := dto.AuthParam{
@@ -95,6 +134,13 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
return err
}
appInstall.Param = string(authByte)
database.Password = password.(string)
if app.Key == "mysql" || app.Key == "mariadb" {
database.Username = "root"
}
if rootUser, ok := params["PANEL_DB_ROOT_USER"]; ok {
database.Username = rootUser.(string)
}
}
}
case "redis":
@@ -109,8 +155,12 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
}
appInstall.Param = string(authByte)
}
database.Password = password.(string)
}
}
if err := databaseRepo.Create(ctx, database); err != nil {
return err
}
}
if app.Type == "website" || app.Type == "tool" {
paramByte, err := json.Marshal(params)
@@ -140,6 +190,7 @@ func createLink(ctx context.Context, app model.App, appInstall *model.AppInstall
var createMysql dto.MysqlDBCreate
createMysql.Name = dbConfig.DbName
createMysql.Username = dbConfig.DbUser
createMysql.Database = dbInstall.Name
createMysql.Format = "utf8mb4"
createMysql.Permission = "%"
createMysql.Password = dbConfig.Password
@@ -173,7 +224,7 @@ func handleAppInstallErr(ctx context.Context, install *model.AppInstall) error {
return err
}
}
if err := deleteLink(ctx, install, true, true); err != nil {
if err := deleteLink(ctx, install, true, true, true); err != nil {
return err
}
return nil
@@ -188,20 +239,33 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
if err != nil && !forceDelete {
return handleErr(install, err, out)
}
if err = runScript(&install, "uninstall"); err != nil {
_, _ = compose.Up(install.GetComposePath())
return err
}
}
tx, ctx := helper.GetTxAndContext()
defer tx.Rollback()
if err := appInstallRepo.Delete(ctx, install); err != nil {
return err
}
if err := deleteLink(ctx, &install, deleteDB, forceDelete); err != nil && !forceDelete {
if err := deleteLink(ctx, &install, deleteDB, forceDelete, deleteBackup); err != nil && !forceDelete {
return err
}
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name))
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType(install.App.Key))
if install.App.Key == constant.AppMysql {
_ = mysqlRepo.DeleteLocal(ctx)
if DatabaseKeys[install.App.Key] > 0 {
_ = databaseRepo.Delete(ctx, databaseRepo.WithAppInstallID(install.ID))
}
switch install.App.Key {
case constant.AppOpenresty:
_ = websiteRepo.DeleteAll(ctx)
_ = websiteDomainRepo.DeleteAll(ctx)
case constant.AppMysql, constant.AppMariaDB:
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
}
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("app"), commonRepo.WithByName(install.App.Key), backupRepo.WithByDetailName(install.Name))
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/app/%s/%s", install.App.Key, install.Name))
if _, err := os.Stat(uploadDir); err == nil {
_ = os.RemoveAll(uploadDir)
@@ -219,21 +283,25 @@ func deleteAppInstall(install model.AppInstall, deleteBackup bool, forceDelete b
return nil
}
func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, forceDelete bool) error {
func deleteLink(ctx context.Context, install *model.AppInstall, deleteDB bool, forceDelete bool, deleteBackup bool) error {
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
if len(resources) == 0 {
return nil
}
for _, re := range resources {
mysqlService := NewIMysqlService()
if re.Key == "mysql" && deleteDB {
if (re.Key == constant.AppMysql || re.Key == constant.AppMariaDB) && deleteDB {
database, _ := mysqlRepo.Get(commonRepo.WithByID(re.ResourceId))
if reflect.DeepEqual(database, model.DatabaseMysql{}) {
continue
}
if err := mysqlService.Delete(ctx, dto.MysqlDBDelete{
ID: database.ID,
ForceDelete: forceDelete,
ID: database.ID,
ForceDelete: forceDelete,
DeleteBackup: deleteBackup,
Type: re.Key,
Database: database.MysqlName,
}); err != nil && !forceDelete {
return err
}
@@ -284,10 +352,19 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
detailDir = path.Join(constant.ResourceDir, "apps", "local", strings.TrimPrefix(install.App.Key, "local"), detail.Version)
}
cmd := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
stdout, _ := cmd.CombinedOutput()
command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath()))
stdout, _ := command.CombinedOutput()
if stdout != nil {
global.LOG.Errorf("upgrade app [%s] [%s] cp file log : %s ", install.App.Key, install.Name, string(stdout))
global.LOG.Infof("upgrade app [%s] [%s] cp file log : %s ", install.App.Key, install.Name, string(stdout))
}
fileOp := files.NewFileOp()
sourceScripts := path.Join(detailDir, "scripts")
if fileOp.Stat(sourceScripts) {
dstScripts := path.Join(install.GetPath(), "scripts")
_ = fileOp.DeleteDir(dstScripts)
_ = fileOp.CreateDir(dstScripts, 0755)
scriptCmd := exec.Command("cp", "-rf", sourceScripts+"/.", dstScripts+"/")
_, _ = scriptCmd.CombinedOutput()
}
composeMap := make(map[string]interface{})
@@ -300,20 +377,21 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
return
}
servicesMap := value.(map[string]interface{})
index := 0
oldServiceName := ""
for k := range servicesMap {
oldServiceName = k
index++
if index > 0 {
break
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)
}
}
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
@@ -366,12 +444,16 @@ func upgradeInstall(installId uint, detailId uint, backup bool) error {
}
return
}
fileOp := files.NewFileOp()
envParams := make(map[string]string, len(envs))
handleMap(envs, envParams)
if err = env.Write(envParams, install.GetEnvPath()); err != nil {
return
}
if err = runScript(&install, "upgrade"); err != nil {
return
}
if upErr = fileOp.WriteFile(install.GetComposePath(), strings.NewReader(install.DockerCompose), 0775); upErr != nil {
return
}
@@ -475,6 +557,8 @@ func handleMap(params map[string]interface{}, envParams map[string]string) {
envParams[k] = t
case float64:
envParams[k] = strconv.FormatFloat(t, 'f', -1, 32)
case uint:
envParams[k] = strconv.Itoa(int(t))
default:
envParams[k] = t.(string)
}
@@ -496,7 +580,7 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
_ = fileOp.CreateDir(appVersionDir, 0755)
}
global.LOG.Infof("download app[%s] from %s", app.Name, appDetail.DownloadUrl)
filePath := path.Join(appVersionDir, appDetail.Version+".tar.gz")
filePath := path.Join(appVersionDir, app.Key+"-"+appDetail.Version+".tar.gz")
defer func() {
if err != nil {
@@ -511,7 +595,7 @@ func downloadApp(app model.App, appDetail model.AppDetail, appInstall *model.App
global.LOG.Errorf("download app[%s] error %v", app.Name, err)
return
}
if err = fileOp.Decompress(filePath, appVersionDir, files.TarGz); err != nil {
if err = fileOp.Decompress(filePath, appResourceDir, files.TarGz); err != nil {
global.LOG.Errorf("decompress app[%s] error %v", app.Name, err)
return
}
@@ -574,17 +658,28 @@ func copyData(app model.App, appDetail model.AppDetail, appInstall *model.AppIns
return
}
// 处理文件夹权限等问题
func upAppPre(app model.App, appInstall *model.AppInstall) error {
dataPath := path.Join(appInstall.GetPath(), "data")
fileOp := files.NewFileOp()
switch app.Key {
case "nexus":
return fileOp.ChownR(dataPath, "200", "0", true)
case "sftpgo":
return fileOp.ChownR(dataPath, "1000", "1000", true)
case "pgadmin4":
return fileOp.ChownR(dataPath, "5050", "5050", true)
func runScript(appInstall *model.AppInstall, operate string) error {
workDir := appInstall.GetPath()
scriptPath := ""
switch operate {
case "init":
scriptPath = path.Join(workDir, "scripts", "init.sh")
case "upgrade":
scriptPath = path.Join(workDir, "scripts", "upgrade.sh")
case "uninstall":
scriptPath = path.Join(workDir, "scripts", "uninstall.sh")
}
if !files.NewFileOp().Stat(scriptPath) {
return nil
}
out, err := cmd.ExecScript(scriptPath, workDir)
if err != nil {
if out != "" {
errMsg := fmt.Sprintf("run script %s error %s", scriptPath, out)
global.LOG.Error(errMsg)
return errors.New(errMsg)
}
return err
}
return nil
}
@@ -660,16 +755,24 @@ func upApp(appInstall *model.AppInstall) {
}
func rebuildApp(appInstall model.AppInstall) error {
dockerComposePath := appInstall.GetComposePath()
out, err := compose.Down(dockerComposePath)
if err != nil {
return handleErr(appInstall, err, out)
}
out, err = compose.Up(dockerComposePath)
if err != nil {
return handleErr(appInstall, err, out)
}
return syncById(appInstall.ID)
appInstall.Status = constant.Rebuilding
_ = appInstallRepo.Save(context.Background(), &appInstall)
go func() {
dockerComposePath := appInstall.GetComposePath()
out, err := compose.Down(dockerComposePath)
if err != nil {
_ = handleErr(appInstall, err, out)
return
}
out, err = compose.Up(dockerComposePath)
if err != nil {
_ = handleErr(appInstall, err, out)
return
}
appInstall.Status = constant.Running
_ = appInstallRepo.Save(context.Background(), &appInstall)
}()
return nil
}
func getAppDetails(details []model.AppDetail, versions []dto.AppConfigVersion) map[string]model.AppDetail {
@@ -840,6 +943,10 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
}
versions = common.GetSortedVersions(versions)
if len(versions) == 0 {
if !updated {
installDTO.CanUpdate = false
res = append(res, installDTO)
}
continue
}
lastVersion := versions[0]
@@ -871,6 +978,16 @@ func getAppInstallByKey(key string) (model.AppInstall, error) {
return appInstall, nil
}
func getAppInstallPort(key string) (httpPort, httpsPort int, err error) {
install, err := getAppInstallByKey(key)
if err != nil {
return
}
httpPort = install.HttpPort
httpsPort = install.HttpsPort
return
}
func updateToolApp(installed *model.AppInstall) {
tooKey, ok := dto.AppToolMap[installed.App.Key]
if !ok {
+19 -5
View File
@@ -4,6 +4,7 @@ import (
"strconv"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
@@ -19,16 +20,16 @@ type AuthService struct{}
type IAuthService interface {
CheckIsSafety(code string) (string, error)
VerifyCode(code string) (bool, error)
Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error)
Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, error)
LogOut(c *gin.Context) error
MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLoginInfo, error)
MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, error)
}
func NewIAuthService() IAuthService {
return &AuthService{}
}
func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) {
func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, error) {
nameSetting, err := settingRepo.Get(settingRepo.WithByKey("UserName"))
if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
@@ -44,6 +45,13 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo,
if info.Password != pass || nameSetting.Value != info.Name {
return nil, constant.ErrAuth
}
entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
if err != nil {
return nil, err
}
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
return nil, buserr.New(constant.ErrEntrance)
}
mfa, err := settingRepo.Get(settingRepo.WithByKey("MFAStatus"))
if err != nil {
return nil, err
@@ -57,7 +65,7 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo,
return u.generateSession(c, info.Name, info.AuthMethod)
}
func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLoginInfo, error) {
func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, error) {
nameSetting, err := settingRepo.Get(settingRepo.WithByKey("UserName"))
if err != nil {
return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error())
@@ -73,7 +81,13 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLogi
if info.Password != pass || nameSetting.Value != info.Name {
return nil, constant.ErrAuth
}
entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
if err != nil {
return nil, err
}
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
return nil, buserr.New(constant.ErrEntrance)
}
mfaSecret, err := settingRepo.Get(settingRepo.WithByKey("MFASecret"))
if err != nil {
return nil, err
+14 -6
View File
@@ -106,7 +106,7 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
for _, resource := range resources {
if resource.Key == "mysql" {
if resource.Key == "mysql" || resource.Key == "mariadb" {
db, err := mysqlRepo.Get(commonRepo.WithByID(resource.ResourceId))
if err != nil {
return err
@@ -173,8 +173,15 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
newEnvFile := ""
resources, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithAppInstallId(install.ID))
for _, resource := range resources {
if resource.Key == "mysql" {
mysqlInfo, err := appInstallRepo.LoadBaseInfo(resource.Key, "")
if resource.From != "local" {
continue
}
resourceApp, err := appInstallRepo.GetFirst(commonRepo.WithByID(resource.LinkId))
if err != nil {
return err
}
if resource.Key == "mysql" || resource.Key == "mariadb" {
mysqlInfo, err := appInstallRepo.LoadBaseInfo(resource.Key, resourceApp.Name)
if err != nil {
return err
}
@@ -183,7 +190,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
return err
}
newDB, envMap, err := reCreateDB(db.ID, oldInstall.Env)
newDB, envMap, err := reCreateDB(db.ID, resourceApp, oldInstall.Env)
if err != nil {
return err
}
@@ -245,10 +252,10 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
return nil
}
func reCreateDB(dbID uint, oldEnv string) (*model.DatabaseMysql, map[string]interface{}, error) {
func reCreateDB(dbID uint, app model.AppInstall, oldEnv string) (*model.DatabaseMysql, map[string]interface{}, error) {
mysqlService := NewIMysqlService()
ctx := context.Background()
_ = mysqlService.Delete(ctx, dto.MysqlDBDelete{ID: dbID, DeleteBackup: true, ForceDelete: true})
_ = mysqlService.Delete(ctx, dto.MysqlDBDelete{ID: dbID, Database: app.Name, Type: app.App.Key, DeleteBackup: false, ForceDelete: true})
envMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(oldEnv), &envMap); err != nil {
@@ -260,6 +267,7 @@ func reCreateDB(dbID uint, oldEnv string) (*model.DatabaseMysql, map[string]inte
createDB, err := mysqlService.Create(context.Background(), dto.MysqlDBCreate{
Name: oldName,
From: "local",
Database: app.Name,
Format: "utf8mb4",
Username: oldUser,
Password: oldPassword,
+7 -7
View File
@@ -23,7 +23,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
}
timeNow := time.Now().Format("20060102150405")
targetDir := path.Join(localDir, fmt.Sprintf("database/mysql/%s/%s", req.Name, req.DetailName))
targetDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", req.Type, req.Name, req.DetailName))
fileName := fmt.Sprintf("%s_%s.sql.gz", req.DetailName, timeNow)
if err := handleMysqlBackup(req.Name, req.DetailName, targetDir, fileName); err != nil {
@@ -31,7 +31,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
}
record := &model.BackupRecord{
Type: "mysql",
Type: req.Type,
Name: req.Name,
DetailName: req.DetailName,
Source: "LOCAL",
@@ -97,12 +97,12 @@ func (u *BackupService) MysqlRecoverByUpload(req dto.CommonRecover) error {
return nil
}
func handleMysqlBackup(name, dbName, targetDir, fileName string) error {
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(dbName), mysqlRepo.WithByMysqlName(name))
func handleMysqlBackup(database, dbName, targetDir, fileName string) error {
dbInfo, err := mysqlRepo.Get(commonRepo.WithByName(dbName), mysqlRepo.WithByMysqlName(database))
if err != nil {
return err
}
cli, _, err := LoadMysqlClientByFrom(dbInfo.From)
cli, _, err := LoadMysqlClientByFrom(database)
if err != nil {
return err
}
@@ -131,13 +131,13 @@ func handleMysqlRecover(req dto.CommonRecover, isRollback bool) error {
if err != nil {
return err
}
cli, _, err := LoadMysqlClientByFrom(dbInfo.From)
cli, _, err := LoadMysqlClientByFrom(req.Name)
if err != nil {
return err
}
if !isRollback {
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/mysql/%s_%s.sql.gz", req.DetailName, time.Now().Format("20060102150405")))
rollbackFile := path.Join(global.CONF.System.TmpDir, fmt.Sprintf("database/%s/%s_%s.sql.gz", req.Type, req.DetailName, time.Now().Format("20060102150405")))
if err := cli.Backup(client.BackupInfo{
Name: req.DetailName,
Format: dbInfo.Format,
+79 -14
View File
@@ -147,14 +147,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
IsFromApp = true
}
var ports []string
for _, port := range item.Ports {
itemPortStr := fmt.Sprintf("%v/%s", port.PrivatePort, port.Type)
if port.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type)
}
ports = append(ports, itemPortStr)
}
ports := simplifyPort(item.Ports)
backDatas[i] = dto.ContainerInfo{
ContainerID: item.ID,
CreateTime: time.Unix(item.Created, 0).Format("2006-01-02 15:04:05"),
@@ -167,6 +160,14 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
IsFromApp: IsFromApp,
IsFromCompose: IsFromCompose,
}
if item.NetworkSettings != nil && len(item.NetworkSettings.Networks) > 0 {
networks := make([]string, 0, len(item.NetworkSettings.Networks))
for key := range item.NetworkSettings.Networks {
networks = append(networks, item.NetworkSettings.Networks[key].IPAddress)
}
sort.Strings(networks)
backDatas[i].Network = networks
}
}
return int64(total), backDatas, nil
@@ -365,6 +366,7 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
}
}
data.Cmd = oldContainer.Config.Cmd
data.Entrypoint = oldContainer.Config.Entrypoint
data.Env = oldContainer.Config.Env
data.CPUShares = oldContainer.HostConfig.CPUShares
for key, val := range oldContainer.Config.Labels {
@@ -531,11 +533,15 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
if err != nil {
return err
}
container, err := client.ContainerInspect(context.Background(), req.Name)
ctx := context.Background()
containerItem, err := client.ContainerInspect(ctx, req.Name)
if err != nil {
return err
}
file, err := os.OpenFile(container.LogPath, os.O_RDWR|os.O_CREATE, 0666)
if err := client.ContainerStop(ctx, containerItem.ID, container.StopOptions{}); err != nil {
return err
}
file, err := os.OpenFile(containerItem.LogPath, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
return err
}
@@ -545,10 +551,14 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
}
_, _ = file.Seek(0, 0)
files, _ := filepath.Glob(fmt.Sprintf("%s.*", container.LogPath))
files, _ := filepath.Glob(fmt.Sprintf("%s.*", containerItem.LogPath))
for _, file := range files {
_ = os.Remove(file)
}
if err := client.ContainerStart(ctx, containerItem.ID, types.ContainerStartOptions{}); err != nil {
return err
}
return nil
}
@@ -558,7 +568,7 @@ func (u *ContainerService) ContainerLogs(wsConn *websocket.Conn, container, sinc
}
command := fmt.Sprintf("docker logs %s", container)
if tail != "0" {
command += " -n " + tail
command += " --tail " + tail
}
if since != "all" {
command += " --since " + since
@@ -631,9 +641,9 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) string {
filePath := ""
switch req.Type {
case "image-pull", "image-push", "image-build":
case "image-pull", "image-push", "image-build", "compose-create":
filePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
case "compose-detail", "compose-create":
case "compose-detail":
client, err := docker.NewDockerClient()
if err != nil {
return ""
@@ -833,6 +843,7 @@ func loadConfigInfo(req dto.ContainerOperate, config *container.Config, hostConf
}
config.Image = req.Image
config.Cmd = req.Cmd
config.Entrypoint = req.Entrypoint
config.Env = req.Env
config.Labels = stringsToMap(req.Labels)
config.ExposedPorts = exposeds
@@ -921,3 +932,57 @@ func loadVolumeBinds(binds []string) []dto.VolumeHelper {
}
return datas
}
func simplifyPort(ports []types.Port) []string {
var datas []string
if len(ports) == 0 {
return datas
}
if len(ports) == 1 {
itemPortStr := fmt.Sprintf("%s:%v/%s", ports[0].IP, ports[0].PrivatePort, ports[0].Type)
if ports[0].PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", ports[0].IP, ports[0].PublicPort, ports[0].PrivatePort, ports[0].Type)
}
datas = append(datas, itemPortStr)
return datas
}
sort.Slice(ports, func(i, j int) bool {
return ports[i].PrivatePort < ports[j].PrivatePort
})
start := ports[0]
for i := 1; i < len(ports); i++ {
if ports[i].PrivatePort != ports[i-1].PrivatePort+1 || ports[i].IP != ports[i-1].IP || ports[i].PublicPort != ports[i-1].PublicPort+1 || ports[i].Type != ports[i-1].Type {
if ports[i-1].PrivatePort == start.PrivatePort {
itemPortStr := fmt.Sprintf("%s:%v/%s", start.IP, start.PrivatePort, start.Type)
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", start.IP, start.PublicPort, start.PrivatePort, start.Type)
}
datas = append(datas, itemPortStr)
} else {
itemPortStr := fmt.Sprintf("%s:%v-%v/%s", start.IP, start.PrivatePort, ports[i-1].PrivatePort, start.Type)
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v-%v->%v-%v/%s", start.IP, start.PublicPort, ports[i-1].PublicPort, start.PrivatePort, ports[i-1].PrivatePort, start.Type)
}
datas = append(datas, itemPortStr)
}
start = ports[i]
} else if i == len(ports)-1 {
if ports[i].PrivatePort == start.PrivatePort {
itemPortStr := fmt.Sprintf("%s:%v/%s", start.IP, start.PrivatePort, start.Type)
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", start.IP, start.PublicPort, start.PrivatePort, start.Type)
}
datas = append(datas, itemPortStr)
} else {
itemPortStr := fmt.Sprintf("%s:%v-%v/%s", start.IP, start.PrivatePort, ports[i].PrivatePort, start.Type)
if start.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v-%v->%v-%v/%s", start.IP, start.PublicPort, ports[i].PublicPort, start.PrivatePort, ports[i].PrivatePort, start.Type)
}
datas = append(datas, itemPortStr)
}
}
}
return datas
}
+10 -3
View File
@@ -159,8 +159,15 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
if req.From == "path" {
req.Name = path.Base(path.Dir(req.Path))
}
logName := path.Dir(req.Path) + "/compose.log"
file, err := os.OpenFile(logName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
dockerLogDir := path.Join(global.CONF.System.TmpDir, "docker_logs")
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
return "", err
}
}
logItem := fmt.Sprintf("%s/compose_create_%s_%s.log", dockerLogDir, req.Name, time.Now().Format("20060102150405"))
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return "", err
}
@@ -181,7 +188,7 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
_, _ = file.WriteString("docker-compose up successful!")
}()
return req.Name, nil
return path.Base(logItem), nil
}
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
+3 -2
View File
@@ -46,7 +46,7 @@ func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, inter
if err := copier.Copy(&item, &cronjob); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
if item.Type == "website" || item.Type == "database" || item.Type == "directory" {
if item.Type == "app" || item.Type == "website" || item.Type == "database" || item.Type == "directory" || item.Type == "snapshot" {
backup, _ := backupRepo.Get(commonRepo.WithByID(uint(item.TargetDirID)))
if len(backup.Type) != 0 {
item.TargetDir = backup.Type
@@ -103,7 +103,7 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
if err != nil {
return err
}
if req.CleanData && (cronjob.Type == "database" || cronjob.Type == "website" || cronjob.Type == "directory") {
if req.CleanData && (cronjob.Type == "app" || cronjob.Type == "database" || cronjob.Type == "website" || cronjob.Type == "directory" || cronjob.Type == "snapshot") {
cronjob.RetainCopies = 0
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
if err != nil {
@@ -261,6 +261,7 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
upMap["hour"] = req.Hour
upMap["minute"] = req.Minute
upMap["second"] = req.Second
upMap["app_id"] = req.AppID
upMap["website"] = req.Website
upMap["exclusion_rules"] = req.ExclusionRules
upMap["db_name"] = req.DBName
+120 -7
View File
@@ -10,6 +10,7 @@ import (
"sync"
"time"
"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/global"
@@ -38,6 +39,10 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
message, err = u.handleShell(cronjob.Type, cronjob.Name, cronjob.Script)
}
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
case "snapshot":
messageItem := ""
messageItem, record.File, err = u.handleSnapshot(cronjob, record.StartTime)
message = []byte(messageItem)
case "curl":
if len(cronjob.URL) == 0 {
return
@@ -47,9 +52,7 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
case "ntp":
err = u.handleNtpSync()
u.HandleRmExpired("LOCAL", "", "", cronjob, nil)
case "website":
record.File, err = u.handleBackup(cronjob, record.StartTime)
case "database":
case "website", "database", "app":
record.File, err = u.handleBackup(cronjob, record.StartTime)
case "directory":
if len(cronjob.SourceDir) == 0 {
@@ -62,6 +65,7 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
global.LOG.Errorf("cut website log file failed, err: %v", err)
}
}
if err != nil {
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), string(message))
return
@@ -85,7 +89,7 @@ func (u *CronjobService) handleShell(cronType, cornName, script string) ([]byte,
}
stdout, err := cmd.ExecCronjobWithTimeOut(script, handleDir, 24*time.Hour)
if err != nil {
return nil, err
return []byte(stdout), err
}
return []byte(stdout), nil
}
@@ -120,6 +124,9 @@ func (u *CronjobService) handleBackup(cronjob *model.Cronjob, startTime time.Tim
case "database":
paths, err := u.handleDatabase(*cronjob, backup, startTime)
return strings.Join(paths, ","), err
case "app":
paths, err := u.handleApp(*cronjob, backup, startTime)
return strings.Join(paths, ","), err
case "website":
paths, err := u.handleWebsite(*cronjob, backup, startTime)
return strings.Join(paths, ","), err
@@ -186,6 +193,10 @@ func (u *CronjobService) HandleRmExpired(backType, backupPath, localDir string,
fileItem = strings.TrimPrefix(file, localDir+"/")
}
}
if cronjob.Type == "snapshot" {
_ = snapshotRepo.Delete(commonRepo.WithByName(strings.TrimSuffix(path.Base(fileItem), ".tar.gz")))
}
_, _ = backClient.Delete(fileItem)
}
}
@@ -221,7 +232,7 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
path = sourceDir
}
commands := fmt.Sprintf("tar --warning=no-file-changed -zcf %s %s %s", targetDir+"/"+name, excludeRules, path)
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 {
@@ -282,12 +293,13 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, backup model.Back
for _, dbInfo := range dbs {
var record model.BackupRecord
record.Type = "mysql"
database, _ := databaseRepo.Get(commonRepo.WithByName(dbInfo.MysqlName))
record.Type = database.Type
record.Source = "LOCAL"
record.BackupType = backup.Type
record.Name = dbInfo.MysqlName
backupDir := path.Join(localDir, fmt.Sprintf("database/mysql/%s/%s", record.Name, dbInfo.Name))
backupDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", database.Type, record.Name, dbInfo.Name))
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format("20060102150405"))
if err = handleMysqlBackup(dbInfo.MysqlName, dbInfo.Name, backupDir, record.FileName); err != nil {
return paths, err
@@ -396,6 +408,83 @@ func (u *CronjobService) handleCutWebsiteLog(cronjob *model.Cronjob, startTime t
return strings.Join(filePaths, ","), nil
}
func (u *CronjobService) handleApp(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
var paths []string
localDir, err := loadLocalDir()
if err != nil {
return paths, err
}
var applist []model.AppInstall
if cronjob.AppID == "all" {
applist, err = appInstallRepo.ListBy()
if err != nil {
return paths, err
}
} else {
itemID, _ := (strconv.Atoi(cronjob.AppID))
app, err := appInstallRepo.GetFirst(commonRepo.WithByID(uint(itemID)))
if err != nil {
return paths, err
}
applist = append(applist, app)
}
var client cloud_storage.CloudStorageClient
if backup.Type != "LOCAL" {
client, err = NewIBackupService().NewClient(&backup)
if err != nil {
return paths, err
}
}
for _, app := range applist {
var record model.BackupRecord
record.Type = "app"
record.Name = app.App.Key
record.DetailName = app.Name
record.Source = "LOCAL"
record.BackupType = backup.Type
backupDir := path.Join(localDir, fmt.Sprintf("app/%s/%s", app.App.Key, app.Name))
record.FileDir = backupDir
itemFileDir := strings.TrimPrefix(backupDir, localDir+"/")
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
record.Source = backup.Type
record.FileDir = strings.TrimPrefix(backupDir, localDir+"/")
}
record.FileName = fmt.Sprintf("app_%s_%s.tar.gz", app.Name, startTime.Format("20060102150405"))
if err := handleAppBackup(&app, backupDir, record.FileName); err != nil {
return paths, err
}
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return paths, err
}
if backup.Type != "LOCAL" {
if !cronjob.KeepLocal {
defer func() {
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
}()
}
if len(backup.BackupPath) != 0 {
itemPath := strings.TrimPrefix(backup.BackupPath, "/")
itemPath = strings.TrimSuffix(itemPath, "/") + "/"
itemFileDir = itemPath + itemFileDir
}
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
return paths, err
}
}
if backup.Type == "LOCAL" || cronjob.KeepLocal {
paths = append(paths, fmt.Sprintf("%s/%s", record.FileDir, record.FileName))
} else {
paths = append(paths, fmt.Sprintf("%s/%s", itemFileDir, record.FileName))
}
}
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
return paths, nil
}
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) ([]string, error) {
var paths []string
localDir, err := loadLocalDir()
@@ -471,3 +560,27 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.Backu
u.HandleRmExpired(backup.Type, backup.BackupPath, localDir, &cronjob, client)
return paths, nil
}
func (u *CronjobService) handleSnapshot(cronjob *model.Cronjob, startTime time.Time) (string, string, error) {
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
if err != nil {
return "", "", err
}
client, err := NewIBackupService().NewClient(&backup)
if err != nil {
return "", "", err
}
req := dto.SnapshotCreate{
From: backup.Type,
}
message, name, err := NewISnapshotService().HandleSnapshot(true, req, startTime.Format("20060102150405"))
if err != nil {
return message, "", err
}
path := path.Join(strings.TrimPrefix(backup.BackupPath, "/"), "system_snapshot", name+".tar.gz")
u.HandleRmExpired(backup.Type, backup.BackupPath, "", cronjob, client)
return message, path, nil
}
@@ -2,40 +2,43 @@ package service
import (
"context"
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"github.com/1Panel-dev/1Panel/backend/utils/mysql"
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
type RemoteDBService struct{}
type DatabaseService struct{}
type IRemoteDBService interface {
Get(name string) (dto.RemoteDBInfo, error)
SearchWithPage(search dto.RemoteDBSearch) (int64, interface{}, error)
CheckeRemoteDB(req dto.RemoteDBCreate) bool
Create(req dto.RemoteDBCreate) error
Update(req dto.RemoteDBUpdate) error
type IDatabaseService interface {
Get(name string) (dto.DatabaseInfo, error)
SearchWithPage(search dto.DatabaseSearch) (int64, interface{}, error)
CheckDatabase(req dto.DatabaseCreate) bool
Create(req dto.DatabaseCreate) error
Update(req dto.DatabaseUpdate) error
Delete(id uint) error
List(dbType string) ([]dto.RemoteDBOption, error)
List(dbType string) ([]dto.DatabaseOption, error)
}
func NewIRemoteDBService() IRemoteDBService {
return &RemoteDBService{}
func NewIDatabaseService() IDatabaseService {
return &DatabaseService{}
}
func (u *RemoteDBService) SearchWithPage(search dto.RemoteDBSearch) (int64, interface{}, error) {
total, dbs, err := remoteDBRepo.Page(search.Page, search.PageSize,
commonRepo.WithByType(search.Type),
func (u *DatabaseService) SearchWithPage(search dto.DatabaseSearch) (int64, interface{}, error) {
total, dbs, err := databaseRepo.Page(search.Page, search.PageSize,
databaseRepo.WithTypeList(search.Type),
commonRepo.WithLikeName(search.Info),
remoteDBRepo.WithoutByFrom("local"),
databaseRepo.WithoutByFrom("local"),
)
var datas []dto.RemoteDBInfo
var datas []dto.DatabaseInfo
for _, db := range dbs {
var item dto.RemoteDBInfo
var item dto.DatabaseInfo
if err := copier.Copy(&item, &db); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
@@ -44,9 +47,9 @@ func (u *RemoteDBService) SearchWithPage(search dto.RemoteDBSearch) (int64, inte
return total, datas, err
}
func (u *RemoteDBService) Get(name string) (dto.RemoteDBInfo, error) {
var data dto.RemoteDBInfo
remote, err := remoteDBRepo.Get(commonRepo.WithByName(name))
func (u *DatabaseService) Get(name string) (dto.DatabaseInfo, error) {
var data dto.DatabaseInfo
remote, err := databaseRepo.Get(commonRepo.WithByName(name))
if err != nil {
return data, err
}
@@ -56,20 +59,21 @@ func (u *RemoteDBService) Get(name string) (dto.RemoteDBInfo, error) {
return data, nil
}
func (u *RemoteDBService) List(dbType string) ([]dto.RemoteDBOption, error) {
dbs, err := remoteDBRepo.GetList(commonRepo.WithByType(dbType))
var datas []dto.RemoteDBOption
func (u *DatabaseService) List(dbType string) ([]dto.DatabaseOption, error) {
dbs, err := databaseRepo.GetList(databaseRepo.WithTypeList(dbType))
var datas []dto.DatabaseOption
for _, db := range dbs {
var item dto.RemoteDBOption
var item dto.DatabaseOption
if err := copier.Copy(&item, &db); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
item.Database = db.Name
datas = append(datas, item)
}
return datas, err
}
func (u *RemoteDBService) CheckeRemoteDB(req dto.RemoteDBCreate) bool {
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
@@ -83,9 +87,12 @@ func (u *RemoteDBService) CheckeRemoteDB(req dto.RemoteDBCreate) bool {
return true
}
func (u *RemoteDBService) Create(req dto.RemoteDBCreate) error {
db, _ := remoteDBRepo.Get(commonRepo.WithByName(req.Name))
func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
db, _ := databaseRepo.Get(commonRepo.WithByName(req.Name))
if db.ID != 0 {
if db.From == "local" {
return buserr.New(constant.ErrLocalExist)
}
return constant.ErrRecordExist
}
if _, err := mysql.NewMysqlClient(client.DBInfo{
@@ -101,29 +108,29 @@ func (u *RemoteDBService) Create(req dto.RemoteDBCreate) error {
if err := copier.Copy(&db, &req); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
if err := remoteDBRepo.Create(&db); err != nil {
if err := databaseRepo.Create(context.Background(), &db); err != nil {
return err
}
return nil
}
func (u *RemoteDBService) Delete(id uint) error {
db, _ := remoteDBRepo.Get(commonRepo.WithByID(id))
func (u *DatabaseService) Delete(id uint) error {
db, _ := databaseRepo.Get(commonRepo.WithByID(id))
if db.ID == 0 {
return constant.ErrRecordNotFound
}
if err := remoteDBRepo.Delete(commonRepo.WithByID(id)); err != nil {
if err := databaseRepo.Delete(context.Background(), commonRepo.WithByID(id)); err != nil {
return err
}
if db.From != "local" {
if err := mysqlRepo.Delete(context.Background(), remoteDBRepo.WithByFrom(db.Name)); err != nil {
if err := mysqlRepo.Delete(context.Background(), mysqlRepo.WithByMysqlName(db.Name)); err != nil {
return err
}
}
return nil
}
func (u *RemoteDBService) Update(req dto.RemoteDBUpdate) error {
func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
if _, err := mysql.NewMysqlClient(client.DBInfo{
From: "remote",
Address: req.Address,
@@ -135,12 +142,18 @@ func (u *RemoteDBService) Update(req dto.RemoteDBUpdate) error {
return err
}
pass, err := encrypt.StringEncrypt(req.Password)
if err != nil {
return fmt.Errorf("decrypt database password failed, err: %v", err)
}
upMap := make(map[string]interface{})
upMap["type"] = req.Type
upMap["version"] = req.Version
upMap["address"] = req.Address
upMap["port"] = req.Port
upMap["username"] = req.Username
upMap["password"] = req.Password
upMap["password"] = pass
upMap["description"] = req.Description
return remoteDBRepo.Update(req.ID, upMap)
return databaseRepo.Update(req.ID, upMap)
}
+99 -101
View File
@@ -21,6 +21,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"github.com/1Panel-dev/1Panel/backend/utils/mysql"
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
_ "github.com/go-sql-driver/mysql"
@@ -34,18 +35,19 @@ type IMysqlService interface {
SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error)
ListDBOption() ([]dto.MysqlOption, error)
Create(ctx context.Context, req dto.MysqlDBCreate) (*model.DatabaseMysql, error)
LoadFromRemote(from string) error
LoadFromRemote(req dto.MysqlLoadDB) error
ChangeAccess(info dto.ChangeDBInfo) error
ChangePassword(info dto.ChangeDBInfo) error
UpdateVariables(updates []dto.MysqlVariablesUpdate) error
UpdateVariables(req dto.MysqlVariablesUpdate) error
UpdateConfByFile(info dto.MysqlConfUpdateByFile) error
UpdateDescription(req dto.UpdateDescription) error
DeleteCheck(id uint) ([]string, error)
DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error)
Delete(ctx context.Context, req dto.MysqlDBDelete) error
LoadStatus() (*dto.MysqlStatus, error)
LoadVariables() (*dto.MysqlVariables, error)
LoadBaseInfo() (*dto.DBBaseInfo, error)
LoadRemoteAccess() (bool, error)
LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlStatus, error)
LoadVariables(req dto.OperationWithNameAndType) (*dto.MysqlVariables, error)
LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error)
LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error)
LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error)
}
@@ -56,7 +58,7 @@ func NewIMysqlService() IMysqlService {
func (u *MysqlService) SearchWithPage(search dto.MysqlDBSearch) (int64, interface{}, error) {
total, mysqls, err := mysqlRepo.Page(search.Page, search.PageSize,
mysqlRepo.WithByFrom(search.From),
mysqlRepo.WithByMysqlName(search.Database),
commonRepo.WithLikeName(search.Info),
commonRepo.WithOrderRuleBy(search.OrderBy, search.Order),
)
@@ -73,12 +75,26 @@ func (u *MysqlService) SearchWithPage(search dto.MysqlDBSearch) (int64, interfac
func (u *MysqlService) ListDBOption() ([]dto.MysqlOption, error) {
mysqls, err := mysqlRepo.List()
if err != nil {
return nil, err
}
databases, err := databaseRepo.GetList(databaseRepo.WithTypeList("mysql,mariadb"))
if err != nil {
return nil, err
}
var dbs []dto.MysqlOption
for _, mysql := range mysqls {
var item dto.MysqlOption
if err := copier.Copy(&item, &mysql); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
item.Database = mysql.MysqlName
for _, database := range databases {
if database.Name == item.Database {
item.Type = database.Type
}
}
dbs = append(dbs, item)
}
return dbs, err
@@ -89,7 +105,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
return nil, buserr.New(constant.ErrCmdIllegal)
}
mysql, _ := mysqlRepo.Get(commonRepo.WithByName(req.Name), remoteDBRepo.WithByFrom(req.From))
mysql, _ := mysqlRepo.Get(commonRepo.WithByName(req.Name), mysqlRepo.WithByMysqlName(req.Database), databaseRepo.WithByFrom(req.From))
if mysql.ID != 0 {
return nil, constant.ErrRecordExist
}
@@ -103,20 +119,11 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
return nil, errors.New("Cannot set root as user name")
}
cli, version, err := LoadMysqlClientByFrom(req.From)
cli, version, err := LoadMysqlClientByFrom(req.Database)
if err != nil {
return nil, err
}
if req.From == "local" {
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
if err != nil {
return nil, err
}
createItem.MysqlName = app.Name
} else {
createItem.MysqlName = req.From
}
createItem.MysqlName = req.Database
defer cli.Close()
if err := cli.Create(client.CreateInfo{
Name: req.Name,
@@ -137,22 +144,13 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
return &createItem, nil
}
func (u *MysqlService) LoadFromRemote(from string) error {
client, version, err := LoadMysqlClientByFrom(from)
func (u *MysqlService) LoadFromRemote(req dto.MysqlLoadDB) error {
client, version, err := LoadMysqlClientByFrom(req.Database)
if err != nil {
return err
}
mysqlName := from
if from == "local" {
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
if err != nil {
return err
}
mysqlName = app.Name
}
databases, err := mysqlRepo.List(remoteDBRepo.WithByFrom(from))
databases, err := mysqlRepo.List(mysqlRepo.WithByMysqlName(req.Database))
if err != nil {
return err
}
@@ -163,7 +161,7 @@ func (u *MysqlService) LoadFromRemote(from string) error {
for _, data := range datas {
hasOld := false
for _, oldData := range databases {
if strings.EqualFold(oldData.Name, data.Name) {
if strings.EqualFold(oldData.Name, data.Name) && strings.EqualFold(oldData.MysqlName, data.MysqlName) {
hasOld = true
break
}
@@ -173,7 +171,6 @@ func (u *MysqlService) LoadFromRemote(from string) error {
if err := copier.Copy(&createItem, &data); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
createItem.MysqlName = mysqlName
if err := mysqlRepo.Create(context.Background(), &createItem); err != nil {
return err
}
@@ -186,15 +183,15 @@ func (u *MysqlService) UpdateDescription(req dto.UpdateDescription) error {
return mysqlRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
}
func (u *MysqlService) DeleteCheck(id uint) ([]string, error) {
func (u *MysqlService) DeleteCheck(req dto.MysqlDBDeleteCheck) ([]string, error) {
var appInUsed []string
db, err := mysqlRepo.Get(commonRepo.WithByID(id))
db, err := mysqlRepo.Get(commonRepo.WithByID(req.ID))
if err != nil {
return appInUsed, err
}
if db.From == "local" {
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return appInUsed, err
}
@@ -223,7 +220,7 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error
if err != nil && !req.ForceDelete {
return err
}
cli, version, err := LoadMysqlClientByFrom(db.From)
cli, version, err := LoadMysqlClientByFrom(req.Database)
if err != nil {
return err
}
@@ -234,41 +231,36 @@ func (u *MysqlService) Delete(ctx context.Context, req dto.MysqlDBDelete) error
Username: db.Username,
Permission: db.Permission,
Timeout: 300,
}); err != nil {
}); err != nil && !req.ForceDelete {
return err
}
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
if err != nil && !req.ForceDelete {
return err
}
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/database/mysql/%s/%s", app.Name, db.Name))
if _, err := os.Stat(uploadDir); err == nil {
_ = os.RemoveAll(uploadDir)
}
if req.DeleteBackup {
uploadDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/uploads/database/%s/%s/%s", req.Type, req.Database, db.Name))
if _, err := os.Stat(uploadDir); err == nil {
_ = os.RemoveAll(uploadDir)
}
localDir, err := loadLocalDir()
if err != nil && !req.ForceDelete {
return err
}
backupDir := path.Join(localDir, fmt.Sprintf("database/mysql/%s/%s", db.MysqlName, db.Name))
backupDir := path.Join(localDir, fmt.Sprintf("database/%s/%s/%s", req.Type, db.MysqlName, db.Name))
if _, err := os.Stat(backupDir); err == nil {
_ = os.RemoveAll(backupDir)
}
global.LOG.Infof("delete database %s-%s backups successful", app.Name, db.Name)
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType(req.Type), commonRepo.WithByName(req.Database), backupRepo.WithByDetailName(db.Name))
global.LOG.Infof("delete database %s-%s backups successful", req.Database, db.Name)
}
_ = backupRepo.DeleteRecord(ctx, commonRepo.WithByType("mysql"), commonRepo.WithByName(app.Name), backupRepo.WithByDetailName(db.Name))
_ = mysqlRepo.Delete(ctx, commonRepo.WithByID(db.ID))
return nil
}
func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
if cmd.CheckIllegal(info.Value) {
func (u *MysqlService) ChangePassword(req dto.ChangeDBInfo) error {
if cmd.CheckIllegal(req.Value) {
return buserr.New(constant.ErrCmdIllegal)
}
cli, version, err := LoadMysqlClientByFrom(info.From)
cli, version, err := LoadMysqlClientByFrom(req.Database)
if err != nil {
return err
}
@@ -277,12 +269,12 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
mysqlData model.DatabaseMysql
passwordInfo client.PasswordChangeInfo
)
passwordInfo.Password = info.Value
passwordInfo.Password = req.Value
passwordInfo.Timeout = 300
passwordInfo.Version = version
if info.ID != 0 {
mysqlData, err = mysqlRepo.Get(commonRepo.WithByID(info.ID))
if req.ID != 0 {
mysqlData, err = mysqlRepo.Get(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
@@ -296,10 +288,10 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
return err
}
if info.ID != 0 {
if req.ID != 0 {
var appRess []model.AppInstallResource
if info.From == "local" {
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
if req.From == "local" {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return err
}
@@ -318,29 +310,30 @@ func (u *MysqlService) ChangePassword(info dto.ChangeDBInfo) error {
}
global.LOG.Infof("start to update mysql password used by app %s-%s", appModel.Key, appInstall.Name)
if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", true, info.Value); err != nil {
if err := updateInstallInfoInDB(appModel.Key, appInstall.Name, "user-password", true, req.Value); err != nil {
return err
}
}
global.LOG.Info("excute password change sql successful")
_ = mysqlRepo.Update(mysqlData.ID, map[string]interface{}{"password": info.Value})
global.LOG.Info("execute password change sql successful")
pass, err := encrypt.StringEncrypt(req.Value)
if err != nil {
return fmt.Errorf("decrypt database db password failed, err: %v", err)
}
_ = mysqlRepo.Update(mysqlData.ID, map[string]interface{}{"password": pass})
return nil
}
if err := updateInstallInfoInDB("mysql", "", "password", false, info.Value); err != nil {
return err
}
if err := updateInstallInfoInDB("phpmyadmin", "", "password", true, info.Value); err != nil {
if err := updateInstallInfoInDB(req.Type, req.Database, "password", false, req.Value); err != nil {
return err
}
return nil
}
func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
if cmd.CheckIllegal(info.Value) {
func (u *MysqlService) ChangeAccess(req dto.ChangeDBInfo) error {
if cmd.CheckIllegal(req.Value) {
return buserr.New(constant.ErrCmdIllegal)
}
cli, version, err := LoadMysqlClientByFrom(info.From)
cli, version, err := LoadMysqlClientByFrom(req.Database)
if err != nil {
return err
}
@@ -349,12 +342,12 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
mysqlData model.DatabaseMysql
accessInfo client.AccessChangeInfo
)
accessInfo.Permission = info.Value
accessInfo.Permission = req.Value
accessInfo.Timeout = 300
accessInfo.Version = version
if info.ID != 0 {
mysqlData, err = mysqlRepo.Get(commonRepo.WithByID(info.ID))
if req.ID != 0 {
mysqlData, err = mysqlRepo.Get(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
@@ -370,40 +363,40 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
}
if mysqlData.ID != 0 {
_ = mysqlRepo.Update(mysqlData.ID, map[string]interface{}{"permission": info.Value})
_ = mysqlRepo.Update(mysqlData.ID, map[string]interface{}{"permission": req.Value})
}
return nil
}
func (u *MysqlService) UpdateConfByFile(info dto.MysqlConfUpdateByFile) error {
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
func (u *MysqlService) UpdateConfByFile(req dto.MysqlConfUpdateByFile) error {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return err
}
path := fmt.Sprintf("%s/mysql/%s/conf/my.cnf", constant.AppInstallDir, app.Name)
path := fmt.Sprintf("%s/%s/%s/conf/my.cnf", constant.AppInstallDir, req.Type, app.Name)
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(info.File)
_, _ = write.WriteString(req.File)
write.Flush()
if _, err := compose.Restart(fmt.Sprintf("%s/mysql/%s/docker-compose.yml", constant.AppInstallDir, app.Name)); err != nil {
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, req.Type, app.Name)); err != nil {
return err
}
return nil
}
func (u *MysqlService) UpdateVariables(updates []dto.MysqlVariablesUpdate) error {
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
func (u *MysqlService) UpdateVariables(req dto.MysqlVariablesUpdate) error {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Database)
if err != nil {
return err
}
var files []string
path := fmt.Sprintf("%s/mysql/%s/conf/my.cnf", constant.AppInstallDir, app.Name)
path := fmt.Sprintf("%s/%s/%s/conf/my.cnf", constant.AppInstallDir, req.Type, app.Name)
lineBytes, err := os.ReadFile(path)
if err != nil {
return err
@@ -411,7 +404,7 @@ func (u *MysqlService) UpdateVariables(updates []dto.MysqlVariablesUpdate) error
files = strings.Split(string(lineBytes), "\n")
group := "[mysqld]"
for _, info := range updates {
for _, info := range req.Variables {
if !strings.HasPrefix(app.Version, "5.7") && !strings.HasPrefix(app.Version, "5.6") {
if info.Param == "query_cache_size" {
continue
@@ -434,16 +427,16 @@ func (u *MysqlService) UpdateVariables(updates []dto.MysqlVariablesUpdate) error
return err
}
if _, err := compose.Restart(fmt.Sprintf("%s/mysql/%s/docker-compose.yml", constant.AppInstallDir, app.Name)); err != nil {
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, req.Type, app.Name)); err != nil {
return err
}
return nil
}
func (u *MysqlService) LoadBaseInfo() (*dto.DBBaseInfo, error) {
func (u *MysqlService) LoadBaseInfo(req dto.OperationWithNameAndType) (*dto.DBBaseInfo, error) {
var data dto.DBBaseInfo
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
if err != nil {
return nil, err
}
@@ -454,8 +447,8 @@ func (u *MysqlService) LoadBaseInfo() (*dto.DBBaseInfo, error) {
return &data, nil
}
func (u *MysqlService) LoadRemoteAccess() (bool, error) {
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
func (u *MysqlService) LoadRemoteAccess(req dto.OperationWithNameAndType) (bool, error) {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
if err != nil {
return false, err
}
@@ -472,8 +465,8 @@ func (u *MysqlService) LoadRemoteAccess() (bool, error) {
return false, nil
}
func (u *MysqlService) LoadVariables() (*dto.MysqlVariables, error) {
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
func (u *MysqlService) LoadVariables(req dto.OperationWithNameAndType) (*dto.MysqlVariables, error) {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
if err != nil {
return nil, err
}
@@ -490,8 +483,8 @@ func (u *MysqlService) LoadVariables() (*dto.MysqlVariables, error) {
return &info, nil
}
func (u *MysqlService) LoadStatus() (*dto.MysqlStatus, error) {
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
func (u *MysqlService) LoadStatus(req dto.OperationWithNameAndType) (*dto.MysqlStatus, error) {
app, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name)
if err != nil {
return nil, err
}
@@ -540,10 +533,14 @@ func (u *MysqlService) LoadDatabaseFile(req dto.OperationWithNameAndType) (strin
switch req.Type {
case "mysql-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/conf/my.cnf", req.Name))
case "mariadb-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/conf/my.cnf", req.Name))
case "redis-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
case "slow-logs":
case "mysql-slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
case "mariadb-slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mariadb/%s/db/data/1Panel-slow.log", req.Name))
}
if _, err := os.Stat(filePath); err != nil {
return "", buserr.New("ErrHttpReqNotFound")
@@ -625,20 +622,21 @@ func updateMyCnf(oldFiles []string, group string, param string, value interface{
return newFiles
}
func LoadMysqlClientByFrom(from string) (mysql.MysqlClient, string, error) {
func LoadMysqlClientByFrom(database string) (mysql.MysqlClient, string, error) {
var (
dbInfo client.DBInfo
version string
err error
)
dbInfo.From = from
dbInfo.Timeout = 300
if from != "local" {
databaseItem, err := remoteDBRepo.Get(commonRepo.WithByName(from))
if err != nil {
return nil, "", err
}
databaseItem, err := databaseRepo.Get(commonRepo.WithByName(database))
if err != nil {
return nil, "", err
}
dbInfo.From = databaseItem.From
dbInfo.Database = database
if dbInfo.From != "local" {
dbInfo.Address = databaseItem.Address
dbInfo.Port = databaseItem.Port
dbInfo.Username = databaseItem.Username
@@ -646,7 +644,7 @@ func LoadMysqlClientByFrom(from string) (mysql.MysqlClient, string, error) {
version = databaseItem.Version
} else {
app, err := appInstallRepo.LoadBaseInfo("mysql", "")
app, err := appInstallRepo.LoadBaseInfo(databaseItem.Type, database)
if err != nil {
return nil, "", err
}
+2 -2
View File
@@ -21,7 +21,7 @@ type RedisService struct{}
type IRedisService interface {
UpdateConf(req dto.RedisConfUpdate) error
UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error
ChangePassword(info dto.ChangeDBInfo) error
ChangePassword(info dto.ChangeRedisPass) error
LoadStatus() (*dto.RedisStatus, error)
LoadConf() (*dto.RedisConf, error)
@@ -54,7 +54,7 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
return nil
}
func (u *RedisService) ChangePassword(req dto.ChangeDBInfo) error {
func (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error {
if err := updateInstallInfoInDB("redis", "", "password", true, req.Value); err != nil {
return err
}
+1 -1
View File
@@ -13,7 +13,7 @@ var (
appInstallResourceRepo = repo.NewIAppInstallResourceRpo()
mysqlRepo = repo.NewIMysqlRepo()
remoteDBRepo = repo.NewIRemoteDBRepo()
databaseRepo = repo.NewIDatabaseRepo()
imageRepoRepo = repo.NewIImageRepoRepo()
composeRepo = repo.NewIComposeTemplateRepo()
+229 -62
View File
@@ -5,14 +5,18 @@ import (
"os"
"strconv"
"strings"
"sync"
"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/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/firewall"
fireClient "github.com/1Panel-dev/1Panel/backend/utils/firewall/client"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
const confPath = "/etc/sysctl.conf"
@@ -27,6 +31,7 @@ type IFirewallService interface {
OperateAddressRule(req dto.AddrRuleOperate, reload bool) error
UpdatePortRule(req dto.PortRuleUpdate) error
UpdateAddrRule(req dto.AddrRuleUpdate) error
UpdateDescription(req dto.UpdateFirewallDescription) error
BatchOperateRule(req dto.BatchRuleOperate) error
}
@@ -36,7 +41,6 @@ func NewIFirewallService() IFirewallService {
func (u *FirewallService) LoadBaseInfo() (dto.FirewallBaseInfo, error) {
var baseInfo dto.FirewallBaseInfo
baseInfo.PingStatus = u.pingStatus()
baseInfo.Status = "not running"
baseInfo.Version = "-"
baseInfo.Name = "-"
@@ -48,17 +52,22 @@ func (u *FirewallService) LoadBaseInfo() (dto.FirewallBaseInfo, error) {
return baseInfo, err
}
baseInfo.Name = client.Name()
baseInfo.Status, err = client.Status()
if err != nil {
return baseInfo, err
}
if baseInfo.Status == "not running" {
return baseInfo, err
}
baseInfo.Version, err = client.Version()
if err != nil {
return baseInfo, err
}
var wg sync.WaitGroup
wg.Add(3)
go func() {
defer wg.Done()
baseInfo.PingStatus = u.pingStatus()
}()
go func() {
defer wg.Done()
baseInfo.Status, _ = client.Status()
}()
go func() {
defer wg.Done()
baseInfo.Version, _ = client.Version()
}()
wg.Wait()
return baseInfo, nil
}
@@ -67,6 +76,7 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
datas []fireClient.FireInfo
backDatas []fireClient.FireInfo
)
client, err := firewall.NewFirewallClient()
if err != nil {
return 0, nil, err
@@ -100,14 +110,61 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
datas = addrs
}
}
total, start, end := len(datas), (req.Page-1)*req.PageSize, req.Page*req.PageSize
var datasFilterStatus []fireClient.FireInfo
if len(req.Status) != 0 {
for _, data := range datas {
portItem, _ := strconv.Atoi(data.Port)
if req.Status == "free" && !common.ScanPortWithProto(portItem, data.Protocol) {
datasFilterStatus = append(datasFilterStatus, data)
}
if req.Status == "used" && common.ScanPortWithProto(portItem, data.Protocol) {
datasFilterStatus = append(datasFilterStatus, data)
}
}
} else {
datasFilterStatus = datas
}
var datasFilterStrategy []fireClient.FireInfo
if len(req.Strategy) != 0 {
for _, data := range datasFilterStatus {
if req.Strategy == data.Strategy {
datasFilterStrategy = append(datasFilterStrategy, data)
}
}
} else {
datasFilterStrategy = datasFilterStatus
}
total, start, end := len(datasFilterStrategy), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total {
backDatas = make([]fireClient.FireInfo, 0)
} else {
if end >= total {
end = total
}
backDatas = datas[start:end]
backDatas = datasFilterStrategy[start:end]
}
datasFromDB, _ := hostRepo.ListFirewallRecord()
for i := 0; i < len(backDatas); i++ {
for _, des := range datasFromDB {
if req.Type != des.Type {
continue
}
if backDatas[i].Port == des.Port &&
req.Type == "port" &&
backDatas[i].Protocol == des.Protocol &&
backDatas[i].Strategy == des.Strategy &&
backDatas[i].Address == des.Address {
backDatas[i].Description = des.Description
break
}
if req.Type == "address" && backDatas[i].Strategy == des.Strategy && backDatas[i].Address == des.Address {
backDatas[i].Description = des.Description
break
}
}
}
if req.Type == "port" {
@@ -127,6 +184,7 @@ func (u *FirewallService) SearchWithPage(req dto.RuleSearch) (int64, interface{}
}
}
}
go u.cleanUnUsedData(client)
return int64(total), backDatas, nil
}
@@ -166,55 +224,92 @@ func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool)
if err != nil {
return err
}
protos := strings.Split(req.Protocol, "/")
itemAddress := strings.Split(strings.TrimSuffix(req.Address, ","), ",")
var wg sync.WaitGroup
if client.Name() == "ufw" {
req.Port = strings.ReplaceAll(req.Port, "-", ":")
if req.Operation == "remove" && req.Protocol == "tcp/udp" {
req.Protocol = ""
return u.operatePort(client, req)
if strings.Contains(req.Port, ",") || strings.Contains(req.Port, "-") {
for _, proto := range protos {
for _, addr := range itemAddress {
if len(addr) == 0 {
addr = "Anywhere"
}
req.Address = addr
req.Port = strings.ReplaceAll(req.Port, "-", ":")
req.Protocol = proto
wg.Add(1)
go func(req dto.PortRuleOperate) {
defer wg.Done()
if err := u.operatePort(client, req); err != nil {
global.LOG.Errorf("%s port %s/%s failed (strategy: %s, address: %s), err: %v", req.Operation, req.Port, req.Protocol, req.Strategy, req.Address, err)
}
_ = u.addPortRecord(req)
}(req)
}
}
return nil
}
if req.Protocol == "tcp/udp" {
req.Protocol = ""
}
for _, addr := range itemAddress {
if len(addr) == 0 {
addr = "Anywhere"
}
req.Address = addr
wg.Add(1)
go func(req dto.PortRuleOperate) {
defer wg.Done()
if err := u.operatePort(client, req); err != nil {
global.LOG.Errorf("%s port %s/%s failed (strategy: %s, address: %s), err: %v", req.Operation, req.Port, req.Protocol, req.Strategy, req.Address, err)
}
_ = u.addPortRecord(req)
}(req)
}
wg.Wait()
return nil
}
if req.Protocol == "tcp/udp" {
if client.Name() == "firewalld" && strings.Contains(req.Port, ",") {
ports := strings.Split(req.Port, ",")
itemPorts := req.Port
for _, proto := range protos {
if strings.Contains(req.Port, "-") {
for _, addr := range itemAddress {
req.Protocol = proto
req.Address = addr
wg.Add(1)
go func(req dto.PortRuleOperate) {
defer wg.Done()
if err := u.operatePort(client, req); err != nil {
global.LOG.Errorf("%s port %s/%s failed (strategy: %s, address: %s), err: %v", req.Operation, req.Port, req.Protocol, req.Strategy, req.Address, err)
}
_ = u.addPortRecord(req)
}(req)
}
} else {
ports := strings.Split(itemPorts, ",")
for _, port := range ports {
if len(port) == 0 {
continue
}
req.Port = port
req.Protocol = "tcp"
if err := u.operatePort(client, req); err != nil {
return err
for _, addr := range itemAddress {
req.Address = addr
req.Port = port
req.Protocol = proto
wg.Add(1)
go func(req dto.PortRuleOperate) {
defer wg.Done()
if err := u.operatePort(client, req); err != nil {
global.LOG.Errorf("%s port %s/%s failed (strategy: %s, address: %s), err: %v", req.Operation, req.Port, req.Protocol, req.Strategy, req.Address, err)
}
_ = u.addPortRecord(req)
}(req)
}
req.Protocol = "udp"
if err := u.operatePort(client, req); err != nil {
return err
}
}
} else {
req.Protocol = "tcp"
if err := u.operatePort(client, req); err != nil {
return err
}
req.Protocol = "udp"
if err := u.operatePort(client, req); err != nil {
return err
}
}
} else {
if strings.Contains(req.Port, ",") {
ports := strings.Split(req.Port, ",")
for _, port := range ports {
req.Port = port
if err := u.operatePort(client, req); err != nil {
return err
}
}
} else {
if err := u.operatePort(client, req); err != nil {
return err
}
}
}
wg.Wait()
if reload {
return client.Reload()
}
@@ -232,16 +327,24 @@ func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate, reload boo
return err
}
var wg sync.WaitGroup
addressList := strings.Split(req.Address, ",")
for _, addr := range addressList {
if len(addr) == 0 {
continue
}
fireInfo.Address = addr
if err := client.RichRules(fireInfo, req.Operation); err != nil {
return err
}
wg.Add(1)
go func(req dto.AddrRuleOperate) {
defer wg.Done()
if err := client.RichRules(fireInfo, req.Operation); err != nil {
global.LOG.Errorf("%s address %s failed (strategy: %s), err: %v", req.Operation, req.Address, req.Strategy, err)
}
req.Address = fireInfo.Address
_ = u.addAddressRecord(req)
}(req)
}
wg.Wait()
if reload {
return client.Reload()
}
@@ -276,24 +379,38 @@ func (u *FirewallService) UpdateAddrRule(req dto.AddrRuleUpdate) error {
return client.Reload()
}
func (u *FirewallService) UpdateDescription(req dto.UpdateFirewallDescription) error {
var firewall model.Firewall
if err := copier.Copy(&firewall, &req); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
return hostRepo.SaveFirewallRecord(&firewall)
}
func (u *FirewallService) BatchOperateRule(req dto.BatchRuleOperate) error {
client, err := firewall.NewFirewallClient()
if err != nil {
return err
}
var wgBatch sync.WaitGroup
if req.Type == "port" {
for _, rule := range req.Rules {
if err := u.OperatePortRule(rule, false); err != nil {
return err
}
wgBatch.Add(1)
go func(item dto.PortRuleOperate) {
defer wgBatch.Done()
_ = u.OperatePortRule(item, false)
}(rule)
}
wgBatch.Wait()
return client.Reload()
}
for _, rule := range req.Rules {
itemRule := dto.AddrRuleOperate{Operation: rule.Operation, Address: rule.Address, Strategy: rule.Strategy}
if err := u.OperateAddressRule(itemRule, false); err != nil {
return err
}
wgBatch.Add(1)
go func(item dto.AddrRuleOperate) {
defer wgBatch.Done()
_ = u.OperateAddressRule(item, false)
}(itemRule)
}
return client.Reload()
}
@@ -323,7 +440,7 @@ func (u *FirewallService) operatePort(client firewall.FirewallClient, req dto.Po
}
if client.Name() == "ufw" {
if len(fireInfo.Address) != 0 && fireInfo.Address != "Anywhere" {
if len(fireInfo.Address) != 0 && !strings.EqualFold(fireInfo.Address, "Anywhere") {
return client.RichRules(fireInfo, req.Operation)
}
return client.Port(fireInfo, req.Operation)
@@ -363,6 +480,29 @@ func (u *FirewallService) loadPortByApp() []portOfApp {
return datas
}
func (u *FirewallService) cleanUnUsedData(client firewall.FirewallClient) {
list, _ := client.ListPort()
addressList, _ := client.ListAddress()
list = append(list, addressList...)
if len(list) == 0 {
return
}
records, _ := hostRepo.ListFirewallRecord()
if len(records) == 0 {
return
}
for _, item := range list {
for i := 0; i < len(records); i++ {
if records[i].Port == item.Port && records[i].Protocol == item.Protocol && records[i].Strategy == item.Strategy && records[i].Address == item.Address {
records = append(records[:i], records[i+1:]...)
}
}
}
for _, record := range records {
_ = hostRepo.DeleteFirewallRecordByID(record.ID)
}
}
func (u *FirewallService) pingStatus() string {
if _, err := os.Stat("/etc/sysctl.conf"); err != nil {
return constant.StatusNone
@@ -441,3 +581,30 @@ func (u *FirewallService) addPortsBeforeStart(client firewall.FirewallClient) er
return client.Reload()
}
func (u *FirewallService) addPortRecord(req dto.PortRuleOperate) error {
if req.Operation == "remove" {
return hostRepo.DeleteFirewallRecord("port", req.Port, req.Protocol, req.Address, req.Strategy)
}
return hostRepo.SaveFirewallRecord(&model.Firewall{
Type: "port",
Port: req.Port,
Protocol: req.Protocol,
Address: req.Address,
Strategy: req.Strategy,
Description: req.Description,
})
}
func (u *FirewallService) addAddressRecord(req dto.AddrRuleOperate) error {
if req.Operation == "remove" {
return hostRepo.DeleteFirewallRecord("address", "", "", req.Address, req.Strategy)
}
return hostRepo.SaveFirewallRecord(&model.Firewall{
Type: "address",
Address: req.Address,
Strategy: req.Strategy,
Description: req.Description,
})
}
+44 -63
View File
@@ -3,14 +3,6 @@ package service
import (
"bytes"
"fmt"
"os/exec"
"os/user"
"path"
"strconv"
"strings"
"sync"
"time"
"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"
@@ -22,6 +14,11 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
"github.com/pkg/errors"
"gopkg.in/ini.v1"
"os/exec"
"os/user"
"path"
"strconv"
"strings"
)
type HostToolService struct{}
@@ -34,7 +31,6 @@ type IHostToolService interface {
GetToolLog(req request.HostToolLogReq) (string, error)
OperateSupervisorProcess(req request.SupervisorProcessConfig) error
GetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error)
LoadProcessStatus() []response.ProcessStatus
OperateSupervisorProcessFile(req request.SupervisorProcessFileReq) (string, error)
}
@@ -327,7 +323,10 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
if err = configFile.SaveTo(iniPath); err != nil {
return err
}
return operateSupervisorCtl("reload", "", "")
if err := operateSupervisorCtl("reread", "", ""); err != nil {
return err
}
return operateSupervisorCtl("update", "", "")
case "update":
configFile, err := ini.Load(iniPath)
if err != nil {
@@ -350,7 +349,10 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
if err = configFile.SaveTo(iniPath); err != nil {
return err
}
return operateSupervisorCtl("reload", "", "")
if err := operateSupervisorCtl("reread", "", ""); err != nil {
return err
}
return operateSupervisorCtl("update", "", "")
case "restart":
return operateSupervisorCtl("restart", req.Name, "")
case "start":
@@ -362,63 +364,15 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
_ = files.NewFileOp().DeleteFile(iniPath)
_ = files.NewFileOp().DeleteFile(outLog)
_ = files.NewFileOp().DeleteFile(errLog)
_ = operateSupervisorCtl("reload", "", "")
if err := operateSupervisorCtl("reread", "", ""); err != nil {
return err
}
return operateSupervisorCtl("update", "", "")
}
return nil
}
func (h *HostToolService) LoadProcessStatus() []response.ProcessStatus {
var datas []response.ProcessStatus
statuLines, err := cmd.Exec("supervisorctl status")
if err != nil {
return datas
}
lines := strings.Split(string(statuLines), "\n")
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) > 1 {
datas = append(datas, response.ProcessStatus{Name: fields[0]})
}
}
var wg sync.WaitGroup
wg.Add(len(datas))
for i := 0; i < len(datas); i++ {
go func(index int) {
for t := 0; t < 3; t++ {
status, err := cmd.ExecWithTimeOut(fmt.Sprintf("supervisorctl status %s", datas[index].Name), 3*time.Second)
if err != nil {
time.Sleep(2 * time.Second)
continue
}
fields := strings.Fields(status)
if len(fields) < 5 {
time.Sleep(2 * time.Second)
continue
}
datas[index].Name = fields[0]
datas[index].Status = fields[1]
if fields[1] != "RUNNING" {
datas[index].Msg = strings.Join(fields[2:], " ")
time.Sleep(1 * time.Second)
continue
}
datas[index].PID = strings.TrimSuffix(fields[3], ",")
datas[index].Uptime = fields[5]
break
}
if len(datas[index].Status) == 0 {
datas[index].Status = "FATAL"
datas[index].Msg = "Timeout for getting process status"
}
wg.Done()
}(i)
}
wg.Wait()
return datas
}
func (h *HostToolService) GetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error) {
var (
result []response.SupervisorProcessConfig
@@ -455,6 +409,7 @@ func (h *HostToolService) GetSupervisorProcessConfig() ([]response.SupervisorPro
if numprocs, _ := section.GetKey("numprocs"); numprocs != nil {
config.Numprocs = numprocs.Value()
}
_ = getProcessStatus(&config)
result = append(result, config)
}
}
@@ -582,3 +537,29 @@ func getProcessName(name, numprocs string) []string {
}
return processNames
}
func getProcessStatus(config *response.SupervisorProcessConfig) error {
var (
processNames = []string{"status"}
)
processNames = append(processNames, getProcessName(config.Name, config.Numprocs)...)
output, _ := exec.Command("supervisorctl", processNames...).Output()
lines := strings.Split(string(output), "\n")
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) >= 5 {
status := response.ProcessStatus{
Name: fields[0],
Status: fields[1],
}
if fields[1] == "RUNNING" {
status.PID = strings.TrimSuffix(fields[3], ",")
status.Uptime = fields[5]
} else {
status.Msg = strings.Join(fields[2:], " ")
}
config.Status = append(config.Status, status)
}
}
return nil
}
+6 -1
View File
@@ -123,7 +123,6 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
return "", err
}
fileName := "Dockerfile"
dockerLogDir := path.Join(global.CONF.System.TmpDir, "docker_logs")
if req.From == "edit" {
dir := fmt.Sprintf("%s/docker/build/%s", constant.DataDir, strings.ReplaceAll(req.Name, ":", "_"))
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
@@ -158,6 +157,12 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
Labels: stringsToMap(req.Tags),
}
dockerLogDir := path.Join(global.CONF.System.TmpDir, "docker_logs")
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
return "", err
}
}
logItem := fmt.Sprintf("%s/image_build_%s_%s.log", dockerLogDir, strings.ReplaceAll(req.Name, ":", "_"), time.Now().Format("20060102150405"))
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
+10 -1
View File
@@ -1,6 +1,7 @@
package service
import (
"fmt"
"io"
"net/http"
"os"
@@ -61,7 +62,15 @@ func (n NginxService) UpdateConfigByScope(req request.NginxConfigUpdate) error {
}
func (n NginxService) GetStatus() (response.NginxStatus, error) {
res, err := http.Get("http://127.0.0.1/nginx_status")
httpPort, _, err := getAppInstallPort(constant.AppOpenresty)
if err != nil {
return response.NginxStatus{}, err
}
url := "http://127.0.0.1/nginx_status"
if httpPort != 80 {
url = fmt.Sprintf("http://127.0.0.1:%v/nginx_status", httpPort)
}
res, err := http.Get(url)
if err != nil {
return response.NginxStatus{}, err
}
+7 -4
View File
@@ -94,7 +94,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
return
}
}
composeContent, envContent, forms, err := handleParams(create.Image, create.Type, newNameDir, create.Params)
composeContent, envContent, forms, err := handleParams(create.Image, create.Type, newNameDir, create.Source, create.Params)
if err != nil {
return
}
@@ -113,7 +113,7 @@ func (r *RuntimeService) Create(create request.RuntimeCreate) (err error) {
if err = runtimeRepo.Create(context.Background(), runtime); err != nil {
return
}
go buildRuntime(runtime, "")
go buildRuntime(runtime, "", false)
return
}
@@ -197,6 +197,9 @@ func (r *RuntimeService) Get(id uint) (*response.RuntimeRes, error) {
if err != nil {
return nil, err
}
if v, ok := envs["CONTAINER_PACKAGE_URL"]; ok {
res.Source = v
}
for _, form := range appForm.FormFields {
if v, ok := envs[form.EnvKey]; ok {
appParam := response.AppParam{
@@ -252,7 +255,7 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
return buserr.New(constant.ErrImageExist)
}
runtimeDir := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
composeContent, envContent, _, err := handleParams(req.Image, runtime.Type, runtimeDir, req.Params)
composeContent, envContent, _, err := handleParams(req.Image, runtime.Type, runtimeDir, req.Source, req.Params)
if err != nil {
return err
}
@@ -272,6 +275,6 @@ func (r *RuntimeService) Update(req request.RuntimeUpdate) error {
if err != nil {
return err
}
go buildRuntime(runtime, imageID)
go buildRuntime(runtime, imageID, req.Rebuild)
return nil
}
+27 -2
View File
@@ -3,6 +3,7 @@ package service
import (
"bytes"
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -17,7 +18,7 @@ import (
"strings"
)
func buildRuntime(runtime *model.Runtime, oldImageID string) {
func buildRuntime(runtime *model.Runtime, oldImageID string, rebuild bool) {
runtimePath := path.Join(constant.RuntimeDir, runtime.Type, runtime.Name)
composePath := path.Join(runtimePath, "docker-compose.yml")
logPath := path.Join(runtimePath, "build.log")
@@ -61,11 +62,34 @@ func buildRuntime(runtime *model.Runtime, oldImageID string) {
global.LOG.Errorf("delete imageID [%s] error %v", oldImageID, err)
}
}
if rebuild && runtime.ID > 0 {
websites, _ := websiteRepo.GetBy(websiteRepo.WithRuntimeID(runtime.ID))
if len(websites) > 0 {
installService := NewIAppInstalledService()
installMap := make(map[uint]string)
for _, website := range websites {
if website.AppInstallID > 0 {
installMap[website.AppInstallID] = website.PrimaryDomain
}
}
for installID, domain := range installMap {
go func(installID uint, domain string) {
global.LOG.Infof("rebuild php runtime [%s] domain [%s]", runtime.Name, domain)
if err := installService.Operate(request.AppInstalledOperate{
InstallId: installID,
Operate: constant.Rebuild,
}); err != nil {
global.LOG.Errorf("rebuild php runtime [%s] domain [%s] error %v", runtime.Name, domain, err)
}
}(installID, domain)
}
}
}
}
_ = runtimeRepo.Save(runtime)
}
func handleParams(image, runtimeType, runtimeDir string, params map[string]interface{}) (composeContent []byte, envContent []byte, forms []byte, err error) {
func handleParams(image, runtimeType, runtimeDir, source string, params map[string]interface{}) (composeContent []byte, envContent []byte, forms []byte, err error) {
fileOp := files.NewFileOp()
composeContent, err = fileOp.GetContent(path.Join(runtimeDir, "docker-compose.yml"))
if err != nil {
@@ -90,6 +114,7 @@ func handleParams(image, runtimeType, runtimeDir string, params map[string]inter
params["PHP_EXTENSIONS"] = strings.Join(strArray, ",")
}
}
params["CONTAINER_PACKAGE_URL"] = source
}
newMap := make(map[string]string)
handleMap(params, newMap)
+7
View File
@@ -103,6 +103,12 @@ func (u *SettingService) Update(key, value string) error {
if err := ntp.UpdateSystemTimeZone(value); err != nil {
return err
}
case "AppStoreLastModified":
exist, _ := settingRepo.Get(settingRepo.WithByKey("AppStoreLastModified"))
if exist.ID == 0 {
_ = settingRepo.Create("AppStoreLastModified", value)
return nil
}
}
if err := settingRepo.Update(key, value); err != nil {
@@ -128,6 +134,7 @@ func (u *SettingService) Update(key, value string) error {
}
case "UserName", "Password":
_ = global.SESSION.Clean()
}
return nil
+233 -200
View File
@@ -9,6 +9,7 @@ import (
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -16,6 +17,7 @@ import (
"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/compose"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/jinzhu/copier"
@@ -34,8 +36,12 @@ type ISnapshotService interface {
SnapshotImport(req dto.SnapshotImport) error
Delete(req dto.BatchDeleteReq) error
LoadSnapShotStatus(id uint) (*dto.SnapshotStatus, error)
UpdateDescription(req dto.UpdateDescription) error
readFromJson(path string) (SnapshotJson, error)
HandleSnapshot(isCronjob bool, req dto.SnapshotCreate, timeNow string) (string, string, error)
}
func NewISnapshotService() ISnapshotService {
@@ -66,13 +72,14 @@ func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
}
}
for _, snap := range req.Names {
nameItems := strings.Split(snap, "_")
if !strings.HasPrefix(snap, "1panel_v") || !strings.HasSuffix(snap, ".tar.gz") || len(nameItems) != 3 {
return fmt.Errorf("incorrect snapshot name format of %s", snap)
shortName := strings.TrimPrefix(snap, "snapshot_")
nameItems := strings.Split(shortName, "_")
if !strings.HasPrefix(shortName, "1panel_v") || !strings.HasSuffix(shortName, ".tar.gz") || len(nameItems) != 3 {
return fmt.Errorf("incorrect snapshot name format of %s", shortName)
}
formatTime, err := time.Parse("20060102150405", strings.ReplaceAll(nameItems[2], ".tar.gz", ""))
if err != nil {
return fmt.Errorf("incorrect snapshot name format of %s", snap)
return fmt.Errorf("incorrect snapshot name format of %s", shortName)
}
if strings.HasSuffix(snap, ".tar.gz") {
snap = strings.ReplaceAll(snap, ".tar.gz", "")
@@ -99,6 +106,18 @@ func (u *SnapshotService) UpdateDescription(req dto.UpdateDescription) error {
return snapshotRepo.Update(req.ID, map[string]interface{}{"description": req.Description})
}
func (u *SnapshotService) LoadSnapShotStatus(id uint) (*dto.SnapshotStatus, error) {
var data dto.SnapshotStatus
status, err := snapshotRepo.GetStatus(id)
if err != nil {
return nil, err
}
if err := copier.Copy(&data, &status); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
return &data, nil
}
type SnapshotJson struct {
OldBaseDir string `json:"oldBaseDir"`
OldDockerDataDir string `json:"oldDockerDataDir"`
@@ -113,112 +132,9 @@ type SnapshotJson struct {
}
func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
global.LOG.Info("start to create snapshot now")
localDir, err := loadLocalDir()
if err != nil {
if _, _, err := u.HandleSnapshot(false, req, time.Now().Format("20060102150405")); err != nil {
return err
}
backup, err := backupRepo.Get(commonRepo.WithByType(req.From))
if err != nil {
return err
}
backupAccount, err := NewIBackupService().NewClient(&backup)
if err != nil {
return err
}
timeNow := time.Now().Format("20060102150405")
versionItem, _ := settingRepo.Get(settingRepo.WithByKey("SystemVersion"))
rootDir := path.Join(localDir, fmt.Sprintf("system/1panel_%s_%s", versionItem.Value, timeNow))
backupPanelDir := fmt.Sprintf("%s/1panel", rootDir)
_ = os.MkdirAll(backupPanelDir, os.ModePerm)
backupDockerDir := fmt.Sprintf("%s/docker", rootDir)
_ = os.MkdirAll(backupDockerDir, os.ModePerm)
_ = settingRepo.Update("SystemStatus", "Snapshoting")
snap := model.Snapshot{
Name: fmt.Sprintf("1panel_%s_%s", versionItem.Value, timeNow),
Description: req.Description,
From: req.From,
Version: versionItem.Value,
Status: constant.StatusSuccess,
}
_ = snapshotRepo.Create(&snap)
go func() {
_ = global.Cron.Stop()
defer func() {
global.Cron.Start()
_ = os.RemoveAll(rootDir)
}()
fileOp := files.NewFileOp()
snapJson := SnapshotJson{
BaseDir: global.CONF.System.BaseDir,
BackupDataDir: localDir,
}
if err := u.handleDockerDatasWithSave(fileOp, "snapshot", "", backupDockerDir); err != nil {
updateSnapshotStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
if err := u.handleDaemonJson(fileOp, "snapshot", "", backupDockerDir); err != nil {
updateSnapshotStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
if err := u.handlePanelBinary(fileOp, "snapshot", "", backupPanelDir+"/1panel"); err != nil {
updateSnapshotStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
if err := u.handlePanelctlBinary(fileOp, "snapshot", "", backupPanelDir+"/1pctl"); err != nil {
updateSnapshotStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
if err := u.handlePanelService(fileOp, "snapshot", "", backupPanelDir+"/1panel.service"); err != nil {
updateSnapshotStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
if err := u.handleBackupDatas(fileOp, "snapshot", localDir, backupPanelDir); err != nil {
updateSnapshotStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
dataDir := path.Join(global.CONF.System.BaseDir, "1panel")
if err := u.handlePanelDatas(snap.ID, fileOp, "snapshot", dataDir, backupPanelDir, localDir, snapJson.DockerDataDir); err != nil {
updateSnapshotStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
_, _ = cmd.Exec("systemctl restart docker")
snapJson.PanelDataDir = dataDir
if err := u.saveJson(snapJson, rootDir); err != nil {
updateSnapshotStatus(snap.ID, constant.StatusFailed, fmt.Sprintf("save snapshot json failed, err: %v", err))
return
}
if err := handleTar(rootDir, path.Join(localDir, "system"), fmt.Sprintf("1panel_%s_%s.tar.gz", versionItem.Value, timeNow), ""); err != nil {
updateSnapshotStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
_ = settingRepo.Update("SystemStatus", "Free")
global.LOG.Infof("start to upload snapshot to %s, please wait", backup.Type)
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading})
localPath := path.Join(localDir, fmt.Sprintf("system/1panel_%s_%s.tar.gz", versionItem.Value, timeNow))
itemBackupPath := strings.TrimPrefix(backup.BackupPath, "/")
itemBackupPath = strings.TrimSuffix(itemBackupPath, "/")
if ok, err := backupAccount.Upload(localPath, fmt.Sprintf("%s/system_snapshot/1panel_%s_%s.tar.gz", itemBackupPath, versionItem.Value, timeNow)); err != nil || !ok {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
global.LOG.Errorf("upload snapshot to %s failed, err: %v", backup.Type, err)
return
}
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
_ = os.RemoveAll(path.Join(localDir, fmt.Sprintf("system/1panel_%s_%s.tar.gz", versionItem.Value, timeNow)))
global.LOG.Infof("upload snapshot to %s success", backup.Type)
}()
return nil
}
@@ -553,14 +469,135 @@ func (u *SnapshotService) readFromJson(path string) (SnapshotJson, error) {
return snap, nil
}
func (u *SnapshotService) HandleSnapshot(isCronjob bool, req dto.SnapshotCreate, timeNow string) (string, string, error) {
localDir, err := loadLocalDir()
if err != nil {
return "", "", err
}
var (
rootDir string
snap model.Snapshot
snapStatus model.SnapshotStatus
)
if req.ID == 0 {
versionItem, _ := settingRepo.Get(settingRepo.WithByKey("SystemVersion"))
name := fmt.Sprintf("1panel_%s_%s", versionItem.Value, timeNow)
if isCronjob {
name = fmt.Sprintf("snapshot_1panel_%s_%s", versionItem.Value, timeNow)
}
rootDir = path.Join(localDir, "system", name)
snap = model.Snapshot{
Name: name,
Description: req.Description,
From: req.From,
Version: versionItem.Value,
Status: constant.StatusWaiting,
}
_ = snapshotRepo.Create(&snap)
snapStatus.SnapID = snap.ID
_ = snapshotRepo.CreateStatus(&snapStatus)
} else {
snap, err = snapshotRepo.Get(commonRepo.WithByID(req.ID))
if err != nil {
return "", "", err
}
snapStatus, _ = snapshotRepo.GetStatus(snap.ID)
if snapStatus.ID == 0 {
snapStatus.SnapID = snap.ID
_ = snapshotRepo.CreateStatus(&snapStatus)
}
rootDir = path.Join(localDir, fmt.Sprintf("system/%s", snap.Name))
}
var wg sync.WaitGroup
itemHelper := snapHelper{SnapID: snap.ID, Status: &snapStatus, Wg: &wg, FileOp: files.NewFileOp(), Ctx: context.Background()}
backupPanelDir := path.Join(rootDir, "1panel")
_ = os.MkdirAll(backupPanelDir, os.ModePerm)
backupDockerDir := path.Join(rootDir, "docker")
_ = os.MkdirAll(backupDockerDir, os.ModePerm)
jsonItem := SnapshotJson{
BaseDir: global.CONF.System.BaseDir,
BackupDataDir: localDir,
PanelDataDir: path.Join(global.CONF.System.BaseDir, "1panel"),
}
if snapStatus.PanelInfo != constant.StatusDone {
wg.Add(1)
go snapJson(itemHelper, jsonItem, rootDir)
}
if snapStatus.Panel != constant.StatusDone {
wg.Add(1)
go snapPanel(itemHelper, backupPanelDir)
}
if snapStatus.DaemonJson != constant.StatusDone {
wg.Add(1)
go snapDaemonJson(itemHelper, backupDockerDir)
}
if snapStatus.AppData != constant.StatusDone {
wg.Add(1)
go snapAppData(itemHelper, backupDockerDir)
}
if snapStatus.BackupData != constant.StatusDone {
wg.Add(1)
go snapBackup(itemHelper, localDir, backupPanelDir)
}
if snapStatus.PanelData != constant.StatusDone {
wg.Add(1)
go snapPanelData(itemHelper, localDir, backupPanelDir)
}
if !isCronjob {
go func() {
wg.Wait()
if !checkIsAllDone(snap.ID) {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
return
}
snapCompress(itemHelper, rootDir)
if snapStatus.Compress != constant.StatusDone {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
return
}
snapUpload(itemHelper, req.From, fmt.Sprintf("%s.tar.gz", rootDir))
if snapStatus.Upload != constant.StatusDone {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
return
}
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
}()
return "", "", nil
}
wg.Wait()
if !checkIsAllDone(snap.ID) {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
return loadLogByStatus(snapStatus), snap.Name, fmt.Errorf("snapshot %s backup failed", snap.Name)
}
snapCompress(itemHelper, rootDir)
if snapStatus.Compress != constant.StatusDone {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
return loadLogByStatus(snapStatus), snap.Name, fmt.Errorf("snapshot %s compress failed", snap.Name)
}
snapUpload(itemHelper, req.From, fmt.Sprintf("%s.tar.gz", rootDir))
if snapStatus.Upload != constant.StatusDone {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed})
return loadLogByStatus(snapStatus), snap.Name, fmt.Errorf("snapshot %s upload failed", snap.Name)
}
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
return loadLogByStatus(snapStatus), snap.Name, nil
}
func (u *SnapshotService) handleDockerDatas(fileOp files.FileOp, operation string, source, target string) error {
switch operation {
case "snapshot":
if err := u.handleTar(source, target, "docker_data.tar.gz", ""); err != nil {
if err := handleSnapTar(source, target, "docker_data.tar.gz", ""); err != nil {
return fmt.Errorf("backup docker data failed, err: %v", err)
}
case "recover":
if err := u.handleTar(target, u.OriginalPath, "docker_data.tar.gz", ""); err != nil {
if err := handleSnapTar(target, u.OriginalPath, "docker_data.tar.gz", ""); err != nil {
return fmt.Errorf("backup docker data failed, err: %v", err)
}
if err := u.handleUnTar(source+"/docker/docker_data.tar.gz", target); err != nil {
@@ -581,31 +618,6 @@ func (u *SnapshotService) handleDockerDatas(fileOp files.FileOp, operation strin
func (u *SnapshotService) handleDockerDatasWithSave(fileOp files.FileOp, operation, source, target string) error {
switch operation {
case "snapshot":
appInstalls, err := appInstallRepo.ListBy()
if err != nil {
return err
}
imageRegex := regexp.MustCompile(`image:\s*(.*)`)
var imageSaveList []string
existStr, _ := cmd.Exec("docker images | awk '{print $1\":\"$2}' | grep -v REPOSITORY:TAG")
existImages := strings.Split(existStr, "\n")
duplicateMap := make(map[string]bool)
for _, app := range appInstalls {
matches := imageRegex.FindAllStringSubmatch(app.DockerCompose, -1)
for _, match := range matches {
for _, existImage := range existImages {
if match[1] == existImage && !duplicateMap[match[1]] {
imageSaveList = append(imageSaveList, match[1])
duplicateMap[match[1]] = true
}
}
}
}
std, err := cmd.Execf("docker save %s | gzip -c > %s", strings.Join(imageSaveList, " "), path.Join(target, "docker_image.tar"))
if err != nil {
return errors.New(std)
}
case "recover":
if err := u.handleDockerDatasWithSave(fileOp, "snapshot", "", u.OriginalPath); err != nil {
return fmt.Errorf("backup docker data failed, err: %v", err)
@@ -663,7 +675,7 @@ func (u *SnapshotService) handlePanelBinary(fileOp files.FileOp, operation strin
if _, err := os.Stat(panelPath); err != nil {
return fmt.Errorf("1panel binary is not found in %s, err: %v", panelPath, err)
} else {
if err := cpBinary(panelPath, target); err != nil {
if err := cpBinary([]string{panelPath}, target); err != nil {
return fmt.Errorf("backup 1panel binary failed, err: %v", err)
}
}
@@ -672,7 +684,7 @@ func (u *SnapshotService) handlePanelBinary(fileOp files.FileOp, operation strin
if _, err := os.Stat(source); err != nil {
return fmt.Errorf("1panel binary is not found in snapshot, err: %v", err)
} else {
if err := cpBinary(source, "/usr/local/bin/1panel"); err != nil {
if err := cpBinary([]string{source}, "/usr/local/bin/1panel"); err != nil {
return fmt.Errorf("recover 1panel binary failed, err: %v", err)
}
}
@@ -687,7 +699,7 @@ func (u *SnapshotService) handlePanelctlBinary(fileOp files.FileOp, operation st
if _, err := os.Stat(panelctlPath); err != nil {
return fmt.Errorf("1pctl binary is not found in %s, err: %v", panelctlPath, err)
} else {
if err := cpBinary(panelctlPath, target); err != nil {
if err := cpBinary([]string{panelctlPath}, target); err != nil {
return fmt.Errorf("backup 1pctl binary failed, err: %v", err)
}
}
@@ -696,7 +708,7 @@ func (u *SnapshotService) handlePanelctlBinary(fileOp files.FileOp, operation st
if _, err := os.Stat(source); err != nil {
return fmt.Errorf("1pctl binary is not found in snapshot, err: %v", err)
} else {
if err := cpBinary(source, "/usr/local/bin/1pctl"); err != nil {
if err := cpBinary([]string{source}, "/usr/local/bin/1pctl"); err != nil {
return fmt.Errorf("recover 1pctl binary failed, err: %v", err)
}
}
@@ -711,7 +723,7 @@ func (u *SnapshotService) handlePanelService(fileOp files.FileOp, operation stri
if _, err := os.Stat(panelServicePath); err != nil {
return fmt.Errorf("1panel service is not found in %s, err: %v", panelServicePath, err)
} else {
if err := cpBinary(panelServicePath, target); err != nil {
if err := cpBinary([]string{panelServicePath}, target); err != nil {
return fmt.Errorf("backup 1panel service failed, err: %v", err)
}
}
@@ -720,7 +732,7 @@ func (u *SnapshotService) handlePanelService(fileOp files.FileOp, operation stri
if _, err := os.Stat(source); err != nil {
return fmt.Errorf("1panel service is not found in snapshot, err: %v", err)
} else {
if err := cpBinary(source, "/etc/systemd/system/1panel.service"); err != nil {
if err := cpBinary([]string{source}, "/etc/systemd/system/1panel.service"); err != nil {
return fmt.Errorf("recover 1panel service failed, err: %v", err)
}
}
@@ -731,12 +743,8 @@ func (u *SnapshotService) handlePanelService(fileOp files.FileOp, operation stri
func (u *SnapshotService) handleBackupDatas(fileOp files.FileOp, operation string, source, target string) error {
switch operation {
case "snapshot":
if err := u.handleTar(source, target, "1panel_backup.tar.gz", "./system;"); err != nil {
return fmt.Errorf("backup panel local backup dir data failed, err: %v", err)
}
case "recover":
if err := u.handleTar(target, u.OriginalPath, "1panel_backup.tar.gz", "./system;"); err != nil {
if err := handleSnapTar(target, u.OriginalPath, "1panel_backup.tar.gz", "./system;"); err != nil {
return fmt.Errorf("restore original local backup dir data failed, err: %v", err)
}
if err := u.handleUnTar(source+"/1panel/1panel_backup.tar.gz", target); err != nil {
@@ -758,7 +766,7 @@ func (u *SnapshotService) handleBackupDatas(fileOp files.FileOp, operation strin
func (u *SnapshotService) handlePanelDatas(snapID uint, fileOp files.FileOp, operation string, source, target, backupDir, dockerDir string) error {
switch operation {
case "snapshot":
exclusionRules := "./tmp;./log;./cache;./db/1Panel.db-*;"
exclusionRules := "./tmp;./log;./cache;"
if strings.Contains(backupDir, source) {
exclusionRules += ("." + strings.ReplaceAll(backupDir, source, "") + ";")
}
@@ -766,11 +774,11 @@ func (u *SnapshotService) handlePanelDatas(snapID uint, fileOp files.FileOp, ope
exclusionRules += ("." + strings.ReplaceAll(dockerDir, source, "") + ";")
}
if err := u.handleTar(source, target, "1panel_data.tar.gz", exclusionRules); err != nil {
if err := handleSnapTar(source, target, "1panel_data.tar.gz", exclusionRules); err != nil {
return fmt.Errorf("backup panel data failed, err: %v", err)
}
case "recover":
exclusionRules := "./tmp;./log;./cache;./db/1Panel.db-*;"
exclusionRules := "./tmp;./log;./cache;"
if strings.Contains(backupDir, target) {
exclusionRules += ("." + strings.ReplaceAll(backupDir, target, "") + ";")
}
@@ -779,7 +787,7 @@ func (u *SnapshotService) handlePanelDatas(snapID uint, fileOp files.FileOp, ope
}
_ = snapshotRepo.Update(snapID, map[string]interface{}{"recover_status": ""})
if err := u.handleTar(target, u.OriginalPath, "1panel_data.tar.gz", exclusionRules); err != nil {
if err := handleSnapTar(target, u.OriginalPath, "1panel_data.tar.gz", exclusionRules); err != nil {
return fmt.Errorf("restore original panel data failed, err: %v", err)
}
_ = snapshotRepo.Update(snapID, map[string]interface{}{"recover_status": constant.StatusWaiting})
@@ -821,10 +829,13 @@ func (u *SnapshotService) Delete(req dto.BatchDeleteReq) error {
return err
}
for _, snap := range backups {
itemFile := path.Join(localDir, fmt.Sprintf("system/%s/%s.tar.gz", snap.Name, snap.Name))
if _, err := os.Stat(itemFile); err == nil {
_ = os.Remove(itemFile)
}
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)
_ = snapshotRepo.DeleteStatus(snap.ID)
}
if err := snapshotRepo.Delete(commonRepo.WithIdsIn(req.Ids)); err != nil {
return err
@@ -833,18 +844,6 @@ func (u *SnapshotService) Delete(req dto.BatchDeleteReq) error {
return nil
}
func updateSnapshotStatus(id uint, status string, message string) {
if status != constant.StatusSuccess {
global.LOG.Errorf("snapshot failed, err: %s", message)
}
if err := snapshotRepo.Update(id, map[string]interface{}{
"status": status,
"message": message,
}); err != nil {
global.LOG.Errorf("update snap snapshot status failed, err: %v", err)
}
_ = settingRepo.Update("SystemStatus", "Free")
}
func updateRecoverStatus(id uint, interruptStep, status string, message string) {
if status != constant.StatusSuccess {
global.LOG.Errorf("recover failed, err: %s", message)
@@ -884,10 +883,11 @@ func updateRollbackStatus(id uint, status string, message string) {
}
}
func cpBinary(src, dst string) error {
stderr, err := cmd.Exec(fmt.Sprintf("\\cp -f %s %s", src, dst))
func cpBinary(src []string, dst string) error {
global.LOG.Debugf(fmt.Sprintf("\\cp -f %s %s", strings.Join(src, " "), dst))
stdout, err := cmd.Exec(fmt.Sprintf("\\cp -f %s %s", strings.Join(src, " "), dst))
if err != nil {
return errors.New(stderr)
return fmt.Errorf("cp file failed, stdout: %v, err: %v", stdout, err)
}
return nil
}
@@ -939,35 +939,6 @@ func (u *SnapshotService) updateLiveRestore(enabled bool) error {
return nil
}
func (u *SnapshotService) handleTar(sourceDir, targetDir, name, exclusionRules string) error {
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
return err
}
}
exStr := ""
excludes := strings.Split(exclusionRules, ";")
for _, exclude := range excludes {
if len(exclude) == 0 {
continue
}
exStr += " --exclude "
exStr += exclude
}
commands := fmt.Sprintf("tar --warning=no-file-changed -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 {
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
return fmt.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
}
}
return nil
}
func (u *SnapshotService) handleUnTar(sourceDir, targetDir string) error {
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
@@ -988,14 +959,35 @@ func (u *SnapshotService) handleUnTar(sourceDir, targetDir string) error {
}
func rebuildAllAppInstall() error {
global.LOG.Debug("start to rebuild all app")
appInstalls, err := appInstallRepo.ListBy()
if err != nil {
global.LOG.Errorf("get all app installed for rebuild failed, err: %v", err)
return err
}
for _, install := range appInstalls {
_ = rebuildApp(install)
var wg sync.WaitGroup
for i := 0; i < len(appInstalls); i++ {
wg.Add(1)
appInstalls[i].Status = constant.Rebuilding
_ = appInstallRepo.Save(context.Background(), &appInstalls[i])
go func(app model.AppInstall) {
defer wg.Done()
dockerComposePath := app.GetComposePath()
out, err := compose.Down(dockerComposePath)
if err != nil {
_ = handleErr(app, err, out)
return
}
out, err = compose.Up(dockerComposePath)
if err != nil {
_ = handleErr(app, err, out)
return
}
app.Status = constant.Running
_ = appInstallRepo.Save(context.Background(), &app)
}(appInstalls[i])
}
wg.Wait()
return nil
}
@@ -1035,3 +1027,44 @@ func min(a, b int) int {
}
return b
}
func checkIsAllDone(snapID uint) bool {
status, err := snapshotRepo.GetStatus(snapID)
if err != nil {
return false
}
if status.Panel != constant.StatusDone {
return false
}
if status.PanelInfo != constant.StatusDone {
return false
}
if status.DaemonJson != constant.StatusDone {
return false
}
if status.AppData != constant.StatusDone {
return false
}
if status.PanelData != constant.StatusDone {
return false
}
if status.BackupData != constant.StatusDone {
return false
}
return true
}
func loadLogByStatus(status model.SnapshotStatus) string {
logs := ""
logs += fmt.Sprintf("Write 1Panel basic information: %s \n", status.PanelInfo)
logs += fmt.Sprintf("Backup 1Panel system files: %s \n", status.Panel)
logs += fmt.Sprintf("Backup Docker configuration file: %s \n", status.DaemonJson)
logs += fmt.Sprintf("Backup installed apps from 1Panel: %s \n", status.AppData)
logs += fmt.Sprintf("Backup 1Panel data directory: %s \n", status.PanelData)
logs += fmt.Sprintf("Backup local backup directory for 1Panel: %s \n", status.BackupData)
logs += fmt.Sprintf("Create snapshot file: %s \n", status.Compress)
logs += fmt.Sprintf("Snapshot size: %s \n", status.Size)
logs += fmt.Sprintf("Upload snapshot file: %s \n", status.Upload)
return logs
}
+236
View File
@@ -0,0 +1,236 @@
package service
import (
"context"
"encoding/json"
"fmt"
"os"
"path"
"regexp"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
)
type snapHelper struct {
SnapID uint
Status *model.SnapshotStatus
Ctx context.Context
FileOp files.FileOp
Wg *sync.WaitGroup
}
func snapJson(snap snapHelper, snapJson SnapshotJson, targetDir string) {
defer snap.Wg.Done()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel_info": constant.Running})
status := constant.StatusDone
remarkInfo, _ := json.MarshalIndent(snapJson, "", "\t")
if err := os.WriteFile(fmt.Sprintf("%s/snapshot.json", targetDir), remarkInfo, 0640); err != nil {
status = err.Error()
}
snap.Status.PanelInfo = status
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel_info": status})
}
func snapPanel(snap snapHelper, targetDir string) {
defer snap.Wg.Done()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel": constant.Running})
status := constant.StatusDone
if err := cpBinary([]string{"/usr/local/bin/1panel", "/usr/local/bin/1pctl", "/etc/systemd/system/1panel.service"}, targetDir); err != nil {
status = err.Error()
}
snap.Status.Panel = status
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel": status})
}
func snapDaemonJson(snap snapHelper, targetDir string) {
defer snap.Wg.Done()
status := constant.StatusDone
if !snap.FileOp.Stat("/etc/docker/daemon.json") {
snap.Status.DaemonJson = status
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"daemon_json": status})
return
}
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"daemon_json": constant.Running})
if err := cpBinary([]string{"/etc/docker/daemon.json"}, path.Join(targetDir, "daemon.json")); err != nil {
status = err.Error()
}
snap.Status.DaemonJson = status
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"daemon_json": status})
}
func snapAppData(snap snapHelper, targetDir string) {
defer snap.Wg.Done()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": constant.Running})
appInstalls, err := appInstallRepo.ListBy()
if err != nil {
snap.Status.AppData = err.Error()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": err.Error()})
return
}
runtimes, err := runtimeRepo.List()
if err != nil {
snap.Status.AppData = err.Error()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": err.Error()})
return
}
imageRegex := regexp.MustCompile(`image:\s*(.*)`)
var imageSaveList []string
existStr, _ := cmd.Exec("docker images | awk '{print $1\":\"$2}' | grep -v REPOSITORY:TAG")
existImages := strings.Split(existStr, "\n")
duplicateMap := make(map[string]bool)
for _, app := range appInstalls {
matches := imageRegex.FindAllStringSubmatch(app.DockerCompose, -1)
for _, match := range matches {
for _, existImage := range existImages {
if match[1] == existImage && !duplicateMap[match[1]] {
imageSaveList = append(imageSaveList, match[1])
duplicateMap[match[1]] = true
}
}
}
}
for _, rumtime := range runtimes {
for _, existImage := range existImages {
if rumtime.Image == existImage && !duplicateMap[rumtime.Image] {
imageSaveList = append(imageSaveList, rumtime.Image)
duplicateMap[rumtime.Image] = true
}
}
}
global.LOG.Debugf("docker save %s | gzip -c > %s", strings.Join(imageSaveList, " "), path.Join(targetDir, "docker_image.tar"))
std, err := cmd.Execf("docker save %s | gzip -c > %s", strings.Join(imageSaveList, " "), path.Join(targetDir, "docker_image.tar"))
if err != nil {
snap.Status.AppData = err.Error()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": std})
return
}
snap.Status.AppData = constant.StatusDone
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"app_data": constant.StatusDone})
}
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;"); err != nil {
status = err.Error()
}
snap.Status.BackupData = status
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"backup_data": status})
}
func snapPanelData(snap snapHelper, localDir, targetDir string) {
defer snap.Wg.Done()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel_data": constant.Running})
status := constant.StatusDone
dataDir := path.Join(global.CONF.System.BaseDir, "1panel")
exclusionRules := "./tmp;./log;./cache;"
if strings.Contains(localDir, dataDir) {
exclusionRules += ("." + strings.ReplaceAll(localDir, dataDir, "") + ";")
}
_ = snapshotRepo.Update(snap.SnapID, map[string]interface{}{"status": "OnSaveData"})
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})
snap.Status.PanelData = status
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"panel_data": status})
}
func snapCompress(snap snapHelper, rootDir string) {
defer func() {
global.LOG.Debugf("remove snapshot file %s", rootDir)
_ = os.RemoveAll(rootDir)
}()
_ = 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 {
snap.Status.Compress = err.Error()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"compress": err.Error()})
return
}
stat, err := os.Stat(path.Join(tmpDir, fileName))
if err != nil {
snap.Status.Compress = err.Error()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"compress": err.Error()})
return
}
size := common.LoadSizeUnit2F(float64(stat.Size()))
global.LOG.Debugf("compress successful! size of file: %s", size)
snap.Status.Compress = constant.StatusDone
snap.Status.Size = size
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"compress": constant.StatusDone, "size": size})
}
func snapUpload(snap snapHelper, account string, file string) {
source := path.Join(global.CONF.System.TmpDir, "system", path.Base(file))
defer func() {
global.LOG.Debugf("remove snapshot file %s", source)
_ = os.Remove(source)
}()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": constant.StatusUploading})
backup, err := backupRepo.Get(commonRepo.WithByType(account))
if err != nil {
snap.Status.Upload = err.Error()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": err.Error()})
return
}
client, err := NewIBackupService().NewClient(&backup)
if err != nil {
snap.Status.Upload = err.Error()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": err.Error()})
return
}
target := path.Join(strings.TrimPrefix(backup.BackupPath, "/"), "system_snapshot", path.Base(file))
global.LOG.Debugf("start upload snapshot to %s, dir: %s", backup.Type, target)
if _, err := client.Upload(source, target); err != nil {
snap.Status.Upload = err.Error()
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": err.Error()})
return
}
snap.Status.Upload = constant.StatusDone
_ = snapshotRepo.UpdateStatus(snap.Status.ID, map[string]interface{}{"upload": constant.StatusDone})
}
func handleSnapTar(sourceDir, targetDir, name, exclusionRules string) error {
if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(targetDir, os.ModePerm); err != nil {
return err
}
}
exStr := ""
excludes := strings.Split(exclusionRules, ";")
for _, exclude := range excludes {
if len(exclude) == 0 {
continue
}
exStr += " --exclude "
exStr += exclude
}
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 {
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
return fmt.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
}
}
return nil
}
+40 -13
View File
@@ -18,6 +18,8 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/qqwry"
"github.com/1Panel-dev/1Panel/backend/utils/systemctl"
"github.com/pkg/errors"
)
const sshPath = "/etc/ssh/sshd_config"
@@ -51,19 +53,20 @@ func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
PermitRootLogin: "yes",
UseDNS: "yes",
}
sudo := cmd.SudoHandleCmd()
stdout, err := cmd.Execf("%s systemctl status sshd", sudo)
serviceName, err := loadServiceName()
if err != nil {
data.Message = stdout
data.Status = constant.StatusDisable
}
stdLines := strings.Split(stdout, "\n")
for _, stdline := range stdLines {
if strings.Contains(stdline, "active (running)") {
data.Message = err.Error()
} else {
active, err := systemctl.IsActive(serviceName)
if !active {
data.Status = constant.StatusDisable
data.Message = err.Error()
} else {
data.Status = constant.StatusEnable
break
}
}
sshConf, err := os.ReadFile(sshPath)
if err != nil {
data.Message = err.Error()
@@ -95,10 +98,14 @@ func (u *SSHService) GetSSHInfo() (*dto.SSHInfo, error) {
func (u *SSHService) OperateSSH(operation string) error {
if operation == "start" || operation == "stop" || operation == "restart" {
sudo := cmd.SudoHandleCmd()
stdout, err := cmd.Execf("%s systemctl %s sshd", sudo, operation)
serviceName, err := loadServiceName()
if err != nil {
return fmt.Errorf("%s sshd failed, stdout: %s, err: %v", operation, stdout, err)
return err
}
sudo := cmd.SudoHandleCmd()
stdout, err := cmd.Execf("%s systemctl %s %s", sudo, operation, serviceName)
if err != nil {
return fmt.Errorf("%s %s failed, stdout: %s, err: %v", operation, serviceName, stdout, err)
}
return nil
}
@@ -106,6 +113,11 @@ func (u *SSHService) OperateSSH(operation string) error {
}
func (u *SSHService) Update(key, value string) error {
serviceName, err := loadServiceName()
if err != nil {
return err
}
sshConf, err := os.ReadFile(sshPath)
if err != nil {
return err
@@ -130,11 +142,17 @@ func (u *SSHService) Update(key, value string) error {
_, _ = cmd.Execf("%s semanage port -a -t ssh_port_t -p tcp %s", sudo, value)
}
}
_, _ = cmd.Execf("%s systemctl restart sshd", sudo)
_, _ = cmd.Execf("%s systemctl restart %s", sudo, serviceName)
return nil
}
func (u *SSHService) UpdateByFile(value string) error {
serviceName, err := loadServiceName()
if err != nil {
return err
}
file, err := os.OpenFile(sshPath, os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
return err
@@ -144,7 +162,7 @@ func (u *SSHService) UpdateByFile(value string) error {
return err
}
sudo := cmd.SudoHandleCmd()
_, _ = cmd.Execf("%s systemctl restart sshd", sudo)
_, _ = cmd.Execf("%s systemctl restart %s", sudo, serviceName)
return nil
}
@@ -458,3 +476,12 @@ func handleGunzip(path string) error {
}
return nil
}
func loadServiceName() (string, error) {
if exist, _ := systemctl.IsExist("sshd"); exist {
return "sshd", nil
} else if exist, _ := systemctl.IsExist("ssh"); exist {
return "ssh", nil
}
return "", errors.New("The ssh or sshd service is unavailable")
}
+15 -11
View File
@@ -104,6 +104,10 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
fileName := fmt.Sprintf("1panel-%s-%s-%s.tar.gz", req.Version, "linux", itemArch)
_ = settingRepo.Update("SystemStatus", "Upgrading")
go func() {
_ = global.Cron.Stop()
defer func() {
global.Cron.Start()
}()
if err := fileOp.DownloadFile(downloadPath+"/"+fileName, rootDir+"/"+fileName); err != nil {
global.LOG.Errorf("download service file failed, err: %v", err)
_ = settingRepo.Update("SystemStatus", "Free")
@@ -127,26 +131,26 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
}
global.LOG.Info("backup original data successful, now start to upgrade!")
if err := cpBinary(tmpDir+"/1panel", "/usr/local/bin/1panel"); err != nil {
u.handleRollback(fileOp, originalDir, 1)
if err := cpBinary([]string{tmpDir + "/1panel"}, "/usr/local/bin/1panel"); err != nil {
global.LOG.Errorf("upgrade 1panel failed, err: %v", err)
u.handleRollback(fileOp, originalDir, 1)
return
}
if err := cpBinary(tmpDir+"/1pctl", "/usr/local/bin/1pctl"); err != nil {
u.handleRollback(fileOp, originalDir, 2)
if err := cpBinary([]string{tmpDir + "/1pctl"}, "/usr/local/bin/1pctl"); err != nil {
global.LOG.Errorf("upgrade 1pctl failed, err: %v", err)
u.handleRollback(fileOp, originalDir, 2)
return
}
if _, err := cmd.Execf("sed -i -e 's#BASE_DIR=.*#BASE_DIR=%s#g' /usr/local/bin/1pctl", global.CONF.System.BaseDir); err != nil {
u.handleRollback(fileOp, originalDir, 2)
global.LOG.Errorf("upgrade basedir in 1pctl failed, err: %v", err)
u.handleRollback(fileOp, originalDir, 2)
return
}
if err := cpBinary(tmpDir+"/1panel.service", "/etc/systemd/system/1panel.service"); err != nil {
u.handleRollback(fileOp, originalDir, 3)
if err := cpBinary([]string{tmpDir + "/1panel.service"}, "/etc/systemd/system/1panel.service"); err != nil {
global.LOG.Errorf("upgrade 1panel.service failed, err: %v", err)
u.handleRollback(fileOp, originalDir, 3)
return
}
@@ -179,22 +183,22 @@ func (u *UpgradeService) handleBackup(fileOp files.FileOp, originalDir string) e
func (u *UpgradeService) handleRollback(fileOp files.FileOp, originalDir string, errStep int) {
dbPath := global.CONF.System.DbPath + "/1Panel.db"
_ = settingRepo.Update("SystemStatus", "Free")
if err := cpBinary(originalDir+"/1Panel.db", dbPath); err != nil {
if err := cpBinary([]string{originalDir + "/1Panel.db"}, dbPath); err != nil {
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
}
if err := cpBinary(originalDir+"/1panel", "/usr/local/bin/1panel"); err != nil {
if err := cpBinary([]string{originalDir + "/1panel"}, "/usr/local/bin/1panel"); err != nil {
global.LOG.Errorf("rollback 1pctl failed, err: %v", err)
}
if errStep == 1 {
return
}
if err := cpBinary(originalDir+"/1pctl", "/usr/local/bin/1pctl"); err != nil {
if err := cpBinary([]string{originalDir + "/1pctl"}, "/usr/local/bin/1pctl"); err != nil {
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
}
if errStep == 2 {
return
}
if err := cpBinary(originalDir+"/1panel.service", "/etc/systemd/system/1panel.service"); err != nil {
if err := cpBinary([]string{originalDir + "/1panel.service"}, "/etc/systemd/system/1panel.service"); err != nil {
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
}
}
+94 -22
View File
@@ -15,14 +15,18 @@ import (
"regexp"
"strconv"
"strings"
"syscall"
"time"
"github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/spf13/afero"
"github.com/1Panel-dev/1Panel/backend/utils/compose"
"github.com/1Panel-dev/1Panel/backend/utils/env"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/nginx"
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
@@ -80,6 +84,7 @@ type IWebsiteService interface {
GetRewriteConfig(req request.NginxRewriteReq) (*response.NginxRewriteRes, error)
UpdateRewriteConfig(req request.NginxRewriteUpdate) error
LoadWebsiteDirConfig(req request.WebsiteCommonReq) (*response.WebsiteDirConfig, error)
UpdateSiteDir(req request.WebsiteUpdateDir) error
UpdateSitePermission(req request.WebsiteUpdateDirPermission) error
OperateProxy(req request.WebsiteProxyConfig) (err error)
@@ -175,6 +180,11 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
if exist, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithDomain(create.PrimaryDomain)); len(exist) > 0 {
return buserr.New(constant.ErrDomainIsExist)
}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return err
}
defaultHttpPort := nginxInstall.HttpPort
defaultDate, _ := time.Parse(constant.DateLayout, constant.DefaultDate)
website := &model.Website{
@@ -291,13 +301,13 @@ func (w WebsiteService) CreateWebsite(create request.WebsiteCreate) (err error)
}
var domains []model.WebsiteDomain
domains = append(domains, model.WebsiteDomain{Domain: website.PrimaryDomain, Port: 80})
domains = append(domains, model.WebsiteDomain{Domain: website.PrimaryDomain, Port: defaultHttpPort})
otherDomainArray := strings.Split(create.OtherDomains, "\n")
for _, domain := range otherDomainArray {
if domain == "" {
continue
}
domainModel, err := getDomain(domain)
domainModel, err := getDomain(domain, defaultHttpPort)
if err != nil {
return err
}
@@ -445,11 +455,20 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate)
ports []int
domains []string
)
if create.Port != 80 {
httpPort, _, err := getAppInstallPort(constant.AppOpenresty)
if err != nil {
return domainModel, err
}
if existDomains, _ := websiteDomainRepo.GetBy(websiteDomainRepo.WithPort(create.Port)); len(existDomains) == 0 {
if existAppInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithPort(create.Port)); !reflect.DeepEqual(existAppInstall, model.AppInstall{}) {
return domainModel, buserr.WithMap(constant.ErrPortInOtherApp, map[string]interface{}{"port": create.Port, "apps": existAppInstall.App.Name}, nil)
}
if common.ScanPort(create.Port) {
return domainModel, buserr.WithDetail(constant.ErrPortInUsed, create.Port, nil)
}
}
website, err := websiteRepo.GetFirst(commonRepo.WithByID(create.WebsiteID))
if err != nil {
return domainModel, err
@@ -466,7 +485,7 @@ func (w WebsiteService) CreateWebsiteDomain(create request.WebsiteDomainCreate)
Port: create.Port,
WebsiteID: create.WebsiteID,
}
if create.Port != 80 {
if create.Port != httpPort {
go func() {
_ = OperateFirewallPort(nil, []int{create.Port})
}()
@@ -652,7 +671,12 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
if !req.Enable {
website.Protocol = constant.ProtocolHTTP
website.WebsiteSSLID = 0
if err := deleteListenAndServerName(website, []string{"443", "[::]:443"}, []string{}); err != nil {
_, httpsPort, err := getAppInstallPort(constant.AppOpenresty)
if err != nil {
return nil, err
}
httpsPortStr := strconv.Itoa(httpsPort)
if err := deleteListenAndServerName(website, []string{httpsPortStr, "[::]:" + httpsPortStr}, []string{}); err != nil {
return nil, err
}
nginxParams := getNginxParamsFromStaticFile(dto.SSL, nil)
@@ -793,7 +817,7 @@ func (w WebsiteService) PreInstallCheck(req request.WebsiteInstallCheckReq) ([]r
checkIds = append(req.InstallIds, appInstall.ID)
}
for _, id := range checkIds {
if err := syncById(id); err != nil {
if err := syncByID(id); err != nil {
return nil, err
}
}
@@ -912,19 +936,13 @@ func (w WebsiteService) OpWebsiteLog(req request.WebsiteLogReq) (*response.Websi
}
}
filePath := path.Join(sitePath, "log", req.LogType)
fileInfo, err := os.Stat(filePath)
lines, end, err := files.ReadFileByLine(filePath, req.Page, req.PageSize)
if err != nil {
return nil, err
}
if fileInfo.Size() > 20<<20 {
return nil, buserr.New(constant.ErrFileToLarge)
}
fileInfo.Size()
content, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
res.Content = string(content)
res.End = end
res.Path = filePath
res.Content = strings.Join(lines, "\n")
return res, nil
case constant.DisableLog:
key := "access_log"
@@ -1007,12 +1025,17 @@ func (w WebsiteService) ChangeDefaultServer(id uint) error {
if err != nil {
return err
}
httpPort, httpsPort, err := getAppInstallPort(constant.AppOpenresty)
if err != nil {
return err
}
var changeParams []dto.NginxParam
for _, param := range params {
paramLen := len(param.Params)
bind := param.Params[0]
var newParam []string
if bind == "80" || bind == "443" || bind == "[::]:80" || bind == "[::]:443" {
if bind == strconv.Itoa(httpPort) || bind == strconv.Itoa(httpsPort) || bind == "[::]:"+strconv.Itoa(httpPort) || bind == "[::]:"+strconv.Itoa(httpsPort) {
if param.Params[paramLen-1] == components.DefaultServer {
newParam = param.Params
} else {
@@ -1188,6 +1211,9 @@ func (w WebsiteService) UpdatePHPConfigFile(req request.WebsitePHPFileUpdate) er
if err := files.NewFileOp().WriteFile(configPath, strings.NewReader(req.Content), 0755); err != nil {
return err
}
if _, err := compose.Restart(runtimeInstall.GetComposePath()); err != nil {
return err
}
return nil
}
@@ -1372,7 +1398,7 @@ func (w WebsiteService) UpdateSiteDir(req request.WebsiteUpdateDir) error {
runDir := req.SiteDir
siteDir := path.Join("/www/sites", website.Alias, "index")
if req.SiteDir != "/" {
siteDir = fmt.Sprintf("%s/%s", siteDir, req.SiteDir)
siteDir = fmt.Sprintf("%s%s", siteDir, req.SiteDir)
}
if err := updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "root", Params: []string{siteDir}}}, &website); err != nil {
return err
@@ -1391,9 +1417,6 @@ func (w WebsiteService) UpdateSitePermission(req request.WebsiteUpdateDirPermiss
return err
}
absoluteIndexPath := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "index")
if website.SiteDir != "/" {
absoluteIndexPath = path.Join(absoluteIndexPath, website.SiteDir)
}
chownCmd := fmt.Sprintf("chown -R %s:%s %s", req.User, req.Group, absoluteIndexPath)
if cmd.HasNoPasswordSudo() {
chownCmd = fmt.Sprintf("sudo %s", chownCmd)
@@ -2301,3 +2324,52 @@ func (w WebsiteService) UpdateWafFile(req request.WebsiteWafFileUpdate) (err err
rulePath := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "waf", "rules", fmt.Sprintf("%s.json", req.Type))
return files.NewFileOp().WriteFile(rulePath, strings.NewReader(req.Content), 0755)
}
func (w WebsiteService) LoadWebsiteDirConfig(req request.WebsiteCommonReq) (*response.WebsiteDirConfig, error) {
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return nil, err
}
res := &response.WebsiteDirConfig{}
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
if err != nil {
return nil, err
}
absoluteIndexPath := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "index")
var appFs = afero.NewOsFs()
info, err := appFs.Stat(absoluteIndexPath)
if err != nil {
return nil, err
}
res.User = strconv.FormatUint(uint64(info.Sys().(*syscall.Stat_t).Uid), 10)
res.UserGroup = strconv.FormatUint(uint64(info.Sys().(*syscall.Stat_t).Gid), 10)
indexFiles, err := os.ReadDir(absoluteIndexPath)
if err != nil {
return nil, err
}
res.Dirs = []string{"/"}
for _, file := range indexFiles {
if !file.IsDir() {
continue
}
res.Dirs = append(res.Dirs, fmt.Sprintf("/%s", file.Name()))
fileInfo, _ := file.Info()
if fileInfo.Sys().(*syscall.Stat_t).Uid != 1000 || fileInfo.Sys().(*syscall.Stat_t).Gid != 1000 {
res.Msg = i18n.GetMsgByKey("ErrPathPermission")
}
childFiles, _ := os.ReadDir(absoluteIndexPath + "/" + file.Name())
for _, childFile := range childFiles {
if !childFile.IsDir() {
continue
}
childInfo, _ := childFile.Info()
if childInfo.Sys().(*syscall.Stat_t).Uid != 1000 || childInfo.Sys().(*syscall.Stat_t).Gid != 1000 {
res.Msg = i18n.GetMsgByKey("ErrPathPermission")
}
res.Dirs = append(res.Dirs, fmt.Sprintf("/%s/%s", file.Name(), childFile.Name()))
}
}
return res, nil
}
+1 -1
View File
@@ -115,7 +115,7 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
if err != nil {
return request.WebsiteSSLCreate{}, err
}
if err := client.UseHTTP(path.Join(constant.AppInstallDir, constant.AppOpenresty, appInstall.Name, "root")); err != nil {
if err := client.UseHTTP(path.Join(appInstall.GetPath(), "root")); err != nil {
return res, err
}
websiteSSL.AutoRenew = create.AutoRenew
+18 -11
View File
@@ -23,12 +23,12 @@ import (
"gorm.io/gorm"
)
func getDomain(domainStr string) (model.WebsiteDomain, error) {
func getDomain(domainStr string, defaultPort int) (model.WebsiteDomain, error) {
domain := model.WebsiteDomain{}
domainArray := strings.Split(domainStr, ":")
if len(domainArray) == 1 {
domain.Domain = domainArray[0]
domain.Port = 80
domain.Port = defaultPort
return domain, nil
}
if len(domainArray) > 1 {
@@ -186,6 +186,7 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
return errors.New("nginx config is not valid")
}
server := servers[0]
server.DeleteListen("80")
var serverNames []string
for _, domain := range domains {
serverNames = append(serverNames, domain.Domain)
@@ -378,27 +379,33 @@ func applySSL(website model.Website, websiteSSL model.WebsiteSSL, req request.We
}
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
server.UpdateListen("443", website.DefaultServer, "ssl", "http2")
httpPort := strconv.Itoa(nginxFull.Install.HttpPort)
httpsPort := strconv.Itoa(nginxFull.Install.HttpsPort)
httpPortIPV6 := "[::]:" + httpPort
httpsPortIPV6 := "[::]:" + httpsPort
server.UpdateListen(httpsPort, website.DefaultServer, "ssl", "http2")
if website.IPV6 {
server.UpdateListen("[::]:443", website.DefaultServer, "ssl", "http2")
server.UpdateListen(httpsPortIPV6, website.DefaultServer, "ssl", "http2")
}
switch req.HttpConfig {
case constant.HTTPSOnly:
server.RemoveListenByBind("80")
server.RemoveListenByBind("[::]:80")
server.RemoveListenByBind(httpPort)
server.RemoveListenByBind(httpPortIPV6)
server.RemoveDirective("if", []string{"($scheme"})
case constant.HTTPToHTTPS:
server.UpdateListen("80", website.DefaultServer)
server.UpdateListen(httpPort, website.DefaultServer)
if website.IPV6 {
server.UpdateListen("[::]:80", website.DefaultServer)
server.UpdateListen(httpPortIPV6, website.DefaultServer)
}
server.AddHTTP2HTTPS()
case constant.HTTPAlso:
server.UpdateListen("80", website.DefaultServer)
server.UpdateListen(httpPort, website.DefaultServer)
server.RemoveDirective("if", []string{"($scheme"})
if website.IPV6 {
server.UpdateListen("[::]:80", website.DefaultServer)
server.UpdateListen(httpPortIPV6, website.DefaultServer)
}
}
@@ -626,7 +633,7 @@ func checkIsLinkApp(website model.Website) bool {
}
func chownRootDir(path string) error {
_, err := cmd.ExecWithTimeOut(fmt.Sprintf("chown -R 1000:1000 %s", path), 1*time.Second)
_, err := cmd.ExecWithTimeOut(fmt.Sprintf(`chown -R 1000:1000 "%s"`, path), 1*time.Second)
if err != nil {
return err
}
+2
View File
@@ -12,6 +12,7 @@ const (
Upgrading = "Upgrading"
UpgradeErr = "UpgradeErr"
PullErr = "PullErr"
Rebuilding = "Rebuilding"
ContainerPrefix = "1Panel-"
@@ -20,6 +21,7 @@ const (
AppOpenresty = "openresty"
AppMysql = "mysql"
AppMariaDB = "mariadb"
AppRedis = "redis"
AppResourceLocal = "local"
+3
View File
@@ -96,6 +96,8 @@ var (
ErrUserIsExist = "ErrUserIsExist"
ErrDatabaseIsExist = "ErrDatabaseIsExist"
ErrExecTimeOut = "ErrExecTimeOut"
ErrRemoteExist = "ErrRemoteExist"
ErrLocalExist = "ErrLocalExist"
)
// redis
@@ -122,4 +124,5 @@ var (
var (
ErrBackupInUsed = "ErrBackupInUsed"
ErrOSSConn = "ErrOSSConn"
ErrEntrance = "ErrEntrance"
)
+1
View File
@@ -2,6 +2,7 @@ package constant
const (
StatusRunning = "Running"
StatusDone = "Done"
StatusStoped = "Stoped"
StatusWaiting = "Waiting"
StatusSuccess = "Success"
+6 -1
View File
@@ -20,7 +20,7 @@ ErrPortInUsed: "{{ .detail }} port already in use"
ErrAppLimit: "App exceeds install limit"
ErrAppRequired: "{{ .detail }} app is required"
ErrNotInstall: "App not installed"
ErrPortInOtherApp: "{{ .port }} port already in use by {{ .apps }}"
ErrPortInOtherApp: "{{ .port }} port already in use by app {{ .apps }}"
ErrDbUserNotValid: "Stock database, username and password do not match!"
ErrDockerComposeNotValid: "docker-compose file format error!"
ErrUpdateBuWebsite: 'The application was updated successfully, but the modification of the website configuration file failed, please check the configuration!'
@@ -46,6 +46,7 @@ ErrContainerNotFound: '{{ .name }} container does not exist'
ErrContainerMsg: '{{ .name }} container is abnormal, please check the log on the container page for details'
ErrAppBackup: '{{ .name }} application backup failed err {{.err}}'
ErrImagePull: '{{ .name }} image pull failed err {{.err}}'
ErrVersionTooLow: 'The current 1Panel version is too low to update the app store, please upgrade the version'
#file
ErrFileCanNotRead: "File can not read"
@@ -65,6 +66,7 @@ ErrGroupIsUsed: 'The group is in use and cannot be deleted'
ErrBackupMatch: 'the backup file does not match the current partial data of the website: {{ .detail}}"'
ErrBackupExist: 'the backup file corresponds to a portion of the original data that does not exist: {{ .detail}}"'
ErrPHPResource: 'The local runtime does not support switching!'
ErrPathPermission: 'A folder with non-1000:1000 permissions was detected in the index directory, which may cause Access denied errors when accessing the website.'
#ssl
ErrSSLCannotDelete: "The certificate is being used by the website and cannot be removed"
@@ -80,6 +82,8 @@ ErrSSLCertificateFormat: 'Certificate file format error, please use pem format'
ErrUserIsExist: "The current user already exists. Please enter a new user"
ErrDatabaseIsExist: "The current database already exists. Please enter a new database"
ErrExecTimeOut: "SQL execution timed out, please check the database"
ErrRemoteExist: "The remote database already exists with that name, please modify it and try again"
ErrLocalExist: "The local database already exists with that name, please modify it and try again"
#redis
ErrTypeOfRedis: "The recovery file type does not match the current persistence mode. Modify the file type and try again"
@@ -99,6 +103,7 @@ ErrDelWithWebsite: "The operating environment has been associated with a website
#setting
ErrBackupInUsed: "The backup account is already being used in a cronjob and cannot be deleted."
ErrOSSConn: "Unable to successfully request the latest version. Please check if the server can connect to the external network environment."
ErrEntrance: "Security entrance information error. Please check and try again!"
#tool
ErrConfigNotFound: "Configuration file does not exist"
+6 -1
View File
@@ -20,7 +20,7 @@ ErrPortInUsed: "{{ .detail }} 端口已被佔用!"
ErrAppLimit: "應用超出安裝數量限制"
ErrAppRequired: "請先安裝 {{ .detail }} 應用"
ErrNotInstall: "應用未安裝"
ErrPortInOtherApp: "{{ .port }} 端口已被 {{ .apps }}佔用!"
ErrPortInOtherApp: "{{ .port }} 端口已被應用 {{ .apps }} 佔用!"
ErrDbUserNotValid: "儲存資料庫,用戶名密碼不匹配!"
ErrDockerComposeNotValid: "docker-compose 文件格式錯誤"
ErrUpdateBuWebsite: '應用更新成功,但是網站配置文件修改失敗,請檢查配置!'
@@ -46,6 +46,7 @@ ErrContainerNotFound: '{{ .name }} 容器不存在'
ErrContainerMsg: '{{ .name }} 容器異常,具體請在容器頁面查看日誌'
ErrAppBackup: '{{ .name }} 應用備份失敗 err {{.err}}'
ErrImagePull: '{{ .name }} 鏡像拉取失敗 err {{.err}}'
ErrVersionTooLow: '當前 1Panel 版本過低,無法更新應用商店,請升級版本之後操作'
#file
ErrFileCanNotRead: "此文件不支持預覽"
@@ -65,6 +66,7 @@ ErrGroupIsUsed: '分組正在使用中,無法刪除'
ErrBackupMatch: '該備份文件與當前網站部分數據不匹配: {{ .detail}}"'
ErrBackupExist: '該備份文件對應部分原數據不存在: {{ .detail}}"'
ErrPHPResource: '本地運行環境不支持切換!'
ErrPathPermission: 'index 目錄下檢測到非 1000:1000 權限文件夾,可能導致網站訪問 Access denied 錯誤'
#ssl
ErrSSLCannotDelete: "證書正在被網站使用,無法刪除"
@@ -80,6 +82,8 @@ ErrSSLCertificateFormat: '證書文件格式錯誤,請使用 pem 格式'
ErrUserIsExist: "當前用戶已存在,請重新輸入"
ErrDatabaseIsExist: "當前資料庫已存在,請重新輸入"
ErrExecTimeOut: "SQL 執行超時,請檢查數據庫"
ErrRemoteExist: "遠程數據庫已存在該名稱,請修改後重試"
ErrLocalExist: "本地數據庫已存在該名稱,請修改後重試"
#redis
ErrTypeOfRedis: "恢復文件類型與當前持久化方式不符,請修改後重試"
@@ -99,6 +103,7 @@ ErrDelWithWebsite: "運行環境已經關聯網站,無法刪除"
#setting
ErrBackupInUsed: "該備份帳號已在計劃任務中使用,無法刪除"
ErrOSSConn: "無法成功請求最新版本,請檢查伺服器是否能夠連接到外部網絡環境。"
ErrEntrance: "安全入口信息錯誤,請檢查後重試!"
#tool
ErrConfigNotFound: "配置文件不存在"
+6 -1
View File
@@ -20,7 +20,7 @@ ErrPortInUsed: "{{ .detail }} 端口已被占用!"
ErrAppLimit: "应用超出安装数量限制"
ErrAppRequired: "请先安装 {{ .detail }} 应用"
ErrNotInstall: "应用未安装"
ErrPortInOtherApp: "{{ .port }} 端口已被 {{ .apps }}占用!"
ErrPortInOtherApp: "{{ .port }} 端口已被应用 {{ .apps }} 占用!"
ErrDbUserNotValid: "存量数据库,用户名密码不匹配!"
ErrDockerComposeNotValid: "docker-compose 文件格式错误"
ErrUpdateBuWebsite: '应用更新成功,但是网站配置文件修改失败,请检查配置!'
@@ -46,6 +46,7 @@ ErrContainerNotFound: '{{ .name }} 容器不存在'
ErrContainerMsg: '{{ .name }} 容器异常,具体请在容器页面查看日志'
ErrAppBackup: '{{ .name }} 应用备份失败 err {{.err}}'
ErrImagePull: '镜像拉取失败 {{.err}}'
ErrVersionTooLow: '当前 1Panel 版本过低,无法更新应用商店,请升级版本之后操作'
#file
ErrFileCanNotRead: "此文件不支持预览"
@@ -65,6 +66,7 @@ ErrGroupIsUsed: '分组正在使用中,无法删除'
ErrBackupMatch: '该备份文件与当前网站部分数据不匹配 {{ .detail}}"'
ErrBackupExist: '该备份文件对应部分源数据不存在 {{ .detail}}"'
ErrPHPResource: '本地运行环境不支持切换!'
ErrPathPermission: 'index 目录下检测到非 1000:1000 权限文件夹,可能导致网站访问 Access denied 错误'
#ssl
ErrSSLCannotDelete: "证书正在被网站使用,无法删除"
@@ -80,6 +82,8 @@ ErrSSLCertificateFormat: '证书文件格式错误,请使用 pem 格式'
ErrUserIsExist: "当前用户已存在,请重新输入"
ErrDatabaseIsExist: "当前数据库已存在,请重新输入"
ErrExecTimeOut: "SQL 执行超时,请检查数据库"
ErrRemoteExist: "远程数据库已存在该名称,请修改后重试"
ErrLocalExist: "本地数据库已存在该名称,请修改后重试"
#redis
ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后重试"
@@ -99,6 +103,7 @@ ErrDelWithWebsite: "运行环境已经关联网站,无法删除"
#setting
ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"
ErrOSSConn: "无法成功请求最新版本,请检查服务器是否能够连接到外部网络环境。"
ErrEntrance: "安全入口信息错误,请检查后重试!"
#tool
ErrConfigNotFound: "配置文件不存在"
+48 -5
View File
@@ -4,6 +4,7 @@ import (
"encoding/base64"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"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"
@@ -17,11 +18,6 @@ func Init() {
global.LOG.Errorf("load service port from setting failed, err: %v", err)
}
global.CONF.System.Port = portSetting.Value
encryptSetting, err := settingRepo.Get(settingRepo.WithByKey("EncryptKey"))
if err != nil {
global.LOG.Errorf("load service encrypt key from setting failed, err: %v", err)
}
global.CONF.System.EncryptKey = encryptSetting.Value
sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL"))
if err != nil {
global.LOG.Errorf("load service ssl from setting failed, err: %v", err)
@@ -63,4 +59,51 @@ func Init() {
sudo := cmd.SudoHandleCmd()
_, _ = cmd.Execf("%s sed -i '/CHANGE_USER_INFO=true/d' /usr/local/bin/1pctl", sudo)
}
handleSnapStatus()
}
func handleSnapStatus() {
snapRepo := repo.NewISnapshotRepo()
snaps, _ := snapRepo.GetList()
for _, snap := range snaps {
if snap.Status == "OnSaveData" {
_ = snapRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusSuccess})
}
if snap.Status == constant.StatusWaiting {
_ = snapRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed, "message": "the task was interrupted due to the restart of the 1panel service"})
}
}
status, _ := snapRepo.GetStatusList()
for _, statu := range status {
updates := make(map[string]interface{})
if statu.Panel == constant.StatusRunning {
updates["panel"] = constant.StatusFailed
}
if statu.PanelInfo == constant.StatusRunning {
updates["panel_info"] = constant.StatusFailed
}
if statu.DaemonJson == constant.StatusRunning {
updates["daemon_json"] = constant.StatusFailed
}
if statu.AppData == constant.StatusRunning {
updates["app_data"] = constant.StatusFailed
}
if statu.PanelData == constant.StatusRunning {
updates["panel_data"] = constant.StatusFailed
}
if statu.BackupData == constant.StatusRunning {
updates["backup_data"] = constant.StatusFailed
}
if statu.Compress == constant.StatusRunning {
updates["compress"] = constant.StatusFailed
}
if statu.Upload == constant.StatusUploading {
updates["upload"] = constant.StatusFailed
}
if len(updates) != 0 {
_ = snapRepo.UpdateStatus(statu.ID, updates)
}
}
}
+5
View File
@@ -38,6 +38,11 @@ func Init() {
migrations.AddRemoteDB,
migrations.UpdateRedisParam,
migrations.UpdateCronjobWithDb,
migrations.AddTableFirewall,
migrations.AddDatabases,
migrations.UpdateDatabase,
migrations.UpdateAppInstallResource,
migrations.DropDatabaseLocal,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
+209 -38
View File
@@ -1,13 +1,14 @@
package migrations
import (
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
@@ -485,40 +486,23 @@ var EncryptHostPassword = &gormigrate.Migration{
var AddRemoteDB = &gormigrate.Migration{
ID: "20230724-add-remote-db",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.RemoteDB{}, &model.DatabaseMysql{}); err != nil {
if err := tx.AutoMigrate(&model.Database{}, &model.DatabaseMysql{}); err != nil {
return err
}
var (
app model.App
appInstall model.AppInstall
)
if err := global.DB.Where("key = ?", "mysql").First(&app).Error; err != nil {
return nil
}
if err := global.DB.Where("app_id = ?", app.ID).First(&appInstall).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
installRepo := repo.NewIAppInstallRepo()
mysqlInfo, err := installRepo.LoadBaseInfo("mysql", "")
if err == nil {
if err := tx.Create(&model.Database{
Name: "local",
Type: "mysql",
Version: mysqlInfo.Version,
From: "local",
Address: "127.0.0.1",
Username: "root",
Password: mysqlInfo.Password,
}).Error; err != nil {
return err
}
return err
}
envMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(appInstall.Env), &envMap); err != nil {
return err
}
password, ok := envMap["PANEL_DB_ROOT_PASSWORD"].(string)
if !ok {
return errors.New("error password in app env")
}
if err := tx.Create(&model.RemoteDB{
Name: "local",
Type: "mysql",
Version: appInstall.Version,
From: "local",
Address: "127.0.0.1",
Username: "root",
Password: password,
}).Error; err != nil {
return err
}
return nil
},
@@ -531,10 +515,10 @@ var UpdateRedisParam = &gormigrate.Migration{
app model.App
appInstall model.AppInstall
)
if err := global.DB.Where("key = ?", "redis").First(&app).Error; err != nil {
if err := tx.Where("key = ?", "redis").First(&app).Error; err != nil {
return nil
}
if err := global.DB.Where("app_id = ?", app.ID).First(&appInstall).Error; err != nil {
if err := tx.Where("app_id = ?", app.ID).First(&appInstall).Error; err != nil {
return nil
}
appInstall.Param = strings.ReplaceAll(appInstall.Param, "PANEL_DB_ROOT_PASSWORD", "PANEL_REDIS_ROOT_PASSWORD")
@@ -544,7 +528,6 @@ var UpdateRedisParam = &gormigrate.Migration{
return err
}
return nil
},
}
@@ -552,13 +535,13 @@ var UpdateCronjobWithDb = &gormigrate.Migration{
ID: "20230809-update-cronjob-with-db",
Migrate: func(tx *gorm.DB) error {
var cronjobs []model.Cronjob
if err := global.DB.Where("type = ? AND db_name != ?", "database", "all").Find(&cronjobs).Error; err != nil {
if err := tx.Where("type = ? AND db_name != ?", "database", "all").Find(&cronjobs).Error; err != nil {
return nil
}
for _, job := range cronjobs {
var db model.DatabaseMysql
if err := global.DB.Where("name = ?", job.DBName).First(&db).Error; err != nil {
if err := tx.Where("name = ?", job.DBName).First(&db).Error; err != nil {
continue
}
if err := tx.Model(&model.Cronjob{}).
@@ -570,3 +553,191 @@ var UpdateCronjobWithDb = &gormigrate.Migration{
return nil
},
}
var AddTableFirewall = &gormigrate.Migration{
ID: "20230908-add-table-firewall",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Firewall{}, model.SnapshotStatus{}, &model.Cronjob{}); err != nil {
return err
}
_ = tx.Exec("alter table remote_dbs rename to databases;").Error
if err := tx.AutoMigrate(&model.Database{}); err != nil {
return err
}
return nil
},
}
var AddDatabases = &gormigrate.Migration{
ID: "20230831-add-databases",
Migrate: func(tx *gorm.DB) error {
installRepo := repo.NewIAppInstallRepo()
_ = tx.Where("name = ? AND address = ?", "local", "127.0.0.1").Delete(&model.Database{}).Error
mysql := addDatabaseData(tx, installRepo, "mysql")
if mysql.AppInstallID != 0 {
if err := tx.Create(mysql).Error; err != nil {
return err
}
}
mariadb := addDatabaseData(tx, installRepo, "mariadb")
if mariadb.AppInstallID != 0 {
if err := tx.Create(mariadb).Error; err != nil {
return err
}
}
redis := addDatabaseData(tx, installRepo, "redis")
if redis.AppInstallID != 0 {
if err := tx.Create(redis).Error; err != nil {
return err
}
}
postgresql := addDatabaseData(tx, installRepo, "postgresql")
if postgresql.AppInstallID != 0 {
if err := tx.Create(postgresql).Error; err != nil {
return err
}
}
mongodb := addDatabaseData(tx, installRepo, "mongodb")
if mongodb.AppInstallID != 0 {
if err := tx.Create(mongodb).Error; err != nil {
return err
}
}
memcached := addDatabaseData(tx, installRepo, "memcached")
if memcached.AppInstallID != 0 {
if err := tx.Create(memcached).Error; err != nil {
return err
}
}
return nil
},
}
var UpdateDatabase = &gormigrate.Migration{
ID: "20230831-update-database",
Migrate: func(tx *gorm.DB) error {
if err := tx.Model(&model.DatabaseMysql{}).Where("`from` != ?", "local").Updates(map[string]interface{}{
"from": "remote",
}).Error; err != nil {
return err
}
var datas []model.Database
if err := tx.Find(&datas).Error; err != nil {
return nil
}
for _, data := range datas {
pass, err := encrypt.StringEncrypt(data.Password)
if err != nil {
global.LOG.Errorf("encrypt database %s password failed, err: %v", data.Name, err)
continue
}
if err := tx.Model(&model.Database{}).Where("id = ?", data.ID).Updates(map[string]interface{}{
"password": pass,
}).Error; err != nil {
global.LOG.Errorf("updata database %s info failed, err: %v", data.Name, err)
}
}
var mysqls []model.DatabaseMysql
if err := tx.Find(&mysqls).Error; err != nil {
return nil
}
for _, data := range mysqls {
pass, err := encrypt.StringEncrypt(data.Password)
if err != nil {
global.LOG.Errorf("encrypt database db %s password failed, err: %v", data.Name, err)
continue
}
if err := tx.Model(&model.DatabaseMysql{}).Where("id = ?", data.ID).Updates(map[string]interface{}{
"password": pass,
}).Error; err != nil {
global.LOG.Errorf("updata database db %s info failed, err: %v", data.Name, err)
}
}
return nil
},
}
var UpdateAppInstallResource = &gormigrate.Migration{
ID: "20230831-update-app_install_resource",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.AppInstallResource{}); err != nil {
return err
}
if err := tx.Model(&model.AppInstallResource{}).Where("1 = 1").Updates(map[string]interface{}{
"from": "local",
}).Error; err != nil {
return err
}
return nil
},
}
var DropDatabaseLocal = &gormigrate.Migration{
ID: "20230914-drop-database-local",
Migrate: func(tx *gorm.DB) error {
_ = tx.Where("name = ? AND address = ?", "local", "127.0.0.1").Delete(&model.Database{}).Error
return nil
},
}
func addDatabaseData(tx *gorm.DB, installRepo repo.IAppInstallRepo, appType string) *model.Database {
dbInfo, err := installRepo.LoadBaseInfo(appType, "")
if err != nil {
return &model.Database{}
}
if appType == "mysql" || appType == "redis" || appType == "mariadb" || appType == "memcached" {
dbInfo.UserName = "root"
}
database := &model.Database{
AppInstallID: dbInfo.ID,
Name: dbInfo.Name,
Type: appType,
Version: dbInfo.Version,
From: "local",
Address: dbInfo.ServiceName,
Port: service.DatabaseKeys[appType],
Username: dbInfo.UserName,
Password: dbInfo.Password,
}
var dbItem model.Database
_ = global.DB.Where("name = ?", dbInfo.Name).First(&dbItem).Error
if dbItem.ID != 0 {
if appType == "mysql" {
var (
backups []model.BackupRecord
mysqls []model.DatabaseMysql
)
_ = tx.Where("name = ? AND type = ?", dbItem.Name, "mysql").Find(&backups)
_ = tx.Where("`from` = ?", "local").Find(&mysqls)
for _, item := range backups {
isLocal := false
for _, mysql := range mysqls {
if item.Name == mysql.MysqlName && item.DetailName == mysql.Name {
isLocal = true
break
}
}
if !isLocal {
_ = tx.Model(&model.BackupRecord{}).Where("id = ?", item.ID).Updates(map[string]interface{}{
"name": "remote-" + dbItem.Name,
}).Error
}
}
}
if err := tx.Debug().Model(&model.DatabaseMysql{}).Where("mysql_name = ? AND `from` != ?", dbItem.Name, "local").Updates(map[string]interface{}{
"mysql_name": "remote-" + dbItem.Name,
}).Error; err != nil {
fmt.Println(err)
}
if err := tx.Debug().Model(&model.Database{}).Where("name = ?", dbItem.Name).Updates(map[string]interface{}{
"name": "remote-" + dbItem.Name,
}).Error; err != nil {
fmt.Println(err)
}
}
return database
}
+5 -4
View File
@@ -24,16 +24,17 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
appRouter.POST("/install", baseApi.InstallApp)
appRouter.GET("/tags", baseApi.GetAppTags)
appRouter.GET("/installed/:appInstallId/versions", baseApi.GetUpdateVersions)
appRouter.GET("/installed/check/:key", baseApi.CheckAppInstalled)
appRouter.GET("/installed/loadport/:key", baseApi.LoadPort)
appRouter.GET("/installed/conninfo/:key", baseApi.LoadConnInfo)
appRouter.POST("/installed/check", baseApi.CheckAppInstalled)
appRouter.POST("/installed/loadport", baseApi.LoadPort)
appRouter.POST("/installed/conninfo", baseApi.LoadConnInfo)
appRouter.GET("/installed/delete/check/:appInstallId", baseApi.DeleteCheck)
appRouter.POST("/installed/search", baseApi.SearchAppInstalled)
appRouter.GET("/installed/list", baseApi.ListAppInstalled)
appRouter.POST("/installed/op", baseApi.OperateInstalled)
appRouter.POST("/installed/sync", baseApi.SyncInstalled)
appRouter.POST("/installed/port/change", baseApi.ChangeAppPort)
appRouter.GET("/services/:key", baseApi.GetServices)
appRouter.GET("/installed/conf/:key", baseApi.GetDefaultConfig)
appRouter.POST("/installed/conf", baseApi.GetDefaultConfig)
appRouter.GET("/installed/params/:appInstallId", baseApi.GetParams)
appRouter.POST("/installed/params/update", baseApi.UpdateInstalled)
appRouter.POST("/installed/ignore", baseApi.IgnoreUpgrade)
+12 -12
View File
@@ -17,7 +17,7 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
baseApi := v1.ApiGroupApp.BaseApi
{
cmdRouter.POST("", baseApi.CreateMysql)
cmdRouter.GET("load/:from", baseApi.LoadDBFromRemote)
cmdRouter.POST("load", baseApi.LoadDBFromRemote)
cmdRouter.POST("/change/access", baseApi.ChangeMysqlAccess)
cmdRouter.POST("/change/password", baseApi.ChangeMysqlPassword)
cmdRouter.POST("/del/check", baseApi.DeleteCheckMysql)
@@ -27,10 +27,10 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
cmdRouter.POST("/conffile/update", baseApi.UpdateMysqlConfByFile)
cmdRouter.POST("/search", baseApi.SearchMysql)
cmdRouter.POST("/load/file", baseApi.LoadDatabaseFile)
cmdRouter.GET("/variables", baseApi.LoadVariables)
cmdRouter.GET("/status", baseApi.LoadStatus)
cmdRouter.GET("/baseinfo", baseApi.LoadBaseinfo)
cmdRouter.GET("/remote", baseApi.LoadRemoteAccess)
cmdRouter.POST("/variables", baseApi.LoadVariables)
cmdRouter.POST("/status", baseApi.LoadStatus)
cmdRouter.POST("/baseinfo", baseApi.LoadBaseinfo)
cmdRouter.POST("/remote", baseApi.LoadRemoteAccess)
cmdRouter.GET("/options", baseApi.ListDBName)
cmdRouter.GET("/redis/persistence/conf", baseApi.LoadPersistenceConf)
@@ -43,12 +43,12 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
cmdRouter.POST("/redis/conffile/update", baseApi.UpdateRedisConfByFile)
cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf)
cmdRouter.POST("/remote/check", baseApi.CheckeRemoteDB)
cmdRouter.POST("/remote", baseApi.CreateRemoteDB)
cmdRouter.GET("/remote/:name", baseApi.GetRemoteDB)
cmdRouter.GET("/remote/list/:type", baseApi.ListRemoteDB)
cmdRouter.POST("/remote/update", baseApi.UpdateRemoteDB)
cmdRouter.POST("/remote/search", baseApi.SearchRemoteDB)
cmdRouter.POST("/remote/del", baseApi.DeleteRemoteDB)
cmdRouter.POST("/db/check", baseApi.CheckDatabase)
cmdRouter.POST("/db", baseApi.CreateDatabase)
cmdRouter.GET("/db/:name", baseApi.GetDatabase)
cmdRouter.GET("/db/list/:type", baseApi.ListDatabase)
cmdRouter.POST("/db/update", baseApi.UpdateDatabase)
cmdRouter.POST("/db/search", baseApi.SearchDatabase)
cmdRouter.POST("/db/del", baseApi.DeleteDatabase)
}
}
+1 -1
View File
@@ -33,6 +33,7 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
hostRouter.POST("/firewall/batch", baseApi.BatchOperateRule)
hostRouter.POST("/firewall/update/port", baseApi.UpdatePortRule)
hostRouter.POST("/firewall/update/addr", baseApi.UpdateAddrRule)
hostRouter.POST("/firewall/update/description", baseApi.UpdateFirewallDescription)
hostRouter.GET("/ssh/conf", baseApi.LoadSSHConf)
hostRouter.POST("/ssh/search", baseApi.GetSSHInfo)
@@ -54,7 +55,6 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
hostRouter.POST("/tool/operate", baseApi.OperateTool)
hostRouter.POST("/tool/config", baseApi.OperateToolConfig)
hostRouter.POST("/tool/log", baseApi.GetToolLog)
hostRouter.POST("/tool/supervisor/process/load", baseApi.LoadProcessStatus)
hostRouter.POST("/tool/supervisor/process", baseApi.OperateProcess)
hostRouter.GET("/tool/supervisor/process", baseApi.GetProcess)
hostRouter.POST("/tool/supervisor/process/file", baseApi.GetProcessFile)
+1
View File
@@ -34,6 +34,7 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
settingRouter.POST("/mfa/bind", baseApi.MFABind)
settingRouter.POST("/snapshot", baseApi.CreateSnapshot)
settingRouter.POST("/snapshot/status", baseApi.LoadSnapShotStatus)
settingRouter.POST("/snapshot/search", baseApi.SearchSnapshot)
settingRouter.POST("/snapshot/import", baseApi.ImportSnapshot)
settingRouter.POST("/snapshot/del", baseApi.DeleteSnapshot)
+1
View File
@@ -53,6 +53,7 @@ func (a *WebsiteRouter) InitWebsiteRouter(Router *gin.RouterGroup) {
groupRouter.POST("/dir/update", baseApi.UpdateSiteDir)
groupRouter.POST("/dir/permission", baseApi.UpdateSiteDirPermission)
groupRouter.POST("/dir", baseApi.GetDirConfig)
groupRouter.POST("/proxies", baseApi.GetProxyConfig)
groupRouter.POST("/proxies/update", baseApi.UpdateProxyConfig)
+29
View File
@@ -141,6 +141,35 @@ func ExecWithCheck(name string, a ...string) (string, error) {
return stdout.String(), nil
}
func ExecScript(scriptPath, workDir string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
cmd := exec.Command("bash", scriptPath)
cmd.Dir = workDir
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if ctx.Err() == context.DeadlineExceeded {
return "", buserr.New(constant.ErrCmdTimeout)
}
if err != nil {
errMsg := ""
if len(stderr.String()) != 0 {
errMsg = fmt.Sprintf("stderr: %s", stderr.String())
}
if len(stdout.String()) != 0 {
if len(errMsg) != 0 {
errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String())
} else {
errMsg = fmt.Sprintf("stdout: %s", stdout.String())
}
}
return errMsg, err
}
return stdout.String(), nil
}
func CheckIllegal(args ...string) bool {
if args == nil {
return false
+46
View File
@@ -14,6 +14,7 @@ import (
"time"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/mozillazg/go-pinyin"
)
func CompareVersion(version1 string, version2 string) bool {
@@ -23,6 +24,17 @@ func CompareVersion(version1 string, version2 string) bool {
version1s := strings.Split(version1, ".")
version2s := strings.Split(version2, ".")
if len(version2s) > len(version1s) {
for i := 0; i < len(version2s)-len(version1s); i++ {
version1s = append(version1s, "0")
}
}
if len(version1s) > len(version2s) {
for i := 0; i < len(version1s)-len(version2s); i++ {
version2s = append(version2s, "0")
}
}
n := min(len(version1s), len(version2s))
re := regexp.MustCompile("[0-9]+")
for i := 0; i < n; i++ {
@@ -113,6 +125,13 @@ func ScanUDPPort(port int) bool {
return false
}
func ScanPortWithProto(port int, proto string) bool {
if proto == "udp" {
return ScanUDPPort(port)
}
return ScanPort(port)
}
func ExistWithStrArray(str string, arr []string) bool {
for _, a := range arr {
if strings.Contains(a, str) {
@@ -148,6 +167,19 @@ func LoadSizeUnit(value float64) string {
return fmt.Sprintf("%v", value)
}
func LoadSizeUnit2F(value float64) string {
if value > 1073741824 {
return fmt.Sprintf("%.2fG", value/1073741824)
}
if value > 1048576 {
return fmt.Sprintf("%.2fM", value/1048576)
}
if value > 1024 {
return fmt.Sprintf("%.2fK", value/1024)
}
return fmt.Sprintf("%.2f", value)
}
func LoadTimeZone() string {
loc := time.Now().Location()
if _, err := time.LoadLocation(loc.String()); err != nil {
@@ -173,3 +205,17 @@ func LoadTimeZoneByCmd() string {
}
return fields[2]
}
func ConvertToPinyin(text string) string {
args := pinyin.NewArgs()
args.Fallback = func(r rune, a pinyin.Args) []string {
return []string{string(r)}
}
p := pinyin.Pinyin(text, args)
var strArr []string
for i := 0; i < len(p); i++ {
strArr = append(strArr, strings.Join(p[i], ""))
}
return strings.Join(strArr, "")
}
+10 -2
View File
@@ -42,7 +42,10 @@ func (c Client) ListAllContainers() ([]types.Container, error) {
}
func (c Client) ListContainersByName(names []string) ([]types.Container, error) {
var options types.ContainerListOptions
var (
options types.ContainerListOptions
res []types.Container
)
options.All = true
if len(names) > 0 {
var array []filters.KeyValuePair
@@ -55,7 +58,12 @@ func (c Client) ListContainersByName(names []string) ([]types.Container, error)
if err != nil {
return nil, err
}
return containers, nil
for _, container := range containers {
if container.Names[0] == "/"+names[0] {
res = append(res, container)
}
}
return res, nil
}
func (c Client) CreateNetwork(name string) error {
+17 -3
View File
@@ -8,16 +8,23 @@ import (
"crypto/rand"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"io"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
)
func StringEncrypt(text string) (string, error) {
if len(text) == 0 {
return "", errors.New("it is not possible to encrypt an empty string.")
return "", nil
}
if len(global.CONF.System.EncryptKey) == 0 {
var encryptSetting model.Setting
if err := global.DB.Where("key = ?", "EncryptKey").First(&encryptSetting).Error; err != nil {
return "", err
}
global.CONF.System.EncryptKey = encryptSetting.Value
}
key := global.CONF.System.EncryptKey
pass := []byte(text)
@@ -31,7 +38,14 @@ func StringEncrypt(text string) (string, error) {
func StringDecrypt(text string) (string, error) {
if len(text) == 0 {
return "", errors.New("it is not possible to decrypt an empty string.")
return "", nil
}
if len(global.CONF.System.EncryptKey) == 0 {
var encryptSetting model.Setting
if err := global.DB.Where("key = ?", "EncryptKey").First(&encryptSetting).Error; err != nil {
return "", err
}
global.CONF.System.EncryptKey = encryptSetting.Value
}
key := global.CONF.System.EncryptKey
bytesPass, err := base64.StdEncoding.DecodeString(text)
+10 -3
View File
@@ -125,9 +125,9 @@ func (f FileOp) Chown(dst string, uid int, gid int) error {
}
func (f FileOp) ChownR(dst string, uid string, gid string, sub bool) error {
cmdStr := fmt.Sprintf("chown %s:%s %s", uid, gid, dst)
cmdStr := fmt.Sprintf(`chown %s:%s "%s"`, uid, gid, dst)
if sub {
cmdStr = fmt.Sprintf("chown -R %s:%s %s", uid, gid, dst)
cmdStr = fmt.Sprintf(`chown -R %s:%s "%s"`, uid, gid, dst)
}
if cmd.HasNoPasswordSudo() {
cmdStr = fmt.Sprintf("sudo %s", cmdStr)
@@ -142,7 +142,7 @@ func (f FileOp) ChownR(dst string, uid string, gid string, sub bool) error {
}
func (f FileOp) ChmodR(dst string, mode int64) error {
cmdStr := fmt.Sprintf("chmod -R %v %s", fmt.Sprintf("%04o", mode), dst)
cmdStr := fmt.Sprintf(`chmod -R %v "%s"`, fmt.Sprintf("%04o", mode), dst)
if cmd.HasNoPasswordSudo() {
cmdStr = fmt.Sprintf("sudo %s", cmdStr)
}
@@ -518,6 +518,13 @@ func (f FileOp) Decompress(srcFile string, dst string, cType CompressType) error
return err
}
return nil
} else {
parentDir := path.Dir(filePath)
if !f.Stat(parentDir) {
if err := f.Fs.MkdirAll(parentDir, info.Mode()); err != nil {
return err
}
}
}
fr, err := archFile.Open()
if err != nil {
+7
View File
@@ -10,6 +10,7 @@ import (
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
@@ -23,6 +24,8 @@ type FileInfo struct {
Name string `json:"name"`
User string `json:"user"`
Group string `json:"group"`
Uid string `json:"uid"`
Gid string `json:"gid"`
Extension string `json:"extension"`
Content string `json:"content"`
Size int64 `json:"size"`
@@ -77,6 +80,8 @@ func NewFileInfo(op FileOption) (*FileInfo, error) {
IsHidden: IsHidden(op.Path),
Mode: fmt.Sprintf("%04o", info.Mode().Perm()),
User: GetUsername(info.Sys().(*syscall.Stat_t).Uid),
Uid: strconv.FormatUint(uint64(info.Sys().(*syscall.Stat_t).Uid), 10),
Gid: strconv.FormatUint(uint64(info.Sys().(*syscall.Stat_t).Gid), 10),
Group: GetGroup(info.Sys().(*syscall.Stat_t).Gid),
MimeType: GetMimeType(op.Path),
}
@@ -208,6 +213,8 @@ func (f *FileInfo) listChildren(dir, showHidden, containSub bool, search string,
Mode: fmt.Sprintf("%04o", df.Mode().Perm()),
User: GetUsername(df.Sys().(*syscall.Stat_t).Uid),
Group: GetGroup(df.Sys().(*syscall.Stat_t).Gid),
Uid: strconv.FormatUint(uint64(df.Sys().(*syscall.Stat_t).Uid), 10),
Gid: strconv.FormatUint(uint64(df.Sys().(*syscall.Stat_t).Gid), 10),
}
if isSymlink {
+34
View File
@@ -1,6 +1,7 @@
package files
import (
"bufio"
"github.com/spf13/afero"
"net/http"
"os"
@@ -75,3 +76,36 @@ const dotCharacter = 46
func IsHidden(path string) bool {
return path[0] == dotCharacter
}
func ReadFileByLine(filename string, page, pageSize int) ([]string, bool, error) {
file, err := os.Open(filename)
if err != nil {
return nil, false, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
var lines []string
currentLine := 0
startLine := (page - 1) * pageSize
endLine := startLine + pageSize
for scanner.Scan() {
if currentLine >= startLine && currentLine < endLine {
lines = append(lines, scanner.Text())
}
currentLine++
if currentLine >= endLine {
break
}
}
if err := scanner.Err(); err != nil {
return nil, false, err
}
isEndOfFile := currentLine < endLine
return lines, isEndOfFile, nil
}
+3 -2
View File
@@ -7,8 +7,9 @@ type FireInfo struct {
Protocol string `json:"protocol"` // tcp udp tcp/udp
Strategy string `json:"strategy"` // accept drop
APPName string `json:"appName"`
IsUsed bool `json:"isUsed"`
APPName string `json:"appName"`
IsUsed bool `json:"isUsed"`
Description string `json:"description"`
}
type Forward struct {
+2 -6
View File
@@ -8,7 +8,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
)
@@ -28,11 +27,8 @@ type MysqlClient interface {
func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
if conn.From == "local" {
if cmd.CheckIllegal(conn.Address, conn.Username, conn.Password) {
return nil, buserr.New(constant.ErrCmdIllegal)
}
connArgs := []string{"exec", conn.Address, "mysql", "-u" + conn.Username, "-p" + conn.Password, "-e"}
return client.NewLocal(connArgs, conn.Address, conn.Password, conn.From), nil
return client.NewLocal(connArgs, conn.Address, conn.Password, conn.Database), nil
}
connArgs := fmt.Sprintf("%s:%s@tcp(%s:%d)/?charset=utf8", conn.Username, conn.Password, conn.Address, conn.Port)
@@ -52,7 +48,7 @@ func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
return client.NewRemote(client.Remote{
Client: db,
From: conn.From,
Database: conn.Database,
User: conn.Username,
Password: conn.Password,
Address: conn.Address,
+29
View File
@@ -1,7 +1,14 @@
package client
import (
"strings"
"github.com/1Panel-dev/1Panel/backend/utils/common"
)
type DBInfo struct {
From string `json:"from"`
Database string `json:"database"`
Address string `json:"address"`
Port uint `json:"port"`
Username string `json:"userName"`
@@ -85,3 +92,25 @@ var formatMap = map[string]string{
"gbk": "gbk_chinese_ci",
"big5": "big5_chinese_ci",
}
func loadNameByDB(name, version string) string {
nameItem := common.ConvertToPinyin(name)
if strings.HasPrefix(version, "5.6") {
if len(nameItem) <= 16 {
return nameItem
}
return strings.TrimSuffix(nameItem[:10], "_") + "_" + common.RandStr(5)
}
if len(nameItem) <= 32 {
return nameItem
}
return strings.TrimSuffix(nameItem[:25], "_") + "_" + common.RandStr(5)
}
func randomPassword(user string) string {
passwdItem := user
if len(user) > 6 {
passwdItem = user[:6]
}
return passwdItem + "@" + common.RandStrAndNum(8)
}
+22 -19
View File
@@ -14,31 +14,30 @@ import (
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
)
type Local struct {
PrefixCommand []string
From string
Database string
Password string
ContainerName string
}
func NewLocal(command []string, containerName, password, from string) *Local {
return &Local{PrefixCommand: command, ContainerName: containerName, Password: password, From: from}
func NewLocal(command []string, containerName, password, database string) *Local {
return &Local{PrefixCommand: command, ContainerName: containerName, Password: password, Database: database}
}
func (r *Local) Create(info CreateInfo) error {
createSql := fmt.Sprintf("create database `%s` default character set %s collate %s", info.Name, info.Format, formatMap[info.Format])
if err := r.ExecSQL(createSql, info.Timeout); err != nil {
if strings.Contains(err.Error(), "ERROR 1007") {
if strings.Contains(strings.ToLower(err.Error()), "error 1007") {
return buserr.New(constant.ErrDatabaseIsExist)
}
return err
}
if err := r.CreateUser(info); err != nil {
if err := r.CreateUser(info, true); err != nil {
_ = r.ExecSQL(fmt.Sprintf("drop database if exists `%s`", info.Name), info.Timeout)
return err
}
@@ -46,7 +45,7 @@ func (r *Local) Create(info CreateInfo) error {
return nil
}
func (r *Local) CreateUser(info CreateInfo) error {
func (r *Local) CreateUser(info CreateInfo, withDeleteDB bool) error {
var userlist []string
if strings.Contains(info.Permission, ",") {
ips := strings.Split(info.Permission, ",")
@@ -61,7 +60,7 @@ func (r *Local) CreateUser(info CreateInfo) error {
for _, user := range userlist {
if err := r.ExecSQL(fmt.Sprintf("create user %s identified by '%s';", user, info.Password), info.Timeout); err != nil {
if strings.Contains(err.Error(), "ERROR 1396") {
if strings.Contains(strings.ToLower(err.Error()), "error 1396") {
return buserr.New(constant.ErrUserIsExist)
}
_ = r.Delete(DeleteInfo{
@@ -79,6 +78,8 @@ func (r *Local) CreateUser(info CreateInfo) error {
}
if strings.HasPrefix(info.Version, "5.7") || strings.HasPrefix(info.Version, "5.6") {
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, info.Password)
} else {
grantStr = grantStr + " with grant option;"
}
if err := r.ExecSQL(grantStr, info.Timeout); err != nil {
_ = r.Delete(DeleteInfo{
@@ -147,7 +148,7 @@ func (r *Local) ChangePassword(info PasswordChangeInfo) error {
for _, user := range userlist {
passwordChangeSql := fmt.Sprintf("set password for %s = password('%s')", user, info.Password)
if !strings.HasPrefix(info.Version, "5.7") && !strings.HasPrefix(info.Version, "5.6") {
passwordChangeSql = fmt.Sprintf("ALTER USER %s IDENTIFIED WITH mysql_native_password BY '%s';", user, info.Password)
passwordChangeSql = fmt.Sprintf("ALTER USER %s IDENTIFIED BY '%s';", user, info.Password)
}
if err := r.ExecSQL(passwordChangeSql, info.Timeout); err != nil {
return err
@@ -164,7 +165,7 @@ func (r *Local) ChangePassword(info PasswordChangeInfo) error {
if host == "%" || host == "localhost" {
passwordRootChangeCMD := fmt.Sprintf("set password for 'root'@'%s' = password('%s')", host, info.Password)
if !strings.HasPrefix(info.Version, "5.7") && !strings.HasPrefix(info.Version, "5.6") {
passwordRootChangeCMD = fmt.Sprintf("alter user 'root'@'%s' identified with mysql_native_password BY '%s';", host, info.Password)
passwordRootChangeCMD = fmt.Sprintf("alter user 'root'@'%s' identified by '%s';", host, info.Password)
}
if err := r.ExecSQL(passwordRootChangeCMD, info.Timeout); err != nil {
return err
@@ -201,7 +202,7 @@ func (r *Local) ChangeAccess(info AccessChangeInfo) error {
Password: info.Password,
Permission: info.Permission,
Timeout: info.Timeout,
}); err != nil {
}, false); err != nil {
return err
}
if err := r.ExecSQL("flush privileges", 300); err != nil {
@@ -272,11 +273,12 @@ func (r *Local) SyncDB(version string) ([]SyncDBInfo, error) {
continue
}
dataItem := SyncDBInfo{
Name: parts[0],
From: r.From,
Format: parts[1],
Name: parts[0],
From: "local",
MysqlName: r.Database,
Format: parts[1],
}
userLines, err := r.ExecSQLForRows(fmt.Sprintf("SELECT USER,HOST FROM mysql.DB WHERE DB = '%s'", parts[0]), 300)
userLines, err := r.ExecSQLForRows(fmt.Sprintf("select user,host from mysql.db where db = '%s'", parts[0]), 300)
if err != nil {
return datas, err
}
@@ -305,18 +307,19 @@ func (r *Local) SyncDB(version string) ([]SyncDBInfo, error) {
}
}
if len(dataItem.Username) == 0 {
dataItem.Username = loadNameByDB(parts[0], version)
dataItem.Password = randomPassword(dataItem.Username)
if err := r.CreateUser(CreateInfo{
Name: parts[0],
Format: parts[1],
Version: version,
Username: parts[0],
Password: common.RandStr(16),
Username: dataItem.Username,
Password: dataItem.Password,
Permission: "%",
Timeout: 300,
}); err != nil {
}, false); err != nil {
global.LOG.Errorf("sync from remote server failed, err: create user failed %v", err)
}
dataItem.Username = parts[0]
dataItem.Permission = "%"
} else {
if isLocal {
+38 -32
View File
@@ -12,14 +12,13 @@ import (
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/mysql/helper"
)
type Remote struct {
Client *sql.DB
From string
Database string
User string
Password string
Address string
@@ -33,13 +32,13 @@ func NewRemote(db Remote) *Remote {
func (r *Remote) Create(info CreateInfo) error {
createSql := fmt.Sprintf("create database `%s` default character set %s collate %s", info.Name, info.Format, formatMap[info.Format])
if err := r.ExecSQL(createSql, info.Timeout); err != nil {
if strings.Contains(err.Error(), "ERROR 1007") {
if strings.Contains(strings.ToLower(err.Error()), "error 1007") {
return buserr.New(constant.ErrDatabaseIsExist)
}
return err
}
if err := r.CreateUser(info); err != nil {
if err := r.CreateUser(info, true); err != nil {
_ = r.ExecSQL(fmt.Sprintf("drop database if exists `%s`", info.Name), info.Timeout)
return err
}
@@ -47,7 +46,7 @@ func (r *Remote) Create(info CreateInfo) error {
return nil
}
func (r *Remote) CreateUser(info CreateInfo) error {
func (r *Remote) CreateUser(info CreateInfo, withDeleteDB bool) error {
var userlist []string
if strings.Contains(info.Permission, ",") {
ips := strings.Split(info.Permission, ",")
@@ -62,16 +61,18 @@ func (r *Remote) CreateUser(info CreateInfo) error {
for _, user := range userlist {
if err := r.ExecSQL(fmt.Sprintf("create user %s identified by '%s';", user, info.Password), info.Timeout); err != nil {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Permission: info.Permission,
ForceDelete: true,
Timeout: 300})
if strings.Contains(err.Error(), "ERROR 1396") {
if strings.Contains(strings.ToLower(err.Error()), "error 1396") {
return buserr.New(constant.ErrUserIsExist)
}
if withDeleteDB {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Permission: info.Permission,
ForceDelete: true,
Timeout: 300})
}
return err
}
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", info.Name, user)
@@ -80,15 +81,19 @@ func (r *Remote) CreateUser(info CreateInfo) error {
}
if strings.HasPrefix(info.Version, "5.7") || strings.HasPrefix(info.Version, "5.6") {
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, info.Password)
} else {
grantStr = grantStr + " with grant option;"
}
if err := r.ExecSQL(grantStr, info.Timeout); err != nil {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Permission: info.Permission,
ForceDelete: true,
Timeout: 300})
if withDeleteDB {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Permission: info.Permission,
ForceDelete: true,
Timeout: 300})
}
return err
}
}
@@ -148,7 +153,7 @@ func (r *Remote) ChangePassword(info PasswordChangeInfo) error {
for _, user := range userlist {
passwordChangeSql := fmt.Sprintf("set password for %s = password('%s')", user, info.Password)
if !strings.HasPrefix(info.Version, "5.7") && !strings.HasPrefix(info.Version, "5.6") {
passwordChangeSql = fmt.Sprintf("ALTER USER %s IDENTIFIED WITH mysql_native_password BY '%s';", user, info.Password)
passwordChangeSql = fmt.Sprintf("ALTER USER %s IDENTIFIED BY '%s';", user, info.Password)
}
if err := r.ExecSQL(passwordChangeSql, info.Timeout); err != nil {
return err
@@ -165,7 +170,7 @@ func (r *Remote) ChangePassword(info PasswordChangeInfo) error {
if host == "%" || host == "localhost" {
passwordRootChangeCMD := fmt.Sprintf("set password for 'root'@'%s' = password('%s')", host, info.Password)
if !strings.HasPrefix(info.Version, "5.7") && !strings.HasPrefix(info.Version, "5.6") {
passwordRootChangeCMD = fmt.Sprintf("alter user 'root'@'%s' identified with mysql_native_password BY '%s';", host, info.Password)
passwordRootChangeCMD = fmt.Sprintf("alter user 'root'@'%s' identified by '%s';", host, info.Password)
}
if err := r.ExecSQL(passwordRootChangeCMD, info.Timeout); err != nil {
return err
@@ -202,7 +207,7 @@ func (r *Remote) ChangeAccess(info AccessChangeInfo) error {
Password: info.Password,
Permission: info.Permission,
Timeout: info.Timeout,
}); err != nil {
}, false); err != nil {
return err
}
if err := r.ExecSQL("flush privileges", 300); err != nil {
@@ -263,7 +268,7 @@ func (r *Remote) Recover(info RecoverInfo) error {
func (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) {
var datas []SyncDBInfo
rows, err := r.Client.Query("SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME FROM information_schema.SCHEMATA")
rows, err := r.Client.Query("select schema_name, default_character_set_name from information_schema.SCHEMATA")
if err != nil {
return datas, err
}
@@ -279,11 +284,11 @@ func (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) {
}
dataItem := SyncDBInfo{
Name: dbName,
From: r.From,
MysqlName: r.From,
From: "remote",
MysqlName: r.Database,
Format: charsetName,
}
userRows, err := r.Client.Query("SELECT USER,HOST FROM mysql.DB WHERE DB = ?", dbName)
userRows, err := r.Client.Query("select user,host from mysql.db where db = ?", dbName)
if err != nil {
return datas, err
}
@@ -312,18 +317,19 @@ func (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) {
i++
}
if len(dataItem.Username) == 0 {
dataItem.Username = loadNameByDB(dbName, version)
dataItem.Password = randomPassword(dataItem.Username)
if err := r.CreateUser(CreateInfo{
Name: dbName,
Format: charsetName,
Version: version,
Username: dbName,
Password: common.RandStr(16),
Username: dataItem.Username,
Password: dataItem.Password,
Permission: "%",
Timeout: 300,
}); err != nil {
global.LOG.Errorf("sync from remote server failed, err: create user failed %v", err)
}, false); err != nil {
return datas, fmt.Errorf("sync db from remote server failed, err: create user failed %v", err)
}
dataItem.Username = dbName
dataItem.Permission = "%"
} else {
if isLocal {
+6 -1
View File
@@ -3,11 +3,12 @@ package cmd
import (
"bytes"
"fmt"
"io"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/cmd/server/app"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"io"
)
var (
@@ -31,6 +32,10 @@ var initCmd = &cobra.Command{
Use: "init",
Short: "初始化应用",
RunE: func(cmd *cobra.Command, args []string) error {
if !isRoot() {
fmt.Println("请使用 sudo 1pctl app init 或者切换到 root 用户")
return nil
}
if len(args) > 0 {
appKey = args[0]
if len(args) > 1 {
+22
View File
@@ -1,6 +1,8 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
@@ -22,6 +24,10 @@ var resetMFACmd = &cobra.Command{
Use: "mfa",
Short: "取消 1Panel 两步验证",
RunE: func(cmd *cobra.Command, args []string) error {
if !isRoot() {
fmt.Println("请使用 sudo 1pctl reset mfa 或者切换到 root 用户")
return nil
}
db, err := loadDBConn()
if err != nil {
return err
@@ -34,6 +40,10 @@ var resetSSLCmd = &cobra.Command{
Use: "https",
Short: "取消 1Panel https 方式登录",
RunE: func(cmd *cobra.Command, args []string) error {
if !isRoot() {
fmt.Println("请使用 sudo 1pctl reset https 或者切换到 root 用户")
return nil
}
db, err := loadDBConn()
if err != nil {
return err
@@ -46,6 +56,10 @@ var resetEntranceCmd = &cobra.Command{
Use: "entrance",
Short: "取消 1Panel 安全入口",
RunE: func(cmd *cobra.Command, args []string) error {
if !isRoot() {
fmt.Println("请使用 sudo 1pctl reset entrance 或者切换到 root 用户")
return nil
}
db, err := loadDBConn()
if err != nil {
return err
@@ -58,6 +72,10 @@ var resetBindIpsCmd = &cobra.Command{
Use: "ips",
Short: "取消 1Panel 授权 IP 限制",
RunE: func(cmd *cobra.Command, args []string) error {
if !isRoot() {
fmt.Println("请使用 sudo 1pctl reset ips 或者切换到 root 用户")
return nil
}
db, err := loadDBConn()
if err != nil {
return err
@@ -70,6 +88,10 @@ var resetDomainCmd = &cobra.Command{
Use: "domain",
Short: "取消 1Panel 访问域名绑定",
RunE: func(cmd *cobra.Command, args []string) error {
if !isRoot() {
fmt.Println("请使用 sudo 1pctl reset domain 或者切换到 root 用户")
return nil
}
db, err := loadDBConn()
if err != nil {
return err

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