Compare commits

...
140 Commits
Author SHA1 Message Date
ssongliu 1d5797fe68 fix: 修复了执行周期更新失败的问题 2023-03-31 17:23:30 +08:00
zhengkunwang223 bbe08ed218 style: 修改编辑器行尾符提示 (#465) 2023-03-31 07:18:15 +00:00
ssongliu 6e12eba356 fix: 修复了重启后计划任务执行周期错误的问题 (#462) 2023-03-31 04:34:14 +00:00
zhengkunwang223 3457b99df6 fix: 解决 nexus 安装失败的问题 (#461) 2023-03-31 04:16:14 +00:00
ssongliu 4ad3b82c84 fix: 解决 opencontainers/runc 系统漏洞 (#459) 2023-03-31 03:22:13 +00:00
ssongliu 85fc07c900 fix: 概览页磁盘显示过滤规则修改 (#458) 2023-03-31 03:20:19 +00:00
gengxin d71e2a74b4 feat: xterm-Support for Ctrl+MouseWheel to scaling fonts (#448)
#### What this PR does / why we need it?
#284 
Support for scaling fonts using Ctrl+mouse wheel

#446 
support Editor config the eol.
Currently, 1Panel only supports Linux, so directly set the default EOL to LF and add a new configuration item 

#### Summary of your change

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

- [x] Made sure tests are passing and test coverage is added if needed.
- [x] 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-03-31 03:16:15 +00:00
zhengkunwang223 a18105349b fix: 解决某些版本的 ubuntu 启动不了的 BUG (#451) 2023-03-30 07:08:13 +00:00
wangdan-fit2cloud 6472227b6b fix: 修复登录页面从大屏切换小屏填写信息消失问题 (#450)
#### 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-03-30 06:42:13 +00:00
ssongliu c927132aa6 fix: 优化了概览页、监控页选项排序 2023-03-29 23:00:32 +08:00
ssongliu b06058ec18 fix: 修复了概览页未显示挂载磁盘信息的问题 (#442) 2023-03-29 13:28:13 +00:00
ssongliu 57329a26c8 fix: 修复了概览页 io 延迟数据错误的问题 (#441) 2023-03-29 12:56:13 +00:00
ssongliu 4a1aa84fa8 fix: 修改 jwt 超时时间为 1 小时 (#437) 2023-03-29 07:34:13 +00:00
ssongliu cbe9c83515 fix: 修复了 jwt 无法登录的问题 (#436) 2023-03-29 03:46:14 +00:00
ssongliu 67479e7060 fix: 修复了创建容器自动拉取镜像失败的问题 (#433) 2023-03-28 11:02:11 +00:00
ssongliu b454c959b4 fix: 修复了部分容器创建完直接退出的问题 (#432) 2023-03-28 09:50:11 +00:00
wangdan-fit2cloud e2d39b9ed0 fix: 登录页面优化适配 (#425) 2023-03-27 23:07:32 +08:00
maninhill b9fbcb0e73 chore: 简化社区软件协议文案 2023-03-26 10:14:25 +08:00
ssongliu 30cb471629 fix: 修改默认升级版本 (#390) 2023-03-24 03:52:10 +00:00
ssongliu d70c22dde8 fix: 解决设置默认分组的问题 (#385) 2023-03-23 12:58:33 +00:00
ssongliu 01bb6b7c01 fix: 解决 mfa 验证回车时触发页面刷新的问题 (#383) 2023-03-23 12:34:33 +00:00
ssongliu 4f4879759e fix: 修改主机默认分组名称 (#381) 2023-03-23 08:26:33 +00:00
ssongliu 92a410fcea fix: 修改构建失败判断条件 (#380) 2023-03-23 08:24:36 +00:00
zhengkunwang223 5d1fced8e9 feat: 网站分组删除校验 (#377) 2023-03-23 04:14:31 +00:00
liqiang-fit2cloud a56e5a8abe Pr@dev@docs add readme en.md (#378)
* docs: add README_EN.md.

* docs: add README_EN.md.

* docs: add README_EN.md.

* docs: add README_EN.md.
2023-03-23 11:43:43 +08:00
liqiang-fit2cloud 28ae7f3a0c docs: add README_EN.md. (#375) 2023-03-23 11:08:11 +08:00
zhengkunwang223 8d675c81c5 feat: 优化网站分组 (#372) 2023-03-22 13:42:30 +00:00
ssongliu 66a345364f fix: 优化分组显示 (#371) 2023-03-22 10:48:29 +00:00
zhengkunwang223 a3cb8be08f feat: 修改文件上传错误处理 (#370) 2023-03-22 10:34:29 +00:00
zhengkunwang223 0861b30a7b fix: 解决应用创建没有默认网络创建失败的BUG (#369) 2023-03-22 10:22:29 +00:00
ssongliu 36f2a3eb4b fix: 优化计划任务记录查询返回 (#364) 2023-03-22 10:16:29 +00:00
zhengkunwang223 092cbbf8da feat: 应用编辑增加 select 类型处理 (#363) 2023-03-22 07:38:30 +00:00
ssongliu 39e3e8f214 fix: 优化容器菜单页面状态加载样式 (#356) 2023-03-22 07:32:29 +00:00
ssongliu fa983bdcc9 fix: 优化导入备份限制 (#361) 2023-03-22 07:20:30 +00:00
zhengkunwang223 67bb30c10c fix: 解决文件编辑,快捷提示不显示的 BUG (#355) 2023-03-22 11:56:09 +08:00
ssongliu e85340ca5d fix: 数据库启用 WAL 模式,增加连接数与超时设置 (#349)
fix: 数据库启用 WAL 模式,增加连接数与超时设置
2023-03-21 11:00:28 +00:00
ssongliu a3a1e17849 fix: 解决监控时间控件显示不全的问题 (#348)
fix: 解决监控时间控件显示不全的问题
2023-03-21 10:48:27 +00:00
ssongliu 2601135225 fix: 解决快照同步恢复失败的问题 (#345)
fix: 解决快照同步恢复失败的问题
2023-03-21 10:46:31 +00:00
ssongliu c556affc91 fix: 解决数据库密码修改未同步到应用的问题 (#343)
fix: 解决数据库密码修改未同步到应用的问题
2023-03-21 10:44:35 +00:00
ssongliu 6ee9789a2f fix: 修改镜像构建和编排创建路径限制,增加 config 校验 (#342)
fix: 修改镜像构建和编排创建路径限制,增加 config 校验
2023-03-21 10:42:37 +00:00
ssongliu 68a457ae89 fix: 升级逻辑调整 (#341)
fix: 升级逻辑调整
2023-03-21 07:16:28 +00:00
zhengkunwang223 72237596f3 feat: 证书列表增加设置自动续签功能 (#337) 2023-03-21 14:42:50 +08:00
zhengkunwang223 f516333682 删除 issue 处理相关 workflow (#339) 2023-03-21 06:40:31 +00:00
zhengkunwang223 873af25684 fix: 解决网站定时任务没有执行的BUG (#333) 2023-03-21 05:10:27 +00:00
zhengkunwang223 8b058a873e feat: 登录增加社区软件许可协议同意选项 (#322) 2023-03-20 11:26:27 +00:00
ssongliu 0c5a5a6454 fix: 创建编排改为异步操作 (#318)
fix:  创建编排改为异步操作
2023-03-20 10:16:26 +00:00
zhengkunwang223 2096049708 feat: 删除自动创建 pr 的 workflow (#313) 2023-03-20 14:07:11 +08:00
1Panel-bot 1095aa2b65 fix: 解决镜像构建输出部分丢失的问题 (#310)
fix: 解决镜像构建输出部分丢失的问题
2023-03-20 03:34:09 +00:00
Ikko Eltociear Ashimine 13679ff301 Fix typo in image.go (#306)
lenth -> length
2023-03-20 10:41:54 +08:00
1Panel-botandzhengkunwang223 89b7a06662 feat: 系统启动增加同步已安装应用步骤 (#295)
Co-authored-by: zhengkunwang223 <zhengkun@fit2cloud.com>
2023-03-20 10:38:39 +08:00
maninhill e8582bea75 Update README.md 2023-03-19 15:59:11 +08:00
wanghe 9af7926eb9 Create SECURITY.md 2023-03-18 23:08:08 +08:00
BugKing bdcdf7e181 ci: add workflow to sync to Gitee repo 2023-03-18 22:48:17 +08:00
ssongliu 887db0aff7 fix: 页面切换时,隐藏默认配置提示 2023-03-18 09:28:30 +00:00
ssongliu 4a974b7e0a fix: 解决计划任务备份文件失效仍能下载的问题 2023-03-18 03:06:31 +00:00
ssongliu fb286d2def fix: 解决慢日志开启后未刷新导致无法关闭的问题 2023-03-17 14:58:31 +00:00
ssongliu 89cb9e6693 fix: 解决 mysql 数据库带 - 字符授权失败的问题 2023-03-17 11:48:30 +00:00
ssongliu 927def4472 fix: 解决 compose 创建错误未存库的问题 2023-03-17 11:40:30 +00:00
ssongliu 84fcd31704 fix: 升级版本判断逻辑修改 2023-03-17 10:12:30 +00:00
maninhill 005e5cc01f Update README.md 2023-03-17 17:46:34 +08:00
ssongliu 2896409b3a fix: 解决计划任务保存到第三方备份账号不生效的问题 2023-03-17 09:08:31 +00:00
zhengkunwang223 791641f3e1 feat: 修改 .gitignore 规则 2023-03-17 09:02:29 +00:00
wangdan 79f266bbda fix: 修复国际化问题 2023-03-17 08:06:30 +00:00
maninhill c9edaf0d1d Update README.md 2023-03-17 14:37:53 +08:00
ssongliu ac5f73c687 fix: 国际化换行问题修改 2023-03-17 04:20:29 +00:00
ssongliu cc0667429a fix: redis 最大连接数限制 2023-03-17 04:18:29 +00:00
zhengkunwang223 92a5d6faeb style: 网站跳转按钮挪到网站域名设置页面 2023-03-17 04:16:30 +00:00
ssongliu 1111b6b494 fix: 增加 ip 或 域名正则校验 2023-03-17 03:28:29 +00:00
zhengkunwang223 be5a7c99e1 feat: 删除自动关闭 issue 的 action 2023-03-17 03:16:29 +00:00
ssongliu d6a963c087 fix: 容器创建默认值修改 2023-03-17 02:04:29 +00:00
ssongliu af753bdffe fix: 表格可点击列超长样式优化 2023-03-16 14:50:29 +00:00
zhengkunwang223 59b025353f fix: 解决手动解析模式 申请证书失败的BUG 2023-03-16 11:34:28 +00:00
zhengkunwang223 355a6b0205 feat: 网站一键部署应用,名称与单独部署应用保持一致 2023-03-16 09:46:30 +00:00
zhengkunwang223 7de80e9d5a fix: 解决打开文件编辑,控制台报错的问题 2023-03-16 09:44:28 +00:00
ssongliu 4c6d8cd20c fix: 备份恢复增加一些失败日志打印 2023-03-16 09:42:28 +00:00
ssongliu 31da89d63a fix: 数据库重启等操作增加 loading 2023-03-16 09:40:32 +00:00
zhengkunwang223 e044ca7d12 style: 修改停用提示的样式 2023-03-16 07:52:28 +00:00
ssongliu 7c037b68cd fix: 容器删除使用中对象提示信息优化 2023-03-16 07:48:28 +00:00
ssongliu 9080824a59 fix: 解决 mysql 慢日志设置修改失败的问题 2023-03-16 06:06:32 +00:00
zhengkunwang223 11f4bc2c89 fix: 解决文件和网站名称过长只显示...的问题 2023-03-16 06:04:28 +00:00
zhengkunwang223 a64ddd1eb8 feat: 修改 action name 2023-03-15 18:25:34 +08:00
1Panel-bot 2fdaecfafe feat: 删除不必要的action (#228)
feat: 删除不必要的action
2023-03-15 10:20:26 +00:00
1Panel-bot 6d1fe20736 feat: 增加 actions (#226)
feat: 增加 actions
2023-03-15 10:08:28 +00:00
wanghe f61bc047cd Update README.md 2023-03-15 17:47:18 +08:00
ssongliu 4f52580938 fix: 容器部分已知问题修改 (#224)
1. 删除存储卷增加使用中判断
2. 容器操作时增加来源判断
3. 解决删除镜像后页面没有自动刷新的问题
2023-03-15 08:48:26 +00:00
ssongliu 281d0cf880 fix: 敏感字符增加传输加密 (#219)
1. 敏感字符增加传输加密
2023-03-15 07:58:26 +00:00
zhengkunwang223 fdf9215d43 fix: 解决打开应用详情页,控制台报错的 BUG (#218) 2023-03-15 07:56:29 +00:00
zhengkunwang223 8c182e907d feat: 增加 OWNERS 文件 2023-03-15 15:39:02 +08:00
ssongliu e55af04568 fix: 移除调试打印信息 2023-03-15 13:00:31 +08:00
wanghe-fit2cloud 2462ffdbab refactor: issue template 2023-03-15 11:42:45 +08:00
ssongliu bfeb57e24f fix: oss 改用分片上传与下载 2023-03-15 11:19:25 +08:00
maninhill 9bb28cda27 Update README.md 2023-03-15 09:28:37 +08:00
ssongliu 42e522abe1 fix: 升级 1pctl 被覆盖后,手动替换 BASE_DIR 2023-03-14 23:17:54 +08:00
zhengkunwang223 fb5c3429e5 feat: 文件下载页面增加 loading 2023-03-14 23:06:19 +08:00
ssongliu 0fe1fd3c7b fix: 初始化安装时,增加默认版本 2023-03-14 19:27:46 +08:00
ssongliu bdcca7f380 fix: 调整上传文件夹 2023-03-14 19:27:46 +08:00
ssongliu 796d47d60e fix: 计划任务分页调整 2023-03-14 19:27:46 +08:00
ssongliu a9a45ce5ac fix: 调整升级逻辑 2023-03-14 19:27:46 +08:00
ssongliu d04121c551 fix: 解决镜像构建失败的问题 2023-03-14 19:27:46 +08:00
ssongliu dd06ff73e6 fix: redis aof 备份恢复与版本匹配 2023-03-14 19:27:46 +08:00
wangdan 24b3501f38 feat: 优化关于页面的版本信息 2023-03-14 19:07:54 +08:00
zhengkunwang223 ed11c0a4a6 fix: 解决删除MYSQL应用没有同步删除表数据的BUG 2023-03-14 18:18:49 +08:00
zhengkunwang223 7591a716f4 fix: 解决应用删除提示页面没有实时刷新的 BUG 2023-03-14 17:37:19 +08:00
zhengkunwang223 ce44ccdedb style: 修改接口过期时间 2023-03-14 17:27:11 +08:00
zhengkunwang223 521fca93bd style: 修改样式 2023-03-14 17:27:11 +08:00
zhengkunwang223 d6a0dc0125 feat: 修改前端超时时间为10秒 2023-03-14 15:44:36 +08:00
zhengkunwang223 0a483383b4 style: 删除无用代码 2023-03-14 15:44:36 +08:00
zhengkunwang223 6c99e04aee feat: 增加判断系统是否是 demo 接口 2023-03-14 15:05:44 +08:00
zhengkunwang223 e120bb0612 feat: 编辑器增加 plaintext 类型 2023-03-14 15:05:44 +08:00
zhengkunwang223 92a11863a7 feat: 已安装应用改为同步 2023-03-14 15:05:44 +08:00
zhengkunwang223 2091fdbe5d feat: 增加应用列表更新提示 2023-03-14 15:05:44 +08:00
zhengkunwang223 b518463c90 style: 取消网站页面的提示 2023-03-14 10:59:32 +08:00
ssongliu 94fbb265fa fix: 数据库权限去掉 localhost 2023-03-14 10:34:17 +08:00
ssongliu daefd650a5 fix: 修复计划任务编辑失败的问题 2023-03-14 10:34:17 +08:00
ssongliu 4994cc39f1 fix: 解决主机密钥连接失败的问题 2023-03-14 10:34:17 +08:00
ssongliu 2dec0bfb3c fix: 容器创建增加端口判断 2023-03-14 10:34:17 +08:00
ssongliu eb56b918a6 fix: 增加一些校验规则 2023-03-14 10:34:17 +08:00
ssongliu 35098ce79c fix: 解决删除应用时,未删除备份记录的问题 2023-03-14 10:34:17 +08:00
ssongliu 5a3a123be7 fix: 解决 compose 创建失败的问题 2023-03-14 10:34:17 +08:00
ssongliu a94e78d31e fix: 解决面板设置部分界面密码过期仍然能访问的问题 2023-03-14 10:34:17 +08:00
ssongliu 5527ef73ad fix: 登录页背景图替换 2023-03-14 10:34:17 +08:00
ssongliu f1ed976c17 fix: 解决错误容器终端参数导致页面卡死的问题 2023-03-14 10:34:17 +08:00
zhengkunwang223 471bbb5c43 feat: 解决 amd64 环境下的打包问题 2023-03-14 10:10:08 +08:00
zhengkunwang223 4ae8e580b9 style: 修改样式 2023-03-13 18:02:48 +08:00
zhengkunwang223 ffb0e72a5b feat: 删除无用代码 2023-03-13 18:02:48 +08:00
zhengkunwang223 7f9793e4bb feat: 增加 SystemUpgrade 组件 2023-03-13 18:02:48 +08:00
zhengkunwang223 c332d0284b feat: 修改 code-editor 样式 2023-03-13 18:02:48 +08:00
zhengkunwang223 8b3d84d667 feat: 增加右下角检查更新功能 2023-03-13 18:02:48 +08:00
zhengkunwang223 f009e6414a feat: 限制上传文件数量 2023-03-13 18:02:48 +08:00
zhengkunwang223 e36cbb0eb7 feat: 前端增加 msg-info 组件 2023-03-13 18:02:48 +08:00
zhengkunwang223 2fbddf3f30 fix: 解决zip文件压缩报错的BUG 2023-03-13 18:02:48 +08:00
zhengkunwang223 8927b59bae fix: 解决cc防护填写数字报错的BUG 2023-03-13 18:02:48 +08:00
zhengkunwang223 90c7f9cc2c fix: 解决上传报错的BUG 2023-03-13 18:02:48 +08:00
zhengkunwang223 83ca72e153 feat: 统一限制最大数字输入位数为15位 2023-03-13 18:02:48 +08:00
zhengkunwang223 9571d82932 fix: 解决创建软连接失败的BUG 2023-03-13 18:02:48 +08:00
zhengkunwang223 c54451c733 feat: 限制数字输入的最大长度 2023-03-13 18:02:48 +08:00
zhengkunwang223 e52ddd3f39 fix: 解决申请证书,域名无效导致的 panic 2023-03-13 18:02:48 +08:00
wangdan c488507d96 feat: 优化样式 2023-03-13 17:37:34 +08:00
maninhill db5853df7d Update README.md 2023-03-13 15:58:38 +08:00
maninhill eb2e533d14 Update README.md 2023-03-13 15:57:30 +08:00
235 changed files with 4143 additions and 2995 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ body:
required: true
attributes:
label: "1Panel 版本"
description: "可通过系统右上角下拉菜单中的`关于`选项,或查看安装目录中的 version 文件获取。"
description: "登录 1Panel Web 控制台,在页面右下角查看当前版本。"
- type: markdown
id: details
attributes:
+1 -1
View File
@@ -14,7 +14,7 @@ body:
required: true
attributes:
label: "1Panel 版本"
description: "可通过系统右上角下拉菜单中的`关于`选项,或查看安装目录中的 version 文件获取。"
description: "登录 1Panel Web 控制台,在页面右下角查看当前版本。"
- type: markdown
id: details
attributes:
+17
View File
@@ -0,0 +1,17 @@
on: pull_request
name: 1Panel 通用 PR 处理
permissions:
pull-requests: write
jobs:
generic_handler:
name: 为 PR 添加标签
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-ecosystem/action-add-labels@v1
with:
github_token: ${{ secrets.GITHUBTOKEN }}
labels: ${{ github.base_ref }}
+17
View File
@@ -0,0 +1,17 @@
on:
schedule:
- cron: "0 1 * * *"
name: Check recent handle issues
jobs:
check-recent-issues-not-handle:
runs-on: ubuntu-latest
steps:
- name: Check recent issues and send msg
uses: jumpserver/action-issues-alert@master
with:
hook: ${{ secrets.WECHAT_GROUP_WEB_HOOK }}
type: recent
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,17 @@
on:
schedule:
- cron: "0 9 * * 1-5"
name: Check untimely handle issues
jobs:
check-untimely-handle-issues:
runs-on: ubuntu-latest
steps:
- name: Check untimely issues and send msg
uses: jumpserver/action-issues-alert@master
with:
hook: ${{ secrets.WECHAT_GROUP_WEB_HOOK }}
type: untimely
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+16
View File
@@ -0,0 +1,16 @@
name: sync2gitee
on: [push]
jobs:
repo-sync:
runs-on: ubuntu-latest
steps:
- name: Mirror the Github organization repos to Gitee.
uses: Yikun/hub-mirror-action@master
with:
src: 'github/1Panel-dev'
dst: 'gitee/fit2cloud-xlab'
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
dst_token: ${{ secrets.GITEE_TOKEN }}
static_list: "1Panel"
force_update: true
-1
View File
@@ -22,5 +22,4 @@ cmd/server/__debug_bin
cmd/server/web/assets
cmd/server/web/monacoeditorwork
cmd/server/web/index.html
cmd/server/web/favicon.png
frontend/auto-imports.d.ts
+1 -1
View File
@@ -16,7 +16,7 @@ build_web:
build_bin:
cd $(SERVER_PATH) \
&& CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-static -fpic"' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
&& CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-static -fpic"' -tags 'osusergo,netgo' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
build_linux_on_mac:
cd $(SERVER_PATH) \
+11
View File
@@ -0,0 +1,11 @@
reviewers:
- zhengkunwang223
- ssongliu
- wanghe-fit2cloud
- wangdan-fit2cloud
approvers:
- zhengkunwang223
- ssongliu
- wanghe-fit2cloud
- wangdan-fit2cloud
+13 -8
View File
@@ -1,3 +1,4 @@
[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">
@@ -11,14 +12,14 @@
1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。1Panel 的功能和优势包括:
- **快速建站**:深度集成 Wordpress 和 Halo,域名绑定、SSL 证书配置等一键搞定;
- **高效管理**:通过 Web 端轻松管理 Linux 服务器,包括主机监控、文件管理、数据库管理、容器管理及常用应用软件管理;
- **快速建站**:深度集成 Wordpress 和 [Halo](https://github.com/halo-dev/halo/),域名绑定、SSL 证书配置等一键搞定;
- **高效管理**:通过 Web 端轻松管理 Linux 服务器,包括应用管理、主机监控、文件管理、数据库管理、容器管理等;
- **安全可靠**:最小漏洞暴露面,提供防火墙和安全审计等功能;
- **一键备份**:支持一键备份和恢复,备份数据云端存储,永不丢失。
## UI 展示
![UI展示](https://1panel.oss-cn-hangzhou.aliyuncs.com/img/overview.png)
![UI展示](https://resource.fit2cloud.com/1panel/img/overview.png)
## 快速开始
@@ -30,7 +31,7 @@
**一键安装**
以 root 用户执行如下命令一键安装 1Panel:
执行如下命令一键安装 1Panel:
```sh
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
@@ -39,7 +40,7 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
**学习资料**
- [在线文档](https://1panel.cn/docs/)
- [入门视频](https://1panel.cn/video.html)
- [教学视频](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
## 社区
@@ -47,18 +48,22 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
**微信交流群**
<img src="http://1panel.oss-cn-hangzhou.aliyuncs.com/img/wechat-group.jpg" width="156" height="156"/>
<img src="https://1panel.cn/img/wechat-group.jpg" width="156" height="156"/>
## 安全说明
如果您在使用过程中发现任何安全问题,请通过以下方式直接联系我们:
- 邮箱:support@fit2cloud.com
- 邮箱: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, All rights reserved.
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
+72
View File
@@ -0,0 +1,72 @@
[中文 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>
</p>
------------------------------
1Panel is a modern and Open-Source linux server operation and management panel, the functions and advantages of 1Panel include:
- **Quick website building**: Deeply integrated with Wordpress and [Halo](https://github.com/halo-dev/halo/), with one-click solutions for domain name binding, SSL certificate configuration, and more;
- **Efficient management**: Easily manage Linux servers through the web interface, including application management, host monitoring, file management, database management, container management, and more;
- **Secure and reliable**: Minimal vulnerability exposure, with firewall and security audit functions provided;
- **One-click backup**: Support for one-click backup and restore, with backup data stored in the cloud and never lost.
## UI Display
![UI Display](https://resource.fit2cloud.com/1panel/img/overview_en.png)
## Quick Start
**Online Demo**
- Address: <https://demo.1panel.cn/>
- Username: demo
- Password: 1panel
**One-Click Installation**
Execute the following command to install 1Panel with one click:
```sh
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
```
**Learning Materials**
- [Online Documentation](https://1panel.cn/docs/)
- [Teaching Videos](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
## Community
If you have any questions or suggestions, please submit a GitHub Issue or join our WeChat group for communication.
**WeChat Group**
<img src="https://1panel.cn/img/wechat-group.jpg" width="156" height="156"/>
## Security Information
If you discover any security issues, please contact us through:
- Email: support@fit2cloud.com
- Phone: 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.
+19
View File
@@ -0,0 +1,19 @@
# 安全说明
如果您发现安全问题,请直接联系我们:
- wanghe@fit2cloud.com
- support@fit2cloud.com
- 400-052-0755
感谢您的支持!
# Security Policy
All security bugs should be reported to the contact as below:
- wanghe@fit2cloud.com
- support@fit2cloud.com
- 400-052-0755
Thanks for your support!
+15
View File
@@ -128,3 +128,18 @@ func (b *BaseApi) GetAppTags(c *gin.Context) {
}
helper.SuccessWithData(c, tags)
}
// @Tags App
// @Summary Get app list update
// @Description 获取应用更新版本
// @Success 200
// @Security ApiKeyAuth
// @Router /apps/checkupdate [get]
func (b *BaseApi) GetAppListUpdate(c *gin.Context) {
res, err := appService.GetAppUpdate()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
+1 -1
View File
@@ -144,7 +144,7 @@ func (b *BaseApi) DeleteCheck(c *gin.Context) {
// @Router /apps/installed/sync [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"同步已安装应用列表","formatEN":"Sync the list of installed apps"}
func (b *BaseApi) SyncInstalled(c *gin.Context) {
if err := appInstallService.SyncAll(); err != nil {
if err := appInstallService.SyncAll(false); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
+14 -3
View File
@@ -28,9 +28,11 @@ func (b *BaseApi) Login(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := captcha.VerifyCode(req.CaptchaID, req.Captcha); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
if req.AuthMethod != "jwt" {
if err := captcha.VerifyCode(req.CaptchaID, req.Captcha); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
}
user, err := authService.Login(c, req)
@@ -167,6 +169,15 @@ func (b *BaseApi) InitUserInfo(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
// @Tags Auth
// @Summary Check System isDemo
// @Description 判断是否为demo环境
// @Success 200
// @Router /auth/demo [get]
func (b *BaseApi) CheckIsDemo(c *gin.Context) {
helper.SuccessWithData(c, global.CONF.System.IsDemo)
}
func saveLoginLogs(c *gin.Context, err error) {
var logs model.LoginLog
if err != nil {
+54 -1
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/constant"
@@ -27,6 +29,23 @@ func (b *BaseApi) CreateBackup(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if len(req.Credential) != 0 {
credential, err := base64.StdEncoding.DecodeString(req.Credential)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Credential = string(credential)
}
if len(req.AccessKey) != 0 {
accessKey, err := base64.StdEncoding.DecodeString(req.AccessKey)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.AccessKey = string(accessKey)
}
if err := backupService.Create(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -52,6 +71,23 @@ func (b *BaseApi) ListBuckets(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if len(req.Credential) != 0 {
credential, err := base64.StdEncoding.DecodeString(req.Credential)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Credential = string(credential)
}
if len(req.AccessKey) != 0 {
accessKey, err := base64.StdEncoding.DecodeString(req.AccessKey)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.AccessKey = string(accessKey)
}
buckets, err := backupService.GetBuckets(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -139,7 +175,7 @@ func (b *BaseApi) DownloadRecord(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
c.File(filePath)
helper.SuccessWithData(c, filePath)
}
// @Tags Backup Account
@@ -188,6 +224,23 @@ func (b *BaseApi) UpdateBackup(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if len(req.Credential) != 0 {
credential, err := base64.StdEncoding.DecodeString(req.Credential)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Credential = string(credential)
}
if len(req.AccessKey) != 0 {
accessKey, err := base64.StdEncoding.DecodeString(req.AccessKey)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.AccessKey = string(accessKey)
}
if err := backupService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
+31 -2
View File
@@ -70,6 +70,34 @@ func (b *BaseApi) SearchCompose(c *gin.Context) {
})
}
// @Tags Container Compose
// @Summary Test compose
// @Description 测试 compose 是否可用
// @Accept json
// @Param request body dto.ComposeCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/compose/test [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"检测 compose [name] 格式","formatEN":"check compose [name]"}
func (b *BaseApi) TestCompose(c *gin.Context) {
var req dto.ComposeCreate
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
}
isOK, err := containerService.TestCompose(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, isOK)
}
// @Tags Container Compose
// @Summary Create compose
// @Description 创建容器编排
@@ -90,11 +118,12 @@ func (b *BaseApi) CreateCompose(c *gin.Context) {
return
}
if err := containerService.CreateCompose(req); err != nil {
log, err := containerService.CreateCompose(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
helper.SuccessWithData(c, log)
}
// @Tags Container Compose
+1 -1
View File
@@ -198,7 +198,7 @@ func (b *BaseApi) TargetDownload(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
c.File(filePath)
helper.SuccessWithData(c, filePath)
}
// @Tags Cronjob
+19
View File
@@ -2,6 +2,7 @@ package v1
import (
"context"
"encoding/base64"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
@@ -29,6 +30,15 @@ func (b *BaseApi) CreateMysql(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if len(req.Password) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Password)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Password = string(password)
}
if _, err := mysqlService.Create(context.Background(), req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@@ -81,6 +91,15 @@ func (b *BaseApi) ChangeMysqlPassword(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if len(req.Value) != 0 {
value, err := base64.StdEncoding.DecodeString(req.Value)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Value = string(value)
}
if err := mysqlService.ChangePassword(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
+10
View File
@@ -2,6 +2,7 @@ package v1
import (
"bufio"
"encoding/base64"
"fmt"
"os"
@@ -106,6 +107,15 @@ func (b *BaseApi) ChangeRedisPassword(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if len(req.Value) != 0 {
value, err := base64.StdEncoding.DecodeString(req.Value)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Value = string(value)
}
if err := redisService.ChangePassword(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
-1
View File
@@ -35,7 +35,6 @@ var (
commandService = service.ServiceGroupApp.CommandService
websiteGroupService = service.ServiceGroupApp.WebsiteGroupService
websiteService = service.ServiceGroupApp.WebsiteService
websiteDnsAccountService = service.ServiceGroupApp.WebsiteDnsAccountService
websiteSSLService = service.ServiceGroupApp.WebsiteSSLService
+137 -5
View File
@@ -3,6 +3,14 @@ package v1
import (
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
@@ -10,14 +18,10 @@ 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/files"
websocket2 "github.com/1Panel-dev/1Panel/backend/utils/websocket"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"io/ioutil"
"net/http"
"os"
"path"
"strings"
)
// @Tags File
@@ -441,6 +445,28 @@ func (b *BaseApi) Download(c *gin.Context) {
c.File(filePath)
}
// @Tags File
// @Summary Download file with path
// @Description 下载指定文件
// @Accept json
// @Param request body dto.FilePath true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/download/bypath [post]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"下载文件 [path]","formatEN":"Download file [path]"}
func (b *BaseApi) DownloadFile(c *gin.Context) {
var req dto.FilePath
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
}
c.File(req.Path)
}
// @Tags File
// @Summary Load file size
// @Description 获取文件夹大小
@@ -492,6 +518,112 @@ func (b *BaseApi) LoadFromFile(c *gin.Context) {
helper.SuccessWithData(c, string(content))
}
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
if _, err := os.Stat(path.Dir(dstDir)); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(path.Dir(dstDir), os.ModePerm); err != nil {
return err
}
}
targetFile, err := os.Create(filepath.Join(dstDir, fileName))
if err != nil {
return err
}
defer targetFile.Close()
for i := 0; i < chunkCount; i++ {
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", fileName, i))
chunkData, err := ioutil.ReadFile(chunkPath)
if err != nil {
return err
}
_, err = targetFile.Write(chunkData)
if err != nil {
return err
}
}
return files.NewFileOp().DeleteDir(fileDir)
}
// @Tags File
// @Summary ChunkUpload file
// @Description 分片上传文件
// @Param file formData file true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /files/chunkupload [post]
func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
fileForm, err := c.FormFile("chunk")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
uploadFile, err := fileForm.Open()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
chunkIndex, err := strconv.Atoi(c.PostForm("chunkIndex"))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
chunkCount, err := strconv.Atoi(c.PostForm("chunkCount"))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
fileOp := files.NewFileOp()
tmpDir := path.Join(global.CONF.System.TmpDir, "upload")
if !fileOp.Stat(tmpDir) {
if err := fileOp.CreateDir(tmpDir, 0755); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
}
filename := c.PostForm("filename")
fileDir := filepath.Join(tmpDir, filename)
_ = os.MkdirAll(fileDir, 0755)
filePath := filepath.Join(fileDir, filename)
emptyFile, err := os.Create(filePath)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
defer emptyFile.Close()
chunkData, err := ioutil.ReadAll(uploadFile)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrFileUpload, err)
return
}
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex))
err = ioutil.WriteFile(chunkPath, chunkData, 0644)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileUpload, err)
return
}
if chunkIndex+1 == chunkCount {
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileUpload, err)
return
}
helper.SuccessWithData(c, true)
} else {
return
}
}
var wsUpgrade = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
-111
View File
@@ -1,111 +0,0 @@
package v1
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/gin-gonic/gin"
)
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
//fileInfoList, err := ioutil.ReadDir(fileDir)
//if err != nil {
// return err
//}
targetFile, err := os.Create(filepath.Join(dstDir, fileName))
if err != nil {
return err
}
defer targetFile.Close()
for i := 0; i < chunkCount; i++ {
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", fileName, i))
chunkData, err := ioutil.ReadFile(chunkPath)
if err != nil {
return err
}
_, err = targetFile.Write(chunkData)
if err != nil {
return err
}
}
return files.NewFileOp().DeleteDir(fileDir)
}
func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
fileForm, err := c.FormFile("chunk")
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
uploadFile, err := fileForm.Open()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
chunkIndex, err := strconv.Atoi(c.PostForm("chunkIndex"))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
chunkCount, err := strconv.Atoi(c.PostForm("chunkCount"))
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
fileOp := files.NewFileOp()
if err := fileOp.CreateDir("uploads", 0755); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
//fileID := uuid.New().String()
filename := c.PostForm("filename")
fileDir := filepath.Join(global.CONF.System.DataDir, "upload", filename)
_ = os.MkdirAll(fileDir, 0755)
filePath := filepath.Join(fileDir, filename)
emptyFile, err := os.Create(filePath)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
emptyFile.Close()
chunkData, err := ioutil.ReadAll(uploadFile)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex))
err = ioutil.WriteFile(chunkPath, chunkData, 0644)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if chunkIndex+1 == chunkCount {
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount)
if err != nil {
fmt.Println(err.Error())
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrAppDelete, err)
return
}
helper.SuccessWithData(c, true)
} else {
return
}
}
+54 -4
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/constant"
@@ -29,6 +31,23 @@ func (b *BaseApi) CreateHost(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if req.AuthMode == "password" && len(req.Password) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Password)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Password = string(password)
}
if req.AuthMode == "key" && len(req.PrivateKey) != 0 {
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.PrivateKey = string(privateKey)
}
host, err := hostService.Create(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
@@ -55,17 +74,32 @@ func (b *BaseApi) TestByInfo(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if req.AuthMode == "password" && len(req.Password) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Password)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Password = string(password)
}
if req.AuthMode == "key" && len(req.PrivateKey) != 0 {
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.PrivateKey = string(privateKey)
}
var connInfo ssh.ConnInfo
if err := copier.Copy(&connInfo, &req); err != nil {
helper.SuccessWithData(c, false)
}
_ = copier.Copy(&connInfo, &req)
connInfo.PrivateKey = []byte(req.PrivateKey)
client, err := connInfo.NewClient()
if err != nil {
helper.SuccessWithData(c, false)
return
}
defer client.Close()
helper.SuccessWithData(c, true)
}
@@ -212,6 +246,22 @@ func (b *BaseApi) UpdateHost(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if req.AuthMode == "password" && len(req.Password) != 0 {
password, err := base64.StdEncoding.DecodeString(req.Password)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.Password = string(password)
}
if req.AuthMode == "key" && len(req.PrivateKey) != 0 {
privateKey, err := base64.StdEncoding.DecodeString(req.PrivateKey)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
req.PrivateKey = string(privateKey)
}
upMap := make(map[string]interface{})
upMap["name"] = req.Name
+3
View File
@@ -1,6 +1,7 @@
package v1
import (
"sort"
"time"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
@@ -88,6 +89,7 @@ func (b *BaseApi) GetNetworkOptions(c *gin.Context) {
for _, net := range netStat {
options = append(options, net.Name)
}
sort.Strings(options)
helper.SuccessWithData(c, options)
}
@@ -98,5 +100,6 @@ func (b *BaseApi) GetIOOptions(c *gin.Context) {
for _, net := range diskStat {
options = append(options, net.Name)
}
sort.Strings(options)
helper.SuccessWithData(c, options)
}
+14 -6
View File
@@ -9,6 +9,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"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/copier"
"github.com/1Panel-dev/1Panel/backend/utils/ssh"
"github.com/1Panel-dev/1Panel/backend/utils/terminal"
@@ -39,10 +40,8 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
return
}
var connInfo ssh.ConnInfo
if err := copier.Copy(&connInfo, &host); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, constant.ErrStructTransform)
return
}
_ = copier.Copy(&connInfo, &host)
connInfo.PrivateKey = []byte(host.PrivateKey)
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
@@ -156,6 +155,15 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
}
defer wsConn.Close()
cmds := fmt.Sprintf("docker exec %s %s", containerID, command)
if len(user) != 0 {
cmds = fmt.Sprintf("docker exec -u %s %s %s", user, containerID, command)
}
stdout, err := cmd.Exec(cmds)
if wshandleError(wsConn, errors.WithMessage(err, stdout)) {
return
}
commands := fmt.Sprintf("docker exec -it %s %s", containerID, command)
if len(user) != 0 {
commands = fmt.Sprintf("docker exec -it -u %s %s %s", user, containerID, command)
@@ -187,8 +195,8 @@ func wshandleError(ws *websocket.Conn, err error) bool {
if err != nil {
global.LOG.Errorf("handler ws faled:, err: %v", err)
dt := time.Now().Add(time.Second)
if err := ws.WriteControl(websocket.CloseMessage, []byte(err.Error()), dt); err != nil {
global.LOG.Errorf("websocket writes control message failed, err: %v", err)
if ctlerr := ws.WriteControl(websocket.CloseMessage, []byte(err.Error()), dt); ctlerr != nil {
_ = ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))
}
return true
}
+22
View File
@@ -22,6 +22,28 @@ func (b *BaseApi) GetUpgradeInfo(c *gin.Context) {
helper.SuccessWithData(c, info)
}
// @Tags System Setting
// @Summary Load release notes by version
// @Description 获取版本 release notes
// @Accept json
// @Param request body dto.Upgrade true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/upgrade [get]
func (b *BaseApi) GetNotesByVersion(c *gin.Context) {
var req dto.Upgrade
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
notes, err := upgradeService.LoadNotes(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, notes)
}
// @Tags System Setting
// @Summary Upgrade
// @Description 系统更新
-89
View File
@@ -1,89 +0,0 @@
package v1
import (
"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/constant"
"github.com/gin-gonic/gin"
)
// @Tags Website Group
// @Summary List website groups
// @Description 获取网站组
// @Success 200 {anrry} model.WebsiteGroup
// @Security ApiKeyAuth
// @Router /websites/groups [get]
func (b *BaseApi) GetWebGroups(c *gin.Context) {
list, err := websiteGroupService.GetGroups()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Website Group
// @Summary Create website group
// @Description 创建网站组
// @Accept json
// @Param request body request.WebsiteGroupCreate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/groups [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建网站组 [name]","formatEN":"Create website groups [name]"}
func (b *BaseApi) CreateWebGroup(c *gin.Context) {
var req request.WebsiteGroupCreate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := websiteGroupService.CreateGroup(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Website Group
// @Summary Update website group
// @Description 更新网站组
// @Accept json
// @Param request body request.WebsiteGroupUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/groups/update [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新网站组 [name]","formatEN":"Update website groups [name]"}
func (b *BaseApi) UpdateWebGroup(c *gin.Context) {
var req request.WebsiteGroupUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := websiteGroupService.UpdateGroup(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Website Group
// @Summary Delete website group
// @Description 删除网站组
// @Accept json
// @Param request body request.WebsiteResourceReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/groups/del [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_groups","output_colume":"name","output_value":"name"}],"formatZH":"删除网站组 [name]","formatEN":"Delete website group [name]"}
func (b *BaseApi) DeleteWebGroup(c *gin.Context) {
var req request.WebsiteResourceReq
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := websiteGroupService.DeleteGroup(req.ID); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
+22
View File
@@ -176,3 +176,25 @@ func (b *BaseApi) GetWebsiteSSLById(c *gin.Context) {
}
helper.SuccessWithData(c, websiteSSL)
}
// @Tags Website SSL
// @Summary Update ssl
// @Description 更新 ssl
// @Accept json
// @Param request body request.WebsiteSSLUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /websites/ssl/update [post]
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_ssls","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新证书设置 [domain]","formatEN":"Update ssl config [domain]"}
func (b *BaseApi) UpdateWebsiteSSL(c *gin.Context) {
var req request.WebsiteSSLUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := websiteSSLService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
+15 -9
View File
@@ -69,15 +69,21 @@ type AppForm struct {
}
type AppFormFields struct {
Type string `json:"type"`
LabelZh string `json:"labelZh"`
LabelEn string `json:"labelEn"`
Required bool `json:"required"`
Default interface{} `json:"default"`
EnvKey string `json:"envKey"`
Disabled bool `json:"disabled"`
Edit bool `json:"edit"`
Rule string `json:"rule"`
Type string `json:"type"`
LabelZh string `json:"labelZh"`
LabelEn string `json:"labelEn"`
Required bool `json:"required"`
Default interface{} `json:"default"`
EnvKey string `json:"envKey"`
Disabled bool `json:"disabled"`
Edit bool `json:"edit"`
Rule string `json:"rule"`
Values []AppFormValue `json:"values"`
}
type AppFormValue struct {
Label string `json:"label"`
Value string `json:"value"`
}
type AppResource struct {
-2
View File
@@ -9,7 +9,6 @@ type UserLoginInfo struct {
Name string `json:"name"`
Token string `json:"token"`
MfaStatus string `json:"mfaStatus"`
MfaSecret string `json:"mfaSecret"`
}
type MfaCredential struct {
@@ -28,7 +27,6 @@ type Login struct {
type MFALogin struct {
Name string `json:"name"`
Password string `json:"password"`
Secret string `json:"secret"`
Code string `json:"code"`
AuthMethod string `json:"authMethod"`
}
+2 -1
View File
@@ -22,6 +22,7 @@ type ContainerInfo struct {
State string `json:"state"`
RunTime string `json:"runTime"`
IsFromApp bool `json:"isFromApp"`
IsFromCompose bool `json:"isFromCompose"`
}
@@ -129,7 +130,7 @@ type ComposeContainer struct {
State string `json:"state"`
}
type ComposeCreate struct {
Name string `json:"name" validate:"required"`
Name string `json:"name"`
From string `json:"from" validate:"required,oneof=edit path template"`
File string `json:"file"`
Path string `json:"path"`
+14 -13
View File
@@ -3,13 +3,6 @@ package dto
import "time"
type DashboardBase struct {
HaloID uint `json:"haloID"`
DateeaseID uint `json:"dateeaseID"`
JumpServerID uint `json:"jumpserverID"`
MeterSphereID uint `json:"metersphereID"`
KubeoperatorID uint `json:"kubeoperatorID"`
KubepiID uint `json:"kubepiID"`
WebsiteNumber int `json:"websiteNumber"`
DatabaseNumber int `json:"databaseNumber"`
CronjobNumber int `json:"cronjobNumber"`
@@ -55,8 +48,21 @@ type DashboardCurrent struct {
IOReadBytes uint64 `json:"ioReadBytes"`
IOWriteBytes uint64 `json:"ioWriteBytes"`
IOCount uint64 `json:"ioCount"`
IOTime uint64 `json:"ioTime"`
IOReadTime uint64 `json:"ioReadTime"`
IOWriteTime uint64 `json:"ioWriteTime"`
DiskData []DiskInfo `json:"diskData"`
NetBytesSent uint64 `json:"netBytesSent"`
NetBytesRecv uint64 `json:"netBytesRecv"`
ShotTime time.Time `json:"shotTime"`
}
type DiskInfo struct {
Path string `json:"path"`
Type string `json:"type"`
Device string `json:"device"`
Total uint64 `json:"total"`
Free uint64 `json:"free"`
Used uint64 `json:"used"`
@@ -66,9 +72,4 @@ type DashboardCurrent struct {
InodesUsed uint64 `json:"inodesUsed"`
InodesFree uint64 `json:"inodesFree"`
InodesUsedPercent float64 `json:"inodesUsedPercent"`
NetBytesSent uint64 `json:"netBytesSent"`
NetBytesRecv uint64 `json:"netBytesRecv"`
ShotTime time.Time `json:"shotTime"`
}
+1
View File
@@ -13,6 +13,7 @@ type GroupSearch struct {
type GroupUpdate struct {
ID uint `json:"id"`
Name string `json:"name"`
Type string `json:"type" validate:"required"`
IsDefault bool `json:"isDefault"`
}
+2 -2
View File
@@ -8,7 +8,7 @@ type HostOperate struct {
ID uint `json:"id"`
GroupID uint `json:"groupID"`
Name string `json:"name"`
Addr string `json:"addr" validate:"required,ip"`
Addr string `json:"addr" validate:"required"`
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
User string `json:"user" validate:"required"`
AuthMode string `json:"authMode" validate:"oneof=password key"`
@@ -19,7 +19,7 @@ type HostOperate struct {
}
type HostConnTest struct {
Addr string `json:"addr" validate:"required,ip"`
Addr string `json:"addr" validate:"required"`
Port uint `json:"port" validate:"required,number,max=65535,min=1"`
User string `json:"user" validate:"required"`
AuthMode string `json:"authMode" validate:"oneof=password key"`
+5
View File
@@ -44,3 +44,8 @@ type WebsiteDnsAccountUpdate struct {
type WebsiteResourceReq struct {
ID uint `json:"id" validate:"required"`
}
type WebsiteSSLUpdate struct {
ID uint `json:"id" validate:"required"`
AutoRenew bool `json:"autoRenew" validate:"required"`
}
+9 -7
View File
@@ -61,11 +61,13 @@ type AppService struct {
}
type AppParam struct {
Value interface{} `json:"value"`
Edit bool `json:"edit"`
Key string `json:"key"`
Rule string `json:"rule"`
LabelZh string `json:"labelZh"`
LabelEn string `json:"labelEn"`
Type string `json:"type"`
Value interface{} `json:"value"`
Edit bool `json:"edit"`
Key string `json:"key"`
Rule string `json:"rule"`
LabelZh string `json:"labelZh"`
LabelEn string `json:"labelEn"`
Type string `json:"type"`
Values interface{} `json:"values"`
ShowValue string `json:"showValue"`
}
+4 -2
View File
@@ -82,9 +82,11 @@ type SnapshotInfo struct {
}
type UpgradeInfo struct {
NewVersion string `json:"newVersion"`
ReleaseNote string `json:"releaseNote"`
NewVersion string `json:"newVersion"`
LatestVersion string `json:"latestVersion"`
ReleaseNote string `json:"releaseNote"`
}
type Upgrade struct {
Version string `json:"version"`
}
-11
View File
@@ -1,11 +0,0 @@
package model
type WebsiteGroup struct {
BaseModel
Name string `gorm:"type:varchar(64);not null" json:"name"`
Default bool `json:"default"`
}
func (w WebsiteGroup) TableName() string {
return "website_groups"
}
+5
View File
@@ -113,6 +113,7 @@ type RootInfo struct {
Name string `json:"name"`
Port int64 `json:"port"`
Password string `json:"password"`
UserPassword string `json:"userPassword"`
ContainerName string `json:"containerName"`
Param string `json:"param"`
Env string `json:"env"`
@@ -146,6 +147,10 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
if ok {
info.Password = password
}
userPassword, ok := envMap["PANEL_DB_USER_PASSWORD"].(string)
if ok {
info.UserPassword = userPassword
}
info.Port = int64(appInstall.HttpPort)
info.ID = appInstall.ID
info.ContainerName = appInstall.ContainerName
+3 -2
View File
@@ -24,7 +24,7 @@ type ICronjobRepo interface {
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
DeleteRecord(opts ...DBOption) error
StartRecords(cronjobID uint, targetPath string) model.JobRecords
StartRecords(cronjobID uint, fromLocal bool, targetPath string) model.JobRecords
EndRecords(record model.JobRecords, status, message, records string)
}
@@ -112,10 +112,11 @@ func (c *CronjobRepo) WithByJobID(id int) DBOption {
}
}
func (u *CronjobRepo) StartRecords(cronjobID uint, targetPath string) model.JobRecords {
func (u *CronjobRepo) StartRecords(cronjobID uint, fromLocal bool, targetPath string) model.JobRecords {
var record model.JobRecords
record.StartTime = time.Now()
record.CronjobID = cronjobID
record.FromLocal = fromLocal
record.Status = constant.StatusWaiting
if err := global.DB.Create(&record).Error; err != nil {
global.LOG.Errorf("create record status failed, err: %v", err)
+5
View File
@@ -19,6 +19,7 @@ type IMysqlRepo interface {
Delete(ctx context.Context, opts ...DBOption) error
Update(id uint, vars map[string]interface{}) error
UpdateDatabaseInfo(id uint, vars map[string]interface{}) error
DeleteAll(ctx context.Context) error
}
func NewIMysqlRepo() IMysqlRepo {
@@ -65,6 +66,10 @@ func (u *MysqlRepo) Delete(ctx context.Context, opts ...DBOption) error {
return getTx(ctx, opts...).Delete(&model.DatabaseMysql{}).Error
}
func (u *MysqlRepo) DeleteAll(ctx context.Context) error {
return getTx(ctx).Where("1 = 1").Delete(&model.DatabaseMysql{}).Error
}
func (u *MysqlRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.DatabaseMysql{}).Where("id = ?", id).Updates(vars).Error
}
-1
View File
@@ -19,7 +19,6 @@ type RepoGroup struct {
BackupRepo
WebsiteRepo
WebsiteDomainRepo
WebsiteGroupRepo
WebsiteDnsAccountRepo
WebsiteSSLRepo
WebsiteAcmeAccountRepo
+3 -3
View File
@@ -14,7 +14,7 @@ type IGroupRepo interface {
Create(group *model.Group) error
Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error
CancelDefault() error
CancelDefault(groupType string) error
WithByIsDefault(isDefault bool) DBOption
}
@@ -64,6 +64,6 @@ func (u *GroupRepo) Delete(opts ...DBOption) error {
return db.Delete(&model.Group{}).Error
}
func (w GroupRepo) CancelDefault() error {
return global.DB.Model(&model.Group{}).Where("`is_default` = 1").Updates(map[string]interface{}{"is_default": 0}).Error
func (u *GroupRepo) CancelDefault(groupType string) error {
return global.DB.Model(&model.Group{}).Where("is_default = ? AND type = ?", 1, groupType).Updates(map[string]interface{}{"is_default": 0}).Error
}
-44
View File
@@ -1,44 +0,0 @@
package repo
import (
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/global"
"gorm.io/gorm/clause"
)
type WebsiteGroupRepo struct {
}
func (w WebsiteGroupRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteGroup, error) {
var groups []model.WebsiteGroup
db := getDb(opts...).Model(&model.WebsiteGroup{})
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Order("`default` desc").Find(&groups).Error
return count, groups, err
}
func (w WebsiteGroupRepo) GetBy(opts ...DBOption) ([]model.WebsiteGroup, error) {
var groups []model.WebsiteGroup
db := getDb(opts...).Model(&model.WebsiteGroup{})
if err := db.Order("`default` desc").Find(&groups).Error; err != nil {
return groups, err
}
return groups, nil
}
func (w WebsiteGroupRepo) Create(app *model.WebsiteGroup) error {
return getDb().Omit(clause.Associations).Create(app).Error
}
func (w WebsiteGroupRepo) Save(app *model.WebsiteGroup) error {
return getDb().Omit(clause.Associations).Save(app).Error
}
func (w WebsiteGroupRepo) DeleteBy(opts ...DBOption) error {
return getDb(opts...).Delete(&model.WebsiteGroup{}).Error
}
func (w WebsiteGroupRepo) CancelDefault() error {
return global.DB.Model(&model.WebsiteGroup{}).Where("`default` = 1").Updates(map[string]interface{}{"default": 0}).Error
}
+8
View File
@@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"io/ioutil"
"net/http"
"os"
@@ -34,6 +35,7 @@ type IAppService interface {
GetAppDetail(appId uint, version string) (response.AppDetailDTO, error)
Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error)
SyncAppList() error
GetAppUpdate() (*response.AppUpdateRes, error)
}
func NewIAppService() IAppService {
@@ -165,6 +167,9 @@ func (a AppService) GetAppDetail(appId uint, version string) (response.AppDetail
}
func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error) {
if err := docker.CreateDefaultDockerNetwork(); err != nil {
return nil, buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil)
}
if list, _ := appInstallRepo.ListBy(commonRepo.WithByName(req.Name)); len(list) > 0 {
return nil, buserr.New(constant.ErrNameIsExist)
}
@@ -252,6 +257,9 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
if err := createLink(ctx, app, &appInstall, req.Params); err != nil {
return nil, err
}
if err := upAppPre(app, appInstall); err != nil {
return nil, err
}
go upApp(appInstall.GetComposePath(), appInstall)
go updateToolApp(appInstall)
return &appInstall, nil
+62 -31
View File
@@ -3,9 +3,6 @@ package service
import (
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/backend/utils/env"
"github.com/1Panel-dev/1Panel/backend/utils/nginx"
"github.com/joho/godotenv"
"io/ioutil"
"math"
"os"
@@ -14,6 +11,10 @@ import (
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/utils/env"
"github.com/1Panel-dev/1Panel/backend/utils/nginx"
"github.com/joho/godotenv"
"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"
@@ -33,7 +34,28 @@ import (
type AppInstallService struct {
}
func (a AppInstallService) Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error) {
type IAppInstallService interface {
Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error)
CheckExist(key string) (*response.AppInstalledCheck, error)
LoadPort(key string) (int64, error)
LoadPassword(key string) (string, error)
SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error)
Operate(req request.AppInstalledOperate) error
Update(req request.AppInstalledUpdate) error
SyncAll(systemInit bool) error
GetServices(key string) ([]response.AppService, error)
GetUpdateVersions(installId uint) ([]dto.AppVersion, error)
GetParams(id uint) ([]response.AppParam, error)
ChangeAppPort(req request.PortUpdate) error
GetDefaultConfigByKey(key string) (string, error)
DeleteCheck(installId uint) ([]dto.AppResource, error)
}
func NewIAppInstalledService() IAppInstallService {
return &AppInstallService{}
}
func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error) {
var opts []repo.DBOption
if req.Name != "" {
@@ -73,7 +95,7 @@ func (a AppInstallService) Page(req request.AppInstalledSearch) (int64, []respon
return total, installDTOs, nil
}
func (a AppInstallService) CheckExist(key string) (*response.AppInstalledCheck, error) {
func (a *AppInstallService) CheckExist(key string) (*response.AppInstalledCheck, error) {
res := &response.AppInstalledCheck{
IsExist: false,
}
@@ -103,7 +125,7 @@ func (a AppInstallService) CheckExist(key string) (*response.AppInstalledCheck,
return res, nil
}
func (a AppInstallService) LoadPort(key string) (int64, error) {
func (a *AppInstallService) LoadPort(key string) (int64, error) {
app, err := appInstallRepo.LoadBaseInfo(key, "")
if err != nil {
return int64(0), nil
@@ -111,7 +133,7 @@ func (a AppInstallService) LoadPort(key string) (int64, error) {
return app.Port, nil
}
func (a AppInstallService) LoadPassword(key string) (string, error) {
func (a *AppInstallService) LoadPassword(key string) (string, error) {
app, err := appInstallRepo.LoadBaseInfo(key, "")
if err != nil {
return "", nil
@@ -119,7 +141,7 @@ func (a AppInstallService) LoadPassword(key string) (string, error) {
return app.Password, nil
}
func (a AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error) {
func (a *AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error) {
var (
installs []model.AppInstall
err error
@@ -152,7 +174,7 @@ func (a AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]r
return handleInstalled(installs, false)
}
func (a AppInstallService) Operate(req request.AppInstalledOperate) error {
func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallId))
if err != nil {
return err
@@ -196,7 +218,7 @@ func (a AppInstallService) Operate(req request.AppInstalledOperate) error {
}
}
func (a AppInstallService) Update(req request.AppInstalledUpdate) error {
func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
installed, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallId))
if err != nil {
return err
@@ -270,25 +292,28 @@ func (a AppInstallService) Update(req request.AppInstalledUpdate) error {
return nil
}
func (a AppInstallService) SyncAll() error {
func (a *AppInstallService) SyncAll(systemInit bool) error {
allList, err := appInstallRepo.ListBy()
if err != nil {
return err
}
go func() {
for _, i := range allList {
if i.Status == constant.Installing {
continue
}
if err := syncById(i.ID); err != nil {
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
for _, i := range allList {
if i.Status == constant.Installing {
if systemInit {
i.Status = constant.Error
i.Message = "System restart causes application exception"
_ = appInstallRepo.Save(&i)
}
continue
}
}()
if err := syncById(i.ID); err != nil {
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
}
}
return nil
}
func (a AppInstallService) GetServices(key string) ([]response.AppService, error) {
func (a *AppInstallService) GetServices(key string) ([]response.AppService, error) {
app, err := appRepo.GetFirst(appRepo.WithKey(key))
if err != nil {
return nil, err
@@ -312,7 +337,7 @@ func (a AppInstallService) GetServices(key string) ([]response.AppService, error
return res, nil
}
func (a AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion, error) {
func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion, error) {
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
var versions []dto.AppVersion
if err != nil {
@@ -337,7 +362,7 @@ func (a AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion,
return versions, nil
}
func (a AppInstallService) ChangeAppPort(req request.PortUpdate) error {
func (a *AppInstallService) ChangeAppPort(req request.PortUpdate) error {
if common.ScanPort(int(req.Port)) {
return buserr.WithDetail(constant.ErrPortInUsed, req.Port, nil)
}
@@ -365,7 +390,7 @@ func (a AppInstallService) ChangeAppPort(req request.PortUpdate) error {
return nil
}
func (a AppInstallService) DeleteCheck(installId uint) ([]dto.AppResource, error) {
func (a *AppInstallService) DeleteCheck(installId uint) ([]dto.AppResource, error) {
var res []dto.AppResource
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
if err != nil {
@@ -406,7 +431,7 @@ func (a AppInstallService) DeleteCheck(installId uint) ([]dto.AppResource, error
return res, nil
}
func (a AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
func (a *AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
appInstall, err := getAppInstallByKey(key)
if err != nil {
return "", err
@@ -428,7 +453,7 @@ func (a AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
return string(contentByte), nil
}
func (a AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
func (a *AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
var (
res []response.AppParam
appForm dto.AppForm
@@ -461,14 +486,20 @@ func (a AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
}
appParam.LabelZh = form.LabelZh
appParam.LabelEn = form.LabelEn
appParam.Value = v
if form.Type == "service" {
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(v.(string)))
appParam.Value = appInstall.Name
res = append(res, appParam)
} else {
appParam.Value = v
res = append(res, appParam)
appParam.ShowValue = appInstall.Name
} else if form.Type == "select" {
for _, fv := range form.Values {
if fv.Value == v {
appParam.ShowValue = fv.Label
break
}
}
appParam.Values = form.Values
}
res = append(res, appParam)
}
}
return res, nil
@@ -624,7 +655,7 @@ func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value
}, commonRepo.WithByID(appInstall.ID))
}
if param == "user-password" {
oldVal = fmt.Sprintf("\"PANEL_DB_USER_PASSWORD\":\"%v\"", appInstall.Password)
oldVal = fmt.Sprintf("\"PANEL_DB_USER_PASSWORD\":\"%v\"", appInstall.UserPassword)
newVal = fmt.Sprintf("\"PANEL_DB_USER_PASSWORD\":\"%v\"", value)
_ = appInstallRepo.BatchUpdateBy(map[string]interface{}{
"param": strings.ReplaceAll(appInstall.Param, oldVal, newVal),
+18 -3
View File
@@ -4,8 +4,6 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/utils/env"
"math"
"os"
"path"
@@ -13,6 +11,9 @@ import (
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/utils/env"
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
"github.com/1Panel-dev/1Panel/backend/buserr"
@@ -159,7 +160,10 @@ func deleteAppInstall(ctx context.Context, install model.AppInstall, deleteBacku
global.LOG.Infof("delete app %s-%s backups successful", install.App.Key, install.Name)
}
_ = 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.DeleteAll(ctx)
}
return nil
}
@@ -335,6 +339,17 @@ func copyAppData(key, version, installName string, params map[string]interface{}
return
}
// 处理文件夹权限等问题
func upAppPre(app model.App, appInstall model.AppInstall) error {
if app.Key == "nexus" {
dataPath := path.Join(appInstall.GetPath(), "data")
if err := files.NewFileOp().Chown(dataPath, 200, 0); err != nil {
return err
}
}
return nil
}
func upApp(composeFilePath string, appInstall model.AppInstall) {
out, err := compose.Up(composeFilePath)
if err != nil {
+3 -3
View File
@@ -86,9 +86,9 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLogi
}
pass, err := encrypt.StringDecrypt(passwrodSetting.Value)
if err != nil {
return nil, constant.ErrAuth
return nil, err
}
if info.Password != pass && nameSetting.Value != info.Name {
if info.Password != pass || nameSetting.Value != info.Name {
return nil, constant.ErrAuth
}
@@ -118,7 +118,7 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (
j := jwt.NewJWT()
claims := j.CreateClaims(jwt.BaseClaims{
Name: name,
}, lifeTime)
})
token, err := j.CreateToken(claims)
if err != nil {
return nil, err
+10 -7
View File
@@ -134,7 +134,7 @@ func (u *BackupService) DownloadRecord(info dto.DownloadRecord) (string, error)
tempPath := fmt.Sprintf("%sdownload%s", constant.DataDir, info.FileDir)
if _, err := os.Stat(tempPath); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(tempPath, os.ModePerm); err != nil {
fmt.Println(err)
global.LOG.Errorf("mkdir %s failed, err: %v", tempPath, err)
}
}
targetPath := tempPath + info.FileName
@@ -222,6 +222,12 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
if err := json.Unmarshal([]byte(req.Vars), &varMap); err != nil {
return err
}
oldVars := backup.Vars
oldDir, err := loadLocalDir()
if err != nil {
return err
}
upMap := make(map[string]interface{})
upMap["bucket"] = req.Bucket
upMap["credential"] = req.Credential
@@ -235,9 +241,8 @@ func (u *BackupService) Update(req dto.BackupOperate) error {
if strings.HasSuffix(dirStr, "/") {
dirStr = dirStr[:strings.LastIndex(dirStr, "/")]
}
if err := updateBackupDir(dirStr); err != nil {
upMap["vars"] = backup.Vars
_ = backupRepo.Update(req.ID, upMap)
if err := updateBackupDir(dirStr, oldDir); err != nil {
_ = backupRepo.Update(req.ID, (map[string]interface{}{"vars": oldVars}))
return err
}
}
@@ -309,8 +314,7 @@ func loadLocalDir() (string, error) {
return "", fmt.Errorf("error type dir: %T", varMap["dir"])
}
func updateBackupDir(dir string) error {
oldDir := global.CONF.System.Backup
func updateBackupDir(dir, oldDir string) error {
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
return err
@@ -323,6 +327,5 @@ func updateBackupDir(dir string) error {
if err != nil {
return errors.New(string(stdout))
}
global.CONF.System.Backup = dir
return nil
}
+4 -1
View File
@@ -103,7 +103,7 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
}
resource, _ := appInstallResourceRepo.GetFirst(appInstallResourceRepo.WithAppInstallId(install.ID))
if resource.ID != 0 {
if resource.ID != 0 && resource.ResourceId != 0 {
mysqlInfo, err := appInstallRepo.LoadBaseInfo(constant.AppMysql, "")
if err != nil {
return err
@@ -181,16 +181,19 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
return err
}
if err := handleMysqlRecover(mysqlInfo, tmpPath, db.Name, fmt.Sprintf("%s.sql.gz", install.Name), true); err != nil {
global.LOG.Errorf("handle recover from sql.gz failed, err: %v", err)
return err
}
}
if err := handleUnTar(tmpPath+"/app.tar.gz", fmt.Sprintf("%s/%s", constant.AppInstallDir, install.App.Key)); err != nil {
global.LOG.Errorf("handle recover from app.tar.gz failed, err: %v", err)
return err
}
oldInstall.Status = constant.Running
if err := appInstallRepo.Save(install); err != nil {
global.LOG.Errorf("save db app install failed, err: %v", err)
return err
}
isOk = true
+6 -2
View File
@@ -121,7 +121,10 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
}
if appendonly == "yes" {
if !strings.HasSuffix(recoverFile, ".tar.gz") || !strings.HasSuffix(recoverFile, ".aof") {
if redisInfo.Version == "6.0.16" && !strings.HasSuffix(recoverFile, ".aof") {
return buserr.New(constant.ErrTypeOfRedis)
}
if redisInfo.Version == "7.0.5" && !strings.HasSuffix(recoverFile, ".tar.gz") {
return buserr.New(constant.ErrTypeOfRedis)
}
} else {
@@ -170,7 +173,7 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
}
} else {
itemName := "dump.rdb"
if redisInfo.Version == "6.0.16" {
if appendonly == "yes" && redisInfo.Version == "6.0.16" {
itemName = "appendonly.aof"
}
input, err := ioutil.ReadFile(recoverFile)
@@ -184,5 +187,6 @@ func handleRedisRecover(redisInfo *repo.RootInfo, recoverFile string, isRollback
if _, err := compose.Up(composeDir + "/docker-compose.yml"); err != nil {
return err
}
isOk = true
return nil
}
+7 -1
View File
@@ -106,8 +106,8 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
return fmt.Errorf("backup website %s for rollback before recover failed, err: %v", website.Alias, err)
}
defer func() {
global.LOG.Info("recover failed, start to rollback now")
if !isOk {
global.LOG.Info("recover failed, start to rollback now")
if err := handleWebsiteRecover(website, rollbackFile, true); err != nil {
global.LOG.Errorf("rollback website %s from %s failed, err: %v", website.Alias, rollbackFile, err)
return
@@ -126,6 +126,7 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
}
nginxConfPath := fmt.Sprintf("%s/openresty/%s/conf/conf.d", constant.AppInstallDir, nginxInfo.Name)
if err := fileOp.CopyFile(fmt.Sprintf("%s/%s.conf", tmpPath, website.Alias), nginxConfPath); err != nil {
global.LOG.Errorf("handle recover from conf.d failed, err: %v", err)
return err
}
@@ -135,22 +136,27 @@ func handleWebsiteRecover(website *model.Website, recoverFile string, isRollback
return err
}
if err := handleAppRecover(&app, fmt.Sprintf("%s/%s.app.tar.gz", tmpPath, website.Alias), true); err != nil {
global.LOG.Errorf("handle recover from app.tar.gz failed, err: %v", err)
return err
}
if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, app.App.Key, app.Name)); err != nil {
global.LOG.Errorf("docker-compose restart failed, err: %v", err)
return err
}
}
siteDir := fmt.Sprintf("%s/openresty/%s/www/sites", constant.AppInstallDir, nginxInfo.Name)
if err := handleUnTar(fmt.Sprintf("%s/%s.web.tar.gz", tmpPath, website.Alias), siteDir); err != nil {
global.LOG.Errorf("handle recover from web.tar.gz failed, err: %v", err)
return err
}
stdout, err := cmd.Execf("docker exec -i %s nginx -s reload", nginxInfo.ContainerName)
if err != nil {
global.LOG.Errorf("nginx -s reload failed, err: %s", stdout)
return errors.New(string(stdout))
}
if err := websiteRepo.SaveWithoutCtx(&oldWebsite); err != nil {
global.LOG.Errorf("handle save website data failed, err: %v", err)
return err
}
isOk = true
+47 -10
View File
@@ -3,7 +3,9 @@ package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"os/exec"
"sort"
@@ -12,13 +14,16 @@ import (
"time"
"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/common"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)
@@ -31,7 +36,7 @@ type IContainerService interface {
PageVolume(req dto.SearchWithPage) (int64, interface{}, error)
ListVolume() ([]dto.Options, error)
PageCompose(req dto.SearchWithPage) (int64, interface{}, error)
CreateCompose(req dto.ComposeCreate) error
CreateCompose(req dto.ComposeCreate) (string, error)
ComposeOperation(req dto.ComposeOperation) error
ContainerCreate(req dto.ContainerCreate) error
ContainerOperation(req dto.ContainerOperation) error
@@ -96,6 +101,10 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
if _, ok := container.Labels[composeProjectLabel]; ok {
IsFromCompose = true
}
IsFromApp := false
if created, ok := container.Labels[composeCreatedBy]; ok && created == "Apps" {
IsFromApp = true
}
backDatas = append(backDatas, dto.ContainerInfo{
ContainerID: container.ID,
CreateTime: time.Unix(container.Created, 0).Format("2006-01-02 15:04:05"),
@@ -104,6 +113,7 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
ImageName: container.Image,
State: container.State,
RunTime: container.Status,
IsFromApp: IsFromApp,
IsFromCompose: IsFromCompose,
})
}
@@ -136,15 +146,24 @@ func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
}
func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
if len(req.ExposedPorts) != 0 {
for _, port := range req.ExposedPorts {
if common.ScanPort(port.HostPort) {
return buserr.WithDetail(constant.ErrPortInUsed, port.HostPort, nil)
}
}
}
client, err := docker.NewDockerClient()
if err != nil {
return err
}
config := &container.Config{
Image: req.Image,
Cmd: req.Cmd,
Env: req.Env,
Labels: stringsToMap(req.Labels),
Image: req.Image,
Cmd: req.Cmd,
Env: req.Env,
Labels: stringsToMap(req.Labels),
Tty: true,
OpenStdin: true,
}
hostConf := &container.HostConfig{
AutoRemove: req.AutoRemove,
@@ -176,14 +195,19 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerCreate) error {
}
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
container, err := client.ContainerCreate(context.TODO(), config, hostConf, &network.NetworkingConfig{}, &v1.Platform{}, req.Name)
ctx := context.Background()
if err := pullImages(ctx, client, req.Image); err != nil {
return err
}
container, err := client.ContainerCreate(ctx, config, hostConf, &network.NetworkingConfig{}, &v1.Platform{}, req.Name)
if err != nil {
_ = client.ContainerRemove(context.Background(), req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
_ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
return err
}
global.LOG.Infof("create container %s successful! now check if the container is started and delete the container information if it is not.", req.Name)
if err := client.ContainerStart(context.TODO(), container.ID, types.ContainerStartOptions{}); err != nil {
_ = client.ContainerRemove(context.Background(), req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
if err := client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}); err != nil {
_ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
return fmt.Errorf("create successful but start failed, err: %v", err)
}
return nil
@@ -225,7 +249,7 @@ func (u *ContainerService) ContainerLogs(req dto.ContainerLog) (string, error) {
}
stdout, err := cmd.CombinedOutput()
if err != nil {
return "", err
return "", errors.New(string(stdout))
}
return string(stdout), nil
}
@@ -306,3 +330,16 @@ func calculateNetwork(network map[string]types.NetworkStats) (float64, float64)
}
return rx, tx
}
func pullImages(ctx context.Context, client *client.Client, image string) error {
out, err := client.ImagePull(ctx, image, types.ImagePullOptions{})
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(ioutil.Discard, out)
if err != nil {
return err
}
return nil
}
+72 -31
View File
@@ -5,6 +5,8 @@ import (
"errors"
"fmt"
"os"
"os/exec"
"path"
"sort"
"strings"
"time"
@@ -123,41 +125,49 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
return int64(total), BackDatas, nil
}
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
if req.From == "template" {
template, err := composeRepo.Get(commonRepo.WithByID(req.Template))
if err != nil {
return err
}
req.From = "edit"
req.File = template.Content
func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
if err := u.loadPath(&req); err != nil {
return false, err
}
if req.From == "edit" {
dir := fmt.Sprintf("%s/docker/compose/%s", constant.DataDir, req.Name)
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
return err
}
}
cmd := exec.Command("docker-compose", "-f", req.Path, "config")
stdout, err := cmd.CombinedOutput()
if err != nil {
return false, errors.New(string(stdout))
}
return true, nil
}
path := fmt.Sprintf("%s/docker-compose.yml", dir)
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(string(req.File))
write.Flush()
req.Path = path
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error) {
if err := u.loadPath(&req); err != nil {
return "", err
}
global.LOG.Infof("docker-compose.yml %s create successful, start to docker-compose up", req.Name)
if stdout, err := compose.Up(req.Path); err != nil {
return errors.New(string(stdout))
}
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
return nil
if req.From == "path" {
req.Name = path.Base(strings.ReplaceAll(req.Path, "/"+path.Base(req.Path), ""))
}
logName := path.Dir(req.Path) + "/compose.log"
file, err := os.OpenFile(logName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return "", err
}
go func() {
defer file.Close()
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
stdout, err := cmd.CombinedOutput()
_, _ = file.Write(stdout)
if err != nil {
global.LOG.Errorf("docker-compose up %s failed, err: %v", req.Name, err)
_, _ = compose.Down(req.Path)
_, _ = file.WriteString("docker-compose up failed!")
return
}
global.LOG.Infof("docker-compose up %s successful!", req.Name)
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
_, _ = file.WriteString("docker-compose up successful!")
}()
return logName, nil
}
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
@@ -170,7 +180,7 @@ func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
global.LOG.Infof("docker-compose %s %s successful", req.Operation, req.Name)
if req.Operation == "down" {
_ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name))
_ = os.RemoveAll(strings.ReplaceAll(req.Path, req.Name+"/docker-compose.yml", ""))
_ = os.RemoveAll(strings.ReplaceAll(req.Path, "/docker-compose.yml", ""))
}
return nil
@@ -199,3 +209,34 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
return nil
}
func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
if req.From == "template" {
template, err := composeRepo.Get(commonRepo.WithByID(req.Template))
if err != nil {
return err
}
req.From = "edit"
req.File = template.Content
}
if req.From == "edit" {
dir := fmt.Sprintf("%s/docker/compose/%s", constant.DataDir, req.Name)
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
return err
}
}
path := fmt.Sprintf("%s/docker-compose.yml", dir)
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer file.Close()
write := bufio.NewWriter(file)
_, _ = write.WriteString(string(req.File))
write.Flush()
req.Path = path
}
return nil
}
+5
View File
@@ -7,6 +7,8 @@ import (
"strings"
"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/docker"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/network"
@@ -80,6 +82,9 @@ func (u *ContainerService) DeleteNetwork(req dto.BatchDelete) error {
}
for _, id := range req.Names {
if err := client.NetworkRemove(context.TODO(), id); err != nil {
if strings.Contains(err.Error(), "has active endpoints") {
return buserr.WithDetail(constant.ErrInUsed, id, nil)
}
return err
}
}
+5 -6
View File
@@ -2,12 +2,12 @@ package service
import (
"context"
"fmt"
"sort"
"strings"
"time"
"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/docker"
"github.com/docker/docker/api/types"
@@ -96,6 +96,9 @@ func (u *ContainerService) DeleteVolume(req dto.BatchDelete) error {
}
for _, id := range req.Names {
if err := client.VolumeRemove(context.TODO(), id, true); err != nil {
if strings.Contains(err.Error(), "volume is in use") {
return buserr.WithDetail(constant.ErrInUsed, id, nil)
}
return err
}
}
@@ -122,12 +125,8 @@ func (u *ContainerService) CreateVolume(req dto.VolumeCreat) error {
DriverOpts: stringsToMap(req.Options),
Labels: stringsToMap(req.Labels),
}
stat, err := client.VolumeCreate(context.TODO(), options)
if err != nil {
if _, err := client.VolumeCreate(context.TODO(), options); err != nil {
return err
}
// if len(stat.CreatedAt) != 0 {
fmt.Println(stat)
// }
return nil
}
+41 -73
View File
@@ -2,17 +2,14 @@ package service
import (
"bufio"
"encoding/json"
"fmt"
"os"
"strings"
"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"
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
"github.com/robfig/cron/v3"
@@ -92,69 +89,28 @@ func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) {
if cronjob.ID == 0 {
return "", constant.ErrRecordNotFound
}
global.LOG.Infof("start to download records %s from %s", cronjob.Type, backup.Type)
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
if backup.Type == "LOCAL" {
if _, err := os.Stat(record.File); err != nil && os.IsNotExist(err) {
return "", constant.ErrRecordNotFound
}
return record.File, nil
}
if record.FromLocal {
local, _ := loadLocalDir()
if _, err := os.Stat(local + "/" + record.File); err == nil {
return local + "/" + record.File, nil
}
}
client, err := NewIBackupService().NewClient(&backup)
if err != nil {
return "", err
}
varMap["type"] = backup.Type
if backup.Type != "LOCAL" {
varMap["bucket"] = backup.Bucket
switch backup.Type {
case constant.Sftp:
varMap["username"] = backup.AccessKey
varMap["password"] = backup.Credential
case constant.OSS, constant.S3, constant.MinIo:
varMap["accessKey"] = backup.AccessKey
varMap["secretKey"] = backup.Credential
}
backClient, err := cloud_storage.NewCloudStorageClient(varMap)
if err != nil {
return "", fmt.Errorf("new cloud storage client failed, err: %v", err)
}
global.LOG.Info("new backup client successful")
commonDir := fmt.Sprintf("%s/%s/", cronjob.Type, cronjob.Name)
name := fmt.Sprintf("%s%s.tar.gz", commonDir, record.StartTime.Format("20060102150405"))
if cronjob.Type == "database" {
name = fmt.Sprintf("%s%s.gz", commonDir, record.StartTime.Format("20060102150405"))
}
tempPath := fmt.Sprintf("%s/download/%s", constant.DataDir, commonDir)
if _, err := os.Stat(tempPath); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(tempPath, os.ModePerm); err != nil {
fmt.Println(err)
}
}
global.LOG.Infof("download records %s from %s to %s", name, commonDir, tempPath)
targetPath := tempPath + strings.ReplaceAll(name, commonDir, "")
if _, err = os.Stat(targetPath); err != nil && os.IsNotExist(err) {
isOK, err := backClient.Download(name, targetPath)
if !isOK {
return "", fmt.Errorf("cloud storage download failed, err: %v", err)
}
}
return targetPath, nil
}
if _, ok := varMap["dir"]; !ok {
return "", errors.New("load local backup dir failed")
}
global.LOG.Infof("record is save in local dir %s", varMap["dir"])
switch cronjob.Type {
case "website":
return fmt.Sprintf("%v/website/%s/website_%s_%s.tar.gz", varMap["dir"], cronjob.Website, cronjob.Website, record.StartTime.Format("20060102150405")), nil
case "database":
mysqlInfo, err := appInstallRepo.LoadBaseInfo("mysql", "")
if err != nil {
return "", fmt.Errorf("load mysqlInfo failed, err: %v", err)
}
return fmt.Sprintf("%v/database/mysql/%s/%s/db_%s_%s.sql.gz", varMap["dir"], mysqlInfo.Name, cronjob.DBName, cronjob.DBName, record.StartTime.Format("20060102150405")), nil
case "directory":
return fmt.Sprintf("%v/%s/%s/directory%s_%s.tar.gz", varMap["dir"], cronjob.Type, cronjob.Name, strings.ReplaceAll(cronjob.SourceDir, "/", "_"), record.StartTime.Format("20060102150405")), nil
default:
return "", fmt.Errorf("not support type %s", cronjob.Type)
tempPath := fmt.Sprintf("%s/download/%s", constant.DataDir, record.File)
isOK, _ := client.Download(record.File, tempPath)
if !isOK || err != nil {
return "", constant.ErrRecordNotFound
}
return tempPath, nil
}
func (u *CronjobService) HandleOnce(id uint) error {
@@ -178,24 +134,27 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
cronjob.Spec = loadSpec(cronjob)
global.LOG.Infof("create cronjob %s successful, spec: %s", cronjob.Name, cronjob.Spec)
if err := u.StartJob(&cronjob); err != nil {
entryID, err := u.StartJob(&cronjob)
if err != nil {
return err
}
cronjob.EntryID = uint64(entryID)
if err := cronjobRepo.Create(&cronjob); err != nil {
return err
}
return nil
}
func (u *CronjobService) StartJob(cronjob *model.Cronjob) error {
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID)
func (u *CronjobService) StartJob(cronjob *model.Cronjob) (int, error) {
if cronjob.EntryID != 0 {
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
}
entryID, err := u.AddCronJob(cronjob)
if err != nil {
return err
return 0, err
}
_ = cronjobRepo.Update(cronjob.ID, map[string]interface{}{"entry_id": entryID})
return nil
global.LOG.Debug(global.Cron.Entries())
return entryID, nil
}
func (u *CronjobService) Delete(ids []uint) error {
@@ -225,13 +184,17 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error {
return constant.ErrRecordNotFound
}
cronjob.EntryID = cronModel.EntryID
cronjob.Type = cronModel.Type
cronjob.Spec = loadSpec(cronjob)
if err := u.StartJob(&cronjob); err != nil {
newEntryID, err := u.StartJob(&cronjob)
if err != nil {
return err
}
upMap := make(map[string]interface{})
upMap["entry_id"] = newEntryID
upMap["name"] = req.Name
upMap["spec"] = cronjob.Spec
upMap["script"] = req.Script
upMap["spec_type"] = req.SpecType
upMap["week"] = req.Week
@@ -254,15 +217,20 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
if cronjob.ID == 0 {
return errors.WithMessage(constant.ErrRecordNotFound, "record not found")
}
var (
entryID int
err error
)
if status == constant.StatusEnable {
if err := u.StartJob(&cronjob); err != nil {
entryID, err = u.StartJob(&cronjob)
if err != nil {
return err
}
} else {
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
global.LOG.Infof("stop cronjob entryID: %d", cronjob.EntryID)
}
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status})
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status, "entry_id": entryID})
}
func (u *CronjobService) AddCronJob(cronjob *model.Cronjob) (int, error) {
+14 -9
View File
@@ -22,8 +22,7 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
message []byte
err error
)
record := cronjobRepo.StartRecords(cronjob.ID, "")
record.FromLocal = cronjob.KeepLocal
record := cronjobRepo.StartRecords(cronjob.ID, cronjob.KeepLocal, "")
go func() {
switch cronjob.Type {
case "shell":
@@ -118,6 +117,8 @@ func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Tim
return "", err
}
}
itemFileDir := strings.ReplaceAll(backupDir, localDir+"/", "")
if len(record.Name) != 0 {
record.FileName = fileName
record.FileDir = backupDir
@@ -125,7 +126,7 @@ func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Tim
record.BackupType = backup.Type
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
record.Source = backup.Type
record.FileDir = strings.ReplaceAll(backupDir, localDir+"/", "")
record.FileDir = itemFileDir
}
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
@@ -133,7 +134,10 @@ func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Tim
}
}
fullPath := fmt.Sprintf("%s/%s", record.FileDir, fileName)
fullPath := fmt.Sprintf("%s/%s", backupDir, fileName)
if backup.Type != "LOCAL" {
fullPath = fmt.Sprintf("%s/%s", itemFileDir, fileName)
}
if backup.Type == "LOCAL" {
u.HandleRmExpired(backup.Type, backupDir, cronjob, nil)
return fullPath, nil
@@ -148,10 +152,13 @@ func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Tim
if err != nil {
return fullPath, err
}
if _, err = client.Upload(backupDir+"/"+fileName, fullPath); err != nil {
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
return fullPath, err
}
u.HandleRmExpired(backup.Type, backupDir, cronjob, client)
u.HandleRmExpired(backup.Type, itemFileDir, cronjob, client)
if cronjob.KeepLocal {
u.HandleRmExpired("LOCAL", backupDir, cronjob, client)
}
return fullPath, nil
}
@@ -185,9 +192,7 @@ func (u *CronjobService) HandleRmExpired(backType, backupDir string, cronjob *mo
for i := 0; i < len(currentObjs)-int(cronjob.RetainCopies); i++ {
_, _ = backClient.Delete(currentObjs[i].(string))
}
if !cronjob.KeepLocal {
return
}
return
}
files, err := ioutil.ReadDir(backupDir)
if err != nil {
+69 -38
View File
@@ -2,9 +2,11 @@ package service
import (
"encoding/json"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/disk"
"github.com/shirou/gopsutil/v3/host"
@@ -39,27 +41,6 @@ func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto
ss, _ := json.Marshal(hostInfo)
baseInfo.VirtualizationSystem = string(ss)
apps, err := appRepo.GetBy()
if err != nil {
return nil, err
}
for _, app := range apps {
switch app.Key {
case "dateease":
baseInfo.DateeaseID = app.ID
case "halo":
baseInfo.HaloID = app.ID
case "metersphere":
baseInfo.MeterSphereID = app.ID
case "jumpserver":
baseInfo.JumpServerID = app.ID
case "kubeoperator":
baseInfo.KubeoperatorID = app.ID
case "kubepi":
baseInfo.KubepiID = app.ID
}
}
appInstall, err := appInstallRepo.ListBy()
if err != nil {
return nil, err
@@ -120,15 +101,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
state, _ := disk.Usage("/")
currentInfo.Total = state.Total
currentInfo.Free = state.Free
currentInfo.Used = state.Used
currentInfo.UsedPercent = state.UsedPercent
currentInfo.InodesTotal = state.InodesTotal
currentInfo.InodesUsed = state.InodesUsed
currentInfo.InodesFree = state.InodesFree
currentInfo.InodesUsedPercent = state.InodesUsedPercent
currentInfo.DiskData = loadDiskInfo()
if ioOption == "all" {
diskInfo, _ := disk.IOCounters()
@@ -136,20 +109,17 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
currentInfo.IOReadBytes += state.ReadBytes
currentInfo.IOWriteBytes += state.WriteBytes
currentInfo.IOCount += (state.ReadCount + state.WriteCount)
currentInfo.IOTime += state.ReadTime / 1000 / 1000
if state.WriteTime > state.ReadTime {
currentInfo.IOTime += state.WriteTime / 1000 / 1000
}
currentInfo.IOReadTime += state.ReadTime
currentInfo.IOWriteTime += state.WriteTime
}
} else {
diskInfo, _ := disk.IOCounters(ioOption)
for _, state := range diskInfo {
currentInfo.IOReadBytes += state.ReadBytes
currentInfo.IOWriteBytes += state.WriteBytes
currentInfo.IOTime += state.ReadTime / 1000 / 1000
if state.WriteTime > state.ReadTime {
currentInfo.IOTime += state.WriteTime / 1000 / 1000
}
currentInfo.IOCount += (state.ReadCount + state.WriteCount)
currentInfo.IOReadTime += state.ReadTime
currentInfo.IOWriteTime += state.WriteTime
}
}
@@ -172,3 +142,64 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
currentInfo.ShotTime = time.Now()
return &currentInfo
}
type diskInfo struct {
Type string
Mount string
Device string
}
func loadDiskInfo() []dto.DiskInfo {
var datas []dto.DiskInfo
stdout, err := cmd.Exec("df -hT -P|grep '/'|grep -v tmpfs|grep -v 'snap/core'|grep -v udev")
if err != nil {
return datas
}
lines := strings.Split(stdout, "\n")
var mounts []diskInfo
var excludes = []string{"/mnt/cdrom", "/boot", "/boot/efi", "/dev", "/dev/shm", "/run/lock", "/run", "/run/shm", "/run/user"}
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) < 7 {
continue
}
if fields[1] == "tmpfs" {
continue
}
if strings.Contains(fields[2], "M") || strings.Contains(fields[2], "K") {
continue
}
if strings.Contains(fields[6], "docker") {
continue
}
isExclude := false
for _, exclude := range excludes {
if exclude == fields[6] {
isExclude = true
}
}
if isExclude {
continue
}
mounts = append(mounts, diskInfo{Type: fields[1], Device: fields[0], Mount: fields[6]})
}
for i := 0; i < len(mounts); i++ {
state, _ := disk.Usage(mounts[i].Mount)
var itemData dto.DiskInfo
itemData.Path = mounts[i].Mount
itemData.Type = mounts[i].Type
itemData.Device = mounts[i].Device
itemData.Total = state.Total
itemData.Free = state.Free
itemData.Used = state.Used
itemData.UsedPercent = state.UsedPercent
itemData.InodesTotal = state.InodesTotal
itemData.InodesUsed = state.InodesUsed
itemData.InodesFree = state.InodesFree
itemData.InodesUsedPercent = state.InodesUsedPercent
datas = append(datas, itemData)
}
return datas
}
+1 -1
View File
@@ -292,7 +292,7 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
if err := excuteSql(app.ContainerName, app.Password, fmt.Sprintf("create user if not exists '%s'@'%s' identified by '%s';", mysql.Username, info.Value, mysql.Password)); err != nil {
return err
}
grantStr := fmt.Sprintf("grant all privileges on %s.* to '%s'@'%s'", mysql.Name, mysql.Username, info.Value)
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to '%s'@'%s'", mysql.Name, mysql.Username, info.Value)
if app.Version == "5.7.39" {
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, mysql.Password)
}
-2
View File
@@ -28,7 +28,6 @@ type ServiceGroup struct {
SettingService
BackupService
WebsiteGroupService
WebsiteService
WebsiteDnsAccountService
WebsiteSSLService
@@ -68,7 +67,6 @@ var (
backupRepo = repo.RepoGroupApp.BackupRepo
websiteRepo = repo.NewIWebsiteRepo()
websiteGroupRepo = repo.RepoGroupApp.WebsiteGroupRepo
websiteDomainRepo = repo.RepoGroupApp.WebsiteDomainRepo
websiteDnsRepo = repo.RepoGroupApp.WebsiteDnsAccountRepo
websiteSSLRepo = repo.NewISSLRepo()
+1 -1
View File
@@ -97,7 +97,7 @@ func (f FileService) Create(op request.FileCreate) error {
return fo.CreateDir(op.Path, fs.FileMode(op.Mode))
} else {
if op.IsLink {
if !fo.Stat(op.Path) {
if !fo.Stat(op.LinkPath) {
return buserr.New(constant.ErrLinkPathNotFound)
}
return fo.LinkFile(op.LinkPath, op.Path, op.IsSymlink)
+4 -4
View File
@@ -22,7 +22,7 @@ func NewIGroupService() IGroupService {
}
func (u *GroupService) List(req dto.GroupSearch) ([]dto.GroupInfo, error) {
groups, err := groupRepo.GetList(commonRepo.WithByType(req.Type))
groups, err := groupRepo.GetList(commonRepo.WithByType(req.Type), commonRepo.WithOrderBy("is_default desc"), commonRepo.WithOrderBy("created_at desc"))
if err != nil {
return nil, constant.ErrRecordNotFound
}
@@ -38,7 +38,7 @@ func (u *GroupService) List(req dto.GroupSearch) ([]dto.GroupInfo, error) {
}
func (u *GroupService) Create(req dto.GroupCreate) error {
group, _ := groupRepo.Get(commonRepo.WithByName(req.Name), commonRepo.WithByName(req.Name))
group, _ := groupRepo.Get(commonRepo.WithByName(req.Name), commonRepo.WithByType(req.Type))
if group.ID != 0 {
return constant.ErrRecordExist
}
@@ -58,7 +58,7 @@ func (u *GroupService) Delete(id uint) error {
}
switch group.Type {
case "website":
websites, _ := websiteRepo.GetBy(commonRepo.WithByGroupID(id))
websites, _ := websiteRepo.GetBy(websiteRepo.WithGroupID(id))
if len(websites) > 0 {
return buserr.New(constant.ErrGroupIsUsed)
}
@@ -73,7 +73,7 @@ func (u *GroupService) Delete(id uint) error {
func (u *GroupService) Update(req dto.GroupUpdate) error {
if req.IsDefault {
if err := groupRepo.CancelDefault(); err != nil {
if err := groupRepo.CancelDefault(req.Type); err != nil {
return err
}
}
+34 -9
View File
@@ -15,6 +15,7 @@ import (
"time"
"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/docker"
@@ -53,8 +54,8 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
return 0, nil, err
}
if len(req.Info) != 0 {
lenth, count := len(list), 0
for count < lenth {
length, count := len(list), 0
for count < length {
hasTag := false
for _, tag := range list[count].RepoTags {
if strings.Contains(tag, req.Info) {
@@ -64,7 +65,7 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
}
if !hasTag {
list = append(list[:count], list[(count+1):]...)
lenth--
length--
} else {
count++
}
@@ -121,8 +122,9 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
if err != nil {
return "", err
}
fileName := "Dockerfile"
if req.From == "edit" {
dir := fmt.Sprintf("%s/docker/build/%s", constant.DataDir, req.Name)
dir := fmt.Sprintf("%s/docker/build/%s", constant.DataDir, strings.ReplaceAll(req.Name, ":", "_"))
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
return "", err
@@ -139,6 +141,9 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
_, _ = write.WriteString(string(req.Dockerfile))
write.Flush()
req.Dockerfile = dir
} else {
fileName = path.Base(req.Dockerfile)
req.Dockerfile = path.Dir(req.Dockerfile)
}
tar, err := archive.TarWithOptions(req.Dockerfile+"/", &archive.TarOptions{})
if err != nil {
@@ -146,7 +151,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
}
opts := types.ImageBuildOptions{
Dockerfile: "Dockerfile",
Dockerfile: fileName,
Tags: []string{req.Name},
Remove: true,
Labels: stringsToMap(req.Tags),
@@ -161,15 +166,29 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
go func() {
defer file.Close()
defer tar.Close()
res, err := client.ImageBuild(context.TODO(), tar, opts)
res, err := client.ImageBuild(context.Background(), tar, opts)
if err != nil {
global.LOG.Errorf("build image %s failed, err: %v", req.Name, err)
_, _ = file.WriteString("image build failed!")
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
global.LOG.Errorf("build image %s failed, err: %v", req.Name, err)
_, _ = file.WriteString(fmt.Sprintf("build image %s failed, err: %v", req.Name, err))
_, _ = file.WriteString("image build failed!")
return
}
if strings.Contains(string(body), "errorDetail") || strings.Contains(string(body), "error:") {
global.LOG.Errorf("build image %s failed", req.Name)
_, _ = file.Write(body)
_, _ = file.WriteString("image build failed!")
return
}
global.LOG.Infof("build image %s successful!", req.Name)
_, _ = io.Copy(file, res.Body)
_, _ = file.Write(body)
_, _ = file.WriteString("image build successful!")
}()
@@ -362,8 +381,14 @@ func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
if err != nil {
return err
}
for _, ids := range req.Names {
if _, err := client.ImageRemove(context.TODO(), ids, types.ImageRemoveOptions{Force: true, PruneChildren: true}); err != nil {
for _, id := range req.Names {
if _, err := client.ImageRemove(context.TODO(), id, types.ImageRemoveOptions{Force: true, PruneChildren: true}); err != nil {
if strings.Contains(err.Error(), "image is being used") {
if strings.Contains(id, "sha256:") {
return buserr.New(constant.ErrObjectInUsed)
}
return buserr.WithDetail(constant.ErrInUsed, id, nil)
}
return err
}
}
+7
View File
@@ -4,12 +4,15 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
)
type LogService struct{}
const logs = "https://resource.fit2cloud.com/installation-log.sh"
type ILogService interface {
CreateLoginLog(operation model.LoginLog) error
PageLoginLog(search dto.SearchLgLogWithPage) (int64, interface{}, error)
@@ -77,3 +80,7 @@ func (u *LogService) CleanLogs(logtype string) error {
}
return logRepo.CleanLogin()
}
func writeLogs(version string) {
_, _ = cmd.Execf("curl -sfL %s | sh -s 1p upgrade %s", logs, version)
}
+8 -5
View File
@@ -66,6 +66,9 @@ func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
if err != nil {
return fmt.Errorf("incorrect snapshot name format of %s", snap)
}
if strings.HasSuffix(snap, ".tar.gz") {
snap = strings.ReplaceAll(snap, ".tar.gz", "")
}
itemSnap := model.Snapshot{
Name: snap,
From: req.From,
@@ -172,17 +175,17 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
return
}
if err := u.handlePanelDatas(snap.ID, fileOp, "snapshot", global.CONF.BaseDir+"/1panel", backupPanelDir, localDir, dockerDataDir); err != nil {
if err := u.handlePanelDatas(snap.ID, fileOp, "snapshot", global.CONF.System.BaseDir+"/1panel", backupPanelDir, localDir, dockerDataDir); err != nil {
updateSnapshotStatus(snap.ID, constant.StatusFailed, err.Error())
return
}
_, _ = cmd.Exec("systemctl restart docker")
snapJson := SnapshotJson{
BaseDir: global.CONF.BaseDir,
BaseDir: global.CONF.System.BaseDir,
DockerDataDir: dockerDataDir,
BackupDataDir: localDir,
PanelDataDir: global.CONF.BaseDir + "/1panel",
PanelDataDir: global.CONF.System.BaseDir + "/1panel",
LiveRestoreEnabled: liveRestoreStatus,
}
if err := u.saveJson(snapJson, rootDir); err != nil {
@@ -287,8 +290,8 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
u.OriginalPath = fmt.Sprintf("%s/original_%s", snapJson.BaseDir, snap.Name)
_ = os.MkdirAll(u.OriginalPath, os.ModePerm)
snapJson.OldBaseDir = global.CONF.BaseDir
snapJson.OldPanelDataDir = global.CONF.BaseDir + "/1panel"
snapJson.OldBaseDir = global.CONF.System.BaseDir
snapJson.OldPanelDataDir = global.CONF.System.BaseDir + "/1panel"
snapJson.OldBackupDataDir = localDir
recoverPanelDir := fmt.Sprintf("%s/%s/1panel", baseDir, snap.Name)
liveRestore := false
+88 -58
View File
@@ -1,18 +1,20 @@
package service
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"runtime"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/files"
)
@@ -20,6 +22,7 @@ type UpgradeService struct{}
type IUpgradeService interface {
Upgrade(req dto.Upgrade) error
LoadNotes(req dto.Upgrade) (string, error)
SearchUpgrade() (*dto.UpgradeInfo, error)
}
@@ -34,36 +37,50 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
return nil, err
}
versionRes, err := http.Get(fmt.Sprintf("%s/%s/latest", global.CONF.System.RepoUrl, global.CONF.System.Mode))
latestVersion, err := u.loadVersion(true, currentVersion.Value)
if err != nil {
global.LOG.Infof("load latest version failed, err: %v", err)
return nil, err
}
defer versionRes.Body.Close()
version, err := ioutil.ReadAll(versionRes.Body)
if err != nil {
if !common.CompareVersion(string(latestVersion), currentVersion.Value) {
return nil, err
}
isNew, err := compareVersion(currentVersion.Value, string(version))
if !isNew || err != nil {
return nil, err
upgrade.LatestVersion = latestVersion
if latestVersion[0:4] == currentVersion.Value[0:4] {
upgrade.NewVersion = ""
} else {
newerVersion, err := u.loadVersion(false, currentVersion.Value)
if err != nil {
global.LOG.Infof("load newer version failed, err: %v", err)
return nil, err
}
if newerVersion == currentVersion.Value {
upgrade.NewVersion = ""
} else {
upgrade.NewVersion = newerVersion
}
}
itemVersion := upgrade.LatestVersion
if upgrade.NewVersion != "" {
itemVersion = upgrade.NewVersion
}
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, itemVersion, itemVersion))
upgrade.NewVersion = string(version)
releaseNotes, err := http.Get(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, upgrade.NewVersion, upgrade.NewVersion))
if err != nil {
return nil, err
return nil, fmt.Errorf("load relase-notes of version %s failed, err: %v", latestVersion, err)
}
defer releaseNotes.Body.Close()
release, err := ioutil.ReadAll(releaseNotes.Body)
if err != nil {
return nil, err
}
upgrade.ReleaseNote = string(release)
upgrade.ReleaseNote = notes
return &upgrade, nil
}
func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) {
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version, req.Version))
if err != nil {
return "", fmt.Errorf("load relase-notes of version %s failed, err: %v", req.Version, err)
}
return notes, nil
}
func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
global.LOG.Info("start to upgrade now...")
fileOp := files.NewFileOp()
@@ -81,7 +98,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
fileName := fmt.Sprintf("1panel-%s-%s-%s.tar.gz", req.Version, "linux", runtime.GOARCH)
_ = settingRepo.Update("SystemStatus", "Upgrading")
go func() {
if err := fileOp.DownloadFile(downloadPath+"/"+fileName, rootDir+"/service.tar.gz"); err != nil {
if err := fileOp.DownloadFile(downloadPath+"/"+fileName, rootDir+"/"+fileName); err != nil {
global.LOG.Errorf("download service file failed, err: %v", err)
_ = settingRepo.Update("SystemStatus", "Free")
return
@@ -90,11 +107,12 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
defer func() {
_ = os.Remove(rootDir)
}()
if err := fileOp.Decompress(rootDir+"/service.tar.gz", rootDir, files.TarGz); err != nil {
if err := handleUnTar(rootDir+"/"+fileName, rootDir); err != nil {
global.LOG.Errorf("decompress file failed, err: %v", err)
_ = settingRepo.Update("SystemStatus", "Free")
return
}
tmpDir := rootDir + "/" + strings.ReplaceAll(fileName, ".tar.gz", "")
if err := u.handleBackup(fileOp, originalDir); err != nil {
global.LOG.Errorf("handle backup original file failed, err: %v", err)
@@ -103,23 +121,31 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
}
global.LOG.Info("backup original data successful, now start to upgrade!")
if err := cpBinary(rootDir+"/1panel", "/usr/local/bin/1panel"); err != nil {
if err := cpBinary(tmpDir+"/1panel", "/usr/local/bin/1panel"); err != nil {
u.handleRollback(fileOp, originalDir, 1)
global.LOG.Errorf("upgrade 1panel failed, err: %v", err)
return
}
if err := cpBinary(fmt.Sprintf("%s/1panel-online-installer-%s/1pctl", rootDir, req.Version), "/usr/local/bin/1pctl"); err != nil {
if err := cpBinary(tmpDir+"/1pctl", "/usr/local/bin/1pctl"); err != nil {
u.handleRollback(fileOp, originalDir, 2)
global.LOG.Errorf("upgrade 1pctl failed, err: %v", err)
return
}
if err := cpBinary(fmt.Sprintf("%s/1panel-online-installer-%s/1panel/conf/1panel.service", rootDir, req.Version), "/etc/systemd/system/1panel.service"); err != nil {
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)
return
}
if err := cpBinary(tmpDir+"/1panel.service", "/etc/systemd/system/1panel.service"); err != nil {
u.handleRollback(fileOp, originalDir, 3)
global.LOG.Errorf("upgrade 1panel.service failed, err: %v", err)
return
}
global.LOG.Info("upgrade successful!")
go writeLogs(req.Version)
_ = settingRepo.Update("SystemVersion", req.Version)
_ = settingRepo.Update("SystemStatus", "Free")
_, _ = cmd.Exec("systemctl daemon-reload && systemctl restart 1panel.service")
@@ -165,45 +191,49 @@ func (u *UpgradeService) handleRollback(fileOp files.FileOp, originalDir string,
if err := cpBinary(originalDir+"/1panel.service", "/etc/systemd/system/1panel.service"); err != nil {
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
}
}
func compareVersion(version, newVersion string) (bool, error) {
if version == newVersion {
return false, nil
func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (string, error) {
path := fmt.Sprintf("%s/%s/latest", global.CONF.System.RepoUrl, global.CONF.System.Mode)
if !isLatest {
path = fmt.Sprintf("%s/%s/latest.current", global.CONF.System.RepoUrl, global.CONF.System.Mode)
}
if len(version) == 0 || len(newVersion) == 0 {
return false, fmt.Errorf("incorrect version or new version entered %v -- %v", version, newVersion)
latestVersionRes, err := http.Get(path)
if err != nil {
return "", err
}
versions := strings.Split(strings.ReplaceAll(version, "v", ""), ".")
if len(versions) != 3 {
return false, fmt.Errorf("incorrect version input %v", version)
defer latestVersionRes.Body.Close()
version, err := ioutil.ReadAll(latestVersionRes.Body)
if err != nil {
return "", err
}
newVersions := strings.Split(strings.ReplaceAll(newVersion, "v", ""), ".")
if len(newVersions) != 3 {
return false, fmt.Errorf("incorrect newVersions input %v", version)
if isLatest {
return string(version), nil
}
version1, _ := strconv.Atoi(versions[0])
newVersion1, _ := strconv.Atoi(newVersions[0])
if newVersion1 > version1 {
return true, nil
} else if newVersion1 == version1 {
version2, _ := strconv.Atoi(versions[1])
newVersion2, _ := strconv.Atoi(newVersions[1])
if newVersion2 > version2 {
return true, nil
} else if newVersion2 == version2 {
version3, _ := strconv.Atoi(versions[2])
newVersion3, _ := strconv.Atoi(newVersions[2])
if newVersion3 > version3 {
return true, nil
} else {
return false, nil
}
} else {
return false, nil
}
} else {
return false, nil
versionMap := make(map[string]string)
if err := json.Unmarshal(version, &versionMap); err != nil {
return "", fmt.Errorf("load version map failed, err: %v", err)
}
if len(currentVersion) < 4 {
return "", fmt.Errorf("current version is error format: %s", currentVersion)
}
if version, ok := versionMap[currentVersion[0:4]]; ok {
return version, nil
}
return "", errors.New("load version failed in latest.current")
}
func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
releaseNotes, err := http.Get(path)
if err != nil {
return "", err
}
defer releaseNotes.Body.Close()
release, err := ioutil.ReadAll(releaseNotes.Body)
if err != nil {
return "", err
}
return string(release), nil
}
-61
View File
@@ -1,61 +0,0 @@
package service
import (
"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"
)
type WebsiteGroupService struct {
}
func (w WebsiteGroupService) CreateGroup(create request.WebsiteGroupCreate) error {
groups, _ := websiteGroupRepo.GetBy(commonRepo.WithByName(create.Name))
if len(groups) > 0 {
return buserr.New(constant.ErrNameIsExist)
}
return websiteGroupRepo.Create(&model.WebsiteGroup{
Name: create.Name,
})
}
func (w WebsiteGroupService) GetGroups() ([]model.WebsiteGroup, error) {
return websiteGroupRepo.GetBy()
}
func (w WebsiteGroupService) UpdateGroup(update request.WebsiteGroupUpdate) error {
if update.Default {
if err := websiteGroupRepo.CancelDefault(); err != nil {
return err
}
return websiteGroupRepo.Save(&model.WebsiteGroup{
BaseModel: model.BaseModel{
ID: update.ID,
},
Name: update.Name,
Default: true,
})
} else {
exists, _ := websiteGroupRepo.GetBy(commonRepo.WithByName(update.Name))
for _, exist := range exists {
if exist.ID != update.ID {
return buserr.New(constant.ErrNameIsExist)
}
}
return websiteGroupRepo.Save(&model.WebsiteGroup{
BaseModel: model.BaseModel{
ID: update.ID,
},
Name: update.Name,
})
}
}
func (w WebsiteGroupService) DeleteGroup(id uint) error {
websites, _ := websiteRepo.GetBy(websiteRepo.WithGroupID(id))
if len(websites) > 0 {
return buserr.New(constant.ErrGroupIsUsed)
}
return websiteGroupRepo.DeleteBy(commonRepo.WithByID(id))
}
+15
View File
@@ -27,6 +27,7 @@ type IWebsiteSSLService interface {
GetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, error)
GetWebsiteSSL(websiteId uint) (response.WebsiteSSLDTO, error)
Delete(id uint) error
Update(update request.WebsiteSSLUpdate) error
}
func NewIWebsiteSSLService() IWebsiteSSLService {
@@ -99,6 +100,10 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
if err := client.UseHTTP(path.Join(constant.AppInstallDir, constant.AppOpenresty, appInstall.Name, "root")); err != nil {
return res, err
}
case constant.DnsManual:
if err := client.UseManualDns(); err != nil {
return res, err
}
}
domains := []string{create.PrimaryDomain}
@@ -128,6 +133,7 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
websiteSSL.StartDate = cert.NotBefore
websiteSSL.Type = cert.Issuer.CommonName
websiteSSL.Organization = cert.Issuer.Organization[0]
websiteSSL.AutoRenew = create.AutoRenew
if err := websiteSSLRepo.Create(context.TODO(), &websiteSSL); err != nil {
return res, err
@@ -254,3 +260,12 @@ func (w WebsiteSSLService) Delete(id uint) error {
}
return websiteSSLRepo.DeleteBy(commonRepo.WithByID(id))
}
func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
websiteSSL, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(update.ID))
if err != nil {
return err
}
websiteSSL.AutoRenew = update.AutoRenew
return websiteSSLRepo.Save(websiteSSL)
}
-1
View File
@@ -1,7 +1,6 @@
package configs
type ServerConfig struct {
BaseDir string `mapstructure:"base_dir"`
System System `mapstructure:"system"`
LogConfig LogConfig `mapstructure:"log"`
}
+1
View File
@@ -13,5 +13,6 @@ type System struct {
BaseDir string `mapstructure:"base_dir"`
Mode string `mapstructure:"mode"`
RepoUrl string `mapstructure:"repo_url"`
Version string `mapstructure:"version"`
IsDemo bool `mapstructure:"is_demo"`
}
+17 -9
View File
@@ -50,15 +50,16 @@ var (
// app
var (
ErrPortInUsed = "ErrPortInUsed"
ErrAppLimit = "ErrAppLimit"
ErrAppRequired = "ErrAppRequired"
ErrFileCanNotRead = "ErrFileCanNotRead"
ErrFileToLarge = "ErrFileToLarge"
ErrNotInstall = "ErrNotInstall"
ErrPortInOtherApp = "ErrPortInOtherApp"
ErrDbUserNotValid = "ErrDbUserNotValid"
ErrUpdateBuWebsite = "ErrUpdateBuWebsite"
ErrPortInUsed = "ErrPortInUsed"
ErrAppLimit = "ErrAppLimit"
ErrAppRequired = "ErrAppRequired"
ErrFileCanNotRead = "ErrFileCanNotRead"
ErrFileToLarge = "ErrFileToLarge"
ErrNotInstall = "ErrNotInstall"
ErrPortInOtherApp = "ErrPortInOtherApp"
ErrDbUserNotValid = "ErrDbUserNotValid"
ErrUpdateBuWebsite = "ErrUpdateBuWebsite"
Err1PanelNetworkFailed = "Err1PanelNetworkFailed"
)
//website
@@ -83,6 +84,7 @@ var (
ErrMovePathFailed = "ErrMovePathFailed"
ErrLinkPathNotFound = "ErrLinkPathNotFound"
ErrFileIsExit = "ErrFileIsExit"
ErrFileUpload = "ErrFileUpload"
)
//mysql
@@ -95,3 +97,9 @@ var (
var (
ErrTypeOfRedis = "ErrTypeOfRedis"
)
//container
var (
ErrInUsed = "ErrInUsed"
ErrObjectInUsed = "ErrObjectInUsed"
)
+1 -1
View File
@@ -6,7 +6,7 @@ const (
AuthMethodJWT = "jwt"
JWTHeaderName = "Authorization"
JWTBufferTime = 86400
JWTBufferTime = 3600
JWTIssuer = "1Panel"
PasswordExpiredName = "expired"
+3 -2
View File
@@ -4,8 +4,9 @@ const (
WebRunning = "Running"
WebStopped = "Stopped"
DateLayout = "2006-01-02"
DefaultDate = "1970-01-01"
DateLayout = "2006-01-02"
DateTimeLayout = "2006-01-02 15:04:05"
DefaultDate = "1970-01-01"
ProtocolHTTP = "HTTP"
ProtocolHTTPS = "HTTPS"
+8 -3
View File
@@ -4,6 +4,7 @@ import (
"time"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/cron/job"
@@ -43,9 +44,13 @@ func Run() {
}).Error; err != nil {
global.LOG.Errorf("start my cronjob failed, err: %v", err)
}
for _, cronjob := range cronJobs {
if err := service.ServiceGroupApp.StartJob(&cronjob); err != nil {
global.LOG.Errorf("start %s job %s failed, err: %v", cronjob.Type, cronjob.Name, err)
for i := 0; i < len(cronJobs); i++ {
entryID, err := service.ServiceGroupApp.StartJob(&cronJobs[i])
if err != nil {
global.LOG.Errorf("start %s job %s failed, err: %v", &cronJobs[i].Type, &cronJobs[i].Name, err)
}
if err := repo.NewICronjobRepo().Update(cronJobs[i].ID, map[string]interface{}{"entry_id": entryID}); err != nil {
global.LOG.Errorf("update cronjob %s %s failed, err: %v", cronJobs[i].Type, cronJobs[i].Name, err)
}
}
}
+3
View File
@@ -118,6 +118,9 @@ func loadNetIO() {
netStatAll2, _ := net.IOCounters(false)
for _, net2 := range netStatAll2 {
for _, net1 := range netStatAll {
if net1.BytesSent == 0 || net1.BytesRecv == 0 {
continue
}
if net2.Name == net1.Name {
var itemNet model.MonitorNetwork
itemNet.Name = net1.Name
+5 -4
View File
@@ -3,6 +3,7 @@ package job
import (
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"time"
)
@@ -19,18 +20,18 @@ func (ssl *ssl) Run() {
sslService := service.NewIWebsiteSSLService()
sslList, _ := sslRepo.List()
global.LOG.Info("ssl renew cron job start...")
now := time.Now()
now := time.Now().Add(10 * time.Second)
for _, s := range sslList {
if !s.AutoRenew || s.Provider == "manual" || s.Provider == "dnsManual" {
continue
}
sum := s.ExpireDate.Sub(now)
if sum.Hours() < 168 {
expireDate, _ := time.ParseInLocation(constant.DateTimeLayout, s.ExpireDate.String(), time.Now().Location())
sum := expireDate.Sub(now)
if sum.Hours() < 720 {
if err := sslService.Renew(s.ID); err != nil {
global.LOG.Errorf("renew doamin [%s] ssl failed err:%s", s.PrimaryDomain, err.Error())
}
}
}
global.LOG.Info("ssl renew cron job end...")
}
+3 -2
View File
@@ -20,7 +20,7 @@ func NewWebsiteJob() *website {
func (w *website) Run() {
websites, _ := repo.NewIWebsiteRepo().List()
global.LOG.Info("website cron job start...")
now := time.Now()
now := time.Now().Add(10 * time.Second)
if len(websites) > 0 {
neverExpireDate, _ := time.Parse(constant.DateLayout, constant.DefaultDate)
var wg sync.WaitGroup
@@ -28,7 +28,8 @@ func (w *website) Run() {
if site.Status != constant.WebRunning || neverExpireDate.Equal(site.ExpireDate) {
continue
}
if site.ExpireDate.Before(now) {
expireDate, _ := time.ParseInLocation(constant.DateTimeLayout, site.ExpireDate.String(), time.Now().Location())
if expireDate.Before(now) {
wg.Add(1)
go func(ws model.Website) {
stopWebsite(ws.ID, &wg)
+7 -7
View File
@@ -11,35 +11,35 @@ import (
"gopkg.in/yaml.v3"
)
func GetMsgWithMap(msg string, maps map[string]interface{}) string {
func GetMsgWithMap(key string, maps map[string]interface{}) string {
content := ""
if maps == nil {
content = ginI18n.MustGetMessage(&i18n.LocalizeConfig{
MessageID: msg,
MessageID: key,
})
} else {
content = ginI18n.MustGetMessage(&i18n.LocalizeConfig{
MessageID: msg,
MessageID: key,
TemplateData: maps,
})
}
content = strings.ReplaceAll(content, ": <no value>", "")
if content == "" {
return msg
return key
} else {
return content
}
}
func GetErrMsg(msg string, maps map[string]interface{}) string {
func GetErrMsg(key string, maps map[string]interface{}) string {
content := ""
if maps == nil {
content = ginI18n.MustGetMessage(&i18n.LocalizeConfig{
MessageID: msg,
MessageID: key,
})
} else {
content = ginI18n.MustGetMessage(&i18n.LocalizeConfig{
MessageID: msg,
MessageID: key,
TemplateData: maps,
})
}
+7 -1
View File
@@ -26,6 +26,7 @@ ErrPortInOtherApp: "{{ .port }} port already in use by {{ .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!'
Err1PanelNetworkFailed: 'Default container network creation failed! {{ .detail }}'
#file
ErrFileCanNotRead: "File can not read"
@@ -34,6 +35,7 @@ ErrPathNotFound: "Path is not found"
ErrMovePathFailed: "The target path cannot contain the original path!"
ErrLinkPathNotFound: "Target path does not exist!"
ErrFileIsExit: "File already exists!"
ErrFileUpload: "Failed to upload file"
#website
ErrDomainIsExist: "Domain is already exist"
@@ -52,4 +54,8 @@ ErrUserIsExist: "The current user already exists. Please enter a new user"
ErrDatabaseIsExist: "The current database already exists. Please enter a new database"
#redis
ErrTypeOfRedis: "The recovery file type does not match the current persistence mode. Modify the file type and try again"
ErrTypeOfRedis: "The recovery file type does not match the current persistence mode. Modify the file type and try again"
#container
ErrInUsed: "{{ .detail }} is in use and cannot be deleted"
ErrObjectInUsed: "This object is in use and cannot be deleted"
+7 -1
View File
@@ -26,6 +26,7 @@ ErrPortInOtherApp: "{{ .port }} 端口已被 {{ .apps }}占用!"
ErrDbUserNotValid: "存量数据库,用户名密码不匹配!"
ErrDockerComposeNotValid: "docker-compose 文件格式错误"
ErrUpdateBuWebsite: '应用更新成功,但是网站配置文件修改失败,请检查配置!'
Err1PanelNetworkFailed: '默认容器网络创建失败!{{ .detail }}'
#file
ErrFileCanNotRead: "此文件不支持预览"
@@ -34,6 +35,7 @@ ErrPathNotFound: "目录不存在"
ErrMovePathFailed: "目标路径不能包含原路径!"
ErrLinkPathNotFound: "目标路径不存在!"
ErrFileIsExit: "文件已存在!"
ErrFileUpload: "上传文件失败"
#website
ErrDomainIsExist: "域名已存在"
@@ -52,4 +54,8 @@ ErrUserIsExist: "当前用户已存在,请重新输入"
ErrDatabaseIsExist: "当前数据库已存在,请重新输入"
#redis
ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后重试"
ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后重试"
#container
ErrInUsed: "{{ .detail }} 正被使用,无法删除"
ErrObjectInUsed: "该对象正被使用,无法删除"
+2 -17
View File
@@ -1,12 +1,11 @@
package app
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"path"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/docker"
"github.com/1Panel-dev/1Panel/backend/utils/files"
)
@@ -23,7 +22,7 @@ func Init() {
createDir(fileOp, dir)
}
createDefaultDockerNetwork()
_ = docker.CreateDefaultDockerNetwork()
}
func createDir(fileOp files.FileOp, dirPath string) {
@@ -31,17 +30,3 @@ func createDir(fileOp files.FileOp, dirPath string) {
_ = fileOp.CreateDir(dirPath, 0755)
}
}
func createDefaultDockerNetwork() {
cli, err := docker.NewClient()
if err != nil {
fmt.Println("init docker client error", err.Error())
return
}
if !cli.NetworkExist("1panel-network") {
if err := cli.CreateNetwork("1panel-network"); err != nil {
fmt.Println("init docker client error", err.Error())
return
}
}
}
+14 -4
View File
@@ -1,25 +1,35 @@
package business
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/global"
)
func Init() {
syncApp()
syncInstalledApp()
}
func syncApp() {
setting, err := service.NewISettingService().GetSettingInfo()
if err != nil {
global.LOG.Errorf("sync app error: %s", err.Error())
return
}
if setting.AppStoreVersion != "" {
fmt.Println(setting.AppStoreVersion)
global.LOG.Info("do not sync")
global.LOG.Info("no need to sync")
return
}
global.LOG.Info("sync app start...")
if err := service.NewIAppService().SyncAppList(); err != nil {
global.LOG.Errorf("sync app error: %s", err.Error())
return
}
global.LOG.Info("sync app successful")
}
func syncInstalledApp() {
if err := service.NewIAppInstalledService().SyncAll(true); err != nil {
global.LOG.Errorf("sync instaled app error: %s", err.Error())
}
global.LOG.Info("sync app success")
}
+12 -1
View File
@@ -2,9 +2,11 @@ package db
import (
"fmt"
"os"
"time"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"os"
"github.com/1Panel-dev/1Panel/backend/global"
)
@@ -28,6 +30,15 @@ func Init() {
if err != nil {
panic(err)
}
_ = db.Exec("PRAGMA journal_mode = WAL;")
sqlDB, dbError := db.DB()
if dbError != nil {
panic(err)
}
sqlDB.SetConnMaxIdleTime(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
global.DB = db
global.LOG.Info("init db successfully")
}
+1
View File
@@ -20,6 +20,7 @@ func Init() {
migrations.AddTableWebsite,
migrations.AddTableDatabaseMysql,
migrations.AddTableSnap,
migrations.AddDefaultGroup,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)
+23 -10
View File
@@ -136,11 +136,10 @@ var AddTableSetting = &gormigrate.Migration{
if err := tx.Create(&model.Setting{Key: "DingVars", Value: ""}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "SystemVersion", Value: "v1.0.0"}).Error; err != nil {
if err := tx.Create(&model.Setting{Key: "SystemVersion", Value: global.CONF.System.Version}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "SystemStatus", Value: "Free"}).Error; err != nil {
tx.Rollback()
return err
}
if err := tx.Create(&model.Setting{Key: "AppStoreVersion", Value: ""}).Error; err != nil {
@@ -211,14 +210,7 @@ var AddTableDatabaseMysql = &gormigrate.Migration{
var AddTableWebsite = &gormigrate.Migration{
ID: "20201009-add-table-website",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Website{}, &model.WebsiteDomain{}, &model.WebsiteGroup{}, &model.WebsiteDnsAccount{}, &model.WebsiteSSL{}, &model.WebsiteAcmeAccount{}); err != nil {
return err
}
item := &model.WebsiteGroup{
Name: "默认",
Default: true,
}
if err := tx.Create(item).Error; err != nil {
if err := tx.AutoMigrate(&model.Website{}, &model.WebsiteDomain{}, &model.WebsiteDnsAccount{}, &model.WebsiteSSL{}, &model.WebsiteAcmeAccount{}); err != nil {
return err
}
return nil
@@ -234,3 +226,24 @@ var AddTableSnap = &gormigrate.Migration{
return nil
},
}
var AddDefaultGroup = &gormigrate.Migration{
ID: "2023022-change-default-group",
Migrate: func(tx *gorm.DB) error {
defaultGroup := &model.Group{
Name: "默认",
IsDefault: true,
Type: "website",
}
if err := tx.Create(defaultGroup).Error; err != nil {
return err
}
if err := tx.Model(&model.Group{}).Where("name = ? AND type = ?", "default", "host").Update("name", "默认").Error; err != nil {
return err
}
if err := tx.Model(&model.Website{}).Where("1 = 1").Update("website_group_id", defaultGroup.ID).Error; err != nil {
return err
}
return tx.Migrator().DropTable("website_groups")
},
}
+4
View File
@@ -63,6 +63,10 @@ func Routers() *gin.Engine {
systemRouter := rou.RouterGroupApp
swaggerRouter := Router.Group("1panel")
docs.SwaggerInfo.BasePath = "/api/v1"
swaggerRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
PublicGroup := Router.Group("")
{
PublicGroup.GET("/health", func(c *gin.Context) {
+22 -19
View File
@@ -20,6 +20,7 @@ func Init() {
baseDir := "/opt"
port := "9999"
mode := ""
version := ""
fileOp := files.NewFileOp()
v := viper.NewWithOptions()
v.SetConfigType("yaml")
@@ -38,23 +39,9 @@ func Init() {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
} else {
stdout, err := cmd.Exec("grep '^BASE_DIR=' /usr/bin/1pctl | cut -d'=' -f2")
if err != nil {
panic(err)
}
baseDir = strings.ReplaceAll(stdout, "\n", "")
if len(baseDir) == 0 {
panic("error `BASE_DIR` find in /usr/bin/1pctl")
}
stdoutPort, err := cmd.Exec("grep '^PANEL_PORT=' /usr/bin/1pctl | cut -d'=' -f2")
if err != nil {
panic(err)
}
port = strings.ReplaceAll(stdoutPort, "\n", "")
if len(port) == 0 {
panic("error `PANEL_PORT` find in /usr/bin/1pctl")
}
baseDir = loadParams("BASE_DIR")
port = loadParams("PANEL_PORT")
version = loadParams("ORIGINAL_INSTALLED_VERSION")
if strings.HasSuffix(baseDir, "/") {
baseDir = baseDir[:strings.LastIndex(baseDir, "/")]
@@ -79,16 +66,32 @@ func Init() {
if mode == "dev" && fileOp.Stat("/opt/1panel/conf/app.yaml") && serverConfig.System.Port != "" {
port = serverConfig.System.Port
}
if mode == "dev" && fileOp.Stat("/opt/1panel/conf/app.yaml") && serverConfig.System.Version != "" {
version = serverConfig.System.Version
}
global.CONF = serverConfig
global.CONF.BaseDir = baseDir
global.CONF.System.BaseDir = baseDir
global.CONF.System.IsDemo = v.GetBool("system.is_demo")
global.CONF.System.DataDir = global.CONF.BaseDir + "/1panel"
global.CONF.System.DataDir = global.CONF.System.BaseDir + "/1panel"
global.CONF.System.Cache = global.CONF.System.DataDir + "/cache"
global.CONF.System.Backup = global.CONF.System.DataDir + "/backup"
global.CONF.System.DbPath = global.CONF.System.DataDir + "/db"
global.CONF.System.LogPath = global.CONF.System.DataDir + "/log"
global.CONF.System.TmpDir = global.CONF.System.DataDir + "/tmp"
global.CONF.System.Port = port
global.CONF.System.Version = version
global.Viper = v
}
func loadParams(param string) string {
stdout, err := cmd.Execf("grep '^%s=' /usr/bin/1pctl | cut -d'=' -f2", param)
if err != nil {
panic(err)
}
baseDir := strings.ReplaceAll(stdout, "\n", "")
if len(baseDir) == 0 {
panic(fmt.Sprintf("error `%s` find in /usr/bin/1pctl", param))
}
return baseDir
}
+3 -4
View File
@@ -1,9 +1,6 @@
package log
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/files"
"io/ioutil"
"log"
"os"
@@ -14,6 +11,9 @@ import (
"sync/atomic"
"time"
"unsafe"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/files"
)
type Writer struct {
@@ -261,7 +261,6 @@ func (w *Writer) CompressFile(logFile string) error {
comFileName := path.Base(logFile) + ".gz"
filePath := path.Dir(logFile)
fmt.Println(path.Dir(logFile))
if err := op.Compress([]string{logFile}, filePath, comFileName, files.Gz); err != nil {
return err
}
-16
View File
@@ -1,22 +1,15 @@
package middleware
import (
"strconv"
"time"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
jwtUtils "github.com/1Panel-dev/1Panel/backend/utils/jwt"
"github.com/golang-jwt/jwt/v4"
"github.com/gin-gonic/gin"
)
func JwtAuth() gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("authMethod", "")
token := c.Request.Header.Get(constant.JWTHeaderName)
if token == "" {
c.Next()
@@ -28,15 +21,6 @@ func JwtAuth() gin.HandlerFunc {
helper.ErrorWithDetail(c, constant.CodeErrUnauthorized, constant.ErrTypeInternalServer, err)
return
}
if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime {
settingRepo := repo.NewISettingRepo()
setting, err := settingRepo.Get(settingRepo.WithByKey("SessionTimeout"))
if err != nil {
global.LOG.Errorf("create operation record failed, err: %v", err)
}
lifeTime, _ := strconv.Atoi(setting.Value)
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(lifeTime)))
}
c.Set("claims", claims)
c.Set("authMethod", constant.AuthMethodJWT)
c.Next()
-1
View File
@@ -96,7 +96,6 @@ func OperationLog() gin.HandlerFunc {
var names []string
if funcs.IsList {
sql := fmt.Sprintf("SELECT %s FROM %s where %s in (?);", funcs.OutputColume, funcs.DB, funcs.InputColume)
fmt.Println(value)
_ = global.DB.Raw(sql, value).Scan(&names)
} else {
_ = global.DB.Raw(fmt.Sprintf("select %s from %s where %s = ?;", funcs.OutputColume, funcs.DB, funcs.InputColume), value).Scan(&names)
+1
View File
@@ -14,6 +14,7 @@ func SessionAuth() gin.HandlerFunc {
return func(c *gin.Context) {
if method, exist := c.Get("authMethod"); exist && method == constant.AuthMethodJWT {
c.Next()
return
}
sId, err := c.Cookie(constant.SessionName)
if err != nil {
+1
View File
@@ -16,6 +16,7 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
baseApi := v1.ApiGroupApp.BaseApi
{
appRouter.POST("/sync", baseApi.SyncApp)
appRouter.GET("/checkupdate", baseApi.GetAppListUpdate)
appRouter.POST("/search", baseApi.SearchApp)
appRouter.GET("/:key", baseApi.GetApp)
appRouter.GET("/detail/:appId/:version", baseApi.GetAppDetail)
+1
View File
@@ -17,5 +17,6 @@ func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) {
baseRouter.GET("/status", baseApi.CheckIsFirstLogin)
baseRouter.POST("/init", baseApi.InitUserInfo)
baseRouter.POST("/logout", baseApi.LogOut)
baseRouter.GET("/demo", baseApi.CheckIsDemo)
}
}
+1
View File
@@ -33,6 +33,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
baRouter.POST("/compose/search", baseApi.SearchCompose)
baRouter.POST("/compose", baseApi.CreateCompose)
baRouter.POST("/compose/test", baseApi.TestCompose)
baRouter.POST("/compose/operate", baseApi.OperatorCompose)
baRouter.POST("/compose/update", baseApi.ComposeUpdate)
+3 -1
View File
@@ -26,11 +26,13 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) {
fileRouter.POST("/content", baseApi.GetContent)
fileRouter.POST("/save", baseApi.SaveContent)
fileRouter.POST("/check", baseApi.CheckFile)
fileRouter.POST("/upload", baseApi.UploadChunkFiles)
fileRouter.POST("/upload", baseApi.UploadFiles)
fileRouter.POST("/chunkupload", baseApi.UploadChunkFiles)
fileRouter.POST("/rename", baseApi.ChangeFileName)
fileRouter.POST("/wget", baseApi.WgetFile)
fileRouter.POST("/move", baseApi.MoveFile)
fileRouter.POST("/download", baseApi.Download)
fileRouter.POST("/download/bypath", baseApi.DownloadFile)
fileRouter.POST("/size", baseApi.Size)
fileRouter.GET("/ws", baseApi.Ws)
fileRouter.GET("/keys", baseApi.Keys)
@@ -10,14 +10,14 @@ type WebsiteGroupRouter struct {
}
func (a *WebsiteGroupRouter) InitWebsiteGroupRouter(Router *gin.RouterGroup) {
groupRouter := Router.Group("websites/groups")
groupRouter := Router.Group("groups")
groupRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.PasswordExpired())
baseApi := v1.ApiGroupApp.BaseApi
{
groupRouter.GET("", baseApi.GetWebGroups)
groupRouter.POST("", baseApi.CreateWebGroup)
groupRouter.POST("/update", baseApi.UpdateWebGroup)
groupRouter.POST("/del", baseApi.DeleteWebGroup)
groupRouter.POST("", baseApi.CreateGroup)
groupRouter.POST("/del", baseApi.DeleteGroup)
groupRouter.POST("/update", baseApi.UpdateGroup)
groupRouter.POST("/search", baseApi.ListGroup)
}
}
-5
View File
@@ -26,11 +26,6 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
hostRouter.POST("/test/byid/:id", baseApi.TestByID)
hostRouter.GET(":id", baseApi.GetHostInfo)
hostRouter.POST("/group", baseApi.CreateGroup)
hostRouter.POST("/group/del", baseApi.DeleteGroup)
hostRouter.POST("/group/update", baseApi.UpdateGroup)
hostRouter.POST("/group/search", baseApi.ListGroup)
hostRouter.GET("/command", baseApi.ListCommand)
hostRouter.POST("/command", baseApi.CreateCommand)
hostRouter.POST("/command/del", baseApi.DeleteCommand)
+2 -1
View File
@@ -19,8 +19,8 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
baseApi := v1.ApiGroupApp.BaseApi
{
router.POST("/search", baseApi.GetSettingInfo)
router.POST("/expired/handle", baseApi.HandlePasswordExpired)
settingRouter.GET("/search/available", baseApi.GetSystemAvailable)
settingRouter.POST("/expired/handle", baseApi.HandlePasswordExpired)
settingRouter.POST("/update", baseApi.UpdateSetting)
settingRouter.POST("/port/update", baseApi.UpdatePort)
settingRouter.POST("/password/update", baseApi.UpdatePassword)
@@ -51,6 +51,7 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
settingRouter.POST("/backup/record/del", baseApi.DeleteBackupRecord)
settingRouter.POST("/upgrade", baseApi.Upgrade)
settingRouter.POST("/upgrade/notes", baseApi.GetNotesByVersion)
settingRouter.GET("/upgrade", baseApi.GetUpgradeInfo)
settingRouter.GET("/basedir", baseApi.LoadBaseDir)
}

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