Compare commits

...
Author SHA1 Message Date
ssongliu 0eee2d9807 fix: 解决数据库用户名显示错误的问题 (#2028) 2023-08-22 16:33:26 +08:00
zhengkunwang f0e78c4b7c fix: 解决运行环境网站切换PHP版本导致已安装应用消失的问题 (#2026) 2023-08-22 15:42:22 +08:00
ssongliu 6da64f2c8f fix: 解决清空容器日志导致的 CPU 占用异常问题 (#2019)
Refs #1999
2023-08-22 15:42:19 +08:00
ssongliu d572c28d1c fix: 服务器地址支持设置为空 (#2016) 2023-08-22 15:42:13 +08:00
ssongliu 7fac0fcbe8 fix: 解决数据库用户名超长导致用户创建失败的问题 (#2011) 2023-08-22 15:42:09 +08:00
ssongliu cb1b6742d4 fix: 解决防火墙多端口创建失败的问题 (#1988) 2023-08-17 16:59:26 +08:00
ssongliu 27bd4a63ef fix: 延长进程状态获取等待时间 (#1986) 2023-08-17 16:11:11 +08:00
ssongliu 62d628c1aa fix: 解决进程状态一直加载中的问题 (#1983)
Refs #1968
2023-08-17 14:48:09 +08:00
ssongliu 06f81fc7f7 fix: 解决网站反代编辑时回显错误的问题 (#1980)
Refs #1976
2023-08-17 13:55:02 +08:00
wangdan-fit2cloud 23c72c8301 fix: 移动端样式优化 (#1979)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2023-08-17 13:54:58 +08:00
ssongliu 280e74e01b fix: 解决 ufw 防火墙创建多端口时无法删除的问题 2023-08-17 10:21:14 +08:00
ssongliu b7dada93da fix: 解决暂停进程被重新拉起的问题 (#1977)
Refs #1964
2023-08-16 23:44:57 +08:00
ssongliu 9447936cc3 fix: 解决获取远程数据库失败的问题 (#1974)
Refs #1925
2023-08-16 23:40:55 +08:00
ssongliu 5e7429cd51 fix: 解决容器编排创建日志加载慢的问题 (#1973)
Refs #1950
2023-08-16 23:40:51 +08:00
ssongliu 7a3716a363 fix: 修改网站重定向列表样式 (#1975)
Refs #1971
2023-08-16 23:40:45 +08:00
wangdan-fit2cloud 5e38723cc1 优化移动端样式 (#1967)
#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
2023-08-16 23:40:36 +08:00
26 changed files with 259 additions and 187 deletions
+6 -1
View File
@@ -196,7 +196,12 @@ func (b *BaseApi) GetProcess(c *gin.Context) {
// @Security ApiKeyAuth
// @Router /host/tool/supervisor/process/load [post]
func (b *BaseApi) LoadProcessStatus(c *gin.Context) {
helper.SuccessWithData(c, hostToolService.LoadProcessStatus())
datas, err := hostToolService.LoadProcessStatus()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, datas)
}
// @Tags Host tool
+4
View File
@@ -840,6 +840,10 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool) ([]respons
}
versions = common.GetSortedVersions(versions)
if len(versions) == 0 {
if !updated {
installDTO.CanUpdate = false
res = append(res, installDTO)
}
continue
}
lastVersion := versions[0]
+13 -5
View File
@@ -531,11 +531,15 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
if err != nil {
return err
}
container, err := client.ContainerInspect(context.Background(), req.Name)
ctx := context.Background()
containerItem, err := client.ContainerInspect(ctx, req.Name)
if err != nil {
return err
}
file, err := os.OpenFile(container.LogPath, os.O_RDWR|os.O_CREATE, 0666)
if err := client.ContainerStop(ctx, containerItem.ID, container.StopOptions{}); err != nil {
return err
}
file, err := os.OpenFile(containerItem.LogPath, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
return err
}
@@ -545,10 +549,14 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
}
_, _ = file.Seek(0, 0)
files, _ := filepath.Glob(fmt.Sprintf("%s.*", container.LogPath))
files, _ := filepath.Glob(fmt.Sprintf("%s.*", containerItem.LogPath))
for _, file := range files {
_ = os.Remove(file)
}
if err := client.ContainerStart(ctx, containerItem.ID, types.ContainerStartOptions{}); err != nil {
return err
}
return nil
}
@@ -631,9 +639,9 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) string {
filePath := ""
switch req.Type {
case "image-pull", "image-push", "image-build":
case "image-pull", "image-push", "image-build", "compose-create":
filePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
case "compose-detail", "compose-create":
case "compose-detail":
client, err := docker.NewDockerClient()
if err != nil {
return ""
+10 -3
View File
@@ -159,8 +159,15 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
if req.From == "path" {
req.Name = path.Base(path.Dir(req.Path))
}
logName := path.Dir(req.Path) + "/compose.log"
file, err := os.OpenFile(logName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
dockerLogDir := path.Join(global.CONF.System.TmpDir, "docker_logs")
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
return "", err
}
}
logItem := fmt.Sprintf("%s/compose_create_%s_%s.log", dockerLogDir, req.Name, time.Now().Format("20060102150405"))
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return "", err
}
@@ -181,7 +188,7 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
_, _ = file.WriteString("docker-compose up successful!")
}()
return req.Name, nil
return path.Base(logItem), nil
}
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
+32 -39
View File
@@ -166,59 +166,52 @@ func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool)
if err != nil {
return err
}
protos := strings.Split(req.Protocol, "/")
if client.Name() == "ufw" {
req.Port = strings.ReplaceAll(req.Port, "-", ":")
if req.Operation == "remove" && req.Protocol == "tcp/udp" {
req.Protocol = ""
return u.operatePort(client, req)
if len(req.Address) == 0 {
req.Address = "Anywhere"
}
if strings.Contains(req.Port, ",") || strings.Contains(req.Port, "-") {
for _, proto := range protos {
req.Port = strings.ReplaceAll(req.Port, "-", ":")
req.Protocol = proto
if err := u.operatePort(client, req); err != nil {
return err
}
}
return nil
}
if req.Protocol == "tcp/udp" {
req.Protocol = ""
}
if err := u.operatePort(client, req); err != nil {
return err
}
return nil
}
if req.Protocol == "tcp/udp" {
if client.Name() == "firewalld" && strings.Contains(req.Port, ",") {
ports := strings.Split(req.Port, ",")
itemPorts := req.Port
for _, proto := range protos {
if strings.Contains(req.Port, "-") {
req.Protocol = proto
if err := u.operatePort(client, req); err != nil {
return err
}
} else {
ports := strings.Split(itemPorts, ",")
for _, port := range ports {
if len(port) == 0 {
continue
}
req.Port = port
req.Protocol = "tcp"
req.Protocol = proto
if err := u.operatePort(client, req); err != nil {
return err
}
req.Protocol = "udp"
if err := u.operatePort(client, req); err != nil {
return err
}
}
} else {
req.Protocol = "tcp"
if err := u.operatePort(client, req); err != nil {
return err
}
req.Protocol = "udp"
if err := u.operatePort(client, req); err != nil {
return err
}
}
} else {
if strings.Contains(req.Port, ",") {
ports := strings.Split(req.Port, ",")
for _, port := range ports {
req.Port = port
if err := u.operatePort(client, req); err != nil {
return err
}
}
} else {
if err := u.operatePort(client, req); err != nil {
return err
}
}
}
if reload {
return client.Reload()
}
return nil
return client.Reload()
}
func (u *FirewallService) OperateAddressRule(req dto.AddrRuleOperate, reload bool) error {
+18 -10
View File
@@ -34,7 +34,7 @@ type IHostToolService interface {
GetToolLog(req request.HostToolLogReq) (string, error)
OperateSupervisorProcess(req request.SupervisorProcessConfig) error
GetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error)
LoadProcessStatus() []response.ProcessStatus
LoadProcessStatus() ([]response.ProcessStatus, error)
OperateSupervisorProcessFile(req request.SupervisorProcessFileReq) (string, error)
}
@@ -327,7 +327,10 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
if err = configFile.SaveTo(iniPath); err != nil {
return err
}
return operateSupervisorCtl("reload", "", "")
if err := operateSupervisorCtl("reread", "", ""); err != nil {
return err
}
return operateSupervisorCtl("update", "", "")
case "update":
configFile, err := ini.Load(iniPath)
if err != nil {
@@ -350,7 +353,10 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
if err = configFile.SaveTo(iniPath); err != nil {
return err
}
return operateSupervisorCtl("reload", "", "")
if err := operateSupervisorCtl("reread", "", ""); err != nil {
return err
}
return operateSupervisorCtl("update", "", "")
case "restart":
return operateSupervisorCtl("restart", req.Name, "")
case "start":
@@ -362,17 +368,20 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
_ = files.NewFileOp().DeleteFile(iniPath)
_ = files.NewFileOp().DeleteFile(outLog)
_ = files.NewFileOp().DeleteFile(errLog)
_ = operateSupervisorCtl("reload", "", "")
if err := operateSupervisorCtl("reread", "", ""); err != nil {
return err
}
return operateSupervisorCtl("update", "", "")
}
return nil
}
func (h *HostToolService) LoadProcessStatus() []response.ProcessStatus {
func (h *HostToolService) LoadProcessStatus() ([]response.ProcessStatus, error) {
var datas []response.ProcessStatus
statuLines, err := cmd.Exec("supervisorctl status")
if err != nil {
return datas
return datas, fmt.Errorf("exec `supervisorctl status` failed, err: %v", statuLines)
}
lines := strings.Split(string(statuLines), "\n")
for _, line := range lines {
@@ -387,7 +396,7 @@ func (h *HostToolService) LoadProcessStatus() []response.ProcessStatus {
for i := 0; i < len(datas); i++ {
go func(index int) {
for t := 0; t < 3; t++ {
status, err := cmd.ExecWithTimeOut(fmt.Sprintf("supervisorctl status %s", datas[index].Name), 3*time.Second)
status, err := cmd.ExecWithTimeOut(fmt.Sprintf("supervisorctl status %s", datas[index].Name), 2*time.Second)
if err != nil {
time.Sleep(2 * time.Second)
continue
@@ -401,8 +410,7 @@ func (h *HostToolService) LoadProcessStatus() []response.ProcessStatus {
datas[index].Status = fields[1]
if fields[1] != "RUNNING" {
datas[index].Msg = strings.Join(fields[2:], " ")
time.Sleep(1 * time.Second)
continue
break
}
datas[index].PID = strings.TrimSuffix(fields[3], ",")
datas[index].Uptime = fields[5]
@@ -416,7 +424,7 @@ func (h *HostToolService) LoadProcessStatus() []response.ProcessStatus {
}(i)
}
wg.Wait()
return datas
return datas, nil
}
func (h *HostToolService) GetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error) {
+6 -1
View File
@@ -123,7 +123,6 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
return "", err
}
fileName := "Dockerfile"
dockerLogDir := path.Join(global.CONF.System.TmpDir, "docker_logs")
if req.From == "edit" {
dir := fmt.Sprintf("%s/docker/build/%s", constant.DataDir, strings.ReplaceAll(req.Name, ":", "_"))
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
@@ -158,6 +157,12 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
Labels: stringsToMap(req.Tags),
}
dockerLogDir := path.Join(global.CONF.System.TmpDir, "docker_logs")
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
return "", err
}
}
logItem := fmt.Sprintf("%s/image_build_%s_%s.log", dockerLogDir, strings.ReplaceAll(req.Name, ":", "_"), time.Now().Format("20060102150405"))
file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
+19
View File
@@ -1,5 +1,11 @@
package client
import (
"strings"
"github.com/1Panel-dev/1Panel/backend/utils/common"
)
type DBInfo struct {
From string `json:"from"`
Address string `json:"address"`
@@ -85,3 +91,16 @@ var formatMap = map[string]string{
"gbk": "gbk_chinese_ci",
"big5": "big5_chinese_ci",
}
func loadNameByDB(name, version string) string {
if strings.HasPrefix(version, "5.6") {
if len(name) <= 16 {
return name
}
return strings.TrimSuffix(name[:10], "_") + "_" + common.RandStr(5)
}
if len(name) <= 32 {
return name
}
return strings.TrimSuffix(name[:25], "_") + "_" + common.RandStr(5)
}
+8 -8
View File
@@ -38,7 +38,7 @@ func (r *Local) Create(info CreateInfo) error {
return err
}
if err := r.CreateUser(info); err != nil {
if err := r.CreateUser(info, true); err != nil {
_ = r.ExecSQL(fmt.Sprintf("drop database if exists `%s`", info.Name), info.Timeout)
return err
}
@@ -46,7 +46,7 @@ func (r *Local) Create(info CreateInfo) error {
return nil
}
func (r *Local) CreateUser(info CreateInfo) error {
func (r *Local) CreateUser(info CreateInfo, withDeleteDB bool) error {
var userlist []string
if strings.Contains(info.Permission, ",") {
ips := strings.Split(info.Permission, ",")
@@ -147,7 +147,7 @@ func (r *Local) ChangePassword(info PasswordChangeInfo) error {
for _, user := range userlist {
passwordChangeSql := fmt.Sprintf("set password for %s = password('%s')", user, info.Password)
if !strings.HasPrefix(info.Version, "5.7") && !strings.HasPrefix(info.Version, "5.6") {
passwordChangeSql = fmt.Sprintf("ALTER USER %s IDENTIFIED WITH mysql_native_password BY '%s';", user, info.Password)
passwordChangeSql = fmt.Sprintf("alter user %s identified by '%s';", user, info.Password)
}
if err := r.ExecSQL(passwordChangeSql, info.Timeout); err != nil {
return err
@@ -201,7 +201,7 @@ func (r *Local) ChangeAccess(info AccessChangeInfo) error {
Password: info.Password,
Permission: info.Permission,
Timeout: info.Timeout,
}); err != nil {
}, false); err != nil {
return err
}
if err := r.ExecSQL("flush privileges", 300); err != nil {
@@ -276,7 +276,7 @@ func (r *Local) SyncDB(version string) ([]SyncDBInfo, error) {
From: r.From,
Format: parts[1],
}
userLines, err := r.ExecSQLForRows(fmt.Sprintf("SELECT USER,HOST FROM mysql.DB WHERE DB = '%s'", parts[0]), 300)
userLines, err := r.ExecSQLForRows(fmt.Sprintf("select user,host from mysql.db where db = '%s'", parts[0]), 300)
if err != nil {
return datas, err
}
@@ -305,18 +305,18 @@ func (r *Local) SyncDB(version string) ([]SyncDBInfo, error) {
}
}
if len(dataItem.Username) == 0 {
dataItem.Username = loadNameByDB(parts[0], version)
if err := r.CreateUser(CreateInfo{
Name: parts[0],
Format: parts[1],
Version: version,
Username: parts[0],
Username: dataItem.Username,
Password: common.RandStr(16),
Permission: "%",
Timeout: 300,
}); err != nil {
}, false); err != nil {
global.LOG.Errorf("sync from remote server failed, err: create user failed %v", err)
}
dataItem.Username = parts[0]
dataItem.Permission = "%"
} else {
if isLocal {
+29 -25
View File
@@ -39,7 +39,7 @@ func (r *Remote) Create(info CreateInfo) error {
return err
}
if err := r.CreateUser(info); err != nil {
if err := r.CreateUser(info, true); err != nil {
_ = r.ExecSQL(fmt.Sprintf("drop database if exists `%s`", info.Name), info.Timeout)
return err
}
@@ -47,7 +47,7 @@ func (r *Remote) Create(info CreateInfo) error {
return nil
}
func (r *Remote) CreateUser(info CreateInfo) error {
func (r *Remote) CreateUser(info CreateInfo, withDeleteDB bool) error {
var userlist []string
if strings.Contains(info.Permission, ",") {
ips := strings.Split(info.Permission, ",")
@@ -62,15 +62,17 @@ func (r *Remote) CreateUser(info CreateInfo) error {
for _, user := range userlist {
if err := r.ExecSQL(fmt.Sprintf("create user %s identified by '%s';", user, info.Password), info.Timeout); err != nil {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Permission: info.Permission,
ForceDelete: true,
Timeout: 300})
if strings.Contains(err.Error(), "ERROR 1396") {
return buserr.New(constant.ErrUserIsExist)
if withDeleteDB {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Permission: info.Permission,
ForceDelete: true,
Timeout: 300})
if strings.Contains(err.Error(), "ERROR 1396") {
return buserr.New(constant.ErrUserIsExist)
}
}
return err
}
@@ -82,13 +84,15 @@ func (r *Remote) CreateUser(info CreateInfo) error {
grantStr = fmt.Sprintf("%s identified by '%s' with grant option;", grantStr, info.Password)
}
if err := r.ExecSQL(grantStr, info.Timeout); err != nil {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Permission: info.Permission,
ForceDelete: true,
Timeout: 300})
if withDeleteDB {
_ = r.Delete(DeleteInfo{
Name: info.Name,
Version: info.Version,
Username: info.Username,
Permission: info.Permission,
ForceDelete: true,
Timeout: 300})
}
return err
}
}
@@ -202,7 +206,7 @@ func (r *Remote) ChangeAccess(info AccessChangeInfo) error {
Password: info.Password,
Permission: info.Permission,
Timeout: info.Timeout,
}); err != nil {
}, false); err != nil {
return err
}
if err := r.ExecSQL("flush privileges", 300); err != nil {
@@ -263,7 +267,7 @@ func (r *Remote) Recover(info RecoverInfo) error {
func (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) {
var datas []SyncDBInfo
rows, err := r.Client.Query("SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME FROM information_schema.SCHEMATA")
rows, err := r.Client.Query("select schema_name, default_character_set_name from information_schema.SCHEMATA")
if err != nil {
return datas, err
}
@@ -283,7 +287,7 @@ func (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) {
MysqlName: r.From,
Format: charsetName,
}
userRows, err := r.Client.Query("SELECT USER,HOST FROM mysql.DB WHERE DB = ?", dbName)
userRows, err := r.Client.Query("select user,host from mysql.db where db = ?", dbName)
if err != nil {
return datas, err
}
@@ -312,18 +316,18 @@ func (r *Remote) SyncDB(version string) ([]SyncDBInfo, error) {
i++
}
if len(dataItem.Username) == 0 {
dataItem.Username = loadNameByDB(dbName, version)
if err := r.CreateUser(CreateInfo{
Name: dbName,
Format: charsetName,
Version: version,
Username: dbName,
Username: dataItem.Username,
Password: common.RandStr(16),
Permission: "%",
Timeout: 300,
}); err != nil {
global.LOG.Errorf("sync from remote server failed, err: create user failed %v", err)
}, false); err != nil {
return datas, fmt.Errorf("sync db from remote server failed, err: create user failed %v", err)
}
dataItem.Username = dbName
dataItem.Permission = "%"
} else {
if isLocal {
+1 -1
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/public/favicon.png" />
@@ -1,7 +1,7 @@
<template>
<div>
<div style="display: flex; flex-wrap: wrap">
<el-select @change="searchLogs" v-model="logSearch.mode" style="width: 100px">
<el-select @change="searchLogs" v-model="logSearch.mode" style="width: 150px">
<template #prefix>{{ $t('container.fetch') }}</template>
<el-option v-for="item in timeOptions" :key="item.label" :value="item.value" :label="item.label" />
</el-select>
@@ -130,7 +130,7 @@ const acceptParams = (props: DialogProps): void => {
};
const onClean = async () => {
ElMessageBox.confirm(i18n.global.t('commons.msg.clean'), i18n.global.t('container.cleanLog'), {
ElMessageBox.confirm(i18n.global.t('container.cleanLogHelper'), i18n.global.t('container.cleanLog'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
+6 -3
View File
@@ -507,6 +507,8 @@ const message = {
lastHour: 'Last Hour',
last10Min: 'Last 10 Minutes',
cleanLog: 'Clean log',
cleanLogHelper:
'Clearing logs requires restarting the container, and this operation cannot be rolled back. Do you want to continue?',
newName: 'New name',
source: 'Resource rate',
cpuShare: 'CPU Share',
@@ -1463,12 +1465,12 @@ const message = {
certificatePath: 'Certificate file',
ipWhiteListHelper: 'The role of IP whitelist: all rules are invalid for IP whitelist',
redirect: 'redirect',
sourceDomain: 'source domain name/path',
sourceDomain: 'Source domain',
targetURL: 'Target URL address',
keepPath: 'Keep URI parameters',
keepPath: 'URI params',
path: 'path',
redirectType: 'redirection type',
redirectWay: 'way',
redirectWay: 'Way',
keep: 'keep',
notKeep: 'Do not keep',
redirectRoot: 'Redirect to the homepage',
@@ -1666,6 +1668,7 @@ const message = {
},
tool: {
supervisor: {
loadStatusErr: 'Failed to retrieve process status, please check the status of the supervisor service.',
notSupport: 'Supervisor is not detected, please refer to the official document for installation',
list: 'Daemon process',
config: 'Supervisor configuration',
+2
View File
@@ -496,6 +496,7 @@ const message = {
lastHour: '最近 1 小時',
last10Min: '最近 10 分鐘',
cleanLog: '清空日誌',
cleanLogHelper: '清空日誌需要重啟容器,該操作無法回滾,是否繼續?',
newName: '新名稱',
source: '資源使用率',
cpuShare: 'CPU 權重',
@@ -1580,6 +1581,7 @@ const message = {
},
tool: {
supervisor: {
loadStatusErr: '獲取進程狀態失敗,請檢查 supervisor 服務狀態',
notSupport: '未檢測到 Supervisor,請參考官方文檔進行安裝',
list: '守護進程',
config: 'Supervisor 配置',
+2
View File
@@ -496,6 +496,7 @@ const message = {
lastHour: '最近 1 小时',
last10Min: '最近 10 分钟',
cleanLog: '清空日志',
cleanLogHelper: '清空日志需要重启容器,该操作无法回滚,是否继续?',
newName: '新名称',
source: '资源使用率',
cpuShare: 'CPU 权重',
@@ -1582,6 +1583,7 @@ const message = {
},
tool: {
supervisor: {
loadStatusErr: '获取进程状态失败,请检查 supervisor 服务状态',
notSupport: '未检测到 Supervisor,请参考官方文档进行安装',
list: '守护进程',
config: 'Supervisor 配置',
+1 -1
View File
@@ -148,7 +148,6 @@ onMounted(() => {
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
overflow: hidden;
}
@@ -178,6 +177,7 @@ onMounted(() => {
transition: transform 0.28s;
width: var(--panel-menu-width) !important;
background: #ffffff;
z-index: 9999;
}
.app-footer {
display: block;
+10
View File
@@ -27,6 +27,16 @@ const databaseRouter = {
requiresAuth: false,
},
},
{
path: 'mysql/setting',
name: 'MySQL-Setting',
component: () => import('@/views/database/mysql/setting/index.vue'),
hidden: true,
meta: {
activeMenu: '/databases',
requiresAuth: false,
},
},
{
path: 'mysql/remote',
name: 'MySQL-Remote',
+1 -1
View File
@@ -181,7 +181,7 @@ html {
.mask-prompt {
position: absolute;
z-index: 9999;
z-index: 9998;
top: 220px;
left: 45%;
transform: translate(-50%, -50%);
+2 -3
View File
@@ -119,7 +119,6 @@
v-model:page-size="paginationConfig.pageSize"
v-bind="paginationConfig"
@change="search(req)"
:page-sizes="[30, 60, 90]"
:layout="mobile ? 'total, prev, pager, next' : 'total, sizes, prev, pager, next, jumper'"
/>
</div>
@@ -259,14 +258,14 @@ onMounted(() => {
}
.app-desc {
margin-top: 5px;
margin-top: 8px;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
height: 45px;
height: 43px;
.desc {
font-size: 14px;
@@ -161,7 +161,7 @@ const onDownload = async () => {
};
const onClean = async () => {
ElMessageBox.confirm(i18n.global.t('commons.msg.clean'), i18n.global.t('container.cleanLog'), {
ElMessageBox.confirm(i18n.global.t('container.cleanLogHelper'), i18n.global.t('container.cleanLog'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
+5 -18
View File
@@ -11,7 +11,7 @@
></AppStatus>
</template>
<template v-if="!isOnSetting" #search>
<template #search>
<el-select v-model="paginationConfig.from" @change="search()">
<template #prefix>{{ $t('commons.table.type') }}</template>
<el-option-group>
@@ -28,7 +28,7 @@
</el-select>
</template>
<template #toolbar v-if="!isOnSetting">
<template #toolbar>
<el-row>
<el-col :xs="24" :sm="20" :md="20" :lg="20" :xl="20">
<el-button
@@ -77,7 +77,7 @@
</el-col>
</el-row>
</template>
<template #main v-if="(mysqlIsExist && !isOnSetting) || !isLocal()">
<template #main v-if="mysqlIsExist || !isLocal()">
<ComplexTable
:pagination-config="paginationConfig"
@sort-change="search"
@@ -162,14 +162,10 @@
</LayoutContent>
</div>
<el-card
v-if="mysqlStatus != 'Running' && !isOnSetting && mysqlIsExist && !loading && maskShow && isLocal"
class="mask-prompt"
>
<el-card v-if="mysqlStatus != 'Running' && mysqlIsExist && !loading && maskShow && isLocal" class="mask-prompt">
<span>{{ $t('commons.service.serviceNotStarted', ['MySQL']) }}</span>
</el-card>
<Setting ref="settingRef" style="margin-top: 20px" />
<el-dialog
v-model="phpVisiable"
:title="$t('app.checkTitle')"
@@ -208,7 +204,6 @@ import DeleteDialog from '@/views/database/mysql/delete/index.vue';
import PasswordDialog from '@/views/database/mysql/password/index.vue';
import RootPasswordDialog from '@/views/database/mysql/conn/index.vue';
import AppResources from '@/views/database/mysql/check/index.vue';
import Setting from '@/views/database/mysql/setting/index.vue';
import AppStatus from '@/components/app-status/index.vue';
import Backups from '@/components/backup/index.vue';
import UploadDialog from '@/components/upload/index.vue';
@@ -238,7 +233,6 @@ const maskShow = ref(true);
const dbOptions = ref<Array<Database.RemoteDBOption>>([]);
const mysqlName = ref();
const isOnSetting = ref<boolean>();
const checkRef = ref();
const deleteRef = ref();
@@ -292,15 +286,8 @@ function isLocal() {
const passwordRef = ref();
const settingRef = ref();
const onSetting = async () => {
isOnSetting.value = true;
let params = {
status: mysqlStatus.value,
mysqlName: mysqlName.value,
mysqlVersion: mysqlVersion.value,
};
settingRef.value!.acceptParams(params);
router.push({ name: 'MySQL-Setting' });
};
const search = async (column?: any) => {
@@ -1,7 +1,11 @@
<template>
<div v-show="onSetting" v-loading="loading">
<LayoutContent :title="'MySQL ' + $t('commons.button.set')" :reload="true">
<template #buttons>
<div v-loading="loading">
<LayoutContent>
<template #title>
<back-button name="MySQL" :header="'MySQL ' + $t('commons.button.set')" />
</template>
<template #toolbar>
<el-button type="primary" :plain="activeName !== 'conf'" @click="jumpToConf">
{{ $t('database.confChange') }}
</el-button>
@@ -115,12 +119,12 @@ import Status from '@/views/database/mysql/setting/status/index.vue';
import Variables from '@/views/database/mysql/setting/variables/index.vue';
import SlowLog from '@/views/database/mysql/setting/slow-log/index.vue';
import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { reactive, ref } from 'vue';
import { onMounted, reactive, ref } from 'vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import { loadDatabaseFile, loadMysqlBaseInfo, loadMysqlVariables, updateMysqlConfByFile } from '@/api/modules/database';
import { ChangePort, GetAppDefaultConfig } from '@/api/modules/app';
import { ChangePort, CheckAppInstalled, GetAppDefaultConfig } from '@/api/modules/app';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
@@ -146,34 +150,12 @@ const statusRef = ref();
const variablesRef = ref();
const slowLogRef = ref();
const onSetting = ref<boolean>(false);
const mysqlName = ref();
const mysqlStatus = ref();
const mysqlVersion = ref();
const variables = ref();
interface DialogProps {
mysqlName: string;
mysqlVersion: string;
status: string;
}
const dialogContainerLogRef = ref();
const acceptParams = (props: DialogProps): void => {
onSetting.value = true;
mysqlStatus.value = props.status;
mysqlVersion.value = props.mysqlVersion;
loadBaseInfo();
if (mysqlStatus.value === 'Running') {
loadVariables();
loadSlowLogs();
statusRef.value!.acceptParams({ mysqlName: props.mysqlName });
}
};
const onClose = (): void => {
onSetting.value = false;
};
const jumpToConf = async () => {
activeName.value = 'conf';
loadMysqlConf();
@@ -303,8 +285,21 @@ const loadMysqlConf = async () => {
mysqlConf.value = res.data;
};
defineExpose({
acceptParams,
onClose,
const onLoadInfo = async () => {
await CheckAppInstalled('mysql').then((res) => {
mysqlName.value = res.data.name;
mysqlStatus.value = res.data.status;
mysqlVersion.value = res.data.version;
loadBaseInfo();
if (mysqlStatus.value === 'Running') {
loadVariables();
loadSlowLogs();
statusRef.value!.acceptParams({ mysqlName: mysqlName.value });
}
});
};
onMounted(() => {
onLoadInfo();
});
</script>
@@ -221,22 +221,27 @@ const search = async () => {
};
const loadStatus = async () => {
const res = await LoadProcessStatus();
let stats = res.data || [];
if (stats.length === 0) {
return;
}
for (const process of data.value) {
process.status = [];
for (const item of stats) {
if (process.name === item.name.split(':')[0]) {
process.status.push(item);
await LoadProcessStatus()
.then((res) => {
let stats = res.data || [];
for (const process of data.value) {
process.status = [];
for (const item of stats) {
if (process.name === item.name.split(':')[0]) {
process.status.push(item);
}
}
if (process.status.length !== 0) {
process.hasLoad = true;
}
}
}
if (process.status.length !== 0) {
process.hasLoad = true;
}
}
})
.catch(() => {
for (const process of data.value) {
process.status = [{ name: '-', status: 'FATAL', msg: i18n.global.t('tool.supervisor.loadStatusErr') }];
process.hasLoad = true;
}
});
};
const mobile = computed(() => {
@@ -4,10 +4,17 @@
<template #header>
<DrawerHeader :header="$t('setting.systemIP')" :back="handleClose" />
</template>
<el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading">
<el-form
ref="formRef"
label-position="top"
:model="form"
:rules="rules"
@submit.prevent
v-loading="loading"
>
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('setting.systemIP')" prop="systemIP" :rules="Rules.ipV4V6OrDomain">
<el-form-item :label="$t('setting.systemIP')" prop="systemIP">
<el-input clearable v-model="form.systemIP" />
<span class="input-help">{{ $t('commons.rule.hostHelper') }}</span>
</el-form-item>
@@ -31,8 +38,8 @@ import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { updateSetting } from '@/api/modules/setting';
import { FormInstance } from 'element-plus';
import { Rules } from '@/global/form-rules';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { checkDomain, checkIpV4V6 } from '@/utils/util';
const emit = defineEmits<{ (e: 'search'): void }>();
@@ -47,6 +54,18 @@ const form = reactive({
});
const formRef = ref<FormInstance>();
const rules = reactive({
systemIP: [{ validator: checkSystemIP, trigger: 'blur' }],
});
function checkSystemIP(rule: any, value: any, callback: any) {
if (form.systemIP !== '') {
if (checkIpV4V6(form.systemIP) && checkDomain(form.systemIP)) {
return callback(new Error(i18n.global.t('commons.rule.host')));
}
}
callback();
}
const acceptParams = (params: DialogProps): void => {
form.systemIP = params.systemIP;
@@ -234,16 +234,13 @@ const submit = async (formEl: FormInstance | undefined) => {
};
const getProtocolAndHost = (url: string): { protocol: string; host: string } | null => {
const regex = /^(https?:\/\/)([^\/]+)/;
const match = url.match(regex);
if (match) {
return {
protocol: match[1],
host: match[2],
};
if (url.startsWith('https://')) {
return { protocol: 'https://', host: url.replaceAll('https://', '') };
}
console.log('err');
return null;
if (url.startsWith('http://')) {
return { protocol: 'http://', host: url.replaceAll('http://', '') };
}
return { protocol: '', host: url };
};
defineExpose({
@@ -5,28 +5,28 @@
{{ $t('commons.button.create') + $t('website.redirect') }}
</el-button>
</template>
<el-table-column :label="$t('commons.table.name')" prop="name"></el-table-column>
<el-table-column :label="$t('website.sourceDomain')" prop="domain" min-width="150px">
<el-table-column :label="$t('commons.table.name')" prop="name" min-width="60px" show-overflow-tooltip />
<el-table-column :label="$t('website.sourceDomain')" prop="domain" min-width="80px" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.type === 'domain'">{{ row.domains.join(',') }}</span>
<span v-else>{{ row.path }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.type')" prop="type" width="80px">
<el-table-column :label="$t('commons.table.type')" prop="type" min-width="60px">
<template #default="{ row }">
<span v-if="row.type != 404">{{ $t('website.' + row.type) }}</span>
<span v-else>{{ 404 }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('website.redirectWay')" prop="redirect" width="80px"></el-table-column>
<el-table-column :label="$t('website.targetURL')" prop="target"></el-table-column>
<el-table-column :label="$t('website.keepPath')" prop="keepPath">
<el-table-column :label="$t('website.redirectWay')" prop="redirect" min-width="50px"></el-table-column>
<el-table-column :label="$t('website.targetURL')" prop="target" min-width="100px" show-overflow-tooltip />
<el-table-column :label="$t('website.keepPath')" prop="keepPath" min-width="80px" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row.type != '404'">{{ row.keepPath ? $t('website.keep') : $t('website.notKeep') }}</span>
<span v-else></span>
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.status')" prop="enable">
<el-table-column :label="$t('commons.table.status')" prop="enable" min-width="50px">
<template #default="{ row }">
<el-button v-if="row.enable" link type="success" :icon="VideoPlay" @click="opProxy(row)">
{{ $t('commons.status.running') }}
@@ -38,7 +38,7 @@
</el-table-column>
<fu-table-operations
:ellipsis="10"
width="220px"
width="180px"
:buttons="buttons"
:label="$t('commons.table.operate')"
:fixed="mobile ? false : 'right'"