mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 06:47:20 +08:00
refactor: MySQL改密逻辑
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
@@ -12,6 +11,7 @@ import (
|
||||
"github.com/TheTNB/panel/app/models"
|
||||
"github.com/TheTNB/panel/internal"
|
||||
"github.com/TheTNB/panel/internal/services"
|
||||
"github.com/TheTNB/panel/pkg/db"
|
||||
"github.com/TheTNB/panel/pkg/io"
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/str"
|
||||
@@ -71,7 +71,7 @@ func (r *MySQLController) Load(ctx http.Context) http.Response {
|
||||
return controllers.Success(ctx, []types.NV{})
|
||||
}
|
||||
|
||||
raw, err := shell.Execf("/www/server/mysql/bin/mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1")
|
||||
raw, err := shell.Execf("mysqladmin -uroot -p" + rootPassword + " extended-status 2>&1")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL负载失败")
|
||||
}
|
||||
@@ -175,80 +175,45 @@ func (r *MySQLController) GetRootPassword(ctx http.Context) http.Response {
|
||||
|
||||
// SetRootPassword 设置root密码
|
||||
func (r *MySQLController) SetRootPassword(ctx http.Context) http.Response {
|
||||
status, err := systemctl.Status("mysqld")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
}
|
||||
if !status {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "MySQL 未运行")
|
||||
}
|
||||
|
||||
rootPassword := ctx.Request().Input("password")
|
||||
if len(rootPassword) == 0 {
|
||||
return controllers.Error(ctx, http.StatusUnprocessableEntity, "MySQL root密码不能为空")
|
||||
}
|
||||
|
||||
oldRootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
if oldRootPassword != rootPassword {
|
||||
if _, err = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';"`, oldRootPassword, rootPassword)); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("设置root密码失败: %v", err))
|
||||
mysql, err := db.NewMySQL("root", oldRootPassword, r.getSock(), "unix")
|
||||
if err != nil {
|
||||
// 尝试安全模式直接改密
|
||||
if err = db.MySQLResetRootPassword(rootPassword); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if _, err = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "FLUSH PRIVILEGES;"`, rootPassword)); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "设置root密码失败")
|
||||
}
|
||||
if err = r.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword); err != nil {
|
||||
_, _ = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';"`, rootPassword, oldRootPassword))
|
||||
_, _ = shell.Execf(fmt.Sprintf(`/www/server/mysql/bin/mysql -uroot -p%s -e "FLUSH PRIVILEGES;"`, oldRootPassword))
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("设置保存失败: %v", err))
|
||||
} else {
|
||||
if err = mysql.UserPassword("root", rootPassword); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
}
|
||||
if err = r.setting.Set(models.SettingKeyMysqlRootPassword, rootPassword); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, fmt.Sprintf("设置保存失败: %v", err))
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// DatabaseList 获取数据库列表
|
||||
func (r *MySQLController) DatabaseList(ctx http.Context) http.Response {
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
type database struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
|
||||
password := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
mysql, err := db.NewMySQL("root", password, r.getSock(), "unix")
|
||||
if err != nil {
|
||||
return controllers.Success(ctx, http.Json{
|
||||
"total": 0,
|
||||
"items": []database{},
|
||||
})
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
if err = db.Ping(); err != nil {
|
||||
return controllers.Success(ctx, http.Json{
|
||||
"total": 0,
|
||||
"items": []database{},
|
||||
"items": []types.MySQLDatabase{},
|
||||
})
|
||||
}
|
||||
|
||||
rows, err := db.Query("SHOW DATABASES")
|
||||
databases, err := mysql.Databases()
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var databases []database
|
||||
for rows.Next() {
|
||||
var d database
|
||||
if err = rows.Scan(&d.Name); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
databases = append(databases, d)
|
||||
}
|
||||
|
||||
if err = rows.Err(); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
|
||||
}
|
||||
|
||||
paged, total := controllers.Paginate(ctx, databases)
|
||||
|
||||
return controllers.Success(ctx, http.Json{
|
||||
@@ -272,17 +237,18 @@ func (r *MySQLController) AddDatabase(ctx http.Context) http.Response {
|
||||
user := ctx.Request().Input("user")
|
||||
password := ctx.Request().Input("password")
|
||||
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE DATABASE IF NOT EXISTS " + database + " DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
if err = mysql.DatabaseCreate(database); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
if err = mysql.UserCreate(user, password); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
if err = mysql.PrivilegesGrant(user, database); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
@@ -298,8 +264,12 @@ func (r *MySQLController) DeleteDatabase(ctx http.Context) http.Response {
|
||||
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
database := ctx.Request().Input("database")
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP DATABASE IF EXISTS " + database + ";\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if err = mysql.DatabaseDrop(database); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
@@ -394,71 +364,20 @@ func (r *MySQLController) RestoreBackup(ctx http.Context) http.Response {
|
||||
|
||||
// UserList 用户列表
|
||||
func (r *MySQLController) UserList(ctx http.Context) http.Response {
|
||||
type user struct {
|
||||
User string `json:"user"`
|
||||
Host string `json:"host"`
|
||||
Grants []string `json:"grants"`
|
||||
}
|
||||
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
|
||||
password := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
mysql, err := db.NewMySQL("root", password, r.getSock(), "unix")
|
||||
if err != nil {
|
||||
return controllers.Success(ctx, http.Json{
|
||||
"total": 0,
|
||||
"items": []user{},
|
||||
})
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
if err = db.Ping(); err != nil {
|
||||
return controllers.Success(ctx, http.Json{
|
||||
"total": 0,
|
||||
"items": []user{},
|
||||
"items": []types.MySQLUser{},
|
||||
})
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT user, host FROM mysql.user")
|
||||
users, err := mysql.Users()
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var userGrants []user
|
||||
|
||||
for rows.Next() {
|
||||
var u user
|
||||
if err = rows.Scan(&u.User, &u.Host); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// 查询用户权限
|
||||
grantsRows, err := db.Query(fmt.Sprintf("SHOW GRANTS FOR '%s'@'%s'", u.User, u.Host))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defer grantsRows.Close()
|
||||
|
||||
for grantsRows.Next() {
|
||||
var grant string
|
||||
if err = grantsRows.Scan(&grant); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
u.Grants = append(u.Grants, grant)
|
||||
}
|
||||
|
||||
if err = grantsRows.Err(); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
userGrants = append(userGrants, u)
|
||||
}
|
||||
|
||||
if err = rows.Err(); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, "获取用户列表失败")
|
||||
}
|
||||
|
||||
paged, total := controllers.Paginate(ctx, userGrants)
|
||||
paged, total := controllers.Paginate(ctx, users)
|
||||
|
||||
return controllers.Success(ctx, http.Json{
|
||||
"total": total,
|
||||
@@ -480,14 +399,15 @@ func (r *MySQLController) AddUser(ctx http.Context) http.Response {
|
||||
user := ctx.Request().Input("user")
|
||||
password := ctx.Request().Input("password")
|
||||
database := ctx.Request().Input("database")
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"CREATE USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + ";'\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
if err = mysql.UserCreate(user, password); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
if err = mysql.PrivilegesGrant(user, database); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
@@ -503,8 +423,12 @@ func (r *MySQLController) DeleteUser(ctx http.Context) http.Response {
|
||||
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
user := ctx.Request().Input("user")
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"DROP USER '" + user + "'@'localhost';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if err = mysql.UserDrop(user); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
@@ -522,11 +446,12 @@ func (r *MySQLController) SetUserPassword(ctx http.Context) http.Response {
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
user := ctx.Request().Input("user")
|
||||
password := ctx.Request().Input("password")
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"ALTER USER '" + user + "'@'localhost' IDENTIFIED BY '" + password + "';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
if err = mysql.UserPassword(user, password); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
@@ -544,15 +469,38 @@ func (r *MySQLController) SetUserPrivileges(ctx http.Context) http.Response {
|
||||
rootPassword := r.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
user := ctx.Request().Input("user")
|
||||
database := ctx.Request().Input("database")
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"REVOKE ALL PRIVILEGES ON *.* FROM '" + user + "'@'localhost';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
mysql, err := db.NewMySQL("root", rootPassword, r.getSock(), "unix")
|
||||
if err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"GRANT ALL PRIVILEGES ON " + database + ".* TO '" + user + "'@'localhost';\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
}
|
||||
if out, err := shell.Execf("/www/server/mysql/bin/mysql -uroot -p" + rootPassword + " -e \"FLUSH PRIVILEGES;\""); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, out)
|
||||
if err = mysql.PrivilegesGrant(user, database); err != nil {
|
||||
return controllers.Error(ctx, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return controllers.Success(ctx, nil)
|
||||
}
|
||||
|
||||
// getSock 获取sock文件位置
|
||||
func (r *MySQLController) getSock() string {
|
||||
if io.Exists("/tmp/mysql.sock") {
|
||||
return "/tmp/mysql.sock"
|
||||
}
|
||||
if io.Exists("/www/server/mysql/config/my.cnf") {
|
||||
config, _ := io.Read("/www/server/mysql/config/my.cnf")
|
||||
re := regexp.MustCompile(`socket\s*=\s*(['"]?)([^'"]+)\1`)
|
||||
matches := re.FindStringSubmatch(config)
|
||||
if len(matches) > 2 {
|
||||
return matches[2]
|
||||
}
|
||||
}
|
||||
if io.Exists("/etc/my.cnf") {
|
||||
config, _ := io.Read("/etc/my.cnf")
|
||||
re := regexp.MustCompile(`socket\s*=\s*(['"]?)([^'"]+)\1`)
|
||||
matches := re.FindStringSubmatch(config)
|
||||
if len(matches) > 2 {
|
||||
return matches[2]
|
||||
}
|
||||
}
|
||||
|
||||
return "/tmp/mysql.sock"
|
||||
}
|
||||
|
||||
173
pkg/db/mysql.go
Normal file
173
pkg/db/mysql.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/types"
|
||||
)
|
||||
|
||||
type MySQL struct {
|
||||
db *sql.DB
|
||||
username string
|
||||
password string
|
||||
address string
|
||||
}
|
||||
|
||||
func NewMySQL(username, password, address string, typ ...string) (*MySQL, error) {
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s)/", username, password, address)
|
||||
if len(typ) > 0 && typ[0] == "unix" {
|
||||
dsn = fmt.Sprintf("%s:%s@unix(%s)/", username, password, address)
|
||||
}
|
||||
db, err := sql.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("初始化MySQL连接失败: %w", err)
|
||||
}
|
||||
if db.Ping() != nil {
|
||||
return nil, fmt.Errorf("连接MySQL失败: %w", err)
|
||||
}
|
||||
return &MySQL{
|
||||
db: db,
|
||||
username: username,
|
||||
password: password,
|
||||
address: address,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *MySQL) Close() error {
|
||||
return m.db.Close()
|
||||
}
|
||||
|
||||
func (m *MySQL) Ping() error {
|
||||
return m.db.Ping()
|
||||
}
|
||||
|
||||
func (m *MySQL) Query(query string, args ...any) (*sql.Rows, error) {
|
||||
return m.db.Query(query, args...)
|
||||
}
|
||||
|
||||
func (m *MySQL) QueryRow(query string, args ...any) *sql.Row {
|
||||
return m.db.QueryRow(query, args...)
|
||||
}
|
||||
|
||||
func (m *MySQL) Exec(query string, args ...any) (sql.Result, error) {
|
||||
return m.db.Exec(query, args...)
|
||||
}
|
||||
|
||||
func (m *MySQL) Prepare(query string) (*sql.Stmt, error) {
|
||||
return m.db.Prepare(query)
|
||||
}
|
||||
|
||||
func (m *MySQL) DatabaseCreate(name string) error {
|
||||
_, err := m.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", name))
|
||||
m.flushPrivileges()
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *MySQL) DatabaseDrop(name string) error {
|
||||
_, err := m.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", name))
|
||||
m.flushPrivileges()
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *MySQL) UserCreate(user, password string) error {
|
||||
_, err := m.Exec(fmt.Sprintf("CREATE USER IF NOT EXISTS '%s'@'localhost' IDENTIFIED BY '%s'", user, password))
|
||||
m.flushPrivileges()
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *MySQL) UserDrop(user string) error {
|
||||
_, err := m.Exec(fmt.Sprintf("DROP USER IF EXISTS '%s'@'localhost'", user))
|
||||
m.flushPrivileges()
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *MySQL) UserPassword(user, password string) error {
|
||||
_, err := m.Exec(fmt.Sprintf("ALTER USER '%s'@'localhost' IDENTIFIED BY '%s'", user, password))
|
||||
m.flushPrivileges()
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *MySQL) PrivilegesGrant(user, database string) error {
|
||||
_, err := m.Exec(fmt.Sprintf("GRANT ALL PRIVILEGES ON %s.* TO '%s'@'localhost'", database, user))
|
||||
m.flushPrivileges()
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *MySQL) PrivilegesRevoke(user, database string) error {
|
||||
_, err := m.Exec(fmt.Sprintf("REVOKE ALL PRIVILEGES ON %s.* FROM '%s'@'localhost'", database, user))
|
||||
m.flushPrivileges()
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *MySQL) Users() ([]types.MySQLUser, error) {
|
||||
rows, err := m.Query("SELECT user, host FROM mysql.user")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var users []types.MySQLUser
|
||||
for rows.Next() {
|
||||
var user, host string
|
||||
if err := rows.Scan(&user, &host); err != nil {
|
||||
continue
|
||||
}
|
||||
grants, err := m.userGrants(user, host)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
users = append(users, types.MySQLUser{
|
||||
User: user,
|
||||
Host: host,
|
||||
Grants: grants,
|
||||
})
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (m *MySQL) Databases() ([]types.MySQLDatabase, error) {
|
||||
rows, err := m.Query("SHOW DATABASES")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var databases []types.MySQLDatabase
|
||||
for rows.Next() {
|
||||
var database string
|
||||
if err := rows.Scan(&database); err != nil {
|
||||
continue
|
||||
}
|
||||
databases = append(databases, types.MySQLDatabase{
|
||||
Name: database,
|
||||
})
|
||||
}
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
func (m *MySQL) userGrants(user, host string) ([]string, error) {
|
||||
rows, err := m.Query(fmt.Sprintf("SHOW GRANTS FOR '%s'@'%s'", user, host))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var grants []string
|
||||
for rows.Next() {
|
||||
var grant string
|
||||
if err := rows.Scan(&grant); err != nil {
|
||||
continue
|
||||
}
|
||||
grants = append(grants, grant)
|
||||
}
|
||||
return grants, nil
|
||||
}
|
||||
|
||||
func (m *MySQL) flushPrivileges() {
|
||||
_, _ = m.Exec("FLUSH PRIVILEGES")
|
||||
}
|
||||
33
pkg/db/mysql_tools.go
Normal file
33
pkg/db/mysql_tools.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/TheTNB/panel/pkg/shell"
|
||||
"github.com/TheTNB/panel/pkg/systemctl"
|
||||
)
|
||||
|
||||
// MySQLResetRootPassword 重置 MySQL root密码
|
||||
func MySQLResetRootPassword(password string) error {
|
||||
_ = systemctl.Stop("mysqld")
|
||||
if run, err := systemctl.Status("mysqld"); err != nil || run {
|
||||
return fmt.Errorf("停止MySQL失败: %w", err)
|
||||
}
|
||||
_, _ = shell.Execf(`systemctl set-environment MYSQLD_OPTS="--skip-grant-tables --skip-networking"`)
|
||||
if err := systemctl.Start("mysqld"); err != nil {
|
||||
return fmt.Errorf("以安全模式启动MySQL失败: %w", err)
|
||||
}
|
||||
if _, err := shell.Execf(`mysql -uroot -e "FLUSH PRIVILEGES;UPDATE mysql.user SET authentication_string=null WHERE user='root' AND host='localhost';ALTER USER 'root'@'localhost' IDENTIFIED BY '%s';FLUSH PRIVILEGES;"`, password); err != nil {
|
||||
return errors.New("设置root密码失败")
|
||||
}
|
||||
if err := systemctl.Stop("mysqld"); err != nil {
|
||||
return fmt.Errorf("停止MySQL失败: %w", err)
|
||||
}
|
||||
_, _ = shell.Execf(`systemctl unset-environment MYSQLD_OPTS`)
|
||||
if err := systemctl.Start("mysqld"); err != nil {
|
||||
return fmt.Errorf("启动MySQL失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
11
pkg/types/mysql.go
Normal file
11
pkg/types/mysql.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package types
|
||||
|
||||
type MySQLUser struct {
|
||||
User string `json:"user"`
|
||||
Host string `json:"host"`
|
||||
Grants []string `json:"grants"`
|
||||
}
|
||||
|
||||
type MySQLDatabase struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
@@ -287,8 +287,8 @@ chmod 644 ${mysqlPath}/conf/my.cnf
|
||||
|
||||
${mysqlPath}/bin/mysqld --initialize-insecure --user=mysql --basedir=${mysqlPath} --datadir=${mysqlPath}/data
|
||||
|
||||
echo "export PATH=${mysqlPath}/bin:\$PATH" >> /etc/profile.d/mysql.sh
|
||||
source /etc/profile
|
||||
# 软链接
|
||||
ln -sf ${mysqlPath}/bin/* /usr/bin/
|
||||
|
||||
# 写入 systemd 配置
|
||||
cat > /etc/systemd/system/mysqld.service << EOF
|
||||
|
||||
@@ -34,9 +34,6 @@ rm -f /usr/lib64/libmysql*
|
||||
userdel -r mysql
|
||||
groupdel mysql
|
||||
|
||||
rm -f /etc/profile.d/mysql.sh
|
||||
source /etc/profile
|
||||
|
||||
panel deletePlugin mysql${1}
|
||||
|
||||
echo -e "${HR}\nMySQL-${1} 卸载完成\n${HR}"
|
||||
|
||||
@@ -120,6 +120,9 @@ chown -R mysql:mysql ${mysqlPath}
|
||||
chmod -R 755 ${mysqlPath}
|
||||
chmod 644 ${mysqlPath}/conf/my.cnf
|
||||
|
||||
# 软链接
|
||||
ln -sf ${mysqlPath}/bin/* /usr/bin/
|
||||
|
||||
# 启动服务
|
||||
systemctl start mysqld
|
||||
|
||||
|
||||
@@ -133,6 +133,16 @@ if version_lt "$oldVersion" "2.2.14"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if version_lt "$oldVersion" "2.2.16"; then
|
||||
echo "更新面板到 v2.2.16 ..."
|
||||
echo "Update panel to v2.2.16 ..."
|
||||
if [ -f "/www/server/mysql/bin/mysql" ]; then
|
||||
ln -sf /www/server/mysql/bin/* /usr/bin/
|
||||
rm -f /etc/profile.d/mysql.sh
|
||||
source /etc/profile
|
||||
fi
|
||||
fi
|
||||
|
||||
echo $HR
|
||||
echo "更新结束"
|
||||
echo "Update finished"
|
||||
|
||||
Reference in New Issue
Block a user