Compare commits

...
87 Commits
Author SHA1 Message Date
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
179 changed files with 2346 additions and 1261 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:
push:
branches:
- 'pr@**'
- 'repr@**'
name: 针对特定分支名自动创建 PR
jobs:
generic_handler:
name: 自动创建 PR
runs-on: ubuntu-latest
steps:
- name: Create pull request
uses: jumpserver/action-generic-handler@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUBTOKEN }}
+16
View File
@@ -0,0 +1,16 @@
name: Issue Close Check
on:
issues:
types: [closed]
jobs:
issue-close-remove-labels:
runs-on: ubuntu-latest
steps:
- name: Remove labels
uses: actions-cool/issues-helper@v2
if: ${{ !github.event.issue.pull_request }}
with:
actions: 'remove-labels'
labels: '状态:待处理'
+38
View File
@@ -0,0 +1,38 @@
on:
issue_comment:
types: [created]
name: Add issues workflow labels
jobs:
add-label-if-is-author:
runs-on: ubuntu-latest
if: ${{ (github.event.issue.user.id == github.event.comment.user.id) && (!github.event.issue.pull_request) }}
steps:
- name: Add require handle label
uses: actions-cool/issues-helper@v2
with:
actions: 'add-labels'
labels: '状态:待处理'
- name: Remove require reply label
uses: actions-cool/issues-helper@v2
with:
actions: 'remove-labels'
labels: '状态:待用户反馈'
add-label-if-not-author:
runs-on: ubuntu-latest
if: ${{ (github.event.issue.user.id != github.event.comment.user.id) && (!github.event.issue.pull_request) && (github.event.issue.state == 'open') }}
steps:
- name: Add require replay label
uses: actions-cool/issues-helper@v2
with:
actions: 'add-labels'
labels: '状态:待用户反馈'
- name: Remove require handle label
uses: actions-cool/issues-helper@v2
with:
actions: 'remove-labels'
labels: '状态:待处理'
+16
View File
@@ -0,0 +1,16 @@
name: Issue Open Check
on:
issues:
types: [opened]
jobs:
issue-open-add-labels:
runs-on: ubuntu-latest
steps:
- name: Add labels
uses: actions-cool/issues-helper@v2
if: ${{ !github.event.issue.pull_request }}
with:
actions: 'add-labels'
labels: '状态:待处理'
+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 }}
-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 -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
+8 -4
View File
@@ -11,8 +11,8 @@
1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。1Panel 的功能和优势包括:
- **快速建站**:深度集成 Wordpress 和 Halo,域名绑定、SSL 证书配置等一键搞定;
- **高效管理**:通过 Web 端轻松管理 Linux 服务器,包括主机监控、文件管理、数据库管理、容器管理及常用应用软件管理;
- **快速建站**:深度集成 Wordpress 和 [Halo](https://github.com/halo-dev/halo/),域名绑定、SSL 证书配置等一键搞定;
- **高效管理**:通过 Web 端轻松管理 Linux 服务器,包括应用管理、主机监控、文件管理、数据库管理、容器管理等;
- **安全可靠**:最小漏洞暴露面,提供防火墙和安全审计等功能;
- **一键备份**:支持一键备份和恢复,备份数据云端存储,永不丢失。
@@ -30,7 +30,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 +39,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)
## 社区
@@ -56,6 +56,10 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
- 邮箱: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.
+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)
}
+9
View File
@@ -167,6 +167,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
+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
+129 -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 request.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,104 @@ func (b *BaseApi) LoadFromFile(c *gin.Context) {
helper.SuccessWithData(c, string(content))
}
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
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]
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"上传文件 [path]","formatEN":"Upload file [path]"}
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 {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrAppDelete, 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
+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
}
+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"`
+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"`
+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
@@ -34,6 +34,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 {
+7 -9
View File
@@ -275,16 +275,14 @@ func (a AppInstallService) SyncAll() error {
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 {
continue
}
}()
if err := syncById(i.ID); err != nil {
global.LOG.Errorf("sync install app[%s] error,mgs: %s", i.Name, err.Error())
}
}
return nil
}
+7 -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
}
+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
+14
View File
@@ -12,8 +12,10 @@ 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"
@@ -96,6 +98,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 +110,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,6 +143,13 @@ 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
+10 -4
View File
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"os"
"path"
"sort"
"strings"
"time"
@@ -152,11 +153,16 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
req.Path = path
}
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))
}
if req.From == "path" {
req.Name = path.Base(strings.ReplaceAll(req.Path, "/docker-compose.yml", ""))
}
if stdout, err := compose.Up(req.Path); err != nil {
_, _ = compose.Down(req.Path)
return errors.New(stdout)
}
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
return nil
}
@@ -170,7 +176,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
+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
}
+40 -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,12 +184,15 @@ 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["script"] = req.Script
upMap["spec_type"] = req.SpecType
@@ -254,15 +216,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 {
+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)
}
+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)
+27 -4
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"
@@ -122,7 +123,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
return "", err
}
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 +140,8 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
_, _ = write.WriteString(string(req.Dockerfile))
write.Flush()
req.Dockerfile = dir
} else {
req.Dockerfile = strings.ReplaceAll(req.Dockerfile, "/Dockerfile", "")
}
tar, err := archive.TarWithOptions(req.Dockerfile+"/", &archive.TarOptions{})
if err != nil {
@@ -161,13 +164,27 @@ 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), "error") && strings.Contains(string(body), "failed:") {
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.WriteString("image build successful!")
@@ -362,8 +379,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)
}
+5 -5
View File
@@ -172,17 +172,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 +287,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
+17 -48
View File
@@ -6,13 +6,13 @@ import (
"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"
)
@@ -43,8 +43,8 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
if err != nil {
return nil, err
}
isNew, err := compareVersion(currentVersion.Value, string(version))
if !isNew || err != nil {
isNew := common.CompareVersion(string(version), currentVersion.Value)
if !isNew {
return nil, err
}
@@ -81,7 +81,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 +90,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 +104,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")
@@ -167,43 +176,3 @@ func (u *UpgradeService) handleRollback(fileOp files.FileOp, originalDir string,
}
}
func compareVersion(version, newVersion string) (bool, error) {
if version == newVersion {
return false, nil
}
if len(version) == 0 || len(newVersion) == 0 {
return false, fmt.Errorf("incorrect version or new version entered %v -- %v", version, newVersion)
}
versions := strings.Split(strings.ReplaceAll(version, "v", ""), ".")
if len(versions) != 3 {
return false, fmt.Errorf("incorrect version input %v", version)
}
newVersions := strings.Split(strings.ReplaceAll(newVersion, "v", ""), ".")
if len(newVersions) != 3 {
return false, fmt.Errorf("incorrect newVersions input %v", version)
}
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
}
}
+4
View File
@@ -99,6 +99,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}
-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"`
}
+6
View File
@@ -95,3 +95,9 @@ var (
var (
ErrTypeOfRedis = "ErrTypeOfRedis"
)
//container
var (
ErrInUsed = "ErrInUsed"
ErrObjectInUsed = "ErrObjectInUsed"
)
+6 -1
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"
@@ -44,8 +45,12 @@ func Run() {
global.LOG.Errorf("start my cronjob failed, err: %v", err)
}
for _, cronjob := range cronJobs {
if err := service.ServiceGroupApp.StartJob(&cronjob); err != nil {
entryID, err := service.ServiceGroupApp.StartJob(&cronjob)
if err != nil {
global.LOG.Errorf("start %s job %s failed, err: %v", cronjob.Type, cronjob.Name, err)
}
if err := repo.NewICronjobRepo().Update(cronjob.ID, map[string]interface{}{"entry_id": entryID}); err != nil {
global.LOG.Errorf("update cronjob %s %s failed, err: %v", cronjob.Type, cronjob.Name, err)
}
}
}
+5 -1
View File
@@ -52,4 +52,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"
+5 -1
View File
@@ -52,4 +52,8 @@ ErrUserIsExist: "当前用户已存在,请重新输入"
ErrDatabaseIsExist: "当前数据库已存在,请重新输入"
#redis
ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后重试"
ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后重试"
#container
ErrInUsed: "{{ .detail }} 正被使用,无法删除"
ErrObjectInUsed: "该对象正被使用,无法删除"
+2 -3
View File
@@ -1,7 +1,6 @@
package app
import (
"fmt"
"path"
"github.com/1Panel-dev/1Panel/backend/constant"
@@ -35,12 +34,12 @@ func createDir(fileOp files.FileOp, dirPath string) {
func createDefaultDockerNetwork() {
cli, err := docker.NewClient()
if err != nil {
fmt.Println("init docker client error", err.Error())
global.LOG.Errorf("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())
global.LOG.Errorf("init docker client error", err.Error())
return
}
}
-2
View File
@@ -1,7 +1,6 @@
package business
import (
"fmt"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/global"
)
@@ -13,7 +12,6 @@ func Init() {
return
}
if setting.AppStoreVersion != "" {
fmt.Println(setting.AppStoreVersion)
global.LOG.Info("do not sync")
return
}
+1 -1
View File
@@ -136,7 +136,7 @@ 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 {
+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
}
-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
@@ -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)
}
}
+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)
+1 -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)
+2 -2
View File
@@ -77,7 +77,7 @@ func (oss ossClient) Upload(src, target string) (bool, error) {
if err != nil {
return false, err
}
err = bucket.PutObjectFromFile(target, src)
err = bucket.UploadFile(target, src, 200*1024*1024, osssdk.Routines(5), osssdk.Checkpoint(true, ""))
if err != nil {
return false, err
}
@@ -89,7 +89,7 @@ func (oss ossClient) Download(src, target string) (bool, error) {
if err != nil {
return false, err
}
err = bucket.GetObjectToFile(src, target)
err = bucket.DownloadFile(src, target, 200*1024*1024, osssdk.Routines(5), osssdk.Checkpoint(true, ""))
if err != nil {
return false, err
}
+1 -1
View File
@@ -7,7 +7,7 @@ import (
)
func Up(filePath string) (string, error) {
stdout, err := cmd.Execf("docker-compose -f %s up -d", filePath)
stdout, err := cmd.Execf("docker-compose -f %s up -d --quiet-pull", filePath)
return stdout, err
}
+61 -4
View File
@@ -1,6 +1,7 @@
package files
import (
"archive/zip"
"bufio"
"context"
"encoding/json"
@@ -12,6 +13,7 @@ import (
"path"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
@@ -379,7 +381,9 @@ func getFormat(cType CompressType) archiver.CompressedArchive {
format.Compression = archiver.Gz{}
format.Archival = archiver.Tar{}
case Zip:
format.Archival = archiver.Zip{}
format.Archival = archiver.Zip{
Compression: zip.Deflate,
}
case Bz2:
format.Compression = archiver.Bz2{}
format.Archival = archiver.Tar{}
@@ -413,9 +417,19 @@ func (f FileOp) Compress(srcRiles []string, dst string, name string, cType Compr
return err
}
err = format.Archive(context.Background(), out, files)
if err != nil {
return err
switch cType {
case Zip:
if err := ZipFile(files, out); err != nil {
_ = f.DeleteFile(dstFile)
return err
}
default:
err = format.Archive(context.Background(), out, files)
if err != nil {
_ = f.DeleteFile(dstFile)
return err
}
break
}
return nil
}
@@ -488,3 +502,46 @@ func (f FileOp) CopyAndBackup(src string) (string, error) {
}
return backupPath, nil
}
func ZipFile(files []archiver.File, dst afero.File) error {
zw := zip.NewWriter(dst)
defer zw.Close()
for _, file := range files {
hdr, err := zip.FileInfoHeader(file)
if err != nil {
return err
}
hdr.Name = file.NameInArchive
if file.IsDir() {
if !strings.HasSuffix(hdr.Name, "/") {
hdr.Name += "/"
}
hdr.Method = zip.Store
}
w, err := zw.CreateHeader(hdr)
if err != nil {
return err
}
if file.IsDir() {
continue
}
if file.LinkTarget != "" {
_, err = w.Write([]byte(filepath.ToSlash(file.LinkTarget)))
if err != nil {
return err
}
} else {
fileReader, err := file.Open()
if err != nil {
return err
}
_, err = io.Copy(w, fileReader)
if err != nil {
return err
}
}
}
return nil
}
+2 -6
View File
@@ -4,11 +4,9 @@ import (
"bytes"
"fmt"
"io"
"net"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/global"
gossh "golang.org/x/crypto/ssh"
)
@@ -46,7 +44,7 @@ func (c *ConnInfo) NewClient() (*ConnInfo, error) {
}
config.Timeout = c.DialTimeOut
config.HostKeyCallback = func(hostname string, remote net.Addr, key gossh.PublicKey) error { return nil }
config.HostKeyCallback = gossh.InsecureIgnoreHostKey()
client, err := gossh.Dial("tcp", addr, config)
if nil != err {
return c, err
@@ -73,9 +71,7 @@ func (c *ConnInfo) Run(shell string) (string, error) {
}
func (c *ConnInfo) Close() {
if err := c.Client.Close(); err != nil {
global.LOG.Errorf("close ssh client failed, err: %v", err)
}
_ = c.Client.Close()
}
type SshConn struct {
-21
View File
@@ -1,21 +0,0 @@
package ssh
import (
"fmt"
"testing"
)
func TestSSH(t *testing.T) {
ss := ConnInfo{
Addr: "172.16.10.111",
Port: 22,
User: "root",
AuthMode: "password",
Password: "Calong@2015",
}
_, err := ss.NewClient()
if err != nil {
fmt.Println(err)
}
fmt.Println(ss.Run("ip a"))
}
+1
View File
@@ -89,6 +89,7 @@ func NewPrivateKeyClient(email string, privateKey string) (*AcmeClient, error) {
func newConfig(user *AcmeUser) *lego.Config {
config := lego.NewConfig(user)
config.CADirURL = "https://acme-v02.api.letsencrypt.org/directory"
//config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
config.UserAgent = "acm_go/0.0.1"
config.Certificate.KeyType = certcrypto.RSA2048
return config
+5 -4
View File
@@ -9,6 +9,7 @@ import (
"io/ioutil"
"os"
"testing"
"time"
"github.com/go-acme/lego/v4/acme/api"
"github.com/go-acme/lego/v4/certcrypto"
@@ -154,10 +155,10 @@ func TestSSL(t *testing.T) {
// panic(err)
//}
// err = client.Challenge.SetDNS01Provider(&plainDnsProvider{}, dns01.AddDNSTimeout(6*time.Minute))
// if err != nil {
// panic(err)
// }
err = client.Challenge.SetDNS01Provider(&manualDnsProvider{}, dns01.AddDNSTimeout(6*time.Minute))
if err != nil {
panic(err)
}
core, err := api.New(config.HTTPClient, config.UserAgent, config.CADirURL, reg.URI, priKey)
if err != nil {
+5 -15
View File
@@ -115,22 +115,12 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string) error {
return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(3*time.Minute))
}
func (c *AcmeClient) UseManualDns(domains []string) (*Resolve, error) {
func (c *AcmeClient) UseManualDns() error {
p := &manualDnsProvider{}
if err := c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(3*time.Minute)); err != nil {
return nil, nil
return err
}
request := certificate.ObtainRequest{
Domains: domains,
Bundle: true,
}
_, err := c.Client.Certificate.Obtain(request)
if err != nil {
return nil, err
}
return p.Resolve, nil
return nil
}
func (c *AcmeClient) UseHTTP(path string) error {
@@ -194,11 +184,11 @@ func (p *manualDnsProvider) CleanUp(domain, token, keyAuth string) error {
func (c *AcmeClient) GetDNSResolve(domains []string) (map[string]Resolve, error) {
core, err := api.New(c.Config.HTTPClient, c.Config.UserAgent, c.Config.CADirURL, c.User.Registration.URI, c.User.Key)
if err != nil {
panic(err)
return nil, err
}
order, err := core.Orders.New(domains)
if err != nil {
panic(err)
return nil, err
}
resolves := make(map[string]Resolve)
resc, errc := make(chan acme.Authorization), make(chan domainError)
+213 -27
View File
@@ -62,6 +62,25 @@ var doc = `{
}
}
},
"/apps/checkupdate": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取应用更新版本",
"tags": [
"App"
],
"summary": "Get app list update",
"responses": {
"200": {
"description": ""
}
}
}
},
"/apps/detail/:appId/:version": {
"get": {
"security": [
@@ -470,6 +489,48 @@ var doc = `{
}
}
},
"/apps/installed/params/update": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "修改应用参数",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "Change app params",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.AppInstalledUpdate"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"installId"
],
"formatEN": "Application param update [installId]",
"formatZH": "应用参数修改 [installId]",
"paramKeys": []
}
}
},
"/apps/installed/port/change": {
"post": {
"security": [
@@ -683,6 +744,20 @@ var doc = `{
}
}
},
"/auth/demo": {
"get": {
"description": "判断是否为demo环境",
"tags": [
"Auth"
],
"summary": "Check System isDemo",
"responses": {
"200": {
"description": ""
}
}
}
},
"/auth/init": {
"post": {
"description": "初始化用户",
@@ -3899,6 +3974,43 @@ var doc = `{
}
}
},
"/files/chunkupload": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "分片上传文件",
"tags": [
"File"
],
"summary": "ChunkUpload file",
"parameters": [
{
"type": "file",
"description": "request",
"name": "file",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"path"
],
"formatEN": "Upload file [path]",
"formatZH": "上传文件 [path]",
"paramKeys": []
}
}
},
"/files/compress": {
"post": {
"security": [
@@ -4112,6 +4224,48 @@ var doc = `{
}
}
},
"/files/download/bypath": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "下载指定文件",
"consumes": [
"application/json"
],
"tags": [
"File"
],
"summary": "Download file with path",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.FilePath"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"path"
],
"formatEN": "Download file [path]",
"formatZH": "下载文件 [path]",
"paramKeys": []
}
}
},
"/files/loadfile": {
"post": {
"security": [
@@ -8623,8 +8777,7 @@ var doc = `{
"dto.ComposeCreate": {
"type": "object",
"required": [
"from",
"name"
"from"
],
"properties": {
"file": {
@@ -8860,9 +9013,7 @@ var doc = `{
],
"properties": {
"day": {
"type": "integer",
"maximum": 31,
"minimum": 1
"type": "integer"
},
"dbName": {
"type": "string"
@@ -8871,17 +9022,13 @@ var doc = `{
"type": "string"
},
"hour": {
"type": "integer",
"maximum": 23,
"minimum": 0
"type": "integer"
},
"keepLocal": {
"type": "boolean"
},
"minute": {
"type": "integer",
"maximum": 59,
"minimum": 0
"type": "integer"
},
"name": {
"type": "string"
@@ -8942,9 +9089,7 @@ var doc = `{
],
"properties": {
"day": {
"type": "integer",
"maximum": 31,
"minimum": 1
"type": "integer"
},
"dbName": {
"type": "string"
@@ -8953,9 +9098,7 @@ var doc = `{
"type": "string"
},
"hour": {
"type": "integer",
"maximum": 23,
"minimum": 0
"type": "integer"
},
"id": {
"type": "integer"
@@ -8964,9 +9107,7 @@ var doc = `{
"type": "boolean"
},
"minute": {
"type": "integer",
"maximum": 59,
"minimum": 0
"type": "integer"
},
"name": {
"type": "string"
@@ -9060,15 +9201,9 @@ var doc = `{
},
"dto.DaemonJsonUpdateByFile": {
"type": "object",
"required": [
"path"
],
"properties": {
"file": {
"type": "string"
},
"path": {
"type": "string"
}
}
},
@@ -9254,6 +9389,12 @@ var doc = `{
"restart",
"stop"
]
},
"stopService": {
"type": "boolean"
},
"stopSocket": {
"type": "boolean"
}
}
},
@@ -11042,6 +11183,22 @@ var doc = `{
}
}
},
"request.AppInstalledUpdate": {
"type": "object",
"required": [
"installId",
"params"
],
"properties": {
"installId": {
"type": "integer"
},
"params": {
"type": "object",
"additionalProperties": true
}
}
},
"request.AppSearch": {
"type": "object",
"required": [
@@ -11284,6 +11441,17 @@ var doc = `{
}
}
},
"request.FilePath": {
"type": "object",
"required": [
"path"
],
"properties": {
"path": {
"type": "string"
}
}
},
"request.FilePathCheck": {
"type": "object",
"required": [
@@ -12042,7 +12210,22 @@ var doc = `{
"response.AppParam": {
"type": "object",
"properties": {
"label": {
"edit": {
"type": "boolean"
},
"key": {
"type": "string"
},
"labelEn": {
"type": "string"
},
"labelZh": {
"type": "string"
},
"rule": {
"type": "string"
},
"type": {
"type": "string"
},
"value": {}
@@ -12185,6 +12368,9 @@ var doc = `{
"appInstallId": {
"type": "integer"
},
"appName": {
"type": "string"
},
"createdAt": {
"type": "string"
},
+213 -27
View File
@@ -48,6 +48,25 @@
}
}
},
"/apps/checkupdate": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取应用更新版本",
"tags": [
"App"
],
"summary": "Get app list update",
"responses": {
"200": {
"description": ""
}
}
}
},
"/apps/detail/:appId/:version": {
"get": {
"security": [
@@ -456,6 +475,48 @@
}
}
},
"/apps/installed/params/update": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "修改应用参数",
"consumes": [
"application/json"
],
"tags": [
"App"
],
"summary": "Change app params",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.AppInstalledUpdate"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"installId"
],
"formatEN": "Application param update [installId]",
"formatZH": "应用参数修改 [installId]",
"paramKeys": []
}
}
},
"/apps/installed/port/change": {
"post": {
"security": [
@@ -669,6 +730,20 @@
}
}
},
"/auth/demo": {
"get": {
"description": "判断是否为demo环境",
"tags": [
"Auth"
],
"summary": "Check System isDemo",
"responses": {
"200": {
"description": ""
}
}
}
},
"/auth/init": {
"post": {
"description": "初始化用户",
@@ -3885,6 +3960,43 @@
}
}
},
"/files/chunkupload": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "分片上传文件",
"tags": [
"File"
],
"summary": "ChunkUpload file",
"parameters": [
{
"type": "file",
"description": "request",
"name": "file",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": ""
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"path"
],
"formatEN": "Upload file [path]",
"formatZH": "上传文件 [path]",
"paramKeys": []
}
}
},
"/files/compress": {
"post": {
"security": [
@@ -4098,6 +4210,48 @@
}
}
},
"/files/download/bypath": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "下载指定文件",
"consumes": [
"application/json"
],
"tags": [
"File"
],
"summary": "Download file with path",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.FilePath"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"path"
],
"formatEN": "Download file [path]",
"formatZH": "下载文件 [path]",
"paramKeys": []
}
}
},
"/files/loadfile": {
"post": {
"security": [
@@ -8609,8 +8763,7 @@
"dto.ComposeCreate": {
"type": "object",
"required": [
"from",
"name"
"from"
],
"properties": {
"file": {
@@ -8846,9 +8999,7 @@
],
"properties": {
"day": {
"type": "integer",
"maximum": 31,
"minimum": 1
"type": "integer"
},
"dbName": {
"type": "string"
@@ -8857,17 +9008,13 @@
"type": "string"
},
"hour": {
"type": "integer",
"maximum": 23,
"minimum": 0
"type": "integer"
},
"keepLocal": {
"type": "boolean"
},
"minute": {
"type": "integer",
"maximum": 59,
"minimum": 0
"type": "integer"
},
"name": {
"type": "string"
@@ -8928,9 +9075,7 @@
],
"properties": {
"day": {
"type": "integer",
"maximum": 31,
"minimum": 1
"type": "integer"
},
"dbName": {
"type": "string"
@@ -8939,9 +9084,7 @@
"type": "string"
},
"hour": {
"type": "integer",
"maximum": 23,
"minimum": 0
"type": "integer"
},
"id": {
"type": "integer"
@@ -8950,9 +9093,7 @@
"type": "boolean"
},
"minute": {
"type": "integer",
"maximum": 59,
"minimum": 0
"type": "integer"
},
"name": {
"type": "string"
@@ -9046,15 +9187,9 @@
},
"dto.DaemonJsonUpdateByFile": {
"type": "object",
"required": [
"path"
],
"properties": {
"file": {
"type": "string"
},
"path": {
"type": "string"
}
}
},
@@ -9240,6 +9375,12 @@
"restart",
"stop"
]
},
"stopService": {
"type": "boolean"
},
"stopSocket": {
"type": "boolean"
}
}
},
@@ -11028,6 +11169,22 @@
}
}
},
"request.AppInstalledUpdate": {
"type": "object",
"required": [
"installId",
"params"
],
"properties": {
"installId": {
"type": "integer"
},
"params": {
"type": "object",
"additionalProperties": true
}
}
},
"request.AppSearch": {
"type": "object",
"required": [
@@ -11270,6 +11427,17 @@
}
}
},
"request.FilePath": {
"type": "object",
"required": [
"path"
],
"properties": {
"path": {
"type": "string"
}
}
},
"request.FilePathCheck": {
"type": "object",
"required": [
@@ -12028,7 +12196,22 @@
"response.AppParam": {
"type": "object",
"properties": {
"label": {
"edit": {
"type": "boolean"
},
"key": {
"type": "string"
},
"labelEn": {
"type": "string"
},
"labelZh": {
"type": "string"
},
"rule": {
"type": "string"
},
"type": {
"type": "string"
},
"value": {}
@@ -12171,6 +12354,9 @@
"appInstallId": {
"type": "integer"
},
"appName": {
"type": "string"
},
"createdAt": {
"type": "string"
},
+133 -18
View File
@@ -152,7 +152,6 @@ definitions:
type: integer
required:
- from
- name
type: object
dto.ComposeOperation:
properties:
@@ -294,22 +293,16 @@ definitions:
dto.CronjobCreate:
properties:
day:
maximum: 31
minimum: 1
type: integer
dbName:
type: string
exclusionRules:
type: string
hour:
maximum: 23
minimum: 0
type: integer
keepLocal:
type: boolean
minute:
maximum: 59
minimum: 0
type: integer
name:
type: string
@@ -352,24 +345,18 @@ definitions:
dto.CronjobUpdate:
properties:
day:
maximum: 31
minimum: 1
type: integer
dbName:
type: string
exclusionRules:
type: string
hour:
maximum: 23
minimum: 0
type: integer
id:
type: integer
keepLocal:
type: boolean
minute:
maximum: 59
minimum: 0
type: integer
name:
type: string
@@ -439,10 +426,6 @@ definitions:
properties:
file:
type: string
path:
type: string
required:
- path
type: object
dto.DashboardBase:
properties:
@@ -564,6 +547,10 @@ definitions:
- restart
- stop
type: string
stopService:
type: boolean
stopSocket:
type: boolean
required:
- operation
type: object
@@ -1752,6 +1739,17 @@ definitions:
- page
- pageSize
type: object
request.AppInstalledUpdate:
properties:
installId:
type: integer
params:
additionalProperties: true
type: object
required:
- installId
- params
type: object
request.AppSearch:
properties:
name:
@@ -1914,6 +1912,13 @@ definitions:
showHidden:
type: boolean
type: object
request.FilePath:
properties:
path:
type: string
required:
- path
type: object
request.FilePathCheck:
properties:
path:
@@ -2421,7 +2426,17 @@ definitions:
type: object
response.AppParam:
properties:
label:
edit:
type: boolean
key:
type: string
labelEn:
type: string
labelZh:
type: string
rule:
type: string
type:
type: string
value: {}
type: object
@@ -2515,6 +2530,8 @@ definitions:
type: string
appInstallId:
type: integer
appName:
type: string
createdAt:
type: string
defaultServer:
@@ -2628,6 +2645,17 @@ paths:
summary: Search app by key
tags:
- App
/apps/checkupdate:
get:
description: 获取应用更新版本
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Get app list update
tags:
- App
/apps/detail/:appId/:version:
get:
consumes:
@@ -2887,6 +2915,33 @@ paths:
summary: Search params by appInstallId
tags:
- App
/apps/installed/params/update:
post:
consumes:
- application/json
description: 修改应用参数
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.AppInstalledUpdate'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Change app params
tags:
- App
x-panel-log:
BeforeFuntions: []
bodyKeys:
- installId
formatEN: Application param update [installId]
formatZH: 应用参数修改 [installId]
paramKeys: []
/apps/installed/port/change:
post:
consumes:
@@ -3022,6 +3077,15 @@ paths:
summary: Load captcha
tags:
- Auth
/auth/demo:
get:
description: 判断是否为demo环境
responses:
"200":
description: ""
summary: Check System isDemo
tags:
- Auth
/auth/init:
post:
consumes:
@@ -5065,6 +5129,30 @@ paths:
formatEN: Check whether file [path] exists
formatZH: 检测文件 [path] 是否存在
paramKeys: []
/files/chunkupload:
post:
description: 分片上传文件
parameters:
- description: request
in: formData
name: file
required: true
type: file
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: ChunkUpload file
tags:
- File
x-panel-log:
BeforeFuntions: []
bodyKeys:
- path
formatEN: Upload file [path]
formatZH: 上传文件 [path]
paramKeys: []
/files/compress:
post:
consumes:
@@ -5202,6 +5290,33 @@ paths:
formatEN: Download file [name]
formatZH: 下载文件 [name]
paramKeys: []
/files/download/bypath:
post:
consumes:
- application/json
description: 下载指定文件
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/request.FilePath'
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Download file with path
tags:
- File
x-panel-log:
BeforeFuntions: []
bodyKeys:
- path
formatEN: Download file [path]
formatZH: 下载文件 [path]
paramKeys: []
/files/loadfile:
post:
consumes:
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

+3 -1
View File
@@ -19,7 +19,6 @@ declare module 'vue' {
ContainerLog: typeof import('./src/components/container-log/index.vue')['default']
DrawerHeader: typeof import('./src/components/drawer-header/index.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElAlter: typeof import('element-plus/es')['ElAlter']
ElAside: typeof import('element-plus/es')['ElAside']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElBadge: typeof import('element-plus/es')['ElBadge']
@@ -80,12 +79,15 @@ declare module 'vue' {
Loading: typeof import('element-plus/es')['ElLoadingDirective']
Logo: typeof import('./src/components/app-layout/menu/components/Logo.vue')['default']
Menu: typeof import('./src/components/app-layout/menu/index.vue')['default']
MsgInfo: typeof import('./src/components/msg-info/index.vue')['default']
Popover: typeof import('element-plus/es')['ElPopoverDirective']
RouterButton: typeof import('./src/components/router-button/index.vue')['default']
Status: typeof import('./src/components/status/index.vue')['default']
SubItem: typeof import('./src/components/app-layout/menu/components/sub-item.vue')['default']
SvgIcon: typeof import('./src/components/svg-icon/svg-icon.vue')['default']
SystemUpgrade: typeof import('./src/components/system-upgrade/index.vue')['default']
TableSetting: typeof import('./src/components/table-setting/index.vue')['default']
Tooltip: typeof import('./src/components/tooltip/index.vue')['default']
Upload: typeof import('./src/components/upload/index.vue')['default']
VCharts: typeof import('./src/components/v-charts/index.vue')['default']
}
+6 -21
View File
@@ -30,7 +30,6 @@
"sass-loader": "^13.0.2",
"screenfull": "^6.0.2",
"unplugin-vue-define-options": "^0.7.3",
"vite-plugin-monaco-editor": "^1.1.0",
"vue": "^3.2.25",
"vue-clipboard3": "^2.0.0",
"vue-codemirror": "^6.1.1",
@@ -10274,14 +10273,6 @@
"entities": "^2.0.0"
}
},
"node_modules/vite-plugin-monaco-editor": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/vite-plugin-monaco-editor/-/vite-plugin-monaco-editor-1.1.0.tgz",
"integrity": "sha512-IvtUqZotrRoVqwT0PBBDIZPNraya3BxN/bfcNfnxZ5rkJiGcNtO5eAOWWSgT7zullIAEqQwxMU83yL9J5k7gww==",
"peerDependencies": {
"monaco-editor": ">=0.33.0"
}
},
"node_modules/vite-plugin-vue-setup-extend": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/vite-plugin-vue-setup-extend/-/vite-plugin-vue-setup-extend-0.4.0.tgz",
@@ -10643,9 +10634,9 @@
}
},
"node_modules/webpack": {
"version": "5.75.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
"integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
"version": "5.76.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz",
"integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==",
"peer": true,
"dependencies": {
"@types/eslint-scope": "^3.7.3",
@@ -18367,12 +18358,6 @@
}
}
},
"vite-plugin-monaco-editor": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/vite-plugin-monaco-editor/-/vite-plugin-monaco-editor-1.1.0.tgz",
"integrity": "sha512-IvtUqZotrRoVqwT0PBBDIZPNraya3BxN/bfcNfnxZ5rkJiGcNtO5eAOWWSgT7zullIAEqQwxMU83yL9J5k7gww==",
"requires": {}
},
"vite-plugin-vue-setup-extend": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/vite-plugin-vue-setup-extend/-/vite-plugin-vue-setup-extend-0.4.0.tgz",
@@ -18679,9 +18664,9 @@
}
},
"webpack": {
"version": "5.75.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
"integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
"version": "5.76.1",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz",
"integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==",
"peer": true,
"requires": {
"@types/eslint-scope": "^3.7.3",
-1
View File
@@ -42,7 +42,6 @@
"sass-loader": "^13.0.2",
"screenfull": "^6.0.2",
"unplugin-vue-define-options": "^0.7.3",
"vite-plugin-monaco-editor": "^1.1.0",
"vue": "^3.2.25",
"vue-clipboard3": "^2.0.0",
"vue-codemirror": "^6.1.1",
+5
View File
@@ -27,6 +27,11 @@ export namespace App {
items: App.App[];
}
export interface AppUpdateRes {
version: string;
canUpdate: boolean;
}
export interface AppDetail extends CommonModel {
appId: string;
icon: string;
+2 -1
View File
@@ -43,7 +43,8 @@ export namespace Container {
state: string;
runTime: string;
isFromCompose: string;
isFromApp: boolean;
isFromCompose: boolean;
}
export interface ContainerStats {
cpuPercent: number;
-7
View File
@@ -32,10 +32,3 @@ export interface DescriptionUpdate {
id: number;
description: string;
}
// * 文件上传模块
export namespace Upload {
export interface ResFileUrl {
fileUrl: string;
}
}
+4
View File
@@ -6,6 +6,10 @@ export const SyncApp = () => {
return http.post<any>('apps/sync', {});
};
export const GetAppListUpdate = () => {
return http.get<App.AppUpdateRes>('apps/checkupdate');
};
export const SearchApp = (req: App.AppReq) => {
return http.post<App.AppResPage>('apps/search', req);
};
+5
View File
@@ -28,6 +28,11 @@ export const loginStatus = () => {
export const checkIsFirst = () => {
return http.get<boolean>('/auth/status');
};
export const initUser = (params: Login.InitUser) => {
return http.post(`/auth/init`, params);
};
export const checkIsDemo = () => {
return http.get<boolean>('/auth/demo');
};
+5 -5
View File
@@ -6,7 +6,7 @@ export const searchContainer = (params: Container.ContainerSearch) => {
return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params);
};
export const createContainer = (params: Container.ContainerCreate) => {
return http.post(`/containers`, params);
return http.post(`/containers`, params, 1200000);
};
export const logContainer = (params: Container.ContainerLogSearch) => {
return http.post<string>(`/containers/search/log`, params);
@@ -38,10 +38,10 @@ export const imagePush = (params: Container.ImagePush) => {
return http.post<string>(`/containers/image/push`, params);
};
export const imageLoad = (params: Container.ImageLoad) => {
return http.post(`/containers/image/load`, params);
return http.post(`/containers/image/load`, params, 1200000);
};
export const imageSave = (params: Container.ImageSave) => {
return http.post(`/containers/image/save`, params);
return http.post(`/containers/image/save`, params, 1200000);
};
export const imageTag = (params: Container.ImageTag) => {
return http.post(`/containers/image/tag`, params);
@@ -117,13 +117,13 @@ export const searchCompose = (params: SearchWithPage) => {
return http.post<ResPage<Container.ComposeInfo>>(`/containers/compose/search`, params);
};
export const upCompose = (params: Container.ComposeCreate) => {
return http.post(`/containers/compose`, params);
return http.post(`/containers/compose`, params, 600000);
};
export const composeOperator = (params: Container.ComposeOpration) => {
return http.post(`/containers/compose/operate`, params);
};
export const composeUpdate = (params: Container.ComposeUpdate) => {
return http.post(`/containers/compose/update`, params);
return http.post(`/containers/compose/update`, params, 600000);
};
// docker
+1 -1
View File
@@ -31,7 +31,7 @@ export const updateStatus = (params: Cronjob.UpdateStatus) => {
};
export const download = (params: Cronjob.Download) => {
return http.download<BlobPart>(`cronjobs/download`, params, { responseType: 'blob' });
return http.post<string>(`cronjobs/download`, params);
};
export const handleOnce = (id: number) => {
+17 -3
View File
@@ -1,4 +1,6 @@
import http from '@/api';
import { deepCopy } from '@/utils/util';
import { Base64 } from 'js-base64';
import { SearchWithPage, ResPage, DescriptionUpdate } from '../interface';
import { Database } from '../interface/database';
@@ -7,13 +9,21 @@ export const searchMysqlDBs = (params: SearchWithPage) => {
};
export const addMysqlDB = (params: Database.MysqlDBCreate) => {
return http.post(`/databases`, params);
let reqest = deepCopy(params) as Database.MysqlDBCreate;
if (reqest.password) {
reqest.password = Base64.encode(reqest.password);
}
return http.post(`/databases`, reqest);
};
export const updateMysqlAccess = (params: Database.ChangeInfo) => {
return http.post(`/databases/change/access`, params);
};
export const updateMysqlPassword = (params: Database.ChangeInfo) => {
return http.post(`/databases/change/password`, params);
let reqest = deepCopy(params) as Database.ChangeInfo;
if (reqest.value) {
reqest.value = Base64.encode(reqest.value);
}
return http.post(`/databases/change/password`, reqest);
};
export const updateMysqlDescription = (params: DescriptionUpdate) => {
return http.post(`/databases/description/update`, params);
@@ -58,7 +68,11 @@ export const redisPersistenceConf = () => {
return http.get<Database.RedisPersistenceConf>(`/databases/redis/persistence/conf`);
};
export const changeRedisPassword = (params: Database.ChangeInfo) => {
return http.post(`/databases/redis/password`, params);
let reqest = deepCopy(params) as Database.ChangeInfo;
if (reqest.value) {
reqest.value = Base64.encode(reqest.value);
}
return http.post(`/databases/redis/password`, reqest);
};
export const updateRedisPersistenceConf = (params: Database.RedisConfPersistenceUpdate) => {
return http.post(`/databases/redis/persistence/update`, params);
+9 -1
View File
@@ -59,6 +59,10 @@ export const UploadFileData = (params: FormData, config: AxiosRequestConfig) =>
return http.upload<File.File>('files/upload', params, config);
};
export const ChunkUploadFileData = (params: FormData, config: AxiosRequestConfig) => {
return http.upload<File.File>('files/chunkupload', params, config);
};
export const RenameRile = (params: File.FileRename) => {
return http.post<File.File>('files/rename', params);
};
@@ -72,7 +76,11 @@ export const MoveFile = (params: File.FileMove) => {
};
export const DownloadFile = (params: File.FileDownload) => {
return http.download<BlobPart>('files/download', params, { responseType: 'blob' });
return http.download<BlobPart>('files/download', params, { responseType: 'blob', timeout: 20000 });
};
export const DownloadByPath = (path: string) => {
return http.download<BlobPart>('files/download/bypath', { path: path }, { responseType: 'blob', timeout: 40000 });
};
export const ComputeDirSize = (params: File.DirSizeReq) => {
+26 -3
View File
@@ -3,6 +3,8 @@ import { ResPage } from '../interface';
import { Command } from '../interface/command';
import { Group } from '../interface/group';
import { Host } from '../interface/host';
import { Base64 } from 'js-base64';
import { deepCopy } from '@/utils/util';
export const searchHosts = (params: Host.SearchWithPage) => {
return http.post<ResPage<Host.Host>>(`/hosts/search`, params);
@@ -14,16 +16,37 @@ export const getHostInfo = (id: number) => {
return http.get<Host.Host>(`/hosts/` + id);
};
export const addHost = (params: Host.HostOperate) => {
return http.post<Host.HostOperate>(`/hosts`, params);
let reqest = deepCopy(params) as Host.HostOperate;
if (reqest.password) {
reqest.password = Base64.encode(reqest.password);
}
if (reqest.privateKey) {
reqest.privateKey = Base64.encode(reqest.privateKey);
}
return http.post<Host.HostOperate>(`/hosts`, reqest);
};
export const testByInfo = (params: Host.HostConnTest) => {
return http.post<boolean>(`/hosts/test/byinfo`, params);
let reqest = deepCopy(params) as Host.HostOperate;
if (reqest.password) {
reqest.password = Base64.encode(reqest.password);
}
if (reqest.privateKey) {
reqest.privateKey = Base64.encode(reqest.privateKey);
}
return http.post<boolean>(`/hosts/test/byinfo`, reqest);
};
export const testByID = (id: number) => {
return http.post<boolean>(`/hosts/test/byid/${id}`);
};
export const editHost = (params: Host.HostOperate) => {
return http.post(`/hosts/update`, params);
let reqest = deepCopy(params) as Host.HostOperate;
if (reqest.password) {
reqest.password = Base64.encode(reqest.password);
}
if (reqest.privateKey) {
reqest.privateKey = Base64.encode(reqest.privateKey);
}
return http.post(`/hosts/update`, reqest);
};
export const editHostGroup = (params: Host.GroupChange) => {
return http.post(`/hosts/update/group`, params);
+30 -7
View File
@@ -1,4 +1,6 @@
import http from '@/api';
import { deepCopy } from '@/utils/util';
import { Base64 } from 'js-base64';
import { ResPage, SearchWithPage, DescriptionUpdate } from '../interface';
import { Backup } from '../interface/backup';
import { Setting } from '../interface/setting';
@@ -52,16 +54,16 @@ export const loadBaseDir = () => {
// backup
export const handleBackup = (params: Backup.Backup) => {
return http.post(`/settings/backup/backup`, params);
return http.post(`/settings/backup/backup`, params, 400000);
};
export const handleRecover = (params: Backup.Recover) => {
return http.post(`/settings/backup/recover`, params);
return http.post(`/settings/backup/recover`, params, 400000);
};
export const handleRecoverByUpload = (params: Backup.Recover) => {
return http.post(`/settings/backup/recover/byupload`, params);
return http.post(`/settings/backup/recover/byupload`, params, 400000);
};
export const downloadBackupRecord = (params: Backup.RecordDownload) => {
return http.download<BlobPart>(`/settings/backup/record/download`, params, { responseType: 'blob' });
return http.post<string>(`/settings/backup/record/download`, params);
};
export const deleteBackupRecord = (params: { ids: number[] }) => {
return http.post(`/settings/backup/record/del`, params);
@@ -77,16 +79,37 @@ export const getFilesFromBackup = (type: string) => {
return http.post<Array<any>>(`/settings/backup/search/files`, { type: type });
};
export const addBackup = (params: Backup.BackupOperate) => {
return http.post<Backup.BackupOperate>(`/settings/backup`, params);
let reqest = deepCopy(params) as Backup.BackupOperate;
if (reqest.accessKey) {
reqest.accessKey = Base64.encode(reqest.accessKey);
}
if (reqest.credential) {
reqest.credential = Base64.encode(reqest.credential);
}
return http.post<Backup.BackupOperate>(`/settings/backup`, reqest);
};
export const editBackup = (params: Backup.BackupOperate) => {
return http.post(`/settings/backup/update`, params);
let reqest = deepCopy(params) as Backup.BackupOperate;
if (reqest.accessKey) {
reqest.accessKey = Base64.encode(reqest.accessKey);
}
if (reqest.credential) {
reqest.credential = Base64.encode(reqest.credential);
}
return http.post(`/settings/backup/update`, reqest);
};
export const deleteBackup = (params: { ids: number[] }) => {
return http.post(`/settings/backup/del`, params);
};
export const listBucket = (params: Backup.ForBucket) => {
return http.post(`/settings/backup/buckets`, params);
let reqest = deepCopy(params) as Backup.BackupOperate;
if (reqest.accessKey) {
reqest.accessKey = Base64.encode(reqest.accessKey);
}
if (reqest.credential) {
reqest.credential = Base64.encode(reqest.credential);
}
return http.post(`/settings/backup/buckets`, reqest);
};
// snapshot
-17
View File
@@ -1,17 +0,0 @@
import { Upload } from '@/api/interface/index';
import { PORT1 } from '@/api/config/service-port';
import http from '@/api';
/**
* @name 文件上传模块
*/
// * 图片上传
export const uploadImg = (params: FormData) => {
return http.post<Upload.ResFileUrl>(PORT1 + `/file/upload/img`, params);
};
// * 视频上传
export const uploadVideo = (params: FormData) => {
return http.post<Upload.ResFileUrl>(PORT1 + `/file/upload/video`, params);
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 KiB

After

Width:  |  Height:  |  Size: 170 KiB

@@ -4,14 +4,18 @@
</div>
<div class="footer flx-justify-between">
<div class="footer-left">
<a href="https://fit2cloud.com/" target="_blank">杭州飞致云信息科技有限公司</a>
<a href="https://fit2cloud.com/" target="_blank">Copyright @2014-2023 FIT2CLOUND 飞致云</a>
</div>
<div class="footer-right">
<span>Version V1</span>
<SystemUpgrade />
</div>
</div>
</template>
<script setup lang="ts">
import SystemUpgrade from '@/components/system-upgrade/index.vue';
</script>
<style scoped lang="scss">
.footer {
height: 45px;
+3 -1
View File
@@ -57,15 +57,17 @@ const loadStatus = async () => {
await getSystemAvailable()
.then((res) => {
if (res) {
location.reload();
clearInterval(Number(timer));
timer = null;
}
})
.catch(() => {
location.reload();
clearInterval(Number(timer));
timer = null;
});
}, 1000 * 20);
}, 1000 * 10);
}
};
+11 -8
View File
@@ -58,6 +58,7 @@ import DrawerHeader from '@/components/drawer-header/index.vue';
import { deleteBackupRecord, downloadBackupRecord, searchBackupRecords } from '@/api/modules/setting';
import { Backup } from '@/api/interface/backup';
import { MsgSuccess } from '@/utils/message';
import { DownloadByPath } from '@/api/modules/files';
const selects = ref<any>([]);
const loading = ref();
@@ -145,14 +146,16 @@ const onDownload = async (row: Backup.RecordInfo) => {
fileDir: row.fileDir,
fileName: row.fileName,
};
const res = await downloadBackupRecord(params);
const downloadUrl = window.URL.createObjectURL(new Blob([res]));
const a = document.createElement('a');
a.style.display = 'none';
a.href = downloadUrl;
a.download = row.fileName;
const event = new MouseEvent('click');
a.dispatchEvent(event);
await downloadBackupRecord(params).then(async (res) => {
const file = await DownloadByPath(res.data);
const downloadUrl = window.URL.createObjectURL(new Blob([file]));
const a = document.createElement('a');
a.style.display = 'none';
a.href = downloadUrl;
a.download = row.fileName;
const event = new MouseEvent('click');
a.dispatchEvent(event);
});
};
const onBatchDelete = async (row: Backup.RecordInfo | null) => {
@@ -53,7 +53,6 @@ defineProps({
const emit = defineEmits(['search', 'update:selects']);
const condition = ref({});
function search(conditions: any, e: any) {
console.log(conditions);
if (conditions) {
condition.value = conditions;
}
@@ -77,7 +77,7 @@ const timeOptions = ref([
const searchLogs = async () => {
const res = await logContainer(logSearch);
logInfo.value = res.data;
logInfo.value = res.data || '';
nextTick(() => {
const state = view.value.state;
view.value.dispatch({
@@ -100,7 +100,6 @@ const props = defineProps({
const em = defineEmits(['choose']);
const checkFile = (row: any) => {
console.log(row.path);
rowName.value = row.name;
em('choose', row.path);
popoverVisible.value = false;
+12 -12
View File
@@ -28,7 +28,7 @@
<el-table-column :label="$t('commons.table.operate')">
<template #default="{ row, $index }">
<div>
<el-button link v-if="row.edit" type="primary" @click="saveGroup(groupForm, row, true)">
<el-button link v-if="row.edit" type="primary" @click="saveGroup(groupForm, row)">
{{ $t('commons.button.save') }}
</el-button>
<el-button link v-if="!row.edit" type="primary" @click="editGroup($index)">
@@ -46,12 +46,7 @@
<el-button link v-if="row.edit" type="primary" @click="search()">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button
link
v-if="!row.edit && !row.isDefault"
type="primary"
@click="saveGroup(groupForm, row, false)"
>
<el-button link v-if="!row.edit && !row.isDefault" type="primary" @click="setDefault(row)">
{{ $t('website.setDefault') }}
</el-button>
</div>
@@ -83,7 +78,7 @@ interface DialogProps {
type: string;
}
const groupForm = ref();
const groupForm = ref<FormInstance>();
const acceptParams = (params: DialogProps): void => {
type.value = params.type;
open.value = true;
@@ -106,16 +101,13 @@ const search = () => {
});
};
const saveGroup = async (formEl: FormInstance, group: Group.GroupInfo, isEdit: boolean) => {
const saveGroup = async (formEl: FormInstance, group: Group.GroupInfo) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (!valid) {
return;
}
group.type = type.value;
if (!isEdit) {
group.isDefault = true;
}
if (group.id == 0) {
CreateGroup(group).then(() => {
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
@@ -130,6 +122,14 @@ const saveGroup = async (formEl: FormInstance, group: Group.GroupInfo, isEdit: b
});
};
const setDefault = (group: Group.GroupInfo) => {
group.isDefault = true;
UpdateGroup(group).then(() => {
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
search();
});
};
const openCreate = () => {
for (const d of data.value) {
if (d.name == '') {
@@ -0,0 +1,34 @@
<template>
<el-tooltip placement="top-start">
<template #content>
<div class="info-break" :style="{ width: width + 'px' }">{{ info }}</div>
</template>
<div class="info-hidden" :style="{ width: width + 'px' }">{{ info }}</div>
</el-tooltip>
</template>
<script lang="ts" setup>
defineProps({
width: {
type: String,
default: '100',
},
info: {
type: String,
default: '',
},
});
</script>
<style scoped>
.info-hidden {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.info-break {
width: 100px;
word-break: break-all;
word-wrap: break-word;
}
</style>
+1 -1
View File
@@ -25,7 +25,7 @@ const getType = (status: string) => {
case 'error':
return 'danger';
case 'stopped':
return 'warning';
return 'danger';
default:
return '';
}

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