1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +08:00

fix: 解决打开越权读取文件的问题 (#1810)

This commit is contained in:
ssongliu 2023-08-02 16:47:30 +08:00 committed by GitHub
parent 01d5bd047f
commit 6f6c836d9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 705 additions and 213 deletions

View File

@ -355,6 +355,28 @@ func (b *BaseApi) CleanContainerLog(c *gin.Context) {
helper.SuccessWithData(c, nil) helper.SuccessWithData(c, nil)
} }
// @Tags Container
// @Summary Load container log
// @Description 获取容器操作日志
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/load/log [post]
func (b *BaseApi) LoadContainerLog(c *gin.Context) {
var req dto.OperationWithNameAndType
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
content, err := containerService.LoadContainerLogs(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, content)
}
// @Tags Container // @Tags Container
// @Summary Operate Container // @Summary Operate Container
// @Description 容器操作 // @Description 容器操作

View File

@ -94,6 +94,28 @@ func (b *BaseApi) SearchJobRecords(c *gin.Context) {
}) })
} }
// @Tags Cronjob
// @Summary Load Cronjob record log
// @Description 获取计划任务记录日志
// @Accept json
// @Param request body dto.OperateByID true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /cronjob/record/log [post]
func (b *BaseApi) LoadRecordLog(c *gin.Context) {
var req dto.OperateByID
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
content, err := cronjobService.LoadRecordLog(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, content)
}
// @Tags Cronjob // @Tags Cronjob
// @Summary Clean job records // @Summary Clean job records
// @Description 清空计划任务记录 // @Description 清空计划任务记录

View File

@ -321,6 +321,28 @@ func (b *BaseApi) LoadBaseinfo(c *gin.Context) {
helper.SuccessWithData(c, data) helper.SuccessWithData(c, data)
} }
// @Tags Database
// @Summary Load Database file
// @Description 获取数据库文件
// @Accept json
// @Param request body dto.OperationWithNameAndType true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /databases/load/file [post]
func (b *BaseApi) LoadDatabaseFile(c *gin.Context) {
var req dto.OperationWithNameAndType
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
content, err := mysqlService.LoadDatabaseFile(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, content)
}
// @Tags Database Mysql // @Tags Database Mysql
// @Summary Load mysql remote access // @Summary Load mysql remote access
// @Description 获取 mysql 远程访问权限 // @Description 获取 mysql 远程访问权限

View File

@ -589,33 +589,6 @@ func (b *BaseApi) Size(c *gin.Context) {
helper.SuccessWithData(c, res) helper.SuccessWithData(c, res)
} }
// @Tags File
// @Summary Read file
// @Description 读取文件
// @Accept json
// @Param request body dto.FilePath true "request"
// @Success 200 {string} content
// @Security ApiKeyAuth
// @Router /files/loadfile [post]
func (b *BaseApi) LoadFromFile(c *gin.Context) {
var req dto.FilePath
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
content, err := os.ReadFile(req.Path)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, string(content))
}
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error { func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
if _, err := os.Stat(path.Dir(dstDir)); err != nil && os.IsNotExist(err) { if _, err := os.Stat(path.Dir(dstDir)); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(path.Dir(dstDir), os.ModePerm); err != nil { if err = os.MkdirAll(path.Dir(dstDir), os.ModePerm); err != nil {

View File

@ -89,3 +89,19 @@ func (b *BaseApi) CleanLogs(c *gin.Context) {
helper.SuccessWithData(c, nil) helper.SuccessWithData(c, nil)
} }
// @Tags Logs
// @Summary Load system logs
// @Description 获取系统日志
// @Success 200
// @Security ApiKeyAuth
// @Router /logs/system [get]
func (b *BaseApi) GetSystemLogs(c *gin.Context) {
data, err := logService.LoadSystemLog()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}

View File

@ -183,3 +183,18 @@ func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
} }
helper.SuccessWithData(c, data) helper.SuccessWithData(c, data)
} }
// @Tags SSH
// @Summary Load host ssh conf
// @Description 获取 ssh 配置文件
// @Success 200
// @Security ApiKeyAuth
// @Router /host/ssh/conf [get]
func (b *BaseApi) LoadSSHConf(c *gin.Context) {
data, err := sshService.LoadSSHConf()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}

View File

