mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 12:40:25 +08:00
feat: update v2.0.10
This commit is contained in:
@@ -4,11 +4,10 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/goravel/framework/contracts/console"
|
||||
"github.com/goravel/framework/contracts/console/command"
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"panel/app/models"
|
||||
"panel/app/services"
|
||||
@@ -73,12 +72,19 @@ func (receiver *Panel) Handle(ctx console.Context) error {
|
||||
color.Greenln("初始化成功")
|
||||
|
||||
case "update":
|
||||
var task models.Task
|
||||
err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task)
|
||||
if err == nil {
|
||||
color.Redln("当前有任务正在执行,禁止更新")
|
||||
return nil
|
||||
}
|
||||
|
||||
input := arg1
|
||||
proxy := false
|
||||
if input == "y" || input == "Y" || input == "yes" || input == "Yes" {
|
||||
proxy = true
|
||||
}
|
||||
err := tools.UpdatePanel(cast.ToBool(proxy))
|
||||
err = tools.UpdatePanel(cast.ToBool(proxy))
|
||||
if err != nil {
|
||||
color.Redln("更新失败: " + err.Error())
|
||||
return nil
|
||||
|
||||
@@ -53,7 +53,7 @@ func (r *CronController) Add(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ func (r *CronController) Update(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -163,8 +163,15 @@ func (c *InfoController) CheckUpdate(ctx http.Context) {
|
||||
}
|
||||
|
||||
func (c *InfoController) Update(ctx http.Context) {
|
||||
var task models.Task
|
||||
err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task)
|
||||
if err == nil {
|
||||
Error(ctx, http.StatusInternalServerError, "当前有任务正在执行,禁止更新")
|
||||
return
|
||||
}
|
||||
|
||||
proxy := ctx.Request().InputBool("proxy")
|
||||
err := tools.UpdatePanel(proxy)
|
||||
err = tools.UpdatePanel(proxy)
|
||||
if err != nil {
|
||||
facades.Log().Error("[面板][InfoController] 更新面板失败 ", err.Error())
|
||||
Error(ctx, http.StatusInternalServerError, "更新失败: "+err.Error())
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mysql57
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -11,8 +12,6 @@ import (
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/goravel/framework/support/carbon"
|
||||
"github.com/spf13/cast"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"panel/app/http/controllers"
|
||||
"panel/app/models"
|
||||
"panel/app/services"
|
||||
@@ -35,7 +34,7 @@ func (c *Mysql57Controller) Status(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
status := tools.ExecShell("systemctl status mysql | grep Active | grep -v grep | awk '{print $2}'")
|
||||
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
return
|
||||
@@ -54,8 +53,8 @@ func (c *Mysql57Controller) Reload(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("systemctl reload mysql")
|
||||
status := tools.ExecShell("systemctl status mysql | grep Active | grep -v grep | awk '{print $2}'")
|
||||
tools.ExecShell("systemctl reload mysqld")
|
||||
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
return
|
||||
@@ -74,8 +73,8 @@ func (c *Mysql57Controller) Restart(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("systemctl restart mysql")
|
||||
status := tools.ExecShell("systemctl status mysql | grep Active | grep -v grep | awk '{print $2}'")
|
||||
tools.ExecShell("systemctl restart mysqld")
|
||||
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
return
|
||||
@@ -94,8 +93,8 @@ func (c *Mysql57Controller) Start(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("systemctl start mysql")
|
||||
status := tools.ExecShell("systemctl status mysql | grep Active | grep -v grep | awk '{print $2}'")
|
||||
tools.ExecShell("systemctl start mysqld")
|
||||
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
return
|
||||
@@ -114,8 +113,8 @@ func (c *Mysql57Controller) Stop(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("systemctl stop mysql")
|
||||
status := tools.ExecShell("systemctl status mysql | grep Active | grep -v grep | awk '{print $2}'")
|
||||
tools.ExecShell("systemctl stop mysqld")
|
||||
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
return
|
||||
@@ -135,7 +134,7 @@ func (c *Mysql57Controller) GetConfig(ctx http.Context) {
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
config := tools.ReadFile("mysql57")
|
||||
config := tools.ReadFile("/www/server/mysql/conf/my.cnf")
|
||||
if len(config) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL配置失败")
|
||||
return
|
||||
@@ -156,7 +155,7 @@ func (c *Mysql57Controller) SaveConfig(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if !tools.WriteFile("mysql57", config, 0644) {
|
||||
if !tools.WriteFile("/www/server/mysql/conf/my.cnf", config, 0644) {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败")
|
||||
return
|
||||
}
|
||||
@@ -197,24 +196,24 @@ func (c *Mysql57Controller) Load(ctx http.Context) {
|
||||
regex string
|
||||
name string
|
||||
}{
|
||||
{`Uptime\s+\|\s+(\d+)\s+\|`, "总查询次数"},
|
||||
{`Queries\s+\|\s+(\d+)\s+\|`, "总连接次数"},
|
||||
{`Connections\s+\|\s+(\d+)\s+\|`, "每秒事务"},
|
||||
{`Com_commit\s+\|\s+(\d+)\s+\|`, "每秒回滚"},
|
||||
{`Com_rollback\s+\|\s+(\d+)\s+\|`, "发送"},
|
||||
{`Bytes_sent\s+\|\s+(\d+)\s+\|`, "接收"},
|
||||
{`Bytes_received\s+\|\s+(\d+)\s+\|`, "活动连接数"},
|
||||
{`Threads_connected\s+\|\s+(\d+)\s+\|`, "峰值连接数"},
|
||||
{`Max_used_connections\s+\|\s+(\d+)\s+\|`, "索引命中率"},
|
||||
{`Key_read_requests\s+\|\s+(\d+)\s+\|`, "Innodb索引命中率"},
|
||||
{`Innodb_buffer_pool_reads\s+\|\s+(\d+)\s+\|`, "创建临时表到磁盘"},
|
||||
{`Created_tmp_disk_tables\s+\|\s+(\d+)\s+\|`, "已打开的表"},
|
||||
{`Open_tables\s+\|\s+(\d+)\s+\|`, "没有使用索引的量"},
|
||||
{`Select_full_join\s+\|\s+(\d+)\s+\|`, "没有索引的JOIN量"},
|
||||
{`Select_full_range_join\s+\|\s+(\d+)\s+\|`, "没有索引的子查询量"},
|
||||
{`Select_range_check\s+\|\s+(\d+)\s+\|`, "排序后的合并次数"},
|
||||
{`Sort_merge_passes\s+\|\s+(\d+)\s+\|`, "锁表次数"},
|
||||
{`Table_locks_waited\s+\|\s+(\d+)\s+\|`, ""},
|
||||
{`Uptime\s+\|\s+(\d+)\s+\|`, "运行时间"},
|
||||
{`Queries\s+\|\s+(\d+)\s+\|`, "总查询次数"},
|
||||
{`Connections\s+\|\s+(\d+)\s+\|`, "总连接次数"},
|
||||
{`Com_commit\s+\|\s+(\d+)\s+\|`, "每秒事务"},
|
||||
{`Com_rollback\s+\|\s+(\d+)\s+\|`, "每秒回滚"},
|
||||
{`Bytes_sent\s+\|\s+(\d+)\s+\|`, "发送"},
|
||||
{`Bytes_received\s+\|\s+(\d+)\s+\|`, "接收"},
|
||||
{`Threads_connected\s+\|\s+(\d+)\s+\|`, "活动连接数"},
|
||||
{`Max_used_connections\s+\|\s+(\d+)\s+\|`, "峰值连接数"},
|
||||
{`Key_read_requests\s+\|\s+(\d+)\s+\|`, "索引命中率"},
|
||||
{`Innodb_buffer_pool_reads\s+\|\s+(\d+)\s+\|`, "Innodb索引命中率"},
|
||||
{`Created_tmp_disk_tables\s+\|\s+(\d+)\s+\|`, "创建临时表到磁盘"},
|
||||
{`Open_tables\s+\|\s+(\d+)\s+\|`, "已打开的表"},
|
||||
{`Select_full_join\s+\|\s+(\d+)\s+\|`, "没有使用索引的量"},
|
||||
{`Select_full_range_join\s+\|\s+(\d+)\s+\|`, "没有索引的JOIN量"},
|
||||
{`Select_range_check\s+\|\s+(\d+)\s+\|`, "没有索引的子查询量"},
|
||||
{`Sort_merge_passes\s+\|\s+(\d+)\s+\|`, "排序后的合并次数"},
|
||||
{`Table_locks_waited\s+\|\s+(\d+)\s+\|`, "锁表次数"},
|
||||
}
|
||||
|
||||
for i, expression := range expressions {
|
||||
@@ -234,7 +233,7 @@ func (c *Mysql57Controller) Load(ctx http.Context) {
|
||||
readRequests := cast.ToFloat64(data[9]["value"])
|
||||
reads := cast.ToFloat64(data[10]["value"])
|
||||
data[9]["value"] = fmt.Sprintf("%.2f%%", readRequests/(reads+readRequests)*100)
|
||||
// Innodb索引命中率
|
||||
// Innodb 索引命中率
|
||||
bufferPoolReads := cast.ToFloat64(data[11]["value"])
|
||||
bufferPoolReadRequests := cast.ToFloat64(data[12]["value"])
|
||||
data[10]["value"] = fmt.Sprintf("%.2f%%", bufferPoolReadRequests/(bufferPoolReads+bufferPoolReadRequests)*100)
|
||||
@@ -303,7 +302,7 @@ func (c *Mysql57Controller) SetRootPassword(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
status := tools.ExecShell("systemctl status mysql | grep Active | grep -v grep | awk '{print $2}'")
|
||||
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
return
|
||||
@@ -341,29 +340,45 @@ func (c *Mysql57Controller) DatabaseList(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
out := tools.ExecShell("mysql -uroot -p" + c.setting.Get(models.SettingKeyMysqlRootPassword) + " -e \"show databases;\"")
|
||||
databases := strings.Split(out, "\n")
|
||||
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
type database struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
databases = databases[1 : len(databases)-1]
|
||||
systemDatabases := []string{"information_schema", "mysql", "performance_schema", "sys"}
|
||||
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
|
||||
if err != nil {
|
||||
facades.Log().Error("[MySQL57] 连接数据库失败" + err.Error())
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "连接数据库失败")
|
||||
return
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
var userDatabases []string
|
||||
for _, db := range databases {
|
||||
if !slices.Contains(systemDatabases, db) {
|
||||
userDatabases = append(userDatabases, db)
|
||||
rows, err := db.Query("SHOW DATABASES")
|
||||
if err != nil {
|
||||
facades.Log().Error("[MySQL57] 获取数据库列表失败" + err.Error())
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var databases []database
|
||||
for rows.Next() {
|
||||
var d database
|
||||
err := rows.Scan(&d.Name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
databases = append(databases, d)
|
||||
}
|
||||
|
||||
type Database struct {
|
||||
Name string
|
||||
if err := rows.Err(); err != nil {
|
||||
facades.Log().Error("[MySQL57] 获取数据库列表失败" + err.Error())
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
var dbStructs []Database
|
||||
for _, db := range userDatabases {
|
||||
dbStructs = append(dbStructs, Database{Name: db})
|
||||
}
|
||||
|
||||
controllers.Success(ctx, dbStructs)
|
||||
controllers.Success(ctx, databases)
|
||||
}
|
||||
|
||||
// AddDatabase 添加数据库
|
||||
@@ -375,14 +390,14 @@ func (c *Mysql57Controller) AddDatabase(ctx http.Context) {
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
|
||||
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
|
||||
"password": "required|min_len:8|max_len:255|regex:^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*(_|[^\\w])).+$",
|
||||
"password": "required|min_len:8|max_len:255",
|
||||
})
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -413,7 +428,7 @@ func (c *Mysql57Controller) DeleteDatabase(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -458,6 +473,38 @@ func (c *Mysql57Controller) BackupList(ctx http.Context) {
|
||||
controllers.Success(ctx, backupFiles)
|
||||
}
|
||||
|
||||
// UploadBackup 上传备份
|
||||
func (c *Mysql57Controller) UploadBackup(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "mysql57") {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO 框架 Bug ? 下面会 panic
|
||||
controllers.Error(ctx, http.StatusBadRequest, "暂不支持")
|
||||
return
|
||||
|
||||
file, err := ctx.Request().File("file")
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql"
|
||||
if !tools.Exists(backupPath) {
|
||||
tools.Mkdir(backupPath, 0644)
|
||||
}
|
||||
|
||||
name := file.GetClientOriginalName()
|
||||
extension := file.GetClientOriginalExtension()
|
||||
_, err = file.Store(backupPath + "/" + name + "." + extension)
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.Success(ctx, "上传文件成功")
|
||||
}
|
||||
|
||||
// CreateBackup 创建备份
|
||||
func (c *Mysql57Controller) CreateBackup(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "mysql57") {
|
||||
@@ -472,14 +519,14 @@ func (c *Mysql57Controller) CreateBackup(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql"
|
||||
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
database := ctx.Request().Input("database")
|
||||
backupFile := backupPath + "/" + database + "_" + carbon.Now().ToShortDateTimeString() + ".sql"
|
||||
backupFile := database + "_" + carbon.Now().ToShortDateTimeString() + ".sql"
|
||||
if !tools.Exists(backupPath) {
|
||||
tools.Mkdir(backupPath, 0644)
|
||||
}
|
||||
@@ -490,9 +537,9 @@ func (c *Mysql57Controller) CreateBackup(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("mysqldump -uroot " + database + " > " + backupFile)
|
||||
tools.ExecShell("zip -c " + backupFile + ".zip " + backupFile)
|
||||
tools.RemoveFile(backupFile)
|
||||
tools.ExecShell("mysqldump -uroot " + database + " > " + backupPath + "/" + backupFile)
|
||||
tools.ExecShell("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile)
|
||||
tools.RemoveFile(backupPath + "/" + backupFile)
|
||||
_ = os.Unsetenv("MYSQL_PWD")
|
||||
|
||||
controllers.Success(ctx, "备份成功")
|
||||
@@ -512,7 +559,7 @@ func (c *Mysql57Controller) DeleteBackup(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -538,7 +585,7 @@ func (c *Mysql57Controller) RestoreBackup(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -602,36 +649,66 @@ func (c *Mysql57Controller) UserList(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Username string `json:"username"`
|
||||
Host string `json:"host"`
|
||||
Privileges string `json:"privileges"`
|
||||
type user struct {
|
||||
User string `json:"user"`
|
||||
Host string `json:"host"`
|
||||
Grants []string `json:"grants"`
|
||||
}
|
||||
|
||||
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
out := tools.ExecShell("mysql -uroot -p" + rootPassword + " -e 'select user,host from mysql.user'")
|
||||
rawUsers := strings.Split(out, "\n")
|
||||
users := make([]User, 0)
|
||||
for _, rawUser := range rawUsers {
|
||||
user := strings.Split(rawUser, "\t")
|
||||
if user[0] == "root" || user[0] == "mysql.sys" || user[0] == "mysql.infoschema" || user[0] == "mysql.session" {
|
||||
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
|
||||
if err != nil {
|
||||
facades.Log().Error("[MYSQL57] 连接数据库失败:" + err.Error())
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "连接数据库失败")
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT user, host FROM mysql.user")
|
||||
if err != nil {
|
||||
facades.Log().Error("[MYSQL57] 查询数据库失败:" + err.Error())
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "查询数据库失败")
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var userGrants []user
|
||||
|
||||
for rows.Next() {
|
||||
var u user
|
||||
err := rows.Scan(&u.User, &u.Host)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
out := tools.ExecShell("mysql -uroot -p" + rootPassword + " -e 'show grants for " + user[0] + "@" + user[1] + "'")
|
||||
rawPrivileges := strings.Split(out, "\n")
|
||||
privileges := make([]string, 0)
|
||||
for _, rawPrivilege := range rawPrivileges {
|
||||
if rawPrivilege == "" {
|
||||
// 查询用户权限
|
||||
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
|
||||
err := grantsRows.Scan(&grant)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
privilege := rawPrivilege[6:strings.Index(rawPrivilege, " TO")]
|
||||
privileges = append(privileges, privilege)
|
||||
|
||||
u.Grants = append(u.Grants, grant)
|
||||
}
|
||||
users = append(users, User{Username: user[0], Host: user[1], Privileges: strings.Join(privileges, " | ")})
|
||||
|
||||
if err := grantsRows.Err(); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
userGrants = append(userGrants, u)
|
||||
}
|
||||
|
||||
controllers.Success(ctx, users)
|
||||
if err := rows.Err(); err != nil {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取用户列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.Success(ctx, userGrants)
|
||||
}
|
||||
|
||||
// AddUser 添加用户
|
||||
@@ -643,14 +720,14 @@ func (c *Mysql57Controller) AddUser(ctx http.Context) {
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
|
||||
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
|
||||
"password": "required|min_len:8|max_len:255|regex:^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*(_|[^\\w])).+$",
|
||||
"password": "required|min_len:8|max_len:255",
|
||||
})
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -679,7 +756,7 @@ func (c *Mysql57Controller) DeleteUser(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -698,14 +775,14 @@ func (c *Mysql57Controller) SetUserPassword(ctx http.Context) {
|
||||
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
|
||||
"password": "required|min_len:8|max_len:255|regex:^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*(_|[^\\w])).+$",
|
||||
"password": "required|min_len:8|max_len:255",
|
||||
})
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -733,7 +810,7 @@ func (c *Mysql57Controller) SetUserPrivileges(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mysql80
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -11,8 +12,6 @@ import (
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/goravel/framework/support/carbon"
|
||||
"github.com/spf13/cast"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"panel/app/http/controllers"
|
||||
"panel/app/models"
|
||||
"panel/app/services"
|
||||
@@ -35,7 +34,7 @@ func (c *Mysql80Controller) Status(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
status := tools.ExecShell("systemctl status mysql | grep Active | grep -v grep | awk '{print $2}'")
|
||||
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
return
|
||||
@@ -54,8 +53,8 @@ func (c *Mysql80Controller) Reload(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("systemctl reload mysql")
|
||||
status := tools.ExecShell("systemctl status mysql | grep Active | grep -v grep | awk '{print $2}'")
|
||||
tools.ExecShell("systemctl reload mysqld")
|
||||
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
return
|
||||
@@ -74,8 +73,8 @@ func (c *Mysql80Controller) Restart(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("systemctl restart mysql")
|
||||
status := tools.ExecShell("systemctl status mysql | grep Active | grep -v grep | awk '{print $2}'")
|
||||
tools.ExecShell("systemctl restart mysqld")
|
||||
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
return
|
||||
@@ -94,8 +93,8 @@ func (c *Mysql80Controller) Start(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("systemctl start mysql")
|
||||
status := tools.ExecShell("systemctl status mysql | grep Active | grep -v grep | awk '{print $2}'")
|
||||
tools.ExecShell("systemctl start mysqld")
|
||||
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
return
|
||||
@@ -114,8 +113,8 @@ func (c *Mysql80Controller) Stop(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("systemctl stop mysql")
|
||||
status := tools.ExecShell("systemctl status mysql | grep Active | grep -v grep | awk '{print $2}'")
|
||||
tools.ExecShell("systemctl stop mysqld")
|
||||
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
return
|
||||
@@ -135,7 +134,7 @@ func (c *Mysql80Controller) GetConfig(ctx http.Context) {
|
||||
}
|
||||
|
||||
// 获取配置
|
||||
config := tools.ReadFile("mysql80")
|
||||
config := tools.ReadFile("/www/server/mysql/conf/my.cnf")
|
||||
if len(config) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL配置失败")
|
||||
return
|
||||
@@ -156,7 +155,7 @@ func (c *Mysql80Controller) SaveConfig(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if !tools.WriteFile("mysql80", config, 0644) {
|
||||
if !tools.WriteFile("/www/server/mysql/conf/my.cnf", config, 0644) {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "写入MySQL配置失败")
|
||||
return
|
||||
}
|
||||
@@ -197,24 +196,24 @@ func (c *Mysql80Controller) Load(ctx http.Context) {
|
||||
regex string
|
||||
name string
|
||||
}{
|
||||
{`Uptime\s+\|\s+(\d+)\s+\|`, "总查询次数"},
|
||||
{`Queries\s+\|\s+(\d+)\s+\|`, "总连接次数"},
|
||||
{`Connections\s+\|\s+(\d+)\s+\|`, "每秒事务"},
|
||||
{`Com_commit\s+\|\s+(\d+)\s+\|`, "每秒回滚"},
|
||||
{`Com_rollback\s+\|\s+(\d+)\s+\|`, "发送"},
|
||||
{`Bytes_sent\s+\|\s+(\d+)\s+\|`, "接收"},
|
||||
{`Bytes_received\s+\|\s+(\d+)\s+\|`, "活动连接数"},
|
||||
{`Threads_connected\s+\|\s+(\d+)\s+\|`, "峰值连接数"},
|
||||
{`Max_used_connections\s+\|\s+(\d+)\s+\|`, "索引命中率"},
|
||||
{`Key_read_requests\s+\|\s+(\d+)\s+\|`, "Innodb索引命中率"},
|
||||
{`Innodb_buffer_pool_reads\s+\|\s+(\d+)\s+\|`, "创建临时表到磁盘"},
|
||||
{`Created_tmp_disk_tables\s+\|\s+(\d+)\s+\|`, "已打开的表"},
|
||||
{`Open_tables\s+\|\s+(\d+)\s+\|`, "没有使用索引的量"},
|
||||
{`Select_full_join\s+\|\s+(\d+)\s+\|`, "没有索引的JOIN量"},
|
||||
{`Select_full_range_join\s+\|\s+(\d+)\s+\|`, "没有索引的子查询量"},
|
||||
{`Select_range_check\s+\|\s+(\d+)\s+\|`, "排序后的合并次数"},
|
||||
{`Sort_merge_passes\s+\|\s+(\d+)\s+\|`, "锁表次数"},
|
||||
{`Table_locks_waited\s+\|\s+(\d+)\s+\|`, ""},
|
||||
{`Uptime\s+\|\s+(\d+)\s+\|`, "运行时间"},
|
||||
{`Queries\s+\|\s+(\d+)\s+\|`, "总查询次数"},
|
||||
{`Connections\s+\|\s+(\d+)\s+\|`, "总连接次数"},
|
||||
{`Com_commit\s+\|\s+(\d+)\s+\|`, "每秒事务"},
|
||||
{`Com_rollback\s+\|\s+(\d+)\s+\|`, "每秒回滚"},
|
||||
{`Bytes_sent\s+\|\s+(\d+)\s+\|`, "发送"},
|
||||
{`Bytes_received\s+\|\s+(\d+)\s+\|`, "接收"},
|
||||
{`Threads_connected\s+\|\s+(\d+)\s+\|`, "活动连接数"},
|
||||
{`Max_used_connections\s+\|\s+(\d+)\s+\|`, "峰值连接数"},
|
||||
{`Key_read_requests\s+\|\s+(\d+)\s+\|`, "索引命中率"},
|
||||
{`Innodb_buffer_pool_reads\s+\|\s+(\d+)\s+\|`, "Innodb索引命中率"},
|
||||
{`Created_tmp_disk_tables\s+\|\s+(\d+)\s+\|`, "创建临时表到磁盘"},
|
||||
{`Open_tables\s+\|\s+(\d+)\s+\|`, "已打开的表"},
|
||||
{`Select_full_join\s+\|\s+(\d+)\s+\|`, "没有使用索引的量"},
|
||||
{`Select_full_range_join\s+\|\s+(\d+)\s+\|`, "没有索引的JOIN量"},
|
||||
{`Select_range_check\s+\|\s+(\d+)\s+\|`, "没有索引的子查询量"},
|
||||
{`Sort_merge_passes\s+\|\s+(\d+)\s+\|`, "排序后的合并次数"},
|
||||
{`Table_locks_waited\s+\|\s+(\d+)\s+\|`, "锁表次数"},
|
||||
}
|
||||
|
||||
for i, expression := range expressions {
|
||||
@@ -234,7 +233,7 @@ func (c *Mysql80Controller) Load(ctx http.Context) {
|
||||
readRequests := cast.ToFloat64(data[9]["value"])
|
||||
reads := cast.ToFloat64(data[10]["value"])
|
||||
data[9]["value"] = fmt.Sprintf("%.2f%%", readRequests/(reads+readRequests)*100)
|
||||
// Innodb索引命中率
|
||||
// Innodb 索引命中率
|
||||
bufferPoolReads := cast.ToFloat64(data[11]["value"])
|
||||
bufferPoolReadRequests := cast.ToFloat64(data[12]["value"])
|
||||
data[10]["value"] = fmt.Sprintf("%.2f%%", bufferPoolReadRequests/(bufferPoolReads+bufferPoolReadRequests)*100)
|
||||
@@ -303,7 +302,7 @@ func (c *Mysql80Controller) SetRootPassword(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
status := tools.ExecShell("systemctl status mysql | grep Active | grep -v grep | awk '{print $2}'")
|
||||
status := tools.ExecShell("systemctl status mysqld | grep Active | grep -v grep | awk '{print $2}'")
|
||||
if len(status) == 0 {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取MySQL状态失败")
|
||||
return
|
||||
@@ -341,29 +340,45 @@ func (c *Mysql80Controller) DatabaseList(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
out := tools.ExecShell("mysql -uroot -p" + c.setting.Get(models.SettingKeyMysqlRootPassword) + " -e \"show databases;\"")
|
||||
databases := strings.Split(out, "\n")
|
||||
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
type database struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
databases = databases[1 : len(databases)-1]
|
||||
systemDatabases := []string{"information_schema", "mysql", "performance_schema", "sys"}
|
||||
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
|
||||
if err != nil {
|
||||
facades.Log().Error("[MySQL80] 连接数据库失败" + err.Error())
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "连接数据库失败")
|
||||
return
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
var userDatabases []string
|
||||
for _, db := range databases {
|
||||
if !slices.Contains(systemDatabases, db) {
|
||||
userDatabases = append(userDatabases, db)
|
||||
rows, err := db.Query("SHOW DATABASES")
|
||||
if err != nil {
|
||||
facades.Log().Error("[MySQL80] 获取数据库列表失败" + err.Error())
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var databases []database
|
||||
for rows.Next() {
|
||||
var d database
|
||||
err := rows.Scan(&d.Name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
databases = append(databases, d)
|
||||
}
|
||||
|
||||
type Database struct {
|
||||
Name string
|
||||
if err := rows.Err(); err != nil {
|
||||
facades.Log().Error("[MySQL80] 获取数据库列表失败" + err.Error())
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取数据库列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
var dbStructs []Database
|
||||
for _, db := range userDatabases {
|
||||
dbStructs = append(dbStructs, Database{Name: db})
|
||||
}
|
||||
|
||||
controllers.Success(ctx, dbStructs)
|
||||
controllers.Success(ctx, databases)
|
||||
}
|
||||
|
||||
// AddDatabase 添加数据库
|
||||
@@ -375,14 +390,14 @@ func (c *Mysql80Controller) AddDatabase(ctx http.Context) {
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
|
||||
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
|
||||
"password": "required|min_len:8|max_len:255|regex:^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*(_|[^\\w])).+$",
|
||||
"password": "required|min_len:8|max_len:255",
|
||||
})
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -413,7 +428,7 @@ func (c *Mysql80Controller) DeleteDatabase(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -458,6 +473,38 @@ func (c *Mysql80Controller) BackupList(ctx http.Context) {
|
||||
controllers.Success(ctx, backupFiles)
|
||||
}
|
||||
|
||||
// UploadBackup 上传备份
|
||||
func (c *Mysql80Controller) UploadBackup(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "mysql80") {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO 框架 Bug ? 下面会 panic
|
||||
controllers.Error(ctx, http.StatusBadRequest, "暂不支持")
|
||||
return
|
||||
|
||||
file, err := ctx.Request().File("file")
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql"
|
||||
if !tools.Exists(backupPath) {
|
||||
tools.Mkdir(backupPath, 0644)
|
||||
}
|
||||
|
||||
name := file.GetClientOriginalName()
|
||||
extension := file.GetClientOriginalExtension()
|
||||
_, err = file.Store(backupPath + "/" + name + "." + extension)
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusBadRequest, "上传文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.Success(ctx, "上传文件成功")
|
||||
}
|
||||
|
||||
// CreateBackup 创建备份
|
||||
func (c *Mysql80Controller) CreateBackup(ctx http.Context) {
|
||||
if !controllers.Check(ctx, "mysql80") {
|
||||
@@ -472,14 +519,14 @@ func (c *Mysql80Controller) CreateBackup(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
backupPath := c.setting.Get(models.SettingKeyBackupPath) + "/mysql"
|
||||
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
database := ctx.Request().Input("database")
|
||||
backupFile := backupPath + "/" + database + "_" + carbon.Now().ToShortDateTimeString() + ".sql"
|
||||
backupFile := database + "_" + carbon.Now().ToShortDateTimeString() + ".sql"
|
||||
if !tools.Exists(backupPath) {
|
||||
tools.Mkdir(backupPath, 0644)
|
||||
}
|
||||
@@ -490,9 +537,9 @@ func (c *Mysql80Controller) CreateBackup(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
tools.ExecShell("mysqldump -uroot " + database + " > " + backupFile)
|
||||
tools.ExecShell("zip -c " + backupFile + ".zip " + backupFile)
|
||||
tools.RemoveFile(backupFile)
|
||||
tools.ExecShell("mysqldump -uroot " + database + " > " + backupPath + "/" + backupFile)
|
||||
tools.ExecShell("cd " + backupPath + " && zip -r " + backupPath + "/" + backupFile + ".zip " + backupFile)
|
||||
tools.RemoveFile(backupPath + "/" + backupFile)
|
||||
_ = os.Unsetenv("MYSQL_PWD")
|
||||
|
||||
controllers.Success(ctx, "备份成功")
|
||||
@@ -512,7 +559,7 @@ func (c *Mysql80Controller) DeleteBackup(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -538,7 +585,7 @@ func (c *Mysql80Controller) RestoreBackup(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -553,7 +600,7 @@ func (c *Mysql80Controller) RestoreBackup(ctx http.Context) {
|
||||
|
||||
err = os.Setenv("MYSQL_PWD", rootPassword)
|
||||
if err != nil {
|
||||
facades.Log().Error("[MySQL80] 设置环境变量 MYSQL_PWD 失败:" + err.Error())
|
||||
facades.Log().Error("[MYSQL80] 设置环境变量 MYSQL_PWD 失败:" + err.Error())
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "还原失败")
|
||||
return
|
||||
}
|
||||
@@ -602,36 +649,66 @@ func (c *Mysql80Controller) UserList(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Username string `json:"username"`
|
||||
Host string `json:"host"`
|
||||
Privileges string `json:"privileges"`
|
||||
type user struct {
|
||||
User string `json:"user"`
|
||||
Host string `json:"host"`
|
||||
Grants []string `json:"grants"`
|
||||
}
|
||||
|
||||
rootPassword := c.setting.Get(models.SettingKeyMysqlRootPassword)
|
||||
out := tools.ExecShell("mysql -uroot -p" + rootPassword + " -e 'select user,host from mysql.user'")
|
||||
rawUsers := strings.Split(out, "\n")
|
||||
users := make([]User, 0)
|
||||
for _, rawUser := range rawUsers {
|
||||
user := strings.Split(rawUser, "\t")
|
||||
if user[0] == "root" || user[0] == "mysql.sys" || user[0] == "mysql.infoschema" || user[0] == "mysql.session" {
|
||||
db, err := sql.Open("mysql", "root:"+rootPassword+"@unix(/tmp/mysql.sock)/")
|
||||
if err != nil {
|
||||
facades.Log().Error("[MYSQL80] 连接数据库失败:" + err.Error())
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "连接数据库失败")
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT user, host FROM mysql.user")
|
||||
if err != nil {
|
||||
facades.Log().Error("[MYSQL80] 查询数据库失败:" + err.Error())
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "查询数据库失败")
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var userGrants []user
|
||||
|
||||
for rows.Next() {
|
||||
var u user
|
||||
err := rows.Scan(&u.User, &u.Host)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
out := tools.ExecShell("mysql -uroot -p" + rootPassword + " -e 'show grants for " + user[0] + "@" + user[1] + "'")
|
||||
rawPrivileges := strings.Split(out, "\n")
|
||||
privileges := make([]string, 0)
|
||||
for _, rawPrivilege := range rawPrivileges {
|
||||
if rawPrivilege == "" {
|
||||
// 查询用户权限
|
||||
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
|
||||
err := grantsRows.Scan(&grant)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
privilege := rawPrivilege[6:strings.Index(rawPrivilege, " TO")]
|
||||
privileges = append(privileges, privilege)
|
||||
|
||||
u.Grants = append(u.Grants, grant)
|
||||
}
|
||||
users = append(users, User{Username: user[0], Host: user[1], Privileges: strings.Join(privileges, " | ")})
|
||||
|
||||
if err := grantsRows.Err(); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
userGrants = append(userGrants, u)
|
||||
}
|
||||
|
||||
controllers.Success(ctx, users)
|
||||
if err := rows.Err(); err != nil {
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "获取用户列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.Success(ctx, userGrants)
|
||||
}
|
||||
|
||||
// AddUser 添加用户
|
||||
@@ -643,14 +720,14 @@ func (c *Mysql80Controller) AddUser(ctx http.Context) {
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"database": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
|
||||
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
|
||||
"password": "required|min_len:8|max_len:255|regex:^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*(_|[^\\w])).+$",
|
||||
"password": "required|min_len:8|max_len:255",
|
||||
})
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -679,7 +756,7 @@ func (c *Mysql80Controller) DeleteUser(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -698,14 +775,14 @@ func (c *Mysql80Controller) SetUserPassword(ctx http.Context) {
|
||||
|
||||
validator, err := ctx.Request().Validate(map[string]string{
|
||||
"user": "required|min_len:1|max_len:255|regex:^[a-zA-Z][a-zA-Z0-9_]+$",
|
||||
"password": "required|min_len:8|max_len:255|regex:^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*(_|[^\\w])).+$",
|
||||
"password": "required|min_len:8|max_len:255",
|
||||
})
|
||||
if err != nil {
|
||||
controllers.Error(ctx, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -733,7 +810,7 @@ func (c *Mysql80Controller) SetUserPrivileges(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
controllers.Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ func (c *Php74Controller) Load(ctx http.Context) {
|
||||
}
|
||||
|
||||
client := req.C().SetTimeout(10 * time.Second)
|
||||
resp, err := client.R().Get("http://127.0.0.1/phpfpm_" + c.version + "_status")
|
||||
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version)
|
||||
if err != nil || !resp.IsSuccessState() {
|
||||
facades.Log().Error("获取PHP-" + c.version + "运行状态失败")
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
|
||||
@@ -258,7 +258,7 @@ func (c *Php74Controller) InstallExtension(ctx http.Context) {
|
||||
var task models.Task
|
||||
task.Name = "安装PHP-" + c.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = "bash /www/panel/scripts/php_extensions/" + item.Slug + ".sh install " + c.version + ">> /tmp/" + item.Slug + ".log 2>&1"
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error())
|
||||
@@ -298,7 +298,7 @@ func (c *Php74Controller) UninstallExtension(ctx http.Context) {
|
||||
var task models.Task
|
||||
task.Name = "卸载PHP-" + c.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = "bash /www/panel/scripts/php_extensions/" + item.Slug + ".sh uninstall " + c.version + ">> /tmp/" + item.Slug + ".log 2>&1"
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error())
|
||||
@@ -361,7 +361,7 @@ func (c *Php74Controller) GetExtensions() []Extension {
|
||||
for _, item := range rawExtensionList {
|
||||
if !strings.Contains(item, "[") && item != "" {
|
||||
for i := range extensions {
|
||||
if extensions[i].Name == item {
|
||||
if extensions[i].Slug == item {
|
||||
extensions[i].Installed = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ func (c *Php80Controller) Load(ctx http.Context) {
|
||||
}
|
||||
|
||||
client := req.C().SetTimeout(10 * time.Second)
|
||||
resp, err := client.R().Get("http://127.0.0.1/phpfpm_" + c.version + "_status")
|
||||
resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + c.version)
|
||||
if err != nil || !resp.IsSuccessState() {
|
||||
facades.Log().Error("获取PHP-" + c.version + "运行状态失败")
|
||||
controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+c.version+"] 获取运行状态失败")
|
||||
@@ -258,7 +258,7 @@ func (c *Php80Controller) InstallExtension(ctx http.Context) {
|
||||
var task models.Task
|
||||
task.Name = "安装PHP-" + c.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = "bash /www/panel/scripts/php_extensions/" + item.Slug + ".sh install " + c.version + ">> /tmp/" + item.Slug + ".log 2>&1"
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Error("[PHP-" + c.version + "] 创建安装拓展任务失败:" + err.Error())
|
||||
@@ -298,7 +298,7 @@ func (c *Php80Controller) UninstallExtension(ctx http.Context) {
|
||||
var task models.Task
|
||||
task.Name = "卸载PHP-" + c.version + "扩展-" + item.Name
|
||||
task.Status = models.TaskStatusWaiting
|
||||
task.Shell = "bash /www/panel/scripts/php_extensions/" + item.Slug + ".sh uninstall " + c.version + ">> /tmp/" + item.Slug + ".log 2>&1"
|
||||
task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + c.version + ` >> /tmp/` + item.Slug + `.log 2>&1`
|
||||
task.Log = "/tmp/" + item.Slug + ".log"
|
||||
if err := facades.Orm().Query().Create(&task); err != nil {
|
||||
facades.Log().Error("[PHP-" + c.version + "] 创建卸载拓展任务失败:" + err.Error())
|
||||
@@ -361,7 +361,7 @@ func (c *Php80Controller) GetExtensions() []Extension {
|
||||
for _, item := range rawExtensionList {
|
||||
if !strings.Contains(item, "[") && item != "" {
|
||||
for i := range extensions {
|
||||
if extensions[i].Name == item {
|
||||
if extensions[i].Slug == item {
|
||||
extensions[i].Installed = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func (c *WebsiteController) Add(ctx http.Context) {
|
||||
return
|
||||
}
|
||||
if validator.Fails() {
|
||||
Error(ctx, http.StatusBadRequest, validator.Errors().All())
|
||||
Error(ctx, http.StatusBadRequest, validator.Errors().One())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -372,9 +372,8 @@ func (c *WebsiteController) SaveConfig(ctx http.Context) {
|
||||
website.Php = ctx.Request().InputInt("php")
|
||||
phpConfigOld := tools.Cut(raw, "# php标记位开始", "# php标记位结束")
|
||||
phpConfig := `
|
||||
include enable-php` + strconv.Itoa(website.Php) + `.conf;
|
||||
|
||||
`
|
||||
include enable-php-` + strconv.Itoa(website.Php) + `.conf;
|
||||
`
|
||||
if len(strings.TrimSpace(phpConfigOld)) != 0 {
|
||||
raw = strings.Replace(raw, phpConfigOld, phpConfig, -1)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func init() {
|
||||
"disks": map[string]any{
|
||||
"local": map[string]any{
|
||||
"driver": "local",
|
||||
"root": "/",
|
||||
"root": "storage/app",
|
||||
"url": "http://localhost/",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -8,6 +8,6 @@ func init() {
|
||||
config := facades.Config()
|
||||
config.Add("panel", map[string]any{
|
||||
"name": "耗子面板",
|
||||
"version": "v2.0.9",
|
||||
"version": "v2.0.10",
|
||||
})
|
||||
}
|
||||
|
||||
4
go.mod
4
go.mod
@@ -5,8 +5,8 @@ go 1.18
|
||||
require (
|
||||
github.com/gertd/go-pluralize v0.2.1
|
||||
github.com/gin-contrib/static v0.0.1
|
||||
github.com/gookit/color v1.5.3
|
||||
github.com/goravel/framework v1.12.1-0.20230717105343-6ce864794883
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/goravel/framework v1.12.1-0.20230721095426-6f24ecdaf6a7
|
||||
github.com/iancoleman/strcase v0.2.0
|
||||
github.com/imroc/req/v3 v3.37.2
|
||||
github.com/mojocn/base64Captcha v1.3.5
|
||||
|
||||
13
go.sum
13
go.sum
@@ -85,6 +85,7 @@ github.com/aws/aws-sdk-go v1.37.16 h1:Q4YOP2s00NpB9wfmTDZArdcLRuG9ijbnoAwTW3ivle
|
||||
github.com/aws/aws-sdk-go v1.37.16/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||
github.com/brianvoe/gofakeit/v6 v6.23.0 h1:pgVhyWpYq4e0GEVCh2gdZnS/nBX+8SnyTBliHg5xjks=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM=
|
||||
github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
@@ -169,8 +170,6 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||
github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc=
|
||||
github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8=
|
||||
github.com/glebarez/sqlite v1.9.0 h1:Aj6bPA12ZEx5GbSF6XADmCkYXlljPNUY+Zf1EQxynXs=
|
||||
github.com/glebarez/sqlite v1.9.0/go.mod h1:YBYCoyupOao60lzp1MVBLEjZfgkq0tdB1voAQ09K9zw=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
@@ -345,8 +344,8 @@ github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK6
|
||||
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg=
|
||||
github.com/gookit/color v1.5.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE=
|
||||
github.com/gookit/color v1.5.3/go.mod h1:NUzwzeehUfl7GIb36pqId+UGmRfQcU/WiiyTTeNjHtE=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/gookit/filter v1.1.4 h1:SXd6PEumiP/0jtF2crQRaz1wmKwHbW9xg5Ds6/ZP16w=
|
||||
github.com/gookit/filter v1.1.4/go.mod h1:0CEPQvudso375RitQf9X8HerUg9cz8N7c/yn6b1RMzM=
|
||||
github.com/gookit/goutil v0.5.12/go.mod h1:6vhWm/bSYXGE8poqFbFz6IGM7jV2r6qVhyK567SX/AI=
|
||||
@@ -358,10 +357,8 @@ github.com/goravel/file-rotatelogs v0.0.0-20211215053220-2ab31dd9575c h1:obhFK91
|
||||
github.com/goravel/file-rotatelogs v0.0.0-20211215053220-2ab31dd9575c/go.mod h1:YSWsLXlG16u5CWFaXNZHhEQD10+NwF3xfgDV816OwLE=
|
||||
github.com/goravel/file-rotatelogs/v2 v2.4.1 h1:ogkeIFcTHSBRUBpZYiyJbpul8hkVXxHPuDbOaP78O1M=
|
||||
github.com/goravel/file-rotatelogs/v2 v2.4.1/go.mod h1:euk9qr52WrzM8ICs1hecFcR4CZ/ZZOPdacHfvHgbOf0=
|
||||
github.com/goravel/framework v1.12.1-0.20230710102458-737cce0f687c h1:KPggCIxAZghopvPXC0g0jCmFNbC+r5EawjHCcbfBOhQ=
|
||||
github.com/goravel/framework v1.12.1-0.20230710102458-737cce0f687c/go.mod h1:SBsBTY8KTqSDipGPEZXnRRC1mWzKmL55Eo1P5aHNDgY=
|
||||
github.com/goravel/framework v1.12.1-0.20230717105343-6ce864794883 h1:8+87CCYqt5O6GqAYmGWz/W6SXGKp6xyn+McMbtWgyug=
|
||||
github.com/goravel/framework v1.12.1-0.20230717105343-6ce864794883/go.mod h1:quBqUHAyZutEs/TFfm9b/caI+BtyF82njGhNIL1QJM0=
|
||||
github.com/goravel/framework v1.12.1-0.20230721095426-6f24ecdaf6a7 h1:2rkC/6M7tLx/Ya1TulO2GjtVRIGXTmpp0XppzZRloYA=
|
||||
github.com/goravel/framework v1.12.1-0.20230721095426-6f24ecdaf6a7/go.mod h1:1lxwtCXMkotSmt0YWRg/s7EnMUFfmne9L1a50X9J0fA=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
|
||||
|
||||
@@ -10,14 +10,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/goravel/framework/facades"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"github.com/shirou/gopsutil/host"
|
||||
"github.com/shirou/gopsutil/load"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"github.com/shirou/gopsutil/net"
|
||||
"panel/app/models"
|
||||
)
|
||||
|
||||
// MonitoringInfo 监控信息
|
||||
@@ -119,12 +117,6 @@ func GetLatestPanelVersion() (PanelInfo, error) {
|
||||
|
||||
// UpdatePanel 更新面板
|
||||
func UpdatePanel(proxy bool) error {
|
||||
var task models.Task
|
||||
err := facades.Orm().Query().Where("status", models.TaskStatusRunning).OrWhere("status", models.TaskStatusWaiting).FirstOrFail(&task)
|
||||
if err == nil {
|
||||
return errors.New("面板有任务正在执行,禁止更新")
|
||||
}
|
||||
|
||||
panelInfo, err := GetLatestPanelVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
558
public/panel/views/plugins/mysql57.html
Normal file
558
public/panel/views/plugins/mysql57.html
Normal file
@@ -0,0 +1,558 @@
|
||||
<!--
|
||||
Name: MySQL管理器
|
||||
Author: 耗子
|
||||
Date: 2022-07-22
|
||||
-->
|
||||
<title>MySQL</title>
|
||||
<div class="layui-fluid" id="component-tabs">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md12">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">MySQL管理</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-tab">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">基本信息</li>
|
||||
<li>管理</li>
|
||||
<li>配置修改</li>
|
||||
<li>负载状态</li>
|
||||
<li>错误日志</li>
|
||||
<li>慢查询日志</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
|
||||
<legend>运行状态</legend>
|
||||
</fieldset>
|
||||
<blockquote id="mysql-status" class="layui-elem-quote layui-quote-nm">当前状态:<span
|
||||
class="layui-badge layui-bg-black">获取中</span></blockquote>
|
||||
<div class="layui-btn-container" style="padding-top: 30px;">
|
||||
<button id="mysql-start" class="layui-btn">启动</button>
|
||||
<button id="mysql-stop" class="layui-btn layui-btn-danger">停止</button>
|
||||
<button id="mysql-restart" class="layui-btn layui-btn-warm">重启</button>
|
||||
<button id="mysql-reload" class="layui-btn layui-btn-normal">重载</button>
|
||||
</div>
|
||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
|
||||
<legend>基本设置</legend>
|
||||
</fieldset>
|
||||
<div class="layui-form" lay-filter="mysql_setting">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" style="font-size: 13px;">root 密码</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="mysql_root_password" value="获取中ing..."
|
||||
class="layui-input" disabled>
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">查看/修改MySQL的root密码</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn layui-btn-sm" lay-submit
|
||||
lay-filter="mysql_setting_submit">确认修改
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<blockquote class="layui-elem-quote">面板仅集成了部分常用功能,如需更多功能,建议安装
|
||||
phpMyAdmin 使用。
|
||||
</blockquote>
|
||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
|
||||
<legend>数据库列表</legend>
|
||||
</fieldset>
|
||||
<table class="layui-hide" id="mysql-database-list"
|
||||
lay-filter="mysql-database-list"></table>
|
||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
|
||||
<legend>用户列表</legend>
|
||||
</fieldset>
|
||||
<table class="layui-hide" id="mysql-user-list" lay-filter="mysql-user-list"></table>
|
||||
<!-- 数据库顶部工具栏 -->
|
||||
<script type="text/html" id="mysql-database-list-bar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="add_database">新建数据库
|
||||
</button>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 用户顶部工具栏 -->
|
||||
<script type="text/html" id="mysql-user-list-bar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="add_user">新建用户</button>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 数据库右侧管理 -->
|
||||
<script type="text/html" id="mysql-database-list-control">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="backup">备份</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
<!-- 用户右侧管理 -->
|
||||
<script type="text/html" id="mysql-user-list-control">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs"
|
||||
lay-event="change_password">改密</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<blockquote class="layui-elem-quote">此处修改的是MySQL主配置文件,如果你不了解各参数的含义,请不要随意修改!<br>
|
||||
提示:Ctrl+F 搜索关键字,Ctrl+S 保存,Ctrl+H 查找替换!
|
||||
</blockquote>
|
||||
<div id="mysql-config-editor"
|
||||
style="height: 600px;"></div>
|
||||
<div class="layui-btn-container" style="padding-top: 30px;">
|
||||
<button id="mysql-config-save" class="layui-btn">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<table class="layui-hide" id="mysql-load-status"></table>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<div class="layui-btn-container">
|
||||
<button id="mysql-clean-error-log" class="layui-btn">清空日志</button>
|
||||
</div>
|
||||
<pre id="mysql-error-log" class="layui-code">
|
||||
获取中...
|
||||
</pre>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<div class="layui-btn-container">
|
||||
<button id="mysql-clean-slow-log" class="layui-btn">清空日志</button>
|
||||
</div>
|
||||
<pre id="mysql-slow-log" class="layui-code">
|
||||
获取中...
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let mysql_config_editor;// 定义mysql配置编辑器的全局变量
|
||||
layui.use(['index', 'code', 'table'], function () {
|
||||
let $ = layui.$
|
||||
, admin = layui.admin
|
||||
, element = layui.element
|
||||
, code = layui.code
|
||||
, table = layui.table
|
||||
, form = layui.form
|
||||
, view = layui.view;
|
||||
|
||||
// 渲染表单
|
||||
form.render();
|
||||
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/rootPassword"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:系统信息获取失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
form.val("mysql_setting", {
|
||||
"mysql_root_password": result.data
|
||||
});
|
||||
$('input').attr('disabled', false);
|
||||
}
|
||||
});
|
||||
// 提交修改
|
||||
form.on('submit(mysql_setting_submit)', function (data) {
|
||||
index = layer.msg('请稍候...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/rootPassword"
|
||||
, type: 'post'
|
||||
, data: data.field
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL设置保存失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
layer.msg('修改成功!')
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
// 获取mysql运行状态并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/status"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL运行状态获取失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
if (result.data) {
|
||||
$('#mysql-status').html('当前状态:<span class="layui-badge layui-bg-green">运行中</span>');
|
||||
} else {
|
||||
$('#mysql-status').html('当前状态:<span class="layui-badge layui-bg-red">已停止</span>');
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// 获取数据库列表
|
||||
table.render({
|
||||
elem: '#mysql-database-list'
|
||||
, url: '/api/plugins/mysql57/database'
|
||||
, toolbar: '#mysql-database-list-bar'
|
||||
, title: '数据库列表'
|
||||
, cols: [[
|
||||
{field: 'name', title: '库名', fixed: 'left', unresize: true, sort: true}
|
||||
, {fixed: 'right', title: '操作', toolbar: '#mysql-database-list-control', width: 150}
|
||||
]]
|
||||
});
|
||||
// 头工具栏事件
|
||||
table.on('toolbar(mysql-database-list)', function (obj) {
|
||||
if (obj.event === 'add_database') {
|
||||
admin.popup({
|
||||
title: '新建数据库'
|
||||
, area: ['600px', '300px']
|
||||
, id: 'LAY-popup-mysql-database-add'
|
||||
, success: function (layer, index) {
|
||||
view(this.id).render('plugins/mysql57/add_database', {}).done(function () {
|
||||
form.render(null, 'LAY-popup-mysql-database-add');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 行工具事件
|
||||
table.on('tool(mysql-database-list)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('高风险操作,确定要删除数据库 <b style="color: red;">' + data.name + '</b> 吗?', function (index) {
|
||||
index = layer.msg('请稍候...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/deleteDatabase"
|
||||
, type: 'post'
|
||||
, data: {
|
||||
database: data.name
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:数据库删除失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert('数据库' + data.name + '删除成功!');
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
});
|
||||
} else if (obj.event === 'backup') {
|
||||
// 打开备份页面
|
||||
admin.popup({
|
||||
title: '备份管理 - ' + data.name
|
||||
, area: ['70%', '80%']
|
||||
, id: 'LAY-popup-mysql-backup'
|
||||
, success: function (layero, index) {
|
||||
view(this.id).render('plugins/mysql57/backup', {
|
||||
data: data
|
||||
}).done(function () {
|
||||
form.render(null, 'LAY-popup-mysql-backup');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取数据库用户列表
|
||||
table.render({
|
||||
elem: '#mysql-user-list'
|
||||
, url: '/api/plugins/mysql57/user'
|
||||
, toolbar: '#mysql-user-list-bar'
|
||||
, title: '用户列表'
|
||||
, cols: [[
|
||||
{field: 'user', title: '用户名', fixed: 'left', width: 300, sort: true}
|
||||
, {field: 'host', title: '主机', width: 250, sort: true}
|
||||
, {field: 'grants', title: '权限'}
|
||||
, {fixed: 'right', title: '操作', toolbar: '#mysql-user-list-control', width: 150}
|
||||
]]
|
||||
});
|
||||
// 头工具栏事件
|
||||
table.on('toolbar(mysql-user-list)', function (obj) {
|
||||
if (obj.event === 'add_user') {
|
||||
admin.popup({
|
||||
title: '新建用户'
|
||||
, area: ['600px', '300px']
|
||||
, id: 'LAY-popup-mysql-user-add'
|
||||
, success: function (layer, index) {
|
||||
view(this.id).render('plugins/mysql57/add_user', {}).done(function () {
|
||||
form.render(null, 'LAY-popup-mysql-user-add');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 行工具事件
|
||||
table.on('tool(mysql-user-list)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('高风险操作,确定要删除用户 <b style="color: red;">' + data.user + '</b> 吗?', function (index) {
|
||||
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/deleteUser"
|
||||
, type: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:用户删除失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert('用户' + data.user + '删除成功!');
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
});
|
||||
} else if (obj.event === 'change_password') {
|
||||
// 弹出输入密码框
|
||||
layer.prompt({
|
||||
formType: 1
|
||||
, title: '请输入新密码(8位以上大小写数字特殊符号混合)'
|
||||
}, function (value, index) {
|
||||
layer.close(index);
|
||||
index = layer.msg('正在提交...', {icon: 16, time: 0});
|
||||
// 发送请求
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/userPassword"
|
||||
, type: 'post'
|
||||
, data: {
|
||||
user: data.user,
|
||||
password: value
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:密码修改失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
layer.alert('用户' + data.user + '密码修改成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取mysql错误日志并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/errorLog"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL错误日志获取失败,接口返回' + result);
|
||||
$('#mysql-error-log').text('MySQL错误日志获取失败,请刷新重试!');
|
||||
code({
|
||||
elem: '#mysql-error-log'
|
||||
, title: 'error.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
return false;
|
||||
}
|
||||
$('#mysql-error-log').text(result.data);
|
||||
code({
|
||||
elem: '#mysql-error-log'
|
||||
, title: 'error.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取mysql慢查询日志并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/slowLog"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL慢查询日志获取失败,接口返回' + result);
|
||||
$('#mysql-slow-log').text('MySQL慢查询日志获取失败,请刷新重试!');
|
||||
code({
|
||||
elem: '#mysql-slow-log'
|
||||
, title: 'slow.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
return false;
|
||||
}
|
||||
$('#mysql-slow-log').text(result.data);
|
||||
code({
|
||||
elem: '#mysql-slow-log'
|
||||
, title: 'slow.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取mysql配置并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/config"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL主配置获取失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
$('#mysql-config-editor').text(result.data);
|
||||
mysql_config_editor = ace.edit("mysql-config-editor", {
|
||||
mode: "ace/mode/ini",
|
||||
selectionStyle: "text"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取mysql负载状态并渲染
|
||||
table.render({
|
||||
elem: '#mysql-load-status'
|
||||
, url: '/api/plugins/mysql57/load'
|
||||
, cols: [[
|
||||
{field: 'name', width: '80%', title: '属性',}
|
||||
, {field: 'value', width: '20%', title: '当前值'}
|
||||
]]
|
||||
});
|
||||
element.render();
|
||||
|
||||
// 事件监听
|
||||
$('#mysql-start').click(function () {
|
||||
layer.confirm('确定要启动MySQL吗?', {
|
||||
btn: ['启动', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('正在启动MySQL...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/start"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL启动失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('MySQL启动成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#mysql-stop').click(function () {
|
||||
layer.confirm('停止MySQL将导致使用MySQL的网站无法访问,是否继续停止?', {
|
||||
btn: ['停止', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('正在停止MySQL...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/stop"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL停止失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('MySQL停止成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#mysql-restart').click(function () {
|
||||
layer.confirm('重启MySQL将导致使用MySQL的网站短时间无法访问,是否继续重启?', {
|
||||
btn: ['重启', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('正在重启MySQL...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/restart"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL重启失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('MySQL重启成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#mysql-reload').click(function () {
|
||||
index = layer.msg('正在重载MySQL...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/reload"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL重载失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('MySQL重载成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#mysql-config-save').click(function () {
|
||||
index = layer.msg('正在保存配置...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/config"
|
||||
, type: 'post'
|
||||
, data: {
|
||||
config: mysql_config_editor.getValue()
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL配置保存失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
layer.alert('MySQL配置保存成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#mysql-clean-error-log').click(function () {
|
||||
index = layer.msg('正在清空错误日志...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/clearErrorLog"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL错误日志清空失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
layer.msg('MySQL错误日志已清空!');
|
||||
setTimeout(function () {
|
||||
admin.events.refresh();
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#mysql-clean-slow-log').click(function () {
|
||||
index = layer.msg('正在清空慢查询日志...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/clearSlowLog"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL慢查询日志清空失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
layer.msg('MySQL慢查询日志已清空!');
|
||||
setTimeout(function () {
|
||||
admin.events.refresh();
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
80
public/panel/views/plugins/mysql57/add_database.html
Normal file
80
public/panel/views/plugins/mysql57/add_database.html
Normal file
@@ -0,0 +1,80 @@
|
||||
<!--
|
||||
Name: MySQL管理器 - 添加数据库
|
||||
Author: 耗子
|
||||
Date: 2023-07-22
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<form class="layui-form" action="" lay-filter="add-mysql-database-form">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">数据库名</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="database" lay-verify="required" placeholder="请输入数据库名"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">用户名</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="user" lay-verify="required" placeholder="请输入用户名"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="password" lay-verify="required" placeholder="请输入密码(8位以上大小写数字特殊符号混合)"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<div class="layui-footer">
|
||||
<button class="layui-btn" lay-submit="" lay-filter="add-mysql-database-submit">立即提交</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
<script>
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form'], function () {
|
||||
var $ = layui.$
|
||||
, admin = layui.admin
|
||||
, layer = layui.layer
|
||||
, form = layui.form
|
||||
, table = layui.table
|
||||
|
||||
form.render();
|
||||
|
||||
// 提交
|
||||
form.on('submit(add-mysql-database-submit)', function (data) {
|
||||
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/addDatabase"
|
||||
, type: 'post'
|
||||
, data: data.field
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:数据库添加失败,接口返回' + result);
|
||||
layer.msg('数据库添加失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
table.reload('mysql-database-list');
|
||||
table.reload('mysql-user-list');
|
||||
layer.alert('数据库添加成功!', {
|
||||
icon: 1
|
||||
, title: '提示'
|
||||
, btn: ['确定']
|
||||
, yes: function (index) {
|
||||
layer.closeAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
80
public/panel/views/plugins/mysql57/add_user.html
Normal file
80
public/panel/views/plugins/mysql57/add_user.html
Normal file
@@ -0,0 +1,80 @@
|
||||
<!--
|
||||
Name: MySQL管理器 - 添加用户
|
||||
Author: 耗子
|
||||
Date: 2023-07-22
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<form class="layui-form" action="" lay-filter="add-mysql-user-form">
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">用户名</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="user" lay-verify="required" placeholder="请输入用户名"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="password" lay-verify="required" placeholder="请输入密码(8位以上大小写数字特殊符号混合)"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">数据库</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="database" lay-verify="required" placeholder="输入授权给该用户的数据库名"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<div class="layui-footer">
|
||||
<button class="layui-btn" lay-submit="" lay-filter="add-mysql-user-submit">立即提交</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
<script>
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form'], function () {
|
||||
var $ = layui.$
|
||||
, admin = layui.admin
|
||||
, layer = layui.layer
|
||||
, form = layui.form
|
||||
, table = layui.table
|
||||
|
||||
form.render();
|
||||
|
||||
// 提交
|
||||
form.on('submit(add-mysql-user-submit)', function (data) {
|
||||
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/addUser"
|
||||
, type: 'post'
|
||||
, data: data.field
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:用户添加失败,接口返回' + result);
|
||||
layer.msg('用户添加失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
table.reload('mysql-database-list');
|
||||
table.reload('mysql-user-list');
|
||||
layer.alert('用户添加成功!', {
|
||||
icon: 1
|
||||
, title: '提示'
|
||||
, btn: ['确定']
|
||||
, yes: function (index) {
|
||||
layer.closeAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
145
public/panel/views/plugins/mysql57/backup.html
Normal file
145
public/panel/views/plugins/mysql57/backup.html
Normal file
@@ -0,0 +1,145 @@
|
||||
<!--
|
||||
Name: MySQL管理器 - 数据库备份
|
||||
Author: 耗子
|
||||
Date: 2023-07-22
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs12 layui-col-sm12 layui-col-md12">
|
||||
<table class="layui-hide" id="mysql-backup-list" lay-filter="mysql-backup-list"></table>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 备份顶部工具栏 -->
|
||||
<script type="text/html" id="mysql-database-backup-bar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="backup_database">备份数据库</button>
|
||||
<button class="layui-btn layui-btn-sm" id="upload_mysql_backup">上传备份</button>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 备份右侧管理 -->
|
||||
<script type="text/html" id="mysql-database-backup-control">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="restore">恢复</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
<script>
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form', 'laydate', 'code'], function () {
|
||||
var $ = layui.$
|
||||
, admin = layui.admin
|
||||
, layer = layui.layer
|
||||
, table = layui.table
|
||||
, upload = layui.upload;
|
||||
|
||||
// 渲染表格
|
||||
table.render({
|
||||
elem: '#mysql-backup-list'
|
||||
, url: '/api/plugins/mysql57/backup'
|
||||
, toolbar: '#mysql-database-backup-bar'
|
||||
, title: '备份列表'
|
||||
, cols: [[
|
||||
{field: 'file', title: '备份名称', width: 500}
|
||||
, {field: 'size', title: '文件大小'}
|
||||
, {field: 'right', title: '操作', width: 150, toolbar: '#mysql-database-backup-control'}
|
||||
]]
|
||||
, text: {
|
||||
none: '无备份数据'
|
||||
}
|
||||
, done: function (res, curr, count) {
|
||||
upload.render({
|
||||
elem: '#upload_mysql_backup'
|
||||
, url: '/api/plugins/mysql57/uploadBackup'
|
||||
, accept: 'file'
|
||||
, ext: 'sql|zip|rar|tar|gz|bz2'
|
||||
, before: function (obj) {
|
||||
index = layer.msg('正在上传备份文件,可能需要较长时间,请勿操作...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
}
|
||||
, done: function (res) {
|
||||
layer.close(index);
|
||||
layer.msg('上传成功!', {icon: 1});
|
||||
table.reload('mysql-backup-list');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 头工具栏事件
|
||||
table.on('toolbar(mysql-backup-list)', function (obj) {
|
||||
if (obj.event === 'backup_database') {
|
||||
index = layer.msg('正在备份数据库,请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
admin.req({
|
||||
url: '/api/plugins/mysql57/createBackup'
|
||||
, type: 'post'
|
||||
, data: {
|
||||
database: params.data.name
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:备份数据库失败,接口返回' + result);
|
||||
layer.alert('备份失败!');
|
||||
return false;
|
||||
}
|
||||
table.reload('mysql-backup-list');
|
||||
layer.msg('备份成功!', {icon: 1});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 行工具事件
|
||||
table.on('tool(mysql-backup-list)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('确定要删除数据库备份 <b style="color: red;">' + data.file + '</b> 吗?', function (index) {
|
||||
index = layer.msg('正在删除数据库备份,请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/deleteBackup"
|
||||
, type: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:数据库备份删除失败,接口返回' + result);
|
||||
layer.msg('数据库备份删除失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert('数据库备份' + data.file + '删除成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (obj.event === 'restore') {
|
||||
layer.confirm('高风险操作,确定要恢复数据库备份 <b style="color: red;">' + data.file + '</b> 吗?', function (index) {
|
||||
index = layer.msg('正在恢复数据库备份,可能需要较长时间,请勿操作...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
data.database = params.data.name;
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql57/restoreBackup"
|
||||
, type: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:数据库恢复失败,接口返回' + result);
|
||||
layer.msg('数据库备份恢复失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
layer.alert('数据库备份' + data.file + '恢复成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
558
public/panel/views/plugins/mysql80.html
Normal file
558
public/panel/views/plugins/mysql80.html
Normal file
@@ -0,0 +1,558 @@
|
||||
<!--
|
||||
Name: MySQL管理器
|
||||
Author: 耗子
|
||||
Date: 2022-07-22
|
||||
-->
|
||||
<title>MySQL</title>
|
||||
<div class="layui-fluid" id="component-tabs">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md12">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">MySQL管理</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-tab">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">基本信息</li>
|
||||
<li>管理</li>
|
||||
<li>配置修改</li>
|
||||
<li>负载状态</li>
|
||||
<li>错误日志</li>
|
||||
<li>慢查询日志</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
|
||||
<legend>运行状态</legend>
|
||||
</fieldset>
|
||||
<blockquote id="mysql-status" class="layui-elem-quote layui-quote-nm">当前状态:<span
|
||||
class="layui-badge layui-bg-black">获取中</span></blockquote>
|
||||
<div class="layui-btn-container" style="padding-top: 30px;">
|
||||
<button id="mysql-start" class="layui-btn">启动</button>
|
||||
<button id="mysql-stop" class="layui-btn layui-btn-danger">停止</button>
|
||||
<button id="mysql-restart" class="layui-btn layui-btn-warm">重启</button>
|
||||
<button id="mysql-reload" class="layui-btn layui-btn-normal">重载</button>
|
||||
</div>
|
||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
|
||||
<legend>基本设置</legend>
|
||||
</fieldset>
|
||||
<div class="layui-form" lay-filter="mysql_setting">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label" style="font-size: 13px;">root 密码</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="mysql_root_password" value="获取中ing..."
|
||||
class="layui-input" disabled>
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">查看/修改MySQL的root密码</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn layui-btn-sm" lay-submit
|
||||
lay-filter="mysql_setting_submit">确认修改
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<blockquote class="layui-elem-quote">面板仅集成了部分常用功能,如需更多功能,建议安装
|
||||
phpMyAdmin 使用。
|
||||
</blockquote>
|
||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
|
||||
<legend>数据库列表</legend>
|
||||
</fieldset>
|
||||
<table class="layui-hide" id="mysql-database-list"
|
||||
lay-filter="mysql-database-list"></table>
|
||||
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
|
||||
<legend>用户列表</legend>
|
||||
</fieldset>
|
||||
<table class="layui-hide" id="mysql-user-list" lay-filter="mysql-user-list"></table>
|
||||
<!-- 数据库顶部工具栏 -->
|
||||
<script type="text/html" id="mysql-database-list-bar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="add_database">新建数据库
|
||||
</button>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 用户顶部工具栏 -->
|
||||
<script type="text/html" id="mysql-user-list-bar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="add_user">新建用户</button>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 数据库右侧管理 -->
|
||||
<script type="text/html" id="mysql-database-list-control">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="backup">备份</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
<!-- 用户右侧管理 -->
|
||||
<script type="text/html" id="mysql-user-list-control">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs"
|
||||
lay-event="change_password">改密</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<blockquote class="layui-elem-quote">此处修改的是MySQL主配置文件,如果你不了解各参数的含义,请不要随意修改!<br>
|
||||
提示:Ctrl+F 搜索关键字,Ctrl+S 保存,Ctrl+H 查找替换!
|
||||
</blockquote>
|
||||
<div id="mysql-config-editor"
|
||||
style="height: 600px;"></div>
|
||||
<div class="layui-btn-container" style="padding-top: 30px;">
|
||||
<button id="mysql-config-save" class="layui-btn">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<table class="layui-hide" id="mysql-load-status"></table>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<div class="layui-btn-container">
|
||||
<button id="mysql-clean-error-log" class="layui-btn">清空日志</button>
|
||||
</div>
|
||||
<pre id="mysql-error-log" class="layui-code">
|
||||
获取中...
|
||||
</pre>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<div class="layui-btn-container">
|
||||
<button id="mysql-clean-slow-log" class="layui-btn">清空日志</button>
|
||||
</div>
|
||||
<pre id="mysql-slow-log" class="layui-code">
|
||||
获取中...
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let mysql_config_editor;// 定义mysql配置编辑器的全局变量
|
||||
layui.use(['index', 'code', 'table'], function () {
|
||||
let $ = layui.$
|
||||
, admin = layui.admin
|
||||
, element = layui.element
|
||||
, code = layui.code
|
||||
, table = layui.table
|
||||
, form = layui.form
|
||||
, view = layui.view;
|
||||
|
||||
// 渲染表单
|
||||
form.render();
|
||||
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/rootPassword"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:系统信息获取失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
form.val("mysql_setting", {
|
||||
"mysql_root_password": result.data
|
||||
});
|
||||
$('input').attr('disabled', false);
|
||||
}
|
||||
});
|
||||
// 提交修改
|
||||
form.on('submit(mysql_setting_submit)', function (data) {
|
||||
index = layer.msg('请稍候...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/rootPassword"
|
||||
, type: 'post'
|
||||
, data: data.field
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL设置保存失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
layer.msg('修改成功!')
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
// 获取mysql运行状态并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/status"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL运行状态获取失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
if (result.data) {
|
||||
$('#mysql-status').html('当前状态:<span class="layui-badge layui-bg-green">运行中</span>');
|
||||
} else {
|
||||
$('#mysql-status').html('当前状态:<span class="layui-badge layui-bg-red">已停止</span>');
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// 获取数据库列表
|
||||
table.render({
|
||||
elem: '#mysql-database-list'
|
||||
, url: '/api/plugins/mysql80/database'
|
||||
, toolbar: '#mysql-database-list-bar'
|
||||
, title: '数据库列表'
|
||||
, cols: [[
|
||||
{field: 'name', title: '库名', fixed: 'left', unresize: true, sort: true}
|
||||
, {fixed: 'right', title: '操作', toolbar: '#mysql-database-list-control', width: 150}
|
||||
]]
|
||||
});
|
||||
// 头工具栏事件
|
||||
table.on('toolbar(mysql-database-list)', function (obj) {
|
||||
if (obj.event === 'add_database') {
|
||||
admin.popup({
|
||||
title: '新建数据库'
|
||||
, area: ['600px', '300px']
|
||||
, id: 'LAY-popup-mysql-database-add'
|
||||
, success: function (layer, index) {
|
||||
view(this.id).render('plugins/mysql80/add_database', {}).done(function () {
|
||||
form.render(null, 'LAY-popup-mysql-database-add');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 行工具事件
|
||||
table.on('tool(mysql-database-list)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('高风险操作,确定要删除数据库 <b style="color: red;">' + data.name + '</b> 吗?', function (index) {
|
||||
index = layer.msg('请稍候...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/deleteDatabase"
|
||||
, type: 'post'
|
||||
, data: {
|
||||
database: data.name
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:数据库删除失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert('数据库' + data.name + '删除成功!');
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
});
|
||||
} else if (obj.event === 'backup') {
|
||||
// 打开备份页面
|
||||
admin.popup({
|
||||
title: '备份管理 - ' + data.name
|
||||
, area: ['70%', '80%']
|
||||
, id: 'LAY-popup-mysql-backup'
|
||||
, success: function (layero, index) {
|
||||
view(this.id).render('plugins/mysql80/backup', {
|
||||
data: data
|
||||
}).done(function () {
|
||||
form.render(null, 'LAY-popup-mysql-backup');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取数据库用户列表
|
||||
table.render({
|
||||
elem: '#mysql-user-list'
|
||||
, url: '/api/plugins/mysql80/user'
|
||||
, toolbar: '#mysql-user-list-bar'
|
||||
, title: '用户列表'
|
||||
, cols: [[
|
||||
{field: 'user', title: '用户名', fixed: 'left', width: 300, sort: true}
|
||||
, {field: 'host', title: '主机', width: 250, sort: true}
|
||||
, {field: 'grants', title: '权限'}
|
||||
, {fixed: 'right', title: '操作', toolbar: '#mysql-user-list-control', width: 150}
|
||||
]]
|
||||
});
|
||||
// 头工具栏事件
|
||||
table.on('toolbar(mysql-user-list)', function (obj) {
|
||||
if (obj.event === 'add_user') {
|
||||
admin.popup({
|
||||
title: '新建用户'
|
||||
, area: ['600px', '300px']
|
||||
, id: 'LAY-popup-mysql-user-add'
|
||||
, success: function (layer, index) {
|
||||
view(this.id).render('plugins/mysql80/add_user', {}).done(function () {
|
||||
form.render(null, 'LAY-popup-mysql-user-add');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 行工具事件
|
||||
table.on('tool(mysql-user-list)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('高风险操作,确定要删除用户 <b style="color: red;">' + data.user + '</b> 吗?', function (index) {
|
||||
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/deleteUser"
|
||||
, type: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:用户删除失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert('用户' + data.user + '删除成功!');
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
});
|
||||
} else if (obj.event === 'change_password') {
|
||||
// 弹出输入密码框
|
||||
layer.prompt({
|
||||
formType: 1
|
||||
, title: '请输入新密码(8位以上大小写数字特殊符号混合)'
|
||||
}, function (value, index) {
|
||||
layer.close(index);
|
||||
index = layer.msg('正在提交...', {icon: 16, time: 0});
|
||||
// 发送请求
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/userPassword"
|
||||
, type: 'post'
|
||||
, data: {
|
||||
user: data.user,
|
||||
password: value
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:密码修改失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
layer.alert('用户' + data.user + '密码修改成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取mysql错误日志并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/errorLog"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL错误日志获取失败,接口返回' + result);
|
||||
$('#mysql-error-log').text('MySQL错误日志获取失败,请刷新重试!');
|
||||
code({
|
||||
elem: '#mysql-error-log'
|
||||
, title: 'error.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
return false;
|
||||
}
|
||||
$('#mysql-error-log').text(result.data);
|
||||
code({
|
||||
elem: '#mysql-error-log'
|
||||
, title: 'error.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取mysql慢查询日志并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/slowLog"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL慢查询日志获取失败,接口返回' + result);
|
||||
$('#mysql-slow-log').text('MySQL慢查询日志获取失败,请刷新重试!');
|
||||
code({
|
||||
elem: '#mysql-slow-log'
|
||||
, title: 'slow.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
return false;
|
||||
}
|
||||
$('#mysql-slow-log').text(result.data);
|
||||
code({
|
||||
elem: '#mysql-slow-log'
|
||||
, title: 'slow.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取mysql配置并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/config"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL主配置获取失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
$('#mysql-config-editor').text(result.data);
|
||||
mysql_config_editor = ace.edit("mysql-config-editor", {
|
||||
mode: "ace/mode/ini",
|
||||
selectionStyle: "text"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取mysql负载状态并渲染
|
||||
table.render({
|
||||
elem: '#mysql-load-status'
|
||||
, url: '/api/plugins/mysql80/load'
|
||||
, cols: [[
|
||||
{field: 'name', width: '80%', title: '属性',}
|
||||
, {field: 'value', width: '20%', title: '当前值'}
|
||||
]]
|
||||
});
|
||||
element.render();
|
||||
|
||||
// 事件监听
|
||||
$('#mysql-start').click(function () {
|
||||
layer.confirm('确定要启动MySQL吗?', {
|
||||
btn: ['启动', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('正在启动MySQL...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/start"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL启动失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('MySQL启动成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#mysql-stop').click(function () {
|
||||
layer.confirm('停止MySQL将导致使用MySQL的网站无法访问,是否继续停止?', {
|
||||
btn: ['停止', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('正在停止MySQL...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/stop"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL停止失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('MySQL停止成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#mysql-restart').click(function () {
|
||||
layer.confirm('重启MySQL将导致使用MySQL的网站短时间无法访问,是否继续重启?', {
|
||||
btn: ['重启', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('正在重启MySQL...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/restart"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL重启失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('MySQL重启成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#mysql-reload').click(function () {
|
||||
index = layer.msg('正在重载MySQL...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/reload"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL重载失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('MySQL重载成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#mysql-config-save').click(function () {
|
||||
index = layer.msg('正在保存配置...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/config"
|
||||
, type: 'post'
|
||||
, data: {
|
||||
config: mysql_config_editor.getValue()
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL配置保存失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
layer.alert('MySQL配置保存成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#mysql-clean-error-log').click(function () {
|
||||
index = layer.msg('正在清空错误日志...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/clearErrorLog"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL错误日志清空失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
layer.msg('MySQL错误日志已清空!');
|
||||
setTimeout(function () {
|
||||
admin.events.refresh();
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#mysql-clean-slow-log').click(function () {
|
||||
index = layer.msg('正在清空慢查询日志...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/clearSlowLog"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:MySQL慢查询日志清空失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
layer.msg('MySQL慢查询日志已清空!');
|
||||
setTimeout(function () {
|
||||
admin.events.refresh();
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
80
public/panel/views/plugins/mysql80/add_database.html
Normal file
80
public/panel/views/plugins/mysql80/add_database.html
Normal file
@@ -0,0 +1,80 @@
|
||||
<!--
|
||||
Name: MySQL管理器 - 添加数据库
|
||||
Author: 耗子
|
||||
Date: 2023-07-22
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<form class="layui-form" action="" lay-filter="add-mysql-database-form">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">数据库名</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="database" lay-verify="required" placeholder="请输入数据库名"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">用户名</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="user" lay-verify="required" placeholder="请输入用户名"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="password" lay-verify="required" placeholder="请输入密码(8位以上大小写数字特殊符号混合)"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<div class="layui-footer">
|
||||
<button class="layui-btn" lay-submit="" lay-filter="add-mysql-database-submit">立即提交</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
<script>
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form'], function () {
|
||||
var $ = layui.$
|
||||
, admin = layui.admin
|
||||
, layer = layui.layer
|
||||
, form = layui.form
|
||||
, table = layui.table
|
||||
|
||||
form.render();
|
||||
|
||||
// 提交
|
||||
form.on('submit(add-mysql-database-submit)', function (data) {
|
||||
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/addDatabase"
|
||||
, type: 'post'
|
||||
, data: data.field
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:数据库添加失败,接口返回' + result);
|
||||
layer.msg('数据库添加失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
table.reload('mysql-database-list');
|
||||
table.reload('mysql-user-list');
|
||||
layer.alert('数据库添加成功!', {
|
||||
icon: 1
|
||||
, title: '提示'
|
||||
, btn: ['确定']
|
||||
, yes: function (index) {
|
||||
layer.closeAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
80
public/panel/views/plugins/mysql80/add_user.html
Normal file
80
public/panel/views/plugins/mysql80/add_user.html
Normal file
@@ -0,0 +1,80 @@
|
||||
<!--
|
||||
Name: MySQL管理器 - 添加用户
|
||||
Author: 耗子
|
||||
Date: 2023-07-22
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<form class="layui-form" action="" lay-filter="add-mysql-user-form">
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">用户名</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="user" lay-verify="required" placeholder="请输入用户名"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">密码</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="password" lay-verify="required" placeholder="请输入密码(8位以上大小写数字特殊符号混合)"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">数据库</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="database" lay-verify="required" placeholder="输入授权给该用户的数据库名"
|
||||
autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<div class="layui-footer">
|
||||
<button class="layui-btn" lay-submit="" lay-filter="add-mysql-user-submit">立即提交</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
<script>
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form'], function () {
|
||||
var $ = layui.$
|
||||
, admin = layui.admin
|
||||
, layer = layui.layer
|
||||
, form = layui.form
|
||||
, table = layui.table
|
||||
|
||||
form.render();
|
||||
|
||||
// 提交
|
||||
form.on('submit(add-mysql-user-submit)', function (data) {
|
||||
index = layer.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/addUser"
|
||||
, type: 'post'
|
||||
, data: data.field
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:用户添加失败,接口返回' + result);
|
||||
layer.msg('用户添加失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
table.reload('mysql-database-list');
|
||||
table.reload('mysql-user-list');
|
||||
layer.alert('用户添加成功!', {
|
||||
icon: 1
|
||||
, title: '提示'
|
||||
, btn: ['确定']
|
||||
, yes: function (index) {
|
||||
layer.closeAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
145
public/panel/views/plugins/mysql80/backup.html
Normal file
145
public/panel/views/plugins/mysql80/backup.html
Normal file
@@ -0,0 +1,145 @@
|
||||
<!--
|
||||
Name: MySQL管理器 - 数据库备份
|
||||
Author: 耗子
|
||||
Date: 2023-07-22
|
||||
-->
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs12 layui-col-sm12 layui-col-md12">
|
||||
<table class="layui-hide" id="mysql-backup-list" lay-filter="mysql-backup-list"></table>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 备份顶部工具栏 -->
|
||||
<script type="text/html" id="mysql-database-backup-bar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="backup_database">备份数据库</button>
|
||||
<button class="layui-btn layui-btn-sm" id="upload_mysql_backup">上传备份</button>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 备份右侧管理 -->
|
||||
<script type="text/html" id="mysql-database-backup-control">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="restore">恢复</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
<script>
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form', 'laydate', 'code'], function () {
|
||||
var $ = layui.$
|
||||
, admin = layui.admin
|
||||
, layer = layui.layer
|
||||
, table = layui.table
|
||||
, upload = layui.upload;
|
||||
|
||||
// 渲染表格
|
||||
table.render({
|
||||
elem: '#mysql-backup-list'
|
||||
, url: '/api/plugins/mysql80/backup'
|
||||
, toolbar: '#mysql-database-backup-bar'
|
||||
, title: '备份列表'
|
||||
, cols: [[
|
||||
{field: 'file', title: '备份名称', width: 500}
|
||||
, {field: 'size', title: '文件大小'}
|
||||
, {field: 'right', title: '操作', width: 150, toolbar: '#mysql-database-backup-control'}
|
||||
]]
|
||||
, text: {
|
||||
none: '无备份数据'
|
||||
}
|
||||
, done: function (res, curr, count) {
|
||||
upload.render({
|
||||
elem: '#upload_mysql_backup'
|
||||
, url: '/api/plugins/mysql80/uploadBackup'
|
||||
, accept: 'file'
|
||||
, ext: 'sql|zip|rar|tar|gz|bz2'
|
||||
, before: function (obj) {
|
||||
index = layer.msg('正在上传备份文件,可能需要较长时间,请勿操作...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
}
|
||||
, done: function (res) {
|
||||
layer.close(index);
|
||||
layer.msg('上传成功!', {icon: 1});
|
||||
table.reload('mysql-backup-list');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 头工具栏事件
|
||||
table.on('toolbar(mysql-backup-list)', function (obj) {
|
||||
if (obj.event === 'backup_database') {
|
||||
index = layer.msg('正在备份数据库,请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
admin.req({
|
||||
url: '/api/plugins/mysql80/createBackup'
|
||||
, type: 'post'
|
||||
, data: {
|
||||
database: params.data.name
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:备份数据库失败,接口返回' + result);
|
||||
layer.alert('备份失败!');
|
||||
return false;
|
||||
}
|
||||
table.reload('mysql-backup-list');
|
||||
layer.msg('备份成功!', {icon: 1});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 行工具事件
|
||||
table.on('tool(mysql-backup-list)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('确定要删除数据库备份 <b style="color: red;">' + data.file + '</b> 吗?', function (index) {
|
||||
index = layer.msg('正在删除数据库备份,请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/deleteBackup"
|
||||
, type: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:数据库备份删除失败,接口返回' + result);
|
||||
layer.msg('数据库备份删除失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert('数据库备份' + data.file + '删除成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (obj.event === 'restore') {
|
||||
layer.confirm('高风险操作,确定要恢复数据库备份 <b style="color: red;">' + data.file + '</b> 吗?', function (index) {
|
||||
index = layer.msg('正在恢复数据库备份,可能需要较长时间,请勿操作...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
data.database = params.data.name;
|
||||
admin.req({
|
||||
url: "/api/plugins/mysql80/restoreBackup"
|
||||
, type: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:数据库恢复失败,接口返回' + result);
|
||||
layer.msg('数据库备份恢复失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
layer.alert('数据库备份' + data.file + '恢复成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
381
public/panel/views/plugins/php74.html
Normal file
381
public/panel/views/plugins/php74.html
Normal file
@@ -0,0 +1,381 @@
|
||||
<!--
|
||||
Name: PHP管理器
|
||||
Author: 耗子
|
||||
Date: 2023-07-22
|
||||
-->
|
||||
<title>PHP-7.4</title>
|
||||
<div class="layui-fluid" id="component-tabs">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md12">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">PHP-7.4管理</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-tab">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">运行状态</li>
|
||||
<li>拓展管理</li>
|
||||
<li>配置修改</li>
|
||||
<li>负载状态</li>
|
||||
<li>运行日志</li>
|
||||
<li>慢日志</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<blockquote id="php74-status" class="layui-elem-quote layui-quote-nm">当前状态:<span
|
||||
class="layui-badge layui-bg-black">获取中</span></blockquote>
|
||||
<div class="layui-btn-container" style="padding-top: 30px;">
|
||||
<button id="php74-start" class="layui-btn">启动</button>
|
||||
<button id="php74-stop" class="layui-btn layui-btn-danger">停止</button>
|
||||
<button id="php74-restart" class="layui-btn layui-btn-warm">重启</button>
|
||||
<button id="php74-reload" class="layui-btn layui-btn-normal">重载</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<table id="php74-extension" lay-filter="php74-extension"></table>
|
||||
<!-- 操作按钮模板 -->
|
||||
<script type="text/html" id="php74-extension-control">
|
||||
{{# if(d.installed == true){ }}
|
||||
<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="uninstall">卸载</a>
|
||||
{{# } else{ }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="install">安装</a>
|
||||
{{# } }}
|
||||
</script>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<blockquote class="layui-elem-quote">此处修改的是PHP主配置文件,如果你不了解各参数的含义,请不要随意修改!<br>
|
||||
提示:Ctrl+F 搜索关键字,Ctrl+S 保存,Ctrl+H 查找替换!
|
||||
</blockquote>
|
||||
<div id="php74-config-editor"
|
||||
style="height: 600px;"></div>
|
||||
<div class="layui-btn-container" style="padding-top: 30px;">
|
||||
<button id="php74-config-save" class="layui-btn">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<table class="layui-hide" id="php74-load-status"></table>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<div class="layui-btn-container">
|
||||
<button id="php74-clean-error-log" class="layui-btn">清空日志</button>
|
||||
</div>
|
||||
<pre id="php74-error-log" class="layui-code">
|
||||
获取中...
|
||||
</pre>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<div class="layui-btn-container">
|
||||
<button id="php74-clean-slow-log" class="layui-btn">清空日志</button>
|
||||
</div>
|
||||
<pre id="php74-slow-log" class="layui-code">
|
||||
获取中...
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let php74_config_editor;
|
||||
layui.use(['index', 'code', 'table'], function () {
|
||||
let $ = layui.$
|
||||
, admin = layui.admin
|
||||
, element = layui.element
|
||||
, code = layui.code
|
||||
, table = layui.table;
|
||||
|
||||
// 获取php74运行状态并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/php74/status"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:PHP运行状态获取失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
if (result.data) {
|
||||
$('#php74-status').html('当前状态:<span class="layui-badge layui-bg-green">运行中</span>');
|
||||
} else {
|
||||
$('#php74-status').html('当前状态:<span class="layui-badge layui-bg-red">已停止</span>');
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// 获取php74错误日志并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/php74/errorLog"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
$('#php74-error-log').text('PHP日志获取失败,请刷新重试!');
|
||||
code({
|
||||
elem: '#php74-error-log'
|
||||
, title: 'php-fpm.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
return false;
|
||||
}
|
||||
$('#php74-error-log').text(result.data);
|
||||
code({
|
||||
elem: '#php74-error-log'
|
||||
, title: 'php-fpm.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取php74慢日志并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/php74/slowLog"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
$('#php74-slow-log').text('PHP慢日志获取失败,请刷新重试!');
|
||||
code({
|
||||
elem: '#php74-slow-log'
|
||||
, title: 'slow.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
return false;
|
||||
}
|
||||
$('#php74-slow-log').text(result.data);
|
||||
code({
|
||||
elem: '#php74-slow-log'
|
||||
, title: 'slow.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取php74配置并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/php74/config"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:PHP主配置获取失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
$('#php74-config-editor').text(result.data);
|
||||
php74_config_editor = ace.edit("php74-config-editor", {
|
||||
mode: "ace/mode/ini",
|
||||
selectionStyle: "text"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取php74负载状态并渲染
|
||||
table.render({
|
||||
elem: '#php74-load-status'
|
||||
, url: '/api/plugins/php74/load'
|
||||
, cols: [[
|
||||
{field: 'name', width: '80%', title: '属性',}
|
||||
, {field: 'value', width: '20%', title: '当前值'}
|
||||
]]
|
||||
});
|
||||
element.render();
|
||||
|
||||
// 获取php74扩展并渲染
|
||||
table.render({
|
||||
elem: '#php74-extension'
|
||||
, url: '/api/plugins/php74/extensions'
|
||||
, cols: [[
|
||||
{field: 'slug', hide: true, title: 'Slug', sort: true}
|
||||
, {field: 'name', width: '20%', title: '拓展名'}
|
||||
, {field: 'description', width: '70%', title: '描述'}
|
||||
, {
|
||||
field: 'control',
|
||||
title: '操作',
|
||||
templet: '#php74-extension-control',
|
||||
fixed: 'right',
|
||||
align: 'left'
|
||||
}
|
||||
]]
|
||||
, page: false
|
||||
, text: {
|
||||
none: '暂无拓展'
|
||||
}
|
||||
});
|
||||
// 工具条
|
||||
table.on('tool(php74-extension)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'install') {
|
||||
layer.confirm('确定安装该拓展吗?', function (index) {
|
||||
layer.close(index);
|
||||
index = layer.msg('请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: '/api/plugins/php74/installExtension',
|
||||
type: 'POST',
|
||||
data: {
|
||||
slug: data.slug
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
table.reload('php74-extension');
|
||||
layer.msg('安装:' + data.name + ' 成功加入任务队列', {
|
||||
icon: 1,
|
||||
time: 1000
|
||||
});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2, time: 1000});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (obj.event === 'uninstall') {
|
||||
layer.confirm('确定卸载该拓展吗?', function (index) {
|
||||
layer.close(index);
|
||||
index = layer.msg('请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: '/api/plugins/php74/uninstallExtension',
|
||||
type: 'POST',
|
||||
data: {
|
||||
slug: data.slug
|
||||
}
|
||||
, success: function (res) {
|
||||
layer.close(index);
|
||||
if (res.code === 0) {
|
||||
table.reload('php74-extension');
|
||||
layer.msg('卸载:' + data.name + ' 成功加入任务队列', {icon: 1, time: 1000});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2, time: 1000});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 事件监听
|
||||
$('#php74-start').click(function () {
|
||||
index = layer.msg('正在启动PHP,请稍后...', {icon: 16, time: 0});
|
||||
layer.confirm('确定要启动PHP吗?', {
|
||||
btn: ['启动', '取消']
|
||||
}, function () {
|
||||
admin.req({
|
||||
url: "/api/plugins/php74/start"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('PHP启动成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#php74-stop').click(function () {
|
||||
layer.confirm('停止PHP将导致使用PHP的网站无法访问,是否继续停止?', {
|
||||
btn: ['停止', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('正在停止PHP,请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/php74/stop"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('PHP停止成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#php74-restart').click(function () {
|
||||
layer.confirm('重启PHP将导致使用PHP的网站短时间无法访问,是否继续重启?', {
|
||||
btn: ['重启', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('正在重启PHP,请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/php74/restart"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('PHP重启成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#php74-reload').click(function () {
|
||||
index = layer.msg('正在重载PHP,请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/php74/reload"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
layer.alert('PHP重载成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#php74-config-save').click(function () {
|
||||
index = layer.msg('正在保存配置,请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/php74/config"
|
||||
, type: 'post'
|
||||
, data: {
|
||||
config: php74_config_editor.getValue()
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
layer.alert('PHP配置保存成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#php74-clean-error-log').click(function () {
|
||||
index = layer.msg('正在清空错误日志,请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/php74/clearErrorLog"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.msg('PHP日志已清空!');
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#php74-clean-slow-log').click(function () {
|
||||
index = layer.msg('正在清空慢日志,请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/php74/clearSlowLog"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.msg('PHP慢日志已清空!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
381
public/panel/views/plugins/php80.html
Normal file
381
public/panel/views/plugins/php80.html
Normal file
@@ -0,0 +1,381 @@
|
||||
<!--
|
||||
Name: PHP管理器
|
||||
Author: 耗子
|
||||
Date: 2023-07-22
|
||||
-->
|
||||
<title>PHP-8.0</title>
|
||||
<div class="layui-fluid" id="component-tabs">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md12">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">PHP-8.0管理</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-tab">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">运行状态</li>
|
||||
<li>拓展管理</li>
|
||||
<li>配置修改</li>
|
||||
<li>负载状态</li>
|
||||
<li>运行日志</li>
|
||||
<li>慢日志</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<blockquote id="php80-status" class="layui-elem-quote layui-quote-nm">当前状态:<span
|
||||
class="layui-badge layui-bg-black">获取中</span></blockquote>
|
||||
<div class="layui-btn-container" style="padding-top: 30px;">
|
||||
<button id="php80-start" class="layui-btn">启动</button>
|
||||
<button id="php80-stop" class="layui-btn layui-btn-danger">停止</button>
|
||||
<button id="php80-restart" class="layui-btn layui-btn-warm">重启</button>
|
||||
<button id="php80-reload" class="layui-btn layui-btn-normal">重载</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<table id="php80-extension" lay-filter="php80-extension"></table>
|
||||
<!-- 操作按钮模板 -->
|
||||
<script type="text/html" id="php80-extension-control">
|
||||
{{# if(d.installed == true){ }}
|
||||
<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="uninstall">卸载</a>
|
||||
{{# } else{ }}
|
||||
<a class="layui-btn layui-btn-xs" lay-event="install">安装</a>
|
||||
{{# } }}
|
||||
</script>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<blockquote class="layui-elem-quote">此处修改的是PHP主配置文件,如果你不了解各参数的含义,请不要随意修改!<br>
|
||||
提示:Ctrl+F 搜索关键字,Ctrl+S 保存,Ctrl+H 查找替换!
|
||||
</blockquote>
|
||||
<div id="php80-config-editor"
|
||||
style="height: 600px;"></div>
|
||||
<div class="layui-btn-container" style="padding-top: 30px;">
|
||||
<button id="php80-config-save" class="layui-btn">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<table class="layui-hide" id="php80-load-status"></table>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<div class="layui-btn-container">
|
||||
<button id="php80-clean-error-log" class="layui-btn">清空日志</button>
|
||||
</div>
|
||||
<pre id="php80-error-log" class="layui-code">
|
||||
获取中...
|
||||
</pre>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<div class="layui-btn-container">
|
||||
<button id="php80-clean-slow-log" class="layui-btn">清空日志</button>
|
||||
</div>
|
||||
<pre id="php80-slow-log" class="layui-code">
|
||||
获取中...
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let php80_config_editor;
|
||||
layui.use(['index', 'code', 'table'], function () {
|
||||
let $ = layui.$
|
||||
, admin = layui.admin
|
||||
, element = layui.element
|
||||
, code = layui.code
|
||||
, table = layui.table;
|
||||
|
||||
// 获取php80运行状态并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/php80/status"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:PHP运行状态获取失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
if (result.data) {
|
||||
$('#php80-status').html('当前状态:<span class="layui-badge layui-bg-green">运行中</span>');
|
||||
} else {
|
||||
$('#php80-status').html('当前状态:<span class="layui-badge layui-bg-red">已停止</span>');
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// 获取php80错误日志并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/php80/errorLog"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
$('#php80-error-log').text('PHP日志获取失败,请刷新重试!');
|
||||
code({
|
||||
elem: '#php80-error-log'
|
||||
, title: 'php-fpm.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
return false;
|
||||
}
|
||||
$('#php80-error-log').text(result.data);
|
||||
code({
|
||||
elem: '#php80-error-log'
|
||||
, title: 'php-fpm.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取php80慢日志并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/php80/slowLog"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
$('#php80-slow-log').text('PHP慢日志获取失败,请刷新重试!');
|
||||
code({
|
||||
elem: '#php80-slow-log'
|
||||
, title: 'slow.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
return false;
|
||||
}
|
||||
$('#php80-slow-log').text(result.data);
|
||||
code({
|
||||
elem: '#php80-slow-log'
|
||||
, title: 'slow.log'
|
||||
, encode: true
|
||||
, about: false
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取php80配置并渲染
|
||||
admin.req({
|
||||
url: "/api/plugins/php80/config"
|
||||
, type: 'get'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:PHP主配置获取失败,接口返回' + result);
|
||||
return false;
|
||||
}
|
||||
$('#php80-config-editor').text(result.data);
|
||||
php80_config_editor = ace.edit("php80-config-editor", {
|
||||
mode: "ace/mode/ini",
|
||||
selectionStyle: "text"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 获取php80负载状态并渲染
|
||||
table.render({
|
||||
elem: '#php80-load-status'
|
||||
, url: '/api/plugins/php80/load'
|
||||
, cols: [[
|
||||
{field: 'name', width: '80%', title: '属性',}
|
||||
, {field: 'value', width: '20%', title: '当前值'}
|
||||
]]
|
||||
});
|
||||
element.render();
|
||||
|
||||
// 获取php80扩展并渲染
|
||||
table.render({
|
||||
elem: '#php80-extension'
|
||||
, url: '/api/plugins/php80/extensions'
|
||||
, cols: [[
|
||||
{field: 'slug', hide: true, title: 'Slug', sort: true}
|
||||
, {field: 'name', width: '20%', title: '拓展名'}
|
||||
, {field: 'description', width: '70%', title: '描述'}
|
||||
, {
|
||||
field: 'control',
|
||||
title: '操作',
|
||||
templet: '#php80-extension-control',
|
||||
fixed: 'right',
|
||||
align: 'left'
|
||||
}
|
||||
]]
|
||||
, page: false
|
||||
, text: {
|
||||
none: '暂无拓展'
|
||||
}
|
||||
});
|
||||
// 工具条
|
||||
table.on('tool(php80-extension)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'install') {
|
||||
layer.confirm('确定安装该拓展吗?', function (index) {
|
||||
layer.close(index);
|
||||
index = layer.msg('请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: '/api/plugins/php80/installExtension',
|
||||
type: 'POST',
|
||||
data: {
|
||||
slug: data.slug
|
||||
}
|
||||
, success: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
table.reload('php80-extension');
|
||||
layer.msg('安装:' + data.name + ' 成功加入任务队列', {
|
||||
icon: 1,
|
||||
time: 1000
|
||||
});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2, time: 1000});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (obj.event === 'uninstall') {
|
||||
layer.confirm('确定卸载该拓展吗?', function (index) {
|
||||
layer.close(index);
|
||||
index = layer.msg('请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: '/api/plugins/php80/uninstallExtension',
|
||||
type: 'POST',
|
||||
data: {
|
||||
slug: data.slug
|
||||
}
|
||||
, success: function (res) {
|
||||
layer.close(index);
|
||||
if (res.code === 0) {
|
||||
table.reload('php80-extension');
|
||||
layer.msg('卸载:' + data.name + ' 成功加入任务队列', {icon: 1, time: 1000});
|
||||
} else {
|
||||
layer.msg(res.msg, {icon: 2, time: 1000});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 事件监听
|
||||
$('#php80-start').click(function () {
|
||||
index = layer.msg('正在启动PHP,请稍后...', {icon: 16, time: 0});
|
||||
layer.confirm('确定要启动PHP吗?', {
|
||||
btn: ['启动', '取消']
|
||||
}, function () {
|
||||
admin.req({
|
||||
url: "/api/plugins/php80/start"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('PHP启动成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#php80-stop').click(function () {
|
||||
layer.confirm('停止PHP将导致使用PHP的网站无法访问,是否继续停止?', {
|
||||
btn: ['停止', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('正在停止PHP,请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/php80/stop"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('PHP停止成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#php80-restart').click(function () {
|
||||
layer.confirm('重启PHP将导致使用PHP的网站短时间无法访问,是否继续重启?', {
|
||||
btn: ['重启', '取消']
|
||||
}, function () {
|
||||
index = layer.msg('正在重启PHP,请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/php80/restart"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.alert('PHP重启成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#php80-reload').click(function () {
|
||||
index = layer.msg('正在重载PHP,请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/php80/reload"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
layer.alert('PHP重载成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#php80-config-save').click(function () {
|
||||
index = layer.msg('正在保存配置,请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/php80/config"
|
||||
, type: 'post'
|
||||
, data: {
|
||||
config: php80_config_editor.getValue()
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
layer.alert('PHP配置保存成功!');
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#php80-clean-error-log').click(function () {
|
||||
index = layer.msg('正在清空错误日志,请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/php80/clearErrorLog"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.msg('PHP日志已清空!');
|
||||
}
|
||||
});
|
||||
});
|
||||
$('#php80-clean-slow-log').click(function () {
|
||||
index = layer.msg('正在清空慢日志,请稍后...', {icon: 16, time: 0});
|
||||
admin.req({
|
||||
url: "/api/plugins/php80/clearSlowLog"
|
||||
, type: 'post'
|
||||
, success: function (result) {
|
||||
if (result.code !== 0) {
|
||||
return false;
|
||||
}
|
||||
admin.events.refresh();
|
||||
layer.msg('PHP慢日志已清空!');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
158
public/panel/views/website/backup.html
Normal file
158
public/panel/views/website/backup.html
Normal file
@@ -0,0 +1,158 @@
|
||||
<!--
|
||||
Name: 网站 - 备份
|
||||
Author: 耗子
|
||||
Date: 2023-07-21
|
||||
-->
|
||||
<h1>这里正在装修,下个版本再来看看吧!</h1>
|
||||
<script type="text/html" template lay-done="layui.data.sendParams(d.params)">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs12 layui-col-sm12 layui-col-md12">
|
||||
<table class="layui-hide" id="website-backup-list" lay-filter="website-backup-list"></table>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 备份顶部工具栏 -->
|
||||
<script type="text/html" id="website-backup-bar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="backup_website">备份网站</button>
|
||||
<button class="layui-btn layui-btn-sm" id="upload_website_backup">上传备份</button>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 备份右侧管理 -->
|
||||
<script type="text/html" id="website-backup-control">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="restore">恢复</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
<script>
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form', 'laydate', 'code'], function () {
|
||||
var $ = layui.$
|
||||
, admin = layui.admin
|
||||
, layer = layui.layer
|
||||
, table = layui.table
|
||||
, upload = layui.upload;
|
||||
|
||||
// 渲染表格
|
||||
table.render({
|
||||
elem: '#website-backup-list'
|
||||
, url: '/api/panel/website/backupList'
|
||||
, toolbar: '#website-backup-bar'
|
||||
, title: '备份列表'
|
||||
, cols: [[
|
||||
{field: 'backup', title: '备份名称', width: 500}
|
||||
, {field: 'size', title: '文件大小'}
|
||||
, {field: 'right', title: '操作', width: 150, toolbar: '#website-backup-control'}
|
||||
]]
|
||||
, text: {
|
||||
none: '无备份数据'
|
||||
}
|
||||
, done: function (res, curr, count) {
|
||||
upload.render({
|
||||
elem: '#upload_website_backup'
|
||||
, url: '/api/panel/website/uploadBackup'
|
||||
, accept: 'file'
|
||||
, exts: 'zip'
|
||||
, before: function (obj) {
|
||||
index = layer.msg('正在上传备份文件,可能需要较长时间,请勿操作...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
}
|
||||
, done: function (res) {
|
||||
layer.close(index);
|
||||
layer.msg('上传成功!', {icon: 1});
|
||||
table.reload('website-backup-list');
|
||||
}
|
||||
, error: function (res) {
|
||||
layer.msg('上传失败:' + res.msg, {icon: 2});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 头工具栏事件
|
||||
table.on('toolbar(website-backup-list)', function (obj) {
|
||||
if (obj.event === 'backup_website') {
|
||||
index = layer.msg('正在备份网站,请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
admin.req({
|
||||
url: '/api/panel/website/createBackup'
|
||||
, type: 'post'
|
||||
, data: {
|
||||
name: params.data.name
|
||||
}
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:网站失败,接口返回' + result);
|
||||
layer.alert('备份失败!');
|
||||
return false;
|
||||
}
|
||||
table.reload('website-backup-list');
|
||||
layer.msg('备份成功!', {icon: 1});
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// 行工具事件
|
||||
table.on('tool(website-backup-list)', function (obj) {
|
||||
let data = obj.data;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('确定要删除网站备份 <b style="color: red;">' + data.backup + '</b> 吗?', function (index) {
|
||||
index = layer.msg('正在删除网站备份,请稍等...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
admin.req({
|
||||
url: "/api/panel/website/deleteBackup"
|
||||
, method: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:网站备份删除失败,接口返回' + result);
|
||||
layer.msg('网站备份删除失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
obj.del();
|
||||
layer.alert('网站备份' + data.backup + '删除成功!');
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (obj.event === 'restore') {
|
||||
layer.confirm('高风险操作,确定要恢复网站备份 <b style="color: red;">' + data.backup + '</b> 吗?', function (index) {
|
||||
index = layer.msg('正在恢复网站备份,可能需要较长时间,请勿操作...', {
|
||||
icon: 16
|
||||
, time: 0
|
||||
});
|
||||
data.name = params.data.name;
|
||||
admin.req({
|
||||
url: "/api/panel/website/restoreBackup"
|
||||
, method: 'post'
|
||||
, data: data
|
||||
, success: function (result) {
|
||||
layer.close(index);
|
||||
if (result.code !== 0) {
|
||||
console.log('耗子Linux面板:网站备份恢复失败,接口返回' + result);
|
||||
layer.msg('网站备份恢复失败,请刷新重试!')
|
||||
return false;
|
||||
}
|
||||
layer.alert('网站备份' + data.backup + '恢复成功!');
|
||||
}
|
||||
, error: function (xhr, status, error) {
|
||||
console.log('耗子Linux面板:ajax请求出错,错误' + error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
76
public/panel/views/website/default_config.html
Normal file
76
public/panel/views/website/default_config.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<!--
|
||||
Name: 网站 - 全局设置
|
||||
Author: 耗子
|
||||
Date: 2023-07-21
|
||||
-->
|
||||
<script type="text/html" template lay-url="/api/panel/website/defaultConfig"
|
||||
lay-done="layui.data.sendParams(d.params)">
|
||||
<div class="layui-tab">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">默认页</li>
|
||||
<li>停止页</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<!-- 默认页 -->
|
||||
<blockquote class="layui-elem-quote layui-quote-nm">
|
||||
设置站点未找到时的提示页面。
|
||||
</blockquote>
|
||||
<div id="index-editor" style="height: 400px;">{{ d.data.index }}</div>
|
||||
</div>
|
||||
<div class="layui-tab-item">
|
||||
<!-- 停止页 -->
|
||||
<blockquote class="layui-elem-quote layui-quote-nm">
|
||||
设置站点停止时的提示页面,设置后需重新开关网站方可生效。
|
||||
</blockquote>
|
||||
<div id="stop-editor" style="height: 400px;">{{ d.data.stop }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-footer">
|
||||
<button id="save-website-default-settings" class="layui-btn">保存设置</button>
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
let indexEditor = '';
|
||||
let stopEditor = '';
|
||||
layui.data.sendParams = function (params) {
|
||||
layui.use(['admin', 'form', 'laydate', 'code'], function () {
|
||||
var $ = layui.$
|
||||
, admin = layui.admin
|
||||
, layer = layui.layer;
|
||||
indexEditor = ace.edit("index-editor", {
|
||||
mode: "ace/mode/html",
|
||||
selectionStyle: "text"
|
||||
});
|
||||
stopEditor = ace.edit("stop-editor", {
|
||||
mode: "ace/mode/html",
|
||||
selectionStyle: "text"
|
||||
});
|
||||
|
||||
$('#save-website-default-settings').click(function () {
|
||||
layer.load();
|
||||
admin.req({
|
||||
url: '/api/panel/website/defaultConfig'
|
||||
, type: 'post'
|
||||
, data: {
|
||||
index: indexEditor.getValue(),
|
||||
stop: stopEditor.getValue()
|
||||
}
|
||||
, success: function (res) {
|
||||
layer.closeAll('loading');
|
||||
if (res.code === 0) {
|
||||
layer.msg('保存成功', {icon: 1, shade: 0.3});
|
||||
setTimeout(function () {
|
||||
admin.render();
|
||||
}, 1000);
|
||||
} else {
|
||||
layer.msg(res.message, {icon: 2, shade: 0.3});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
</script>
|
||||
@@ -107,7 +107,7 @@ Date: 2023-07-21
|
||||
<div class="layui-input-block">
|
||||
<select name="php" lay-filter="website-php">
|
||||
{{# layui.each(d.params.php, function(index, item){ }}
|
||||
{{# if(item == d.params.config.php){ }}
|
||||
{{# if(item.slug == d.params.config.php){ }}
|
||||
<option value="{{ item.slug }}" selected="">{{ item.name }}</option>
|
||||
{{# }else{ }}
|
||||
<option value="{{ item.slug }}">{{ item.name }}</option>
|
||||
|
||||
@@ -38,9 +38,9 @@ func Plugin() {
|
||||
route.Get("config", mysql57Controller.GetConfig)
|
||||
route.Post("config", mysql57Controller.SaveConfig)
|
||||
route.Get("errorLog", mysql57Controller.ErrorLog)
|
||||
route.Get("clearErrorLog", mysql57Controller.ClearErrorLog)
|
||||
route.Post("clearErrorLog", mysql57Controller.ClearErrorLog)
|
||||
route.Get("slowLog", mysql57Controller.SlowLog)
|
||||
route.Get("clearSlowLog", mysql57Controller.ClearSlowLog)
|
||||
route.Post("clearSlowLog", mysql57Controller.ClearSlowLog)
|
||||
route.Get("rootPassword", mysql57Controller.GetRootPassword)
|
||||
route.Post("rootPassword", mysql57Controller.SetRootPassword)
|
||||
route.Get("database", mysql57Controller.DatabaseList)
|
||||
@@ -48,13 +48,14 @@ func Plugin() {
|
||||
route.Post("deleteDatabase", mysql57Controller.DeleteDatabase)
|
||||
route.Get("backup", mysql57Controller.BackupList)
|
||||
route.Post("createBackup", mysql57Controller.CreateBackup)
|
||||
route.Post("uploadBackup", mysql57Controller.UploadBackup)
|
||||
route.Post("deleteBackup", mysql57Controller.DeleteBackup)
|
||||
route.Post("restoreBackup", mysql57Controller.RestoreBackup)
|
||||
route.Get("user", mysql57Controller.UserList)
|
||||
route.Post("addUser", mysql57Controller.AddUser)
|
||||
route.Post("deleteUser", mysql57Controller.DeleteUser)
|
||||
route.Post("setUserPassword", mysql57Controller.SetUserPassword)
|
||||
route.Post("setUserPrivileges", mysql57Controller.SetUserPrivileges)
|
||||
route.Post("userPassword", mysql57Controller.SetUserPassword)
|
||||
route.Post("userPrivileges", mysql57Controller.SetUserPrivileges)
|
||||
})
|
||||
facades.Route().Prefix("api/plugins/mysql80").Middleware(middleware.Jwt()).Group(func(route route.Route) {
|
||||
mysql80Controller := mysql80.NewMysql80Controller()
|
||||
@@ -67,9 +68,9 @@ func Plugin() {
|
||||
route.Get("config", mysql80Controller.GetConfig)
|
||||
route.Post("config", mysql80Controller.SaveConfig)
|
||||
route.Get("errorLog", mysql80Controller.ErrorLog)
|
||||
route.Get("clearErrorLog", mysql80Controller.ClearErrorLog)
|
||||
route.Post("clearErrorLog", mysql80Controller.ClearErrorLog)
|
||||
route.Get("slowLog", mysql80Controller.SlowLog)
|
||||
route.Get("clearSlowLog", mysql80Controller.ClearSlowLog)
|
||||
route.Post("clearSlowLog", mysql80Controller.ClearSlowLog)
|
||||
route.Get("rootPassword", mysql80Controller.GetRootPassword)
|
||||
route.Post("rootPassword", mysql80Controller.SetRootPassword)
|
||||
route.Get("database", mysql80Controller.DatabaseList)
|
||||
@@ -77,13 +78,14 @@ func Plugin() {
|
||||
route.Post("deleteDatabase", mysql80Controller.DeleteDatabase)
|
||||
route.Get("backup", mysql80Controller.BackupList)
|
||||
route.Post("createBackup", mysql80Controller.CreateBackup)
|
||||
route.Post("uploadBackup", mysql80Controller.UploadBackup)
|
||||
route.Post("deleteBackup", mysql80Controller.DeleteBackup)
|
||||
route.Post("restoreBackup", mysql80Controller.RestoreBackup)
|
||||
route.Get("user", mysql80Controller.UserList)
|
||||
route.Post("addUser", mysql80Controller.AddUser)
|
||||
route.Post("deleteUser", mysql80Controller.DeleteUser)
|
||||
route.Post("setUserPassword", mysql80Controller.SetUserPassword)
|
||||
route.Post("setUserPrivileges", mysql80Controller.SetUserPrivileges)
|
||||
route.Post("userPassword", mysql80Controller.SetUserPassword)
|
||||
route.Post("userPrivileges", mysql80Controller.SetUserPrivileges)
|
||||
})
|
||||
facades.Route().Prefix("api/plugins/php74").Middleware(middleware.Jwt()).Group(func(route route.Route) {
|
||||
php74Controller := php74.NewPhp74Controller()
|
||||
@@ -97,8 +99,8 @@ func Plugin() {
|
||||
route.Post("config", php74Controller.SaveConfig)
|
||||
route.Get("errorLog", php74Controller.ErrorLog)
|
||||
route.Get("slowLog", php74Controller.SlowLog)
|
||||
route.Get("clearErrorLog", php74Controller.ClearErrorLog)
|
||||
route.Get("clearSlowLog", php74Controller.ClearSlowLog)
|
||||
route.Post("clearErrorLog", php74Controller.ClearErrorLog)
|
||||
route.Post("clearSlowLog", php74Controller.ClearSlowLog)
|
||||
route.Get("extensions", php74Controller.GetExtensionList)
|
||||
route.Post("installExtension", php74Controller.InstallExtension)
|
||||
route.Post("uninstallExtension", php74Controller.UninstallExtension)
|
||||
@@ -115,8 +117,8 @@ func Plugin() {
|
||||
route.Post("config", php80Controller.SaveConfig)
|
||||
route.Get("errorLog", php80Controller.ErrorLog)
|
||||
route.Get("slowLog", php80Controller.SlowLog)
|
||||
route.Get("clearErrorLog", php80Controller.ClearErrorLog)
|
||||
route.Get("clearSlowLog", php80Controller.ClearSlowLog)
|
||||
route.Post("clearErrorLog", php80Controller.ClearErrorLog)
|
||||
route.Post("clearSlowLog", php80Controller.ClearSlowLog)
|
||||
route.Get("extensions", php80Controller.GetExtensionList)
|
||||
route.Post("installExtension", php80Controller.InstallExtension)
|
||||
route.Post("uninstallExtension", php80Controller.UninstallExtension)
|
||||
|
||||
@@ -165,7 +165,7 @@ Init_Panel() {
|
||||
mkdir ${setup_Path}/server
|
||||
mkdir ${setup_Path}/server/cron
|
||||
mkdir ${setup_Path}/server/cron/logs
|
||||
chmod -R 644 ${setup_Path}/server
|
||||
chmod -R 755 ${setup_Path}/server
|
||||
mkdir ${setup_Path}/panel
|
||||
rm -rf ${setup_Path}/panel/*
|
||||
# 下载面板zip包并解压
|
||||
|
||||
@@ -266,16 +266,13 @@ elif [[ ${memTotal} -ge 32768 ]]; then
|
||||
sed -i "s#^innodb_log_buffer_size.*#innodb_log_buffer_size = 512M#" ${mysqlPath}/conf/my.cnf
|
||||
fi
|
||||
|
||||
chmod 644 ${mysqlPath}/conf/my.cnf
|
||||
chown -R mysql:mysql ${mysqlPath}
|
||||
chmod -R 755 ${mysqlPath}
|
||||
|
||||
# 初始化
|
||||
rm -rf ${mysqlPath}/src
|
||||
rm -rf ${mysqlPath}/data
|
||||
mkdir -p ${mysqlPath}/data
|
||||
chown -R mysql:mysql ${mysqlPath}
|
||||
chmod -R 755 ${mysqlPath}
|
||||
chmod 644 ${mysqlPath}/conf/my.cnf
|
||||
|
||||
${mysqlPath}/bin/mysqld --initialize-insecure --user=mysql --basedir=${mysqlPath} --datadir=${mysqlPath}/data
|
||||
|
||||
@@ -284,7 +281,7 @@ source /etc/profile
|
||||
|
||||
# 启动
|
||||
cp ${mysqlPath}/lib/systemd/system/mysqld.service /etc/systemd/system/mysqld.service
|
||||
sed -i "/ExecStartPre/d" /etc/systemd/system/mysqld.service
|
||||
sed -i '/ExecStartPre/d' /etc/systemd/system/mysqld.service
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable mysqld
|
||||
@@ -292,7 +289,7 @@ systemctl start mysqld
|
||||
|
||||
${mysqlPath}/bin/mysqladmin -u root password ${mysqlPassword}
|
||||
|
||||
panel writePlugin mysqsl${1} ${mysqlVersion}
|
||||
panel writePlugin mysql${1} ${mysqlVersion}
|
||||
panel writeMysqlPassword ${mysqlPassword}
|
||||
|
||||
echo -e "${HR}\nMySQL-${1} 安装完成\n${HR}"
|
||||
|
||||
@@ -39,10 +39,10 @@ if [ "${OS}" == "centos" ]; then
|
||||
/usr/bin/crb enable
|
||||
dnf makecache
|
||||
dnf groupinstall "Development Tools" -y
|
||||
dnf install tar unzip gd gd-devel git-core flex perl perl-CPAN oniguruma oniguruma-devel libsodium-devel libxml2-devel libxslt-devel GeoIP-devel bison yajl yajl-devel curl curl-devel libtermcap-devel ncurses-devel libevent-devel readline-devel libuuid-devel brotli-devel icu libicu libicu-devel openssl openssl-devel -y
|
||||
dnf install tar unzip gd gd-devel git-core flex perl oniguruma oniguruma-devel libsodium-devel libxml2-devel libxslt-devel GeoIP-devel bison yajl yajl-devel curl curl-devel libtermcap-devel ncurses-devel libevent-devel readline-devel libuuid-devel brotli-devel icu libicu libicu-devel openssl openssl-devel -y
|
||||
elif [ "${OS}" == "debian" ]; then
|
||||
apt update
|
||||
apt install build-essential tar unzip libgd3 libgd-dev git flex perl perl-modules libonig-dev libsodium-dev libxml2-dev libxslt1-dev libgeoip-dev bison libyajl-dev curl libcurl4-openssl-dev libncurses5-dev libevent-dev libreadline-dev uuid-dev libbrotli-dev icu-devtools libicu-dev openssl libssl-dev -y
|
||||
apt install build-essential tar unzip libgd3 libgd-dev git flex perl libonig-dev libsodium-dev libxml2-dev libxslt1-dev libgeoip-dev bison libyajl-dev curl libcurl4-openssl-dev libncurses5-dev libevent-dev libreadline-dev uuid-dev libbrotli-dev icu-devtools libicu-dev openssl libssl-dev -y
|
||||
else
|
||||
echo -e $HR
|
||||
echo "错误:耗子Linux面板不支持该系统"
|
||||
|
||||
@@ -174,7 +174,7 @@ pm.min_spare_servers = 5
|
||||
pm.max_spare_servers = 10
|
||||
request_terminate_timeout = 100
|
||||
request_slowlog_timeout = 30
|
||||
pm.status_path = /phpfpm_${phpVersion}_status
|
||||
pm.status_path = /phpfpm_status/${phpVersion}
|
||||
slowlog = var/log/slow.log
|
||||
EOF
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ limitations under the License.
|
||||
'
|
||||
|
||||
HR="+----------------------------------------------------"
|
||||
OS=$(source /etc/os-release && { [[ "$ID" == "debian" ]] && echo "debian"; } || { [[ "$ID" == "centos" ]] || [[ "$ID" == "rhel" ]] || [[ "$ID" == "rocky" ]] || [[ "$ID" == "almalinux" ]] && echo "centos"; } || echo "unknown")
|
||||
|
||||
downloadUrl="https://dl.cdn.haozi.net/panel/php_extensions"
|
||||
action="$1"
|
||||
@@ -34,7 +35,15 @@ Install() {
|
||||
fi
|
||||
|
||||
# 安装依赖
|
||||
dnf install ImageMagick ImageMagick-devel -y
|
||||
if [ "${OS}" == "centos" ]; then
|
||||
dnf install ImageMagick ImageMagick-devel -y
|
||||
elif [ "${OS}" == "debian" ]; then
|
||||
apt install imagemagick libmagickwand-dev
|
||||
else
|
||||
echo -e $HR
|
||||
echo "错误:耗子Linux面板不支持该系统"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd /www/server/php/${phpVersion}/src/ext
|
||||
rm -rf imagick
|
||||
Reference in New Issue
Block a user