mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 08:00:53 +00:00
fix(database): support multiple MySQL user hosts (#13342)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
@@ -16,6 +17,7 @@ type IDatabaseUserRepo interface {
|
||||
List(opts ...DBOption) ([]model.DatabaseUser, error)
|
||||
Save(user *model.DatabaseUser) error
|
||||
Delete(opts ...DBOption) error
|
||||
DeleteBy(ctx context.Context, opts ...DBOption) error
|
||||
Update(vars map[string]interface{}, opts ...DBOption) error
|
||||
WithByDatabase(database string) DBOption
|
||||
WithByUser(username, host string) DBOption
|
||||
@@ -81,6 +83,10 @@ func (u *DatabaseUserRepo) Delete(opts ...DBOption) error {
|
||||
return db.Delete(&model.DatabaseUser{}).Error
|
||||
}
|
||||
|
||||
func (u *DatabaseUserRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {
|
||||
return getTx(ctx, opts...).Delete(&model.DatabaseUser{}).Error
|
||||
}
|
||||
|
||||
func (u *DatabaseUserRepo) Update(vars map[string]interface{}, opts ...DBOption) error {
|
||||
db := global.DB.Model(&model.DatabaseUser{})
|
||||
for _, opt := range opts {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"gorm.io/gorm"
|
||||
@@ -14,6 +16,7 @@ type IDatabaseUserGrantRepo interface {
|
||||
Save(grant *model.DatabaseUserGrant) error
|
||||
Replace(dbType, database string, grants []model.DatabaseUserGrant) error
|
||||
Delete(opts ...DBOption) error
|
||||
DeleteBy(ctx context.Context, opts ...DBOption) error
|
||||
Update(vars map[string]interface{}, opts ...DBOption) error
|
||||
WithByDatabase(database string) DBOption
|
||||
WithByDBName(dbName string) DBOption
|
||||
@@ -69,6 +72,10 @@ func (u *DatabaseUserGrantRepo) Delete(opts ...DBOption) error {
|
||||
return db.Delete(&model.DatabaseUserGrant{}).Error
|
||||
}
|
||||
|
||||
func (u *DatabaseUserGrantRepo) DeleteBy(ctx context.Context, opts ...DBOption) error {
|
||||
return getTx(ctx, opts...).Delete(&model.DatabaseUserGrant{}).Error
|
||||
}
|
||||
|
||||
func (u *DatabaseUserGrantRepo) Update(vars map[string]interface{}, opts ...DBOption) error {
|
||||
db := global.DB.Model(&model.DatabaseUserGrant{})
|
||||
for _, opt := range opts {
|
||||
|
||||
@@ -412,6 +412,20 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
||||
|
||||
switch install.App.Key {
|
||||
case constant.AppMysql, constant.AppMariaDB, constant.AppMysqlCluster:
|
||||
if err = databaseUserGrantRepo.DeleteBy(
|
||||
ctx,
|
||||
repo.WithByType(install.App.Key),
|
||||
databaseUserGrantRepo.WithByDatabase(install.Name),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = databaseUserRepo.DeleteBy(
|
||||
ctx,
|
||||
repo.WithByType(install.App.Key),
|
||||
databaseUserRepo.WithByDatabase(install.Name),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = mysqlRepo.Delete(ctx, mysqlRepo.WithByMysqlName(install.Name))
|
||||
case constant.AppMongodb:
|
||||
_ = mongodbRepo.Delete(ctx, mongodbRepo.WithByMongodbName(install.Name))
|
||||
|
||||
@@ -89,19 +89,32 @@ func databaseUserKey(username, host string) string {
|
||||
func splitMysqlHosts(permission string) []string {
|
||||
hosts := strings.Split(permission, ",")
|
||||
res := make([]string, 0, len(hosts))
|
||||
seen := make(map[string]struct{}, len(hosts))
|
||||
for _, host := range hosts {
|
||||
host = strings.TrimSpace(host)
|
||||
if len(host) == 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[host]; ok {
|
||||
continue
|
||||
}
|
||||
seen[host] = struct{}{}
|
||||
res = append(res, host)
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return []string{"%"}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func parseMysqlHosts(permission string, allowMultiple bool) ([]string, error) {
|
||||
hosts := splitMysqlHosts(permission)
|
||||
if len(hosts) == 0 {
|
||||
return nil, errors.New("mysql user host is required")
|
||||
}
|
||||
if !allowMultiple && len(hosts) > 1 {
|
||||
return nil, errors.New("multiple mysql user hosts are not supported for this operation")
|
||||
}
|
||||
return hosts, nil
|
||||
}
|
||||
|
||||
func checkMysqlNormalUser(username string) error {
|
||||
if isMysqlSystemUser(username) {
|
||||
return errors.New("mysql system user does not support this operation")
|
||||
@@ -109,14 +122,16 @@ func checkMysqlNormalUser(username string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// isMysqlSystemUser identifies built-in accounts which must only be managed by
|
||||
// MySQL itself. The root account is managed through the dedicated root API.
|
||||
// isMysqlSystemUser identifies internal accounts which must only be managed by
|
||||
// MySQL or its container runtime. The root account is managed through the
|
||||
// dedicated root API.
|
||||
func isMysqlSystemUser(username string) bool {
|
||||
switch strings.ToLower(username) {
|
||||
case "root",
|
||||
"mysql.session", "mysql.sys", "mysql.infoschema", "mysqlxsys",
|
||||
"mariadb.sys", "mariadb-sys",
|
||||
"debian-sys-maint":
|
||||
"debian-sys-maint",
|
||||
"healthcheck":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@@ -424,6 +439,15 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
||||
if len(req.Username) != 0 && len(req.Password) == 0 {
|
||||
return nil, errors.New("password is required when creating mysql user")
|
||||
}
|
||||
permissionHosts := make([]string, 0)
|
||||
if len(req.Username) != 0 {
|
||||
var err error
|
||||
permissionHosts, err = parseMysqlHosts(req.Permission, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Permission = strings.Join(permissionHosts, ",")
|
||||
}
|
||||
dbType, err := resolveDatabaseUserType(req.Database)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -468,7 +492,7 @@ func (u *MysqlService) Create(ctx context.Context, req dto.MysqlDBCreate) (*mode
|
||||
if err := saveDatabaseUserCredentials(dbType, req.Database, req.Username, req.Permission, req.Password, req.Description); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, host := range splitMysqlHosts(req.Permission) {
|
||||
for _, host := range permissionHosts {
|
||||
if err := saveDatabaseUserGrant(dbType, req.Database, req.Name, req.Username, host); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -597,19 +621,53 @@ func (u *MysqlService) CreateUser(req dto.MysqlUserCreate) error {
|
||||
if err := checkMysqlNormalUser(req.Username); err != nil {
|
||||
return err
|
||||
}
|
||||
hosts, err := parseMysqlHosts(req.Host, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dbType, err := resolveDatabaseUserType(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli, _, err := LoadMysqlClientByFrom(req.Database)
|
||||
cli, version, err := LoadMysqlClientByFrom(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
if err := cli.CreateUserOnly(client.UserInfo{Username: req.Username, Host: req.Host}, req.Password, 300); err != nil {
|
||||
return err
|
||||
createdHosts := make([]string, 0, len(hosts))
|
||||
rollbackCreatedHosts := func() {
|
||||
for i := len(createdHosts) - 1; i >= 0; i-- {
|
||||
host := createdHosts[i]
|
||||
if rollbackErr := cli.DeleteUser(client.UserInfo{Username: req.Username, Host: host}, version, 300); rollbackErr != nil {
|
||||
global.LOG.Errorf("rollback mysql user %s@%s failed, err: %v", req.Username, host, rollbackErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
return saveDatabaseUserCredential(dbType, req.Database, req.Username, req.Host, req.Password, req.Description)
|
||||
for _, host := range hosts {
|
||||
if err := cli.CreateUserOnly(client.UserInfo{Username: req.Username, Host: host}, req.Password, 300); err != nil {
|
||||
rollbackCreatedHosts()
|
||||
return err
|
||||
}
|
||||
createdHosts = append(createdHosts, host)
|
||||
}
|
||||
savedHosts := make([]string, 0, len(hosts))
|
||||
for _, host := range hosts {
|
||||
if err := saveDatabaseUserCredential(dbType, req.Database, req.Username, host, req.Password, req.Description); err != nil {
|
||||
for _, savedHost := range savedHosts {
|
||||
if rollbackErr := databaseUserRepo.Delete(
|
||||
repo.WithByType(dbType),
|
||||
databaseUserRepo.WithByDatabase(req.Database),
|
||||
databaseUserRepo.WithByUser(req.Username, savedHost),
|
||||
); rollbackErr != nil {
|
||||
global.LOG.Errorf("rollback mysql user record %s@%s failed, err: %v", req.Username, savedHost, rollbackErr)
|
||||
}
|
||||
}
|
||||
rollbackCreatedHosts()
|
||||
return err
|
||||
}
|
||||
savedHosts = append(savedHosts, host)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) UpdateUser(req dto.MysqlUserUpdate) error {
|
||||
@@ -619,10 +677,20 @@ func (u *MysqlService) UpdateUser(req dto.MysqlUserUpdate) error {
|
||||
if err := checkMysqlNormalUser(req.Username); err != nil {
|
||||
return err
|
||||
}
|
||||
oldHosts, err := parseMysqlHosts(req.Host, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newHosts, err := parseMysqlHosts(req.NewHost, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dbType, err := resolveDatabaseUserType(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Host = oldHosts[0]
|
||||
req.NewHost = newHosts[0]
|
||||
if req.Host != req.NewHost {
|
||||
targetUser, _ := databaseUserRepo.Get(repo.WithByType(dbType), databaseUserRepo.WithByDatabase(req.Database), databaseUserRepo.WithByUser(req.Username, req.NewHost))
|
||||
if targetUser.ID != 0 {
|
||||
@@ -687,6 +755,11 @@ func (u *MysqlService) ChangeUserPassword(req dto.MysqlUserPassword) error {
|
||||
if err := checkMysqlNormalUser(req.Username); err != nil {
|
||||
return err
|
||||
}
|
||||
hosts, err := parseMysqlHosts(req.Host, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Host = hosts[0]
|
||||
dbType, err := resolveDatabaseUserType(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -722,18 +795,7 @@ func (u *MysqlService) ChangeUserPassword(req dto.MysqlUserPassword) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
user, err := databaseUserRepo.Get(repo.WithByType(dbType), databaseUserRepo.WithByDatabase(req.Database), databaseUserRepo.WithByUser(req.Username, req.Host))
|
||||
if err != nil {
|
||||
user = model.DatabaseUser{
|
||||
Type: dbType,
|
||||
Database: req.Database,
|
||||
Username: req.Username,
|
||||
Host: req.Host,
|
||||
}
|
||||
}
|
||||
user.Password = req.Password
|
||||
user.IsDelete = false
|
||||
if err := databaseUserRepo.Save(&user); err != nil {
|
||||
if err := saveDatabaseUserCredential(dbType, req.Database, req.Username, req.Host, req.Password, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
return updateMysqlPasswordAppTargets(appTargets, req.Password)
|
||||
@@ -746,6 +808,11 @@ func (u *MysqlService) SaveUserPassword(req dto.MysqlUserPassword) error {
|
||||
if err := checkMysqlNormalUser(req.Username); err != nil {
|
||||
return err
|
||||
}
|
||||
hosts, err := parseMysqlHosts(req.Host, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Host = hosts[0]
|
||||
dbType, err := resolveDatabaseUserType(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -768,6 +835,11 @@ func (u *MysqlService) DeleteUser(req dto.MysqlUserDelete) error {
|
||||
if err := checkMysqlNormalUser(req.Username); err != nil {
|
||||
return err
|
||||
}
|
||||
hosts, err := parseMysqlHosts(req.Host, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Host = hosts[0]
|
||||
dbType, err := resolveDatabaseUserType(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -805,6 +877,10 @@ func (u *MysqlService) GrantUser(req dto.MysqlGrantCreate) error {
|
||||
if err := checkMysqlNormalUser(req.Username); err != nil {
|
||||
return err
|
||||
}
|
||||
hosts, err := parseMysqlHosts(req.Host, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dbType, err := resolveDatabaseUserType(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -814,10 +890,15 @@ func (u *MysqlService) GrantUser(req dto.MysqlGrantCreate) error {
|
||||
return err
|
||||
}
|
||||
defer cli.Close()
|
||||
if err := cli.GrantUser(client.GrantInfo{Database: req.DB, Username: req.Username, Host: req.Host}, 300); err != nil {
|
||||
return err
|
||||
for _, host := range hosts {
|
||||
if err := cli.GrantUser(client.GrantInfo{Database: req.DB, Username: req.Username, Host: host}, 300); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := saveDatabaseUserGrant(dbType, req.Database, req.DB, req.Username, host); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return saveDatabaseUserGrant(dbType, req.Database, req.DB, req.Username, req.Host)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *MysqlService) RevokeGrant(req dto.MysqlGrantDelete) error {
|
||||
@@ -830,6 +911,11 @@ func (u *MysqlService) RevokeGrant(req dto.MysqlGrantDelete) error {
|
||||
if err := checkMysqlNormalUser(req.Username); err != nil {
|
||||
return err
|
||||
}
|
||||
hosts, err := parseMysqlHosts(req.Host, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Host = hosts[0]
|
||||
dbType, err := resolveDatabaseUserType(req.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -558,10 +558,8 @@ const message = {
|
||||
addUserAuthorization: 'Add user authorization',
|
||||
revokeAuthorization: 'Revoke authorization',
|
||||
revokeAuthorizationHelper: "Revoke user {0}'s access to database {1}?",
|
||||
passwordPendingSupplement: 'Pending',
|
||||
supplementPassword: 'Add password',
|
||||
supplementPasswordHelper: 'This only saves the existing password to 1Panel and does not change it in MySQL.',
|
||||
deleteUserRecordHelper: 'This user has been deleted from MySQL. This only removes the local 1Panel record.',
|
||||
noUserBind: 'No binding',
|
||||
pgBindHelper:
|
||||
'This operation is used to create a new user and bind it to the target database. Currently, selecting users already existing in the database is not supported.',
|
||||
|
||||
@@ -531,7 +531,7 @@ const message = {
|
||||
hostSelect: 'Server Selection',
|
||||
selectHelper: 'Estas seguro de importar el archivo de backup {0}?',
|
||||
remoteAccess: 'Acceso remoto',
|
||||
remoteHelper: 'Múltiples IP separadas por comas, ejemplo: 172.16.10.111, 172.16.10.112',
|
||||
remoteHelper: 'Múltiples IP separadas por comas, ejemplo: 172.16.10.111,172.16.10.112',
|
||||
remoteConnHelper:
|
||||
'Conectarse remotamente como usuario root en MySQL puede presentar riesgos de seguridad. Por lo tanto, realice esta operación con precaución.',
|
||||
changePassword: 'Cambiar contraseña',
|
||||
@@ -555,11 +555,8 @@ const message = {
|
||||
addUserAuthorization: 'Añadir autorización de usuario',
|
||||
revokeAuthorization: 'Revocar autorización',
|
||||
revokeAuthorizationHelper: '¿Revocar el acceso del usuario {0} a la base de datos {1}?',
|
||||
passwordPendingSupplement: 'Pendiente',
|
||||
supplementPassword: 'Añadir contraseña',
|
||||
supplementPasswordHelper: 'Solo guarda la contraseña existente en 1Panel; no cambia la contraseña en MySQL.',
|
||||
deleteUserRecordHelper:
|
||||
'El usuario ya se eliminó de MySQL. Esta acción solo elimina el registro local de 1Panel.',
|
||||
pgBindHelper:
|
||||
'Esta operación se utiliza para crear un nuevo usuario y vincularlo a la base de datos destino. Actualmente no se admite seleccionar usuarios ya existentes en la base de datos.',
|
||||
pgSuperUser: 'Superusuario',
|
||||
|
||||
@@ -546,10 +546,8 @@ const message = {
|
||||
addUserAuthorization: 'افزودن مجوز کاربر',
|
||||
revokeAuthorization: 'لغو مجوز',
|
||||
revokeAuthorizationHelper: 'دسترسی کاربر {0} به پایگاه داده {1} لغو شود؟',
|
||||
passwordPendingSupplement: 'در انتظار تکمیل',
|
||||
supplementPassword: 'افزودن گذرواژه',
|
||||
supplementPasswordHelper: 'فقط گذرواژه فعلی را در 1Panel ذخیره میکند و گذرواژه MySQL را تغییر نمیدهد.',
|
||||
deleteUserRecordHelper: 'این کاربر از MySQL حذف شده است. این عملیات فقط رکورد محلی 1Panel را حذف میکند.',
|
||||
pgBindHelper:
|
||||
'این عملیات برای ایجاد کاربر جدید و اتصال آن به پایگاه داده هدف استفاده میشود. در حال حاضر انتخاب کاربرانی که از قبل در پایگاه داده وجود دارند پشتیبانی نمیشود.',
|
||||
pgSuperUser: 'کاربر فوقالعاده',
|
||||
|
||||
@@ -529,7 +529,7 @@ const message = {
|
||||
hostSelect: 'サーバー選択',
|
||||
selectHelper: 'バックアップファイル {0} をインポートしてもよろしいですか?',
|
||||
remoteAccess: 'リモートアクセス',
|
||||
remoteHelper: '複数のIP Comma delimited、例:172.16.10.111、172.16.10.112',
|
||||
remoteHelper: '複数のIP Comma delimited、例:172.16.10.111,172.16.10.112',
|
||||
remoteConnHelper:
|
||||
'ユーザールートとしてのMySQLへのリモート接続には、セキュリティリスクがある場合があります。したがって、この操作を慎重に実行します。',
|
||||
changePassword: 'パスワード',
|
||||
@@ -553,10 +553,8 @@ const message = {
|
||||
addUserAuthorization: 'ユーザー権限を追加',
|
||||
revokeAuthorization: '権限を取り消す',
|
||||
revokeAuthorizationHelper: 'ユーザー {0} のデータベース {1} へのアクセス権を取り消しますか?',
|
||||
passwordPendingSupplement: '未登録',
|
||||
supplementPassword: 'パスワードを登録',
|
||||
supplementPasswordHelper: '既存のパスワードを 1Panel に保存するだけで、MySQL のパスワードは変更しません。',
|
||||
deleteUserRecordHelper: 'このユーザーは MySQL から削除済みです。1Panel のローカル記録のみ削除します。',
|
||||
pgBindHelper:
|
||||
'この操作は、新しいユーザーを作成し、ターゲットデータベースにバインドするために使用されます。現在、データベースに既存のユーザーを選択することはサポートされていません。',
|
||||
pgSuperUser: 'スーパーユーザー',
|
||||
|
||||
@@ -524,7 +524,7 @@ const message = {
|
||||
hostSelect: '서버 선택',
|
||||
selectHelper: '백업 파일 {0}을(를) 가져오시겠습니까?',
|
||||
remoteAccess: '원격 접근',
|
||||
remoteHelper: '여러 IP 를 쉼표로 구분하여 입력, 예: 172.16.10.111, 172.16.10.112',
|
||||
remoteHelper: '여러 IP 를 쉼표로 구분하여 입력, 예: 172.16.10.111,172.16.10.112',
|
||||
remoteConnHelper:
|
||||
'MySQL 의 root 사용자로 원격 접속은 보안 위험을 초래할 수 있습니다. 따라서 이 작업은 신중히 수행해야 합니다.',
|
||||
changePassword: '비밀번호',
|
||||
@@ -547,10 +547,8 @@ const message = {
|
||||
addUserAuthorization: '사용자 권한 추가',
|
||||
revokeAuthorization: '권한 취소',
|
||||
revokeAuthorizationHelper: '사용자 {0}의 데이터베이스 {1} 접근 권한을 취소하시겠습니까?',
|
||||
passwordPendingSupplement: '입력 필요',
|
||||
supplementPassword: '비밀번호 추가',
|
||||
supplementPasswordHelper: '기존 비밀번호를 1Panel에만 저장하며 MySQL 비밀번호는 변경하지 않습니다.',
|
||||
deleteUserRecordHelper: '이 사용자는 MySQL에서 이미 삭제되었습니다. 1Panel의 로컬 기록만 삭제합니다.',
|
||||
pgBindHelper:
|
||||
'이 작업은 새 사용자를 생성하여 대상 데이터베이스에 바인딩하는 데 사용됩니다. 현재 데이터베이스에 이미 존재하는 사용자 선택은 지원되지 않습니다.',
|
||||
pgSuperUser: '슈퍼 사용자',
|
||||
|
||||
@@ -554,10 +554,8 @@ const message = {
|
||||
addUserAuthorization: 'ເພີ່ມສິດຜູ້ໃຊ້',
|
||||
revokeAuthorization: 'ຖອນສິດ',
|
||||
revokeAuthorizationHelper: 'ຖອນສິດຜູ້ໃຊ້ {0} ໃນຖານຂໍ້ມູນ {1} ຫຼືບໍ່?',
|
||||
passwordPendingSupplement: 'ລໍຖ້າເພີ່ມ',
|
||||
supplementPassword: 'ເພີ່ມລະຫັດຜ່ານ',
|
||||
supplementPasswordHelper: 'ບັນທຶກລະຫັດຜ່ານປັດຈຸບັນໄວ້ໃນ 1Panel ເທົ່ານັ້ນ ແລະບໍ່ປ່ຽນລະຫັດ MySQL.',
|
||||
deleteUserRecordHelper: 'ຜູ້ໃຊ້ນີ້ຖືກລຶບຈາກ MySQL ແລ້ວ. ຈະລຶບສະເພາະຂໍ້ມູນທ້ອງຖິ່ນໃນ 1Panel.',
|
||||
noUserBind: 'ບໍ່ມີການຜູກມັດ',
|
||||
pgBindHelper:
|
||||
'ການດຳເນີນການນີ້ໃຊ້ເພື່ອສ້າງຜູ້ໃຊ້ໃໝ່ ແລະ ຜູກມັດກັບຖານຂໍ້ມູນເປົ້າໝາຍ. ໃນປັດຈຸບັນ, ຍັງບໍ່ຮອງຮັບການເລືອກຜູ້ໃຊ້ທີ່ມີຢູ່ແລ້ວໃນຖານຂໍ້ມູນ.',
|
||||
|
||||
@@ -534,7 +534,7 @@ const message = {
|
||||
hostSelect: 'Pemilihan Pelayan',
|
||||
selectHelper: 'Adakah anda pasti ingin mengimport fail sandaran {0}?',
|
||||
remoteAccess: 'Akses jauh',
|
||||
remoteHelper: 'Berbilang IP dipisahkan dengan koma, contoh: 172.16.10.111, 172.16.10.112',
|
||||
remoteHelper: 'Berbilang IP dipisahkan dengan koma, contoh: 172.16.10.111,172.16.10.112',
|
||||
remoteConnHelper:
|
||||
'Sambungan jauh ke MySQL sebagai pengguna root mungkin mempunyai risiko keselamatan. Oleh itu, lakukan operasi ini dengan berhati-hati.',
|
||||
changePassword: 'Kata laluan',
|
||||
@@ -558,11 +558,8 @@ const message = {
|
||||
addUserAuthorization: 'Tambah kebenaran pengguna',
|
||||
revokeAuthorization: 'Tarik balik kebenaran',
|
||||
revokeAuthorizationHelper: 'Tarik balik akses pengguna {0} kepada pangkalan data {1}?',
|
||||
passwordPendingSupplement: 'Belum dilengkapkan',
|
||||
supplementPassword: 'Tambah kata laluan',
|
||||
supplementPasswordHelper: 'Hanya menyimpan kata laluan sedia ada ke 1Panel dan tidak mengubahnya dalam MySQL.',
|
||||
deleteUserRecordHelper:
|
||||
'Pengguna ini telah dipadam daripada MySQL. Tindakan ini hanya memadam rekod setempat 1Panel.',
|
||||
pgBindHelper:
|
||||
'Operasi ini digunakan untuk mencipta pengguna baharu dan mengaitkannya dengan pangkalan data sasaran. Pada masa ini, memilih pengguna yang sudah wujud dalam pangkalan data tidak disokong.',
|
||||
pgSuperUser: 'Pengguna Super',
|
||||
|
||||
@@ -528,7 +528,7 @@ const message = {
|
||||
hostSelect: 'Seleção de Servidor',
|
||||
selectHelper: 'Tem certeza de que deseja importar o arquivo de backup {0}?',
|
||||
remoteAccess: 'Acesso remoto',
|
||||
remoteHelper: 'Vários IPs separados por vírgula, exemplo: 172.16.10.111, 172.16.10.112',
|
||||
remoteHelper: 'Vários IPs separados por vírgula, exemplo: 172.16.10.111,172.16.10.112',
|
||||
remoteConnHelper:
|
||||
'Conectar-se ao MySQL como usuário root pode representar riscos de segurança. Realize esta operação com cautela.',
|
||||
changePassword: 'Senha',
|
||||
@@ -552,11 +552,8 @@ const message = {
|
||||
addUserAuthorization: 'Adicionar autorização de usuário',
|
||||
revokeAuthorization: 'Revogar autorização',
|
||||
revokeAuthorizationHelper: 'Revogar o acesso do usuário {0} ao banco de dados {1}?',
|
||||
passwordPendingSupplement: 'Pendente',
|
||||
supplementPassword: 'Adicionar senha',
|
||||
supplementPasswordHelper: 'Apenas salva a senha existente no 1Panel; não altera a senha no MySQL.',
|
||||
deleteUserRecordHelper:
|
||||
'O usuário já foi excluído do MySQL. Esta ação remove apenas o registro local do 1Panel.',
|
||||
pgBindHelper:
|
||||
'Esta operação cria um novo usuário e o vincula ao banco de dados alvo. A seleção de usuários já existentes no banco de dados não é suportada.',
|
||||
pgSuperUser: 'Superusuário',
|
||||
|
||||
@@ -523,7 +523,7 @@ const message = {
|
||||
hostSelect: 'Выбор сервера',
|
||||
selectHelper: 'Вы уверены, что хотите импортировать файл резервной копии {0}?',
|
||||
remoteAccess: 'Удаленный доступ',
|
||||
remoteHelper: 'Несколько IP через запятую, например: 172.16.10.111, 172.16.10.112',
|
||||
remoteHelper: 'Несколько IP через запятую, например: 172.16.10.111,172.16.10.112',
|
||||
remoteConnHelper:
|
||||
'Удаленное подключение к MySQL как пользователь root может иметь риски безопасности. Поэтому выполняйте эту операцию с осторожностью.',
|
||||
changePassword: 'Пароль',
|
||||
@@ -546,10 +546,8 @@ const message = {
|
||||
addUserAuthorization: 'Добавить доступ пользователю',
|
||||
revokeAuthorization: 'Отозвать доступ',
|
||||
revokeAuthorizationHelper: 'Отозвать у пользователя {0} доступ к базе данных {1}?',
|
||||
passwordPendingSupplement: 'Требуется пароль',
|
||||
supplementPassword: 'Добавить пароль',
|
||||
supplementPasswordHelper: 'Сохраняет текущий пароль только в 1Panel и не изменяет пароль в MySQL.',
|
||||
deleteUserRecordHelper: 'Пользователь уже удалён из MySQL. Будет удалена только локальная запись 1Panel.',
|
||||
pgBindHelper:
|
||||
'Эта операция используется для создания нового пользователя и привязки его к целевой базе данных. В настоящее время выбор уже существующих пользователей в базе данных не поддерживается.',
|
||||
pgSuperUser: 'Суперпользователь',
|
||||
|
||||
@@ -529,7 +529,7 @@ const message = {
|
||||
hostSelect: 'Sunucu Seçimi',
|
||||
selectHelper: '{0} yedek dosyasını içe aktarmak istediğinizden emin misiniz?',
|
||||
remoteAccess: 'Uzaktan erişim',
|
||||
remoteHelper: 'Birden fazla IP virgülle ayrılır, örnek: 172.16.10.111, 172.16.10.112',
|
||||
remoteHelper: 'Birden fazla IP virgülle ayrılır, örnek: 172.16.10.111,172.16.10.112',
|
||||
remoteConnHelper:
|
||||
'Root kullanıcısı olarak MySQLe uzaktan bağlantı güvenlik riski oluşturabilir. Bu nedenle bu işlemi dikkatli yapın.',
|
||||
changePassword: 'Şifre değiştir',
|
||||
@@ -553,10 +553,8 @@ const message = {
|
||||
addUserAuthorization: 'Kullanıcı yetkisi ekle',
|
||||
revokeAuthorization: 'Yetkiyi kaldır',
|
||||
revokeAuthorizationHelper: '{0} kullanıcısının {1} veritabanına erişimi kaldırılsın mı?',
|
||||
passwordPendingSupplement: 'Bekliyor',
|
||||
supplementPassword: 'Parola ekle',
|
||||
supplementPasswordHelper: 'Yalnızca mevcut parolayı 1Panel içine kaydeder; MySQL parolasını değiştirmez.',
|
||||
deleteUserRecordHelper: 'Bu kullanıcı MySQL üzerinden silinmiş. Bu işlem yalnızca yerel 1Panel kaydını siler.',
|
||||
pgBindHelper:
|
||||
'Bu işlem yeni bir kullanıcı oluşturmak ve hedef veritabanına bağlamak için kullanılır. Şu anda veritabanında mevcut olan kullanıcıları seçmek desteklenmiyor.',
|
||||
pgSuperUser: 'Süper Kullanıcı',
|
||||
|
||||
@@ -530,10 +530,8 @@ const message = {
|
||||
addUserAuthorization: '新增使用者授權',
|
||||
revokeAuthorization: '撤銷授權',
|
||||
revokeAuthorizationHelper: '確定撤銷使用者 {0} 對資料庫 {1} 的存取授權嗎?',
|
||||
passwordPendingSupplement: '待補充',
|
||||
supplementPassword: '補充密碼',
|
||||
supplementPasswordHelper: '僅將現有密碼儲存到 1Panel,不會修改 MySQL 使用者密碼。',
|
||||
deleteUserRecordHelper: '該使用者已從 MySQL 中刪除,此操作僅刪除 1Panel 中的本機記錄。',
|
||||
pgBindHelper: '此操作用於建立新使用者並將其綁定到目標資料庫,暫不支援選擇已存在於資料庫中的使用者。',
|
||||
pgSuperUser: '超級使用者',
|
||||
loadFromRemoteHelper: '此操作將同步伺服器上的資料庫、使用者及授權資訊到 1Panel,是否繼續?',
|
||||
|
||||
@@ -522,10 +522,8 @@ const message = {
|
||||
addUserAuthorization: '添加用户授权',
|
||||
revokeAuthorization: '撤销授权',
|
||||
revokeAuthorizationHelper: '确定撤销用户 {0} 对数据库 {1} 的访问授权吗?',
|
||||
passwordPendingSupplement: '待补充',
|
||||
supplementPassword: '补充密码',
|
||||
supplementPasswordHelper: '仅将现有密码保存到 1Panel,不会修改 MySQL 用户密码。',
|
||||
deleteUserRecordHelper: '该用户已从 MySQL 中删除,此操作仅删除 1Panel 中的本地记录。',
|
||||
noUserBind: '不绑定',
|
||||
pgBindHelper: '该操作用于创建新用户并将其绑定到目标数据库,暂不支持选择已存在于数据库中的用户。',
|
||||
pgSuperUser: '超级用户',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<DialogPro v-model="dialogVisible" :title="$t('database.authorizationManagement')" size="small">
|
||||
<DialogPro v-model="dialogVisible" :title="$t('database.authorizationManagement')" size="large">
|
||||
<div v-loading="loading">
|
||||
<div class="authorization-toolbar">
|
||||
<el-button type="primary" @click="openAddDialog">
|
||||
@@ -8,7 +8,31 @@
|
||||
</div>
|
||||
<el-table :data="authorizedUsers" :empty-text="$t('commons.msg.noneData')">
|
||||
<el-table-column :label="$t('commons.login.username')" min-width="140">
|
||||
<template #default="{ row }">{{ row.username }}@{{ row.host }}</template>
|
||||
<template #default="{ row }">{{ row.username }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.login.password')" prop="password" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<span v-if="!row.password">-</span>
|
||||
<div v-else class="password-cell">
|
||||
<span v-if="!row.showPassword" class="password-text">**********</span>
|
||||
<span v-else class="password-text">{{ row.password }}</span>
|
||||
<el-button
|
||||
v-if="!row.showPassword"
|
||||
link
|
||||
@click="row.showPassword = true"
|
||||
icon="View"
|
||||
class="password-action"
|
||||
/>
|
||||
<el-button
|
||||
v-else
|
||||
link
|
||||
@click="row.showPassword = false"
|
||||
icon="Hide"
|
||||
class="password-action"
|
||||
/>
|
||||
<CopyButton class="password-copy" :content="row.password" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('database.permission')" min-width="110">
|
||||
<template #default="{ row }">{{ permissionLabel(row.host) }}</template>
|
||||
@@ -162,11 +186,19 @@ const form = reactive({
|
||||
password: '',
|
||||
description: '',
|
||||
});
|
||||
const checkMysqlHosts = (_rule: unknown, value: string, callback: (error?: Error) => void) => {
|
||||
const hosts = value.split(',').map((item) => item.trim());
|
||||
if (hosts.length === 0 || hosts.some((item) => !item)) {
|
||||
callback(new Error(i18n.global.t('commons.rule.requiredInput')));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
const rules = reactive({
|
||||
userKey: [Rules.requiredSelect],
|
||||
username: [Rules.requiredInput, Rules.name],
|
||||
permission: [Rules.requiredSelect],
|
||||
host: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
|
||||
host: [Rules.requiredInput, Rules.noSpace, Rules.illegal, { validator: checkMysqlHosts, trigger: 'blur' }],
|
||||
password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
|
||||
});
|
||||
|
||||
@@ -189,13 +221,13 @@ const availableUsers = computed(() =>
|
||||
);
|
||||
|
||||
const loadContext = async () => {
|
||||
if (!form.database) {
|
||||
const database = form.database;
|
||||
if (!database) {
|
||||
users.value = [];
|
||||
grants.value = [];
|
||||
return;
|
||||
}
|
||||
const [userRes, grantRes] = await Promise.all([
|
||||
searchMysqlUsers({ database: form.database }),
|
||||
searchMysqlGrants({ database: form.database }),
|
||||
]);
|
||||
const [userRes, grantRes] = await Promise.all([searchMysqlUsers({ database }), searchMysqlGrants({ database })]);
|
||||
users.value = userRes.data || [];
|
||||
grants.value = grantRes.data || [];
|
||||
};
|
||||
@@ -333,6 +365,25 @@ defineExpose({
|
||||
display: flex;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.password-cell {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
max-width: 100%;
|
||||
}
|
||||
.password-text {
|
||||
overflow: hidden;
|
||||
max-width: 96px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.password-action {
|
||||
min-height: 20px;
|
||||
padding: 0;
|
||||
}
|
||||
.password-copy {
|
||||
margin-left: -2px;
|
||||
}
|
||||
:deep(.user-select-option) {
|
||||
height: auto;
|
||||
min-height: 48px;
|
||||
|
||||
@@ -169,6 +169,14 @@ const form = reactive({
|
||||
permissionIPs: '',
|
||||
description: '',
|
||||
});
|
||||
const checkMysqlHosts = (_rule: unknown, value: string, callback: (error?: Error) => void) => {
|
||||
const hosts = value.split(',').map((item) => item.trim());
|
||||
if (hosts.length === 0 || hosts.some((item) => !item)) {
|
||||
callback(new Error(i18n.global.t('commons.rule.requiredInput')));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
const rules = reactive({
|
||||
name: [Rules.requiredInput, Rules.dbName],
|
||||
format: [Rules.requiredSelect],
|
||||
@@ -177,7 +185,7 @@ const rules = reactive({
|
||||
username: [Rules.requiredInput, Rules.name],
|
||||
password: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
|
||||
permission: [Rules.requiredSelect],
|
||||
permissionIPs: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
|
||||
permissionIPs: [Rules.requiredInput, Rules.noSpace, Rules.illegal, { validator: checkMysqlHosts, trigger: 'blur' }],
|
||||
});
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
@@ -234,12 +242,22 @@ const loadCollations = async () => {
|
||||
};
|
||||
|
||||
const loadUsers = async () => {
|
||||
const res = await searchMysqlUsers({ database: form.database });
|
||||
const database = form.database;
|
||||
if (!database) {
|
||||
users.value = [];
|
||||
return;
|
||||
}
|
||||
const res = await searchMysqlUsers({ database });
|
||||
users.value = res.data || [];
|
||||
};
|
||||
|
||||
const loadGrants = async () => {
|
||||
const res = await searchMysqlGrants({ database: form.database });
|
||||
const database = form.database;
|
||||
if (!database) {
|
||||
grants.value = [];
|
||||
return;
|
||||
}
|
||||
const res = await searchMysqlGrants({ database });
|
||||
grants.value = res.data || [];
|
||||
};
|
||||
|
||||
|
||||
@@ -442,7 +442,7 @@ const openUserDrawer = async () => {
|
||||
};
|
||||
|
||||
const openAuthorizationManagement = (row: Database.MysqlDBInfo) => {
|
||||
if (!currentDB.value) {
|
||||
if (!currentDB.value?.database) {
|
||||
return;
|
||||
}
|
||||
bindRef.value!.acceptParams({
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<ComplexTable :data="users" :heightDiff="260">
|
||||
<el-table-column :label="$t('commons.login.username')" prop="username" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.username }}@{{ row.host }}</span>
|
||||
<span>{{ row.username }}</span>
|
||||
<el-tag v-if="row.isDelete" round type="info" class="ml-1" size="small">
|
||||
{{ $t('database.isDelete') }}
|
||||
</el-tag>
|
||||
@@ -18,7 +18,6 @@
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.isDelete">-</span>
|
||||
<div v-else-if="!row.password" class="password-cell">
|
||||
<el-tag type="warning" size="small">{{ $t('database.passwordPendingSupplement') }}</el-tag>
|
||||
<el-button link type="primary" @click="openSupplementPasswordDialog(row)">
|
||||
{{ $t('database.supplementPassword') }}
|
||||
</el-button>
|
||||
@@ -44,7 +43,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('database.permission')" prop="host" min-width="120">
|
||||
<el-table-column :label="$t('database.permission')" show-overflow-tooltip prop="host" min-width="120">
|
||||
<template #default="{ row }">
|
||||
{{ permissionLabel(row.host) }}
|
||||
</template>
|
||||
@@ -88,8 +87,11 @@
|
||||
</el-form-item>
|
||||
<el-form-item v-if="userForm.permission === 'ip'" prop="host">
|
||||
<el-input v-model.trim="userForm.host" />
|
||||
<span v-if="userDialogMode === 'create'" class="input-help">
|
||||
{{ $t('database.remoteHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.login.password')" prop="password">
|
||||
<el-form-item :label="$t('commons.login.password')" prop="password" :required="userDialogMode === 'create'">
|
||||
<el-input type="password" clearable show-password v-model.trim="userForm.password" />
|
||||
<span v-if="userDialogMode === 'edit'" class="input-help">
|
||||
{{ $t('setting.passwordEmptyTip') }}
|
||||
@@ -140,14 +142,13 @@
|
||||
</template>
|
||||
</DialogPro>
|
||||
|
||||
<DialogPro v-model="deleteDialogVisible" :title="$t('commons.button.delete')" size="small">
|
||||
<DialogPro
|
||||
v-model="deleteDialogVisible"
|
||||
:title="$t('commons.button.delete') + ' - ' + deleteUserIdentity"
|
||||
size="small"
|
||||
>
|
||||
<div v-loading="loading">
|
||||
<div class="delete-user-title">
|
||||
<template v-if="deleteUserDbs.length > 0">
|
||||
{{ deleteUser.username }} {{ $t('database.userBoundDatabases') }}
|
||||
</template>
|
||||
<template v-else>{{ deleteUser.username }}</template>
|
||||
</div>
|
||||
<div class="delete-user-title">{{ deleteUserIdentity }} {{ $t('database.userBoundDatabases') }}</div>
|
||||
<div class="delete-user-section">
|
||||
<div v-if="deleteUserDbs.length === 0" class="delete-user-empty">-</div>
|
||||
<div v-else class="bind-db-list delete-user-dbs">
|
||||
@@ -158,18 +159,13 @@
|
||||
</div>
|
||||
<div class="delete-user-section">
|
||||
<div>
|
||||
<template v-if="deleteUser.isDelete">
|
||||
<span style="font-size: 12px">{{ $t('database.deleteUserRecordHelper') }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span style="font-size: 12px">{{ $t('database.delete') }}</span>
|
||||
<span style="font-size: 12px; color: red; font-weight: 500">
|
||||
{{ deleteUser.username }}
|
||||
</span>
|
||||
<span style="font-size: 12px">{{ $t('database.deleteUserHelper') }}</span>
|
||||
</template>
|
||||
<span style="font-size: 12px">{{ $t('database.delete') }}</span>
|
||||
<span style="font-size: 12px; color: red; font-weight: 500">
|
||||
{{ deleteUserIdentity }}
|
||||
</span>
|
||||
<span style="font-size: 12px">{{ $t('database.deleteUserHelper') }}</span>
|
||||
</div>
|
||||
<el-input v-model="deleteConfirmInput" :placeholder="deleteUser.username" />
|
||||
<el-input v-model="deleteConfirmInput" :placeholder="deleteUserIdentity" />
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
@@ -179,7 +175,7 @@
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="submitDeleteUser()"
|
||||
:disabled="deleteConfirmInput !== deleteUser.username || loading"
|
||||
:disabled="deleteConfirmInput !== deleteUserIdentity || loading"
|
||||
>
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
@@ -239,6 +235,7 @@ const deleteUser = reactive({
|
||||
host: '',
|
||||
isDelete: false,
|
||||
});
|
||||
const deleteUserIdentity = computed(() => `${deleteUser.username}@${deleteUser.host}`);
|
||||
const deleteUserDbs = ref<string[]>([]);
|
||||
const supplementDialogVisible = ref(false);
|
||||
const supplementFormRef = ref();
|
||||
@@ -269,10 +266,22 @@ const checkPassword = (_rule: unknown, value: string, callback: (error?: Error)
|
||||
}
|
||||
callback();
|
||||
};
|
||||
const checkUserHosts = (_rule: unknown, value: string, callback: (error?: Error) => void) => {
|
||||
const hosts = value.split(',').map((item) => item.trim());
|
||||
if (hosts.length === 0 || hosts.some((item) => !item)) {
|
||||
callback(new Error(i18n.global.t('commons.rule.requiredInput')));
|
||||
return;
|
||||
}
|
||||
if (userDialogMode.value === 'edit' && hosts.length > 1) {
|
||||
callback(new Error(i18n.global.t('commons.rule.illegalInput')));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
const userRules = reactive({
|
||||
username: [Rules.requiredInput, Rules.name],
|
||||
permission: [Rules.requiredSelect],
|
||||
host: [Rules.requiredInput, Rules.noSpace, Rules.illegal],
|
||||
host: [Rules.requiredInput, Rules.noSpace, Rules.illegal, { validator: checkUserHosts, trigger: 'blur' }],
|
||||
password: [{ validator: checkPassword, trigger: 'blur' }],
|
||||
});
|
||||
|
||||
@@ -290,12 +299,22 @@ const acceptParams = async (params: DialogProps) => {
|
||||
};
|
||||
|
||||
const loadUsers = async () => {
|
||||
const res = await searchMysqlUsers({ database: database.value });
|
||||
const databaseName = database.value;
|
||||
if (!databaseName) {
|
||||
users.value = [];
|
||||
return;
|
||||
}
|
||||
const res = await searchMysqlUsers({ database: databaseName });
|
||||
users.value = res.data || [];
|
||||
};
|
||||
|
||||
const loadGrants = async () => {
|
||||
const res = await searchMysqlGrants({ database: database.value });
|
||||
const databaseName = database.value;
|
||||
if (!databaseName) {
|
||||
grants.value = [];
|
||||
return;
|
||||
}
|
||||
const res = await searchMysqlGrants({ database: databaseName });
|
||||
grants.value = res.data || [];
|
||||
};
|
||||
|
||||
@@ -416,15 +435,7 @@ const submitUser = async () => {
|
||||
const selectedDbs = userForm.dbs || [];
|
||||
const addDbs = selectedDbs.filter((item) => !originalDbs.value.includes(item));
|
||||
const removeDbs = originalDbs.value.filter((item) => !selectedDbs.includes(item));
|
||||
if (userForm.host !== originalHost.value) {
|
||||
await updateMysqlUser({
|
||||
database: database.value,
|
||||
username: userForm.username,
|
||||
host: originalHost.value,
|
||||
newHost: userForm.host,
|
||||
description: userForm.description,
|
||||
});
|
||||
} else if (userForm.description !== originalDescription.value) {
|
||||
if (userForm.host !== originalHost.value || userForm.description !== originalDescription.value) {
|
||||
await updateMysqlUser({
|
||||
database: database.value,
|
||||
username: userForm.username,
|
||||
|
||||
Reference in New Issue
Block a user