Compare commits

...
14 Commits
Author SHA1 Message Date
ssongliu c90ad0f4ed fix: 解决容器创建默认 Cmd 失败的问题 (#2641) 2023-10-23 15:27:04 +08:00
ssongliu 61bbfac3cd fix: 计划任务报告样式调整 (#2640) 2023-10-23 15:27:01 +08:00
ssongliu fcca83b3e9 fix: 解决容器创建默认 Entrypoint 失败的问题 (#2638) 2023-10-23 14:33:10 +08:00
ssongliu 8244b679fe feat: 优化计划任务脚本执行输出 (#2637) 2023-10-23 14:33:07 +08:00
ssongliu 604949de18 feat: 优化计划任务脚本执行错误处理 (#2635) 2023-10-23 14:33:03 +08:00
ssongliu 7716df55f9 fix: 修改 OneDrive 授权码长度限制 (#2629)
Refs #2626
2023-10-23 14:33:00 +08:00
igophperand江杭辉 08fe607012 fix: ssh日志识别错误 (#2630)
Co-authored-by: 江杭辉 <jianghanghui@k.app>
2023-10-23 14:32:54 +08:00
igophper 4222a985f9 fix: 修复数据库修改端口错误 (#2627) 2023-10-23 14:32:50 +08:00
ssongliu 0ed5ae71c5 feat: OneDrive 授权码增加格式校验 (#2614)
Refs #2610
2023-10-22 08:43:44 +08:00
ssongliu a2bfa5acaa feat: 解决容器编辑内存限额失败的问题 (#2607)
Refs #2608
2023-10-22 08:43:27 +08:00
ssongliu 609aa04aba fix: 系统用户名、密码增加空格校验 (#2604)
Refs #2602
2023-10-22 08:43:07 +08:00
ssongliu 0b5b90d0c5 fix: 同步数据库时,忽略 recycle_bin (#2591)
Refs #2589
2023-10-22 08:42:41 +08:00
ssongliu 0449100e23 fix: 解决容器设置 host 网络失败的问题 (#2582)
Refs #2550
2023-10-22 08:42:13 +08:00
ssongliu 5f009152d3 feat: SSH 登陆日志适配 Debian12 (#2567)
Refs #2367
2023-10-22 08:41:22 +08:00
21 changed files with 272 additions and 162 deletions
+1 -1
View File
@@ -518,7 +518,7 @@ func (a *AppInstallService) ChangeAppPort(req request.PortUpdate) error {
return nil return nil
} }
if err := updateInstallInfoInDB(req.Key, "", "port", true, strconv.FormatInt(req.Port, 10)); err != nil { if err := updateInstallInfoInDB(req.Key, req.Name, "port", true, strconv.FormatInt(req.Port, 10)); err != nil {
return nil return nil
} }
+20 -5
View File
@@ -316,11 +316,6 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error {
return buserr.New(constant.ErrContainerName) return buserr.New(constant.ErrContainerName)
} }
config, hostConf, networkConf, err := loadConfigInfo(true, req, nil)
if err != nil {
return err
}
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
if !checkImageExist(client, req.Image) || req.ForcePull { if !checkImageExist(client, req.Image) || req.ForcePull {
if err := pullImages(ctx, client, req.Image); err != nil { if err := pullImages(ctx, client, req.Image); err != nil {
if !req.ForcePull { if !req.ForcePull {
@@ -329,6 +324,21 @@ func (u *ContainerService) ContainerCreate(req dto.ContainerOperate) error {
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err) global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
} }
} }
imageInfo, _, err := client.ImageInspectWithRaw(ctx, req.Image)
if err != nil {
return err
}
if len(req.Entrypoint) == 0 {
req.Entrypoint = imageInfo.Config.Entrypoint
}
if len(req.Cmd) == 0 {
req.Cmd = imageInfo.Config.Cmd
}
config, hostConf, networkConf, err := loadConfigInfo(true, req, nil)
if err != nil {
return err
}
global.LOG.Infof("new container info %s has been made, now start to create", req.Name)
container, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name) container, err := client.ContainerCreate(ctx, config, hostConf, networkConf, &v1.Platform{}, req.Name)
if err != nil { if err != nil {
_ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true}) _ = client.ContainerRemove(ctx, req.Name, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
@@ -867,6 +877,10 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
config.Tty = req.Tty config.Tty = req.Tty
if len(req.Network) != 0 { if len(req.Network) != 0 {
switch req.Network {
case "host", "none", "bridge":
hostConf.NetworkMode = container.NetworkMode(req.Network)
}
networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}} networkConf.EndpointsConfig = map[string]*network.EndpointSettings{req.Network: {}}
} else { } else {
networkConf = network.NetworkingConfig{} networkConf = network.NetworkingConfig{}
@@ -881,6 +895,7 @@ func loadConfigInfo(isCreate bool, req dto.ContainerOperate, oldContainer *types
} }
hostConf.NanoCPUs = int64(req.NanoCPUs * 1000000000) hostConf.NanoCPUs = int64(req.NanoCPUs * 1000000000)
hostConf.Memory = int64(req.Memory * 1024 * 1024) hostConf.Memory = int64(req.Memory * 1024 * 1024)
hostConf.MemorySwap = 0
hostConf.PortBindings = portMap hostConf.PortBindings = portMap
hostConf.Binds = []string{} hostConf.Binds = []string{}
config.Volumes = make(map[string]struct{}) config.Volumes = make(map[string]struct{})
+92 -36
View File
@@ -287,9 +287,9 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
case constant.StatusSuccess: case constant.StatusSuccess:
commandItem = fmt.Sprintf("cat %s | grep -a Accepted %s", file.Name, command) commandItem = fmt.Sprintf("cat %s | grep -a Accepted %s", file.Name, command)
case constant.StatusFailed: case constant.StatusFailed:
commandItem = fmt.Sprintf("cat %s | grep -a 'Connection closed by authenticating user' | grep -a 'preauth' %s", file.Name, command) commandItem = fmt.Sprintf("cat %s | grep -aE 'Failed password for|Connection closed by authenticating user' | grep -a 'preauth' %s", file.Name, command)
default: default:
commandItem = fmt.Sprintf("cat %s | grep -aE \"(Connection closed by authenticating user|Accepted)\" | grep -v 'invalid' %s", file.Name, command) commandItem = fmt.Sprintf("cat %s | grep -aE \"(Failed password for|Connection closed by authenticating user|Accepted)\" | grep -v 'invalid' %s", file.Name, command)
} }
} }
dataItem, successCount, failedCount := loadSSHData(commandItem, showCountFrom, showCountTo, file.Year, qqWry, nyc) dataItem, successCount, failedCount := loadSSHData(commandItem, showCountFrom, showCountTo, file.Year, qqWry, nyc)
@@ -466,7 +466,7 @@ func loadSSHData(command string, showCountFrom, showCountTo, currentYear int, qq
if len(itemData.Address) != 0 { if len(itemData.Address) != 0 {
if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo { if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo {
itemData.Area = qqWry.Find(itemData.Address).Area itemData.Area = qqWry.Find(itemData.Address).Area
itemData.Date, _ = time.ParseInLocation("2006 Jan 2 15:04:05", fmt.Sprintf("%d %s", currentYear, itemData.DateStr), nyc) itemData.Date = loadDate(currentYear, itemData.DateStr, nyc)
datas = append(datas, itemData) datas = append(datas, itemData)
} }
failedCount++ failedCount++
@@ -476,7 +476,7 @@ func loadSSHData(command string, showCountFrom, showCountTo, currentYear int, qq
if len(itemData.Address) != 0 { if len(itemData.Address) != 0 {
if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo { if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo {
itemData.Area = qqWry.Find(itemData.Address).Area itemData.Area = qqWry.Find(itemData.Address).Area
itemData.Date, _ = time.ParseInLocation("2006 Jan 2 15:04:05", fmt.Sprintf("%d %s", currentYear, itemData.DateStr), nyc) itemData.Date = loadDate(currentYear, itemData.DateStr, nyc)
datas = append(datas, itemData) datas = append(datas, itemData)
} }
failedCount++ failedCount++
@@ -486,7 +486,7 @@ func loadSSHData(command string, showCountFrom, showCountTo, currentYear int, qq
if len(itemData.Address) != 0 { if len(itemData.Address) != 0 {
if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo { if successCount+failedCount >= showCountFrom && successCount+failedCount < showCountTo {
itemData.Area = qqWry.Find(itemData.Address).Area itemData.Area = qqWry.Find(itemData.Address).Area
itemData.Date, _ = time.ParseInLocation("2006 Jan 2 15:04:05", fmt.Sprintf("%d %s", currentYear, itemData.DateStr), nyc) itemData.Date = loadDate(currentYear, itemData.DateStr, nyc)
datas = append(datas, itemData) datas = append(datas, itemData)
} }
successCount++ successCount++
@@ -540,16 +540,31 @@ func loadSSHDataForAnalysis(analysisMap map[string]dto.SSHLogAnalysis, commandIt
func loadSuccessDatas(line string) dto.SSHHistory { func loadSuccessDatas(line string) dto.SSHHistory {
var data dto.SSHHistory var data dto.SSHHistory
parts := strings.Fields(line) parts := strings.Fields(line)
if len(parts) < 14 { t, err := time.Parse("2006-01-02T15:04:05.999999-07:00", parts[0])
return data if err != nil {
} if len(parts) < 14 {
data = dto.SSHHistory{ return data
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]), }
AuthMode: parts[6], data = dto.SSHHistory{
User: parts[8], DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
Address: parts[10], AuthMode: parts[6],
Port: parts[12], User: parts[8],
Status: constant.StatusSuccess, Address: parts[10],
Port: parts[12],
Status: constant.StatusSuccess,
}
} else {
if len(parts) < 12 {
return data
}
data = dto.SSHHistory{
DateStr: t.Format("2006 Jan 2 15:04:05"),
AuthMode: parts[4],
User: parts[6],
Address: parts[8],
Port: parts[10],
Status: constant.StatusSuccess,
}
} }
return data return data
} }
@@ -557,16 +572,31 @@ func loadSuccessDatas(line string) dto.SSHHistory {
func loadFailedAuthDatas(line string) dto.SSHHistory { func loadFailedAuthDatas(line string) dto.SSHHistory {
var data dto.SSHHistory var data dto.SSHHistory
parts := strings.Fields(line) parts := strings.Fields(line)
if len(parts) < 14 { t, err := time.Parse("2006-01-02T15:04:05.999999-07:00", parts[0])
return data if err != nil {
} if len(parts) < 14 {
data = dto.SSHHistory{ return data
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]), }
AuthMode: parts[8], data = dto.SSHHistory{
User: parts[10], DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
Address: parts[11], AuthMode: parts[8],
Port: parts[13], User: parts[10],
Status: constant.StatusFailed, Address: parts[11],
Port: parts[13],
Status: constant.StatusFailed,
}
} else {
if len(parts) < 12 {
return data
}
data = dto.SSHHistory{
DateStr: t.Format("2006 Jan 2 15:04:05"),
AuthMode: parts[6],
User: parts[7],
Address: parts[9],
Port: parts[11],
Status: constant.StatusFailed,
}
} }
if strings.Contains(line, ": ") { if strings.Contains(line, ": ") {
data.Message = strings.Split(line, ": ")[1] data.Message = strings.Split(line, ": ")[1]
@@ -577,21 +607,39 @@ func loadFailedAuthDatas(line string) dto.SSHHistory {
func loadFailedSecureDatas(line string) dto.SSHHistory { func loadFailedSecureDatas(line string) dto.SSHHistory {
var data dto.SSHHistory var data dto.SSHHistory
parts := strings.Fields(line) parts := strings.Fields(line)
if len(parts) < 14 { t, err := time.Parse("2006-01-02T15:04:05.999999-07:00", parts[0])
return data if err != nil {
} if len(parts) < 14 {
data = dto.SSHHistory{ return data
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]), }
AuthMode: parts[6], data = dto.SSHHistory{
User: parts[8], DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
Address: parts[10], AuthMode: parts[6],
Port: parts[12], User: parts[8],
Status: constant.StatusFailed, Address: parts[10],
Port: parts[12],
Status: constant.StatusFailed,
}
} else {
if len(parts) < 12 {
return data
}
index := 0
if strings.Contains(line, " invalid user") {
index = 2
}
data = dto.SSHHistory{
DateStr: t.Format("2006 Jan 2 15:04:05"),
AuthMode: parts[4],
User: parts[index+6],
Address: parts[index+8],
Port: parts[index+10],
Status: constant.StatusFailed,
}
} }
if strings.Contains(line, ": ") { if strings.Contains(line, ": ") {
data.Message = strings.Split(line, ": ")[1] data.Message = strings.Split(line, ": ")[1]
} }
return data return data
} }
@@ -610,3 +658,11 @@ func loadServiceName() (string, error) {
} }
return "", errors.New("The ssh or sshd service is unavailable") return "", errors.New("The ssh or sshd service is unavailable")
} }
func loadDate(currentYear int, DateStr string, nyc *time.Location) time.Time {
itemDate, err := time.ParseInLocation("2006 Jan 2 15:04:05", fmt.Sprintf("%d %s", currentYear, DateStr), nyc)
if err != nil {
itemDate, _ = time.ParseInLocation("2006 Jan 2 15:04:05", DateStr, nyc)
}
return itemDate
}
+5
View File
@@ -133,3 +133,8 @@ var (
var ( var (
ErrFirewall = "ErrFirewall" ErrFirewall = "ErrFirewall"
) )
// cronjob
var (
ErrBashExecute = "ErrBashExecute"
)
+4 -1
View File
@@ -124,4 +124,7 @@ ErrConfigAlreadyExist: "A configuration file with the same name already exists"
ErrUserFindErr: "Failed to find user {{ .name }} {{ .err }}" ErrUserFindErr: "Failed to find user {{ .name }} {{ .err }}"
#ssh #ssh
ErrFirewall: "No firewalld or ufw service is detected. Please check and try again!" ErrFirewall: "No firewalld or ufw service is detected. Please check and try again!"
#cronjob
ErrBashExecute: "Script execution error, please check the specific information in the task output text area."
+4 -1
View File
@@ -124,4 +124,7 @@ ErrConfigAlreadyExist: "已存在同名配置文件"
ErrUserFindErr: "用戶 {{ .name }} 查找失敗 {{ .err }}" ErrUserFindErr: "用戶 {{ .name }} 查找失敗 {{ .err }}"
#ssh #ssh
ErrFirewall: "當前未檢測到系統 firewalld 或 ufw 服務,請檢查後重試!" ErrFirewall: "當前未檢測到系統 firewalld 或 ufw 服務,請檢查後重試!"
#cronjob
ErrBashExecute: "腳本執行錯誤,請在任務輸出文本域中查看具體信息。"
+3
View File
@@ -125,3 +125,6 @@ ErrUserFindErr: "用户 {{ .name }} 查找失败 {{ .err }}"
#ssh #ssh
ErrFirewall: "当前未检测到系统 firewalld 或 ufw 服务,请检查后重试!" ErrFirewall: "当前未检测到系统 firewalld 或 ufw 服务,请检查后重试!"
#cronjob
ErrBashExecute: "脚本执行错误,请在任务输出文本域中查看具体信息。"
+7 -10
View File
@@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"io"
"os/exec" "os/exec"
"strings" "strings"
"time" "time"
@@ -88,23 +89,19 @@ func ExecCronjobWithTimeOut(cmdStr string, workdir string, timeout time.Duration
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout cmd.Stdout = &stdout
cmd.Stderr = &stderr cmd.Stderr = &stderr
output := new(bytes.Buffer)
cmd.Stdout = io.MultiWriter(output, cmd.Stdout)
cmd.Stderr = io.MultiWriter(output, cmd.Stderr)
err := cmd.Run() err := cmd.Run()
if ctx.Err() == context.DeadlineExceeded { if ctx.Err() == context.DeadlineExceeded {
return "", buserr.New(constant.ErrCmdTimeout) return "", buserr.New(constant.ErrCmdTimeout)
} }
errMsg := ""
if len(stderr.String()) != 0 { if len(stderr.String()) != 0 {
errMsg = fmt.Sprintf("stderr:\n %s", stderr.String()) err = buserr.New(constant.ErrBashExecute)
} }
if len(stdout.String()) != 0 { return output.String(), err
if len(errMsg) != 0 {
errMsg = fmt.Sprintf("%s \n\n; stdout:\n %s", errMsg, stdout.String())
} else {
errMsg = fmt.Sprintf("stdout:\n %s", stdout.String())
}
}
return errMsg, err
} }
func Execf(cmdStr string, a ...interface{}) (string, error) { func Execf(cmdStr string, a ...interface{}) (string, error) {
-26
View File
@@ -1,26 +0,0 @@
package encrypt
import (
"fmt"
"testing"
"github.com/1Panel-dev/1Panel/backend/init/viper"
)
func TestStringEncrypt(t *testing.T) {
viper.Init()
p, err := StringEncrypt("1Panel@2022")
if err != nil {
t.Fatal(err)
}
fmt.Println(p)
}
func TestStringDecrypt(t *testing.T) {
viper.Init()
p, err := StringDecrypt("dXn5bVtea+KVLDrLJlpnPIJNfW8TAMmqX1QNMdSGp88=")
if err != nil {
t.Fatal(err)
}
fmt.Println(p)
}
+1 -1
View File
@@ -269,7 +269,7 @@ func (r *Local) SyncDB(version string) ([]SyncDBInfo, error) {
if len(parts) != 2 { if len(parts) != 2 {
continue continue
} }
if parts[0] == "SCHEMA_NAME" || parts[0] == "information_schema" || parts[0] == "mysql" || parts[0] == "performance_schema" || parts[0] == "sys" { if parts[0] == "SCHEMA_NAME" || parts[0] == "information_schema" || parts[0] == "mysql" || parts[0] == "performance_schema" || parts[0] == "sys" || parts[0] == "recycle_bin" {
continue continue
} }
dataItem := SyncDBInfo{ dataItem := SyncDBInfo{
+1 -1
View File
@@ -279,7 +279,7 @@ func (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) {
if err = rows.Scan(&dbName, &charsetName); err != nil { if err = rows.Scan(&dbName, &charsetName); err != nil {
return datas, err return datas, err
} }
if dbName == "information_schema" || dbName == "mysql" || dbName == "performance_schema" || dbName == "sys" { if dbName == "information_schema" || dbName == "mysql" || dbName == "performance_schema" || dbName == "sys" || dbName == "recycle_bin" {
continue continue
} }
dataItem := SyncDBInfo{ dataItem := SyncDBInfo{
+13
View File
@@ -1,6 +1,13 @@
import i18n from '@/lang'; import i18n from '@/lang';
import { FormItemRule } from 'element-plus'; import { FormItemRule } from 'element-plus';
const checkNoSpace = (rule: any, value: any, callback: any) => {
if (value.indexOf(' ') !== -1) {
return callback(new Error(i18n.global.t('setting.noSpace')));
}
callback();
};
const checkIp = (rule: any, value: any, callback: any) => { const checkIp = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) { if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.requiredInput'))); callback(new Error(i18n.global.t('commons.rule.requiredInput')));
@@ -431,6 +438,7 @@ interface CommonRule {
requiredInput: FormItemRule; requiredInput: FormItemRule;
requiredSelect: FormItemRule; requiredSelect: FormItemRule;
requiredSelectBusiness: FormItemRule; requiredSelectBusiness: FormItemRule;
noSpace: FormItemRule;
name: FormItemRule; name: FormItemRule;
userName: FormItemRule; userName: FormItemRule;
simpleName: FormItemRule; simpleName: FormItemRule;
@@ -484,6 +492,11 @@ export const Rules: CommonRule = {
message: i18n.global.t('commons.rule.requiredSelect'), message: i18n.global.t('commons.rule.requiredSelect'),
trigger: 'change', trigger: 'change',
}, },
noSpace: {
required: true,
validator: checkNoSpace,
trigger: 'blur',
},
simpleName: { simpleName: {
required: true, required: true,
validator: checkSimpleName, validator: checkSimpleName,
+2
View File
@@ -1050,6 +1050,7 @@ const message = {
oldPassword: 'Original password', oldPassword: 'Original password',
newPassword: 'New password', newPassword: 'New password',
retryPassword: 'Confirm password', retryPassword: 'Confirm password',
noSpace: 'Input information cannot include space characters',
duplicatePassword: 'The new password cannot be the same as the original password, please re-enter!', duplicatePassword: 'The new password cannot be the same as the original password, please re-enter!',
diskClean: 'Cache Clean', diskClean: 'Cache Clean',
@@ -1064,6 +1065,7 @@ const message = {
SFTP: 'SFTP', SFTP: 'SFTP',
OneDrive: 'Microsoft OneDrive', OneDrive: 'Microsoft OneDrive',
backupDir: 'Backup dir', backupDir: 'Backup dir',
codeWarning: 'The current authorization code format is incorrect, please confirm again!',
isCN: 'Domestic version', isCN: 'Domestic version',
code: 'Auth code', code: 'Auth code',
codeHelper: codeHelper:
+2
View File
@@ -1006,6 +1006,7 @@ const message = {
oldPassword: '原密碼', oldPassword: '原密碼',
newPassword: '新密碼', newPassword: '新密碼',
retryPassword: '確認密碼', retryPassword: '確認密碼',
noSpace: '輸入信息不能包括空格符號',
duplicatePassword: '新密碼不能與原始密碼一致,請重新輸入!', duplicatePassword: '新密碼不能與原始密碼一致,請重新輸入!',
diskClean: '缓存清理', diskClean: '缓存清理',
@@ -1019,6 +1020,7 @@ const message = {
MINIO: 'MINIO', MINIO: 'MINIO',
SFTP: 'SFTP', SFTP: 'SFTP',
OneDrive: '微軟 OneDrive', OneDrive: '微軟 OneDrive',
codeWarning: '當前授權碼格式錯誤,請重新確認!',
backupDir: '備份路徑', backupDir: '備份路徑',
isCN: '國內版', isCN: '國內版',
code: '授權碼', code: '授權碼',
+2
View File
@@ -1006,6 +1006,7 @@ const message = {
oldPassword: '原密码', oldPassword: '原密码',
newPassword: '新密码', newPassword: '新密码',
retryPassword: '确认密码', retryPassword: '确认密码',
noSpace: '输入信息不能包含空格符',
duplicatePassword: '新密码不能与原始密码一致,请重新输入!', duplicatePassword: '新密码不能与原始密码一致,请重新输入!',
diskClean: '缓存清理', diskClean: '缓存清理',
@@ -1019,6 +1020,7 @@ const message = {
MINIO: 'MINIO', MINIO: 'MINIO',
SFTP: 'SFTP', SFTP: 'SFTP',
OneDrive: '微软 OneDrive', OneDrive: '微软 OneDrive',
codeWarning: '当前授权码格式错误,请重新确认!',
backupDir: '备份路径', backupDir: '备份路径',
isCN: '国内版', isCN: '国内版',
code: '授权码', code: '授权码',
+1 -1
View File
@@ -77,7 +77,7 @@
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('cronjob.cronSpec')" :min-width="120"> <el-table-column :label="$t('cronjob.cronSpec')" show-overflow-tooltip :min-width="120">
<template #default="{ row }"> <template #default="{ row }">
<span v-if="row.specType.indexOf('N') === -1 || row.specType === 'perWeek'"> <span v-if="row.specType.indexOf('N') === -1 || row.specType === 'perWeek'">
{{ $t('cronjob.' + row.specType) }}&nbsp; {{ $t('cronjob.' + row.specType) }}&nbsp;
+33 -25
View File
@@ -125,23 +125,29 @@
<template #main> <template #main>
<div class="mainClass"> <div class="mainClass">
<el-row :gutter="20" v-show="hasRecords" class="mainRowClass"> <el-row :gutter="20" v-show="hasRecords" class="mainRowClass">
<el-col :span="8"> <el-col :span="6">
<div> <div>
<ul class="infinite-list" style="overflow: auto"> <ul class="infinite-list" style="overflow: auto">
<li <el-card
v-for="(item, index) in records" v-for="(item, index) in records"
:key="index" :key="index"
@click="forDetail(item)" @click="forDetail(item)"
class="infinite-list-item" class="infinite-list-item"
> >
<el-icon v-if="item.status === 'Success'"><Select /></el-icon> <el-tag v-if="item.status === 'Success'" type="success">
<el-icon v-if="item.status === 'Waiting'"><Loading /></el-icon> {{ $t('commons.status.success') }}
<el-icon v-if="item.status === 'Failed'"><CloseBold /></el-icon> </el-tag>
<span v-if="item.id === currentRecord.id" style="color: red"> <el-tag v-if="item.status === 'Waiting'" type="info">
{{ $t('commons.status.waiting') }}
</el-tag>
<el-tag v-if="item.status === 'Failed'" type="danger">
{{ $t('commons.status.failed') }}
</el-tag>
<span v-if="item.id === currentRecord.id" class="select-card">
{{ dateFormat(0, 0, item.startTime) }} {{ dateFormat(0, 0, item.startTime) }}
</span> </span>
<span v-else>{{ dateFormat(0, 0, item.startTime) }}</span> <span v-else>{{ dateFormat(0, 0, item.startTime) }}</span>
</li> </el-card>
</ul> </ul>
<div style="margin-top: 10px; font-size: 12px; float: right"> <div style="margin-top: 10px; font-size: 12px; float: right">
<el-pagination <el-pagination
@@ -158,7 +164,7 @@
</div> </div>
</div> </div>
</el-col> </el-col>
<el-col :span="16"> <el-col :span="18">
<el-form label-position="top" :v-key="refresh"> <el-form label-position="top" :v-key="refresh">
<el-row type="flex" justify="center"> <el-row type="flex" justify="center">
<el-form-item class="descriptionWide" v-if="isBackup()"> <el-form-item class="descriptionWide" v-if="isBackup()">
@@ -714,30 +720,32 @@ defineExpose({
<style lang="scss" scoped> <style lang="scss" scoped>
.infinite-list { .infinite-list {
height: calc(100vh - 435px); height: calc(100vh - 416px);
padding: 0; padding: 0;
margin: 0; margin: 0;
}
.infinite-list .infinite-list-item { .el-card {
display: flex; border: var(--panel-border) !important;
align-items: center; &:hover {
justify-content: center; border: 1px solid var(--el-color-primary) !important;
height: 30px; box-shadow: 2px 2px 2px #888888;
background: var(--el-color-primary-light-9); }
margin: 10px;
color: var(--el-color-primary);
cursor: pointer;
&:hover {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 30px; height: 40px;
background: var(--el-color-primary-light-9); font-size: 14px;
margin: 10px; margin: 5px;
font-weight: 500; cursor: pointer;
color: red; }
.el-tag {
margin-right: 15px;
} }
} }
.select-card {
color: var(--el-color-primary);
}
.descriptionWide { .descriptionWide {
width: 40%; width: 40%;
} }
@@ -52,13 +52,20 @@
v-if="dialogData.rowData!.type === 'OneDrive'" v-if="dialogData.rowData!.type === 'OneDrive'"
:label="$t('setting.code')" :label="$t('setting.code')"
prop="varsJson.code" prop="varsJson.code"
:rules="Rules.requiredInput" :rules="rules.driveCode"
> >
<el-input clearable v-model.trim="dialogData.rowData!.varsJson['code']"> <div style="width: 100%">
<template #append> <el-input
<el-button @click="jumpAzure">{{ $t('setting.loadCode') }}</el-button> style="width: calc(100% - 80px)"
</template> :autosize="{ minRows: 3, maxRows: 15 }"
</el-input> type="textarea"
clearable
v-model.trim="dialogData.rowData!.varsJson['code']"
/>
<el-button class="append-button" @click="jumpAzure">
{{ $t('setting.loadCode') }}
</el-button>
</div>
<span class="input-help"> <span class="input-help">
{{ $t('setting.codeHelper') }} {{ $t('setting.codeHelper') }}
<el-link <el-link
@@ -247,7 +254,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { reactive, ref } from 'vue';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import FileList from '@/components/file-list/index.vue'; import FileList from '@/components/file-list/index.vue';
import i18n from '@/lang'; import i18n from '@/lang';
@@ -265,6 +272,16 @@ const buckets = ref();
const errBuckets = ref(); const errBuckets = ref();
const endpoints = ref('http'); const endpoints = ref('http');
const rules = reactive({
driveCode: [{ validator: checkDriveCode, required: true, trigger: 'blur' }],
});
function checkDriveCode(rule: any, value: any, callback: any) {
const reg = /^[A-Za-z0-9_.-]+$/;
if (!reg.test(value)) {
return callback(new Error(i18n.global.t('setting.codeWarning')));
}
callback();
}
const emit = defineEmits<{ (e: 'search'): void }>(); const emit = defineEmits<{ (e: 'search'): void }>();
@@ -411,3 +428,11 @@ defineExpose({
acceptParams, acceptParams,
}); });
</script> </script>
<style lang="scss" scoped>
.append-button {
width: 80px;
background-color: var(--el-fill-color-light);
color: var(--el-color-info);
}
</style>
+27 -36
View File
@@ -16,30 +16,22 @@
label-position="left" label-position="left"
label-width="160px" label-width="160px"
> >
<el-form-item :label="$t('setting.oldPassword')" prop="oldPassword"> <el-form-item :label="$t('setting.oldPassword')" prop="oldPass">
<el-input type="password" show-password clearable v-model="passForm.oldPassword" /> <el-input type="password" show-password clearable v-model.trim="passForm.oldPass" />
</el-form-item> </el-form-item>
<el-form-item <el-form-item v-if="!isComplexity" :label="$t('setting.newPassword')" prop="newPass">
v-if="settingForm?.complexityVerification === 'disable'" <el-input type="password" show-password clearable v-model.trim="passForm.newPass" />
:label="$t('setting.newPassword')"
prop="newPassword"
>
<el-input type="password" show-password clearable v-model="passForm.newPassword" />
</el-form-item> </el-form-item>
<el-form-item <el-form-item v-if="isComplexity" :label="$t('setting.newPassword')" prop="newPassComplexity">
v-if="settingForm?.complexityVerification === 'enable'"
:label="$t('setting.newPassword')"
prop="newPasswordComplexity"
>
<el-input <el-input
type="password" type="password"
show-password show-password
clearable clearable
v-model="passForm.newPasswordComplexity" v-model.trim="passForm.newPassComplexity"
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.retryPassword')" prop="retryPassword"> <el-form-item :label="$t('setting.retryPassword')" prop="rePass">
<el-input type="password" show-password clearable v-model="passForm.retryPassword" /> <el-input type="password" show-password clearable v-model.trim="passForm.rePass" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="submitChangePassword(passFormRef)"> <el-button type="primary" @click="submitChangePassword(passFormRef)">
@@ -61,30 +53,31 @@ import i18n from '@/lang';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import router from '@/routers'; import router from '@/routers';
import { MsgError, MsgSuccess } from '@/utils/message'; import { MsgError, MsgSuccess } from '@/utils/message';
let settingForm = ref();
let isComplexity = ref(false);
type FormInstance = InstanceType<typeof ElForm>; type FormInstance = InstanceType<typeof ElForm>;
const passFormRef = ref<FormInstance>(); const passFormRef = ref<FormInstance>();
const passRules = reactive({ const passRules = reactive({
oldPassword: [Rules.requiredInput], oldPass: [Rules.noSpace, Rules.requiredInput],
newPassword: [ newPass: [
Rules.requiredInput, Rules.requiredInput,
Rules.noSpace,
{ min: 6, message: i18n.global.t('commons.rule.commonPassword'), trigger: 'blur' }, { min: 6, message: i18n.global.t('commons.rule.commonPassword'), trigger: 'blur' },
], ],
newPasswordComplexity: [Rules.requiredInput, Rules.password], newPassComplexity: [Rules.requiredInput, Rules.noSpace, Rules.password],
retryPassword: [Rules.requiredInput, { validator: checkPassword, trigger: 'blur' }], rePass: [Rules.requiredInput, Rules.noSpace, { validator: checkPasswordSame, trigger: 'blur' }],
}); });
const passForm = reactive({ const passForm = reactive({
oldPassword: '', oldPass: '',
newPassword: '', newPass: '',
newPasswordComplexity: '', newPassComplexity: '',
retryPassword: '', rePass: '',
}); });
function checkPassword(rule: any, value: any, callback: any) { function checkPasswordSame(rule: any, value: any, callback: any) {
let password = let password = !isComplexity.value ? passForm.newPass : passForm.newPassComplexity;
settingForm.value.complexityVerification === 'disable' ? passForm.newPassword : passForm.newPasswordComplexity; if (password !== passForm.rePass) {
if (password !== passForm.retryPassword) {
return callback(new Error(i18n.global.t('commons.rule.rePassword'))); return callback(new Error(i18n.global.t('commons.rule.rePassword')));
} }
callback(); callback();
@@ -94,22 +87,20 @@ const submitChangePassword = async (formEl: FormInstance | undefined) => {
if (!formEl) return; if (!formEl) return;
formEl.validate(async (valid) => { formEl.validate(async (valid) => {
if (!valid) return; if (!valid) return;
let password = let password = !isComplexity.value ? passForm.newPass : passForm.newPassComplexity;
settingForm.value.complexityVerification === 'disable' if (password === passForm.oldPass) {
? passForm.newPassword
: passForm.newPasswordComplexity;
if (password === passForm.oldPassword) {
MsgError(i18n.global.t('setting.duplicatePassword')); MsgError(i18n.global.t('setting.duplicatePassword'));
return; return;
} }
await handleExpired({ oldPassword: passForm.oldPassword, newPassword: password }); await handleExpired({ oldPassword: passForm.oldPass, newPassword: password });
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
router.push({ name: 'home' }); router.push({ name: 'home' });
}); });
}; };
const search = async () => { const search = async () => {
const res = await getSettingInfo(); const res = await getSettingInfo();
settingForm.value = res.data; let settingForm = res.data;
isComplexity.value = settingForm?.complexityVerification === 'enable';
}; };
onMounted(() => { onMounted(() => {
@@ -8,14 +8,14 @@
<el-row type="flex" justify="center"> <el-row type="flex" justify="center">
<el-col :span="22"> <el-col :span="22">
<el-form-item :label="$t('setting.oldPassword')" prop="oldPassword"> <el-form-item :label="$t('setting.oldPassword')" prop="oldPassword">
<el-input type="password" show-password clearable v-model="passForm.oldPassword" /> <el-input type="password" show-password clearable v-model.trim="passForm.oldPassword" />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="complexityVerification === 'disable'" v-if="complexityVerification === 'disable'"
:label="$t('setting.newPassword')" :label="$t('setting.newPassword')"
prop="newPassword" prop="newPassword"
> >
<el-input type="password" show-password clearable v-model="passForm.newPassword" /> <el-input type="password" show-password clearable v-model.trim="passForm.newPassword" />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="complexityVerification === 'enable'" v-if="complexityVerification === 'enable'"
@@ -26,11 +26,11 @@
type="password" type="password"
show-password show-password
clearable clearable
v-model="passForm.newPasswordComplexity" v-model.trim="passForm.newPasswordComplexity"
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.retryPassword')" prop="retryPassword"> <el-form-item :label="$t('setting.retryPassword')" prop="retryPassword">
<el-input type="password" show-password clearable v-model="passForm.retryPassword" /> <el-input type="password" show-password clearable v-model.trim="passForm.retryPassword" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@@ -64,14 +64,16 @@ import { logOutApi } from '@/api/modules/auth';
const globalStore = GlobalStore(); const globalStore = GlobalStore();
const passFormRef = ref<FormInstance>(); const passFormRef = ref<FormInstance>();
const passRules = reactive({ const passRules = reactive({
oldPassword: [Rules.requiredInput], oldPassword: [Rules.noSpace, Rules.requiredInput],
newPassword: [ newPassword: [
Rules.requiredInput, Rules.requiredInput,
Rules.noSpace,
{ min: 6, message: i18n.global.t('commons.rule.commonPassword'), trigger: 'blur' }, { min: 6, message: i18n.global.t('commons.rule.commonPassword'), trigger: 'blur' },
], ],
newPasswordComplexity: [Rules.requiredInput, Rules.password], newPasswordComplexity: [Rules.requiredInput, Rules.noSpace, Rules.password],
retryPassword: [Rules.requiredInput, { validator: checkPassword, trigger: 'blur' }], retryPassword: [Rules.requiredInput, Rules.noSpace, { validator: checkPassword, trigger: 'blur' }],
}); });
const loading = ref(false); const loading = ref(false);
const passwordVisible = ref<boolean>(false); const passwordVisible = ref<boolean>(false);
const passForm = reactive({ const passForm = reactive({
@@ -4,11 +4,18 @@
<template #header> <template #header>
<DrawerHeader :header="$t('commons.login.username')" :back="handleClose" /> <DrawerHeader :header="$t('commons.login.username')" :back="handleClose" />
</template> </template>
<el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading"> <el-form
ref="formRef"
label-position="top"
:model="form"
@submit.prevent
v-loading="loading"
:rules="rules"
>
<el-row type="flex" justify="center"> <el-row type="flex" justify="center">
<el-col :span="22"> <el-col :span="22">
<el-form-item :label="$t('commons.login.username')" prop="userName" :rules="Rules.userName"> <el-form-item :label="$t('commons.login.username')" prop="userName">
<el-input clearable v-model="form.userName" /> <el-input clearable v-model.trim="form.userName" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@@ -46,7 +53,9 @@ const loading = ref();
const form = reactive({ const form = reactive({
userName: '', userName: '',
}); });
const rules = reactive({
userName: [Rules.userName, Rules.noSpace],
});
const formRef = ref<FormInstance>(); const formRef = ref<FormInstance>();
const acceptParams = (params: DialogProps): void => { const acceptParams = (params: DialogProps): void => {