mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-10 16:00:31 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30cb471629 | ||
|
|
d70c22dde8 | ||
|
|
01bb6b7c01 | ||
|
|
4f4879759e | ||
|
|
92a410fcea | ||
|
|
5d1fced8e9 | ||
|
|
a56e5a8abe | ||
|
|
28ae7f3a0c | ||
|
|
8d675c81c5 | ||
|
|
66a345364f | ||
|
|
a3cb8be08f | ||
|
|
0861b30a7b | ||
|
|
36f2a3eb4b | ||
|
|
092cbbf8da | ||
|
|
39e3e8f214 | ||
|
|
fa983bdcc9 | ||
|
|
67bb30c10c | ||
|
|
e85340ca5d | ||
|
|
a3a1e17849 | ||
|
|
2601135225 | ||
|
|
c556affc91 | ||
|
|
6ee9789a2f | ||
|
|
68a457ae89 | ||
|
|
72237596f3 | ||
|
|
f516333682 | ||
|
|
873af25684 | ||
|
|
8b058a873e | ||
|
|
0c5a5a6454 | ||
|
|
2096049708 | ||
|
|
1095aa2b65 | ||
|
|
13679ff301 | ||
|
|
89b7a06662 | ||
|
|
e8582bea75 | ||
|
|
9af7926eb9 | ||
|
|
bdcdf7e181 |
@@ -1,17 +0,0 @@
|
||||
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 }}
|
||||
@@ -1,16 +0,0 @@
|
||||
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: '状态:待处理'
|
||||
@@ -1,38 +0,0 @@
|
||||
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: '状态:待处理'
|
||||
@@ -1,16 +0,0 @@
|
||||
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: '状态:待处理'
|
||||
@@ -0,0 +1,16 @@
|
||||
name: sync2gitee
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
repo-sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mirror the Github organization repos to Gitee.
|
||||
uses: Yikun/hub-mirror-action@master
|
||||
with:
|
||||
src: 'github/1Panel-dev'
|
||||
dst: 'gitee/fit2cloud-xlab'
|
||||
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
|
||||
dst_token: ${{ secrets.GITEE_TOKEN }}
|
||||
static_list: "1Panel"
|
||||
force_update: true
|
||||
@@ -1,3 +1,4 @@
|
||||
[README_EN.md](README_EN.md)
|
||||
<p align="center"><a href="https://1panel.cn"><img src="http://1panel.oss-cn-hangzhou.aliyuncs.com/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
|
||||
<p align="center"><b>现代化、开源的 Linux 服务器运维管理面板</b></p>
|
||||
<p align="center">
|
||||
@@ -18,7 +19,7 @@
|
||||
|
||||
## UI 展示
|
||||
|
||||

|
||||