@ -7,6 +7,7 @@ import (
"io" "io"
"os" "os"
"os/exec" "os/exec"
"path"
"path/filepath" "path/filepath"
"sort" "sort"
"strconv" "strconv"
@ -63,6 +64,8 @@ type IContainerService interface {
TestCompose(req dto.ComposeCreate) (bool, error) TestCompose(req dto.ComposeCreate) (bool, error)
ComposeUpdate(req dto.ComposeUpdate) error ComposeUpdate(req dto.ComposeUpdate) error
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error) Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error)
LoadContainerLogs(req dto.OperationWithNameAndType) (string, error)
} }
func NewIContainerService() IContainerService { func NewIContainerService() IContainerService {
@ -182,7 +185,7 @@ func (u *ContainerService) List() ([]string, error) {
for _, container := range containers { for _, container := range containers {
for _, name := range container.Names { for _, name := range container.Names {
if len(name) != 0 { if len(name) != 0 {
datas = append(datas, strings.TrimLeft(name, "/")) datas = append(datas, strings.TrimPrefix(name, "/"))
} }
} }
} }
@ -625,6 +628,48 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
return &data, nil return &data, nil
} }
func (u *ContainerService) LoadContainerLogs(req dto.OperationWithNameAndType) (string, error) {
filePath := ""
switch req.Type {
case "image-pull", "image-push", "image-build":
filePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
case "compose-detail", "compose-create":
client, err := docker.NewDockerClient()
if err != nil {
return "", err
}
options := types.ContainerListOptions{All: true}
options.Filters = filters.NewArgs()
options.Filters.Add("label", fmt.Sprintf("%s=%s", composeProjectLabel, req.Name))
containers, err := client.ContainerList(context.Background(), options)
if err != nil {
return "", err
}
for _, container := range containers {
config := container.Labels[composeConfigLabel]
workdir := container.Labels[composeWorkdirLabel]
if len(config) != 0 && len(workdir) != 0 && strings.Contains(config, workdir) {
filePath = config
break
} else {
filePath = workdir
break
}
}
if req.Type == "compose-create" {
filePath = path.Join(path.Dir(filePath), "/compose.log")
}
}
if _, err := os.Stat(filePath); err != nil {
return "", buserr.New("ErrHttpReqNotFound")
}
content, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(content), nil
}
func stringsToMap(list []string) map[string]string { func stringsToMap(list []string) map[string]string {
var lableMap = make(map[string]string) var lableMap = make(map[string]string)
for _, label := range list { for _, label := range list {

View File

@ -157,7 +157,7 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
global.LOG.Infof("docker-compose.yml %s create successful, start to docker-compose up", req.Name) global.LOG.Infof("docker-compose.yml %s create successful, start to docker-compose up", req.Name)
if req.From == "path" { if req.From == "path" {
req.Name = path.Base(strings.ReplaceAll(req.Path, "/"+path.Base(req.Path), "")) req.Name = path.Base(path.Dir(req.Path))
} }
logName := path.Dir(req.Path) + "/compose.log" logName := path.Dir(req.Path) + "/compose.log"
file, err := os.OpenFile(logName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) file, err := os.OpenFile(logName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
@ -181,7 +181,7 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error)
_, _ = file.WriteString("docker-compose up successful!") _, _ = file.WriteString("docker-compose up successful!")
}() }()
return logName, nil return req.Name, nil
} }
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error { func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {

View File

@ -9,6 +9,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
@ -29,6 +30,8 @@ type ICronjobService interface {
Download(down dto.CronjobDownload) (string, error) Download(down dto.CronjobDownload) (string, error)
StartJob(cronjob *model.Cronjob) (int, error) StartJob(cronjob *model.Cronjob) (int, error)
CleanRecord(req dto.CronjobClean) error CleanRecord(req dto.CronjobClean) error
LoadRecordLog(req dto.OperateByID) (string, error)
} }
func NewICronjobService() ICronjobService { func NewICronjobService() ICronjobService {
@ -80,6 +83,21 @@ func (u *CronjobService) SearchRecords(search dto.SearchRecord) (int64, interfac
return total, dtoCronjobs, err return total, dtoCronjobs, err
} }
func (u *CronjobService) LoadRecordLog(req dto.OperateByID) (string, error) {
record, err := cronjobRepo.GetRecord(commonRepo.WithByID(req.ID))
if err != nil {
return "", err
}
if _, err := os.Stat(record.Records); err != nil {
return "", buserr.New("ErrHttpReqNotFound")
}
content, err := os.ReadFile(record.Records)
if err != nil {
return "", err
}
return string(content), nil
}
func (u *CronjobService) CleanRecord(req dto.CronjobClean) error { func (u *CronjobService) CleanRecord(req dto.CronjobClean) error {
cronjob, err := cronjobRepo.Get(commonRepo.WithByID(req.CronjobID)) cronjob, err := cronjobRepo.Get(commonRepo.WithByID(req.CronjobID))
if err != nil { if err != nil {

View File

@ -46,6 +46,8 @@ type IMysqlService interface {
LoadVariables() (*dto.MysqlVariables, error) LoadVariables() (*dto.MysqlVariables, error)
LoadBaseInfo() (*dto.DBBaseInfo, error) LoadBaseInfo() (*dto.DBBaseInfo, error)
LoadRemoteAccess() (bool, error) LoadRemoteAccess() (bool, error)
LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error)
} }
func NewIMysqlService() IMysqlService { func NewIMysqlService() IMysqlService {
@ -514,6 +516,26 @@ func (u *MysqlService) LoadStatus() (*dto.MysqlStatus, error) {
return &info, nil return &info, nil
} }
func (u *MysqlService) LoadDatabaseFile(req dto.OperationWithNameAndType) (string, error) {
filePath := ""
switch req.Type {
case "mysql-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/conf/my.cnf", req.Name))
case "redis-conf":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/redis/%s/conf/redis.conf", req.Name))
case "slow-logs":
filePath = path.Join(global.CONF.System.DataDir, fmt.Sprintf("apps/mysql/%s/data/1Panel-slow.log", req.Name))
}
if _, err := os.Stat(filePath); err != nil {
return "", buserr.New("ErrHttpReqNotFound")
}
content, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(content), nil
}
func excuteSqlForMaps(containerName, password, command string) (map[string]string, error) { func excuteSqlForMaps(containerName, password, command string) (map[string]string, error) {
cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command) cmd := exec.Command("docker", "exec", containerName, "mysql", "-uroot", "-p"+password, "-e", command)
stdout, err := cmd.CombinedOutput() stdout, err := cmd.CombinedOutput()

View File

@ -136,14 +136,14 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
switch req.Key { switch req.Key {
case "Registries": case "Registries":
req.Value = strings.TrimRight(req.Value, ",") req.Value = strings.TrimSuffix(req.Value, ",")
if len(req.Value) == 0 { if len(req.Value) == 0 {
delete(daemonMap, "insecure-registries") delete(daemonMap, "insecure-registries")
} else { } else {
daemonMap["insecure-registries"] = strings.Split(req.Value, ",") daemonMap["insecure-registries"] = strings.Split(req.Value, ",")
} }
case "Mirrors": case "Mirrors":
req.Value = strings.TrimRight(req.Value, ",") req.Value = strings.TrimSuffix(req.Value, ",")
if len(req.Value) == 0 { if len(req.Value) == 0 {
delete(daemonMap, "registry-mirrors") delete(daemonMap, "registry-mirrors")
} else { } else {

View File

@ -123,6 +123,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
return "", err return "", err
} }
fileName := "Dockerfile" fileName := "Dockerfile"
dockerLogDir := path.Join(global.CONF.System.TmpDir, "/docker_logs")
if req.From == "edit" { if req.From == "edit" {
dir := fmt.Sprintf("%s/docker/build/%s", constant.DataDir, strings.ReplaceAll(req.Name, ":", "_")) dir := fmt.Sprintf("%s/docker/build/%s", constant.DataDir, strings.ReplaceAll(req.Name, ":", "_"))
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) { if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
@ -156,10 +157,9 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
Remove: true, Remove: true,
Labels: stringsToMap(req.Tags), Labels: stringsToMap(req.Tags),
} }
logName := fmt.Sprintf("%s/build.log", req.Dockerfile)
pathItem := logName logItem := fmt.Sprintf("%s/image_build_%s_%s.log", dockerLogDir, strings.ReplaceAll(req.Name, ":", "_"), time.Now().Format("20060102150405"))
file, err := os.OpenFile(pathItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -192,7 +192,7 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
_, _ = file.WriteString("image build successful!") _, _ = file.WriteString("image build successful!")
}() }()
return logName, nil return path.Base(logItem), nil
} }
func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) { func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
@ -200,15 +200,15 @@ func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
dockerLogDir := global.CONF.System.TmpDir + "/docker_logs" dockerLogDir := path.Join(global.CONF.System.TmpDir, "/docker_logs")
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) { if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil { if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
return "", err return "", err
} }
} }
imageItemName := strings.ReplaceAll(path.Base(req.ImageName), ":", "_") imageItemName := strings.ReplaceAll(path.Base(req.ImageName), ":", "_")
pathItem := fmt.Sprintf("%s/image_pull_%s_%s.log", dockerLogDir, imageItemName, time.Now().Format("20060102150405")) logItem := fmt.Sprintf("%s/image_pull_%s_%s.log", dockerLogDir, imageItemName, time.Now().Format("20060102150405"))
file, err := os.OpenFile(pathItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -224,7 +224,7 @@ func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
global.LOG.Infof("pull image %s successful!", req.ImageName) global.LOG.Infof("pull image %s successful!", req.ImageName)
_, _ = io.Copy(file, out) _, _ = io.Copy(file, out)
}() }()
return pathItem, nil return path.Base(logItem), nil
} }
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID)) repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
if err != nil { if err != nil {
@ -257,7 +257,7 @@ func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
_, _ = io.Copy(file, out) _, _ = io.Copy(file, out)
_, _ = file.WriteString("image pull successful!") _, _ = file.WriteString("image pull successful!")
}() }()
return pathItem, nil return path.Base(logItem), nil
} }
func (u *ImageService) ImageLoad(req dto.ImageLoad) error { func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
@ -354,8 +354,8 @@ func (u *ImageService) ImagePush(req dto.ImagePush) (string, error) {
} }
} }
imageItemName := strings.ReplaceAll(path.Base(req.Name), ":", "_") imageItemName := strings.ReplaceAll(path.Base(req.Name), ":", "_")
pathItem := fmt.Sprintf("%s/image_push_%s_%s.log", dockerLogDir, imageItemName, time.Now().Format("20060102150405")) logItem := fmt.Sprintf("%s/image_push_%s_%s.log", dockerLogDir, imageItemName, time.Now().Format("20060102150405"))
file, err := os.OpenFile(pathItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) file, err := os.OpenFile(logItem, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -373,7 +373,7 @@ func (u *ImageService) ImagePush(req dto.ImagePush) (string, error) {
_, _ = file.WriteString("image push successful!") _, _ = file.WriteString("image push successful!")
}() }()
return pathItem, nil return path.Base(logItem), nil
} }
func (u *ImageService) ImageRemove(req dto.BatchDelete) error { func (u *ImageService) ImageRemove(req dto.BatchDelete) error {

View File

@ -1,9 +1,14 @@
package service package service
import ( import (
"os"
"path"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd" "github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -20,6 +25,8 @@ type ILogService interface {
CreateOperationLog(operation model.OperationLog) error CreateOperationLog(operation model.OperationLog) error
PageOperationLog(search dto.SearchOpLogWithPage) (int64, interface{}, error) PageOperationLog(search dto.SearchOpLogWithPage) (int64, interface{}, error)
LoadSystemLog() (string, error)
CleanLogs(logtype string) error CleanLogs(logtype string) error
} }
@ -74,6 +81,18 @@ func (u *LogService) PageOperationLog(req dto.SearchOpLogWithPage) (int64, inter
return total, dtoOps, err return total, dtoOps, err
} }
func (u *LogService) LoadSystemLog() (string, error) {
filePath := path.Join(global.CONF.System.DataDir, "log/1Panel.log")
if _, err := os.Stat(filePath); err != nil {
return "", buserr.New("ErrHttpReqNotFound")
}
content, err := os.ReadFile(filePath)
if err != nil {
return "", err
}
return string(content), nil
}
func (u *LogService) CleanLogs(logtype string) error { func (u *LogService) CleanLogs(logtype string) error {
if logtype == "operation" { if logtype == "operation" {
return logRepo.CleanOperation() return logRepo.CleanOperation()

View File

@ -205,8 +205,8 @@ func (u *SnapshotService) SnapshotCreate(req dto.SnapshotCreate) error {
global.LOG.Infof("start to upload snapshot to %s, please wait", backup.Type) global.LOG.Infof("start to upload snapshot to %s, please wait", backup.Type)
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading}) _ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusUploading})
localPath := path.Join(localDir, fmt.Sprintf("system/1panel_%s_%s.tar.gz", versionItem.Value, timeNow)) localPath := path.Join(localDir, fmt.Sprintf("system/1panel_%s_%s.tar.gz", versionItem.Value, timeNow))
itemBackupPath := strings.TrimLeft(backup.BackupPath, "/") itemBackupPath := strings.TrimPrefix(backup.BackupPath, "/")
itemBackupPath = strings.TrimRight(itemBackupPath, "/") itemBackupPath = strings.TrimSuffix(itemBackupPath, "/")
if ok, err := backupAccount.Upload(localPath, fmt.Sprintf("%s/system_snapshot/1panel_%s_%s.tar.gz", itemBackupPath, versionItem.Value, timeNow)); err != nil || !ok { if ok, err := backupAccount.Upload(localPath, fmt.Sprintf("%s/system_snapshot/1panel_%s_%s.tar.gz", itemBackupPath, versionItem.Value, timeNow)); err != nil || !ok {
_ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()}) _ = snapshotRepo.Update(snap.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error()})
global.LOG.Errorf("upload snapshot to %s failed, err: %v", backup.Type, err) global.LOG.Errorf("upload snapshot to %s failed, err: %v", backup.Type, err)
@ -259,8 +259,8 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error {
operation = "re-recover" operation = "re-recover"
} }
if !isReTry || snap.InterruptStep == "Download" || (isReTry && req.ReDownload) { if !isReTry || snap.InterruptStep == "Download" || (isReTry && req.ReDownload) {
itemBackupPath := strings.TrimLeft(backup.BackupPath, "/") itemBackupPath := strings.TrimPrefix(backup.BackupPath, "/")
itemBackupPath = strings.TrimRight(itemBackupPath, "/") itemBackupPath = strings.TrimSuffix(itemBackupPath, "/")
ok, err := client.Download(fmt.Sprintf("%s/system_snapshot/%s.tar.gz", itemBackupPath, snap.Name), fmt.Sprintf("%s/%s.tar.gz", baseDir, snap.Name)) ok, err := client.Download(fmt.Sprintf("%s/system_snapshot/%s.tar.gz", itemBackupPath, snap.Name), fmt.Sprintf("%s/%s.tar.gz", baseDir, snap.Name))
if err != nil || !ok { if err != nil || !ok {
if req.ReDownload { if req.ReDownload {
@ -947,7 +947,10 @@ func (u *SnapshotService) handleTar(sourceDir, targetDir, name, exclusionRules s
global.LOG.Debug(commands) global.LOG.Debug(commands)
stdout, err := cmd.ExecWithTimeOut(commands, 30*time.Minute) stdout, err := cmd.ExecWithTimeOut(commands, 30*time.Minute)
if err != nil { if err != nil {
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err) if len(stdout) != 0 {
global.LOG.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
return fmt.Errorf("do handle tar failed, stdout: %s, err: %v", stdout, err)
}
} }
return nil return nil
} }
@ -963,7 +966,10 @@ func (u *SnapshotService) handleUnTar(sourceDir, targetDir string) error {
global.LOG.Debug(commands) global.LOG.Debug(commands)
stdout, err := cmd.ExecWithTimeOut(commands, 30*time.Minute) stdout, err := cmd.ExecWithTimeOut(commands, 30*time.Minute)
if err != nil { if err != nil {
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err) if len(stdout) != 0 {
global.LOG.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
return fmt.Errorf("do handle untar failed, stdout: %s, err: %v", stdout, err)
}
} }
return nil return nil
} }

View File

@ -32,6 +32,8 @@ type ISSHService interface {
GenerateSSH(req dto.GenerateSSH) error GenerateSSH(req dto.GenerateSSH) error
LoadSSHSecret(mode string) (string, error) LoadSSHSecret(mode string) (string, error)
LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error)
LoadSSHConf() (string, error)
} }
func NewISSHService() ISSHService { func NewISSHService() ISSHService {
@ -283,6 +285,17 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
return &data, nil return &data, nil
} }
func (u *SSHService) LoadSSHConf() (string, error) {
if _, err := os.Stat("/etc/ssh/sshd_config"); err != nil {
return "", buserr.New("ErrHttpReqNotFound")
}
content, err := os.ReadFile("/etc/ssh/sshd_config")
if err != nil {
return "", err
}
return string(content), nil
}
func sortFileList(fileNames []sshFileItem) []sshFileItem { func sortFileList(fileNames []sshFileItem) []sshFileItem {
if len(fileNames) < 2 { if len(fileNames) < 2 {
return fileNames return fileNames

View File

@ -493,7 +493,7 @@ var AddRemoteDB = &gormigrate.Migration{
appInstall model.AppInstall appInstall model.AppInstall
) )
if err := global.DB.Where("key = ?", "mysql").First(&app).Error; err != nil { if err := global.DB.Where("key = ?", "mysql").First(&app).Error; err != nil {
return err return nil
} }
if err := global.DB.Where("app_id = ?", app.ID).First(&appInstall).Error; err != nil { if err := global.DB.Where("app_id = ?", app.ID).First(&appInstall).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {

View File

@ -28,6 +28,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
baRouter.GET("/search/log", baseApi.ContainerLogs) baRouter.GET("/search/log", baseApi.ContainerLogs)
baRouter.GET("/limit", baseApi.LoadResouceLimit) baRouter.GET("/limit", baseApi.LoadResouceLimit)
baRouter.POST("/clean/log", baseApi.CleanContainerLog) baRouter.POST("/clean/log", baseApi.CleanContainerLog)
baRouter.POST("/load/log", baseApi.LoadContainerLog)
baRouter.POST("/inspect", baseApi.Inspect) baRouter.POST("/inspect", baseApi.Inspect)
baRouter.POST("/operate", baseApi.ContainerOperation) baRouter.POST("/operate", baseApi.ContainerOperation)
baRouter.POST("/prune", baseApi.ContainerPrune) baRouter.POST("/prune", baseApi.ContainerPrune)

View File

@ -24,6 +24,7 @@ func (s *CronjobRouter) InitCronjobRouter(Router *gin.RouterGroup) {
cmdRouter.POST("/download", baseApi.TargetDownload) cmdRouter.POST("/download", baseApi.TargetDownload)
cmdRouter.POST("/search", baseApi.SearchCronjob) cmdRouter.POST("/search", baseApi.SearchCronjob)
cmdRouter.POST("/search/records", baseApi.SearchJobRecords) cmdRouter.POST("/search/records", baseApi.SearchJobRecords)
cmdRouter.POST("/records/log", baseApi.LoadRecordLog)
cmdRouter.POST("/records/clean", baseApi.CleanRecord) cmdRouter.POST("/records/clean", baseApi.CleanRecord)
} }
} }

View File

@ -26,6 +26,7 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
cmdRouter.POST("/variables/update", baseApi.UpdateMysqlVariables) cmdRouter.POST("/variables/update", baseApi.UpdateMysqlVariables)
cmdRouter.POST("/conffile/update", baseApi.UpdateMysqlConfByFile) cmdRouter.POST("/conffile/update", baseApi.UpdateMysqlConfByFile)
cmdRouter.POST("/search", baseApi.SearchMysql) cmdRouter.POST("/search", baseApi.SearchMysql)
cmdRouter.POST("/load/file", baseApi.LoadDatabaseFile)
cmdRouter.GET("/variables", baseApi.LoadVariables) cmdRouter.GET("/variables", baseApi.LoadVariables)
cmdRouter.GET("/status", baseApi.LoadStatus) cmdRouter.GET("/status", baseApi.LoadStatus)
cmdRouter.GET("/baseinfo", baseApi.LoadBaseinfo) cmdRouter.GET("/baseinfo", baseApi.LoadBaseinfo)

View File

@ -38,7 +38,5 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) {
fileRouter.POST("/size", baseApi.Size) fileRouter.POST("/size", baseApi.Size)
fileRouter.GET("/ws", baseApi.Ws) fileRouter.GET("/ws", baseApi.Ws)
fileRouter.GET("/keys", baseApi.Keys) fileRouter.GET("/keys", baseApi.Keys)
fileRouter.POST("/loadfile", baseApi.LoadFromFile)
} }
} }

