mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 16:00:29 +00:00
Compare commits
28
Commits
v2.2.1
...
release-1.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7145d4ca07 | ||
|
|
9683a23fda | ||
|
|
5e182d84d0 | ||
|
|
9800e7e03d | ||
|
|
cbf4f8ec36 | ||
|
|
3e3b1f2c56 | ||
|
|
e5a7f104f1 | ||
|
|
17062da52e | ||
|
|
512c7daeee | ||
|
|
ff120c421e | ||
|
|
4d5993c6ab | ||
|
|
627e1e3755 | ||
|
|
0eee2d9807 | ||
|
|
f0e78c4b7c | ||
|
|
6da64f2c8f | ||
|
|
d572c28d1c | ||
|
|
7fac0fcbe8 | ||
|
|
cb1b6742d4 | ||
|
|
27bd4a63ef | ||
|
|
62d628c1aa | ||
|
|
06f81fc7f7 | ||
|
|
23c72c8301 | ||
|
|
280e74e01b | ||
|
|
b7dada93da | ||
|
|
9447936cc3 | ||
|
|
5e7429cd51 | ||
|
|
7a3716a363 | ||
|
|
5e38723cc1 |
@@ -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
|
||||
|
||||
@@ -67,7 +67,8 @@ type LocalAppInstallDefine struct {
|
||||
}
|
||||
|
||||
type ExtraProperties struct {
|
||||
Tags []Tag `json:"tags"`
|
||||
Tags []Tag `json:"tags"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type AppProperty struct {
|
||||
|
||||
@@ -668,6 +668,21 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||
res.CanUpdate = true
|
||||
return res, err
|
||||
}
|
||||
if err = getAppFromRepo(fmt.Sprintf("%s/%s/1panel.json.zip", global.CONF.System.AppRepo, global.CONF.System.Mode)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listFile := path.Join(constant.ResourceDir, "1panel.json")
|
||||
content, err := os.ReadFile(listFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := &dto.AppList{}
|
||||
if err = json.Unmarshal(content, list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if list.Extra.Version != "" && setting.SystemVersion != list.Extra.Version && !common.CompareVersion(setting.SystemVersion, list.Extra.Version) {
|
||||
return nil, buserr.New("ErrVersionTooLow")
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -766,7 +781,8 @@ func (a AppService) SyncAppListFromRemote() (err error) {
|
||||
version := v.Name
|
||||
detail := detailsMap[version]
|
||||
|
||||
dockerComposeUrl := fmt.Sprintf("%s/%s/%s/%s", baseRemoteUrl, app.Key, version, "docker-compose.yml")
|
||||
remoteVersionUrl := fmt.Sprintf("%s/%s/%s", baseRemoteUrl, app.Key, version)
|
||||
dockerComposeUrl := fmt.Sprintf("%s/%s", remoteVersionUrl, "docker-compose.yml")
|
||||
composeRes, err := http.Get(dockerComposeUrl)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -779,7 +795,7 @@ func (a AppService) SyncAppListFromRemote() (err error) {
|
||||
|
||||
paramByte, _ := json.Marshal(v.AppForm)
|
||||
detail.Params = string(paramByte)
|
||||
detail.DownloadUrl = v.DownloadUrl
|
||||
detail.DownloadUrl = fmt.Sprintf("%s/%s", remoteVersionUrl, version+".tar.gz")
|
||||
detail.DownloadCallBackUrl = v.DownloadCallBackUrl
|
||||
detail.Update = true
|
||||
detail.LastModified = v.LastModified
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,32 +368,35 @@ 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 {
|
||||
var datas []response.ProcessStatus
|
||||
statuLines, err := cmd.Exec("supervisorctl status")
|
||||
if err != nil {
|
||||
return datas
|
||||
func (h *HostToolService) LoadProcessStatus() ([]response.ProcessStatus, error) {
|
||||
var res []response.ProcessStatus
|
||||
statusLines, _ := cmd.Exec("supervisorctl status")
|
||||
if len(statusLines) == 0 {
|
||||
return res, nil
|
||||
}
|
||||
lines := strings.Split(string(statuLines), "\n")
|
||||
lines := strings.Split(statusLines, "\n")
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) > 1 {
|
||||
datas = append(datas, response.ProcessStatus{Name: fields[0]})
|
||||
res = append(res, response.ProcessStatus{Name: fields[0]})
|
||||
}
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(datas))
|
||||
for i := 0; i < len(datas); i++ {
|
||||
wg.Add(len(res))
|
||||
for i := 0; i < len(res); 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", res[index].Name), 2*time.Second)
|
||||
if err != nil {
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
@@ -397,26 +406,25 @@ func (h *HostToolService) LoadProcessStatus() []response.ProcessStatus {
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
datas[index].Name = fields[0]
|
||||
datas[index].Status = fields[1]
|
||||
res[index].Name = fields[0]
|
||||
res[index].Status = fields[1]
|
||||
if fields[1] != "RUNNING" {
|
||||
datas[index].Msg = strings.Join(fields[2:], " ")
|
||||
time.Sleep(1 * time.Second)
|
||||
continue
|
||||
res[index].Msg = strings.Join(fields[2:], " ")
|
||||
break
|
||||
}
|
||||
datas[index].PID = strings.TrimSuffix(fields[3], ",")
|
||||
datas[index].Uptime = fields[5]
|
||||
res[index].PID = strings.TrimSuffix(fields[3], ",")
|
||||
res[index].Uptime = fields[5]
|
||||
break
|
||||
}
|
||||
if len(datas[index].Status) == 0 {
|
||||
datas[index].Status = "FATAL"
|
||||
datas[index].Msg = "Timeout for getting process status"
|
||||
if len(res[index].Status) == 0 {
|
||||
res[index].Status = "FATAL"
|
||||
res[index].Msg = "Timeout for getting process status"
|
||||
}
|
||||
wg.Done()
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
return datas
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *HostToolService) GetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -46,6 +46,7 @@ ErrContainerNotFound: '{{ .name }} container does not exist'
|
||||
ErrContainerMsg: '{{ .name }} container is abnormal, please check the log on the container page for details'
|
||||
ErrAppBackup: '{{ .name }} application backup failed err {{.err}}'
|
||||
ErrImagePull: '{{ .name }} image pull failed err {{.err}}'
|
||||
ErrVersionTooLow: 'The current 1Panel version is too low to update the app store, please upgrade the version'
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "File can not read"
|
||||
|
||||
@@ -46,6 +46,7 @@ ErrContainerNotFound: '{{ .name }} 容器不存在'
|
||||
ErrContainerMsg: '{{ .name }} 容器異常,具體請在容器頁面查看日誌'
|
||||
ErrAppBackup: '{{ .name }} 應用備份失敗 err {{.err}}'
|
||||
ErrImagePull: '{{ .name }} 鏡像拉取失敗 err {{.err}}'
|
||||
ErrVersionTooLow: '當前 1Panel 版本過低,無法更新應用商店,請升級版本之後操作'
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "此文件不支持預覽"
|
||||
|
||||
@@ -46,6 +46,7 @@ ErrContainerNotFound: '{{ .name }} 容器不存在'
|
||||
ErrContainerMsg: '{{ .name }} 容器异常,具体请在容器页面查看日志'
|
||||
ErrAppBackup: '{{ .name }} 应用备份失败 err {{.err}}'
|
||||
ErrImagePull: '镜像拉取失败 {{.err}}'
|
||||
ErrVersionTooLow: '当前 1Panel 版本过低,无法更新应用商店,请升级版本之后操作'
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "此文件不支持预览"
|
||||
|
||||
@@ -518,6 +518,13 @@ func (f FileOp) Decompress(srcFile string, dst string, cType CompressType) error
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
parentDir := path.Dir(filePath)
|
||||
if !f.Stat(parentDir) {
|
||||
if err := f.Fs.MkdirAll(parentDir, info.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
fr, err := archFile.Open()
|
||||
if err != nil {
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/mysql/client"
|
||||
)
|
||||
|
||||
@@ -28,9 +27,6 @@ type MysqlClient interface {
|
||||
|
||||
func NewMysqlClient(conn client.DBInfo) (MysqlClient, error) {
|
||||
if conn.From == "local" {
|
||||
if cmd.CheckIllegal(conn.Address, conn.Username, conn.Password) {
|
||||
return nil, buserr.New(constant.ErrCmdIllegal)
|
||||
}
|
||||
connArgs := []string{"exec", conn.Address, "mysql", "-u" + conn.Username, "-p" + conn.Password, "-e"}
|
||||
return client.NewLocal(connArgs, conn.Address, conn.Password, conn.From), nil
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -3,11 +3,12 @@ package cmd
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/cmd/server/app"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -31,6 +32,10 @@ var initCmd = &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "初始化应用",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !isRoot() {
|
||||
fmt.Println("请使用 sudo 1pctl app init 或者切换到 root 用户")
|
||||
return nil
|
||||
}
|
||||
if len(args) > 0 {
|
||||
appKey = args[0]
|
||||
if len(args) > 1 {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -22,6 +24,10 @@ var resetMFACmd = &cobra.Command{
|
||||
Use: "mfa",
|
||||
Short: "取消 1Panel 两步验证",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !isRoot() {
|
||||
fmt.Println("请使用 sudo 1pctl reset mfa 或者切换到 root 用户")
|
||||
return nil
|
||||
}
|
||||
db, err := loadDBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -34,6 +40,10 @@ var resetSSLCmd = &cobra.Command{
|
||||
Use: "https",
|
||||
Short: "取消 1Panel https 方式登录",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !isRoot() {
|
||||
fmt.Println("请使用 sudo 1pctl reset https 或者切换到 root 用户")
|
||||
return nil
|
||||
}
|
||||
db, err := loadDBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -46,6 +56,10 @@ var resetEntranceCmd = &cobra.Command{
|
||||
Use: "entrance",
|
||||
Short: "取消 1Panel 安全入口",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !isRoot() {
|
||||
fmt.Println("请使用 sudo 1pctl reset entrance 或者切换到 root 用户")
|
||||
return nil
|
||||
}
|
||||
db, err := loadDBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -58,6 +72,10 @@ var resetBindIpsCmd = &cobra.Command{
|
||||
Use: "ips",
|
||||
Short: "取消 1Panel 授权 IP 限制",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !isRoot() {
|
||||
fmt.Println("请使用 sudo 1pctl reset ips 或者切换到 root 用户")
|
||||
return nil
|
||||
}
|
||||
db, err := loadDBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -70,6 +88,10 @@ var resetDomainCmd = &cobra.Command{
|
||||
Use: "domain",
|
||||
Short: "取消 1Panel 访问域名绑定",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !isRoot() {
|
||||
fmt.Println("请使用 sudo 1pctl reset domain 或者切换到 root 用户")
|
||||
return nil
|
||||
}
|
||||
db, err := loadDBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -2,6 +2,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/user"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -61,3 +62,11 @@ func getSettingByKey(db *gorm.DB, key string) string {
|
||||
func setSettingByKey(db *gorm.DB, key, value string) error {
|
||||
return db.Model(&setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error
|
||||
}
|
||||
|
||||
func isRoot() bool {
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return currentUser.Uid == "0"
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@ var userinfoCmd = &cobra.Command{
|
||||
Use: "user-info",
|
||||
Short: "获取用户信息",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !isRoot() {
|
||||
fmt.Println("请使用 sudo 1pctl user-info 或者切换到 root 用户")
|
||||
return nil
|
||||
}
|
||||
db, err := loadDBConn()
|
||||
if err != nil {
|
||||
return fmt.Errorf("init my db conn failed, err: %v \n", err)
|
||||
|
||||
@@ -18,6 +18,10 @@ var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "获取系统版本信息",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !isRoot() {
|
||||
fmt.Println("请使用 sudo 1pctl version 或者切换到 root 用户")
|
||||
return nil
|
||||
}
|
||||
db, err := loadDBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+1
-1
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 配置',
|
||||
|
||||
@@ -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 配置',
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
}
|
||||
}
|
||||
&.is-active {
|
||||
background-color: var(--el-menu-item-bg-color-active);;
|
||||
background-color: var(--el-menu-item-bg-color-active);
|
||||
border: 2px solid $primary-color;
|
||||
|
||||
&::before {
|
||||
@@ -81,3 +81,7 @@
|
||||
padding: 4px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-sub-menu__title {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -181,7 +181,7 @@ html {
|
||||
|
||||
.mask-prompt {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
z-index: 9998;
|
||||
top: 220px;
|
||||
left: 45%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -51,7 +51,9 @@
|
||||
</el-table-column>
|
||||
<el-table-column width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag effect="dark" round v-if="row.isSystem">system</el-tag>
|
||||
<el-tag effect="dark" round v-if="row.isSystem || row.name === '1panel-network'">
|
||||
system
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -78,11 +80,12 @@
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="createdAt"
|
||||
show-overflow-tooltip
|
||||
min-width="90"
|
||||
:label="$t('commons.table.date')"
|
||||
:formatter="dateFormat"
|
||||
/>
|
||||
<fu-table-operations :buttons="buttons" :label="$t('commons.table.operate')" fix />
|
||||
<fu-table-operations width="100" :buttons="buttons" :label="$t('commons.table.operate')" fix />
|
||||
</ComplexTable>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
@@ -222,7 +225,7 @@ const onInspect = async (id: string) => {
|
||||
};
|
||||
|
||||
function isSystem(val: string) {
|
||||
return val === 'bridge' || val === '1panel-network' || val === 'none' || val === 'host';
|
||||
return val === 'bridge' || val === 'none' || val === 'host';
|
||||
}
|
||||
|
||||
const buttons = [
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -374,9 +374,10 @@ const onLoadCurrentInfo = async () => {
|
||||
const res = await loadCurrentInfo(searchInfo.ioOption, searchInfo.netOption);
|
||||
currentInfo.value.timeSinceUptime = res.data.timeSinceUptime;
|
||||
|
||||
let timeInterval = Number(res.data.uptime - currentInfo.value.uptime) || 3;
|
||||
currentChartInfo.netBytesSent =
|
||||
res.data.netBytesSent - currentInfo.value.netBytesSent > 0
|
||||
? Number(((res.data.netBytesSent - currentInfo.value.netBytesSent) / 1024 / 3).toFixed(2))
|
||||
? Number(((res.data.netBytesSent - currentInfo.value.netBytesSent) / 1024 / timeInterval).toFixed(2))
|
||||
: 0;
|
||||
netBytesSents.value.push(currentChartInfo.netBytesSent);
|
||||
if (netBytesSents.value.length > 20) {
|
||||
@@ -385,7 +386,7 @@ const onLoadCurrentInfo = async () => {
|
||||
|
||||
currentChartInfo.netBytesRecv =
|
||||
res.data.netBytesRecv - currentInfo.value.netBytesRecv > 0
|
||||
? Number(((res.data.netBytesRecv - currentInfo.value.netBytesRecv) / 1024 / 3).toFixed(2))
|
||||
? Number(((res.data.netBytesRecv - currentInfo.value.netBytesRecv) / 1024 / timeInterval).toFixed(2))
|
||||
: 0;
|
||||
netBytesRecvs.value.push(currentChartInfo.netBytesRecv);
|
||||
if (netBytesRecvs.value.length > 20) {
|
||||
@@ -394,7 +395,7 @@ const onLoadCurrentInfo = async () => {
|
||||
|
||||
currentChartInfo.ioReadBytes =
|
||||
res.data.ioReadBytes - currentInfo.value.ioReadBytes > 0
|
||||
? Number(((res.data.ioReadBytes - currentInfo.value.ioReadBytes) / 1024 / 1024 / 3).toFixed(2))
|
||||
? Number(((res.data.ioReadBytes - currentInfo.value.ioReadBytes) / 1024 / 1024 / timeInterval).toFixed(2))
|
||||
: 0;
|
||||
ioReadBytes.value.push(currentChartInfo.ioReadBytes);
|
||||
if (ioReadBytes.value.length > 20) {
|
||||
@@ -403,17 +404,17 @@ const onLoadCurrentInfo = async () => {
|
||||
|
||||
currentChartInfo.ioWriteBytes =
|
||||
res.data.ioWriteBytes - currentInfo.value.ioWriteBytes > 0
|
||||
? Number(((res.data.ioWriteBytes - currentInfo.value.ioWriteBytes) / 1024 / 1024 / 3).toFixed(2))
|
||||
? Number(((res.data.ioWriteBytes - currentInfo.value.ioWriteBytes) / 1024 / 1024 / timeInterval).toFixed(2))
|
||||
: 0;
|
||||
ioWriteBytes.value.push(currentChartInfo.ioWriteBytes);
|
||||
if (ioWriteBytes.value.length > 20) {
|
||||
ioWriteBytes.value.splice(0, 1);
|
||||
}
|
||||
currentChartInfo.ioCount = Math.round(Number((res.data.ioCount - currentInfo.value.ioCount) / 3));
|
||||
currentChartInfo.ioCount = Math.round(Number((res.data.ioCount - currentInfo.value.ioCount) / timeInterval));
|
||||
let ioReadTime = res.data.ioReadTime - currentInfo.value.ioReadTime;
|
||||
let ioWriteTime = res.data.ioWriteTime - currentInfo.value.ioWriteTime;
|
||||
let ioChoose = ioReadTime > ioWriteTime ? ioReadTime : ioWriteTime;
|
||||
currentChartInfo.ioTime = Math.round(Number(ioChoose / 3));
|
||||
currentChartInfo.ioTime = Math.round(Number(ioChoose / timeInterval));
|
||||
|
||||
timeIODatas.value.push(dateFormatForSecond(res.data.shotTime));
|
||||
if (timeIODatas.value.length > 20) {
|
||||
|
||||
@@ -247,7 +247,7 @@ const onDelete = async (row: Host.RuleIP | null) => {
|
||||
}
|
||||
}
|
||||
loading.value = true;
|
||||
await batchOperateRule({ type: 'port', rules: rules })
|
||||
await batchOperateRule({ type: 'address', rules: rules })
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
@search="search"
|
||||
:data="data"
|
||||
>
|
||||
<el-table-column type="selection" :selectable="selectable" fix />
|
||||
<el-table-column type="selection" fix />
|
||||
<el-table-column :label="$t('commons.table.protocol')" :min-width="90" prop="protocol" />
|
||||
<el-table-column :label="$t('commons.table.port')" :min-width="120" prop="port" />
|
||||
<el-table-column :label="$t('commons.table.status')" :min-width="120">
|
||||
@@ -87,7 +87,6 @@
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="row.strategy === 'accept'"
|
||||
:disabled="row.appName === '1panel'"
|
||||
@click="onChangeStatus(row, 'drop')"
|
||||
link
|
||||
type="success"
|
||||
@@ -303,28 +302,18 @@ const onDelete = async (row: Host.RuleInfo | null) => {
|
||||
});
|
||||
};
|
||||
|
||||
function selectable(row) {
|
||||
return row.appName !== '1panel';
|
||||
}
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('commons.button.edit'),
|
||||
click: (row: Host.RulePort) => {
|
||||
onOpenDialog('edit', row);
|
||||
},
|
||||
disabled: (row: any) => {
|
||||
return row.appName === '1panel';
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
click: (row: Host.RuleInfo) => {
|
||||
onDelete(row);
|
||||
},
|
||||
disabled: (row: any) => {
|
||||
return row.appName === '1panel';
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -184,6 +184,7 @@ const setting = () => {
|
||||
|
||||
const getStatus = (status: any) => {
|
||||
supervisorStatus.value = status;
|
||||
search();
|
||||
};
|
||||
|
||||
const showStopped = computed((): boolean => {
|
||||
@@ -211,6 +212,9 @@ const openCreate = () => {
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
if (!supervisorStatus.value.isExist) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
loadStatus();
|
||||
try {
|
||||
@@ -221,22 +225,25 @@ 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);
|
||||
}
|
||||
}
|
||||
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'"
|
||||
|
||||
Reference in New Issue
Block a user