|
||||
|
||||
## 快速开始
|
||||
|
||||
@@ -47,13 +48,13 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
|
||||
|
||||
**微信交流群**
|
||||
|
||||
<img src="http://1panel.oss-cn-hangzhou.aliyuncs.com/img/wechat-group.jpg" width="156" height="156"/>
|
||||
<img src="https://1panel.cn/img/wechat-group.jpg" width="156" height="156"/>
|
||||
|
||||
## 安全说明
|
||||
|
||||
如果您在使用过程中发现任何安全问题,请通过以下方式直接联系我们:
|
||||
|
||||
- 邮箱:support@fit2cloud.com
|
||||
- 邮箱:support@fit2cloud.com
|
||||
- 电话:400-052-0755
|
||||
|
||||
## Star History
|
||||
@@ -62,7 +63,7 @@ curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2014-2023 飞致云 FIT2CLOUD, All rights reserved.
|
||||
Copyright (c) 2014-2023 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
|
||||
|
||||
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
[中文 README.md](README.md)
|
||||
<p align="center"><a href="https://1panel.cn"><img src="http://1panel.oss-cn-hangzhou.aliyuncs.com/img/1panel-logo.png" alt="1Panel" width="300" /></a></p>
|
||||
<p align="center"><b>Modern and Open-Source Linux Server Operation and Management Panel</b></p>
|
||||
<p align="center">
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://shields.io/github/license/1Panel-dev/1Panel" alt="License: GPL v3"></a>
|
||||
<a href="https://app.codacy.com/gh/1Panel-dev/1Panel?utm_source=github.com&utm_medium=referral&utm_content=1Panel-dev/1Panel&utm_campaign=Badge_Grade_Dashboard"><img src="https://app.codacy.com/project/badge/Grade/da67574fd82b473992781d1386b937ef" alt="Codacy"></a>
|
||||
<a href="https://github.com/1Panel-dev/1Panel/releases"><img src="https://img.shields.io/github/v/release/1Panel-dev/1Panel" alt="GitHub release"></a>
|
||||
<a href="https://github.com/1Panel-dev/1Panel"><img src="https://img.shields.io/github/stars/1Panel-dev/1Panel?color=%231890FF&style=flat-square" alt="Stars"></a>
|
||||
</p>
|
||||
|
||||
------------------------------
|
||||
|
||||
1Panel is a modern and Open-Source linux server operation and management panel, the functions and advantages of 1Panel include:
|
||||
|
||||
- **Quick website building**: Deeply integrated with Wordpress and [Halo](https://github.com/halo-dev/halo/), with one-click solutions for domain name binding, SSL certificate configuration, and more;
|
||||
- **Efficient management**: Easily manage Linux servers through the web interface, including application management, host monitoring, file management, database management, container management, and more;
|
||||
- **Secure and reliable**: Minimal vulnerability exposure, with firewall and security audit functions provided;
|
||||
- **One-click backup**: Support for one-click backup and restore, with backup data stored in the cloud and never lost.
|
||||
|
||||
## UI Display
|
||||
|
||||

|
||||
|
||||
## Quick Start
|
||||
|
||||
**Online Demo**
|
||||
|
||||
- Address: <https://demo.1panel.cn/>
|
||||
- Username: demo
|
||||
- Password: 1panel
|
||||
|
||||
**One-Click Installation**
|
||||
|
||||
Execute the following command to install 1Panel with one click:
|
||||
|
||||
```sh
|
||||
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
|
||||
```
|
||||
|
||||
**Learning Materials**
|
||||
|
||||
- [Online Documentation](https://1panel.cn/docs/)
|
||||
- [Teaching Videos](https://space.bilibili.com/510493147/channel/collectiondetail?sid=1199760)
|
||||
|
||||
## Community
|
||||
|
||||
If you have any questions or suggestions, please submit a GitHub Issue or join our WeChat group for communication.
|
||||
|
||||
**WeChat Group**
|
||||
|
||||
<img src="https://1panel.cn/img/wechat-group.jpg" width="156" height="156"/>
|
||||
|
||||
## Security Information
|
||||
|
||||
If you discover any security issues, please contact us through:
|
||||
|
||||
- Email: support@fit2cloud.com
|
||||
- Phone: 400-052-0755
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#1Panel-dev/1Panel&Date)
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2014-2023 [FIT2CLOUD 飞致云](https://fit2cloud.com/), All rights reserved.
|
||||
|
||||
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
||||
|
||||
<https://www.gnu.org/licenses/gpl-3.0.html>
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
# 安全说明
|
||||
|
||||
如果您发现安全问题,请直接联系我们:
|
||||
|
||||
- wanghe@fit2cloud.com
|
||||
- support@fit2cloud.com
|
||||
- 400-052-0755
|
||||
|
||||
感谢您的支持!
|
||||
|
||||
# Security Policy
|
||||
|
||||
All security bugs should be reported to the contact as below:
|
||||
|
||||
- wanghe@fit2cloud.com
|
||||
- support@fit2cloud.com
|
||||
- 400-052-0755
|
||||
|
||||
Thanks for your support!
|
||||
@@ -144,7 +144,7 @@ func (b *BaseApi) DeleteCheck(c *gin.Context) {
|
||||
// @Router /apps/installed/sync [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"同步已安装应用列表","formatEN":"Sync the list of installed apps"}
|
||||
func (b *BaseApi) SyncInstalled(c *gin.Context) {
|
||||
if err := appInstallService.SyncAll(); err != nil {
|
||||
if err := appInstallService.SyncAll(false); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -70,6 +70,34 @@ func (b *BaseApi) SearchCompose(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Container Compose
|
||||
// @Summary Test compose
|
||||
// @Description 测试 compose 是否可用
|
||||
// @Accept json
|
||||
// @Param request body dto.ComposeCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /containers/compose/test [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"检测 compose [name] 格式","formatEN":"check compose [name]"}
|
||||
func (b *BaseApi) TestCompose(c *gin.Context) {
|
||||
var req dto.ComposeCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := global.VALID.Struct(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
|
||||
isOK, err := containerService.TestCompose(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, isOK)
|
||||
}
|
||||
|
||||
// @Tags Container Compose
|
||||
// @Summary Create compose
|
||||
// @Description 创建容器编排
|
||||
@@ -90,11 +118,12 @@ func (b *BaseApi) CreateCompose(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := containerService.CreateCompose(req); err != nil {
|
||||
log, err := containerService.CreateCompose(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
helper.SuccessWithData(c, log)
|
||||
}
|
||||
|
||||
// @Tags Container Compose
|
||||
|
||||
@@ -35,7 +35,6 @@ var (
|
||||
|
||||
commandService = service.ServiceGroupApp.CommandService
|
||||
|
||||
websiteGroupService = service.ServiceGroupApp.WebsiteGroupService
|
||||
websiteService = service.ServiceGroupApp.WebsiteService
|
||||
websiteDnsAccountService = service.ServiceGroupApp.WebsiteDnsAccountService
|
||||
websiteSSLService = service.ServiceGroupApp.WebsiteSSLService
|
||||
|
||||
+19
-11
@@ -449,7 +449,7 @@ func (b *BaseApi) Download(c *gin.Context) {
|
||||
// @Summary Download file with path
|
||||
// @Description 下载指定文件
|
||||
// @Accept json
|
||||
// @Param request body request.FilePath true "request"
|
||||
// @Param request body dto.FilePath true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /files/download/bypath [post]
|
||||
@@ -519,6 +519,12 @@ func (b *BaseApi) LoadFromFile(c *gin.Context) {
|
||||
}
|
||||
|
||||
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
|
||||
if _, err := os.Stat(path.Dir(dstDir)); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(path.Dir(dstDir), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
targetFile, err := os.Create(filepath.Join(dstDir, fileName))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -547,7 +553,6 @@ func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int)
|
||||
// @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 {
|
||||
@@ -573,13 +578,16 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||
}
|
||||
|
||||
fileOp := files.NewFileOp()
|
||||
if err := fileOp.CreateDir("uploads", 0755); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
tmpDir := path.Join(global.CONF.System.TmpDir, "upload")
|
||||
if !fileOp.Stat(tmpDir) {
|
||||
if err := fileOp.CreateDir(tmpDir, 0755); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
//fileID := uuid.New().String()
|
||||
|
||||
filename := c.PostForm("filename")
|
||||
fileDir := filepath.Join(global.CONF.System.DataDir, "upload", filename)
|
||||
fileDir := filepath.Join(tmpDir, filename)
|
||||
|
||||
_ = os.MkdirAll(fileDir, 0755)
|
||||
filePath := filepath.Join(fileDir, filename)
|
||||
@@ -589,25 +597,25 @@ func (b *BaseApi) UploadChunkFiles(c *gin.Context) {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
emptyFile.Close()
|
||||
defer emptyFile.Close()
|
||||
|
||||
chunkData, err := ioutil.ReadAll(uploadFile)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrFileUpload, err)
|
||||
return
|
||||
}
|
||||
|
||||
chunkPath := filepath.Join(fileDir, fmt.Sprintf("%s.%d", filename, chunkIndex))
|
||||
err = ioutil.WriteFile(chunkPath, chunkData, 0644)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileUpload, err)
|
||||
return
|
||||
}
|
||||
|
||||
if chunkIndex+1 == chunkCount {
|
||||
err = mergeChunks(filename, fileDir, c.PostForm("path"), chunkCount)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrAppDelete, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrFileUpload, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, true)
|
||||
|
||||
@@ -22,6 +22,28 @@ func (b *BaseApi) GetUpgradeInfo(c *gin.Context) {
|
||||
helper.SuccessWithData(c, info)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load release notes by version
|
||||
// @Description 获取版本 release notes
|
||||
// @Accept json
|
||||
// @Param request body dto.Upgrade true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/upgrade [get]
|
||||
func (b *BaseApi) GetNotesByVersion(c *gin.Context) {
|
||||
var req dto.Upgrade
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
notes, err := upgradeService.LoadNotes(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, notes)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Upgrade
|
||||
// @Description 系统更新
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// @Tags Website Group
|
||||
// @Summary List website groups
|
||||
// @Description 获取网站组
|
||||
// @Success 200 {anrry} model.WebsiteGroup
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/groups [get]
|
||||
func (b *BaseApi) GetWebGroups(c *gin.Context) {
|
||||
list, err := websiteGroupService.GetGroups()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Website Group
|
||||
// @Summary Create website group
|
||||
// @Description 创建网站组
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteGroupCreate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/groups [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"创建网站组 [name]","formatEN":"Create website groups [name]"}
|
||||
func (b *BaseApi) CreateWebGroup(c *gin.Context) {
|
||||
var req request.WebsiteGroupCreate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteGroupService.CreateGroup(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Website Group
|
||||
// @Summary Update website group
|
||||
// @Description 更新网站组
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteGroupUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/groups/update [post]
|
||||
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"更新网站组 [name]","formatEN":"Update website groups [name]"}
|
||||
func (b *BaseApi) UpdateWebGroup(c *gin.Context) {
|
||||
var req request.WebsiteGroupUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteGroupService.UpdateGroup(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags Website Group
|
||||
// @Summary Delete website group
|
||||
// @Description 删除网站组
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteResourceReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/groups/del [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_groups","output_colume":"name","output_value":"name"}],"formatZH":"删除网站组 [name]","formatEN":"Delete website group [name]"}
|
||||
func (b *BaseApi) DeleteWebGroup(c *gin.Context) {
|
||||
var req request.WebsiteResourceReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteGroupService.DeleteGroup(req.ID); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
@@ -176,3 +176,25 @@ func (b *BaseApi) GetWebsiteSSLById(c *gin.Context) {
|
||||
}
|
||||
helper.SuccessWithData(c, websiteSSL)
|
||||
}
|
||||
|
||||
// @Tags Website SSL
|
||||
// @Summary Update ssl
|
||||
// @Description 更新 ssl
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteSSLUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/ssl/update [post]
|
||||
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFuntions":[{"input_colume":"id","input_value":"id","isList":false,"db":"website_ssls","output_colume":"primary_domain","output_value":"domain"}],"formatZH":"更新证书设置 [domain]","formatEN":"Update ssl config [domain]"}
|
||||
func (b *BaseApi) UpdateWebsiteSSL(c *gin.Context) {
|
||||
var req request.WebsiteSSLUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteSSLService.Update(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
+15
-9
@@ -69,15 +69,21 @@ type AppForm struct {
|
||||
}
|
||||
|
||||
type AppFormFields struct {
|
||||
Type string `json:"type"`
|
||||
LabelZh string `json:"labelZh"`
|
||||
LabelEn string `json:"labelEn"`
|
||||
Required bool `json:"required"`
|
||||
Default interface{} `json:"default"`
|
||||
EnvKey string `json:"envKey"`
|
||||
Disabled bool `json:"disabled"`
|
||||
Edit bool `json:"edit"`
|
||||
Rule string `json:"rule"`
|
||||
Type string `json:"type"`
|
||||
LabelZh string `json:"labelZh"`
|
||||
LabelEn string `json:"labelEn"`
|
||||
Required bool `json:"required"`
|
||||
Default interface{} `json:"default"`
|
||||
EnvKey string `json:"envKey"`
|
||||
Disabled bool `json:"disabled"`
|
||||
Edit bool `json:"edit"`
|
||||
Rule string `json:"rule"`
|
||||
Values []AppFormValue `json:"values"`
|
||||
}
|
||||
|
||||
type AppFormValue struct {
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type AppResource struct {
|
||||
|
||||
@@ -9,7 +9,6 @@ type UserLoginInfo struct {
|
||||
Name string `json:"name"`
|
||||
Token string `json:"token"`
|
||||
MfaStatus string `json:"mfaStatus"`
|
||||
MfaSecret string `json:"mfaSecret"`
|
||||
}
|
||||
|
||||
type MfaCredential struct {
|
||||
@@ -28,7 +27,6 @@ type Login struct {
|
||||
type MFALogin struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
Secret string `json:"secret"`
|
||||
Code string `json:"code"`
|
||||
AuthMethod string `json:"authMethod"`
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ type GroupSearch struct {
|
||||
type GroupUpdate struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
IsDefault bool `json:"isDefault"`
|
||||
}
|
||||
|
||||
|
||||
@@ -44,3 +44,8 @@ type WebsiteDnsAccountUpdate struct {
|
||||
type WebsiteResourceReq struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteSSLUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
AutoRenew bool `json:"autoRenew" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -61,11 +61,13 @@ type AppService struct {
|
||||
}
|
||||
|
||||
type AppParam struct {
|
||||
Value interface{} `json:"value"`
|
||||
Edit bool `json:"edit"`
|
||||
Key string `json:"key"`
|
||||
Rule string `json:"rule"`
|
||||
LabelZh string `json:"labelZh"`
|
||||
LabelEn string `json:"labelEn"`
|
||||
Type string `json:"type"`
|
||||
Value interface{} `json:"value"`
|
||||
Edit bool `json:"edit"`
|
||||
Key string `json:"key"`
|
||||
Rule string `json:"rule"`
|
||||
LabelZh string `json:"labelZh"`
|
||||
LabelEn string `json:"labelEn"`
|
||||
Type string `json:"type"`
|
||||
Values interface{} `json:"values"`
|
||||
ShowValue string `json:"showValue"`
|
||||
}
|
||||
|
||||
@@ -82,9 +82,11 @@ type SnapshotInfo struct {
|
||||
}
|
||||
|
||||
type UpgradeInfo struct {
|
||||
NewVersion string `json:"newVersion"`
|
||||
ReleaseNote string `json:"releaseNote"`
|
||||
NewVersion string `json:"newVersion"`
|
||||
LatestVersion string `json:"latestVersion"`
|
||||
ReleaseNote string `json:"releaseNote"`
|
||||
}
|
||||
|
||||
type Upgrade struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package model
|
||||
|
||||
type WebsiteGroup struct {
|
||||
BaseModel
|
||||
Name string `gorm:"type:varchar(64);not null" json:"name"`
|
||||
Default bool `json:"default"`
|
||||
}
|
||||
|
||||
func (w WebsiteGroup) TableName() string {
|
||||
return "website_groups"
|
||||
}
|
||||
@@ -113,6 +113,7 @@ type RootInfo struct {
|
||||
Name string `json:"name"`
|
||||
Port int64 `json:"port"`
|
||||
Password string `json:"password"`
|
||||
UserPassword string `json:"userPassword"`
|
||||
ContainerName string `json:"containerName"`
|
||||
Param string `json:"param"`
|
||||
Env string `json:"env"`
|
||||
@@ -146,6 +147,10 @@ func (a *AppInstallRepo) LoadBaseInfo(key string, name string) (*RootInfo, error
|
||||
if ok {
|
||||
info.Password = password
|
||||
}
|
||||
userPassword, ok := envMap["PANEL_DB_USER_PASSWORD"].(string)
|
||||
if ok {
|
||||
info.UserPassword = userPassword
|
||||
}
|
||||
info.Port = int64(appInstall.HttpPort)
|
||||
info.ID = appInstall.ID
|
||||
info.ContainerName = appInstall.ContainerName
|
||||
|
||||
@@ -19,7 +19,6 @@ type RepoGroup struct {
|
||||
BackupRepo
|
||||
WebsiteRepo
|
||||
WebsiteDomainRepo
|
||||
WebsiteGroupRepo
|
||||
WebsiteDnsAccountRepo
|
||||
WebsiteSSLRepo
|
||||
WebsiteAcmeAccountRepo
|
||||
|
||||
@@ -14,7 +14,7 @@ type IGroupRepo interface {
|
||||
Create(group *model.Group) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
CancelDefault() error
|
||||
CancelDefault(groupType string) error
|
||||
WithByIsDefault(isDefault bool) DBOption
|
||||
}
|
||||
|
||||
@@ -64,6 +64,6 @@ func (u *GroupRepo) Delete(opts ...DBOption) error {
|
||||
return db.Delete(&model.Group{}).Error
|
||||
}
|
||||
|
||||
func (w GroupRepo) CancelDefault() error {
|
||||
return global.DB.Model(&model.Group{}).Where("`is_default` = 1").Updates(map[string]interface{}{"is_default": 0}).Error
|
||||
func (u *GroupRepo) CancelDefault(groupType string) error {
|
||||
return global.DB.Model(&model.Group{}).Where("is_default = ? AND type = ?", 1, groupType).Updates(map[string]interface{}{"is_default": 0}).Error
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type WebsiteGroupRepo struct {
|
||||
}
|
||||
|
||||
func (w WebsiteGroupRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteGroup, error) {
|
||||
var groups []model.WebsiteGroup
|
||||
db := getDb(opts...).Model(&model.WebsiteGroup{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Order("`default` desc").Find(&groups).Error
|
||||
return count, groups, err
|
||||
}
|
||||
|
||||
func (w WebsiteGroupRepo) GetBy(opts ...DBOption) ([]model.WebsiteGroup, error) {
|
||||
var groups []model.WebsiteGroup
|
||||
db := getDb(opts...).Model(&model.WebsiteGroup{})
|
||||
if err := db.Order("`default` desc").Find(&groups).Error; err != nil {
|
||||
return groups, err
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (w WebsiteGroupRepo) Create(app *model.WebsiteGroup) error {
|
||||
return getDb().Omit(clause.Associations).Create(app).Error
|
||||
}
|
||||
|
||||
func (w WebsiteGroupRepo) Save(app *model.WebsiteGroup) error {
|
||||
return getDb().Omit(clause.Associations).Save(app).Error
|
||||
}
|
||||
|
||||
func (w WebsiteGroupRepo) DeleteBy(opts ...DBOption) error {
|
||||
return getDb(opts...).Delete(&model.WebsiteGroup{}).Error
|
||||
}
|
||||
|
||||
func (w WebsiteGroupRepo) CancelDefault() error {
|
||||
return global.DB.Model(&model.WebsiteGroup{}).Where("`default` = 1").Updates(map[string]interface{}{"default": 0}).Error
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -166,6 +167,9 @@ func (a AppService) GetAppDetail(appId uint, version string) (response.AppDetail
|
||||
}
|
||||
|
||||
func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (*model.AppInstall, error) {
|
||||
if err := docker.CreateDefaultDockerNetwork(); err != nil {
|
||||
return nil, buserr.WithDetail(constant.Err1PanelNetworkFailed, err.Error(), nil)
|
||||
}
|
||||
if list, _ := appInstallRepo.ListBy(commonRepo.WithByName(req.Name)); len(list) > 0 {
|
||||
return nil, buserr.New(constant.ErrNameIsExist)
|
||||
}
|
||||
|
||||
@@ -3,9 +3,6 @@ package service
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/env"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx"
|
||||
"github.com/joho/godotenv"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"os"
|
||||
@@ -14,6 +11,10 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/env"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/nginx"
|
||||
"github.com/joho/godotenv"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
@@ -33,7 +34,28 @@ import (
|
||||
type AppInstallService struct {
|
||||
}
|
||||
|
||||
func (a AppInstallService) Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error) {
|
||||
type IAppInstallService interface {
|
||||
Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error)
|
||||
CheckExist(key string) (*response.AppInstalledCheck, error)
|
||||
LoadPort(key string) (int64, error)
|
||||
LoadPassword(key string) (string, error)
|
||||
SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error)
|
||||
Operate(req request.AppInstalledOperate) error
|
||||
Update(req request.AppInstalledUpdate) error
|
||||
SyncAll(systemInit bool) error
|
||||
GetServices(key string) ([]response.AppService, error)
|
||||
GetUpdateVersions(installId uint) ([]dto.AppVersion, error)
|
||||
GetParams(id uint) ([]response.AppParam, error)
|
||||
ChangeAppPort(req request.PortUpdate) error
|
||||
GetDefaultConfigByKey(key string) (string, error)
|
||||
DeleteCheck(installId uint) ([]dto.AppResource, error)
|
||||
}
|
||||
|
||||
func NewIAppInstalledService() IAppInstallService {
|
||||
return &AppInstallService{}
|
||||
}
|
||||
|
||||
func (a *AppInstallService) Page(req request.AppInstalledSearch) (int64, []response.AppInstalledDTO, error) {
|
||||
var opts []repo.DBOption
|
||||
|
||||
if req.Name != "" {
|
||||
@@ -73,7 +95,7 @@ func (a AppInstallService) Page(req request.AppInstalledSearch) (int64, []respon
|
||||
return total, installDTOs, nil
|
||||
}
|
||||
|
||||
func (a AppInstallService) CheckExist(key string) (*response.AppInstalledCheck, error) {
|
||||
func (a *AppInstallService) CheckExist(key string) (*response.AppInstalledCheck, error) {
|
||||
res := &response.AppInstalledCheck{
|
||||
IsExist: false,
|
||||
}
|
||||
@@ -103,7 +125,7 @@ func (a AppInstallService) CheckExist(key string) (*response.AppInstalledCheck,
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppInstallService) LoadPort(key string) (int64, error) {
|
||||
func (a *AppInstallService) LoadPort(key string) (int64, error) {
|
||||
app, err := appInstallRepo.LoadBaseInfo(key, "")
|
||||
if err != nil {
|
||||
return int64(0), nil
|
||||
@@ -111,7 +133,7 @@ func (a AppInstallService) LoadPort(key string) (int64, error) {
|
||||
return app.Port, nil
|
||||
}
|
||||
|
||||
func (a AppInstallService) LoadPassword(key string) (string, error) {
|
||||
func (a *AppInstallService) LoadPassword(key string) (string, error) {
|
||||
app, err := appInstallRepo.LoadBaseInfo(key, "")
|
||||
if err != nil {
|
||||
return "", nil
|
||||
@@ -119,7 +141,7 @@ func (a AppInstallService) LoadPassword(key string) (string, error) {
|
||||
return app.Password, nil
|
||||
}
|
||||
|
||||
func (a AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error) {
|
||||
func (a *AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]response.AppInstalledDTO, error) {
|
||||
var (
|
||||
installs []model.AppInstall
|
||||
err error
|
||||
@@ -152,7 +174,7 @@ func (a AppInstallService) SearchForWebsite(req request.AppInstalledSearch) ([]r
|
||||
return handleInstalled(installs, false)
|
||||
}
|
||||
|
||||
func (a AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
func (a *AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallId))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -196,7 +218,7 @@ func (a AppInstallService) Operate(req request.AppInstalledOperate) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (a AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
installed, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallId))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -270,13 +292,18 @@ func (a AppInstallService) Update(req request.AppInstalledUpdate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AppInstallService) SyncAll() error {
|
||||
func (a *AppInstallService) SyncAll(systemInit bool) error {
|
||||
allList, err := appInstallRepo.ListBy()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, i := range allList {
|
||||
if i.Status == constant.Installing {
|
||||
if systemInit {
|
||||
i.Status = constant.Error
|
||||
i.Message = "System restart causes application exception"
|
||||
_ = appInstallRepo.Save(&i)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := syncById(i.ID); err != nil {
|
||||
@@ -286,7 +313,7 @@ func (a AppInstallService) SyncAll() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AppInstallService) GetServices(key string) ([]response.AppService, error) {
|
||||
func (a *AppInstallService) GetServices(key string) ([]response.AppService, error) {
|
||||
app, err := appRepo.GetFirst(appRepo.WithKey(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -310,7 +337,7 @@ func (a AppInstallService) GetServices(key string) ([]response.AppService, error
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion, error) {
|
||||
func (a *AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion, error) {
|
||||
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
|
||||
var versions []dto.AppVersion
|
||||
if err != nil {
|
||||
@@ -335,7 +362,7 @@ func (a AppInstallService) GetUpdateVersions(installId uint) ([]dto.AppVersion,
|
||||
return versions, nil
|
||||
}
|
||||
|
||||
func (a AppInstallService) ChangeAppPort(req request.PortUpdate) error {
|
||||
func (a *AppInstallService) ChangeAppPort(req request.PortUpdate) error {
|
||||
if common.ScanPort(int(req.Port)) {
|
||||
return buserr.WithDetail(constant.ErrPortInUsed, req.Port, nil)
|
||||
}
|
||||
@@ -363,7 +390,7 @@ func (a AppInstallService) ChangeAppPort(req request.PortUpdate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AppInstallService) DeleteCheck(installId uint) ([]dto.AppResource, error) {
|
||||
func (a *AppInstallService) DeleteCheck(installId uint) ([]dto.AppResource, error) {
|
||||
var res []dto.AppResource
|
||||
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(installId))
|
||||
if err != nil {
|
||||
@@ -404,7 +431,7 @@ func (a AppInstallService) DeleteCheck(installId uint) ([]dto.AppResource, error
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (a AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
|
||||
func (a *AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
|
||||
appInstall, err := getAppInstallByKey(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -426,7 +453,7 @@ func (a AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
|
||||
return string(contentByte), nil
|
||||
}
|
||||
|
||||
func (a AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
|
||||
func (a *AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
|
||||
var (
|
||||
res []response.AppParam
|
||||
appForm dto.AppForm
|
||||
@@ -459,14 +486,20 @@ func (a AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
|
||||
}
|
||||
appParam.LabelZh = form.LabelZh
|
||||
appParam.LabelEn = form.LabelEn
|
||||
appParam.Value = v
|
||||
if form.Type == "service" {
|
||||
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(v.(string)))
|
||||
appParam.Value = appInstall.Name
|
||||
res = append(res, appParam)
|
||||
} else {
|
||||
appParam.Value = v
|
||||
res = append(res, appParam)
|
||||
appParam.ShowValue = appInstall.Name
|
||||
} else if form.Type == "select" {
|
||||
for _, fv := range form.Values {
|
||||
if fv.Value == v {
|
||||
appParam.ShowValue = fv.Label
|
||||
break
|
||||
}
|
||||
}
|
||||
appParam.Values = form.Values
|
||||
}
|
||||
res = append(res, appParam)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
@@ -622,7 +655,7 @@ func updateInstallInfoInDB(appKey, appName, param string, isRestart bool, value
|
||||
}, commonRepo.WithByID(appInstall.ID))
|
||||
}
|
||||
if param == "user-password" {
|
||||
oldVal = fmt.Sprintf("\"PANEL_DB_USER_PASSWORD\":\"%v\"", appInstall.Password)
|
||||
oldVal = fmt.Sprintf("\"PANEL_DB_USER_PASSWORD\":\"%v\"", appInstall.UserPassword)
|
||||
newVal = fmt.Sprintf("\"PANEL_DB_USER_PASSWORD\":\"%v\"", value)
|
||||
_ = appInstallRepo.BatchUpdateBy(map[string]interface{}{
|
||||
"param": strings.ReplaceAll(appInstall.Param, oldVal, newVal),
|
||||
|
||||
@@ -86,9 +86,9 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLogi
|
||||
}
|
||||
pass, err := encrypt.StringDecrypt(passwrodSetting.Value)
|
||||
if err != nil {
|
||||
return nil, constant.ErrAuth
|
||||
return nil, err
|
||||
}
|
||||
if info.Password != pass && nameSetting.Value != info.Name {
|
||||
if info.Password != pass || nameSetting.Value != info.Name {
|
||||
return nil, constant.ErrAuth
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
@@ -33,7 +34,7 @@ type IContainerService interface {
|
||||
PageVolume(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
ListVolume() ([]dto.Options, error)
|
||||
PageCompose(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
CreateCompose(req dto.ComposeCreate) error
|
||||
CreateCompose(req dto.ComposeCreate) (string, error)
|
||||
ComposeOperation(req dto.ComposeOperation) error
|
||||
ContainerCreate(req dto.ContainerCreate) error
|
||||
ContainerOperation(req dto.ContainerOperation) error
|
||||
@@ -239,7 +240,7 @@ func (u *ContainerService) ContainerLogs(req dto.ContainerLog) (string, error) {
|
||||
}
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", errors.New(string(stdout))
|
||||
}
|
||||
return string(stdout), nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -124,46 +125,49 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
return int64(total), BackDatas, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
|
||||
if req.From == "template" {
|
||||
template, err := composeRepo.Get(commonRepo.WithByID(req.Template))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.From = "edit"
|
||||
req.File = template.Content
|
||||
func (u *ContainerService) TestCompose(req dto.ComposeCreate) (bool, error) {
|
||||
if err := u.loadPath(&req); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if req.From == "edit" {
|
||||
dir := fmt.Sprintf("%s/docker/compose/%s", constant.DataDir, req.Name)
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cmd := exec.Command("docker-compose", "-f", req.Path, "config")
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return false, errors.New(string(stdout))
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%s/docker-compose.yml", dir)
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.WriteString(string(req.File))
|
||||
write.Flush()
|
||||
req.Path = path
|
||||
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error) {
|
||||
if err := u.loadPath(&req); err != nil {
|
||||
return "", err
|
||||
}
|
||||
global.LOG.Infof("docker-compose.yml %s create successful, start to docker-compose up", req.Name)
|
||||
|
||||
if req.From == "path" {
|
||||
req.Name = path.Base(strings.ReplaceAll(req.Path, "/docker-compose.yml", ""))
|
||||
req.Name = path.Base(strings.ReplaceAll(req.Path, "/"+path.Base(req.Path), ""))
|
||||
}
|
||||
if stdout, err := compose.Up(req.Path); err != nil {
|
||||
_, _ = compose.Down(req.Path)
|
||||
return errors.New(stdout)
|
||||
logName := path.Dir(req.Path) + "/compose.log"
|
||||
file, err := os.OpenFile(logName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
|
||||
go func() {
|
||||
defer file.Close()
|
||||
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
_, _ = file.Write(stdout)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("docker-compose up %s failed, err: %v", req.Name, err)
|
||||
_, _ = compose.Down(req.Path)
|
||||
_, _ = file.WriteString("docker-compose up failed!")
|
||||
return
|
||||
}
|
||||
global.LOG.Infof("docker-compose up %s successful!", req.Name)
|
||||
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
|
||||
_, _ = file.WriteString("docker-compose up successful!")
|
||||
}()
|
||||
|
||||
return nil
|
||||
return logName, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
||||
@@ -205,3 +209,34 @@ func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) loadPath(req *dto.ComposeCreate) error {
|
||||
if req.From == "template" {
|
||||
template, err := composeRepo.Get(commonRepo.WithByID(req.Template))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.From = "edit"
|
||||
req.File = template.Content
|
||||
}
|
||||
if req.From == "edit" {
|
||||
dir := fmt.Sprintf("%s/docker/compose/%s", constant.DataDir, req.Name)
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%s/docker-compose.yml", dir)
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.WriteString(string(req.File))
|
||||
write.Flush()
|
||||
req.Path = path
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ type ServiceGroup struct {
|
||||
SettingService
|
||||
BackupService
|
||||
|
||||
WebsiteGroupService
|
||||
WebsiteService
|
||||
WebsiteDnsAccountService
|
||||
WebsiteSSLService
|
||||
@@ -68,7 +67,6 @@ var (
|
||||
backupRepo = repo.RepoGroupApp.BackupRepo
|
||||
|
||||
websiteRepo = repo.NewIWebsiteRepo()
|
||||
websiteGroupRepo = repo.RepoGroupApp.WebsiteGroupRepo
|
||||
websiteDomainRepo = repo.RepoGroupApp.WebsiteDomainRepo
|
||||
websiteDnsRepo = repo.RepoGroupApp.WebsiteDnsAccountRepo
|
||||
websiteSSLRepo = repo.NewISSLRepo()
|
||||
|
||||
@@ -22,7 +22,7 @@ func NewIGroupService() IGroupService {
|
||||
}
|
||||
|
||||
func (u *GroupService) List(req dto.GroupSearch) ([]dto.GroupInfo, error) {
|
||||
groups, err := groupRepo.GetList(commonRepo.WithByType(req.Type))
|
||||
groups, err := groupRepo.GetList(commonRepo.WithByType(req.Type), commonRepo.WithOrderBy("is_default desc"), commonRepo.WithOrderBy("created_at desc"))
|
||||
if err != nil {
|
||||
return nil, constant.ErrRecordNotFound
|
||||
}
|
||||
@@ -38,7 +38,7 @@ func (u *GroupService) List(req dto.GroupSearch) ([]dto.GroupInfo, error) {
|
||||
}
|
||||
|
||||
func (u *GroupService) Create(req dto.GroupCreate) error {
|
||||
group, _ := groupRepo.Get(commonRepo.WithByName(req.Name), commonRepo.WithByName(req.Name))
|
||||
group, _ := groupRepo.Get(commonRepo.WithByName(req.Name), commonRepo.WithByType(req.Type))
|
||||
if group.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
}
|
||||
@@ -58,7 +58,7 @@ func (u *GroupService) Delete(id uint) error {
|
||||
}
|
||||
switch group.Type {
|
||||
case "website":
|
||||
websites, _ := websiteRepo.GetBy(commonRepo.WithByGroupID(id))
|
||||
websites, _ := websiteRepo.GetBy(websiteRepo.WithGroupID(id))
|
||||
if len(websites) > 0 {
|
||||
return buserr.New(constant.ErrGroupIsUsed)
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func (u *GroupService) Delete(id uint) error {
|
||||
|
||||
func (u *GroupService) Update(req dto.GroupUpdate) error {
|
||||
if req.IsDefault {
|
||||
if err := groupRepo.CancelDefault(); err != nil {
|
||||
if err := groupRepo.CancelDefault(req.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
return 0, nil, err
|
||||
}
|
||||
if len(req.Info) != 0 {
|
||||
lenth, count := len(list), 0
|
||||
for count < lenth {
|
||||
length, count := len(list), 0
|
||||
for count < length {
|
||||
hasTag := false
|
||||
for _, tag := range list[count].RepoTags {
|
||||
if strings.Contains(tag, req.Info) {
|
||||
@@ -65,7 +65,7 @@ func (u *ImageService) Page(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
}
|
||||
if !hasTag {
|
||||
list = append(list[:count], list[(count+1):]...)
|
||||
lenth--
|
||||
length--
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
@@ -122,6 +122,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fileName := "Dockerfile"
|
||||
if req.From == "edit" {
|
||||
dir := fmt.Sprintf("%s/docker/build/%s", constant.DataDir, strings.ReplaceAll(req.Name, ":", "_"))
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
@@ -141,7 +142,8 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
||||
write.Flush()
|
||||
req.Dockerfile = dir
|
||||
} else {
|
||||
req.Dockerfile = strings.ReplaceAll(req.Dockerfile, "/Dockerfile", "")
|
||||
fileName = path.Base(req.Dockerfile)
|
||||
req.Dockerfile = path.Dir(req.Dockerfile)
|
||||
}
|
||||
tar, err := archive.TarWithOptions(req.Dockerfile+"/", &archive.TarOptions{})
|
||||
if err != nil {
|
||||
@@ -149,7 +151,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
||||
}
|
||||
|
||||
opts := types.ImageBuildOptions{
|
||||
Dockerfile: "Dockerfile",
|
||||
Dockerfile: fileName,
|
||||
Tags: []string{req.Name},
|
||||
Remove: true,
|
||||
Labels: stringsToMap(req.Tags),
|
||||
@@ -179,14 +181,14 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(string(body), "error") && strings.Contains(string(body), "failed:") {
|
||||
if strings.Contains(string(body), "errorDetail") || strings.Contains(string(body), "error:") {
|
||||
global.LOG.Errorf("build image %s failed", req.Name)
|
||||
_, _ = file.Write(body)
|
||||
_, _ = file.WriteString("image build failed!")
|
||||
return
|
||||
}
|
||||
global.LOG.Infof("build image %s successful!", req.Name)
|
||||
_, _ = io.Copy(file, res.Body)
|
||||
_, _ = file.Write(body)
|
||||
_, _ = file.WriteString("image build successful!")
|
||||
}()
|
||||
|
||||
|
||||
@@ -66,6 +66,9 @@ func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("incorrect snapshot name format of %s", snap)
|
||||
}
|
||||
if strings.HasSuffix(snap, ".tar.gz") {
|
||||
snap = strings.ReplaceAll(snap, ".tar.gz", "")
|
||||
}
|
||||
itemSnap := model.Snapshot{
|
||||
Name: snap,
|
||||
From: req.From,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@@ -20,6 +22,7 @@ type UpgradeService struct{}
|
||||
|
||||
type IUpgradeService interface {
|
||||
Upgrade(req dto.Upgrade) error
|
||||
LoadNotes(req dto.Upgrade) (string, error)
|
||||
SearchUpgrade() (*dto.UpgradeInfo, error)
|
||||
}
|
||||
|
||||
@@ -34,36 +37,50 @@ func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
versionRes, err := http.Get(fmt.Sprintf("%s/%s/latest", global.CONF.System.RepoUrl, global.CONF.System.Mode))
|
||||
latestVersion, err := u.loadVersion(true, currentVersion.Value)
|
||||
if err != nil {
|
||||
global.LOG.Infof("load latest version failed, err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
defer versionRes.Body.Close()
|
||||
version, err := ioutil.ReadAll(versionRes.Body)
|
||||
if err != nil {
|
||||
if !common.CompareVersion(string(latestVersion), currentVersion.Value) {
|
||||
return nil, err
|
||||
}
|
||||
isNew := common.CompareVersion(string(version), currentVersion.Value)
|
||||
if !isNew {
|
||||
return nil, err
|
||||
upgrade.LatestVersion = latestVersion
|
||||
if latestVersion[0:4] == currentVersion.Value[0:4] {
|
||||
upgrade.NewVersion = ""
|
||||
} else {
|
||||
newerVersion, err := u.loadVersion(false, currentVersion.Value)
|
||||
if err != nil {
|
||||
global.LOG.Infof("load newer version failed, err: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
if newerVersion == currentVersion.Value {
|
||||
upgrade.NewVersion = ""
|
||||
} else {
|
||||
upgrade.NewVersion = newerVersion
|
||||
}
|
||||
}
|
||||
itemVersion := upgrade.LatestVersion
|
||||
if upgrade.NewVersion != "" {
|
||||
itemVersion = upgrade.NewVersion
|
||||
}
|
||||
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, itemVersion, itemVersion))
|
||||
|
||||
upgrade.NewVersion = string(version)
|
||||
|
||||
releaseNotes, err := http.Get(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, upgrade.NewVersion, upgrade.NewVersion))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("load relase-notes of version %s failed, err: %v", latestVersion, err)
|
||||
}
|
||||
defer releaseNotes.Body.Close()
|
||||
release, err := ioutil.ReadAll(releaseNotes.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
upgrade.ReleaseNote = string(release)
|
||||
|
||||
upgrade.ReleaseNote = notes
|
||||
return &upgrade, nil
|
||||
}
|
||||
|
||||
func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) {
|
||||
notes, err := u.loadReleaseNotes(fmt.Sprintf("%s/%s/%s/release/1panel-%s-release-notes", global.CONF.System.RepoUrl, global.CONF.System.Mode, req.Version, req.Version))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load relase-notes of version %s failed, err: %v", req.Version, err)
|
||||
}
|
||||
return notes, nil
|
||||
}
|
||||
|
||||
func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
|
||||
global.LOG.Info("start to upgrade now...")
|
||||
fileOp := files.NewFileOp()
|
||||
@@ -174,5 +191,49 @@ func (u *UpgradeService) handleRollback(fileOp files.FileOp, originalDir string,
|
||||
if err := cpBinary(originalDir+"/1panel.service", "/etc/systemd/system/1panel.service"); err != nil {
|
||||
global.LOG.Errorf("rollback 1panel failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UpgradeService) loadVersion(isLatest bool, currentVersion string) (string, error) {
|
||||
path := fmt.Sprintf("%s/%s/latest", global.CONF.System.RepoUrl, global.CONF.System.Mode)
|
||||
if !isLatest {
|
||||
path = fmt.Sprintf("%s/%s/latest.current", global.CONF.System.RepoUrl, global.CONF.System.Mode)
|
||||
}
|
||||
latestVersionRes, err := http.Get(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer latestVersionRes.Body.Close()
|
||||
version, err := ioutil.ReadAll(latestVersionRes.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if isLatest {
|
||||
return string(version), nil
|
||||
}
|
||||
|
||||
versionMap := make(map[string]string)
|
||||
if err := json.Unmarshal(version, &versionMap); err != nil {
|
||||
return "", fmt.Errorf("load version map failed, err: %v", err)
|
||||
}
|
||||
|
||||
if len(currentVersion) < 4 {
|
||||
return "", fmt.Errorf("current version is error format: %s", currentVersion)
|
||||
}
|
||||
if version, ok := versionMap[currentVersion[0:4]]; ok {
|
||||
return version, nil
|
||||
}
|
||||
return "", errors.New("load version failed in latest.current")
|
||||
}
|
||||
|
||||
func (u *UpgradeService) loadReleaseNotes(path string) (string, error) {
|
||||
releaseNotes, err := http.Get(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer releaseNotes.Body.Close()
|
||||
release, err := ioutil.ReadAll(releaseNotes.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(release), nil
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
)
|
||||
|
||||
type WebsiteGroupService struct {
|
||||
}
|
||||
|
||||
func (w WebsiteGroupService) CreateGroup(create request.WebsiteGroupCreate) error {
|
||||
groups, _ := websiteGroupRepo.GetBy(commonRepo.WithByName(create.Name))
|
||||
if len(groups) > 0 {
|
||||
return buserr.New(constant.ErrNameIsExist)
|
||||
}
|
||||
return websiteGroupRepo.Create(&model.WebsiteGroup{
|
||||
Name: create.Name,
|
||||
})
|
||||
}
|
||||
|
||||
func (w WebsiteGroupService) GetGroups() ([]model.WebsiteGroup, error) {
|
||||
return websiteGroupRepo.GetBy()
|
||||
}
|
||||
|
||||
func (w WebsiteGroupService) UpdateGroup(update request.WebsiteGroupUpdate) error {
|
||||
if update.Default {
|
||||
if err := websiteGroupRepo.CancelDefault(); err != nil {
|
||||
return err
|
||||
}
|
||||
return websiteGroupRepo.Save(&model.WebsiteGroup{
|
||||
BaseModel: model.BaseModel{
|
||||
ID: update.ID,
|
||||
},
|
||||
Name: update.Name,
|
||||
Default: true,
|
||||
})
|
||||
} else {
|
||||
exists, _ := websiteGroupRepo.GetBy(commonRepo.WithByName(update.Name))
|
||||
for _, exist := range exists {
|
||||
if exist.ID != update.ID {
|
||||
return buserr.New(constant.ErrNameIsExist)
|
||||
}
|
||||
}
|
||||
return websiteGroupRepo.Save(&model.WebsiteGroup{
|
||||
BaseModel: model.BaseModel{
|
||||
ID: update.ID,
|
||||
},
|
||||
Name: update.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (w WebsiteGroupService) DeleteGroup(id uint) error {
|
||||
websites, _ := websiteRepo.GetBy(websiteRepo.WithGroupID(id))
|
||||
if len(websites) > 0 {
|
||||
return buserr.New(constant.ErrGroupIsUsed)
|
||||
}
|
||||
return websiteGroupRepo.DeleteBy(commonRepo.WithByID(id))
|
||||
}
|
||||
@@ -27,6 +27,7 @@ type IWebsiteSSLService interface {
|
||||
GetDNSResolve(req request.WebsiteDNSReq) ([]response.WebsiteDNSRes, error)
|
||||
GetWebsiteSSL(websiteId uint) (response.WebsiteSSLDTO, error)
|
||||
Delete(id uint) error
|
||||
Update(update request.WebsiteSSLUpdate) error
|
||||
}
|
||||
|
||||
func NewIWebsiteSSLService() IWebsiteSSLService {
|
||||
@@ -132,6 +133,7 @@ func (w WebsiteSSLService) Create(create request.WebsiteSSLCreate) (request.Webs
|
||||
websiteSSL.StartDate = cert.NotBefore
|
||||
websiteSSL.Type = cert.Issuer.CommonName
|
||||
websiteSSL.Organization = cert.Issuer.Organization[0]
|
||||
websiteSSL.AutoRenew = create.AutoRenew
|
||||
|
||||
if err := websiteSSLRepo.Create(context.TODO(), &websiteSSL); err != nil {
|
||||
return res, err
|
||||
@@ -258,3 +260,12 @@ func (w WebsiteSSLService) Delete(id uint) error {
|
||||
}
|
||||
return websiteSSLRepo.DeleteBy(commonRepo.WithByID(id))
|
||||
}
|
||||
|
||||
func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
|
||||
websiteSSL, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(update.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
websiteSSL.AutoRenew = update.AutoRenew
|
||||
return websiteSSLRepo.Save(websiteSSL)
|
||||
}
|
||||
|
||||
@@ -50,15 +50,16 @@ var (
|
||||
|
||||
// app
|
||||
var (
|
||||
ErrPortInUsed = "ErrPortInUsed"
|
||||
ErrAppLimit = "ErrAppLimit"
|
||||
ErrAppRequired = "ErrAppRequired"
|
||||
ErrFileCanNotRead = "ErrFileCanNotRead"
|
||||
ErrFileToLarge = "ErrFileToLarge"
|
||||
ErrNotInstall = "ErrNotInstall"
|
||||
ErrPortInOtherApp = "ErrPortInOtherApp"
|
||||
ErrDbUserNotValid = "ErrDbUserNotValid"
|
||||
ErrUpdateBuWebsite = "ErrUpdateBuWebsite"
|
||||
ErrPortInUsed = "ErrPortInUsed"
|
||||
ErrAppLimit = "ErrAppLimit"
|
||||
ErrAppRequired = "ErrAppRequired"
|
||||
ErrFileCanNotRead = "ErrFileCanNotRead"
|
||||
ErrFileToLarge = "ErrFileToLarge"
|
||||
ErrNotInstall = "ErrNotInstall"
|
||||
ErrPortInOtherApp = "ErrPortInOtherApp"
|
||||
ErrDbUserNotValid = "ErrDbUserNotValid"
|
||||
ErrUpdateBuWebsite = "ErrUpdateBuWebsite"
|
||||
Err1PanelNetworkFailed = "Err1PanelNetworkFailed"
|
||||
)
|
||||
|
||||
//website
|
||||
@@ -83,6 +84,7 @@ var (
|
||||
ErrMovePathFailed = "ErrMovePathFailed"
|
||||
ErrLinkPathNotFound = "ErrLinkPathNotFound"
|
||||
ErrFileIsExit = "ErrFileIsExit"
|
||||
ErrFileUpload = "ErrFileUpload"
|
||||
)
|
||||
|
||||
//mysql
|
||||
|
||||
@@ -4,8 +4,9 @@ const (
|
||||
WebRunning = "Running"
|
||||
WebStopped = "Stopped"
|
||||
|
||||
DateLayout = "2006-01-02"
|
||||
DefaultDate = "1970-01-01"
|
||||
DateLayout = "2006-01-02"
|
||||
DateTimeLayout = "2006-01-02 15:04:05"
|
||||
DefaultDate = "1970-01-01"
|
||||
|
||||
ProtocolHTTP = "HTTP"
|
||||
ProtocolHTTPS = "HTTPS"
|
||||
|
||||
@@ -118,6 +118,9 @@ func loadNetIO() {
|
||||
netStatAll2, _ := net.IOCounters(false)
|
||||
for _, net2 := range netStatAll2 {
|
||||
for _, net1 := range netStatAll {
|
||||
if net1.BytesSent == 0 || net1.BytesRecv == 0 {
|
||||
continue
|
||||
}
|
||||
if net2.Name == net1.Name {
|
||||
var itemNet model.MonitorNetwork
|
||||
itemNet.Name = net1.Name
|
||||
|
||||
@@ -3,6 +3,7 @@ package job
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/service"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"time"
|
||||
)
|
||||
@@ -19,18 +20,18 @@ func (ssl *ssl) Run() {
|
||||
sslService := service.NewIWebsiteSSLService()
|
||||
sslList, _ := sslRepo.List()
|
||||
global.LOG.Info("ssl renew cron job start...")
|
||||
now := time.Now()
|
||||
now := time.Now().Add(10 * time.Second)
|
||||
for _, s := range sslList {
|
||||
if !s.AutoRenew || s.Provider == "manual" || s.Provider == "dnsManual" {
|
||||
continue
|
||||
}
|
||||
sum := s.ExpireDate.Sub(now)
|
||||
if sum.Hours() < 168 {
|
||||
expireDate, _ := time.ParseInLocation(constant.DateTimeLayout, s.ExpireDate.String(), time.Now().Location())
|
||||
sum := expireDate.Sub(now)
|
||||
if sum.Hours() < 720 {
|
||||
if err := sslService.Renew(s.ID); err != nil {
|
||||
global.LOG.Errorf("renew doamin [%s] ssl failed err:%s", s.PrimaryDomain, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
global.LOG.Info("ssl renew cron job end...")
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ func NewWebsiteJob() *website {
|
||||
func (w *website) Run() {
|
||||
websites, _ := repo.NewIWebsiteRepo().List()
|
||||
global.LOG.Info("website cron job start...")
|
||||
now := time.Now()
|
||||
now := time.Now().Add(10 * time.Second)
|
||||
if len(websites) > 0 {
|
||||
neverExpireDate, _ := time.Parse(constant.DateLayout, constant.DefaultDate)
|
||||
var wg sync.WaitGroup
|
||||
@@ -28,7 +28,8 @@ func (w *website) Run() {
|
||||
if site.Status != constant.WebRunning || neverExpireDate.Equal(site.ExpireDate) {
|
||||
continue
|
||||
}
|
||||
if site.ExpireDate.Before(now) {
|
||||
expireDate, _ := time.ParseInLocation(constant.DateTimeLayout, site.ExpireDate.String(), time.Now().Location())
|
||||
if expireDate.Before(now) {
|
||||
wg.Add(1)
|
||||
go func(ws model.Website) {
|
||||
stopWebsite(ws.ID, &wg)
|
||||
|
||||
@@ -11,35 +11,35 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func GetMsgWithMap(msg string, maps map[string]interface{}) string {
|
||||
func GetMsgWithMap(key string, maps map[string]interface{}) string {
|
||||
content := ""
|
||||
if maps == nil {
|
||||
content = ginI18n.MustGetMessage(&i18n.LocalizeConfig{
|
||||
MessageID: msg,
|
||||
MessageID: key,
|
||||
})
|
||||
} else {
|
||||
content = ginI18n.MustGetMessage(&i18n.LocalizeConfig{
|
||||
MessageID: msg,
|
||||
MessageID: key,
|
||||
TemplateData: maps,
|
||||
})
|
||||
}
|
||||
content = strings.ReplaceAll(content, ": <no value>", "")
|
||||
if content == "" {
|
||||
return msg
|
||||
return key
|
||||
} else {
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
||||
func GetErrMsg(msg string, maps map[string]interface{}) string {
|
||||
func GetErrMsg(key string, maps map[string]interface{}) string {
|
||||
content := ""
|
||||
if maps == nil {
|
||||
content = ginI18n.MustGetMessage(&i18n.LocalizeConfig{
|
||||
MessageID: msg,
|
||||
MessageID: key,
|
||||
})
|
||||
} else {
|
||||
content = ginI18n.MustGetMessage(&i18n.LocalizeConfig{
|
||||
MessageID: msg,
|
||||
MessageID: key,
|
||||
TemplateData: maps,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ ErrPortInOtherApp: "{{ .port }} port already in use by {{ .apps }}"
|
||||
ErrDbUserNotValid: "Stock database, username and password do not match!"
|
||||
ErrDockerComposeNotValid: "docker-compose file format error!"
|
||||
ErrUpdateBuWebsite: 'The application was updated successfully, but the modification of the website configuration file failed, please check the configuration!'
|
||||
Err1PanelNetworkFailed: 'Default container network creation failed! {{ .detail }}'
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "File can not read"
|
||||
@@ -34,6 +35,7 @@ ErrPathNotFound: "Path is not found"
|
||||
ErrMovePathFailed: "The target path cannot contain the original path!"
|
||||
ErrLinkPathNotFound: "Target path does not exist!"
|
||||
ErrFileIsExit: "File already exists!"
|
||||
ErrFileUpload: "Failed to upload file"
|
||||
|
||||
#website
|
||||
ErrDomainIsExist: "Domain is already exist"
|
||||
|
||||
@@ -26,6 +26,7 @@ ErrPortInOtherApp: "{{ .port }} 端口已被 {{ .apps }}占用!"
|
||||
ErrDbUserNotValid: "存量数据库,用户名密码不匹配!"
|
||||
ErrDockerComposeNotValid: "docker-compose 文件格式错误"
|
||||
ErrUpdateBuWebsite: '应用更新成功,但是网站配置文件修改失败,请检查配置!'
|
||||
Err1PanelNetworkFailed: '默认容器网络创建失败!{{ .detail }}'
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "此文件不支持预览"
|
||||
@@ -34,6 +35,7 @@ ErrPathNotFound: "目录不存在"
|
||||
ErrMovePathFailed: "目标路径不能包含原路径!"
|
||||
ErrLinkPathNotFound: "目标路径不存在!"
|
||||
ErrFileIsExit: "文件已存在!"
|
||||
ErrFileUpload: "上传文件失败"
|
||||
|
||||
#website
|
||||
ErrDomainIsExist: "域名已存在"
|
||||
|
||||
+2
-16
@@ -1,11 +1,11 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"path"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
)
|
||||
|
||||
@@ -22,7 +22,7 @@ func Init() {
|
||||
createDir(fileOp, dir)
|
||||
}
|
||||
|
||||
createDefaultDockerNetwork()
|
||||
_ = docker.CreateDefaultDockerNetwork()
|
||||
}
|
||||
|
||||
func createDir(fileOp files.FileOp, dirPath string) {
|
||||
@@ -30,17 +30,3 @@ func createDir(fileOp files.FileOp, dirPath string) {
|
||||
_ = fileOp.CreateDir(dirPath, 0755)
|
||||
}
|
||||
}
|
||||
|
||||
func createDefaultDockerNetwork() {
|
||||
cli, err := docker.NewClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("init docker client error", err.Error())
|
||||
return
|
||||
}
|
||||
if !cli.NetworkExist("1panel-network") {
|
||||
if err := cli.CreateNetwork("1panel-network"); err != nil {
|
||||
global.LOG.Errorf("init docker client error", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,18 +6,30 @@ import (
|
||||
)
|
||||
|
||||
func Init() {
|
||||
syncApp()
|
||||
syncInstalledApp()
|
||||
}
|
||||
|
||||
func syncApp() {
|
||||
setting, err := service.NewISettingService().GetSettingInfo()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("sync app error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if setting.AppStoreVersion != "" {
|
||||
global.LOG.Info("do not sync")
|
||||
global.LOG.Info("no need to sync")
|
||||
return
|
||||
}
|
||||
global.LOG.Info("sync app start...")
|
||||
if err := service.NewIAppService().SyncAppList(); err != nil {
|
||||
global.LOG.Errorf("sync app error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
global.LOG.Info("sync app successful")
|
||||
}
|
||||
|
||||
func syncInstalledApp() {
|
||||
if err := service.NewIAppInstalledService().SyncAll(true); err != nil {
|
||||
global.LOG.Errorf("sync instaled app error: %s", err.Error())
|
||||
}
|
||||
global.LOG.Info("sync app success")
|
||||
}
|
||||
|
||||
+12
-1
@@ -2,9 +2,11 @@ package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
)
|
||||
@@ -28,6 +30,15 @@ func Init() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_ = db.Exec("PRAGMA journal_mode = WAL;")
|
||||
sqlDB, dbError := db.DB()
|
||||
if dbError != nil {
|
||||
panic(err)
|
||||
}
|
||||
sqlDB.SetConnMaxIdleTime(10)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||
|
||||
global.DB = db
|
||||
global.LOG.Info("init db successfully")
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ func Init() {
|
||||
migrations.AddTableWebsite,
|
||||
migrations.AddTableDatabaseMysql,
|
||||
migrations.AddTableSnap,
|
||||
migrations.AddDefaultGroup,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
||||
@@ -140,7 +140,6 @@ var AddTableSetting = &gormigrate.Migration{
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "SystemStatus", Value: "Free"}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&model.Setting{Key: "AppStoreVersion", Value: ""}).Error; err != nil {
|
||||
@@ -211,14 +210,7 @@ var AddTableDatabaseMysql = &gormigrate.Migration{
|
||||
var AddTableWebsite = &gormigrate.Migration{
|
||||
ID: "20201009-add-table-website",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.AutoMigrate(&model.Website{}, &model.WebsiteDomain{}, &model.WebsiteGroup{}, &model.WebsiteDnsAccount{}, &model.WebsiteSSL{}, &model.WebsiteAcmeAccount{}); err != nil {
|
||||
return err
|
||||
}
|
||||
item := &model.WebsiteGroup{
|
||||
Name: "默认",
|
||||
Default: true,
|
||||
}
|
||||
if err := tx.Create(item).Error; err != nil {
|
||||
if err := tx.AutoMigrate(&model.Website{}, &model.WebsiteDomain{}, &model.WebsiteDnsAccount{}, &model.WebsiteSSL{}, &model.WebsiteAcmeAccount{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -234,3 +226,24 @@ var AddTableSnap = &gormigrate.Migration{
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var AddDefaultGroup = &gormigrate.Migration{
|
||||
ID: "2023022-change-default-group",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
defaultGroup := &model.Group{
|
||||
Name: "默认",
|
||||
IsDefault: true,
|
||||
Type: "website",
|
||||
}
|
||||
if err := tx.Create(defaultGroup).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Model(&model.Group{}).Where("name = ? AND type = ?", "default", "host").Update("name", "默认").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Model(&model.Website{}).Where("1 = 1").Update("website_group_id", defaultGroup.ID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Migrator().DropTable("website_groups")
|
||||
},
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
|
||||
|
||||
baRouter.POST("/compose/search", baseApi.SearchCompose)
|
||||
baRouter.POST("/compose", baseApi.CreateCompose)
|
||||
baRouter.POST("/compose/test", baseApi.TestCompose)
|
||||
baRouter.POST("/compose/operate", baseApi.OperatorCompose)
|
||||
baRouter.POST("/compose/update", baseApi.ComposeUpdate)
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ type WebsiteGroupRouter struct {
|
||||
}
|
||||
|
||||
func (a *WebsiteGroupRouter) InitWebsiteGroupRouter(Router *gin.RouterGroup) {
|
||||
groupRouter := Router.Group("websites/groups")
|
||||
groupRouter := Router.Group("groups")
|
||||
groupRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()).Use(middleware.PasswordExpired())
|
||||
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
groupRouter.GET("", baseApi.GetWebGroups)
|
||||
groupRouter.POST("", baseApi.CreateWebGroup)
|
||||
groupRouter.POST("/update", baseApi.UpdateWebGroup)
|
||||
groupRouter.POST("/del", baseApi.DeleteWebGroup)
|
||||
groupRouter.POST("", baseApi.CreateGroup)
|
||||
groupRouter.POST("/del", baseApi.DeleteGroup)
|
||||
groupRouter.POST("/update", baseApi.UpdateGroup)
|
||||
groupRouter.POST("/search", baseApi.ListGroup)
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,6 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
|
||||
hostRouter.POST("/test/byid/:id", baseApi.TestByID)
|
||||
hostRouter.GET(":id", baseApi.GetHostInfo)
|
||||
|
||||
hostRouter.POST("/group", baseApi.CreateGroup)
|
||||
hostRouter.POST("/group/del", baseApi.DeleteGroup)
|
||||
hostRouter.POST("/group/update", baseApi.UpdateGroup)
|
||||
hostRouter.POST("/group/search", baseApi.ListGroup)
|
||||
|
||||
hostRouter.GET("/command", baseApi.ListCommand)
|
||||
hostRouter.POST("/command", baseApi.CreateCommand)
|
||||
hostRouter.POST("/command/del", baseApi.DeleteCommand)
|
||||
|
||||
@@ -51,6 +51,7 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
|
||||
settingRouter.POST("/backup/record/del", baseApi.DeleteBackupRecord)
|
||||
|
||||
settingRouter.POST("/upgrade", baseApi.Upgrade)
|
||||
settingRouter.POST("/upgrade/notes", baseApi.GetNotesByVersion)
|
||||
settingRouter.GET("/upgrade", baseApi.GetUpgradeInfo)
|
||||
settingRouter.GET("/basedir", baseApi.LoadBaseDir)
|
||||
}
|
||||
|
||||
@@ -22,5 +22,6 @@ func (a *WebsiteSSLRouter) InitWebsiteSSLRouter(Router *gin.RouterGroup) {
|
||||
groupRouter.POST("/del", baseApi.DeleteWebsiteSSL)
|
||||
groupRouter.GET("/website/:websiteId", baseApi.GetWebsiteSSLByWebsiteId)
|
||||
groupRouter.GET("/:id", baseApi.GetWebsiteSSLById)
|
||||
groupRouter.POST("/update", baseApi.UpdateWebsiteSSL)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func Up(filePath string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker-compose -f %s up -d --quiet-pull", filePath)
|
||||
stdout, err := cmd.Execf("docker-compose -f %s up -d", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
@@ -73,3 +74,18 @@ func (c Client) NetworkExist(name string) bool {
|
||||
}
|
||||
return len(networks) > 0
|
||||
}
|
||||
|
||||
func CreateDefaultDockerNetwork() error {
|
||||
cli, err := NewClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("init docker client error %s", err.Error())
|
||||
return err
|
||||
}
|
||||
if !cli.NetworkExist("1panel-network") {
|
||||
if err := cli.CreateNetwork("1panel-network"); err != nil {
|
||||
global.LOG.Errorf("create default docker network error %s", err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+134
-28
@@ -1045,6 +1045,48 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers/compose/test": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "测试 compose 是否可用",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Container Compose"
|
||||
],
|
||||
"summary": "Test compose",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.ComposeCreate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"name"
|
||||
],
|
||||
"formatEN": "check compose [name]",
|
||||
"formatZH": "检测 compose [name] 格式",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers/compose/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -3999,15 +4041,6 @@ var doc = `{
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"path"
|
||||
],
|
||||
"formatEN": "Upload file [path]",
|
||||
"formatZH": "上传文件 [path]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4246,7 +4279,7 @@ var doc = `{
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.FilePath"
|
||||
"$ref": "#/definitions/dto.FilePath"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -6860,17 +6893,28 @@ var doc = `{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "系统更新信息",
|
||||
"description": "获取版本 release notes",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "Load upgrade info",
|
||||
"summary": "Load release notes by version",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.Upgrade"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.UpgradeInfo"
|
||||
}
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8410,6 +8454,57 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/ssl/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新 ssl",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website SSL"
|
||||
],
|
||||
"summary": "Update ssl",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteSSLUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [
|
||||
{
|
||||
"db": "website_ssls",
|
||||
"input_colume": "id",
|
||||
"input_value": "id",
|
||||
"isList": false,
|
||||
"output_colume": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"id"
|
||||
],
|
||||
"formatEN": "Update ssl config [domain]",
|
||||
"formatZH": "更新证书设置 [domain]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/ssl/website/:websiteId": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -10652,6 +10747,9 @@ var doc = `{
|
||||
"dto.UpgradeInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"latestVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"newVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -11441,17 +11539,6 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.FilePath": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"path"
|
||||
],
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.FilePathCheck": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -11987,6 +12074,21 @@ var doc = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteSSLUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"autoRenew",
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"autoRenew": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteSearch": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -12225,10 +12327,14 @@ var doc = `{
|
||||
"rule": {
|
||||
"type": "string"
|
||||
},
|
||||
"showValue": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {}
|
||||
"value": {},
|
||||
"values": {}
|
||||
}
|
||||
},
|
||||
"response.FileInfo": {
|
||||
|
||||
+134
-28
@@ -1031,6 +1031,48 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers/compose/test": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "测试 compose 是否可用",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Container Compose"
|
||||
],
|
||||
"summary": "Test compose",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.ComposeCreate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"name"
|
||||
],
|
||||
"formatEN": "check compose [name]",
|
||||
"formatZH": "检测 compose [name] 格式",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/containers/compose/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -3985,15 +4027,6 @@
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [],
|
||||
"bodyKeys": [
|
||||
"path"
|
||||
],
|
||||
"formatEN": "Upload file [path]",
|
||||
"formatZH": "上传文件 [path]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4232,7 +4265,7 @@
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.FilePath"
|
||||
"$ref": "#/definitions/dto.FilePath"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -6846,17 +6879,28 @@
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "系统更新信息",
|
||||
"description": "获取版本 release notes",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"System Setting"
|
||||
],
|
||||
"summary": "Load upgrade info",
|
||||
"summary": "Load release notes by version",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.Upgrade"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/dto.UpgradeInfo"
|
||||
}
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8396,6 +8440,57 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/ssl/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新 ssl",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website SSL"
|
||||
],
|
||||
"summary": "Update ssl",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteSSLUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [
|
||||
{
|
||||
"db": "website_ssls",
|
||||
"input_colume": "id",
|
||||
"input_value": "id",
|
||||
"isList": false,
|
||||
"output_colume": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"id"
|
||||
],
|
||||
"formatEN": "Update ssl config [domain]",
|
||||
"formatZH": "更新证书设置 [domain]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/ssl/website/:websiteId": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -10638,6 +10733,9 @@
|
||||
"dto.UpgradeInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"latestVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"newVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -11427,17 +11525,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.FilePath": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"path"
|
||||
],
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.FilePathCheck": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -11973,6 +12060,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteSSLUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"autoRenew",
|
||||
"id"
|
||||
],
|
||||
"properties": {
|
||||
"autoRenew": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.WebsiteSearch": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -12211,10 +12313,14 @@
|
||||
"rule": {
|
||||
"type": "string"
|
||||
},
|
||||
"showValue": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {}
|
||||
"value": {},
|
||||
"values": {}
|
||||
}
|
||||
},
|
||||
"response.FileInfo": {
|
||||
|
||||
@@ -1389,6 +1389,8 @@ definitions:
|
||||
type: object
|
||||
dto.UpgradeInfo:
|
||||
properties:
|
||||
latestVersion:
|
||||
type: string
|
||||
newVersion:
|
||||
type: string
|
||||
releaseNote:
|
||||
@@ -1912,13 +1914,6 @@ definitions:
|
||||
showHidden:
|
||||
type: boolean
|
||||
type: object
|
||||
request.FilePath:
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
required:
|
||||
- path
|
||||
type: object
|
||||
request.FilePathCheck:
|
||||
properties:
|
||||
path:
|
||||
@@ -2277,6 +2272,16 @@ definitions:
|
||||
- page
|
||||
- pageSize
|
||||
type: object
|
||||
request.WebsiteSSLUpdate:
|
||||
properties:
|
||||
autoRenew:
|
||||
type: boolean
|
||||
id:
|
||||
type: integer
|
||||
required:
|
||||
- autoRenew
|
||||
- id
|
||||
type: object
|
||||
request.WebsiteSearch:
|
||||
properties:
|
||||
name:
|
||||
@@ -2436,9 +2441,12 @@ definitions:
|
||||
type: string
|
||||
rule:
|
||||
type: string
|
||||
showValue:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
value: {}
|
||||
values: {}
|
||||
type: object
|
||||
response.FileInfo:
|
||||
properties:
|
||||
@@ -3269,6 +3277,33 @@ paths:
|
||||
summary: Page composes
|
||||
tags:
|
||||
- Container Compose
|
||||
/containers/compose/test:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 测试 compose 是否可用
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.ComposeCreate'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Test compose
|
||||
tags:
|
||||
- Container Compose
|
||||
x-panel-log:
|
||||
BeforeFuntions: []
|
||||
bodyKeys:
|
||||
- name
|
||||
formatEN: check compose [name]
|
||||
formatZH: 检测 compose [name] 格式
|
||||
paramKeys: []
|
||||
/containers/compose/update:
|
||||
post:
|
||||
consumes:
|
||||
@@ -5146,13 +5181,6 @@ paths:
|
||||
summary: ChunkUpload file
|
||||
tags:
|
||||
- File
|
||||
x-panel-log:
|
||||
BeforeFuntions: []
|
||||
bodyKeys:
|
||||
- path
|
||||
formatEN: Upload file [path]
|
||||
formatZH: 上传文件 [path]
|
||||
paramKeys: []
|
||||
/files/compress:
|
||||
post:
|
||||
consumes:
|
||||
@@ -5301,7 +5329,7 @@ paths:
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.FilePath'
|
||||
$ref: '#/definitions/dto.FilePath'
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
@@ -6968,15 +6996,22 @@ paths:
|
||||
paramKeys: []
|
||||
/settings/upgrade:
|
||||
get:
|
||||
description: 系统更新信息
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取版本 release notes
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/dto.Upgrade'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/dto.UpgradeInfo'
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Load upgrade info
|
||||
summary: Load release notes by version
|
||||
tags:
|
||||
- System Setting
|
||||
post:
|
||||
@@ -7955,6 +7990,39 @@ paths:
|
||||
summary: Page website ssl
|
||||
tags:
|
||||
- Website SSL
|
||||
/websites/ssl/update:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新 ssl
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.WebsiteSSLUpdate'
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update ssl
|
||||
tags:
|
||||
- Website SSL
|
||||
x-panel-log:
|
||||
BeforeFuntions:
|
||||
- db: website_ssls
|
||||
input_colume: id
|
||||
input_value: id
|
||||
isList: false
|
||||
output_colume: primary_domain
|
||||
output_value: domain
|
||||
bodyKeys:
|
||||
- id
|
||||
formatEN: Update ssl config [domain]
|
||||
formatZH: 更新证书设置 [domain]
|
||||
paramKeys: []
|
||||
/websites/ssl/website/:websiteId:
|
||||
get:
|
||||
consumes:
|
||||
|
||||
@@ -163,5 +163,7 @@ export namespace App {
|
||||
key: string;
|
||||
rule: string;
|
||||
type: string;
|
||||
values?: any;
|
||||
showValue?: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ export namespace Login {
|
||||
export interface MFALoginForm {
|
||||
name: string;
|
||||
password: string;
|
||||
secret: string;
|
||||
code: string;
|
||||
authMethod: string;
|
||||
}
|
||||
@@ -17,7 +16,6 @@ export namespace Login {
|
||||
name: string;
|
||||
token: string;
|
||||
mfaStatus: string;
|
||||
mfaSecret: string;
|
||||
}
|
||||
export interface InitUser {
|
||||
name: string;
|
||||
|
||||
@@ -82,6 +82,7 @@ export namespace Setting {
|
||||
}
|
||||
export interface UpgradeInfo {
|
||||
newVersion: string;
|
||||
latestVersion: string;
|
||||
releaseNote: string;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,17 +79,6 @@ export namespace Website {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface Group extends CommonModel {
|
||||
name: string;
|
||||
default: boolean;
|
||||
}
|
||||
|
||||
export interface GroupOp {
|
||||
name: string;
|
||||
id?: number;
|
||||
default: boolean;
|
||||
}
|
||||
|
||||
export interface Domain {
|
||||
websiteId: number;
|
||||
port: number;
|
||||
@@ -160,6 +149,7 @@ export namespace Website {
|
||||
startDate: string;
|
||||
provider: string;
|
||||
websites?: Website.Website[];
|
||||
autoRenew: boolean;
|
||||
}
|
||||
|
||||
export interface SSLCreate {
|
||||
@@ -179,6 +169,11 @@ export namespace Website {
|
||||
SSLId: number;
|
||||
}
|
||||
|
||||
export interface SSLUpdate {
|
||||
id: number;
|
||||
autoRenew: boolean;
|
||||
}
|
||||
|
||||
export interface AcmeAccount extends CommonModel {
|
||||
email: string;
|
||||
url: string;
|
||||
|
||||
@@ -9,7 +9,7 @@ export const createContainer = (params: Container.ContainerCreate) => {
|
||||
return http.post(`/containers`, params, 1200000);
|
||||
};
|
||||
export const logContainer = (params: Container.ContainerLogSearch) => {
|
||||
return http.post<string>(`/containers/search/log`, params);
|
||||
return http.post<string>(`/containers/search/log`, params, 400000);
|
||||
};
|
||||
export const ContainerStats = (id: string) => {
|
||||
return http.get<Container.ContainerStats>(`/containers/stats/${id}`);
|
||||
@@ -117,7 +117,10 @@ 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, 600000);
|
||||
return http.post<string>(`/containers/compose`, params);
|
||||
};
|
||||
export const testCompose = (params: Container.ComposeCreate) => {
|
||||
return http.post<boolean>(`/containers/compose/test`, params);
|
||||
};
|
||||
export const composeOperator = (params: Container.ComposeOpration) => {
|
||||
return http.post(`/containers/compose/operate`, params);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Group } from '../interface/group';
|
||||
import http from '@/api';
|
||||
|
||||
export const GetGroupList = (params: Group.GroupSearch) => {
|
||||
return http.post<Array<Group.GroupInfo>>(`/groups/search`, params);
|
||||
};
|
||||
export const CreateGroup = (params: Group.GroupCreate) => {
|
||||
return http.post<Group.GroupCreate>(`/groups`, params);
|
||||
};
|
||||
export const UpdateGroup = (params: Group.GroupUpdate) => {
|
||||
return http.post(`/groups/update`, params);
|
||||
};
|
||||
export const DeleteGroup = (id: number) => {
|
||||
return http.post(`/groups/del`, { id: id });
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
import http from '@/api';
|
||||
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';
|
||||
@@ -55,20 +54,6 @@ export const deleteHost = (params: { ids: number[] }) => {
|
||||
return http.post(`/hosts/del`, params);
|
||||
};
|
||||
|
||||
// group
|
||||
export const GetGroupList = (params: Group.GroupSearch) => {
|
||||
return http.post<Array<Group.GroupInfo>>(`/hosts/group/search`, params);
|
||||
};
|
||||
export const CreateGroup = (params: Group.GroupCreate) => {
|
||||
return http.post<Group.GroupCreate>(`/hosts/group`, params);
|
||||
};
|
||||
export const UpdateGroup = (params: Group.GroupUpdate) => {
|
||||
return http.post(`/hosts/group/update`, params);
|
||||
};
|
||||
export const DeleteGroup = (id: number) => {
|
||||
return http.post(`/hosts/group/del`, { id: id });
|
||||
};
|
||||
|
||||
// command
|
||||
export const getCommandList = () => {
|
||||
return http.get<Array<Command.CommandInfo>>(`/hosts/command`, {});
|
||||
|
||||
@@ -139,6 +139,9 @@ export const searchSnapshotPage = (param: SearchWithPage) => {
|
||||
export const loadUpgradeInfo = () => {
|
||||
return http.get<Setting.UpgradeInfo>(`/settings/upgrade`);
|
||||
};
|
||||
export const loadReleaseNotes = (version: string) => {
|
||||
return http.post<string>(`/settings/upgrade/notes`, { version: version });
|
||||
};
|
||||
export const upgrade = (version: string) => {
|
||||
return http.post(`/settings/upgrade`, { version: version });
|
||||
};
|
||||
|
||||
@@ -43,22 +43,6 @@ export const DeleteWebsite = (req: Website.WebSiteDel) => {
|
||||
return http.post<any>(`/websites/del`, req);
|
||||
};
|
||||
|
||||
export const ListGroups = () => {
|
||||
return http.get<Website.Group[]>(`/websites/groups`);
|
||||
};
|
||||
|
||||
export const CreateGroup = (req: Website.GroupOp) => {
|
||||
return http.post<any>(`/websites/groups`, req);
|
||||
};
|
||||
|
||||
export const UpdateGroup = (req: Website.GroupOp) => {
|
||||
return http.post<any>(`/websites/groups/update`, req);
|
||||
};
|
||||
|
||||
export const DeleteGroup = (req: Website.DelReq) => {
|
||||
return http.post<any>(`/websites/groups/del`, req);
|
||||
};
|
||||
|
||||
export const ListDomains = (id: number) => {
|
||||
return http.get<Website.Domain[]>(`/websites/domains/${id}`);
|
||||
};
|
||||
@@ -139,6 +123,10 @@ export const RenewSSL = (req: Website.SSLRenew) => {
|
||||
return http.post<any>(`/websites/ssl/renew`, req);
|
||||
};
|
||||
|
||||
export const UpdateSSL = (req: Website.SSLUpdate) => {
|
||||
return http.post<any>(`/websites/ssl/update`, req);
|
||||
};
|
||||
|
||||
export const GetDnsResolve = (req: Website.DNSResolveReq) => {
|
||||
return http.post<Website.DNSResolve[]>(`/websites/ssl/resolve`, req);
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<el-form ref="groupForm" v-if="row.edit" :model="row">
|
||||
<el-form-item prop="name" v-if="row.edit" :rules="Rules.name">
|
||||
<el-input v-model="row.name"></el-input>
|
||||
<div style="margin-top: 20px; width: 100%"><el-input v-model="row.name" /></div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
@@ -59,7 +59,7 @@
|
||||
import { ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import { CreateGroup, DeleteGroup, GetGroupList, UpdateGroup } from '@/api/modules/host';
|
||||
import { CreateGroup, DeleteGroup, GetGroupList, UpdateGroup } from '@/api/modules/group';
|
||||
import Header from '@/components/drawer-header/index.vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { Group } from '@/api/interface/group';
|
||||
@@ -124,6 +124,7 @@ const saveGroup = async (formEl: FormInstance, group: Group.GroupInfo) => {
|
||||
|
||||
const setDefault = (group: Group.GroupInfo) => {
|
||||
group.isDefault = true;
|
||||
group.type = type.value;
|
||||
UpdateGroup(group).then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||
search();
|
||||
@@ -145,7 +146,7 @@ const openCreate = () => {
|
||||
isDefault: false,
|
||||
edit: true,
|
||||
};
|
||||
data.value.push(g);
|
||||
data.value.unshift(g);
|
||||
};
|
||||
|
||||
const deleteGroup = (index: number) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<span class="version">{{ version }}</span>
|
||||
<el-button v-if="version !== 'Waiting'" type="primary" link @click="onLoadUpgradeInfo">
|
||||
<el-button v-if="version !== 'Waiting'" style="margin-top: -2px" type="primary" link @click="onLoadUpgradeInfo">
|
||||
{{ $t('setting.upgradeCheck') }}
|
||||
</el-button>
|
||||
<el-tag v-else round style="margin-left: 10px">{{ $t('setting.upgrading') }}</el-tag>
|
||||
@@ -12,10 +12,22 @@
|
||||
<DrawerHeader :header="$t('setting.upgrade')" :back="handleClose" />
|
||||
</template>
|
||||
<div class="panel-MdEditor">
|
||||
<el-alert :closable="false">
|
||||
{{ $t('setting.versionHelper') }}
|
||||
<li>{{ $t('setting.versionHelper1') }}</li>
|
||||
<li>{{ $t('setting.versionHelper2') }}</li>
|
||||
</el-alert>
|
||||
<div class="default-theme">
|
||||
<h2 class="inline-block">{{ $t('setting.newVersion') }}</h2>
|
||||
<el-tag class="inline-block tag">{{ upgradeInfo.newVersion }}</el-tag>
|
||||
<h2 class="inline-block">{{ $t('app.version') }}</h2>
|
||||
</div>
|
||||
<el-radio-group class="inline-block tag" v-model="upgradeVersion" @change="changeOption">
|
||||
<el-radio v-if="upgradeInfo.newVersion" :label="upgradeInfo.newVersion">
|
||||
{{ upgradeInfo.newVersion }} {{ $t('setting.newVersion') }}
|
||||
</el-radio>
|
||||
<el-radio :label="upgradeInfo.latestVersion">
|
||||
{{ upgradeInfo.latestVersion }} {{ $t('setting.latestVersion') }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
<MdEditor v-model="upgradeInfo.releaseNote" previewOnly />
|
||||
</div>
|
||||
<template #footer>
|
||||
@@ -27,13 +39,14 @@
|
||||
</el-drawer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { getSettingInfo, loadUpgradeInfo, upgrade } from '@/api/modules/setting';
|
||||
import { getSettingInfo, loadReleaseNotes, loadUpgradeInfo, upgrade } from '@/api/modules/setting';
|
||||
import MdEditor from 'md-editor-v3';
|
||||
import i18n from '@/lang';
|
||||
import 'md-editor-v3/lib/style.css';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const version = ref();
|
||||
@@ -42,6 +55,8 @@ const drawerVisiable = ref(false);
|
||||
const upgradeInfo = ref();
|
||||
const refresh = ref();
|
||||
|
||||
const upgradeVersion = ref();
|
||||
|
||||
const search = async () => {
|
||||
const res = await getSettingInfo();
|
||||
version.value = res.data.systemVersion;
|
||||
@@ -56,12 +71,12 @@ const onLoadUpgradeInfo = async () => {
|
||||
await loadUpgradeInfo()
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
|
||||
if (!res.data) {
|
||||
MsgSuccess(i18n.global.t('setting.noUpgrade'));
|
||||
return;
|
||||
}
|
||||
upgradeInfo.value = res.data;
|
||||
upgradeVersion.value = upgradeInfo.value.newVersion || upgradeInfo.value.latestVersion;
|
||||
drawerVisiable.value = true;
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -69,6 +84,11 @@ const onLoadUpgradeInfo = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const changeOption = async () => {
|
||||
const res = await loadReleaseNotes(upgradeVersion.value);
|
||||
upgradeInfo.value.releaseNote = res.data;
|
||||
};
|
||||
|
||||
const onUpgrade = async () => {
|
||||
ElMessageBox.confirm(i18n.global.t('setting.upgradeHelper', i18n.global.t('setting.upgrade')), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
@@ -76,7 +96,7 @@ const onUpgrade = async () => {
|
||||
type: 'info',
|
||||
}).then(async () => {
|
||||
globalStore.isLoading = true;
|
||||
await upgrade(upgradeInfo.value.newVersion);
|
||||
await upgrade(upgradeVersion.value);
|
||||
drawerVisiable.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
search();
|
||||
@@ -99,7 +119,6 @@ onMounted(() => {
|
||||
height: calc(100vh - 330px);
|
||||
margin-left: 70px;
|
||||
.tag {
|
||||
margin-left: 20px;
|
||||
margin-top: -6px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
<em>{{ $t('database.clickHelper') }}</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<el-progress
|
||||
v-if="isUpload"
|
||||
text-inside
|
||||
:stroke-width="12"
|
||||
:percentage="uploadPrecent"
|
||||
></el-progress>
|
||||
<div v-if="type === 'mysql'" class="el-upload__tip">
|
||||
<span class="input-help">{{ $t('database.supportUpType') }}</span>
|
||||
<span class="input-help">
|
||||
@@ -26,7 +32,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<el-button v-if="uploaderFiles.length === 1" icon="Upload" @click="onSubmit">
|
||||
<el-button :disabled="isUpload" v-if="uploaderFiles.length === 1" icon="Upload" @click="onSubmit">
|
||||
{{ $t('commons.button.upload') }}
|
||||
</el-button>
|
||||
|
||||
@@ -82,11 +88,13 @@ import i18n from '@/lang';
|
||||
import { UploadFile, UploadFiles, UploadInstance } from 'element-plus';
|
||||
import { File } from '@/api/interface/file';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { BatchDeleteFile, CheckFile, GetUploadList, UploadFileData } from '@/api/modules/files';
|
||||
import { BatchDeleteFile, CheckFile, ChunkUploadFileData, GetUploadList } from '@/api/modules/files';
|
||||
import { loadBaseDir } from '@/api/modules/setting';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
|
||||
const loading = ref();
|
||||
const isUpload = ref();
|
||||
const uploadPrecent = ref<number>(0);
|
||||
const selects = ref<any>([]);
|
||||
const baseDir = ref();
|
||||
|
||||
@@ -166,20 +174,12 @@ const beforeAvatarUpload = (rawFile) => {
|
||||
MsgError(i18n.global.t('commons.msg.unSupportType'));
|
||||
return false;
|
||||
}
|
||||
if (rawFile.size / 1024 / 1024 > 50) {
|
||||
MsgError(i18n.global.t('commons.msg.unSupportSize', [50]));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!rawFile.name.endsWith('.sql') && !rawFile.name.endsWith('.tar.gz') && !rawFile.name.endsWith('.sql.gz')) {
|
||||
MsgError(i18n.global.t('commons.msg.unSupportType'));
|
||||
return false;
|
||||
}
|
||||
if (rawFile.size / 1024 / 1024 > 10) {
|
||||
MsgError(i18n.global.t('commons.msg.unSupportSize', [10]));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -194,42 +194,73 @@ const handleClose = () => {
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
const formData = new FormData();
|
||||
if (uploaderFiles.value.length !== 1) {
|
||||
return;
|
||||
}
|
||||
if (!uploaderFiles.value[0]!.raw.name) {
|
||||
const file = uploaderFiles.value[0];
|
||||
if (!file.raw.name) {
|
||||
MsgError(i18n.global.t('commons.msg.fileNameErr'));
|
||||
return;
|
||||
}
|
||||
let reg = /^[a-zA-Z0-9\u4e00-\u9fa5]{1}[a-z:A-Z0-9_.\u4e00-\u9fa5-]{0,256}$/;
|
||||
if (!reg.test(uploaderFiles.value[0]!.raw.name)) {
|
||||
if (!reg.test(file.raw.name)) {
|
||||
MsgError(i18n.global.t('commons.msg.fileNameErr'));
|
||||
return;
|
||||
}
|
||||
const res = await CheckFile(baseDir.value + uploaderFiles.value[0]!.raw.name);
|
||||
const res = await CheckFile(baseDir.value + file.raw.name);
|
||||
if (!res.data) {
|
||||
MsgError(i18n.global.t('commons.msg.fileExist'));
|
||||
return;
|
||||
}
|
||||
formData.append('file', uploaderFiles.value[0]!.raw);
|
||||
let isOk = beforeAvatarUpload(uploaderFiles.value[0]!.raw);
|
||||
let isOk = beforeAvatarUpload(file.raw);
|
||||
if (!isOk) {
|
||||
return;
|
||||
}
|
||||
formData.append('path', baseDir.value);
|
||||
loading.value = true;
|
||||
UploadFileData(formData, {})
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
submitUpload(file);
|
||||
};
|
||||
|
||||
const submitUpload = async (file: any) => {
|
||||
isUpload.value = true;
|
||||
const CHUNK_SIZE = 1024 * 1024;
|
||||
const fileSize = file.size;
|
||||
const chunkCount = Math.ceil(fileSize / CHUNK_SIZE);
|
||||
let uploadedChunkCount = 0;
|
||||
|
||||
for (let i = 0; i < chunkCount; i++) {
|
||||
const start = i * CHUNK_SIZE;
|
||||
const end = Math.min(start + CHUNK_SIZE, fileSize);
|
||||
const chunk = file.raw.slice(start, end);
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('filename', file.name);
|
||||
formData.append('path', baseDir.value);
|
||||
formData.append('chunk', chunk);
|
||||
formData.append('chunkIndex', i.toString());
|
||||
formData.append('chunkCount', chunkCount.toString());
|
||||
|
||||
try {
|
||||
await ChunkUploadFileData(formData, {
|
||||
onUploadProgress: (progressEvent) => {
|
||||
const progress = Math.round(
|
||||
((uploadedChunkCount + progressEvent.loaded / progressEvent.total) * 100) / chunkCount,
|
||||
);
|
||||
uploadPrecent.value = progress;
|
||||
},
|
||||
});
|
||||
uploadedChunkCount++;
|
||||
} catch (error) {
|
||||
isUpload.value = false;
|
||||
break;
|
||||
}
|
||||
if (uploadedChunkCount == chunkCount) {
|
||||
isUpload.value = false;
|
||||
uploadRef.value?.clearFiles();
|
||||
uploaderFiles.value = [];
|
||||
MsgSuccess(i18n.global.t('file.uploadSuccess'));
|
||||
search();
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onBatchDelete = async (row: File.File | null) => {
|
||||
|
||||
@@ -120,7 +120,12 @@ const message = {
|
||||
warnning:
|
||||
'Note: [Closing the security entrance] will make your panel login address directly exposed to the Internet, very dangerous, please exercise caution',
|
||||
codeInput: 'Please enter the 6-digit verification code of the MFA validator',
|
||||
mfaTitle: 'MFA Certification',
|
||||
mfaCode: 'MFA verification code',
|
||||
title: 'Linux Server Management Panel',
|
||||
licenseHelper:
|
||||
'Agree to FIT2CLOUD « <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank">Community Software License Agreement</a> »',
|
||||
errorAgree: 'Please click to agree to the Community Software License Agreement',
|
||||
},
|
||||
rule: {
|
||||
username: 'Please enter a username',
|
||||
@@ -226,7 +231,7 @@ const message = {
|
||||
io: 'Disk IO',
|
||||
totalSend: 'Total send',
|
||||
totalRecv: 'Total recv',
|
||||
rwPerSecond: 'RW per second',
|
||||
rwPerSecond: 'IO times',
|
||||
ioDelay: 'IO delay',
|
||||
time: 'Times',
|
||||
uptime: 'Up Time',
|
||||
@@ -302,7 +307,7 @@ const message = {
|
||||
'This port is the exposed port of the container. You need to save the modification separately and restart the container!',
|
||||
|
||||
selectFile: 'Select file',
|
||||
supportUpType: 'Only sql, sql.gz, and tar.gz files within 10 MB are supported',
|
||||
supportUpType: 'Only sql, sql.gz, and tar.gz files are supported',
|
||||
zipFormat: 'tar.gz compressed package structure: test.tar.gz compressed package must contain test.sql',
|
||||
|
||||
currentStatus: 'Current state',
|
||||
@@ -518,6 +523,8 @@ const message = {
|
||||
registrieHelper: 'One in a row, for example:\n172.16.10.111:8081 \n172.16.10.112:8081',
|
||||
|
||||
compose: 'Compose',
|
||||
composeHelper:
|
||||
'The current content has passed the format verification. Please click Submit to complete the creation',
|
||||
apps: 'Apps',
|
||||
local: 'Local',
|
||||
createCompose: 'Create compose',
|
||||
@@ -851,7 +858,12 @@ const message = {
|
||||
upgrading: 'Please wait while the upgrade is underway...',
|
||||
upgradeHelper: 'The upgrade requires a service restart. Do you want to continue?',
|
||||
noUpgrade: 'It is currently the latest version',
|
||||
newVersion: 'Latest version',
|
||||
versionHelper:
|
||||
'Name rules: [major version].[functional version].[Bug fix version], as shown in the following example:',
|
||||
versionHelper1: 'v1.0.1 is a Bug fix after v1.0.0',
|
||||
versionHelper2: 'v1.1.0 is a feature release after v1.0.0',
|
||||
newVersion: '(Bug fix version)',
|
||||
latestVersion: '(Functional version)',
|
||||
upgradeCheck: 'Check for updates',
|
||||
upgradeNotes: 'Release note',
|
||||
upgradeNow: 'Upgrade now',
|
||||
@@ -955,7 +967,7 @@ const message = {
|
||||
type: 'Type',
|
||||
static: 'Static',
|
||||
deployment: 'Deployment',
|
||||
supportUpType: 'Only .tar.gz files within 50 MB are supported',
|
||||
supportUpType: 'Only .tar.gz files are supported',
|
||||
zipFormat: '.tar.gz compressed package structure: test.tar.gz compressed package must contain {0} file',
|
||||
proxy: 'Reverse Proxy',
|
||||
alias: 'Path Name',
|
||||
|
||||
@@ -123,8 +123,12 @@ const message = {
|
||||
solutionHelper: '在 SSH 终端输入以下一种命令来解决 1.查看面板入口:/etc/init.d/bt default',
|
||||
warnning: '注意:【关闭安全入口】将使您的面板登录地址被直接暴露在互联网上,非常危险,请谨慎操作',
|
||||
codeInput: '请输入 MFA 验证器的 6 位验证码',
|
||||
mfaTitle: 'MFA认证',
|
||||
mfaTitle: 'MFA 认证',
|
||||
mfaCode: 'MFA 验证码',
|
||||
title: 'Linux 服务器运维管理面板',
|
||||
licenseHelper:
|
||||
'同意 FIT2CLOUD 飞致云 « <a href="https://www.fit2cloud.com/legal/licenses.html" target="_blank"> 社区软件许可协议</a> »',
|
||||
errorAgree: '请点击同意社区软件许可协议',
|
||||
},
|
||||
rule: {
|
||||
username: '请输入用户名',
|
||||
@@ -231,8 +235,8 @@ const message = {
|
||||
io: '磁盘 IO',
|
||||
totalSend: '总发送',
|
||||
totalRecv: '总接收',
|
||||
rwPerSecond: '每秒读写',
|
||||
ioDelay: 'IO 延迟',
|
||||
rwPerSecond: '读写次数',
|
||||
ioDelay: '读写延迟',
|
||||
time: '次',
|
||||
uptime: '启动时间',
|
||||
runningTime: '运行时间',
|
||||
@@ -308,7 +312,7 @@ const message = {
|
||||
selectFile: '选择文件',
|
||||
dropHelper: '将上传文件拖拽到此处,或者',
|
||||
clickHelper: '点击上传',
|
||||
supportUpType: '仅支持 10M 以内 sql、sql.gz、tar.gz 文件',
|
||||
supportUpType: '仅支持 sql、sql.gz、tar.gz 文件',
|
||||
zipFormat: 'tar.gz 压缩包结构:test.tar.gz 压缩包内,必需包含 test.sql',
|
||||
|
||||
currentStatus: '当前状态',
|
||||
@@ -529,6 +533,7 @@ const message = {
|
||||
registrieHelper: '一行一个,例:\n172.16.10.111:8081 \n172.16.10.112:8081',
|
||||
|
||||
compose: '编排',
|
||||
composeHelper: '当前内容已通过格式验证,请点击确认完成创建',
|
||||
composePathHelper: '容器编排将保存在: {0}',
|
||||
apps: '应用商店',
|
||||
local: '本地',
|
||||
@@ -836,7 +841,11 @@ const message = {
|
||||
upgrading: '正在升级中,请稍候...',
|
||||
upgradeHelper: '升级操作需要重启服务,是否继续?',
|
||||
noUpgrade: '当前已经是最新版本',
|
||||
newVersion: '最新版本',
|
||||
versionHelper: '1Panel 版本号命名规则为: [大版本].[功能版本].[Bug 修复版本],示例如下:',
|
||||
versionHelper1: 'v1.0.1 是 v1.0.0 之后的 Bug 修复版本',
|
||||
versionHelper2: 'v1.1.0 是 v1.0.0 之后的功能版本',
|
||||
newVersion: '(Bug 修复版本)',
|
||||
latestVersion: '(功能版本)',
|
||||
upgradeCheck: '检查更新',
|
||||
upgradeNotes: '更新内容',
|
||||
upgradeNow: '立即更新',
|
||||
@@ -963,7 +972,7 @@ const message = {
|
||||
type: '类型',
|
||||
static: '静态网站',
|
||||
deployment: '一键部署',
|
||||
supportUpType: '仅支持 50M 以内 .tar.gz 文件',
|
||||
supportUpType: '仅支持 .tar.gz 文件',
|
||||
zipFormat: '.tar.gz 压缩包结构:test.tar.gz 压缩包内,必需包含 {0} 文件',
|
||||
proxy: '反向代理',
|
||||
alias: '代号',
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<div v-if="slots.prompt" class="prompt">
|
||||
<slot name="prompt"></slot>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="main-content">
|
||||
<slot name="main"></slot>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -120,10 +120,6 @@ const showBack = computed(() => {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.main {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.prompt {
|
||||
margin-top: 10px;
|
||||
}
|
||||
@@ -137,4 +133,8 @@ const showBack = computed(() => {
|
||||
.main-box {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,6 +20,7 @@ export const GlobalStore = defineStore({
|
||||
footer: true,
|
||||
},
|
||||
isFullScreen: false,
|
||||
agreeLicense: false,
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
@@ -45,6 +46,9 @@ export const GlobalStore = defineStore({
|
||||
setThemeConfig(themeConfig: ThemeConfigProp) {
|
||||
this.themeConfig = themeConfig;
|
||||
},
|
||||
setAgreeLicense(agree: boolean) {
|
||||
this.agreeLicense = agree;
|
||||
},
|
||||
},
|
||||
persist: piniaPersistConfig('GlobalState'),
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface GlobalState {
|
||||
// assemblySize: string; // small | default | large
|
||||
themeConfig: ThemeConfigProp;
|
||||
isFullScreen: boolean;
|
||||
agreeLicense: boolean;
|
||||
}
|
||||
|
||||
export interface MenuState {
|
||||
|
||||
@@ -179,9 +179,12 @@
|
||||
top: 220px;
|
||||
left: 45%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 300px;
|
||||
width: 400px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
.bt {
|
||||
margin-top: -2px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-button {
|
||||
|
||||
@@ -42,7 +42,14 @@
|
||||
{{ $t('app.toInstall') }}
|
||||
</el-link>
|
||||
</span>
|
||||
|
||||
<el-select v-model="form[p.envKey]" v-if="p.type == 'select'">
|
||||
<el-option
|
||||
v-for="service in p.values"
|
||||
:key="service.label"
|
||||
:value="service.value"
|
||||
:label="service.label"
|
||||
></el-option>
|
||||
</el-select>
|
||||
<el-row :gutter="10" v-if="p.type == 'apps'">
|
||||
<el-col :span="12">
|
||||
<el-form-item :prop="p.prop">
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</template>
|
||||
<el-descriptions border :column="1" v-if="!edit">
|
||||
<el-descriptions-item v-for="(param, key) in params" :label="getLabel(param)" :key="key">
|
||||
{{ param.value }}
|
||||
<span>{{ param.showValue && param.showValue != '' ? param.showValue : param.value }}</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-row v-else v-loading="loading">
|
||||
@@ -26,6 +26,15 @@
|
||||
v-model.number="paramModel[p.key]"
|
||||
:disabled="!p.edit"
|
||||
></el-input>
|
||||
<el-select v-model="paramModel[p.key]" v-else-if="p.type == 'select'">
|
||||
<el-option
|
||||
v-for="value in p.values"
|
||||
:key="value.label"
|
||||
:value="value.value"
|
||||
:label="value.label"
|
||||
:disabled="!p.edit"
|
||||
></el-option>
|
||||
</el-select>
|
||||
<el-input v-else v-model.trim="paramModel[p.key]" :disabled="!p.edit"></el-input>
|
||||
</el-form-item>
|
||||
</div>
|
||||
@@ -116,6 +125,8 @@ const get = async () => {
|
||||
edit: d.edit,
|
||||
key: d.key,
|
||||
type: d.type,
|
||||
values: d.values,
|
||||
showValue: d.showValue,
|
||||
});
|
||||
rules[d.key] = [Rules.requiredInput];
|
||||
if (d.rule) {
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
<template>
|
||||
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
|
||||
<el-drawer
|
||||
v-model="drawerVisiable"
|
||||
@close="handleClose"
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
size="50%"
|
||||
>
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('container.compose')" :back="handleClose" />
|
||||
</template>
|
||||
<div v-loading="loading">
|
||||
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" label-width="80px">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item :label="$t('container.from')">
|
||||
<el-radio-group v-model="form.from">
|
||||
<el-radio-group v-model="form.from" @change="hasChecked = false">
|
||||
<el-radio label="edit">{{ $t('commons.button.edit') }}</el-radio>
|
||||
<el-radio label="path">{{ $t('container.pathSelect') }}</el-radio>
|
||||
<el-radio label="template">{{ $t('container.composeTemplate') }}</el-radio>
|
||||
@@ -31,7 +37,7 @@
|
||||
<span class="input-help">{{ $t('container.composePathHelper', [composeFile]) }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.from === 'template'" prop="template">
|
||||
<el-select v-model="form.template">
|
||||
<el-select v-model="form.template" @change="hasChecked = false">
|
||||
<el-option
|
||||
v-for="item in templateOptions"
|
||||
:key="item.id"
|
||||
@@ -46,25 +52,61 @@
|
||||
placeholder="#Define or paste the content of your docker-compose file here"
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
style="width: 100%; height: calc(100vh - 340px)"
|
||||
style="width: 100%; height: 250px"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
theme="cobalt"
|
||||
@change="hasChecked = false"
|
||||
:styleActiveLine="true"
|
||||
:extensions="extensions"
|
||||
v-model="form.file"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-form>
|
||||
<codemirror
|
||||
v-if="logVisiable && form.from !== 'edit'"
|
||||
:autofocus="true"
|
||||
placeholder="Waiting for docker-compose up output..."
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
style="height: calc(100vh - 370px)"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
theme="cobalt"
|
||||
:styleActiveLine="true"
|
||||
:extensions="extensions"
|
||||
@ready="handleReady"
|
||||
v-model="logInfo"
|
||||
:readOnly="true"
|
||||
/>
|
||||
<codemirror
|
||||
v-if="logVisiable && form.from === 'edit'"
|
||||
:autofocus="true"
|
||||
placeholder="Waiting for docker-compose up output..."
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
style="height: calc(100vh - 590px)"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
theme="cobalt"
|
||||
:styleActiveLine="true"
|
||||
:extensions="extensions"
|
||||
@ready="handleReady"
|
||||
v-model="logInfo"
|
||||
:readOnly="true"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button :disabled="loading" @click="drawerVisiable = false">
|
||||
<el-button @click="drawerVisiable = false">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" :disabled="loading" @click="onSubmit(formRef)">
|
||||
<el-button :disabled="buttonDisabled" @click="onTest(formRef)">
|
||||
{{ $t('commons.button.verify') }}
|
||||
</el-button>
|
||||
<el-button type="primary" :disabled="buttonDisabled || !hasChecked" @click="onSubmit(formRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
@@ -73,7 +115,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { nextTick, reactive, ref, shallowRef } from 'vue';
|
||||
import FileList from '@/components/file-list/index.vue';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
@@ -82,24 +124,33 @@ import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { listComposeTemplate, upCompose } from '@/api/modules/container';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { listComposeTemplate, testCompose, upCompose } from '@/api/modules/container';
|
||||
import { loadBaseDir } from '@/api/modules/setting';
|
||||
import { LoadFile } from '@/api/modules/files';
|
||||
import { formatImageStdout } from '@/utils/docker';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
|
||||
const loading = ref();
|
||||
|
||||
const extensions = [javascript(), oneDark];
|
||||
const view = shallowRef();
|
||||
const handleReady = (payload) => {
|
||||
view.value = payload.view;
|
||||
};
|
||||
const logVisiable = ref();
|
||||
const logInfo = ref();
|
||||
|
||||
const drawerVisiable = ref(false);
|
||||
const templateOptions = ref();
|
||||
const buttonDisabled = ref(false);
|
||||
|
||||
const loading = ref(false);
|
||||
const baseDir = ref();
|
||||
const composeFile = ref();
|
||||
|
||||
const varifyPath = (rule: any, value: any, callback: any) => {
|
||||
if (value.indexOf('docker-compose.yml') === -1) {
|
||||
callback(new Error(i18n.global.t('commons.rule.selectHelper', ['docker-compose.yml'])));
|
||||
}
|
||||
callback();
|
||||
};
|
||||
const hasChecked = ref();
|
||||
|
||||
let timer: NodeJS.Timer | null = null;
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
from: 'edit',
|
||||
@@ -109,7 +160,7 @@ const form = reactive({
|
||||
});
|
||||
const rules = reactive({
|
||||
name: [Rules.requiredInput, Rules.imageName],
|
||||
path: [Rules.requiredSelect, { validator: varifyPath, trigger: 'change', required: true }],
|
||||
path: [Rules.requiredSelect],
|
||||
});
|
||||
|
||||
const loadTemplates = async () => {
|
||||
@@ -126,12 +177,18 @@ const acceptParams = (): void => {
|
||||
form.from = 'edit';
|
||||
form.path = '';
|
||||
form.file = '';
|
||||
logVisiable.value = false;
|
||||
hasChecked.value = false;
|
||||
logInfo.value = '';
|
||||
loadTemplates();
|
||||
loadPath();
|
||||
};
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
const handleClose = () => {
|
||||
emit('search');
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
drawerVisiable.value = false;
|
||||
};
|
||||
|
||||
@@ -148,26 +205,64 @@ const changePath = async () => {
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
const onTest = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
loading.value = true;
|
||||
upCompose(form)
|
||||
.then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
await testCompose(form)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
emit('search');
|
||||
drawerVisiable.value = false;
|
||||
if (res.data) {
|
||||
MsgSuccess(i18n.global.t('container.composeHelper'));
|
||||
hasChecked.value = true;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
const res = await upCompose(form);
|
||||
logInfo.value = '';
|
||||
buttonDisabled.value = true;
|
||||
logVisiable.value = true;
|
||||
loadLogs(res.data);
|
||||
});
|
||||
};
|
||||
|
||||
const loadLogs = async (path: string) => {
|
||||
timer = setInterval(async () => {
|
||||
if (logVisiable.value) {
|
||||
const res = await LoadFile({ path: path });
|
||||
logInfo.value = formatImageStdout(res.data);
|
||||
nextTick(() => {
|
||||
const state = view.value.state;
|
||||
view.value.dispatch({
|
||||
selection: { anchor: state.doc.length, head: state.doc.length },
|
||||
scrollIntoView: true,
|
||||
});
|
||||
});
|
||||
if (
|
||||
logInfo.value.endsWith('docker-compose up failed!') ||
|
||||
logInfo.value.endsWith('docker-compose up successful!')
|
||||
) {
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
buttonDisabled.value = false;
|
||||
}
|
||||
}
|
||||
}, 1000 * 3);
|
||||
};
|
||||
|
||||
const loadDir = async (path: string) => {
|
||||
form.path = path;
|
||||
hasChecked.value = false;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
|
||||
@@ -3,20 +3,13 @@
|
||||
<div v-show="isOnDetail">
|
||||
<ComposeDetial @back="backList" ref="composeDetailRef" />
|
||||
</div>
|
||||
<el-card width="30%" v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span style="font-size: 14px">{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link style="font-size: 14px; margin-bottom: 5px" @click="goSetting">
|
||||
【 {{ $t('container.setting') }} 】
|
||||
</el-button>
|
||||
<span style="font-size: 14px">{{ $t('container.startIn') }}</span>
|
||||
<el-card v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span>{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link class="bt" @click="goSetting">【 {{ $t('container.setting') }} 】</el-button>
|
||||
<span>{{ $t('container.startIn') }}</span>
|
||||
</el-card>
|
||||
|
||||
<LayoutContent
|
||||
v-loading="loading"
|
||||
v-if="!isOnDetail"
|
||||
:title="$t('container.compose')"
|
||||
:class="{ mask: dockerStatus != 'Running' }"
|
||||
>
|
||||
<LayoutContent v-if="!isOnDetail" :title="$t('container.compose')" :class="{ mask: dockerStatus != 'Running' }">
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
@@ -111,13 +104,21 @@ const paginationConfig = reactive({
|
||||
});
|
||||
const searchName = ref();
|
||||
|
||||
const dockerStatus = ref();
|
||||
const dockerStatus = ref('Running');
|
||||
const loadStatus = async () => {
|
||||
const res = await loadDockerStatus();
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
}
|
||||
loading.value = true;
|
||||
await loadDockerStatus()
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
dockerStatus.value = 'Failed';
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const goSetting = async () => {
|
||||
router.push({ name: 'ContainerSetting' });
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card width="30%" v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span style="font-size: 14px">{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link style="font-size: 14px; margin-bottom: 5px" @click="goSetting">
|
||||
【 {{ $t('container.setting') }} 】
|
||||
</el-button>
|
||||
<span style="font-size: 14px">{{ $t('container.startIn') }}</span>
|
||||
<div v-loading="loading">
|
||||
<el-card v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span>{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" class="bt" link @click="goSetting">【 {{ $t('container.setting') }} 】</el-button>
|
||||
<span>{{ $t('container.startIn') }}</span>
|
||||
</el-card>
|
||||
<LayoutContent
|
||||
v-loading="loading"
|
||||
:title="$t('container.container')"
|
||||
:class="{ mask: dockerStatus != 'Running' }"
|
||||
>
|
||||
<LayoutContent :title="$t('container.container')" :class="{ mask: dockerStatus != 'Running' }">
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
@@ -141,13 +135,21 @@ const paginationConfig = reactive({
|
||||
});
|
||||
const searchName = ref();
|
||||
|
||||
const dockerStatus = ref();
|
||||
const dockerStatus = ref('Running');
|
||||
const loadStatus = async () => {
|
||||
const res = await loadDockerStatus();
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
}
|
||||
loading.value = true;
|
||||
await loadDockerStatus()
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
dockerStatus.value = 'Failed';
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const goSetting = async () => {
|
||||
router.push({ name: 'ContainerSetting' });
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<el-drawer
|
||||
v-model="drawerVisiable"
|
||||
:destroy-on-close="true"
|
||||
@close="onCloseLog"
|
||||
@close="handleClose"
|
||||
:close-on-click-modal="false"
|
||||
size="50%"
|
||||
>
|
||||
@@ -116,16 +116,11 @@ const form = reactive({
|
||||
tagStr: '',
|
||||
tags: [] as Array<string>,
|
||||
});
|
||||
const varifyPath = (rule: any, value: any, callback: any) => {
|
||||
if (value.indexOf('Dockerfile') === -1) {
|
||||
callback(new Error(i18n.global.t('commons.rule.selectHelper', ['Dockerfile'])));
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
const rules = reactive({
|
||||
name: [Rules.requiredInput, Rules.imageName],
|
||||
from: [Rules.requiredSelect],
|
||||
dockerfile: [Rules.requiredInput, { validator: varifyPath, trigger: 'change', required: true }],
|
||||
dockerfile: [Rules.requiredInput],
|
||||
});
|
||||
const acceptParams = async () => {
|
||||
logVisiable.value = false;
|
||||
@@ -141,6 +136,9 @@ const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
const handleClose = () => {
|
||||
drawerVisiable.value = false;
|
||||
emit('search');
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
};
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
@@ -181,11 +179,6 @@ const loadLogs = async (path: string) => {
|
||||
}
|
||||
}, 1000 * 3);
|
||||
};
|
||||
const onCloseLog = async () => {
|
||||
emit('search');
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
};
|
||||
|
||||
const loadBuildDir = async (path: string) => {
|
||||
form.dockerfile = path;
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<template>
|
||||
<div v-loading="loading">
|
||||
<el-card width="30%" v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span style="font-size: 14px">{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link style="font-size: 14px; margin-bottom: 5px" @click="goSetting">
|
||||
【 {{ $t('container.setting') }} 】
|
||||
</el-button>
|
||||
<span style="font-size: 14px">{{ $t('container.startIn') }}</span>
|
||||
<el-card v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span>{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link class="bt" @click="goSetting">【 {{ $t('container.setting') }} 】</el-button>
|
||||
<span>{{ $t('container.startIn') }}</span>
|
||||
</el-card>
|
||||
|
||||
<LayoutContent v-loading="loading" :title="$t('container.image')" :class="{ mask: dockerStatus != 'Running' }">
|
||||
<LayoutContent :title="$t('container.image')" :class="{ mask: dockerStatus != 'Running' }">
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
@@ -110,14 +108,22 @@ const paginationConfig = reactive({
|
||||
});
|
||||
const searchName = ref();
|
||||
|
||||
const dockerStatus = ref();
|
||||
const dockerStatus = ref('Running');
|
||||
const loadStatus = async () => {
|
||||
const res = await loadDockerStatus();
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
loadRepos();
|
||||
}
|
||||
loading.value = true;
|
||||
await loadDockerStatus()
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
loadRepos();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
dockerStatus.value = 'Failed';
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const goSetting = async () => {
|
||||
router.push({ name: 'ContainerSetting' });
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card width="30%" v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span style="font-size: 14px">{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link style="font-size: 14px; margin-bottom: 5px" @click="goSetting">
|
||||
【 {{ $t('container.setting') }} 】
|
||||
</el-button>
|
||||
<span style="font-size: 14px">{{ $t('container.startIn') }}</span>
|
||||
<div v-loading="loading">
|
||||
<el-card v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span>{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link class="bt" @click="goSetting">【 {{ $t('container.setting') }} 】</el-button>
|
||||
<span>{{ $t('container.startIn') }}</span>
|
||||
</el-card>
|
||||
|
||||
<LayoutContent
|
||||
v-loading="loading"
|
||||
:title="$t('container.network')"
|
||||
:class="{ mask: dockerStatus != 'Running' }"
|
||||
>
|
||||
<LayoutContent :title="$t('container.network')" :class="{ mask: dockerStatus != 'Running' }">
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
@@ -124,13 +118,21 @@ const paginationConfig = reactive({
|
||||
});
|
||||
const searchName = ref();
|
||||
|
||||
const dockerStatus = ref();
|
||||
const dockerStatus = ref('Running');
|
||||
const loadStatus = async () => {
|
||||
const res = await loadDockerStatus();
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
}
|
||||
loading.value = true;
|
||||
await loadDockerStatus()
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
dockerStatus.value = 'Failed';
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const goSetting = async () => {
|
||||
router.push({ name: 'ContainerSetting' });
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card width="30%" v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span style="font-size: 14px">{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link style="font-size: 14px; margin-bottom: 5px" @click="goSetting">
|
||||
【 {{ $t('container.setting') }} 】
|
||||
</el-button>
|
||||
<span style="font-size: 14px">{{ $t('container.startIn') }}</span>
|
||||
<div v-loading="loading">
|
||||
<el-card v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span>{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link class="bt" @click="goSetting">【 {{ $t('container.setting') }} 】</el-button>
|
||||
<span>{{ $t('container.startIn') }}</span>
|
||||
</el-card>
|
||||
|
||||
<LayoutContent v-loading="loading" :title="$t('container.repo')" :class="{ mask: dockerStatus != 'Running' }">
|
||||
<LayoutContent :title="$t('container.repo')" :class="{ mask: dockerStatus != 'Running' }">
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
@@ -99,13 +97,21 @@ const paginationConfig = reactive({
|
||||
});
|
||||
const searchName = ref();
|
||||
|
||||
const dockerStatus = ref();
|
||||
const dockerStatus = ref('Running');
|
||||
const loadStatus = async () => {
|
||||
const res = await loadDockerStatus();
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
}
|
||||
loading.value = true;
|
||||
await loadDockerStatus()
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
dockerStatus.value = 'Failed';
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const goSetting = async () => {
|
||||
router.push({ name: 'ContainerSetting' });
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card width="30%" v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span style="font-size: 14px">{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link style="font-size: 14px; margin-bottom: 5px" @click="goSetting">
|
||||
【 {{ $t('container.setting') }} 】
|
||||
</el-button>
|
||||
<span style="font-size: 14px">{{ $t('container.startIn') }}</span>
|
||||
<div v-loading="loading">
|
||||
<el-card v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span>{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link class="bt" @click="goSetting">【 {{ $t('container.setting') }} 】</el-button>
|
||||
<span>{{ $t('container.startIn') }}</span>
|
||||
</el-card>
|
||||
|
||||
<LayoutContent
|
||||
v-loading="loading"
|
||||
:title="$t('container.composeTemplate')"
|
||||
:class="{ mask: dockerStatus != 'Running' }"
|
||||
>
|
||||
<LayoutContent :title="$t('container.composeTemplate')" :class="{ mask: dockerStatus != 'Running' }">
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
@@ -96,13 +90,21 @@ const searchName = ref();
|
||||
|
||||
const detailRef = ref();
|
||||
|
||||
const dockerStatus = ref();
|
||||
const dockerStatus = ref('Running');
|
||||
const loadStatus = async () => {
|
||||
const res = await loadDockerStatus();
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
}
|
||||
loading.value = true;
|
||||
await loadDockerStatus()
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
dockerStatus.value = 'Failed';
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const goSetting = async () => {
|
||||
router.push({ name: 'ContainerSetting' });
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card width="30%" v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span style="font-size: 14px">{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link style="font-size: 14px; margin-bottom: 5px" @click="goSetting">
|
||||
【 {{ $t('container.setting') }} 】
|
||||
</el-button>
|
||||
<span style="font-size: 14px">{{ $t('container.startIn') }}</span>
|
||||
<div v-loading="loading">
|
||||
<el-card v-if="dockerStatus != 'Running'" class="mask-prompt">
|
||||
<span>{{ $t('container.serviceUnavailable') }}</span>
|
||||
<el-button type="primary" link class="bt" @click="goSetting">【 {{ $t('container.setting') }} 】</el-button>
|
||||
<span>{{ $t('container.startIn') }}</span>
|
||||
</el-card>
|
||||
|
||||
<LayoutContent v-loading="loading" :title="$t('container.volume')" :class="{ mask: dockerStatus != 'Running' }">
|
||||
<LayoutContent :title="$t('container.volume')" :class="{ mask: dockerStatus != 'Running' }">
|
||||
<template #toolbar>
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
@@ -104,13 +102,21 @@ const paginationConfig = reactive({
|
||||
});
|
||||
const searchName = ref();
|
||||
|
||||
const dockerStatus = ref();
|
||||
const dockerStatus = ref('Running');
|
||||
const loadStatus = async () => {
|
||||
const res = await loadDockerStatus();
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
}
|
||||
loading.value = true;
|
||||
await loadDockerStatus()
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
dockerStatus.value = res.data;
|
||||
if (dockerStatus.value === 'Running') {
|
||||
search();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
dockerStatus.value = 'Failed';
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const goSetting = async () => {
|
||||
router.push({ name: 'ContainerSetting' });
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
<el-col :span="8">
|
||||
<el-date-picker
|
||||
style="width: calc(100% - 20px)"
|
||||
@change="search()"
|
||||
@change="search(true)"
|
||||
v-model="timeRangeLoad"
|
||||
type="datetimerange"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
@@ -94,7 +94,7 @@
|
||||
></el-date-picker>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-select @change="search()" v-model="searchInfo.status">
|
||||
<el-select @change="search(true)" v-model="searchInfo.status">
|
||||
<template #prefix>{{ $t('commons.table.status') }}</template>
|
||||
<el-option :label="$t('commons.table.all')" value="" />
|
||||
<el-option :label="$t('commons.status.success')" value="Success" />
|
||||
@@ -301,34 +301,15 @@ interface DialogProps {
|
||||
}
|
||||
const recordShow = ref(false);
|
||||
const dialogData = ref();
|
||||
const records = ref<Array<Cronjob.Record>>();
|
||||
const records = ref<Array<Cronjob.Record>>([]);
|
||||
const currentRecord = ref<Cronjob.Record>();
|
||||
const currentRecordDetail = ref<string>('');
|
||||
const currentRecordIndex = ref();
|
||||
|
||||
const acceptParams = async (params: DialogProps): Promise<void> => {
|
||||
dialogData.value = params;
|
||||
let itemSearch = {
|
||||
page: searchInfo.page,
|
||||
pageSize: searchInfo.pageSize,
|
||||
cronjobID: dialogData.value.rowData!.id,
|
||||
startTime: new Date(new Date().setHours(0, 0, 0, 0)),
|
||||
endTime: new Date(new Date().setHours(23, 59, 59, 0)),
|
||||
status: searchInfo.status,
|
||||
};
|
||||
const res = await searchRecords(itemSearch);
|
||||
records.value = res.data.items || [];
|
||||
if (records.value.length === 0) {
|
||||
hasRecords.value = false;
|
||||
recordShow.value = true;
|
||||
return;
|
||||
}
|
||||
hasRecords.value = true;
|
||||
currentRecord.value = records.value[0];
|
||||
currentRecordIndex.value = 0;
|
||||
loadRecord(currentRecord.value);
|
||||
searchInfo.recordTotal = res.data.total;
|
||||
recordShow.value = true;
|
||||
search(true);
|
||||
};
|
||||
|
||||
const shortcuts = [
|
||||
@@ -343,33 +324,36 @@ const shortcuts = [
|
||||
{
|
||||
text: i18n.global.t('monitor.yestoday'),
|
||||
value: () => {
|
||||
const yestoday = new Date(new Date().getTime() - 3600 * 1000 * 24 * 1);
|
||||
const end = new Date(yestoday.setHours(23, 59, 59, 999));
|
||||
const start = new Date(yestoday.setHours(0, 0, 0, 0));
|
||||
const itemDate = new Date(new Date().getTime() - 3600 * 1000 * 24 * 1);
|
||||
const end = new Date(itemDate.setHours(23, 59, 59, 999));
|
||||
const start = new Date(itemDate.setHours(0, 0, 0, 0));
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.global.t('monitor.lastNDay', [3]),
|
||||
value: () => {
|
||||
const start = new Date(new Date().getTime() - 3600 * 1000 * 24 * 3);
|
||||
const itemDate = new Date(new Date().getTime() - 3600 * 1000 * 24 * 3);
|
||||
const end = new Date(new Date().setHours(23, 59, 59, 999));
|
||||
const start = new Date(itemDate.setHours(0, 0, 0, 0));
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.global.t('monitor.lastNDay', [7]),
|
||||
value: () => {
|
||||
const start = new Date(new Date().getTime() - 3600 * 1000 * 24 * 7);
|
||||
const itemDate = new Date(new Date().getTime() - 3600 * 1000 * 24 * 7);
|
||||
const end = new Date(new Date().setHours(23, 59, 59, 999));
|
||||
const start = new Date(itemDate.setHours(0, 0, 0, 0));
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.global.t('monitor.lastNDay', [30]),
|
||||
value: () => {
|
||||
const start = new Date(new Date().getTime() - 3600 * 1000 * 24 * 30);
|
||||
const itemDate = new Date(new Date().getTime() - 3600 * 1000 * 24 * 30);
|
||||
const end = new Date(new Date().setHours(23, 59, 59, 999));
|
||||
const start = new Date(itemDate.setHours(0, 0, 0, 0));
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
@@ -384,16 +368,16 @@ const weekOptions = [
|
||||
{ label: i18n.global.t('cronjob.sunday'), value: 7 },
|
||||
];
|
||||
const timeRangeLoad = ref<[Date, Date]>([
|
||||
new Date(new Date().setHours(0, 0, 0, 0)),
|
||||
new Date(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7).setHours(0, 0, 0, 0)),
|
||||
new Date(new Date().setHours(23, 59, 59, 999)),
|
||||
]);
|
||||
const searchInfo = reactive({
|
||||
page: 1,
|
||||
pageSize: 8,
|
||||
pageSize: 12,
|
||||
recordTotal: 0,
|
||||
cronjobID: 0,
|
||||
startTime: new Date(new Date().setHours(0, 0, 0, 0)),
|
||||
endTime: new Date(new Date().setHours(23, 59, 59, 999)),
|
||||
startTime: new Date(),
|
||||
endTime: new Date(),
|
||||
status: '',
|
||||
});
|
||||
|
||||
@@ -406,7 +390,7 @@ const onHandle = async (row: Cronjob.CronjobInfo) => {
|
||||
searchInfo.pageSize = searchInfo.pageSize * searchInfo.page;
|
||||
searchInfo.page = 1;
|
||||
records.value = [];
|
||||
search();
|
||||
search(false);
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
@@ -425,7 +409,12 @@ const onChangeStatus = async (id: number, status: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
const search = async (isInit: boolean) => {
|
||||
if (isInit) {
|
||||
searchInfo.page = 1;
|
||||
searchInfo.pageSize = 12;
|
||||
records.value = [];
|
||||
}
|
||||
if (timeRangeLoad.value && timeRangeLoad.value.length === 2) {
|
||||
searchInfo.startTime = timeRangeLoad.value[0];
|
||||
searchInfo.endTime = timeRangeLoad.value[1];
|
||||
@@ -446,6 +435,9 @@ const search = async () => {
|
||||
hasRecords.value = false;
|
||||
return;
|
||||
}
|
||||
if (!res.data.items) {
|
||||
return;
|
||||
}
|
||||
for (const item of res.data.items) {
|
||||
records.value.push(item);
|
||||
}
|
||||
@@ -460,7 +452,7 @@ const onRefresh = () => {
|
||||
records.value = [];
|
||||
searchInfo.pageSize = searchInfo.pageSize * searchInfo.page;
|
||||
searchInfo.page = 1;
|
||||
search();
|
||||
search(true);
|
||||
};
|
||||
|
||||
const onDownload = async (record: any, backupID: number) => {
|
||||
@@ -492,7 +484,7 @@ const nextPage = async () => {
|
||||
return;
|
||||
}
|
||||
searchInfo.page = searchInfo.page + 1;
|
||||
search();
|
||||
search(false);
|
||||
};
|
||||
const forDetail = async (row: Cronjob.Record, index: number) => {
|
||||
currentRecord.value = row;
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
v-if="mysqlStatus != 'Running' && !isOnSetting && mysqlIsExist && !loading"
|
||||
class="mask-prompt"
|
||||
>
|
||||
<span style="font-size: 14px">{{ $t('commons.service.serviceNotStarted', ['MySQL']) }}</span>
|
||||
<span>{{ $t('commons.service.serviceNotStarted', ['MySQL']) }}</span>
|
||||
</el-card>
|
||||
|
||||
<Setting ref="settingRef" style="margin-top: 20px" />
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</LayoutContent>
|
||||
|
||||
<el-card width="30%" v-if="redisStatus != 'Running' && !isOnSetting && redisIsExist" class="mask-prompt">
|
||||
<span style="font-size: 14px">{{ $t('commons.service.serviceNotStarted', ['Redis']) }}</span>
|
||||
<span>{{ $t('commons.service.serviceNotStarted', ['Redis']) }}</span>
|
||||
</el-card>
|
||||
|
||||
<Setting ref="settingRef" style="margin-top: 30px" />
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
<el-tag>
|
||||
{{ $t('home.rwPerSecond') }}: {{ currentChartInfo.ioCount }} {{ $t('home.time') }}
|
||||
</el-tag>
|
||||
<el-tag>{{ $t('home.rwPerSecond') }}: {{ currentInfo.ioTime }} ms</el-tag>
|
||||
<el-tag>{{ $t('home.ioDelay') }}: {{ currentInfo.ioTime }} ms</el-tag>
|
||||
</div>
|
||||
|
||||
<div v-if="chartOption === 'io'" style="margin-top: 40px">
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
:auto-upload="false"
|
||||
ref="uploadRef"
|
||||
:on-change="fileOnChange"
|
||||
v-loading="loading"
|
||||
:limit="1"
|
||||
:on-exceed="handleExceed"
|
||||
>
|
||||
@@ -24,8 +23,10 @@
|
||||
{{ $t('database.dropHelper') }}
|
||||
<em>{{ $t('database.clickHelper') }}</em>
|
||||
</div>
|
||||
<template #tip>
|
||||
<el-progress v-if="loading" text-inside :stroke-width="12" :percentage="uploadPrecent"></el-progress>
|
||||
</template>
|
||||
</el-upload>
|
||||
<el-progress v-if="loading" :text-inside="true" :stroke-width="26" :percentage="uploadPrecent"></el-progress>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||
@@ -109,6 +110,7 @@ const submit = async () => {
|
||||
uploadedChunkCount++;
|
||||
} catch (error) {
|
||||
loading.value = false;
|
||||
break;
|
||||
}
|
||||
if (uploadedChunkCount == chunkCount) {
|
||||
loading.value = false;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; width: 330px"
|
||||
style="float: right; width: 360px"
|
||||
></el-date-picker>
|
||||
</template>
|
||||
<div id="loadLoadChart" style="width: 100%; height: 400px"></div>
|
||||
@@ -41,7 +41,7 @@
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; width: 330px"
|
||||
style="float: right; width: 360px"
|
||||
></el-date-picker>
|
||||
</template>
|
||||
<div id="loadCPUChart" style="width: 100%; height: 400px"></div>
|
||||
@@ -59,7 +59,7 @@
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; width: 330px"
|
||||
style="float: right; width: 360px"
|
||||
></el-date-picker>
|
||||
</template>
|
||||
<div id="loadMemoryChart" style="width: 100%; height: 400px"></div>
|
||||
@@ -79,7 +79,7 @@
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; width: 330px"
|
||||
style="float: right; width: 360px"
|
||||
></el-date-picker>
|
||||
</template>
|
||||
<div id="loadIOChart" style="width: 100%; height: 400px"></div>
|
||||
@@ -114,7 +114,7 @@
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; width: 330px"
|
||||
style="float: right; width: 360px"
|
||||
></el-date-picker>
|
||||
</template>
|
||||
<div id="loadNetworkChart" style="width: 100%; height: 400px"></div>
|
||||
|
||||
@@ -10,12 +10,7 @@
|
||||
<el-form-item :label="$t('commons.table.group')" prop="group">
|
||||
<el-select filterable v-model="dialogData.groupID" clearable style="width: 100%">
|
||||
<div v-for="item in groupList" :key="item.id">
|
||||
<el-option
|
||||
v-if="item.name === 'default'"
|
||||
:label="$t('website.default')"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-option v-else :label="item.name" :value="item.id" />
|
||||
<el-option :label="item.name" :value="item.id" />
|
||||
</div>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -38,7 +33,8 @@
|
||||
import { ref, reactive } from 'vue';
|
||||
import type { ElForm } from 'element-plus';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { editHostGroup, GetGroupList } from '@/api/modules/host';
|
||||
import { editHostGroup } from '@/api/modules/host';
|
||||
import { GetGroupList } from '@/api/modules/group';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
|
||||
|
||||
@@ -34,8 +34,7 @@
|
||||
<template #prefix>{{ $t('terminal.group') }}</template>
|
||||
<el-option :label="$t('commons.table.all')" value=""></el-option>
|
||||
<div v-for="item in groupList" :key="item.name">
|
||||
<el-option v-if="item.name === 'default'" :label="$t('website.default')" :value="item.id" />
|
||||
<el-option v-else :value="item.id" :label="item.name" />
|
||||
<el-option :value="item.id" :label="item.name" />
|
||||
</div>
|
||||
</el-select>
|
||||
</template>
|
||||
@@ -84,7 +83,8 @@ import GroupDialog from '@/components/group/index.vue';
|
||||
import GroupChangeDialog from '@/views/host/terminal/host/change-group/index.vue';
|
||||
import OperateDialog from '@/views/host/terminal/host/operate/index.vue';
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import { deleteHost, GetGroupList, searchHosts } from '@/api/modules/host';
|
||||
import { deleteHost, searchHosts } from '@/api/modules/host';
|
||||
import { GetGroupList } from '@/api/modules/group';
|
||||
import { reactive, ref } from 'vue';
|
||||
import i18n from '@/lang';
|
||||
import { Host } from '@/api/interface/host';
|
||||
|
||||
@@ -77,7 +77,8 @@
|
||||
import { ref, reactive } from 'vue';
|
||||
import type { ElForm } from 'element-plus';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { addHost, editHost, testByInfo, GetGroupList } from '@/api/modules/host';
|
||||
import { addHost, editHost, testByInfo } from '@/api/modules/host';
|
||||
import { GetGroupList } from '@/api/modules/group';
|
||||
import i18n from '@/lang';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-loading="loading">
|
||||
<div v-if="isFirst">
|
||||
<div class="login-form">
|
||||
<el-form ref="registerFormRef" :model="registerForm" size="default" :rules="registerRules">
|
||||
@@ -70,13 +70,13 @@
|
||||
</div>
|
||||
<div v-else-if="mfaShow">
|
||||
<div class="login-form">
|
||||
<el-form>
|
||||
<el-form @submit.prevent>
|
||||
<div class="login-title">{{ $t('commons.login.mfaTitle') }}</div>
|
||||
<el-form-item class="no-border">
|
||||
<el-input
|
||||
size="default"
|
||||
:placeholder="$t('commons.login.captchaHelper')"
|
||||
v-model="mfaLoginForm.code"
|
||||
:placeholder="$t('commons.login.mfaCode')"
|
||||
v-model.trim="mfaLoginForm.code"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon class="el-input__icon">
|
||||
@@ -89,7 +89,15 @@
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button class="login-button" type="primary" size="default" round @click="mfaLogin()">
|
||||
<el-button
|
||||
@focus="mfaButtonFocused = true"
|
||||
@blur="mfaButtonFocused = false"
|
||||
class="login-button"
|
||||
type="primary"
|
||||
size="default"
|
||||
round
|
||||
@click="mfaLogin()"
|
||||
>
|
||||
{{ $t('commons.button.verify') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
@@ -157,6 +165,20 @@
|
||||
{{ $t('commons.button.login') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item prop="agreeLicense">
|
||||
<el-checkbox v-model="loginForm.agreeLicense">
|
||||
<template #default>
|
||||
<span v-html="$t('commons.login.licenseHelper')"></span>
|
||||
</template>
|
||||
</el-checkbox>
|
||||
<span
|
||||
v-if="errAgree && loginForm.agreeLicense === false"
|
||||
class="input-error"
|
||||
style="line-height: 14px"
|
||||
>
|
||||
{{ $t('commons.login.errorAgree') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="demo">
|
||||
<span v-if="isDemo">
|
||||
@@ -186,6 +208,7 @@ const errAuthInfo = ref(false);
|
||||
const errCaptcha = ref(false);
|
||||
const errMfaInfo = ref(false);
|
||||
const isDemo = ref(false);
|
||||
const errAgree = ref(false);
|
||||
|
||||
const isFirst = ref();
|
||||
|
||||
@@ -212,11 +235,14 @@ const loginForm = reactive({
|
||||
captcha: '',
|
||||
captchaID: '',
|
||||
authMethod: '',
|
||||
agreeLicense: false,
|
||||
});
|
||||
const loginRules = reactive({
|
||||
name: [{ required: true, message: i18n.global.t('commons.rule.username'), trigger: 'blur' }],
|
||||
password: [{ required: true, message: i18n.global.t('commons.rule.password'), trigger: 'blur' }],
|
||||
});
|
||||
|
||||
const mfaButtonFocused = ref();
|
||||
const mfaLoginForm = reactive({
|
||||
name: '',
|
||||
password: '',
|
||||
@@ -250,36 +276,43 @@ const login = (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
loading.value = true;
|
||||
let requestLoginForm = {
|
||||
name: loginForm.name,
|
||||
password: loginForm.password,
|
||||
captcha: loginForm.captcha,
|
||||
captchaID: captcha.captchaID,
|
||||
authMethod: '',
|
||||
};
|
||||
if (requestLoginForm.captcha == '') {
|
||||
errCaptcha.value = true;
|
||||
return;
|
||||
}
|
||||
if (loginForm.agreeLicense == false) {
|
||||
errAgree.value = true;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let requestLoginForm = {
|
||||
name: loginForm.name,
|
||||
password: loginForm.password,
|
||||
captcha: loginForm.captcha,
|
||||
captchaID: captcha.captchaID,
|
||||
authMethod: '',
|
||||
};
|
||||
loading.value = true;
|
||||
const res = await loginApi(requestLoginForm);
|
||||
if (res.code === 406) {
|
||||
if (res.message === 'ErrCaptchaCode') {
|
||||
errCaptcha.value = true;
|
||||
errAuthInfo.value = false;
|
||||
loginVerify();
|
||||
}
|
||||
if (res.message === 'ErrAuth') {
|
||||
errCaptcha.value = false;
|
||||
errAuthInfo.value = true;
|
||||
loginVerify();
|
||||
}
|
||||
loginVerify();
|
||||
return;
|
||||
}
|
||||
if (res.data.mfaStatus === 'enable') {
|
||||
mfaShow.value = true;
|
||||
errMfaInfo.value = false;
|
||||
mfaLoginForm.secret = res.data.mfaSecret;
|
||||
return;
|
||||
}
|
||||
globalStore.setLogStatus(true);
|
||||
globalStore.setAgreeLicense(true);
|
||||
menuStore.setMenuList([]);
|
||||
MsgSuccess(i18n.global.t('commons.msg.loginSuccess'));
|
||||
router.push({ name: 'home' });
|
||||
@@ -335,17 +368,22 @@ function checkPassword(rule: any, value: any, callback: any) {
|
||||
|
||||
onMounted(() => {
|
||||
document.title = globalStore.themeConfig.panelName;
|
||||
loginForm.agreeLicense = globalStore.agreeLicense;
|
||||
checkStatus();
|
||||
checkIsSystemDemo();
|
||||
document.onkeydown = (e: any) => {
|
||||
e = window.event || e;
|
||||
if (e.keyCode === 13) {
|
||||
if (loading.value) return;
|
||||
if (isFirst.value && !registerButtonFocused.value) {
|
||||
register(registerFormRef.value);
|
||||
if (!mfaShow.value) {
|
||||
if (isFirst.value && !registerButtonFocused.value) {
|
||||
register(registerFormRef.value);
|
||||
}
|
||||
if (!isFirst.value && !loginButtonFocused.value) {
|
||||
login(loginFormRef.value);
|
||||
}
|
||||
}
|
||||
if (!isFirst.value && !loginButtonFocused.value) {
|
||||
login(loginFormRef.value);
|
||||
if (mfaShow.value && !mfaButtonFocused.value) {
|
||||
mfaLogin();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user