mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-10 16:00:31 +00:00
Compare commits
40
Commits
v1.3.0
...
release-1.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2879f2a56 | ||
|
|
67221e7f70 | ||
|
|
c76cc07c1c | ||
|
|
7a091a152e | ||
|
|
d2087641f7 | ||
|
|
321ed00734 | ||
|
|
8ff160408f | ||
|
|
662eae1ba4 | ||
|
|
f808e81958 | ||
|
|
f161bcbc31 | ||
|
|
73f789062b | ||
|
|
1fdf4ffd2f | ||
|
|
6d58fe7a1e | ||
|
|
c8bbfada8c | ||
|
|
47dda4ac9f | ||
|
|
b1d40960c4 | ||
|
|
5222caecf9 | ||
|
|
bc8d4cbb0f | ||
|
|
0bb31f6caf | ||
|
|
03c8e4d3cb | ||
|
|
68ad653545 | ||
|
|
fd5b34d644 | ||
|
|
000096bc26 | ||
|
|
02023102d3 | ||
|
|
95bc3d9855 | ||
|
|
f9fb16198b | ||
|
|
11ae05163c | ||
|
|
0275716ff5 | ||
|
|
e9bdca5279 | ||
|
|
cd84116a03 | ||
|
|
457effae85 | ||
|
|
9361c358f1 | ||
|
|
39f952e460 | ||
|
|
7c600a357c | ||
|
|
8f036a0c3d | ||
|
|
1184ae2b52 | ||
|
|
de7ef19034 | ||
|
|
e91b7caf4f | ||
|
|
033b3c10c0 | ||
|
|
97774c88d5 |
@@ -12,9 +12,7 @@ jobs:
|
||||
build-linux-binary:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install cross-compilers
|
||||
run: sudo apt-get update && sudo apt-get -y install gcc-x86-64-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabi gcc-powerpc64le-linux-gnu gcc-s390x-linux-gnu
|
||||
- name: Checkout code
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
@@ -23,7 +21,7 @@ jobs:
|
||||
- name: Build Web
|
||||
id: build_frontend
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:dev
|
||||
cd frontend && npm install && npm run build:pro
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
- name: Setup Go
|
||||
|
||||
@@ -10,9 +10,7 @@ jobs:
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install cross-compilers
|
||||
run: sudo apt-get update && sudo apt-get -y install gcc-x86-64-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabi gcc-powerpc64le-linux-gnu gcc-s390x-linux-gnu
|
||||
- name: Checkout code
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
@@ -20,7 +18,7 @@ jobs:
|
||||
node-version: '18.14'
|
||||
- name: Build Web
|
||||
run: |
|
||||
cd frontend && npm install && npm run build:dev
|
||||
cd frontend && npm install && npm run build:pro
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
- name: Setup Go
|
||||
@@ -30,14 +28,39 @@ jobs:
|
||||
- name: Build Release
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: release --skip-publish --clean
|
||||
- name: Upload Assets
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
draft: true
|
||||
body: |
|
||||
# 一、安装和升级
|
||||
|
||||
## 1.1 一键安装
|
||||
```sh
|
||||
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
|
||||
```
|
||||
|
||||
## 1.2 在线升级
|
||||
|
||||
登录 1Panel Web 控制台,在页面右下角点击 **【检查更新】** 进行在线升级。
|
||||
|
||||
>更多信息请查阅在线文档:https://1panel.cn/docs/
|
||||
|
||||
# 二、更新日志
|
||||
|
||||
files: |
|
||||
dist/*.tar.gz
|
||||
dist/checksums.txt
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Setup OSSUTIL
|
||||
uses: yizhoumo/setup-ossutil@v1
|
||||
with:
|
||||
endpoint: ${{ secrets.OSS_ENDPOINT }}
|
||||
access-key-id: ${{ secrets.OSS_ACCESS_KEY_ID }}
|
||||
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
|
||||
ossutil-version: '1.7.14'
|
||||
- name: Upload Assets to OSS
|
||||
run: ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir --force
|
||||
@@ -30,5 +30,6 @@ dist/
|
||||
1pctl
|
||||
1panel.service
|
||||
install.sh
|
||||
quick_start.sh
|
||||
cmd/server/web/.DS_Store
|
||||
cmd/server/.DS_Store
|
||||
|
||||
+1
-11
@@ -16,18 +16,8 @@ builds:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -w -s
|
||||
- --extldflags "-static -fpic"
|
||||
tags:
|
||||
- osusergo
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- >-
|
||||
{{- if eq .Arch "amd64"}}CC=x86_64-linux-gnu-gcc{{- end }}
|
||||
{{- if eq .Arch "arm64"}}CC=aarch64-linux-gnu-gcc{{- end }}
|
||||
{{- if eq .Arch "arm"}}CC=arm-linux-gnueabi-gcc{{- end }}
|
||||
{{- if eq .Arch "loong64"}}CC=loongarch64-linux-gnu-gcc{{- end }}
|
||||
{{- if eq .Arch "ppc64le"}}CC=powerpc64le-linux-gnu-gcc{{- end }}
|
||||
{{- if eq .Arch "s390x"}}CC=s390x-linux-gnu-gcc{{- end }}
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
goarm:
|
||||
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
FROM node:18.14 as build_web
|
||||
ARG TARGETARCH
|
||||
ARG NPM_REGISTRY="https://registry.npmmirror.com"
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
WORKDIR /data
|
||||
|
||||
RUN set -ex \
|
||||
&& npm config set registry ${NPM_REGISTRY}
|
||||
|
||||
ADD . /data
|
||||
|
||||
RUN set -ex \
|
||||
&& cd /data/frontend \
|
||||
&& npm install
|
||||
|
||||
RUN set -ex \
|
||||
&& cd /data/frontend \
|
||||
&& npm run build:dev
|
||||
|
||||
FROM golang:1.20
|
||||
ARG TARGETARCH
|
||||
ARG GOPROXY="https://goproxy.cn,direct"
|
||||
|
||||
COPY --from=build_web /data /data
|
||||
|
||||
WORKDIR /data
|
||||
|
||||
RUN set -ex \
|
||||
&& go env -w GOPROXY=${GOPROXY} \
|
||||
&& go install github.com/goreleaser/goreleaser@latest \
|
||||
&& goreleaser build --single-target --snapshot --clean
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
@@ -23,16 +23,16 @@ build_frontend:
|
||||
|
||||
build_backend_on_linux:
|
||||
cd $(SERVER_PATH) \
|
||||
&& CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-static -fpic"' -tags 'osusergo,netgo' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_backend_on_darwin:
|
||||
cd $(SERVER_PATH) \
|
||||
&& CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC=x86_64-linux-musl-gcc CXX=x86_64-linux-musl-g++ $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-static -fpic"' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
&& GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_backend_on_archlinux:
|
||||
cd $(SERVER_PATH) \
|
||||
&& CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-fpic"' -tags osusergo -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
&& GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
|
||||
|
||||
build_all: build_frontend build_backend_on_linux
|
||||
build_all: build_frontend build_backend_on_linux
|
||||
|
||||
build_on_local: clean_assets build_frontend build_backend_on_darwin upx_bin
|
||||
build_on_local: clean_assets build_frontend build_backend_on_darwin upx_bin
|
||||
|
||||
@@ -163,11 +163,16 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
cmds := fmt.Sprintf("docker exec %s %s", containerID, command)
|
||||
cmds := []string{"exec", containerID, command}
|
||||
if len(user) != 0 {
|
||||
cmds = fmt.Sprintf("docker exec -u %s %s %s", user, containerID, command)
|
||||
cmds = []string{"exec", "-u", user, containerID, command}
|
||||
}
|
||||
stdout, err := cmd.Exec(cmds)
|
||||
if cmd.CheckIllegal(user, containerID, command) {
|
||||
if wshandleError(wsConn, errors.New(" The command contains illegal characters.")) {
|
||||
return
|
||||
}
|
||||
}
|
||||
stdout, err := cmd.ExecWithCheck("docker", cmds...)
|
||||
if wshandleError(wsConn, errors.WithMessage(err, stdout)) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ type CronjobCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
SpecType string `json:"specType" validate:"required"`
|
||||
Week int `json:"week" validate:"number,max=7,min=1"`
|
||||
Week int `json:"week" validate:"number,max=6,min=0"`
|
||||
Day int `json:"day" validate:"number"`
|
||||
Hour int `json:"hour" validate:"number"`
|
||||
Minute int `json:"minute" validate:"number"`
|
||||
@@ -27,7 +27,7 @@ type CronjobUpdate struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
SpecType string `json:"specType" validate:"required"`
|
||||
Week int `json:"week" validate:"number,max=7,min=1"`
|
||||
Week int `json:"week" validate:"number,max=6,min=0"`
|
||||
Day int `json:"day" validate:"number"`
|
||||
Hour int `json:"hour" validate:"number"`
|
||||
Minute int `json:"minute" validate:"number"`
|
||||
|
||||
@@ -19,6 +19,7 @@ type IAppInstallResourceRpo interface {
|
||||
GetFirst(opts ...DBOption) (model.AppInstallResource, error)
|
||||
Create(ctx context.Context, resource *model.AppInstallResource) error
|
||||
DeleteBy(ctx context.Context, opts ...DBOption) error
|
||||
BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error
|
||||
}
|
||||
|
||||
func NewIAppInstallResourceRpo() IAppInstallResourceRpo {
|
||||
@@ -71,3 +72,11 @@ func (a AppInstallResourceRpo) Create(ctx context.Context, resource *model.AppIn
|
||||
func (a AppInstallResourceRpo) DeleteBy(ctx context.Context, opts ...DBOption) error {
|
||||
return getTx(ctx, opts...).Delete(&model.AppInstallResource{}).Error
|
||||
}
|
||||
|
||||
func (a *AppInstallResourceRpo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOption) error {
|
||||
db := getDb(opts...).Model(&model.AppInstallResource{})
|
||||
if len(opts) == 0 {
|
||||
db = db.Where("1=1")
|
||||
}
|
||||
return db.Updates(&maps).Error
|
||||
}
|
||||
|
||||
@@ -296,6 +296,10 @@ func upgradeInstall(installId uint, detailId uint) error {
|
||||
return
|
||||
}
|
||||
config := getAppCommonConfig(envs)
|
||||
if config.ContainerName == "" {
|
||||
config.ContainerName = install.ContainerName
|
||||
envs[constant.ContainerName] = install.ContainerName
|
||||
}
|
||||
config.Advanced = true
|
||||
if upErr = addDockerComposeCommonParam(composeMap, install.ServiceName, config, envs); upErr != nil {
|
||||
return
|
||||
@@ -314,10 +318,6 @@ func upgradeInstall(installId uint, detailId uint) error {
|
||||
install.Version = detail.Version
|
||||
install.AppDetailId = detailId
|
||||
|
||||
go func() {
|
||||
_, _ = http.Get(detail.DownloadCallBackUrl)
|
||||
}()
|
||||
|
||||
if out, err := compose.Down(install.GetComposePath()); err != nil {
|
||||
if out != "" {
|
||||
upErr = errors.New(out)
|
||||
|
||||
@@ -99,7 +99,7 @@ func handleAppBackup(install *model.AppInstall, backupDir, fileName string) erro
|
||||
return err
|
||||
}
|
||||
|
||||
appPath := fmt.Sprintf("%s/%s", install.GetPath(), install.Name)
|
||||
appPath := install.GetPath()
|
||||
if err := handleTar(appPath, tmpDir, "app.tar.gz", ""); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -148,7 +148,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
if err := json.Unmarshal(appjson, &oldInstall); err != nil {
|
||||
return fmt.Errorf("unmarshal app.json failed, err: %v", err)
|
||||
}
|
||||
if oldInstall.App.Key != install.App.Key || oldInstall.Name != install.Name || oldInstall.Version != install.Version || oldInstall.ID != install.ID {
|
||||
if oldInstall.App.Key != install.App.Key || oldInstall.Name != install.Name {
|
||||
return errors.New("the current backup file does not match the application")
|
||||
}
|
||||
|
||||
@@ -172,6 +172,7 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
}()
|
||||
}
|
||||
|
||||
newEnvFile := ""
|
||||
resource, _ := appInstallResourceRepo.GetFirst(appInstallResourceRepo.WithAppInstallId(install.ID))
|
||||
if resource.ID != 0 && install.App.Key != "mysql" {
|
||||
mysqlInfo, err := appInstallRepo.LoadBaseInfo(resource.Key, "")
|
||||
@@ -182,7 +183,22 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := handleMysqlRecover(mysqlInfo, tmpPath, db.Name, fmt.Sprintf("%s.sql.gz", install.Name), true); err != nil {
|
||||
|
||||
newDB, envMap, err := reCreateDB(db.ID, oldInstall.Env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", envMap["PANEL_DB_HOST"].(string))
|
||||
newHost := fmt.Sprintf("\"PANEL_DB_HOST\":\"%v\"", mysqlInfo.ServiceName)
|
||||
oldInstall.Env = strings.ReplaceAll(oldInstall.Env, oldHost, newHost)
|
||||
envMap["PANEL_DB_HOST"] = mysqlInfo.ServiceName
|
||||
newEnvFile, err = coverEnvJsonToStr(oldInstall.Env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = appInstallResourceRepo.BatchUpdateBy(map[string]interface{}{"resource_id": newDB.ID}, commonRepo.WithByID(resource.ID))
|
||||
|
||||
if err := handleMysqlRecover(mysqlInfo, tmpPath, newDB.Name, fmt.Sprintf("%s.sql.gz", install.Name), true); err != nil {
|
||||
global.LOG.Errorf("handle recover from sql.gz failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
@@ -193,11 +209,50 @@ func handleAppRecover(install *model.AppInstall, recoverFile string, isRollback
|
||||
return err
|
||||
}
|
||||
|
||||
oldInstall.Status = constant.Running
|
||||
if err := appInstallRepo.Save(context.Background(), install); err != nil {
|
||||
if len(newEnvFile) != 0 {
|
||||
envPath := fmt.Sprintf("%s/%s/%s/.env", constant.AppInstallDir, install.App.Key, install.Name)
|
||||
file, err := os.OpenFile(envPath, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, _ = file.WriteString(newEnvFile)
|
||||
}
|
||||
|
||||
oldInstall.ID = install.ID
|
||||
oldInstall.Status = constant.StatusRunning
|
||||
oldInstall.AppId = install.AppId
|
||||
oldInstall.AppDetailId = install.AppDetailId
|
||||
if err := appInstallRepo.Save(context.Background(), &oldInstall); err != nil {
|
||||
global.LOG.Errorf("save db app install failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
isOk = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func reCreateDB(dbID uint, oldEnv string) (*model.DatabaseMysql, map[string]interface{}, error) {
|
||||
mysqlService := NewIMysqlService()
|
||||
ctx := context.Background()
|
||||
_ = mysqlService.Delete(ctx, dto.MysqlDBDelete{ID: dbID, DeleteBackup: true, ForceDelete: true})
|
||||
|
||||
envMap := make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(oldEnv), &envMap); err != nil {
|
||||
return nil, envMap, err
|
||||
}
|
||||
oldName, _ := envMap["PANEL_DB_NAME"].(string)
|
||||
oldUser, _ := envMap["PANEL_DB_USER"].(string)
|
||||
oldPassword, _ := envMap["PANEL_DB_USER_PASSWORD"].(string)
|
||||
createDB, err := mysqlService.Create(context.Background(), dto.MysqlDBCreate{
|
||||
Name: oldName,
|
||||
Format: "utf8mb4",
|
||||
Username: oldUser,
|
||||
Password: oldPassword,
|
||||
Permission: "%",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, envMap, err
|
||||
}
|
||||
|
||||
return createDB, envMap, nil
|
||||
}
|
||||
|
||||
@@ -196,7 +196,10 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
|
||||
path := ""
|
||||
if strings.Contains(sourceDir, "/") {
|
||||
itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "")
|
||||
aheadDir := strings.ReplaceAll(sourceDir, itemDir, "")
|
||||
aheadDir := sourceDir[:strings.LastIndex(sourceDir, "/")]
|
||||
if len(aheadDir) == 0 {
|
||||
aheadDir = "/"
|
||||
}
|
||||
path += fmt.Sprintf("-C %s %s", aheadDir, itemDir)
|
||||
} else {
|
||||
path = sourceDir
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
@@ -100,7 +99,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if err := u.createUser(app, req); err != nil {
|
||||
if err := u.createUser(app.ContainerName, app.Password, app.Version, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -291,7 +290,7 @@ func (u *MysqlService) ChangeAccess(info dto.ChangeDBInfo) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := u.createUser(app, dto.MysqlDBCreate{
|
||||
if err := u.createUser(app.ContainerName, app.Password, app.Version, dto.MysqlDBCreate{
|
||||
Username: mysql.Username,
|
||||
Name: mysql.Name,
|
||||
Permission: info.Value,
|
||||
@@ -470,7 +469,7 @@ func (u *MysqlService) LoadStatus() (*dto.MysqlStatus, error) {
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) createUser(app *repo.RootInfo, req dto.MysqlDBCreate) error {
|
||||
func (u *MysqlService) createUser(container, password, version string, req dto.MysqlDBCreate) error {
|
||||
var userlist []string
|
||||
if strings.Contains(req.Permission, ",") {
|
||||
ips := strings.Split(req.Permission, ",")
|
||||
@@ -484,32 +483,35 @@ func (u *MysqlService) createUser(app *repo.RootInfo, req dto.MysqlDBCreate) err
|
||||
}
|
||||
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("create user %s identified by '%s';", user, req.Password)); err != nil {
|
||||
handleCreateError(req.Name, userlist, app)
|
||||
if err := excSQL(container, password, fmt.Sprintf("create user %s identified by '%s';", user, req.Password)); err != nil {
|
||||
if strings.Contains(err.Error(), "ERROR 1396") {
|
||||
handleCreateError(container, password, req.Name, userlist, false)
|
||||
return buserr.New(constant.ErrUserIsExist)
|
||||
}
|
||||
handleCreateError(container, password, req.Name, userlist, true)
|
||||
return err
|
||||
}
|
||||
grantStr := fmt.Sprintf("grant all privileges on `%s`.* to %s", req.Name, user)
|
||||
if req.Name == "*" {
|
||||
grantStr = fmt.Sprintf("grant all privileges on *.* to %s", user)
|
||||
}
|
||||
if strings.HasPrefix(app.Version, "5.7") {
|
||||
if strings.HasPrefix(version, "5.7") {
|
||||
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, req.Password)
|
||||
}
|
||||
if err := excSQL(app.ContainerName, app.Password, grantStr); err != nil {
|
||||
handleCreateError(req.Name, userlist, app)
|
||||
if err := excSQL(container, password, grantStr); err != nil {
|
||||
handleCreateError(container, password, req.Name, userlist, true)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func handleCreateError(dbName string, userlist []string, app *repo.RootInfo) {
|
||||
_ = excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop database `%s`", dbName))
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(app.ContainerName, app.Password, fmt.Sprintf("drop user if exists %s", user)); err != nil {
|
||||
global.LOG.Errorf("drop user failed, err: %v", err)
|
||||
func handleCreateError(contaienr, password, dbName string, userlist []string, dropUser bool) {
|
||||
_ = excSQL(contaienr, password, fmt.Sprintf("drop database `%s`", dbName))
|
||||
if dropUser {
|
||||
for _, user := range userlist {
|
||||
if err := excSQL(contaienr, password, fmt.Sprintf("drop user if exists %s", user)); err != nil {
|
||||
global.LOG.Errorf("drop user failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,12 +136,14 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
|
||||
|
||||
switch req.Key {
|
||||
case "Registries":
|
||||
req.Value = strings.TrimRight(req.Value, ",")
|
||||
if len(req.Value) == 0 {
|
||||
delete(daemonMap, "insecure-registries")
|
||||
} else {
|
||||
daemonMap["insecure-registries"] = strings.Split(req.Value, ",")
|
||||
}
|
||||
case "Mirrors":
|
||||
req.Value = strings.TrimRight(req.Value, ",")
|
||||
if len(req.Value) == 0 {
|
||||
delete(daemonMap, "registry-mirrors")
|
||||
} else {
|
||||
|
||||
@@ -3,12 +3,14 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
@@ -76,6 +78,9 @@ func (u *ImageRepoService) List() ([]dto.ImageRepoOption, error) {
|
||||
}
|
||||
|
||||
func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
if cmd.CheckIllegal(req.Username, req.Password, req.DownloadUrl) {
|
||||
return buserr.New(constant.ErrRepoConn)
|
||||
}
|
||||
imageRepo, _ := imageRepoRepo.Get(commonRepo.WithByName(req.Name))
|
||||
if imageRepo.ID != 0 {
|
||||
return constant.ErrRecordExist
|
||||
@@ -142,6 +147,9 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
||||
if req.ID == 1 {
|
||||
return errors.New("The default value cannot be deleted !")
|
||||
}
|
||||
if cmd.CheckIllegal(req.Username, req.Password, req.DownloadUrl) {
|
||||
return buserr.New(constant.ErrRepoConn)
|
||||
}
|
||||
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -149,7 +157,7 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
||||
if repo.DownloadUrl != req.DownloadUrl || (!repo.Auth && req.Auth) {
|
||||
_ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update")
|
||||
if repo.Auth {
|
||||
_, _ = cmd.Execf("docker logout %s", repo.DownloadUrl)
|
||||
_, _ = cmd.ExecWithCheck("docker", "logout", repo.DownloadUrl)
|
||||
}
|
||||
stdout, err := cmd.Exec("systemctl restart docker")
|
||||
if err != nil {
|
||||
@@ -176,9 +184,9 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
||||
}
|
||||
|
||||
func (u *ImageRepoService) CheckConn(host, user, password string) error {
|
||||
stdout, err := cmd.Execf("docker login -u %s -p %s %s", user, password, host)
|
||||
stdout, err := cmd.ExecWithCheck("docker", "login", "-u", user, "-p", password, host)
|
||||
if err != nil {
|
||||
return errors.New(string(stdout))
|
||||
return fmt.Errorf("stdout: %s, stderr: %v", stdout, err)
|
||||
}
|
||||
if strings.Contains(string(stdout), "Login Succeeded") {
|
||||
return nil
|
||||
|
||||
+110
-81
@@ -229,26 +229,29 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
|
||||
if len(req.Info) != 0 {
|
||||
command = fmt.Sprintf(" | grep '%s'", req.Info)
|
||||
}
|
||||
|
||||
for i := 0; i < len(fileList); i++ {
|
||||
if strings.HasPrefix(path.Base(fileList[i]), "secure") {
|
||||
dataItem := loadFailedSecureDatas(fmt.Sprintf("cat %s | grep -a 'Failed password for' | grep -v 'invalid' %s", fileList[i], command))
|
||||
data.FailedCount += len(dataItem)
|
||||
data.TotalCount += len(dataItem)
|
||||
if req.Status != constant.StatusSuccess {
|
||||
withAppend := len(data.Logs) < req.Page*req.PageSize
|
||||
if req.Status != constant.StatusSuccess {
|
||||
if strings.HasPrefix(path.Base(fileList[i]), "secure") {
|
||||
commandItem := fmt.Sprintf("cat %s | grep -a 'Failed password for' | grep -v 'invalid' %s", fileList[i], command)
|
||||
dataItem, itemTotal := loadFailedSecureDatas(commandItem, withAppend)
|
||||
data.FailedCount += itemTotal
|
||||
data.TotalCount += itemTotal
|
||||
data.Logs = append(data.Logs, dataItem...)
|
||||
}
|
||||
if strings.HasPrefix(path.Base(fileList[i]), "auth.log") {
|
||||
commandItem := fmt.Sprintf("cat %s | grep -a 'Connection closed by authenticating user' | grep -a 'preauth' %s", fileList[i], command)
|
||||
dataItem, itemTotal := loadFailedAuthDatas(commandItem, withAppend)
|
||||
data.FailedCount += itemTotal
|
||||
data.TotalCount += itemTotal
|
||||
data.Logs = append(data.Logs, dataItem...)
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(path.Base(fileList[i]), "auth.log") {
|
||||
dataItem := loadFailedAuthDatas(fmt.Sprintf("cat %s | grep -a 'Connection closed by authenticating user' | grep -a 'preauth' %s", fileList[i], command))
|
||||
data.FailedCount += len(dataItem)
|
||||
data.TotalCount += len(dataItem)
|
||||
if req.Status != constant.StatusSuccess {
|
||||
data.Logs = append(data.Logs, dataItem...)
|
||||
}
|
||||
}
|
||||
dataItem := loadSuccessDatas(fmt.Sprintf("cat %s | grep Accepted %s", fileList[i], command))
|
||||
data.TotalCount += len(dataItem)
|
||||
if req.Status != constant.StatusFailed {
|
||||
commandItem := fmt.Sprintf("cat %s | grep -a Accepted %s", fileList[i], command)
|
||||
dataItem, itemTotal := loadSuccessDatas(commandItem, withAppend)
|
||||
data.TotalCount += itemTotal
|
||||
data.Logs = append(data.Logs, dataItem...)
|
||||
}
|
||||
}
|
||||
@@ -257,20 +260,6 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
timeNow := time.Now()
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
qqWry, err := qqwry.NewQQwry()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load qqwry datas failed: %s", err)
|
||||
}
|
||||
var itemLogs []dto.SSHHistory
|
||||
for i := len(data.Logs) - 1; i >= 0; i-- {
|
||||
data.Logs[i].Area = qqWry.Find(data.Logs[i].Address).Area
|
||||
data.Logs[i].Date, _ = time.ParseInLocation("2006 Jan 2 15:04:05", fmt.Sprintf("%d %s", timeNow.Year(), data.Logs[i].DateStr), nyc)
|
||||
itemLogs = append(itemLogs, data.Logs[i])
|
||||
}
|
||||
data.Logs = itemLogs
|
||||
|
||||
var itemDatas []dto.SSHHistory
|
||||
total, start, end := len(data.Logs), (req.Page-1)*req.PageSize, req.Page*req.PageSize
|
||||
if start > total {
|
||||
@@ -283,6 +272,20 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
|
||||
}
|
||||
data.Logs = itemDatas
|
||||
|
||||
timeNow := time.Now()
|
||||
nyc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
qqWry, err := qqwry.NewQQwry()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load qqwry datas failed: %s", err)
|
||||
}
|
||||
var itemLogs []dto.SSHHistory
|
||||
for i := 0; i < len(data.Logs); i++ {
|
||||
data.Logs[i].Area = qqWry.Find(data.Logs[i].Address).Area
|
||||
data.Logs[i].Date, _ = time.ParseInLocation("2006 Jan 2 15:04:05", fmt.Sprintf("%d %s", timeNow.Year(), data.Logs[i].DateStr), nyc)
|
||||
itemLogs = append(itemLogs, data.Logs[i])
|
||||
}
|
||||
data.Logs = itemLogs
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
@@ -290,18 +293,17 @@ func sortFileList(fileNames []string) []string {
|
||||
if len(fileNames) < 2 {
|
||||
return fileNames
|
||||
}
|
||||
var itemFile []string
|
||||
if strings.Contains(fileNames[0], "secure") {
|
||||
if strings.HasPrefix(path.Base(fileNames[0]), "secure") {
|
||||
var itemFile []string
|
||||
sort.Slice(fileNames, func(i, j int) bool {
|
||||
return fileNames[i] < fileNames[j]
|
||||
return fileNames[i] > fileNames[j]
|
||||
})
|
||||
itemFile = append(itemFile, fileNames[1:]...)
|
||||
itemFile = append(itemFile, fileNames[0])
|
||||
itemFile = append(itemFile, fileNames[len(fileNames)-1])
|
||||
itemFile = append(itemFile, fileNames[:len(fileNames)-2]...)
|
||||
return itemFile
|
||||
}
|
||||
|
||||
sort.Slice(fileNames, func(i, j int) bool {
|
||||
return fileNames[i] > fileNames[j]
|
||||
return fileNames[i] < fileNames[j]
|
||||
})
|
||||
return fileNames
|
||||
}
|
||||
@@ -336,82 +338,109 @@ func updateSSHConf(oldFiles []string, param string, value interface{}) []string
|
||||
return newFiles
|
||||
}
|
||||
|
||||
func loadSuccessDatas(command string) []dto.SSHHistory {
|
||||
var datas []dto.SSHHistory
|
||||
func loadSuccessDatas(command string, withAppend bool) ([]dto.SSHHistory, int) {
|
||||
var (
|
||||
datas []dto.SSHHistory
|
||||
totalNum int
|
||||
)
|
||||
stdout2, err := cmd.Exec(command)
|
||||
if err == nil {
|
||||
lines := strings.Split(string(stdout2), "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(lines) == 0 {
|
||||
return datas, 0
|
||||
}
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
parts := strings.Fields(lines[i])
|
||||
if len(parts) < 14 {
|
||||
continue
|
||||
}
|
||||
historyItem := dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[6],
|
||||
User: parts[8],
|
||||
Address: parts[10],
|
||||
Port: parts[12],
|
||||
Status: constant.StatusSuccess,
|
||||
totalNum++
|
||||
if withAppend {
|
||||
historyItem := dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[6],
|
||||
User: parts[8],
|
||||
Address: parts[10],
|
||||
Port: parts[12],
|
||||
Status: constant.StatusSuccess,
|
||||
}
|
||||
datas = append(datas, historyItem)
|
||||
}
|
||||
datas = append(datas, historyItem)
|
||||
}
|
||||
}
|
||||
return datas
|
||||
return datas, totalNum
|
||||
}
|
||||
|
||||
func loadFailedAuthDatas(command string) []dto.SSHHistory {
|
||||
var datas []dto.SSHHistory
|
||||
func loadFailedAuthDatas(command string, withAppend bool) ([]dto.SSHHistory, int) {
|
||||
var (
|
||||
datas []dto.SSHHistory
|
||||
totalNum int
|
||||
)
|
||||
stdout2, err := cmd.Exec(command)
|
||||
if err == nil {
|
||||
lines := strings.Split(string(stdout2), "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(lines) == 0 {
|
||||
return datas, 0
|
||||
}
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
parts := strings.Fields(lines[i])
|
||||
if len(parts) < 14 {
|
||||
continue
|
||||
}
|
||||
historyItem := dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[8],
|
||||
User: parts[10],
|
||||
Address: parts[11],
|
||||
Port: parts[13],
|
||||
Status: constant.StatusFailed,
|
||||
totalNum++
|
||||
if withAppend {
|
||||
historyItem := dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[8],
|
||||
User: parts[10],
|
||||
Address: parts[11],
|
||||
Port: parts[13],
|
||||
Status: constant.StatusFailed,
|
||||
}
|
||||
if strings.Contains(lines[i], ": ") {
|
||||
historyItem.Message = strings.Split(lines[i], ": ")[1]
|
||||
}
|
||||
datas = append(datas, historyItem)
|
||||
}
|
||||
if strings.Contains(line, ": ") {
|
||||
historyItem.Message = strings.Split(line, ": ")[1]
|
||||
}
|
||||
datas = append(datas, historyItem)
|
||||
}
|
||||
}
|
||||
return datas
|
||||
return datas, totalNum
|
||||
}
|
||||
|
||||
func loadFailedSecureDatas(command string) []dto.SSHHistory {
|
||||
var datas []dto.SSHHistory
|
||||
func loadFailedSecureDatas(command string, withAppend bool) ([]dto.SSHHistory, int) {
|
||||
var (
|
||||
datas []dto.SSHHistory
|
||||
totalNum int
|
||||
)
|
||||
stdout2, err := cmd.Exec(command)
|
||||
if err == nil {
|
||||
lines := strings.Split(string(stdout2), "\n")
|
||||
for _, line := range lines {
|
||||
parts := strings.Fields(line)
|
||||
if len(lines) == 0 {
|
||||
return datas, 0
|
||||
}
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
parts := strings.Fields(lines[i])
|
||||
if len(parts) < 14 {
|
||||
continue
|
||||
}
|
||||
historyItem := dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[6],
|
||||
User: parts[8],
|
||||
Address: parts[10],
|
||||
Port: parts[12],
|
||||
Status: constant.StatusFailed,
|
||||
totalNum++
|
||||
if withAppend {
|
||||
historyItem := dto.SSHHistory{
|
||||
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
|
||||
AuthMode: parts[6],
|
||||
User: parts[8],
|
||||
Address: parts[10],
|
||||
Port: parts[12],
|
||||
Status: constant.StatusFailed,
|
||||
}
|
||||
if strings.Contains(lines[i], ": ") {
|
||||
historyItem.Message = strings.Split(lines[i], ": ")[1]
|
||||
}
|
||||
datas = append(datas, historyItem)
|
||||
}
|
||||
if strings.Contains(line, ": ") {
|
||||
historyItem.Message = strings.Split(line, ": ")[1]
|
||||
}
|
||||
datas = append(datas, historyItem)
|
||||
}
|
||||
}
|
||||
return datas
|
||||
return datas, totalNum
|
||||
}
|
||||
|
||||
func handleGunzip(path string) error {
|
||||
|
||||
@@ -105,6 +105,7 @@ var (
|
||||
ErrInUsed = "ErrInUsed"
|
||||
ErrObjectInUsed = "ErrObjectInUsed"
|
||||
ErrPortRules = "ErrPortRules"
|
||||
ErrRepoConn = "ErrRepoConn"
|
||||
)
|
||||
|
||||
// runtime
|
||||
|
||||
@@ -78,6 +78,7 @@ ErrTypeOfRedis: "The recovery file type does not match the current persistence m
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} is in use and cannot be deleted"
|
||||
ErrObjectInUsed: "This object is in use and cannot be deleted"
|
||||
ErrRepoConn: "The repository information contains illegal characters"
|
||||
|
||||
#runtime
|
||||
ErrDirNotFound: "The build folder does not exist! Please check file integrity!"
|
||||
|
||||
@@ -78,6 +78,7 @@ ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后
|
||||
#container
|
||||
ErrInUsed: "{{ .detail }} 正被使用,无法删除"
|
||||
ErrObjectInUsed: "该对象正被使用,无法删除"
|
||||
ErrRepoConn: "仓库信息中存在不合法的字符"
|
||||
|
||||
#runtime
|
||||
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"
|
||||
|
||||
@@ -22,7 +22,7 @@ func syncApp() {
|
||||
}
|
||||
global.LOG.Info("sync app start...")
|
||||
if err := service.NewIAppService().SyncAppListFromRemote(); err != nil {
|
||||
global.LOG.Errorf("sync app error: %s", err.Error())
|
||||
global.LOG.Errorf("sync app error")
|
||||
return
|
||||
}
|
||||
global.LOG.Info("sync app successful")
|
||||
|
||||
@@ -2,12 +2,13 @@ package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gorm.io/gorm/logger"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm/logger"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
|
||||
@@ -15,7 +15,7 @@ func BindDomain() gin.HandlerFunc {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
status, err := settingRepo.Get(settingRepo.WithByKey("BindDomain"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrDomain, constant.ErrTypeInternalServer, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
if len(status.Value) == 0 {
|
||||
|
||||
@@ -15,7 +15,7 @@ func WhiteAllow() gin.HandlerFunc {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
status, err := settingRepo.Get(settingRepo.WithByKey("AllowIPs"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrIP, constant.ErrTypeInternalServer, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ func PasswordExpired() gin.HandlerFunc {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
setting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypePasswordExpired, err)
|
||||
return
|
||||
}
|
||||
expiredDays, _ := strconv.Atoi(setting.Value)
|
||||
@@ -27,7 +27,7 @@ func PasswordExpired() gin.HandlerFunc {
|
||||
|
||||
extime, err := settingRepo.Get(settingRepo.WithByKey("ExpirationTime"))
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, err)
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypePasswordExpired, err)
|
||||
return
|
||||
}
|
||||
loc, _ := time.LoadLocation(common.LoadTimeZone())
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
@@ -75,11 +75,18 @@ func (s sftpClient) Upload(src, target string) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
defer dstFile.Close()
|
||||
ff, err := io.ReadAll(srcFile)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
||||
reader := bufio.NewReaderSize(srcFile, 128*1024*1024)
|
||||
for {
|
||||
chunk, err := reader.Peek(8 * 1024 * 1024)
|
||||
if len(chunk) != 0 {
|
||||
_, _ = dstFile.Write(chunk)
|
||||
_, _ = reader.Discard(len(chunk))
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
_, _ = dstFile.Write(ff)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
@@ -117,6 +118,43 @@ func Execf(cmdStr string, a ...interface{}) (string, error) {
|
||||
return stdout.String(), nil
|
||||
}
|
||||
|
||||
func ExecWithCheck(name string, a ...string) (string, error) {
|
||||
cmd := exec.Command(name, a...)
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
errMsg := ""
|
||||
if len(stderr.String()) != 0 {
|
||||
errMsg = fmt.Sprintf("stderr: %s", stderr.String())
|
||||
}
|
||||
if len(stdout.String()) != 0 {
|
||||
if len(errMsg) != 0 {
|
||||
errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String())
|
||||
} else {
|
||||
errMsg = fmt.Sprintf("stdout: %s", stdout.String())
|
||||
}
|
||||
}
|
||||
return errMsg, err
|
||||
}
|
||||
return stdout.String(), nil
|
||||
}
|
||||
|
||||
func CheckIllegal(args ...string) bool {
|
||||
if args == nil {
|
||||
return false
|
||||
}
|
||||
for _, arg := range args {
|
||||
if strings.Contains(arg, "&") || strings.Contains(arg, "|") || strings.Contains(arg, ";") ||
|
||||
strings.Contains(arg, "$") || strings.Contains(arg, "'") || strings.Contains(arg, "`") ||
|
||||
strings.Contains(arg, "(") || strings.Contains(arg, ")") || strings.Contains(arg, "\"") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func HasNoPasswordSudo() bool {
|
||||
cmd2 := exec.Command("sudo", "-n", "ls")
|
||||
err2 := cmd2.Run()
|
||||
|
||||
@@ -5,31 +5,31 @@ import (
|
||||
)
|
||||
|
||||
func Up(filePath string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker compose -f %s up -d", filePath)
|
||||
stdout, err := cmd.Execf("docker-compose -f %s up -d", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Down(filePath string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker compose -f %s down --remove-orphans", filePath)
|
||||
stdout, err := cmd.Execf("docker-compose -f %s down --remove-orphans", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Start(filePath string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker compose -f %s start", filePath)
|
||||
stdout, err := cmd.Execf("docker-compose -f %s start", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Stop(filePath string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker compose -f %s stop", filePath)
|
||||
stdout, err := cmd.Execf("docker-compose -f %s stop", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Restart(filePath string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker compose -f %s restart", filePath)
|
||||
stdout, err := cmd.Execf("docker-compose -f %s restart", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Operate(filePath, operation string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker compose -f %s %s", filePath, operation)
|
||||
stdout, err := cmd.Execf("docker-compose -f %s %s", filePath, operation)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/server"
|
||||
cmdUtils "github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/spf13/cobra"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
<el-divider v-if="slots.buttons" direction="vertical" />
|
||||
<slot v-if="slots.buttons" name="buttons"></slot>
|
||||
</template>
|
||||
<template #extra>
|
||||
<slot v-if="slots.extra" name="extra"></slot>
|
||||
</template>
|
||||
</el-page-header>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
<MdEditor
|
||||
v-model="upgradeInfo.releaseNote"
|
||||
previewOnly
|
||||
:theme="globalStore.$state.themeConfig.theme || 'light'"
|
||||
:theme="globalStore.$state.themeConfig.theme === 'dark' ? 'dark' : 'light'"
|
||||
/>
|
||||
</div>
|
||||
<template #footer>
|
||||
|
||||
@@ -18,13 +18,13 @@
|
||||
:stroke-width="12"
|
||||
:percentage="uploadPrecent"
|
||||
></el-progress>
|
||||
<div v-if="type === 'mysql'" class="el-upload__tip">
|
||||
<div v-if="type === 'mysql'" style="width: 80%" class="el-upload__tip">
|
||||
<span class="input-help">{{ $t('database.supportUpType') }}</span>
|
||||
<span class="input-help">
|
||||
{{ $t('database.zipFormat') }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-else class="el-upload__tip">
|
||||
<div v-else style="width: 80%" class="el-upload__tip">
|
||||
<span class="input-help">{{ $t('website.supportUpType') }}</span>
|
||||
<span class="input-help">
|
||||
{{ $t('website.zipFormat', [type + '.json']) }}
|
||||
@@ -280,7 +280,16 @@ const buttons = [
|
||||
{
|
||||
label: i18n.global.t('commons.button.recover'),
|
||||
click: (row: File.File) => {
|
||||
onRecover(row);
|
||||
if (type.value !== 'app') {
|
||||
onRecover(row);
|
||||
} else {
|
||||
ElMessageBox.confirm(i18n.global.t('app.restoreWarn'), i18n.global.t('commons.button.recover'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
}).then(async () => {
|
||||
onRecover(row);
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -30,6 +30,27 @@ const checkHost = (rule: any, value: any, callback: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
const checkIllegal = (rule: any, value: any, callback: any) => {
|
||||
if (value === '' || typeof value === 'undefined' || value == null) {
|
||||
callback(new Error(i18n.global.t('commons.rule.requiredInput')));
|
||||
return;
|
||||
}
|
||||
if (
|
||||
value.indexOf('&') !== -1 ||
|
||||
value.indexOf('|') !== -1 ||
|
||||
value.indexOf(';') !== -1 ||
|
||||
value.indexOf('$') !== -1 ||
|
||||
value.indexOf("'") !== -1 ||
|
||||
value.indexOf('`') !== -1 ||
|
||||
value.indexOf('(') !== -1 ||
|
||||
value.indexOf(')') !== -1
|
||||
) {
|
||||
callback(new Error(i18n.global.t('commons.rule.illegalInput')));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const complexityPassword = (rule: any, value: any, callback: any) => {
|
||||
if (value === '' || typeof value === 'undefined' || value == null) {
|
||||
callback(new Error(i18n.global.t('commons.rule.complexityPassword')));
|
||||
@@ -333,6 +354,7 @@ interface CommonRule {
|
||||
integerNumber: FormItemRule;
|
||||
ip: FormItemRule;
|
||||
host: FormItemRule;
|
||||
illegal: FormItemRule;
|
||||
port: FormItemRule;
|
||||
domain: FormItemRule;
|
||||
databaseName: FormItemRule;
|
||||
@@ -440,6 +462,11 @@ export const Rules: CommonRule = {
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
},
|
||||
illegal: {
|
||||
validator: checkIllegal,
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
},
|
||||
port: {
|
||||
required: true,
|
||||
trigger: 'blur',
|
||||
|
||||
@@ -97,6 +97,7 @@ const message = {
|
||||
updateSuccess: 'Update Success',
|
||||
uploadSuccess: 'Update Success',
|
||||
operate: 'Operate',
|
||||
operateConfirm: 'If you are sure about the operation, please input it manually',
|
||||
inputOrSelect: 'Please select or enter',
|
||||
backupSuccess: 'Backup Success',
|
||||
unSupportType: 'Current file type is not supported!',
|
||||
@@ -134,6 +135,7 @@ const message = {
|
||||
rePassword: 'The passwords are inconsistent. Please check and re-enter the password',
|
||||
requiredInput: 'Please enter the required fields',
|
||||
requiredSelect: 'Please select the required fields',
|
||||
illegalInput: 'There are illegal characters in the input box.',
|
||||
commonName: 'Support English, Chinese, numbers, .-, and _ length 1-30',
|
||||
userName: 'Support English, Chinese, numbers and _ length 3-30',
|
||||
simpleName: 'Support English, numbers and _ length 1-30',
|
||||
@@ -553,6 +555,7 @@ const message = {
|
||||
cache: 'Cache',
|
||||
|
||||
volume: 'Volume',
|
||||
volumeDir: 'Volume dir',
|
||||
volumeName: 'Name',
|
||||
nfsEnable: 'Enable NFS storage',
|
||||
nfsAddress: 'Address',
|
||||
@@ -632,8 +635,10 @@ const message = {
|
||||
cronjob: {
|
||||
cronTask: 'Task',
|
||||
changeStatus: 'Change status',
|
||||
disableMsg: 'The cronjob cannot continue to run after it is stopped. Do you want to stop it?',
|
||||
enableMsg: 'The cronjob has been stopped. Enable now?',
|
||||
disableMsg:
|
||||
'Stopping the scheduled task will result in the task no longer automatically executing. Do you want to continue?',
|
||||
enableMsg:
|
||||
'Enabling the scheduled task will allow the task to automatically execute on a regular basis. Do you want to continue?',
|
||||
taskType: 'Task type',
|
||||
record: 'Records',
|
||||
shell: 'Shell script',
|
||||
@@ -1139,7 +1144,7 @@ const message = {
|
||||
backupdate: 'Backup time',
|
||||
restore: 'Restore',
|
||||
restoreWarn:
|
||||
'The restore operation will restart the application and replace the data. This operation cannot be rolled back. Do you want to continue?',
|
||||
'The recovery operation will delete the current data of this application and perform a restart. This action is irreversible. Do you wish to continue?',
|
||||
update: 'update',
|
||||
upgrade: 'upgrade',
|
||||
versionSelect: 'Please select a version',
|
||||
|
||||
@@ -138,6 +138,7 @@ const message = {
|
||||
rePassword: '密码不一致,请检查后重新输入',
|
||||
requiredInput: '请填写必填项',
|
||||
requiredSelect: '请选择必选项',
|
||||
illegalInput: '输入框中存在不合法字符',
|
||||
commonName: '支持英文、中文、数字、.-和_,长度1-30',
|
||||
userName: '支持英文、中文、数字和_,长度3-30',
|
||||
simpleName: '支持英文、数字、_,长度1-30',
|
||||
@@ -564,6 +565,7 @@ const message = {
|
||||
gateway: '网关',
|
||||
|
||||
volume: '存储卷',
|
||||
volumeDir: '存储卷目录',
|
||||
nfsEnable: '启用 NFS 存储',
|
||||
nfsAddress: '地址',
|
||||
nfsAddressHelper: '支持输入 ip 或者域名',
|
||||
@@ -633,8 +635,8 @@ const message = {
|
||||
cronjob: {
|
||||
cronTask: '计划任务',
|
||||
changeStatus: '状态修改',
|
||||
disableMsg: '计划任务停止后将无法继续运行,是否停止?',
|
||||
enableMsg: '该计划任务已停止,是否启用?',
|
||||
disableMsg: '停止计划任务会导致该任务不再自动执行。是否继续?',
|
||||
enableMsg: '启用计划任务会让该任务定期自动执行。是否继续?',
|
||||
taskType: '任务类型',
|
||||
record: '报告',
|
||||
shell: 'Shell 脚本',
|
||||
@@ -1109,7 +1111,7 @@ const message = {
|
||||
backupPath: '文件路径',
|
||||
backupdate: '备份时间',
|
||||
restore: '恢复',
|
||||
restoreWarn: '恢复操作会重启应用,并替换数据,此操作不可回滚,是否继续?',
|
||||
restoreWarn: '恢复操作将删除该应用当前数据并重启。此操作不可回滚,是否继续?',
|
||||
update: '更新',
|
||||
upgrade: '升级',
|
||||
versionSelect: '请选择版本',
|
||||
@@ -1157,8 +1159,8 @@ const message = {
|
||||
containerName: '容器名称',
|
||||
containerNameHelper: '可以为空,为空自动生成',
|
||||
allowPort: '端口外部访问',
|
||||
allowPortHelper: '允许外部端口访问会放开防火墙端口,php运行环境请勿放开',
|
||||
appInstallWarn: '应用默认不放开外部访问端口,可以在高级设置中选择放开',
|
||||
allowPortHelper: '允许端口外部访问会放开防火墙端口,php运行环境请勿放开',
|
||||
appInstallWarn: '应用端口默认不允许外部访问,可以在下方高级设置中选择放开',
|
||||
upgradeStart: '开始升级!请稍后刷新页面',
|
||||
toFolder: '进入安装目录',
|
||||
editCompose: '编辑 compose 文件',
|
||||
|
||||
@@ -39,6 +39,7 @@ let timer: NodeJS.Timer | null = null;
|
||||
|
||||
const classObj = computed(() => {
|
||||
return {
|
||||
fullScreen: globalStore.isFullScreen,
|
||||
hideSidebar: menuStore.isCollapse,
|
||||
openSidebar: !menuStore.isCollapse,
|
||||
mobile: globalStore.device === DeviceType.Mobile,
|
||||
@@ -162,6 +163,12 @@ onMounted(() => {
|
||||
width: calc(100% - var(--panel-menu-hide-width));
|
||||
}
|
||||
}
|
||||
|
||||
.fullScreen {
|
||||
.main-container {
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
// for mobile response 适配移动端
|
||||
.mobile {
|
||||
.main-container {
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flx-wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.clearfix::after {
|
||||
display: block;
|
||||
height: 0;
|
||||
|
||||
@@ -65,4 +65,19 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.database-status {
|
||||
.title {
|
||||
margin-left: 10px !important;
|
||||
}
|
||||
.content {
|
||||
margin-left: 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.el-col-xs-24 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,15 @@
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="page-button">
|
||||
<fu-table-pagination
|
||||
v-model:current-page="paginationConfig.currentPage"
|
||||
v-model:page-size="paginationConfig.pageSize"
|
||||
v-bind="paginationConfig"
|
||||
@change="search(req)"
|
||||
:layout="'total, sizes, prev, pager, next, jumper'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
<Detail v-if="showDetail" :id="appId"></Detail>
|
||||
@@ -116,6 +125,12 @@ import { useI18n } from 'vue-i18n';
|
||||
|
||||
const language = useI18n().locale.value;
|
||||
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 50,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const req = reactive({
|
||||
name: '',
|
||||
tags: [],
|
||||
@@ -138,9 +153,12 @@ const getColor = (index: number) => {
|
||||
|
||||
const search = async (req: App.AppReq) => {
|
||||
loading.value = true;
|
||||
req.pageSize = paginationConfig.pageSize;
|
||||
req.page = paginationConfig.currentPage;
|
||||
await SearchApp(req)
|
||||
.then((res) => {
|
||||
apps.value = res.data.items;
|
||||
paginationConfig.total = res.data.total;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
@@ -260,4 +278,10 @@ onMounted(() => {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.page-button {
|
||||
float: right;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -83,7 +83,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-left: 10px">
|
||||
<MdEditor v-model="app.readMe" previewOnly :themes="globalStore.$state.themeConfig.theme || 'light'" />
|
||||
<MdEditor
|
||||
v-model="app.readMe"
|
||||
previewOnly
|
||||
:theme="globalStore.$state.themeConfig.theme === 'dark' ? 'dark' : 'light'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</template>
|
||||
<el-row v-loading="loading">
|
||||
<el-col :span="22" :offset="1">
|
||||
<el-alert type="info" :closable="false">
|
||||
<el-alert type="error" :closable="false">
|
||||
<p>{{ $t('app.appInstallWarn') }}</p>
|
||||
</el-alert>
|
||||
<el-form
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer v-model="logVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
|
||||
<el-drawer
|
||||
v-model="logVisiable"
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
:size="globalStore.isFullScreen ? '100%' : '50%'"
|
||||
>
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('commons.button.log')" :resource="logSearch.container" :back="handleClose" />
|
||||
<DrawerHeader :header="$t('commons.button.log')" :resource="logSearch.container" :back="handleClose">
|
||||
<template #extra v-if="!mobile">
|
||||
<el-button @click="toggleFullscreen" class="fullScreen" icon="FullScreen" plain></el-button>
|
||||
</template>
|
||||
</DrawerHeader>
|
||||
</template>
|
||||
<div>
|
||||
<el-select @change="searchLogs" style="width: 30%; float: left" v-model="logSearch.mode">
|
||||
@@ -47,23 +56,28 @@
|
||||
import { cleanContainerLog, logContainer } from '@/api/modules/container';
|
||||
import i18n from '@/lang';
|
||||
import { dateFormatForName } from '@/utils/util';
|
||||
import { nextTick, onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
|
||||
import { computed, nextTick, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import screenfull from 'screenfull';
|
||||
import { GlobalStore } from '@/store';
|
||||
|
||||
const extensions = [javascript(), oneDark];
|
||||
|
||||
const logVisiable = ref(false);
|
||||
const mobile = computed(() => {
|
||||
return globalStore.isMobile();
|
||||
});
|
||||
|
||||
const logInfo = ref();
|
||||
const view = shallowRef();
|
||||
const handleReady = (payload) => {
|
||||
view.value = payload.view;
|
||||
};
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const logSearch = reactive({
|
||||
isWatch: false,
|
||||
@@ -93,12 +107,19 @@ const timeOptions = ref([
|
||||
},
|
||||
]);
|
||||
|
||||
function toggleFullscreen() {
|
||||
if (screenfull.isEnabled) {
|
||||
screenfull.toggle();
|
||||
}
|
||||
}
|
||||
const handleClose = async () => {
|
||||
logVisiable.value = false;
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
};
|
||||
|
||||
watch(logVisiable, (val) => {
|
||||
if (screenfull.isEnabled && !val && !mobile.value) screenfull.exit();
|
||||
});
|
||||
const searchLogs = async () => {
|
||||
const res = await logContainer(logSearch);
|
||||
logInfo.value = res.data || '';
|
||||
@@ -150,6 +171,12 @@ const acceptParams = (props: DialogProps): void => {
|
||||
searchLogs();
|
||||
}
|
||||
}, 1000 * 5);
|
||||
|
||||
if (!mobile.value) {
|
||||
screenfull.on('change', () => {
|
||||
globalStore.isFullScreen = screenfull.isFullscreen;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@@ -166,4 +193,7 @@ defineExpose({
|
||||
.margin-button {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.fullScreen {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -111,10 +111,10 @@ const handleClose = () => {
|
||||
};
|
||||
const rules = reactive({
|
||||
name: [Rules.requiredInput, Rules.name],
|
||||
downloadUrl: [Rules.requiredInput],
|
||||
downloadUrl: [Rules.illegal],
|
||||
protocol: [Rules.requiredSelect],
|
||||
username: [Rules.requiredInput],
|
||||
password: [Rules.requiredInput],
|
||||
username: [Rules.illegal],
|
||||
password: [Rules.illegal],
|
||||
auth: [Rules.requiredSelect],
|
||||
});
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@
|
||||
</el-radio-group>
|
||||
<el-row style="margin-top: 20px" v-if="confShowType === 'base'">
|
||||
<el-col :span="1"><br /></el-col>
|
||||
<el-col :span="10">
|
||||
<el-form :model="form" label-position="left" :rules="rules" ref="formRef" label-width="150px">
|
||||
<el-col :xs="24" :sm="24" :md="15" :lg="12" :xl="10">
|
||||
<el-form :model="form" label-position="left" :rules="rules" ref="formRef" label-width="120px">
|
||||
<el-form-item :label="$t('container.mirrors')" prop="mirrors">
|
||||
<div style="width: 100%" v-if="form.mirrors">
|
||||
<el-input
|
||||
@@ -103,7 +103,7 @@
|
||||
<el-form-item :label="$t('container.cutLog')" prop="hasLogOption">
|
||||
<el-switch v-model="form.logOptionShow" @change="handleLogOption"></el-switch>
|
||||
<span class="input-help"></span>
|
||||
<div v-if="form.logOptionShow">
|
||||
<div v-if="logOptionShow">
|
||||
<el-tag>{{ $t('container.maxSize') }}: {{ form.logMaxSize }}</el-tag>
|
||||
<el-tag style="margin-left: 5px">
|
||||
{{ $t('container.maxFile') }}: {{ form.logMaxFile }}
|
||||
@@ -251,6 +251,7 @@ const registriesRef = ref();
|
||||
const confirmDialogRefLive = ref();
|
||||
const confirmDialogRefCgroup = ref();
|
||||
const confirmDialogRefIptable = ref();
|
||||
const logOptionShow = ref();
|
||||
|
||||
const form = reactive({
|
||||
isSwarm: false,
|
||||
@@ -437,10 +438,12 @@ const search = async () => {
|
||||
form.registries = res.data.insecureRegistries ? res.data.insecureRegistries.join('\n') : '';
|
||||
if (res.data.logMaxFile || res.data.logMaxSize) {
|
||||
form.logOptionShow = true;
|
||||
logOptionShow.value = true;
|
||||
form.logMaxFile = Number(res.data.logMaxFile);
|
||||
form.logMaxSize = res.data.logMaxSize;
|
||||
} else {
|
||||
form.logOptionShow = false;
|
||||
logOptionShow.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
<el-input v-model.number="form.logMaxSize">
|
||||
<template #append>
|
||||
<el-select v-model="form.sizeUnit" style="width: 70px">
|
||||
<el-option label="byte" value="b"></el-option>
|
||||
<el-option label="kb" value="k"></el-option>
|
||||
<el-option label="mb" value="m"></el-option>
|
||||
<el-option label="gb" value="g"></el-option>
|
||||
<el-option label="Byte" value="b"></el-option>
|
||||
<el-option label="KB" value="k"></el-option>
|
||||
<el-option label="MB" value="m"></el-option>
|
||||
<el-option label="GB" value="g"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
@@ -49,6 +49,15 @@
|
||||
<Tooltip @click="onInspect(row.name)" :text="row.name" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('container.volumeDir')" min-width="50">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="toFolder(row.mountpoint)">
|
||||
<el-icon>
|
||||
<FolderOpened />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('container.mountpoint')"
|
||||
show-overflow-tooltip
|
||||
@@ -124,7 +133,9 @@ const loadStatus = async () => {
|
||||
const goSetting = async () => {
|
||||
router.push({ name: 'ContainerSetting' });
|
||||
};
|
||||
|
||||
const toFolder = (folder: string) => {
|
||||
router.push({ path: '/hosts/files', query: { path: folder } });
|
||||
};
|
||||
const dialogCreateRef = ref<DialogExpose>();
|
||||
|
||||
interface DialogExpose {
|
||||
|
||||
@@ -15,6 +15,12 @@
|
||||
<el-button type="primary" @click="onOpenDialog('create')">
|
||||
{{ $t('commons.button.create') }}{{ $t('cronjob.cronTask') }}
|
||||
</el-button>
|
||||
<el-button plain :disabled="selects.length === 0" @click="onBatchChangeStatus('enable')">
|
||||
{{ $t('commons.button.enable') }}
|
||||
</el-button>
|
||||
<el-button plain :disabled="selects.length === 0" @click="onBatchChangeStatus('disable')">
|
||||
{{ $t('commons.button.disable') }}
|
||||
</el-button>
|
||||
<el-button plain :disabled="selects.length === 0" @click="onDelete(null)">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
@@ -320,6 +326,20 @@ const onChangeStatus = async (id: number, status: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
const onBatchChangeStatus = async (status: string) => {
|
||||
ElMessageBox.confirm(i18n.global.t('cronjob.' + status + 'Msg'), i18n.global.t('cronjob.changeStatus'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
}).then(async () => {
|
||||
let itemStatus = status === 'enable' ? 'Enable' : 'Disable';
|
||||
for (const item of selects.value) {
|
||||
await updateStatus({ id: item.id, status: itemStatus });
|
||||
}
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
search();
|
||||
});
|
||||
};
|
||||
|
||||
const onHandle = async (row: Cronjob.CronjobInfo) => {
|
||||
loading.value = true;
|
||||
await handleOnce(row.id)
|
||||
|
||||
@@ -330,7 +330,7 @@ const weekOptions = [
|
||||
{ label: i18n.global.t('cronjob.thursday'), value: 4 },
|
||||
{ label: i18n.global.t('cronjob.friday'), value: 5 },
|
||||
{ label: i18n.global.t('cronjob.saturday'), value: 6 },
|
||||
{ label: i18n.global.t('cronjob.sunday'), value: 7 },
|
||||
{ label: i18n.global.t('cronjob.sunday'), value: 0 },
|
||||
];
|
||||
const rules = reactive({
|
||||
name: [Rules.requiredInput],
|
||||
@@ -456,7 +456,9 @@ function checkScript() {
|
||||
case 'perMonth':
|
||||
return row.day > 0 && row.day < 32 && row.hour >= 0 && row.hour < 24 && row.minute >= 0 && row.minute < 60;
|
||||
case 'perWeek':
|
||||
return row.week > 0 && row.week < 8 && row.hour >= 0 && row.hour < 24 && row.minute >= 0 && row.minute < 60;
|
||||
return (
|
||||
row.week >= 0 && row.week < 7 && row.hour >= 0 && row.hour < 24 && row.minute >= 0 && row.minute < 60
|
||||
);
|
||||
case 'perDay':
|
||||
return row.hour >= 0 && row.hour < 24 && row.minute >= 0 && row.minute < 60;
|
||||
case 'perHour':
|
||||
|
||||
@@ -123,194 +123,198 @@
|
||||
</el-row>
|
||||
</template>
|
||||
<template #main>
|
||||
<el-row :gutter="20" v-show="hasRecords">
|
||||
<el-col :span="8">
|
||||
<div>
|
||||
<ul class="infinite-list" style="overflow: auto">
|
||||
<li
|
||||
v-for="(item, index) in records"
|
||||
:key="index"
|
||||
@click="forDetail(item)"
|
||||
class="infinite-list-item"
|
||||
>
|
||||
<el-icon v-if="item.status === 'Success'"><Select /></el-icon>
|
||||
<el-icon v-if="item.status === 'Waiting'"><Loading /></el-icon>
|
||||
<el-icon v-if="item.status === 'Failed'"><CloseBold /></el-icon>
|
||||
<span v-if="item.id === currentRecord.id" style="color: red">
|
||||
{{ dateFormat(0, 0, item.startTime) }}
|
||||
</span>
|
||||
<span v-else>{{ dateFormat(0, 0, item.startTime) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div style="margin-top: 10px; font-size: 12px; float: right">
|
||||
<el-pagination
|
||||
:page-size="searchInfo.pageSize"
|
||||
:current-page="searchInfo.page"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
:pager-count="5"
|
||||
:page-sizes="[6, 8, 10, 12, 14]"
|
||||
small
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="searchInfo.recordTotal"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-form label-position="top" :v-key="refresh">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-form-item class="descriptionWide" v-if="isBackup()">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('cronjob.target') }}</span>
|
||||
</template>
|
||||
<span class="status-count">{{ dialogData.rowData!.targetDir }}</span>
|
||||
<el-button
|
||||
v-if="currentRecord?.status === 'Success'"
|
||||
type="primary"
|
||||
style="margin-left: 10px"
|
||||
link
|
||||
icon="Download"
|
||||
@click="onDownload(currentRecord, dialogData.rowData!.targetDirID)"
|
||||
<div style="overflow: auto">
|
||||
<el-row :gutter="20" v-show="hasRecords" style="min-width: 900px">
|
||||
<el-col :span="8">
|
||||
<div>
|
||||
<ul class="infinite-list" style="overflow: auto">
|
||||
<li
|
||||
v-for="(item, index) in records"
|
||||
:key="index"
|
||||
@click="forDetail(item)"
|
||||
class="infinite-list-item"
|
||||
>
|
||||
{{ $t('file.download') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item class="description" v-if="dialogData.rowData!.type === 'website'">
|
||||
<el-icon v-if="item.status === 'Success'"><Select /></el-icon>
|
||||
<el-icon v-if="item.status === 'Waiting'"><Loading /></el-icon>
|
||||
<el-icon v-if="item.status === 'Failed'"><CloseBold /></el-icon>
|
||||
<span v-if="item.id === currentRecord.id" style="color: red">
|
||||
{{ dateFormat(0, 0, item.startTime) }}
|
||||
</span>
|
||||
<span v-else>{{ dateFormat(0, 0, item.startTime) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div style="margin-top: 10px; font-size: 12px; float: right">
|
||||
<el-pagination
|
||||
:page-size="searchInfo.pageSize"
|
||||
:current-page="searchInfo.page"
|
||||
@current-change="handleCurrentChange"
|
||||
@size-change="handleSizeChange"
|
||||
:pager-count="5"
|
||||
:page-sizes="[6, 8, 10, 12, 14]"
|
||||
small
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="searchInfo.recordTotal"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-form label-position="top" :v-key="refresh">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-form-item class="descriptionWide" v-if="isBackup()">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('cronjob.target') }}</span>
|
||||
</template>
|
||||
<span class="status-count">{{ dialogData.rowData!.targetDir }}</span>
|
||||
<el-button
|
||||
v-if="currentRecord?.status === 'Success'"
|
||||
type="primary"
|
||||
style="margin-left: 10px"
|
||||
link
|
||||
icon="Download"
|
||||
@click="onDownload(currentRecord, dialogData.rowData!.targetDirID)"
|
||||
>
|
||||
{{ $t('file.download') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item class="description" v-if="dialogData.rowData!.type === 'website'">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('cronjob.website') }}</span>
|
||||
</template>
|
||||
<span v-if="dialogData.rowData!.website !== 'all'" class="status-count">
|
||||
{{ dialogData.rowData!.website }}
|
||||
</span>
|
||||
<span v-else class="status-count">
|
||||
{{ $t('commons.table.all') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item class="description" v-if="dialogData.rowData!.type === 'database'">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('cronjob.database') }}</span>
|
||||
</template>
|
||||
<span v-if="dialogData.rowData!.dbName !== 'all'" class="status-count">
|
||||
{{ dialogData.rowData!.dbName }}
|
||||
</span>
|
||||
<span v-else class="status-count">
|
||||
{{ $t('commons.table.all') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item class="description" v-if="dialogData.rowData!.type === 'directory'">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('cronjob.directory') }}</span>
|
||||
</template>
|
||||
<span v-if="dialogData.rowData!.sourceDir.length <= 12" class="status-count">
|
||||
{{ dialogData.rowData!.sourceDir }}
|
||||
</span>
|
||||
<div v-else>
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
trigger="hover"
|
||||
width="250"
|
||||
:content="dialogData.rowData!.sourceDir"
|
||||
>
|
||||
<template #reference>
|
||||
<span class="status-count">
|
||||
{{ dialogData.rowData!.sourceDir.substring(0, 12) }}...
|
||||
</span>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item class="description" v-if="isBackup()">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('cronjob.retainCopies') }}</span>
|
||||
</template>
|
||||
<span class="status-count">{{ dialogData.rowData!.retainCopies }}</span>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-form-item
|
||||
class="description"
|
||||
v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'directory'"
|
||||
>
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('cronjob.website') }}</span>
|
||||
<span class="status-label">{{ $t('cronjob.exclusionRules') }}</span>
|
||||
</template>
|
||||
<span v-if="dialogData.rowData!.website !== 'all'" class="status-count">
|
||||
{{ dialogData.rowData!.website }}
|
||||
</span>
|
||||
<span v-else class="status-count">
|
||||
{{ $t('commons.table.all') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item class="description" v-if="dialogData.rowData!.type === 'database'">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('cronjob.database') }}</span>
|
||||
</template>
|
||||
<span v-if="dialogData.rowData!.dbName !== 'all'" class="status-count">
|
||||
{{ dialogData.rowData!.dbName }}
|
||||
</span>
|
||||
<span v-else class="status-count">
|
||||
{{ $t('commons.table.all') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item class="description" v-if="dialogData.rowData!.type === 'directory'">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('cronjob.directory') }}</span>
|
||||
</template>
|
||||
<span v-if="dialogData.rowData!.sourceDir.length <= 12" class="status-count">
|
||||
{{ dialogData.rowData!.sourceDir }}
|
||||
<span v-if="dialogData.rowData!.exclusionRules.length <= 12" class="status-count">
|
||||
{{ dialogData.rowData!.exclusionRules }}
|
||||
</span>
|
||||
<div v-else>
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
trigger="hover"
|
||||
width="250"
|
||||
:content="dialogData.rowData!.sourceDir"
|
||||
:content="dialogData.rowData!.exclusionRules"
|
||||
>
|
||||
<template #reference>
|
||||
<span class="status-count">
|
||||
{{ dialogData.rowData!.sourceDir.substring(0, 12) }}...
|
||||
{{ dialogData.rowData!.exclusionRules.substring(0, 12) }}...
|
||||
</span>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item class="description" v-if="isBackup()">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('cronjob.retainCopies') }}</span>
|
||||
</template>
|
||||
<span class="status-count">{{ dialogData.rowData!.retainCopies }}</span>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-form-item
|
||||
class="description"
|
||||
v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'directory'"
|
||||
>
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('cronjob.exclusionRules') }}</span>
|
||||
</template>
|
||||
<span v-if="dialogData.rowData!.exclusionRules.length <= 12" class="status-count">
|
||||
{{ dialogData.rowData!.exclusionRules }}
|
||||
</span>
|
||||
<div v-else>
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
trigger="hover"
|
||||
width="250"
|
||||
:content="dialogData.rowData!.exclusionRules"
|
||||
>
|
||||
<template #reference>
|
||||
<span class="status-count">
|
||||
{{ dialogData.rowData!.exclusionRules.substring(0, 12) }}...
|
||||
</span>
|
||||
<el-row type="flex" justify="center">
|
||||
<el-form-item class="descriptionWide">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('commons.search.timeStart') }}</span>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-row type="flex" justify="center">
|
||||
<el-form-item class="descriptionWide">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('commons.search.timeStart') }}</span>
|
||||
</template>
|
||||
<span class="status-count">{{ dateFormat(0, 0, currentRecord?.startTime) }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item class="description">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('commons.table.interval') }}</span>
|
||||
</template>
|
||||
<span class="status-count" v-if="currentRecord?.interval! <= 1000">
|
||||
{{ currentRecord?.interval }} ms
|
||||
</span>
|
||||
<span class="status-count" v-if="currentRecord?.interval! > 1000">
|
||||
{{ currentRecord?.interval! / 1000 }} s
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item class="description">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('commons.table.status') }}</span>
|
||||
</template>
|
||||
<el-tooltip v-if="currentRecord?.status === 'Failed'" placement="top">
|
||||
<template #content>
|
||||
<div style="width: 300px; word-break: break-all">
|
||||
{{ currentRecord?.message }}
|
||||
</div>
|
||||
<span class="status-count">
|
||||
{{ dateFormat(0, 0, currentRecord?.startTime) }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item class="description">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('commons.table.interval') }}</span>
|
||||
</template>
|
||||
<el-tag type="danger">{{ $t('commons.table.statusFailed') }}</el-tag>
|
||||
</el-tooltip>
|
||||
<el-tag type="success" v-if="currentRecord?.status === 'Success'">
|
||||
{{ $t('commons.table.statusSuccess') }}
|
||||
</el-tag>
|
||||
<el-tag type="info" v-if="currentRecord?.status === 'Waiting'">
|
||||
{{ $t('commons.table.statusWaiting') }}
|
||||
</el-tag>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row v-if="currentRecord?.records">
|
||||
<span>{{ $t('commons.table.records') }}</span>
|
||||
<codemirror
|
||||
ref="mymirror"
|
||||
:autofocus="true"
|
||||
:placeholder="$t('cronjob.noLogs')"
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
style="height: calc(100vh - 484px); width: 100%; margin-top: 5px"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
theme="cobalt"
|
||||
:styleActiveLine="true"
|
||||
:extensions="extensions"
|
||||
v-model="currentRecordDetail"
|
||||
:disabled="true"
|
||||
/>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<span class="status-count" v-if="currentRecord?.interval! <= 1000">
|
||||
{{ currentRecord?.interval }} ms
|
||||
</span>
|
||||
<span class="status-count" v-if="currentRecord?.interval! > 1000">
|
||||
{{ currentRecord?.interval! / 1000 }} s
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item class="description">
|
||||
<template #label>
|
||||
<span class="status-label">{{ $t('commons.table.status') }}</span>
|
||||
</template>
|
||||
<el-tooltip v-if="currentRecord?.status === 'Failed'" placement="top">
|
||||
<template #content>
|
||||
<div style="width: 300px; word-break: break-all">
|
||||
{{ currentRecord?.message }}
|
||||
</div>
|
||||
</template>
|
||||
<el-tag type="danger">{{ $t('commons.table.statusFailed') }}</el-tag>
|
||||
</el-tooltip>
|
||||
<el-tag type="success" v-if="currentRecord?.status === 'Success'">
|
||||
{{ $t('commons.table.statusSuccess') }}
|
||||
</el-tag>
|
||||
<el-tag type="info" v-if="currentRecord?.status === 'Waiting'">
|
||||
{{ $t('commons.table.statusWaiting') }}
|
||||
</el-tag>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row v-if="currentRecord?.records">
|
||||
<span>{{ $t('commons.table.records') }}</span>
|
||||
<codemirror
|
||||
ref="mymirror"
|
||||
:autofocus="true"
|
||||
:placeholder="$t('cronjob.noLogs')"
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
style="height: calc(100vh - 484px); width: 100%; margin-top: 5px"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
theme="cobalt"
|
||||
:styleActiveLine="true"
|
||||
:extensions="extensions"
|
||||
v-model="currentRecordDetail"
|
||||
:disabled="true"
|
||||
/>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="app-warn" v-show="!hasRecords">
|
||||
<div>
|
||||
<span>{{ $t('cronjob.noRecord') }}</span>
|
||||
|
||||
@@ -263,7 +263,7 @@ const onSaveConf = async () => {
|
||||
};
|
||||
|
||||
const loadContainerLog = async (containerID: string) => {
|
||||
dialogContainerLogRef.value!.acceptParams({ containerID: containerID, container: mysqlName.value });
|
||||
dialogContainerLogRef.value!.acceptParams({ containerID: containerID, container: containerID });
|
||||
};
|
||||
|
||||
const loadBaseInfo = async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div v-if="persistenceShow">
|
||||
<el-row :gutter="20" style="margin-top: 5px" class="row-box">
|
||||
<el-col :span="12">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-card class="el-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
@@ -36,7 +36,7 @@
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-card class="el-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
|
||||
@@ -1,108 +1,131 @@
|
||||
<template>
|
||||
<div v-if="statusShow">
|
||||
<div v-if="statusShow" class="database-status">
|
||||
<el-form label-position="top">
|
||||
<span class="title">{{ $t('database.baseParam') }}</span>
|
||||
<el-divider class="devider" />
|
||||
<el-row type="flex" style="margin-left: 50px" justify="center">
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">uptime_in_days</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.uptime_in_days }}</span>
|
||||
<span class="input-help">{{ $t('database.uptimeInDays') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">tcp_port</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.tcp_port }}</span>
|
||||
<span class="input-help">{{ $t('database.tcpPort') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">connected_clients</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.connected_clients }}</span>
|
||||
<span class="input-help">{{ $t('database.connectedClients') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%"></el-form-item>
|
||||
<el-row class="content">
|
||||
<el-col :xs="8" :sm="6" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">uptime_in_days</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.uptime_in_days }}</span>
|
||||
<span class="input-help">{{ $t('database.uptimeInDays') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="8" :sm="6" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">tcp_port</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.tcp_port }}</span>
|
||||
<span class="input-help">{{ $t('database.tcpPort') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="8" :sm="6" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">connected_clients</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.connected_clients }}</span>
|
||||
<span class="input-help">{{ $t('database.connectedClients') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<span class="title">{{ $t('database.performanceParam') }}</span>
|
||||
<el-divider class="devider" />
|
||||
<el-row type="flex" style="margin-left: 50px" justify="center">
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">used_memory_rss</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.used_memory_rss }}</span>
|
||||
<span class="input-help">{{ $t('database.usedMemoryRss') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">used_memory</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.used_memory }}</span>
|
||||
<span class="input-help">{{ $t('database.usedMemory') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">mem_fragmentation_ratio</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.mem_fragmentation_ratio }}</span>
|
||||
<span class="input-help">{{ $t('database.tmpTableToDBHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">total_connections_received</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.total_connections_received }}</span>
|
||||
<span class="input-help">{{ $t('database.totalConnectionsReceived') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">total_commands_processed</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.total_commands_processed }}</span>
|
||||
<span class="input-help">{{ $t('database.totalCommandsProcessed') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">instantaneous_ops_per_sec</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.instantaneous_ops_per_sec }}</span>
|
||||
<span class="input-help">{{ $t('database.instantaneousOpsPerSec') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">keyspace_hits</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.keyspace_hits }}</span>
|
||||
<span class="input-help">{{ $t('database.keyspaceHits') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">keyspace_misses</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.keyspace_misses }}</span>
|
||||
<span class="input-help">{{ $t('database.keyspaceMisses') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">hit</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.hit }}</span>
|
||||
<span class="input-help">{{ $t('database.hit') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%">
|
||||
<template #label>
|
||||
<span class="status-label">latest_fork_usec</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.latest_fork_usec }}</span>
|
||||
<span class="input-help">{{ $t('database.latestForkUsec') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 25%"></el-form-item>
|
||||
<el-form-item style="width: 25%"></el-form-item>
|
||||
<el-row class="content">
|
||||
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">used_memory_rss</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.used_memory_rss }}</span>
|
||||
<span class="input-help">{{ $t('database.usedMemoryRss') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">used_memory</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.used_memory }}</span>
|
||||
<span class="input-help">{{ $t('database.usedMemory') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">mem_fragmentation_ratio</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.mem_fragmentation_ratio }}</span>
|
||||
<span class="input-help">{{ $t('database.tmpTableToDBHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="8" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">total_connections_received</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.total_connections_received }}</span>
|
||||
<span class="input-help">{{ $t('database.totalConnectionsReceived') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">total_commands_processed</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.total_commands_processed }}</span>
|
||||
<span class="input-help">{{ $t('database.totalCommandsProcessed') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">instantaneous_ops_per_sec</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.instantaneous_ops_per_sec }}</span>
|
||||
<span class="input-help">{{ $t('database.instantaneousOpsPerSec') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">keyspace_hits</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.keyspace_hits }}</span>
|
||||
<span class="input-help">{{ $t('database.keyspaceHits') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">keyspace_misses</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.keyspace_misses }}</span>
|
||||
<span class="input-help">{{ $t('database.keyspaceMisses') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">hit</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.hit }}</span>
|
||||
<span class="input-help">{{ $t('database.hit') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<el-form-item>
|
||||
<template #label>
|
||||
<span class="status-label">latest_fork_usec</span>
|
||||
</template>
|
||||
<span class="status-count">{{ redisStatus.latest_fork_usec }}</span>
|
||||
<span class="input-help">{{ $t('database.latestForkUsec') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
@@ -169,16 +192,21 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.devider {
|
||||
display: block;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
margin: 12px 0;
|
||||
border-top: 1px var(--el-border-color) var(--el-border-style);
|
||||
}
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
margin-left: 50px;
|
||||
.database-status {
|
||||
.devider {
|
||||
display: block;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
margin: 12px 0;
|
||||
border-top: 1px var(--el-border-color) var(--el-border-style);
|
||||
}
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
margin-left: 50px;
|
||||
}
|
||||
.content {
|
||||
margin-left: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,17 +6,20 @@
|
||||
<el-col :span="24">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<span class="title">{{ $t('monitor.avgLoad') }}</span>
|
||||
<el-date-picker
|
||||
@change="search('load')"
|
||||
v-model="timeRangeLoad"
|
||||
type="datetimerange"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; margin-top: -5px; width: 360px"
|
||||
></el-date-picker>
|
||||
<div :class="mobile ? 'flx-wrap' : 'flx-justify-between'">
|
||||
<span class="title">{{ $t('monitor.avgLoad') }}</span>
|
||||
<el-date-picker
|
||||
@change="search('load')"
|
||||
v-model="timeRangeLoad"
|
||||
type="datetimerange"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="max-width: 360px; width: 100%"
|
||||
:size="mobile ? 'small' : 'default'"
|
||||
></el-date-picker>
|
||||
</div>
|
||||
</template>
|
||||
<div id="loadLoadChart" class="chart"></div>
|
||||
</el-card>
|
||||
@@ -26,17 +29,20 @@
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<span class="title">CPU</span>
|
||||
<el-date-picker
|
||||
@change="search('cpu')"
|
||||
v-model="timeRangeCpu"
|
||||
type="datetimerange"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; margin-top: -5px; width: 360px"
|
||||
></el-date-picker>
|
||||
<div :class="mobile ? 'flx-wrap' : 'flx-justify-between'">
|
||||
<span class="title">CPU</span>
|
||||
<el-date-picker
|
||||
@change="search('cpu')"
|
||||
v-model="timeRangeCpu"
|
||||
type="datetimerange"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="max-width: 360px; width: 100%"
|
||||
:size="mobile ? 'small' : 'default'"
|
||||
></el-date-picker>
|
||||
</div>
|
||||
</template>
|
||||
<div id="loadCPUChart" class="chart"></div>
|
||||
</el-card>
|
||||
@@ -44,17 +50,20 @@
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<span class="title">{{ $t('monitor.memory') }}</span>
|
||||
<el-date-picker
|
||||
@change="search('memory')"
|
||||
v-model="timeRangeMemory"
|
||||
type="datetimerange"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; margin-top: -5px; width: 360px"
|
||||
></el-date-picker>
|
||||
<div :class="mobile ? 'flx-wrap' : 'flx-justify-between'">
|
||||
<span class="title">{{ $t('monitor.memory') }}</span>
|
||||
<el-date-picker
|
||||
@change="search('memory')"
|
||||
v-model="timeRangeMemory"
|
||||
type="datetimerange"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="max-width: 360px; width: 100%"
|
||||
:size="mobile ? 'small' : 'default'"
|
||||
></el-date-picker>
|
||||
</div>
|
||||
</template>
|
||||
<div id="loadMemoryChart" class="chart"></div>
|
||||
</el-card>
|
||||
@@ -64,17 +73,20 @@
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<span class="title">{{ $t('monitor.disk') }} IO</span>
|
||||
<el-date-picker
|
||||
@change="search('io')"
|
||||
v-model="timeRangeIO"
|
||||
type="datetimerange"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; margin-top: -5px; width: 360px"
|
||||
></el-date-picker>
|
||||
<div :class="mobile ? 'flx-wrap' : 'flx-justify-between'">
|
||||
<span class="title">{{ $t('monitor.disk') }} IO</span>
|
||||
<el-date-picker
|
||||
@change="search('io')"
|
||||
v-model="timeRangeIO"
|
||||
type="datetimerange"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="max-width: 360px; width: 100%"
|
||||
:size="mobile ? 'small' : 'default'"
|
||||
></el-date-picker>
|
||||
</div>
|
||||
</template>
|
||||
<div id="loadIOChart" class="chart"></div>
|
||||
</el-card>
|
||||
@@ -82,34 +94,43 @@
|
||||
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
||||
<el-card style="overflow: inherit">
|
||||
<template #header>
|
||||
<span class="title">{{ $t('monitor.network') }} IO:</span>
|
||||
<el-popover placement="bottom" :width="200" trigger="click">
|
||||
<el-select @change="search('network')" v-model="networkChoose">
|
||||
<template #prefix>{{ $t('monitor.networkCard') }}</template>
|
||||
<div v-for="item in netOptions" :key="item">
|
||||
<el-option v-if="item === 'all'" :label="$t('commons.table.all')" :value="item" />
|
||||
<el-option v-else :label="item" :value="item" />
|
||||
</div>
|
||||
</el-select>
|
||||
<template #reference>
|
||||
<span class="networkOption" v-if="networkChoose === 'all'">
|
||||
{{ $t('commons.table.all') }}
|
||||
</span>
|
||||
<span v-else class="networkOption">
|
||||
{{ networkChoose }}
|
||||
</span>
|
||||
</template>
|
||||
</el-popover>
|
||||
<el-date-picker
|
||||
@change="search('network')"
|
||||
v-model="timeRangeNetwork"
|
||||
type="datetimerange"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="float: right; margin-top: -5px; width: 360px"
|
||||
></el-date-picker>
|
||||
<div :class="mobile ? 'flx-wrap' : 'flx-justify-between'">
|
||||
<div>
|
||||
<span class="title">{{ $t('monitor.network') }} IO:</span>
|
||||
<el-popover placement="bottom" :width="200" trigger="click">
|
||||
<el-select @change="search('network')" v-model="networkChoose">
|
||||
<template #prefix>{{ $t('monitor.networkCard') }}</template>
|
||||
<div v-for="item in netOptions" :key="item">
|
||||
<el-option
|
||||
v-if="item === 'all'"
|
||||
:label="$t('commons.table.all')"
|
||||
:value="item"
|
||||
/>
|
||||
<el-option v-else :label="item" :value="item" />
|
||||
</div>
|
||||
</el-select>
|
||||
<template #reference>
|
||||
<span class="networkOption" v-if="networkChoose === 'all'">
|
||||
{{ $t('commons.table.all') }}
|
||||
</span>
|
||||
<span v-else class="networkOption">
|
||||
{{ networkChoose }}
|
||||
</span>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
<el-date-picker
|
||||
@change="search('network')"
|
||||
v-model="timeRangeNetwork"
|
||||
type="datetimerange"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
style="max-width: 360px; width: 100%"
|
||||
:size="mobile ? 'small' : 'default'"
|
||||
></el-date-picker>
|
||||
</div>
|
||||
</template>
|
||||
<div id="loadNetworkChart" class="chart"></div>
|
||||
</el-card>
|
||||
@@ -119,13 +140,20 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { ref, reactive, onMounted, onBeforeUnmount, computed } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { loadMonitor, getNetworkOptions } from '@/api/modules/monitor';
|
||||
import { Monitor } from '@/api/interface/monitor';
|
||||
import { dateFormatWithoutYear } from '@/utils/util';
|
||||
import i18n from '@/lang';
|
||||
import MonitorRouter from '@/views/host/monitor/index.vue';
|
||||
import { GlobalStore } from '@/store';
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const mobile = computed(() => {
|
||||
return globalStore.isMobile();
|
||||
});
|
||||
|
||||
const zoomStart = ref();
|
||||
const monitorBase = ref();
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
<el-option :label="$t('commons.status.success')" value="Success"></el-option>
|
||||
<el-option :label="$t('commons.status.failed')" value="Failed"></el-option>
|
||||
</el-select>
|
||||
<el-tag type="success" size="large" style="margin-left: 15px">
|
||||
<el-tag v-if="searchStatus === 'All'" type="success" size="large" style="margin-left: 15px">
|
||||
{{ $t('commons.status.success') }}: {{ successfulCount }}
|
||||
</el-tag>
|
||||
<el-tag type="danger" size="large" style="margin-left: 5px">
|
||||
<el-tag v-if="searchStatus === 'All'" type="danger" size="large" style="margin-left: 5px">
|
||||
{{ $t('commons.status.failed') }}: {{ faliedCount }}
|
||||
</el-tag>
|
||||
</el-col>
|
||||
|
||||
@@ -123,14 +123,14 @@
|
||||
></el-empty>
|
||||
</div>
|
||||
</el-tabs>
|
||||
<el-button @click="toggleFullscreen" class="fullScreen" icon="FullScreen"></el-button>
|
||||
<el-button @click="toggleFullscreen" v-if="!mobile" class="fullScreen" icon="FullScreen"></el-button>
|
||||
|
||||
<HostDialog ref="dialogRef" @on-conn-terminal="onConnTerminal" @load-host-tree="loadHostTree" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, getCurrentInstance, watch, nextTick } from 'vue';
|
||||
import { ref, getCurrentInstance, watch, nextTick, computed } from 'vue';
|
||||
import Terminal from '@/components/terminal/index.vue';
|
||||
import HostDialog from '@/views/host/terminal/terminal/host-create.vue';
|
||||
import type Node from 'element-plus/es/components/tree/src/model/node';
|
||||
@@ -145,11 +145,13 @@ import { GlobalStore } from '@/store';
|
||||
const dialogRef = ref();
|
||||
const ctx = getCurrentInstance() as any;
|
||||
const globalStore = GlobalStore();
|
||||
const mobile = computed(() => {
|
||||
return globalStore.isMobile();
|
||||
});
|
||||
|
||||
function toggleFullscreen() {
|
||||
if (screenfull.isEnabled) {
|
||||
screenfull.toggle();
|
||||
globalStore.setScreenFull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,6 +183,7 @@ interface Tree {
|
||||
}
|
||||
|
||||
const acceptParams = async () => {
|
||||
globalStore.isFullScreen = false;
|
||||
loadCommand();
|
||||
const res = await getHostTree({});
|
||||
hostTree.value = res.data;
|
||||
@@ -206,6 +209,12 @@ const acceptParams = async () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mobile.value) {
|
||||
screenfull.on('change', () => {
|
||||
globalStore.isFullScreen = screenfull.isFullscreen;
|
||||
});
|
||||
}
|
||||
};
|
||||
const cleanTimer = () => {
|
||||
clearInterval(Number(timer));
|
||||
|
||||
@@ -15,39 +15,44 @@
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="8" :md="8" :lg="8" :xl="8">
|
||||
<TableSetting @search="search()" />
|
||||
<div class="search-button">
|
||||
<el-input
|
||||
v-model="searchIP"
|
||||
clearable
|
||||
@clear="search()"
|
||||
suffix-icon="Search"
|
||||
@keyup.enter="search()"
|
||||
@change="search()"
|
||||
:placeholder="$t('commons.button.search') + ' ip'"
|
||||
></el-input>
|
||||
<div class="flx-align-center">
|
||||
<div class="search-button">
|
||||
<el-input
|
||||
v-model="searchIP"
|
||||
clearable
|
||||
@clear="search()"
|
||||
suffix-icon="Search"
|
||||
@keyup.enter="search()"
|
||||
@change="search()"
|
||||
:placeholder="$t('commons.button.search') + ' ip'"
|
||||
></el-input>
|
||||
</div>
|
||||
<TableSetting @search="search()" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<template #search>
|
||||
<el-select v-model="searchStatus" @change="search()" clearable>
|
||||
<template #prefix>{{ $t('commons.table.status') }}</template>
|
||||
<el-option :label="$t('commons.table.all')" value=""></el-option>
|
||||
<el-option :label="$t('commons.status.success')" value="Success"></el-option>
|
||||
<el-option :label="$t('commons.status.failed')" value="Failed"></el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" plain @click="onClean()" style="margin-left: 10px">
|
||||
{{ $t('logs.deleteLogs') }}
|
||||
</el-button>
|
||||
<div class="flx-align-center">
|
||||
<el-select v-model="searchStatus" @change="search()" clearable>
|
||||
<template #prefix>{{ $t('commons.table.status') }}</template>
|
||||
<el-option :label="$t('commons.table.all')" value=""></el-option>
|
||||
<el-option :label="$t('commons.status.success')" value="Success"></el-option>
|
||||
<el-option :label="$t('commons.status.failed')" value="Failed"></el-option>
|
||||
</el-select>
|
||||
|
||||
<el-button type="primary" plain @click="onClean()" style="margin-left: 10px">
|
||||
{{ $t('logs.deleteLogs') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #main>
|
||||
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search">
|
||||
<el-table-column min-width="40" :label="$t('logs.loginIP')" prop="ip" />
|
||||
<el-table-column min-width="40" :label="$t('logs.loginAddress')" prop="address" />
|
||||
<el-table-column :label="$t('logs.loginIP')" prop="ip" />
|
||||
<el-table-column :label="$t('logs.loginAddress')" prop="address" />
|
||||
<el-table-column :label="$t('logs.loginAgent')" show-overflow-tooltip prop="agent" />
|
||||
<el-table-column min-width="40" :label="$t('logs.loginStatus')" prop="status">
|
||||
<el-table-column :label="$t('logs.loginStatus')" prop="status">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.status === 'Success'">
|
||||
<el-tag type="success">{{ $t('commons.status.success') }}</el-tag>
|
||||
|
||||
@@ -145,9 +145,9 @@ onMounted(() => {
|
||||
font-size: 40px;
|
||||
font-family: pingFangSC-Regular;
|
||||
font-weight: 600;
|
||||
// @media only screen and (max-width: 1440px) {
|
||||
// margin-left: 0;
|
||||
// }
|
||||
@media only screen and (max-width: 768px) {
|
||||
font-size: 35px;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 1110px) {
|
||||
margin-bottom: 20px;
|
||||
@@ -170,6 +170,9 @@ onMounted(() => {
|
||||
@media only screen and (max-width: 1110px) {
|
||||
margin: 60px auto 0;
|
||||
}
|
||||
@media only screen and (max-width: 768px) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -100,9 +100,9 @@ onMounted(() => {
|
||||
font-size: 40px;
|
||||
font-family: pingFangSC-Regular;
|
||||
font-weight: 600;
|
||||
// @media only screen and (max-width: 1440px) {
|
||||
// margin-left: 0;
|
||||
// }
|
||||
@media only screen and (max-width: 768px) {
|
||||
font-size: 35px;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 1110px) {
|
||||
margin-bottom: 20px;
|
||||
@@ -125,6 +125,10 @@ onMounted(() => {
|
||||
@media only screen and (max-width: 1110px) {
|
||||
margin: 60px auto 0;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -17,6 +17,7 @@ require (
|
||||
github.com/gin-contrib/gzip v0.0.6
|
||||
github.com/gin-contrib/i18n v0.0.1
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/glebarez/sqlite v1.8.0
|
||||
github.com/go-acme/lego/v4 v4.9.0
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.0.2
|
||||
github.com/go-playground/validator/v10 v10.14.0
|
||||
@@ -55,8 +56,7 @@ require (
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/sqlite v1.4.4
|
||||
gorm.io/gorm v1.24.5
|
||||
gorm.io/gorm v1.24.6
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -112,6 +112,7 @@ require (
|
||||
github.com/fvbommel/sortorder v1.0.2 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.1 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
@@ -166,7 +167,6 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/miekg/dns v1.1.50 // indirect
|
||||
@@ -200,6 +200,7 @@ require (
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rs/xid v1.4.0 // indirect
|
||||
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect
|
||||
@@ -258,6 +259,10 @@ require (
|
||||
k8s.io/client-go v0.24.1 // indirect
|
||||
k8s.io/klog/v2 v2.80.1 // indirect
|
||||
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect
|
||||
modernc.org/libc v1.22.3 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.21.1 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
@@ -276,6 +276,10 @@ github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjX
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/glebarez/go-sqlite v1.21.1 h1:7MZyUPh2XTrHS7xNEHQbrhfMZuPSzhkm2A1qgg0y5NY=
|
||||
github.com/glebarez/go-sqlite v1.21.1/go.mod h1:ISs8MF6yk5cL4n/43rSOmVMGJJjHYr7L2MbZZ5Q4E2E=
|
||||
github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc=
|
||||
github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8=
|
||||
github.com/go-acme/lego/v4 v4.9.0 h1:8Hjj44IqRS7cigshMyFQ+0pIZvwgkG/+9A0UnNh7G8A=
|
||||
github.com/go-acme/lego/v4 v4.9.0/go.mod h1:g3JRUyWS3L/VObpp4bCxzJftKyf/Wba8QrSSnoiqjg4=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
@@ -431,6 +435,7 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
@@ -504,7 +509,6 @@ github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLl
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
@@ -592,9 +596,7 @@ github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
|
||||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
@@ -748,6 +750,9 @@ github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdk
|
||||
github.com/qiniu/go-sdk/v7 v7.14.0 h1:6icihMTKHoKMmeU1mqtIoHUv7c1LrLjYm8wTQaYDqmw=
|
||||
github.com/qiniu/go-sdk/v7 v7.14.0/go.mod h1:btsaOc8CA3hdVloULfFdDgDc+g4f3TDZEFsDY0BLE+w=
|
||||
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
@@ -1339,12 +1344,10 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.3.3 h1:jXG9ANrwBc4+bMvBcSl8zCfPBaVoPyBEBshA8dA93X8=
|
||||
gorm.io/driver/postgres v1.3.7 h1:FKF6sIMDHDEvvMF/XJvbnCl0nu6KSKUaPXevJ4r+VYQ=
|
||||
gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
|
||||
gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
||||
gorm.io/driver/sqlite v1.3.2 h1:nWTy4cE52K6nnMhv23wLmur9Y3qWbZvOBz+V4PrGAxg=
|
||||
gorm.io/driver/sqlserver v1.3.2 h1:yYt8f/xdAKLY7lCCyXxIUEgZ/WsURos3dHrx8MKFGAk=
|
||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.5 h1:g6OPREKqqlWq4kh/3MCQbZKImeB9e6Xgc4zD+JgNZGE=
|
||||
gorm.io/gorm v1.24.5/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
|
||||
gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -1368,6 +1371,14 @@ k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2R
|
||||
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs=
|
||||
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY=
|
||||
modernc.org/libc v1.22.3/go.mod h1:MQrloYP209xa2zHome2a8HLiLm6k0UT8CoHpV74tOFw=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.21.1 h1:GyDFqNnESLOhwwDRaHGdp2jKLDzpyT/rNLglX3ZkMSU=
|
||||
modernc.org/sqlite v1.21.1/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
|
||||
Reference in New Issue
Block a user