fix: Merge dev code up to pr-8598 (#8602)

This commit is contained in:
ssongliu 2025-05-12 15:59:45 +08:00 committed by GitHub
parent ac922ae879
commit 32b9b3f31f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 242 additions and 191 deletions

View file

@ -410,11 +410,20 @@ func (b *BaseApi) CheckFile(c *gin.Context) {
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if _, err := os.Stat(req.Path); err != nil {
helper.SuccessWithData(c, false)
fileOp := files.NewFileOp()
if fileOp.Stat(req.Path) {
helper.SuccessWithData(c, true)
return
}
helper.SuccessWithData(c, true)
if req.WithInit {
if err := fileOp.CreateDir(req.Path, 0644); err != nil {
helper.SuccessWithData(c, false)
return
}
helper.SuccessWithData(c, true)
return
}
helper.SuccessWithData(c, false)
}
// @Tags File

View file

@ -59,10 +59,8 @@ func (b *BaseApi) WsSSH(c *gin.Context) {
<-quitChan
global.LOG.Info("websocket finished")
if wshandleError(wsConn, err) {
return
}
dt := time.Now().Add(time.Second)
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
}
func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
@ -125,9 +123,8 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
<-quitChan
global.LOG.Info("websocket finished")
if wshandleError(wsConn, err) {
return
}
dt := time.Now().Add(time.Second)
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
}
func loadRedisInitCmd(c *gin.Context) (string, []string, error) {

View file

@ -76,7 +76,8 @@ type FileRename struct {
}
type FilePathCheck struct {
Path string `json:"path" validate:"required"`
Path string `json:"path" validate:"required"`
WithInit bool `json:"withInit"`
}
type FilePathsCheck struct {

View file

@ -869,6 +869,9 @@ func updateInstallInfoInDB(appKey, appName, param string, value interface{}) err
"param": strings.ReplaceAll(appInstall.Param, oldVal, newVal),
"env": strings.ReplaceAll(appInstall.Env, oldVal, newVal),
}, repo.WithByID(appInstall.ID))
if appKey == "mysql" || appKey == "postgresql" {
return nil
}
}
if param == "user-password" {
oldVal = fmt.Sprintf("\"PANEL_DB_USER_PASSWORD\":\"%v\"", appInstall.UserPassword)

View file

@ -210,7 +210,9 @@ func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
}
}
cronjob.RetainCopies = 0
u.removeExpiredBackup(cronjob, accountMap, model.BackupRecord{})
if len(accountMap) != 0 {
u.removeExpiredBackup(cronjob, accountMap, model.BackupRecord{})
}
}
}
if req.IsDelete {

View file

@ -258,7 +258,8 @@ func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, accountMap
if len(account) != 0 {
global.LOG.Debugf("start upload file to %s, dir: %s", accountMap[account].name, pathUtils.Join(accountMap[account].backupPath, cloudSrc))
if _, err := accountMap[account].client.Upload(file, pathUtils.Join(accountMap[account].backupPath, cloudSrc)); err != nil {
return "", err
global.LOG.Errorf("upload file to %s failed, err: %v", accountMap[account].name, err)
continue
}
global.LOG.Debugf("upload successful!")
}
@ -285,6 +286,9 @@ func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap m
if cronjob.Type == "snapshot" {
for _, account := range accounts {
if len(account) != 0 {
if _, ok := accountMap[account]; !ok {
continue
}
_, _ = accountMap[account].client.Delete(pathUtils.Join(accountMap[account].backupPath, "system_snapshot", records[i].FileName))
}
}
@ -292,6 +296,9 @@ func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap m
} else {
for _, account := range accounts {
if len(account) != 0 {
if _, ok := accountMap[account]; !ok {
continue
}
_, _ = accountMap[account].client.Delete(pathUtils.Join(accountMap[account].backupPath, records[i].FileDir, records[i].FileName))
}
}

View file

@ -316,9 +316,8 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
<-quitChan
if wshandleError(wsConn, err) {
return
}
dt := time.Now().Add(time.Second)
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
}
var upGrader = websocket.Upgrader{

View file

@ -6,6 +6,7 @@ import (
"path"
"strconv"
"strings"
"time"
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/core/app/dto"
@ -15,6 +16,7 @@ import (
"github.com/1Panel-dev/1Panel/core/utils/terminal"
"github.com/1Panel-dev/1Panel/core/utils/xpack"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/pkg/errors"
)
@ -216,7 +218,7 @@ func (b *BaseApi) RunScript(c *gin.Context) {
<-quitChan
global.LOG.Info("websocket finished")
if wshandleError(wsConn, err) {
return
}
global.LOG.Info("websocket finished")
dt := time.Now().Add(time.Second)
_ = wsConn.WriteControl(websocket.CloseMessage, nil, dt)
}

View file

@ -99,4 +99,5 @@ func (lcmd *LocalCommand) Wait(quitChan chan bool) {
global.LOG.Errorf("ssh session wait failed, err: %v", err)
setQuit(quitChan)
}
setQuit(quitChan)
}

View file

@ -58,8 +58,8 @@ export const saveFileContent = (params: File.FileEdit) => {
return http.post<File.File>('files/save', params);
};
export const checkFile = (path: string) => {
return http.post<boolean>('files/check', { path: path });
export const checkFile = (path: string, withInit: boolean) => {
return http.post<boolean>('files/check', { path: path, withInit: withInit });
};
export const uploadFileData = (params: FormData, config: AxiosRequestConfig) => {

View file

@ -124,7 +124,9 @@ function onClose(isKeepShow: boolean = false) {
term.value.dispose();
} catch {}
}
terminalElement.value.innerHTML = '';
if (terminalElement.value) {
terminalElement.value.innerHTML = '';
}
}
// terminal start
@ -224,8 +226,8 @@ const closeRealTerminal = (ev: CloseEvent) => {
if (heartbeatTimer.value) {
clearInterval(Number(heartbeatTimer.value));
}
term.value.write('The connection has been disconnected.');
term.value.write(ev.reason);
term.value?.write('The connection has been disconnected.');
term.value?.write(ev.reason);
};
const isWsOpen = () => {

View file

@ -322,7 +322,7 @@ const onSubmit = async () => {
MsgError(i18n.global.t('commons.msg.fileNameErr'));
return;
}
const res = await checkFile(baseDir.value + file.raw.name);
const res = await checkFile(baseDir.value + file.raw.name, false);
if (res.data) {
MsgError(i18n.global.t('commons.msg.fileExist'));
return;

View file

@ -138,7 +138,8 @@ const checkIllegal = (rule: any, value: any, callback: any) => {
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 {

View file

@ -207,6 +207,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',
illegalChar: 'Injection of characters & ; $ \' ` ( ) " > < | is currently not supported',
illegalInput: 'There are illegal characters in the input box.',
commonName:
'Supports non-special characters starting with English, Chinese, numbers, .- and _, length 1-128',
@ -441,6 +442,8 @@ const message = {
permission: 'Permission',
permissionForIP: 'IP',
permissionAll: 'All of them(%)',
localhostHelper:
'Configuring database permissions as "localhost" for container deployment will prevent external access to the container. Please choose carefully!',
databaseConnInfo: 'Conn info',
rootPassword: 'Root password',
serviceName: 'Service Name',
@ -452,6 +455,7 @@ const message = {
remoteConnHelper:
'Remote connection to mysql as user root may have security risks. Therefore, perform this operation with caution.',
changePassword: 'Password',
changeConnHelper: 'This operation will modify the current database {0}. Do you want to continue?',
changePasswordHelper:
'The database has been associated with an application. Changing the password will change the database password of the application at the same time. The change takes effect after the application restarts.',
@ -1457,6 +1461,7 @@ const message = {
existFileHelper: 'The uploaded file contains a file with the same name, do you want to overwrite it?',
existFileSize: 'File size (new -> old)',
existFileDirHelper: 'The selected file/folder has a duplicate name. Please proceed with caution!',
noSuchFile: 'The file or directory was not found. Please check and try again.',
},
ssh: {
autoStart: 'Auto Start',

View file

@ -197,6 +197,7 @@ const message = {
rePassword: 'パスワードがパスワードと矛盾することを確認してください',
requiredInput: 'この項目は必須です',
requiredSelect: 'リスト内のアイテムを選択します',
illegalChar: '現在文字 & ; $ \' ` ( ) " > < | の注入はサポートされていません',
illegalInput: `このフィールドには違法なキャラクターが含まれてはなりません。`,
commonName:
'このフィールドは特別なキャラクターではなく英語中国語数字で構成されている必要がありますおよび_文字が1128の文字で構成されている必要があります',
@ -430,6 +431,8 @@ const message = {
permission: '権限',
permissionForIP: 'ip',
permissionAll: 'それらすべて',
localhostHelper:
'コンテナ展開でデータベース権限を"localhost"に設定するとコンテナ外部からのアクセスができなくなります慎重に選択してください',
databaseConnInfo: '接続情報',
rootPassword: 'ルートパスワード',
serviceName: 'サービス名',
@ -441,6 +444,7 @@ const message = {
remoteConnHelper:
'ユーザールートとしてのMySQLへのリモート接続にはセキュリティリスクがある場合がありますしたがってこの操作を慎重に実行します',
changePassword: 'パスワード',
changeConnHelper: 'この操作は現在のデータベース {0} を変更します続行しますか',
changePasswordHelper:
'データベースはアプリケーションに関連付けられていますパスワードを変更するとアプリケーションのデータベースパスワードが同時に変更されますアプリケーションが再起動した後変更は有効になります',
@ -1398,6 +1402,7 @@ const message = {
existFileHelper: 'アップロードしたファイルに同じ名前のファイルが含まれています上書きしますか',
existFileSize: 'ファイルサイズ新しい -> 古い',
existFileDirHelper: '選択したファイル/フォルダーには同じ名前のものが既に存在します慎重に操作してください',
noSuchFile: 'ファイルまたはディレクトリが見つかりませんでした確認して再試行してください',
},
ssh: {
setting: '設定',

View file

@ -196,6 +196,7 @@ const message = {
rePassword: '확인 비밀번호가 비밀번호와 일치하지 않습니다.',
requiredInput: ' 필드는 필수 항목입니다.',
requiredSelect: '목록에서 항목을 선택하세요',
illegalChar: '현재 & ; $ \' ` ( ) " > < | 문자 주입은 지원되지 않습니다',
illegalInput: ' 필드에는 유효하지 않은 문자가 포함될 없습니다.',
commonName:
' 필드는 특수 문자로 시작할 없으며, 영어, 한자, 숫자, ".", "-", "_" 문자로 구성되어야 하며 길이는 1-128자여야 합니다.',
@ -433,6 +434,8 @@ const message = {
permission: '권한',
permissionForIP: 'IP',
permissionAll: '모두(%)',
localhostHelper:
'컨테이너 배포 데이터베이스 권한을 "localhost" 설정하면 컨테이너 외부에서 접근할 없게 됩니다. 신중하게 선택하세요!',
databaseConnInfo: '연결 정보',
rootPassword: '루트 비밀번호',
serviceName: '서비스 이름',
@ -444,6 +447,7 @@ const message = {
remoteConnHelper:
'MySQL root 사용자로 원격 접속은 보안 위험을 초래할 있습니다. 따라서 작업은 신중히 수행해야 합니다.',
changePassword: '비밀번호',
changeConnHelper: ' 작업은 현재 데이터베이스 {0}() 수정합니다. 계속하시겠습니까?',
changePasswordHelper:
'데이터베이스가 애플리케이션과 연결되어 있습니다. 비밀번호를 변경하면 애플리케이션의 데이터베이스 비밀번호도 변경됩니다. 변경 사항은 애플리케이션이 재시작된 후에 적용됩니다.',
confChange: '설정',
@ -1384,6 +1388,7 @@ const message = {
existFileHelper: '업로드한 파일에 동일한 이름의 파일이 포함되어 있습니다. 덮어쓰시겠습니까?',
existFileSize: '파일 크기 (새로운 -> 오래된)',
existFileDirHelper: '선택한 파일/폴더에 동일한 이름이 이미 존재합니다. 신중하게 작업하세요!',
noSuchFile: '파일 또는 디렉터리를 찾을 없습니다. 확인 다시 시도하세요.',
},
ssh: {
setting: '설정',

View file

@ -199,6 +199,7 @@ const message = {
rePassword: 'Pengesahan kata laluan tidak sepadan dengan kata laluan.',
requiredInput: 'Ruangan ini wajib diisi.',
requiredSelect: 'Pilih satu item dalam senarai',
illegalChar: 'Suntikan aksara & ; $ \' ` ( ) " > < | tidak disokong buat masa ini',
illegalInput: 'Ruangan ini tidak boleh mengandungi aksara tidak sah.',
commonName:
'Ruangan ini mesti bermula dengan aksara bukan khas dan mesti terdiri daripada aksara rumi, Cina, nombor, ".", "-", dan "_" dengan panjang 1-128 aksara.',
@ -439,6 +440,8 @@ const message = {
permission: 'Kebenaran',
permissionForIP: 'IP',
permissionAll: 'Kesemuanya(%)',
localhostHelper:
'Mengkonfigurasi kebenaran pangkalan data sebagai "localhost" untuk penyebaran kontena akan menghalang akses luar ke kontena. Sila pilih dengan teliti!',
databaseConnInfo: 'Maklumat sambungan',
rootPassword: 'Kata laluan root',
serviceName: 'Nama Perkhidmatan',
@ -450,6 +453,7 @@ const message = {
remoteConnHelper:
'Sambungan jauh ke MySQL sebagai pengguna root mungkin mempunyai risiko keselamatan. Oleh itu, lakukan operasi ini dengan berhati-hati.',
changePassword: 'Kata laluan',
changeConnHelper: 'Operasi ini akan mengubah pangkalan data semasa {0}. Adakah anda ingin meneruskan?',
changePasswordHelper:
'Pangkalan data telah dikaitkan dengan aplikasi. Menukar kata laluan akan menukar kata laluan pangkalan data aplikasi pada masa yang sama. Perubahan ini akan berkuat kuasa selepas aplikasi dimulakan semula.',
@ -1442,6 +1446,7 @@ const message = {
existFileHelper: 'Fail yang dimuat naik mengandungi fail dengan nama yang sama. Adakah anda mahu menimpanya?',
existFileSize: 'Saiz fail (baru -> lama)',
existFileDirHelper: 'Fail/folder yang dipilih mempunyai nama yang sama. Sila berhati-hati!',
noSuchFile: 'Fail atau direktori tidak ditemui. Sila periksa dan cuba lagi.',
},
ssh: {
setting: 'tetapan',

View file

@ -198,6 +198,7 @@ const message = {
rePassword: 'A confirmação da senha não corresponde à senha.',
requiredInput: 'Este campo é obrigatório.',
requiredSelect: 'Selecione um item na lista',
illegalChar: 'Atualmente não suporte para injeção dos caracteres & ; $ \' ` ( ) " > < |',
illegalInput: 'Este campo não deve conter caracteres ilegais.',
commonName:
'Este campo deve começar com caracteres não especiais e consistir em letras, números, ".", "-", e "_" com comprimento de 1-128.',
@ -437,6 +438,8 @@ const message = {
permission: 'Permissões',
permissionForIP: 'IP',
permissionAll: 'Todos (% de)',
localhostHelper:
'Configurar permissões de banco de dados como "localhost" para implantação em contêiner impedirá o acesso externo ao contêiner. Por favor, escolha com cuidado!',
databaseConnInfo: 'Informações de conexão',
rootPassword: 'Senha root',
serviceName: 'Nome do serviço',
@ -448,6 +451,7 @@ const message = {
remoteConnHelper:
'Conectar-se ao MySQL como usuário root pode representar riscos de segurança. Realize esta operação com cautela.',
changePassword: 'Senha',
changeConnHelper: 'Esta operação modificará o banco de dados atual {0}. Deseja continuar?',
changePasswordHelper:
'O banco de dados está associado a um aplicativo. Alterar a senha alterará a senha do banco de dados do aplicativo ao mesmo tempo. A mudança surtirá efeito após a reinicialização do aplicativo.',
@ -1428,6 +1432,7 @@ const message = {
existFileHelper: 'O arquivo enviado contém um arquivo com o mesmo nome. Deseja substituí-lo?',
existFileSize: 'Tamanho do arquivo (novo -> antigo)',
existFileDirHelper: 'O arquivo/pasta selecionado tem um nome duplicado. Por favor, prossiga com cautela!',
noSuchFile: 'O arquivo ou diretório não foi encontrado. Por favor, verifique e tente novamente.',
},
ssh: {
setting: 'configuração',

View file

@ -197,6 +197,7 @@ const message = {
rePassword: 'Подтверждение пароля не совпадает с паролем.',
requiredInput: 'Это поле обязательно для заполнения.',
requiredSelect: 'Выберите элемент из списка',
illegalChar: 'В настоящее время не поддерживается вставка символов & ; $ \' ` ( ) " > < |',
illegalInput: 'Это поле не должно содержать недопустимых символов.',
commonName:
'Это поле должно начинаться с неспециальных символов и должно состоять из английских букв, китайских иероглифов, цифр, ".", "-" и "_" длиной 1-128.',
@ -1431,6 +1432,7 @@ const message = {
existFileHelper: 'Загруженный файл содержит файл с таким же именем. Заменить его?',
existFileSize: 'Размер файла (новый -> старый)',
existFileDirHelper: 'Выбранный файл/папка имеет дублирующееся имя. Пожалуйста, действуйте осторожно!',
noSuchFile: 'Файл или каталог не найдены. Пожалуйста, проверьте и повторите попытку.',
},
ssh: {
setting: 'настройка',

View file

@ -205,6 +205,7 @@ const message = {
rePassword: '密碼不一致請檢查後重新輸入',
requiredInput: '請填寫必填項',
requiredSelect: '請選擇必選項',
illegalChar: '暫不支援注入字元 & ; $ \' ` ( ) " > < |',
illegalInput: '輸入框中存在不合法字符',
commonName: '支持非特殊字元開頭,英文中文數字.-和_,長度1-128',
userName: '支持非特殊字符開頭英文中文數字和_,長度3-30',
@ -433,6 +434,7 @@ const message = {
permission: '權限',
permissionForIP: '指定 IP',
permissionAll: '所有人(%)',
localhostHelper: '將容器部署的資料庫權限配置為"localhost"會導致容器外部無法存取請謹慎選擇',
databaseConnInfo: '連接信息',
rootPassword: 'root 密碼',
serviceName: '服務名稱',
@ -443,6 +445,7 @@ const message = {
remoteHelper: '多個 ip 以逗號分隔172.16.10.111,172.16.10.112',
remoteConnHelper: 'root 帳號遠程連接 mysql 有安全風險開啟需謹慎',
changePassword: '改密',
changeConnHelper: '此操作將修改當前資料庫 {0}是否繼續',
changePasswordHelper: '當前數據庫已經關聯應用修改密碼將同步修改應用中數據庫密碼修改後重啟生效',
portHelper: '該端口為容器對外暴露端口修改需要單獨保存並且重啟容器',
@ -1384,6 +1387,7 @@ const message = {
existFileHelper: '上傳的檔案存在同名檔案是否覆蓋',
existFileSize: '文件大小->',
existFileDirHelper: '選擇的檔案/資料夾存在同名請謹慎操作',
noSuchFile: '找不到該檔案或目錄請檢查後重試',
},
ssh: {
autoStart: '開機自啟',

View file

@ -205,6 +205,7 @@ const message = {
rePassword: '密码不一致请检查后重新输入',
requiredInput: '请填写必填项',
requiredSelect: '请选择必选项',
illegalChar: '暂不支持注入字符 & ; $ \' ` ( ) " > < |',
illegalInput: '输入框中存在不合法字符',
commonName: '支持非特殊字符开头,英文中文数字.-和_,长度1-128',
userName: '支持非特殊字符开头英文中文数字和_,长度3-30',
@ -431,6 +432,7 @@ const message = {
permission: '权限',
permissionForIP: '指定 IP',
permissionAll: '所有人(%)',
localhostHelper: '将容器部署的数据库权限配置为 localhost 会导致容器外部无法访问请谨慎选择',
databaseConnInfo: '连接信息',
rootPassword: 'root 密码',
serviceName: '服务名称',
@ -441,6 +443,7 @@ const message = {
remoteHelper: '多个 ip 以逗号分隔172.16.10.111,172.16.10.112',
remoteConnHelper: 'root 帐号远程连接 mysql 有安全风险开启需谨慎',
changePassword: '改密',
changeConnHelper: '此操作将修改当前数据库 {0}是否继续',
changePasswordHelper: '当前数据库已经关联应用修改密码将同步修改应用中数据库密码修改后重启生效',
portHelper: '该端口为容器对外暴露端口修改需要单独保存并且重启容器',
@ -1380,6 +1383,7 @@ const message = {
existFileHelper: '上传的文件存在同名文件是否覆盖',
existFileSize: '文件大小 ( -> )',
existFileDirHelper: '选择的文件/文件夹存在同名请谨慎操作',
noSuchFile: '未能找到该文件或目录请检查后重试',
},
ssh: {
autoStart: '开机自启',

View file

@ -233,6 +233,7 @@ const onDelete = async (row: Cronjob.CronjobInfo | null) => {
let ids = [];
showClean.value = false;
cleanData.value = false;
cleanRemoteData.value = true;
if (row) {
ids = [row.id];
names = [row.name];

View file

@ -13,13 +13,21 @@
</el-form-item>
<el-form-item :label="$t('commons.login.password')" prop="password">
<el-input type="password" clearable show-password v-model="form.password"></el-input>
<span class="input-help">{{ $t('commons.rule.illegalChar') }}</span>
</el-form-item>
<el-form-item :label="$t('database.permission')" prop="permission">
<el-select v-model="form.permission">
<el-option value="%" :label="$t('database.permissionAll')" />
<el-option v-if="form.from !== 'local'" value="localhost" :label="$t('terminal.localhost')" />
<el-option
v-if="form.from !== 'local'"
value="localhost"
:label="$t('terminal.localhost') + '(localhost)'"
/>
<el-option value="ip" :label="$t('database.permissionForIP')" />
</el-select>
<span v-if="form.from !== 'local'" class="input-help">
{{ $t('database.localhostHelper') }}
</span>
</el-form-item>
<el-form-item v-if="form.permission === 'ip'" prop="permissionIPs">
<el-input clearable :rows="3" type="textarea" v-model="form.permissionIPs" />
@ -47,7 +55,6 @@ import { ElForm } from 'element-plus';
import { bindUser } from '@/api/modules/database';
import { Rules } from '@/global/form-rules';
import { MsgSuccess } from '@/utils/message';
import { checkIp } from '@/utils/util';
const loading = ref();
const bindVisible = ref(false);
@ -66,21 +73,11 @@ const confirmDialogRef = ref();
const rules = reactive({
username: [Rules.requiredInput, Rules.name],
password: [Rules.paramComplexity],
password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
permission: [Rules.requiredSelect],
permissionIPs: [{ validator: checkIPs, trigger: 'blur', required: true }],
permissionIPs: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
});
function checkIPs(rule: any, value: any, callback: any) {
let ips = form.permissionIPs.split(',');
for (const item of ips) {
if (checkIp(item)) {
return callback(new Error(i18n.global.t('commons.rule.ip')));
}
}
callback();
}
interface DialogProps {
from: string;
database: string;

View file

@ -1,6 +1,6 @@
<template>
<DrawerPro v-model="dialogVisible" :header="$t('database.databaseConnInfo')" @close="handleClose" size="small">
<el-form @submit.prevent v-loading="loading" ref="formRef" :model="form" label-position="top">
<el-form @submit.prevent v-loading="loading" ref="formRef" :rules="rules" :model="form" label-position="top">
<el-form-item :label="$t('database.containerConn')" v-if="form.from === 'local'">
<el-card class="mini-border-card">
<el-descriptions :column="1">
@ -60,7 +60,7 @@
<el-switch v-model="form.privilege" :disabled="form.status !== 'Running'" @change="onSaveAccess" />
<span class="input-help">{{ $t('database.remoteConnHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('database.rootPassword')" :rules="Rules.paramComplexity" prop="password">
<el-form-item :label="$t('database.rootPassword')" prop="password">
<el-input
style="width: calc(100% - 205px)"
type="password"
@ -74,6 +74,7 @@
{{ $t('commons.button.random') }}
</el-button>
</el-button-group>
<span class="input-help">{{ $t('commons.rule.illegalChar') }}</span>
</el-form-item>
</div>
<div v-if="form.from !== 'local'">
@ -87,8 +88,6 @@
</el-form-item>
</div>
</el-form>
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmit" @cancel="loadPassword"></ConfirmDialog>
<ConfirmDialog ref="confirmAccessDialogRef" @confirm="onSubmitAccess" @cancel="loadAccess"></ConfirmDialog>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="dialogVisible = false">
@ -108,7 +107,6 @@ import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { getDatabase, loadRemoteAccess, updateMysqlAccess, updateMysqlPassword } from '@/api/modules/database';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { getAppConnInfo } from '@/api/modules/app';
import { MsgSuccess } from '@/utils/message';
import { getRandomStr } from '@/utils/util';
@ -125,6 +123,7 @@ const form = reactive({
password: '',
serviceName: '',
containerName: '',
oldPrivilege: false,
privilege: false,
port: 0,
@ -134,9 +133,9 @@ const form = reactive({
username: '',
remoteIP: '',
});
const confirmDialogRef = ref();
const confirmAccessDialogRef = ref();
const rules = reactive({
password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
});
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
@ -177,6 +176,7 @@ const loadAccess = async () => {
if (form.from === 'local') {
const res = await loadRemoteAccess(form.type, form.database);
form.privilege = res.data;
form.oldPrivilege = res.data;
}
};
@ -204,68 +204,72 @@ const loadPassword = async () => {
form.remoteIP = res.data.address;
};
const onSubmit = async () => {
let param = {
id: 0,
from: form.from,
type: form.type,
database: form.database,
value: form.password,
};
loading.value = true;
await updateMysqlPassword(param)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
dialogVisible.value = false;
})
.catch(() => {
loading.value = false;
});
};
const onSave = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
let params = {
header: i18n.global.t('database.confChange'),
operationInfo: i18n.global.t('database.restartNowHelper'),
submitInputInfo: i18n.global.t('database.restartNow'),
};
confirmDialogRef.value!.acceptParams(params);
ElMessageBox.confirm(
i18n.global.t('database.changeConnHelper', [i18n.global.t('commons.login.password')]),
i18n.global.t('commons.msg.infoTitle'),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
},
).then(async () => {
let param = {
id: 0,
from: form.from,
type: form.type,
database: form.database,
value: form.password,
};
loading.value = true;
await updateMysqlPassword(param)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
dialogVisible.value = false;
})
.catch(() => {
loading.value = false;
});
});
});
};
const onSubmitAccess = async () => {
let param = {
id: 0,
from: form.from,
type: form.type,
database: form.database,
value: form.privilege ? '%' : 'localhost',
};
loading.value = true;
await updateMysqlAccess(param)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
dialogVisible.value = false;
const onSaveAccess = async () => {
ElMessageBox.confirm(
i18n.global.t('database.changeConnHelper', [i18n.global.t('database.remoteAccess')]),
i18n.global.t('commons.msg.infoTitle'),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
},
)
.then(async () => {
let param = {
id: 0,
from: form.from,
type: form.type,
database: form.database,
value: form.privilege ? '%' : 'localhost',
};
loading.value = true;
await updateMysqlAccess(param)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
dialogVisible.value = false;
})
.catch(() => {
loading.value = false;
});
})
.catch(() => {
loading.value = false;
form.privilege = form.oldPrivilege;
});
};
const onSaveAccess = () => {
let params = {
header: i18n.global.t('database.confChange'),
operationInfo: i18n.global.t('database.restartNowHelper'),
submitInputInfo: i18n.global.t('database.restartNow'),
};
confirmAccessDialogRef.value!.acceptParams(params);
};
defineExpose({
acceptParams,
});

View file

@ -22,13 +22,21 @@
<el-button @click="random">{{ $t('commons.button.random') }}</el-button>
</template>
</el-input>
<span class="input-help">{{ $t('commons.rule.illegalChar') }}</span>
</el-form-item>
<el-form-item :label="$t('database.permission')" prop="permission">
<el-select v-model="form.permission">
<el-option value="%" :label="$t('database.permissionAll')" />
<el-option v-if="form.from !== 'local'" value="localhost" :label="$t('terminal.localhost')" />
<el-option
v-if="form.from !== 'local'"
value="localhost"
:label="$t('terminal.localhost') + '(localhost)'"
/>
<el-option value="ip" :label="$t('database.permissionForIP')" />
</el-select>
<span v-if="form.from !== 'local'" class="input-help">
{{ $t('database.localhostHelper') }}
</span>
</el-form-item>
<el-form-item v-if="form.permission === 'ip'" prop="permissionIPs">
<el-input clearable :rows="3" type="textarea" v-model="form.permissionIPs" />
@ -61,7 +69,7 @@ import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { addMysqlDB } from '@/api/modules/database';
import { MsgSuccess } from '@/utils/message';
import { checkIp, getRandomStr } from '@/utils/util';
import { getRandomStr } from '@/utils/util';
const loading = ref();
const createVisible = ref(false);
@ -80,19 +88,10 @@ const form = reactive({
const rules = reactive({
name: [Rules.requiredInput, Rules.dbName],
username: [Rules.requiredInput, Rules.name],
password: [Rules.paramComplexity],
password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
permission: [Rules.requiredSelect],
permissionIPs: [{ validator: checkIPs, trigger: 'blur', required: true }],
permissionIPs: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
});
function checkIPs(rule: any, value: any, callback: any) {
let ips = form.permissionIPs.split(',');
for (const item of ips) {
if (checkIp(item)) {
return callback(new Error(i18n.global.t('commons.rule.ip')));
}
}
callback();
}
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();

View file

@ -13,8 +13,9 @@
<el-input disabled v-model="changeForm.userName"></el-input>
</el-form-item>
<el-form-item :label="$t('commons.login.password')" prop="password">
<el-input type="password" clearable show-password v-model="changeForm.password"></el-input>
<el-input type="password" clearable show-password v-model="changeForm.password" />
</el-form-item>
<span class="input-help">{{ $t('commons.rule.illegalChar') }}</span>
</div>
<div v-if="changeForm.operation === 'privilege'">
<el-form-item :label="$t('database.permission')" prop="privilege">
@ -23,10 +24,13 @@
<el-option
v-if="changeForm.from !== 'local'"
value="localhost"
:label="$t('terminal.localhost')"
:label="$t('terminal.localhost') + '(localhost)'"
/>
<el-option value="ip" :label="$t('database.permissionForIP')" />
</el-select>
<span v-if="changeForm.from !== 'local'" class="input-help">
{{ $t('database.localhostHelper') }}
</span>
</el-form-item>
<el-form-item v-if="changeForm.privilege === 'ip'" prop="privilegeIPs">
<el-input clearable :rows="3" type="textarea" v-model="changeForm.privilegeIPs" />
@ -56,13 +60,14 @@ import { ElForm } from 'element-plus';
import { deleteCheckMysqlDB, updateMysqlAccess, updateMysqlPassword } from '@/api/modules/database';
import { Rules } from '@/global/form-rules';
import { MsgSuccess } from '@/utils/message';
import { checkIp } from '@/utils/util';
const loading = ref();
const changeVisible = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const changeFormRef = ref<FormInstance>();
const title = ref();
const oldPrivilege = ref();
const oldPrivilegeIPs = ref();
const changeForm = reactive({
id: 0,
from: '',
@ -79,20 +84,10 @@ const changeForm = reactive({
const confirmDialogRef = ref();
const rules = reactive({
password: [Rules.paramComplexity],
privilegeIPs: [{ validator: checkIPs, trigger: 'blur', required: true }],
password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
privilegeIPs: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
});
function checkIPs(rule: any, value: any, callback: any) {
let ips = changeForm.privilegeIPs.split(',');
for (const item of ips) {
if (checkIp(item)) {
return callback(new Error(i18n.global.t('commons.rule.ip')));
}
}
callback();
}
interface DialogProps {
id: number;
from: string;
@ -123,6 +118,8 @@ const acceptParams = (params: DialogProps): void => {
changeForm.privilegeIPs = params.privilegeIPs;
changeForm.value = params.value;
changeVisible.value = true;
oldPrivilege.value = params.privilege;
oldPrivilegeIPs.value = params.privilegeIPs;
};
const emit = defineEmits<{ (e: 'search'): void }>();
@ -166,6 +163,10 @@ const submitChangeInfo = async (formEl: FormInstance | undefined) => {
}
return;
}
if (changeForm.privilege === oldPrivilege.value && changeForm.privilegeIPs === oldPrivilegeIPs.value) {
changeVisible.value = false;
return;
}
if (changeForm.privilege !== 'ip') {
param.value = changeForm.privilege;
} else {

View file

@ -14,6 +14,7 @@
</el-form-item>
<el-form-item :label="$t('commons.login.password')" prop="password">
<el-input type="password" clearable show-password v-model="form.password" />
<span class="input-help">{{ $t('commons.rule.illegalChar') }}</span>
</el-form-item>
<el-form-item :label="$t('database.permission')" prop="superUser">
<el-checkbox v-model="form.superUser">{{ $t('database.pgSuperUser') }}</el-checkbox>
@ -57,7 +58,7 @@ const confirmDialogRef = ref();
const rules = reactive({
username: [Rules.requiredInput, Rules.name],
password: [Rules.paramComplexity],
password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
});
interface DialogProps {

View file

@ -1,6 +1,6 @@
<template>
<DrawerPro v-model="dialogVisible" :header="$t('database.databaseConnInfo')" @close="handleClose" size="small">
<el-form @submit.prevent v-loading="loading" ref="formRef" :model="form" label-position="top">
<el-form @submit.prevent v-loading="loading" ref="formRef" :rules="rules" :model="form" label-position="top">
<el-form-item :label="$t('database.containerConn')" v-if="form.from === 'local'">
<el-card class="mini-border-card">
<el-descriptions :column="1">
@ -53,7 +53,7 @@
<el-divider border-style="dashed" />
<div v-if="form.from === 'local'">
<el-form-item :label="$t('commons.login.username')" prop="username">
<el-input readonly disabled v-model="form.username">
<el-input type="text" style="width: calc(100% - 60px)" readonly disabled v-model="form.username">
<template #append>
<el-button-group>
<CopyButton :content="form.username" />
@ -61,7 +61,7 @@
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('commons.login.password')" :rules="Rules.paramComplexity" prop="password">
<el-form-item :label="$t('commons.login.password')" prop="password">
<el-input
style="width: calc(100% - 205px)"
type="password"
@ -75,6 +75,7 @@
{{ $t('commons.button.random') }}
</el-button>
</el-button-group>
<span class="input-help">{{ $t('commons.rule.illegalChar') }}</span>
</el-form-item>
</div>
<div v-if="form.from !== 'local'">
@ -89,8 +90,6 @@
</div>
</el-form>
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmit" @cancel="loadPassword"></ConfirmDialog>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="dialogVisible = false">
@ -110,7 +109,6 @@ import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { ElForm } from 'element-plus';
import { getDatabase, updatePostgresqlPassword } from '@/api/modules/database';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { getAppConnInfo } from '@/api/modules/app';
import { MsgSuccess } from '@/utils/message';
import { getRandomStr } from '@/utils/util';
@ -136,8 +134,9 @@ const form = reactive({
username: '',
remoteIP: '',
});
const confirmDialogRef = ref();
const rules = reactive({
password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
});
type FormInstance = InstanceType<typeof ElForm>;
const formRef = ref<FormInstance>();
@ -205,36 +204,36 @@ const loadPassword = async () => {
form.remoteIP = res.data.address;
};
const onSubmit = async () => {
let param = {
id: 0,
from: form.from,
type: form.type,
database: form.database,
value: form.password,
};
loading.value = true;
await updatePostgresqlPassword(param)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
dialogVisible.value = false;
})
.catch(() => {
loading.value = false;
});
};
const onSave = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
let params = {
header: i18n.global.t('database.confChange'),
operationInfo: i18n.global.t('database.restartNowHelper'),
submitInputInfo: i18n.global.t('database.restartNow'),
};
confirmDialogRef.value!.acceptParams(params);
ElMessageBox.confirm(
i18n.global.t('database.changeConnHelper', [i18n.global.t('commons.login.password')]),
i18n.global.t('commons.msg.infoTitle'),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
},
).then(async () => {
let param = {
id: 0,
from: form.from,
type: form.type,
database: form.database,
value: form.password,
};
loading.value = true;
await updatePostgresqlPassword(param)
.then(() => {
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
dialogVisible.value = false;
})
.catch(() => {
loading.value = false;
});
});
});
};

View file

@ -14,6 +14,7 @@
<el-button @click="random">{{ $t('commons.button.random') }}</el-button>
</template>
</el-input>
<span class="input-help">{{ $t('commons.rule.illegalChar') }}</span>
</el-form-item>
<el-form-item :label="$t('database.permission')" prop="superUser">
<el-checkbox v-model="form.superUser">{{ $t('database.pgSuperUser') }}</el-checkbox>
@ -66,7 +67,7 @@ const form = reactive({
const rules = reactive({
name: [Rules.requiredInput, Rules.dbName],
username: [Rules.requiredInput, Rules.name],
password: [Rules.paramComplexity],
password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
});
type FormInstance = InstanceType<typeof ElForm>;

View file

@ -14,6 +14,7 @@
</el-form-item>
<el-form-item :label="$t('commons.login.password')" prop="password">
<el-input type="password" clearable show-password v-model="changeForm.password"></el-input>
<span class="input-help">{{ $t('commons.rule.illegalChar') }}</span>
</el-form-item>
</div>
</el-form>
@ -58,7 +59,7 @@ const changeForm = reactive({
});
const confirmDialogRef = ref();
const rules = reactive({
password: [Rules.paramComplexity],
password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
});
interface DialogProps {

View file

@ -69,6 +69,7 @@
{{ $t('commons.button.random') }}
</el-button>
</el-button-group>
<span class="input-help">{{ $t('commons.rule.illegalChar') }}</span>
</el-form-item>
<div v-if="form.from !== 'local'">
@ -104,6 +105,7 @@ import { getAppConnInfo } from '@/api/modules/app';
import { MsgSuccess } from '@/utils/message';
import { getRandomStr } from '@/utils/util';
import { getAgentSettingInfo } from '@/api/modules/setting';
import { Rules } from '@/global/form-rules';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
@ -123,21 +125,9 @@ const form = reactive({
remoteIP: '',
});
const rules = reactive({
password: [{ validator: checkPassword, trigger: 'blur' }],
password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
});
function checkPassword(rule: any, value: any, callback: any) {
if (form.password !== '') {
const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9.%@!~_-]{4,126}[a-zA-Z0-9]{1}$/;
if (!reg.test(value) && value !== '') {
callback(new Error(i18n.global.t('commons.rule.paramComplexity', ['.%@!~_-'])));
} else {
callback();
}
}
callback();
}
const confirmDialogRef = ref();
const emit = defineEmits(['checkExist', 'closeTerminal']);

View file

@ -220,7 +220,7 @@ const acceptParams = async (props: MoveProps) => {
type.value = props.type;
if (props.name && props.name != '') {
oldName.value = props.name;
const res = await checkFile(props.path + '/' + props.name);
const res = await checkFile(props.path + '/' + props.name, false);
if (res.data) {
changeName.value = true;
addForm.cover = false;

View file

@ -59,12 +59,8 @@ let isComplexity = ref(false);
type FormInstance = InstanceType<typeof ElForm>;
const passFormRef = ref<FormInstance>();
const passRules = reactive({
oldPass: [Rules.noSpace, Rules.requiredInput],
newPass: [
Rules.requiredInput,
Rules.noSpace,
{ min: 6, message: i18n.global.t('commons.rule.commonPassword'), trigger: 'blur' },
],
oldPass: [Rules.requiredInput, Rules.noSpace],
newPass: [Rules.requiredInput, Rules.noSpace],
newPassComplexity: [Rules.requiredInput, Rules.noSpace, Rules.password],
rePass: [Rules.requiredInput, Rules.noSpace, { validator: checkPasswordSame, trigger: 'blur' }],
});

View file

@ -47,12 +47,8 @@ import { logOutApi } from '@/api/modules/auth';
const globalStore = GlobalStore();
const passFormRef = ref<FormInstance>();
const passRules = reactive({
oldPassword: [Rules.noSpace, Rules.requiredInput],
newPassword: [
Rules.requiredInput,
Rules.noSpace,
{ min: 6, message: i18n.global.t('commons.rule.commonPassword'), trigger: 'blur' },
],
oldPassword: [Rules.requiredInput, Rules.noSpace],
newPassword: [Rules.requiredInput, Rules.noSpace],
newPasswordComplexity: [Rules.requiredInput, Rules.noSpace, Rules.password],
retryPassword: [Rules.requiredInput, Rules.noSpace, { validator: checkPassword, trigger: 'blur' }],
});

View file

@ -43,8 +43,9 @@ import i18n from '@/lang';
import { snapshotImport } from '@/api/modules/setting';
import { listBackupOptions, getFilesFromBackup } from '@/api/modules/backup';
import { Rules } from '@/global/form-rules';
import { MsgSuccess } from '@/utils/message';
import { MsgError, MsgSuccess } from '@/utils/message';
import router from '@/routers';
import { checkFile } from '@/api/modules/files';
const drawerVisible = ref(false);
const loading = ref();
@ -92,7 +93,12 @@ const checkDisable = (val: string) => {
return false;
};
const toFolder = async () => {
router.push({ path: '/hosts/files', query: { path: backupPath.value } });
const res = await checkFile(backupPath.value, true);
if (res.data) {
router.push({ path: '/hosts/files', query: { path: backupPath.value } });
} else {
MsgError(i18n.global.t('file.noSuchFile'));
}
};
const submitImport = async (formEl: FormInstance | undefined) => {