View File

@ -34,6 +34,7 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
hostRouter.POST("/firewall/update/port", baseApi.UpdatePortRule) hostRouter.POST("/firewall/update/port", baseApi.UpdatePortRule)
hostRouter.POST("/firewall/update/addr", baseApi.UpdateAddrRule) hostRouter.POST("/firewall/update/addr", baseApi.UpdateAddrRule)
hostRouter.GET("/ssh/conf", baseApi.LoadSSHConf)
hostRouter.POST("/ssh/search", baseApi.GetSSHInfo) hostRouter.POST("/ssh/search", baseApi.GetSSHInfo)
hostRouter.POST("/ssh/update", baseApi.UpdateSSH) hostRouter.POST("/ssh/update", baseApi.UpdateSSH)
hostRouter.POST("/ssh/generate", baseApi.GenerateSSH) hostRouter.POST("/ssh/generate", baseApi.GenerateSSH)

View File

@ -17,5 +17,7 @@ func (s *LogRouter) InitLogRouter(Router *gin.RouterGroup) {
operationRouter.POST("/login", baseApi.GetLoginLogs) operationRouter.POST("/login", baseApi.GetLoginLogs)
operationRouter.POST("/operation", baseApi.GetOperationLogs) operationRouter.POST("/operation", baseApi.GetOperationLogs)
operationRouter.POST("/clean", baseApi.CleanLogs) operationRouter.POST("/clean", baseApi.CleanLogs)
operationRouter.GET("/system", baseApi.GetSystemLogs)
} }
} }

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"path"
"strings" "strings"
"time" "time"
@ -214,7 +215,7 @@ func (r *Local) Backup(info BackupInfo) error {
return fmt.Errorf("mkdir %s failed, err: %v", info.TargetDir, err) return fmt.Errorf("mkdir %s failed, err: %v", info.TargetDir, err)
} }
} }
outfile, _ := os.OpenFile(info.FileName, os.O_RDWR|os.O_CREATE, 0755) outfile, _ := os.OpenFile(path.Join(info.TargetDir, info.FileName), os.O_RDWR|os.O_CREATE, 0755)
global.LOG.Infof("start to mysqldump | gzip > %s.gzip", info.TargetDir+"/"+info.FileName) global.LOG.Infof("start to mysqldump | gzip > %s.gzip", info.TargetDir+"/"+info.FileName)
cmd := exec.Command("docker", "exec", r.ContainerName, "mysqldump", "-uroot", "-p"+r.Password, info.Name) cmd := exec.Command("docker", "exec", r.ContainerName, "mysqldump", "-uroot", "-p"+r.Password, info.Name)
gzipCmd := exec.Command("gzip", "-cf") gzipCmd := exec.Command("gzip", "-cf")

View File

@ -1,5 +1,5 @@
// Package docs GENERATED BY SWAG; DO NOT EDIT // Code generated by swaggo/swag. DO NOT EDIT.
// This file was generated by swaggo/swag
package docs package docs
import "github.com/swaggo/swag" import "github.com/swaggo/swag"
@ -2005,6 +2005,39 @@ const docTemplate = `{
} }
} }
}, },
"/containers/load/log": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取容器操作日志",
"consumes": [
"application/json"
],
"tags": [
"Container"
],
"summary": "Load container log",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OperationWithNameAndType"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/containers/network": { "/containers/network": {
"get": { "get": {
"security": [ "security": [
@ -3058,6 +3091,39 @@ const docTemplate = `{
} }
} }
}, },
"/cronjob/record/log": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取计划任务记录日志",
"consumes": [
"application/json"
],
"tags": [
"Cronjob"
],
"summary": "Load Cronjob record log",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OperateByID"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/cronjobs": { "/cronjobs": {
"post": { "post": {
"security": [ "security": [
@ -3925,6 +3991,39 @@ const docTemplate = `{
"responses": {} "responses": {}
} }
}, },
"/databases/load/file": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取数据库文件",
"consumes": [
"application/json"
],
"tags": [
"Database"
],
"summary": "Load Database file",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OperationWithNameAndType"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/databases/options": { "/databases/options": {
"get": { "get": {
"security": [ "security": [
@ -5036,42 +5135,6 @@ const docTemplate = `{
} }
} }
}, },
"/files/loadfile": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "读取文件",
"consumes": [
"application/json"
],
"tags": [
"File"
],
"summary": "Read file",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.FilePath"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/files/mode": { "/files/mode": {
"post": { "post": {
"security": [ "security": [
@ -5748,6 +5811,25 @@ const docTemplate = `{
} }
} }
}, },
"/host/ssh/conf": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 ssh 配置文件",
"tags": [
"SSH"
],
"summary": "Load host ssh conf",
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/host/ssh/generate": { "/host/ssh/generate": {
"post": { "post": {
"security": [ "security": [
@ -6251,7 +6333,7 @@ const docTemplate = `{
"bodyKeys": [ "bodyKeys": [
"operate" "operate"
], ],
"formatEN": "[operate] [operate] Supervisor 进程文件", "formatEN": "[operate] Supervisor Process Config file",
"formatZH": "[operate] Supervisor 进程文件 ", "formatZH": "[operate] Supervisor 进程文件 ",
"paramKeys": [] "paramKeys": []
} }
@ -7188,6 +7270,25 @@ const docTemplate = `{
} }
} }
}, },
"/logs/system": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取系统日志",
"tags": [
"Logs"
],
"summary": "Load system logs",
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/openResty": { "/openResty": {
"get": { "get": {
"security": [ "security": [
@ -13291,6 +13392,21 @@ const docTemplate = `{
} }
} }
}, },
"dto.OperationWithNameAndType": {
"type": "object",
"required": [
"name",
"type"
],
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"dto.Options": { "dto.Options": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -15773,7 +15889,8 @@ const docTemplate = `{
"type": "string", "type": "string",
"enum": [ "enum": [
"out.log", "out.log",
"err.log" "err.log",
"config"
] ]
}, },
"name": { "name": {

View File

@ -1998,6 +1998,39 @@
} }
} }
}, },
"/containers/load/log": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取容器操作日志",
"consumes": [
"application/json"
],
"tags": [
"Container"
],
"summary": "Load container log",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OperationWithNameAndType"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/containers/network": { "/containers/network": {
"get": { "get": {
"security": [ "security": [
@ -3051,6 +3084,39 @@
} }
} }
}, },
"/cronjob/record/log": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取计划任务记录日志",
"consumes": [
"application/json"
],
"tags": [
"Cronjob"
],
"summary": "Load Cronjob record log",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OperateByID"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/cronjobs": { "/cronjobs": {
"post": { "post": {
"security": [ "security": [
@ -3918,6 +3984,39 @@
"responses": {} "responses": {}
} }
}, },
"/databases/load/file": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取数据库文件",
"consumes": [
"application/json"
],
"tags": [
"Database"
],
"summary": "Load Database file",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OperationWithNameAndType"
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/databases/options": { "/databases/options": {
"get": { "get": {
"security": [ "security": [
@ -5029,42 +5128,6 @@
} }
} }
}, },
"/files/loadfile": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "读取文件",
"consumes": [
"application/json"
],
"tags": [
"File"
],
"summary": "Read file",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.FilePath"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
}
}
},
"/files/mode": { "/files/mode": {
"post": { "post": {
"security": [ "security": [
@ -5741,6 +5804,25 @@
} }
} }
}, },
"/host/ssh/conf": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 ssh 配置文件",
"tags": [
"SSH"
],
"summary": "Load host ssh conf",
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/host/ssh/generate": { "/host/ssh/generate": {
"post": { "post": {
"security": [ "security": [
@ -6244,7 +6326,7 @@
"bodyKeys": [ "bodyKeys": [
"operate" "operate"
], ],
"formatEN": "[operate] [operate] Supervisor 进程文件", "formatEN": "[operate] Supervisor Process Config file",
"formatZH": "[operate] Supervisor 进程文件 ", "formatZH": "[operate] Supervisor 进程文件 ",
"paramKeys": [] "paramKeys": []
} }
@ -7181,6 +7263,25 @@
} }
} }
}, },
"/logs/system": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取系统日志",
"tags": [
"Logs"
],
"summary": "Load system logs",
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/openResty": { "/openResty": {
"get": { "get": {
"security": [ "security": [
@ -13284,6 +13385,21 @@
} }
} }
}, },
"dto.OperationWithNameAndType": {
"type": "object",
"required": [
"name",
"type"
],
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"dto.Options": { "dto.Options": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -15766,7 +15882,8 @@
"type": "string", "type": "string",
"enum": [ "enum": [
"out.log", "out.log",
"err.log" "err.log",
"config"
] ]
}, },
"name": { "name": {

View File

@ -1332,6 +1332,16 @@ definitions:
required: required:
- name - name
type: object type: object
dto.OperationWithNameAndType:
properties:
name:
type: string
type:
type: string
required:
- name
- type
type: object
dto.Options: dto.Options:
properties: properties:
option: option:
@ -2986,6 +2996,7 @@ definitions:
enum: enum:
- out.log - out.log
- err.log - err.log
- config
type: string type: string
name: name:
type: string type: string
@ -5112,6 +5123,26 @@ paths:
security: security:
- ApiKeyAuth: [] - ApiKeyAuth: []
summary: Load container stats summary: Load container stats
/containers/load/log:
post:
consumes:
- application/json
description: 获取容器操作日志
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OperationWithNameAndType'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Load container log
tags:
- Container
/containers/network: /containers/network:
get: get:
consumes: consumes:
@ -5781,6 +5812,26 @@ paths:
summary: Page volumes summary: Page volumes
tags: tags:
- Container Volume - Container Volume
/cronjob/record/log:
post:
consumes:
- application/json
description: 获取计划任务记录日志
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OperateByID'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Load Cronjob record log
tags:
- Cronjob
/cronjobs: /cronjobs:
post: post:
consumes: consumes:
@ -6336,6 +6387,26 @@ paths:
summary: Load mysql database from remote summary: Load mysql database from remote
tags: tags:
- Database Mysql - Database Mysql
/databases/load/file:
post:
consumes:
- application/json
description: 获取数据库文件
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OperationWithNameAndType'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Load Database file
tags:
- Database
/databases/options: /databases/options:
get: get:
consumes: consumes:
@ -7038,28 +7109,6 @@ paths:
formatEN: Download file [path] formatEN: Download file [path]
formatZH: 下载文件 [path] formatZH: 下载文件 [path]
paramKeys: [] paramKeys: []
/files/loadfile:
post:
consumes:
- application/json
description: 读取文件
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.FilePath'
responses:
"200":
description: OK
schema:
type: string
security:
- ApiKeyAuth: []
summary: Read file
tags:
- File
/files/mode: /files/mode:
post: post:
consumes: consumes:
@ -7495,6 +7544,17 @@ paths:
formatEN: update SSH conf formatEN: update SSH conf
formatZH: 修改 SSH 配置文件 formatZH: 修改 SSH 配置文件
paramKeys: [] paramKeys: []
/host/ssh/conf:
get:
description: 获取 ssh 配置文件
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Load host ssh conf
tags:
- SSH
/host/ssh/generate: /host/ssh/generate:
post: post:
consumes: consumes:
@ -7814,7 +7874,7 @@ paths:
BeforeFuntions: [] BeforeFuntions: []
bodyKeys: bodyKeys:
- operate - operate
formatEN: '[operate] [operate] Supervisor 进程文件' formatEN: '[operate] Supervisor Process Config file'
formatZH: '[operate] Supervisor 进程文件 ' formatZH: '[operate] Supervisor 进程文件 '
paramKeys: [] paramKeys: []
/hosts: /hosts:
@ -8404,6 +8464,17 @@ paths:
summary: Page operation logs summary: Page operation logs
tags: tags:
- Logs - Logs
/logs/system:
get:
description: 获取系统日志
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Load system logs
tags:
- Logs
/openResty: /openResty:
get: get:
description: 获取 OpenResty 配置信息 description: 获取 OpenResty 配置信息

View File

@ -26,6 +26,9 @@ export const loadContainerInfo = (name: string) => {
export const cleanContainerLog = (containerName: string) => { export const cleanContainerLog = (containerName: string) => {
return http.post(`/containers/clean/log`, { name: containerName }); return http.post(`/containers/clean/log`, { name: containerName });
}; };
export const loadContainerLog = (type: string, name: string) => {
return http.post<string>(`/containers/load/log`, { type: type, name: name });
};
export const containerListStats = () => { export const containerListStats = () => {
return http.get<Array<Container.ContainerListStats>>(`/containers/list/stats`); return http.get<Array<Container.ContainerListStats>>(`/containers/list/stats`);
}; };

View File

@ -6,6 +6,10 @@ export const getCronjobPage = (params: SearchWithPage) => {
return http.post<ResPage<Cronjob.CronjobInfo>>(`/cronjobs/search`, params); return http.post<ResPage<Cronjob.CronjobInfo>>(`/cronjobs/search`, params);
}; };
export const getRecordLog = (id: number) => {
return http.post<string>(`/cronjobs/records/log`, { id: id });
};
export const addCronjob = (params: Cronjob.CronjobCreate) => { export const addCronjob = (params: Cronjob.CronjobCreate) => {
return http.post<Cronjob.CronjobCreate>(`/cronjobs`, params); return http.post<Cronjob.CronjobCreate>(`/cronjobs`, params);
}; };

View File

@ -7,6 +7,9 @@ import { Database } from '../interface/database';
export const searchMysqlDBs = (params: Database.SearchDBWithPage) => { export const searchMysqlDBs = (params: Database.SearchDBWithPage) => {
return http.post<ResPage<Database.MysqlDBInfo>>(`/databases/search`, params); return http.post<ResPage<Database.MysqlDBInfo>>(`/databases/search`, params);
}; };
export const loadDatabaseFile = (type: string, name: string) => {
return http.post<string>(`/databases/load/file`, { type: type, name: name });
};
export const addMysqlDB = (params: Database.MysqlDBCreate) => { export const addMysqlDB = (params: Database.MysqlDBCreate) => {
let reqest = deepCopy(params) as Database.MysqlDBCreate; let reqest = deepCopy(params) as Database.MysqlDBCreate;

View File

@ -31,10 +31,6 @@ export const ChangeFileMode = (form: File.FileCreate) => {
return http.post<File.File>('files/mode', form); return http.post<File.File>('files/mode', form);
}; };
export const LoadFile = (form: File.FilePath) => {
return http.post<string>('files/loadfile', form);
};
export const CompressFile = (form: File.FileCompress) => { export const CompressFile = (form: File.FileCompress) => {
return http.post<File.File>('files/compress', form); return http.post<File.File>('files/compress', form);
}; };

View File

@ -98,6 +98,9 @@ export const batchOperateRule = (params: Host.BatchRule) => {
export const getSSHInfo = () => { export const getSSHInfo = () => {
return http.post<Host.SSHInfo>(`/hosts/ssh/search`); return http.post<Host.SSHInfo>(`/hosts/ssh/search`);
}; };
export const getSSHConf = () => {
return http.get<string>(`/hosts/ssh/conf`);
};
export const operateSSH = (operation: string) => { export const operateSSH = (operation: string) => {
return http.post(`/hosts/ssh/operate`, { operation: operation }); return http.post(`/hosts/ssh/operate`, { operation: operation });
}; };

View File

@ -10,6 +10,10 @@ export const getLoginLogs = (info: Log.SearchLgLog) => {
return http.post<ResPage<Log.OperationLog>>(`/logs/login`, info); return http.post<ResPage<Log.OperationLog>>(`/logs/login`, info);
}; };
export const getSystemLogs = () => {
return http.get<string>(`/logs/system`);
};
export const cleanLogs = (param: Log.CleanLog) => { export const cleanLogs = (param: Log.CleanLog) => {
return http.post(`/logs/clean`, param); return http.post(`/logs/clean`, param);
}; };

View File

@ -57,7 +57,6 @@ import DrawerHeader from '@/components/drawer-header/index.vue';
import { deleteBackupRecord, downloadBackupRecord, searchBackupRecords } from '@/api/modules/setting'; import { deleteBackupRecord, downloadBackupRecord, searchBackupRecords } from '@/api/modules/setting';
import { Backup } from '@/api/interface/backup'; import { Backup } from '@/api/interface/backup';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
// import { DownloadByPath } from '@/api/modules/files';
const selects = ref<any>([]); const selects = ref<any>([]);
const loading = ref(); const loading = ref();

View File

@ -122,9 +122,8 @@ import { Rules } from '@/global/form-rules';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElForm, ElMessageBox } from 'element-plus'; import { ElForm, ElMessageBox } from 'element-plus';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
import { listComposeTemplate, testCompose, upCompose } from '@/api/modules/container'; import { listComposeTemplate, loadContainerLog, testCompose, upCompose } from '@/api/modules/container';
import { loadBaseDir } from '@/api/modules/setting'; import { loadBaseDir } from '@/api/modules/setting';
import { LoadFile } from '@/api/modules/files';
import { formatImageStdout } from '@/utils/docker'; import { formatImageStdout } from '@/utils/docker';
import { MsgError } from '@/utils/message'; import { MsgError } from '@/utils/message';
@ -268,9 +267,9 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
}); });
}; };
const loadLogs = async (path: string) => { const loadLogs = async (name: string) => {
timer = setInterval(async () => { timer = setInterval(async () => {
const res = await LoadFile({ path: path }); const res = await loadContainerLog('compose-create', name);
logInfo.value = formatImageStdout(res.data); logInfo.value = formatImageStdout(res.data);
nextTick(() => { nextTick(() => {
const state = view.value.state; const state = view.value.state;

View File

@ -98,10 +98,9 @@ import EditDialog from '@/views/container/compose/edit/index.vue';
import CreateDialog from '@/views/container/compose/create/index.vue'; import CreateDialog from '@/views/container/compose/create/index.vue';
import DeleteDialog from '@/views/container/compose/delete/index.vue'; import DeleteDialog from '@/views/container/compose/delete/index.vue';
import ComposeDetial from '@/views/container/compose/detail/index.vue'; import ComposeDetial from '@/views/container/compose/detail/index.vue';
import { loadDockerStatus, searchCompose } from '@/api/modules/container'; import { loadContainerLog, loadDockerStatus, searchCompose } from '@/api/modules/container';
import i18n from '@/lang'; import i18n from '@/lang';
import { Container } from '@/api/interface/container'; import { Container } from '@/api/interface/container';
import { LoadFile } from '@/api/modules/files';
import { loadBaseDir } from '@/api/modules/setting'; import { loadBaseDir } from '@/api/modules/setting';
import router from '@/routers'; import router from '@/routers';
@ -198,7 +197,7 @@ const onDelete = async (row: Container.ComposeInfo) => {
const dialogEditRef = ref(); const dialogEditRef = ref();
const onEdit = async (row: Container.ComposeInfo) => { const onEdit = async (row: Container.ComposeInfo) => {
const res = await LoadFile({ path: row.path }); const res = await loadContainerLog('compose-detail', row.name);
let params = { let params = {
name: row.name, name: row.name,
path: row.path, path: row.path,

View File

@ -93,8 +93,7 @@ import { nextTick, onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElForm, ElMessage } from 'element-plus'; import { ElForm, ElMessage } from 'element-plus';
import { imageBuild } from '@/api/modules/container'; import { imageBuild, loadContainerLog } from '@/api/modules/container';
import { LoadFile } from '@/api/modules/files';
import { formatImageStdout } from '@/utils/docker'; import { formatImageStdout } from '@/utils/docker';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
@ -163,7 +162,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
const loadLogs = async (path: string) => { const loadLogs = async (path: string) => {
timer = setInterval(async () => { timer = setInterval(async () => {
if (logVisiable.value) { if (logVisiable.value) {
const res = await LoadFile({ path: path }); const res = await loadContainerLog('image-build', path);
logInfo.value = formatImageStdout(res.data); logInfo.value = formatImageStdout(res.data);
nextTick(() => { nextTick(() => {
const state = view.value.state; const state = view.value.state;

View File

@ -68,12 +68,11 @@ import { nextTick, onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElForm } from 'element-plus'; import { ElForm } from 'element-plus';
import { imagePull } from '@/api/modules/container'; import { imagePull, loadContainerLog } from '@/api/modules/container';
import { Container } from '@/api/interface/container'; import { Container } from '@/api/interface/container';
import { Codemirror } from 'vue-codemirror'; import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript'; import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark'; import { oneDark } from '@codemirror/theme-one-dark';
import { LoadFile } from '@/api/modules/files';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
import { formatImageStdout } from '@/utils/docker'; import { formatImageStdout } from '@/utils/docker';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
@ -133,7 +132,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
const loadLogs = async (path: string) => { const loadLogs = async (path: string) => {
timer = setInterval(async () => { timer = setInterval(async () => {
if (logVisiable.value) { if (logVisiable.value) {
const res = await LoadFile({ path: path }); const res = await loadContainerLog('image-pull', path);
logInfo.value = formatImageStdout(res.data); logInfo.value = formatImageStdout(res.data);
nextTick(() => { nextTick(() => {
const state = view.value.state; const state = view.value.state;

View File

@ -71,12 +71,11 @@ import { nextTick, onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElForm } from 'element-plus'; import { ElForm } from 'element-plus';
import { imagePush } from '@/api/modules/container'; import { imagePush, loadContainerLog } from '@/api/modules/container';
import { Container } from '@/api/interface/container'; import { Container } from '@/api/interface/container';
import { Codemirror } from 'vue-codemirror'; import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript'; import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark'; import { oneDark } from '@codemirror/theme-one-dark';
import { LoadFile } from '@/api/modules/files';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
import { formatImageStdout } from '@/utils/docker'; import { formatImageStdout } from '@/utils/docker';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
@ -138,7 +137,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
const loadLogs = async (path: string) => { const loadLogs = async (path: string) => {
timer = setInterval(async () => { timer = setInterval(async () => {
if (logVisiable.value) { if (logVisiable.value) {
const res = await LoadFile({ path: path }); const res = await loadContainerLog('image-push', path);
logInfo.value = formatImageStdout(res.data); logInfo.value = formatImageStdout(res.data);
nextTick(() => { nextTick(() => {
const state = view.value.state; const state = view.value.state;

View File

@ -358,11 +358,11 @@
import { onBeforeUnmount, reactive, ref } from 'vue'; import { onBeforeUnmount, reactive, ref } from 'vue';
import { Cronjob } from '@/api/interface/cronjob'; import { Cronjob } from '@/api/interface/cronjob';
import { loadZero } from '@/utils/util'; import { loadZero } from '@/utils/util';
import { searchRecords, download, handleOnce, updateStatus, cleanRecords } from '@/api/modules/cronjob'; import { searchRecords, download, handleOnce, updateStatus, cleanRecords, getRecordLog } from '@/api/modules/cronjob';
import { dateFormat } from '@/utils/util'; import { dateFormat } from '@/utils/util';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElMessageBox } from 'element-plus'; import { ElMessageBox } from 'element-plus';
import { DownloadByPath, LoadFile } from '@/api/modules/files'; import { DownloadByPath } from '@/api/modules/files';
import { Codemirror } from 'vue-codemirror'; import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript'; import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark'; import { oneDark } from '@codemirror/theme-one-dark';
@ -587,7 +587,7 @@ const loadRecord = async (row: Cronjob.Record) => {
return; return;
} }
if (row.records) { if (row.records) {
const res = await LoadFile({ path: row.records }); const res = await getRecordLog(row.id);
currentRecordDetail.value = res.data; currentRecordDetail.value = res.data;
} }
}; };

View File

@ -119,17 +119,14 @@ import { reactive, ref } from 'vue';
import { Codemirror } from 'vue-codemirror'; import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript'; import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark'; import { oneDark } from '@codemirror/theme-one-dark';
import { LoadFile } from '@/api/modules/files'; import { loadDatabaseFile, loadMysqlBaseInfo, loadMysqlVariables, updateMysqlConfByFile } from '@/api/modules/database';
import { loadMysqlBaseInfo, loadMysqlVariables, updateMysqlConfByFile } from '@/api/modules/database';
import { ChangePort, GetAppDefaultConfig } from '@/api/modules/app'; import { ChangePort, GetAppDefaultConfig } from '@/api/modules/app';
import { Rules } from '@/global/form-rules'; import { Rules } from '@/global/form-rules';
import i18n from '@/lang'; import i18n from '@/lang';
import { loadBaseDir } from '@/api/modules/setting';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
const loading = ref(false); const loading = ref(false);
const baseDir = ref();
const extensions = [javascript(), oneDark]; const extensions = [javascript(), oneDark];
const activeName = ref('conf'); const activeName = ref('conf');
@ -179,8 +176,7 @@ const onClose = (): void => {
const jumpToConf = async () => { const jumpToConf = async () => {
activeName.value = 'conf'; activeName.value = 'conf';
const pathRes = await loadBaseDir(); loadMysqlConf();
loadMysqlConf(`${pathRes.data}/apps/mysql/${mysqlName.value}/conf/my.cnf`);
}; };
const jumpToSlowlog = async () => { const jumpToSlowlog = async () => {
@ -271,9 +267,7 @@ const loadBaseInfo = async () => {
mysqlName.value = res.data?.name; mysqlName.value = res.data?.name;
baseInfo.port = res.data?.port; baseInfo.port = res.data?.port;
baseInfo.containerID = res.data?.containerName; baseInfo.containerID = res.data?.containerName;
const pathRes = await loadBaseDir(); loadMysqlConf();
baseDir.value = pathRes.data;
loadMysqlConf(`${pathRes.data}/apps/mysql/${mysqlName.value}/conf/my.cnf`);
loadContainerLog(baseInfo.containerID); loadContainerLog(baseInfo.containerID);
}; };
@ -302,9 +296,9 @@ const loadSlowLogs = async () => {
slowLogRef.value!.acceptParams(param); slowLogRef.value!.acceptParams(param);
}; };
const loadMysqlConf = async (path: string) => { const loadMysqlConf = async () => {
useOld.value = false; useOld.value = false;
const res = await LoadFile({ path: path }); const res = await loadDatabaseFile('mysql-conf', mysqlName.value);
loading.value = false; loading.value = false;
mysqlConf.value = res.data; mysqlConf.value = res.data;
}; };

View File

@ -51,12 +51,10 @@ import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark'; import { oneDark } from '@codemirror/theme-one-dark';
import { nextTick, onBeforeUnmount, reactive, ref, shallowRef } from 'vue'; import { nextTick, onBeforeUnmount, reactive, ref, shallowRef } from 'vue';
import { Database } from '@/api/interface/database'; import { Database } from '@/api/interface/database';
import { LoadFile } from '@/api/modules/files';
import ConfirmDialog from '@/components/confirm-dialog/index.vue'; import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { updateMysqlVariables } from '@/api/modules/database'; import { loadDatabaseFile, updateMysqlVariables } from '@/api/modules/database';
import { dateFormatForName, downloadWithContent } from '@/utils/util'; import { dateFormatForName, downloadWithContent } from '@/utils/util';
import i18n from '@/lang'; import i18n from '@/lang';
import { loadBaseDir } from '@/api/modules/setting';
import { MsgError, MsgInfo, MsgSuccess } from '@/utils/message'; import { MsgError, MsgInfo, MsgSuccess } from '@/utils/message';
const extensions = [javascript(), oneDark]; const extensions = [javascript(), oneDark];
@ -91,12 +89,10 @@ const acceptParams = async (params: DialogProps): Promise<void> => {
if (variables.slow_query_log === 'ON') { if (variables.slow_query_log === 'ON') {
currentStatus.value = true; currentStatus.value = true;
detailShow.value = true; detailShow.value = true;
const pathRes = await loadBaseDir(); loadMysqlSlowlogs();
let path = `${pathRes.data}/apps/mysql/${mysqlName.value}/data/1Panel-slow.log`;
loadMysqlSlowlogs(path);
timer = setInterval(() => { timer = setInterval(() => {
if (variables.slow_query_log === 'ON' && isWatch.value) { if (variables.slow_query_log === 'ON' && isWatch.value) {
loadMysqlSlowlogs(path); loadMysqlSlowlogs();
} }
}, 1000 * 5); }, 1000 * 5);
} else { } else {
@ -168,8 +164,8 @@ const onDownload = async () => {
downloadWithContent(slowLogs.value, mysqlName.value + '-slowlogs-' + dateFormatForName(new Date()) + '.log'); downloadWithContent(slowLogs.value, mysqlName.value + '-slowlogs-' + dateFormatForName(new Date()) + '.log');
}; };
const loadMysqlSlowlogs = async (path: string) => { const loadMysqlSlowlogs = async () => {
const res = await LoadFile({ path: path }); const res = await loadDatabaseFile('slow-logs', mysqlName.value);
slowLogs.value = res.data || ''; slowLogs.value = res.data || '';
nextTick(() => { nextTick(() => {
const state = view.value.state; const state = view.value.state;

View File

@ -132,15 +132,13 @@ import { nextTick, reactive, ref, shallowRef } from 'vue';
import { Codemirror } from 'vue-codemirror'; import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript'; import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark'; import { oneDark } from '@codemirror/theme-one-dark';
import { LoadFile } from '@/api/modules/files';
import ConfirmDialog from '@/components/confirm-dialog/index.vue'; import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import Status from '@/views/database/redis/setting/status/index.vue'; import Status from '@/views/database/redis/setting/status/index.vue';
import Persistence from '@/views/database/redis/setting/persistence/index.vue'; import Persistence from '@/views/database/redis/setting/persistence/index.vue';
import { loadRedisConf, updateRedisConf, updateRedisConfByFile } from '@/api/modules/database'; import { loadDatabaseFile, loadRedisConf, updateRedisConf, updateRedisConfByFile } from '@/api/modules/database';
import i18n from '@/lang'; import i18n from '@/lang';
import { checkNumberRange, Rules } from '@/global/form-rules'; import { checkNumberRange, Rules } from '@/global/form-rules';
import { ChangePort, GetAppDefaultConfig } from '@/api/modules/app'; import { ChangePort, GetAppDefaultConfig } from '@/api/modules/app';
import { loadBaseDir } from '@/api/modules/setting';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
const extensions = [javascript(), oneDark]; const extensions = [javascript(), oneDark];
@ -335,11 +333,9 @@ const loadform = async () => {
}; };
const loadConfFile = async () => { const loadConfFile = async () => {
const pathRes = await loadBaseDir();
let path = `${pathRes.data}/apps/redis/${redisName.value}/conf/redis.conf`;
useOld.value = false; useOld.value = false;
loading.value = true; loading.value = true;
await LoadFile({ path: path }) await loadDatabaseFile('redis-conf', redisName.value)
.then((res) => { .then((res) => {
loading.value = false; loading.value = false;
redisConf.value = res.data; redisConf.value = res.data;

View File

@ -158,8 +158,7 @@ import Port from '@/views/host/ssh/ssh/port/index.vue';
import Address from '@/views/host/ssh/ssh/address/index.vue'; import Address from '@/views/host/ssh/ssh/address/index.vue';
import i18n from '@/lang'; import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import { getSSHInfo, operateSSH, updateSSH, updateSSHByfile } from '@/api/modules/host'; import { getSSHConf, getSSHInfo, operateSSH, updateSSH, updateSSHByfile } from '@/api/modules/host';
import { LoadFile } from '@/api/modules/files';
import { ElMessageBox, FormInstance } from 'element-plus'; import { ElMessageBox, FormInstance } from 'element-plus';
const loading = ref(false); const loading = ref(false);
@ -289,7 +288,7 @@ const changei18n = (value: string) => {
}; };
const loadSSHConf = async () => { const loadSSHConf = async () => {
const res = await LoadFile({ path: '/etc/ssh/sshd_config' }); const res = await getSSHConf();
sshConf.value = res.data || ''; sshConf.value = res.data || '';
}; };

View File

@ -42,9 +42,8 @@ import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript'; import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark'; import { oneDark } from '@codemirror/theme-one-dark';
import { nextTick, onMounted, ref, shallowRef } from 'vue'; import { nextTick, onMounted, ref, shallowRef } from 'vue';
import { LoadFile } from '@/api/modules/files';
import { loadBaseDir } from '@/api/modules/setting';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { getSystemLogs } from '@/api/modules/log';
const router = useRouter(); const router = useRouter();
const loading = ref(); const loading = ref();
@ -56,9 +55,7 @@ const handleReady = (payload) => {
}; };
const loadSystemlogs = async () => { const loadSystemlogs = async () => {
const pathRes = await loadBaseDir(); await getSystemLogs()
let logPath = pathRes.data + '/log';
await LoadFile({ path: `${logPath}/1Panel.log` })
.then((res) => { .then((res) => {
loading.value = false; loading.value = false;
logs.value = res.data; logs.value = res.data;