mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-09 16:00:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf230ca6e7 | ||
|
|
bb99853000 | ||
|
|
0c64c8ff52 | ||
|
|
e3e2006c19 | ||
|
|
eddc1b6a8d | ||
|
|
83e1db723e | ||
|
|
76c608c703 | ||
|
|
d61a77bec7 | ||
|
|
a6a2d9991c | ||
|
|
060dd08167 | ||
|
|
dd1d93e096 | ||
|
|
e4b24f51fc | ||
|
|
e472269a4e | ||
|
|
4032276556 | ||
|
|
420537f092 | ||
|
|
3867e536b2 | ||
|
|
4bc7b96ae4 | ||
|
|
9f0338382f | ||
|
|
692c77eb1a | ||
|
|
0778e3e81d | ||
|
|
80e217e839 | ||
|
|
47547ad4e1 | ||
|
|
8b22acce27 | ||
|
|
754ac64bf6 | ||
|
|
3b37d5ae72 | ||
|
|
4e2f24a8f0 | ||
|
|
23a1fbdd4b | ||
|
|
795223e7fa | ||
|
|
10039c6173 | ||
|
|
146b628cc1 | ||
|
|
0c31fcc8a3 | ||
|
|
2c290dbbf7 | ||
|
|
0df8a33b9e | ||
|
|
1278e910f5 | ||
|
|
429e2c6cbe | ||
|
|
00a5cc654a | ||
|
|
d68a0b076b | ||
|
|
99ef8e9310 | ||
|
|
5e549fdb71 | ||
|
|
8f7b026f0e | ||
|
|
fabf33a83b | ||
|
|
211c956c4b | ||
|
|
55d966a52e | ||
|
|
a0d5319cd9 | ||
|
|
d9358b87c4 | ||
|
|
89526d9075 |
@@ -35,6 +35,29 @@ func (b *BaseApi) SearchContainer(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary Load container users
|
||||
// @Accept json
|
||||
// @Param request body dto.OperationWithName true "request"
|
||||
// @Produce json
|
||||
// @Success 200 {array} string
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /containers/users [post]
|
||||
func (b *BaseApi) LoadContainerUsers(c *gin.Context) {
|
||||
var req dto.OperationWithName
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
list, err := containerService.LoadUsers(req)
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, list)
|
||||
}
|
||||
|
||||
// @Tags Container
|
||||
// @Summary List containers
|
||||
// @Accept json
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// @Success 200 {object} response.HostToolRes
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool [post]
|
||||
// @Router /hosts/tool [post]
|
||||
func (b *BaseApi) GetToolStatus(c *gin.Context) {
|
||||
var req request.HostToolReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -35,7 +35,7 @@ func (b *BaseApi) GetToolStatus(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/create [post]
|
||||
// @Router /hosts/tool/create [post]
|
||||
// @x-panel-log {"bodyKeys":["type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 [type] 配置","formatEN":"create [type] config"}
|
||||
func (b *BaseApi) InitToolConfig(c *gin.Context) {
|
||||
var req request.HostToolCreate
|
||||
@@ -57,7 +57,7 @@ func (b *BaseApi) InitToolConfig(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/operate [post]
|
||||
// @Router /hosts/tool/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operate","type"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] [type] ","formatEN":"[operate] [type]"}
|
||||
func (b *BaseApi) OperateTool(c *gin.Context) {
|
||||
var req request.HostToolReq
|
||||
@@ -79,7 +79,7 @@ func (b *BaseApi) OperateTool(c *gin.Context) {
|
||||
// @Success 200 {object} response.HostToolConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/config [post]
|
||||
// @Router /hosts/tool/config [post]
|
||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 主机工具配置文件 ","formatEN":"[operate] tool config"}
|
||||
func (b *BaseApi) OperateToolConfig(c *gin.Context) {
|
||||
var req request.HostToolConfig
|
||||
@@ -102,7 +102,7 @@ func (b *BaseApi) OperateToolConfig(c *gin.Context) {
|
||||
// @Success 200 {string} logContent
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/log [post]
|
||||
// @Router /hosts/tool/log [post]
|
||||
func (b *BaseApi) GetToolLog(c *gin.Context) {
|
||||
var req request.HostToolLogReq
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -124,7 +124,7 @@ func (b *BaseApi) GetToolLog(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/supervisor/process [post]
|
||||
// @Router /hosts/tool/supervisor/process [post]
|
||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] 守护进程 ","formatEN":"[operate] process"}
|
||||
func (b *BaseApi) OperateProcess(c *gin.Context) {
|
||||
var req request.SupervisorProcessConfig
|
||||
@@ -146,7 +146,7 @@ func (b *BaseApi) OperateProcess(c *gin.Context) {
|
||||
// @Success 200 {object} response.SupervisorProcessConfig
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/supervisor/process [get]
|
||||
// @Router /hosts/tool/supervisor/process [get]
|
||||
func (b *BaseApi) GetProcess(c *gin.Context) {
|
||||
configs, err := hostToolService.GetSupervisorProcessConfig()
|
||||
if err != nil {
|
||||
@@ -163,7 +163,7 @@ func (b *BaseApi) GetProcess(c *gin.Context) {
|
||||
// @Success 200 {string} content
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/tool/supervisor/process/file [post]
|
||||
// @Router /hosts/tool/supervisor/process/file [post]
|
||||
// @x-panel-log {"bodyKeys":["operate"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operate] Supervisor 进程文件 ","formatEN":"[operate] Supervisor Process Config file"}
|
||||
func (b *BaseApi) GetProcessFile(c *gin.Context) {
|
||||
var req request.SupervisorProcessFileReq
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
// @Success 200 {object} dto.SSHInfo
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/search [post]
|
||||
// @Router /hosts/ssh/search [post]
|
||||
func (b *BaseApi) GetSSHInfo(c *gin.Context) {
|
||||
info, err := sshService.GetSSHInfo()
|
||||
if err != nil {
|
||||
@@ -28,7 +28,7 @@ func (b *BaseApi) GetSSHInfo(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/operate [post]
|
||||
// @Router /hosts/ssh/operate [post]
|
||||
// @x-panel-log {"bodyKeys":["operation"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"[operation] SSH ","formatEN":"[operation] SSH"}
|
||||
func (b *BaseApi) OperateSSH(c *gin.Context) {
|
||||
var req dto.Operate
|
||||
@@ -50,7 +50,7 @@ func (b *BaseApi) OperateSSH(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/update [post]
|
||||
// @Router /hosts/ssh/update [post]
|
||||
// @x-panel-log {"bodyKeys":["key","value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置 [key] => [value]","formatEN":"update SSH setting [key] => [value]"}
|
||||
func (b *BaseApi) UpdateSSH(c *gin.Context) {
|
||||
var req dto.SSHUpdate
|
||||
@@ -72,7 +72,7 @@ func (b *BaseApi) UpdateSSH(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/conffile/update [post]
|
||||
// @Router /hosts/conffile/update [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"修改 SSH 配置文件","formatEN":"update SSH conf"}
|
||||
func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
|
||||
var req dto.SSHConf
|
||||
@@ -94,7 +94,7 @@ func (b *BaseApi) UpdateSSHByfile(c *gin.Context) {
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/generate [post]
|
||||
// @Router /hosts/ssh/generate [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"生成 SSH 密钥 ","formatEN":"generate SSH secret"}
|
||||
func (b *BaseApi) GenerateSSH(c *gin.Context) {
|
||||
var req dto.GenerateSSH
|
||||
@@ -116,7 +116,7 @@ func (b *BaseApi) GenerateSSH(c *gin.Context) {
|
||||
// @Success 200 {string} secret
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/secret [post]
|
||||
// @Router /hosts/ssh/secret [post]
|
||||
func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
|
||||
var req dto.GenerateLoad
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -138,7 +138,7 @@ func (b *BaseApi) LoadSSHSecret(c *gin.Context) {
|
||||
// @Success 200 {object} dto.SSHLog
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/log [post]
|
||||
// @Router /hosts/ssh/log [post]
|
||||
func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
|
||||
var req dto.SearchSSHLog
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
@@ -158,7 +158,7 @@ func (b *BaseApi) LoadSSHLogs(c *gin.Context) {
|
||||
// @Success 200 {string} conf
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /host/ssh/conf [get]
|
||||
// @Router /hosts/ssh/conf [get]
|
||||
func (b *BaseApi) LoadSSHConf(c *gin.Context) {
|
||||
data, err := sshService.LoadSSHConf()
|
||||
if err != nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -16,7 +17,6 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
||||
"github.com/docker/docker/api/types"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/env"
|
||||
@@ -703,6 +703,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
Rule: form.Rule,
|
||||
Type: form.Type,
|
||||
Multiple: form.Multiple,
|
||||
Required: form.Required,
|
||||
}
|
||||
if form.Edit {
|
||||
appParam.Edit = true
|
||||
@@ -761,6 +762,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
||||
Value: form.Default,
|
||||
Values: form.Values,
|
||||
Multiple: form.Multiple,
|
||||
Required: form.Required,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -789,15 +791,15 @@ func syncAppInstallStatus(appInstall *model.AppInstall, force bool) error {
|
||||
defer cli.Close()
|
||||
|
||||
var (
|
||||
containers []types.Container
|
||||
containersMap map[string]types.Container
|
||||
containers []container.Summary
|
||||
containersMap map[string]container.Summary
|
||||
containerNames = strings.Split(appInstall.ContainerName, ",")
|
||||
)
|
||||
containers, err = cli.ListContainersByName(containerNames)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
containersMap = make(map[string]types.Container)
|
||||
containersMap = make(map[string]container.Summary)
|
||||
for _, con := range containers {
|
||||
containersMap[con.Names[0]] = con
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"log"
|
||||
"math"
|
||||
"net/http"
|
||||
@@ -25,8 +26,6 @@ import (
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
|
||||
@@ -786,6 +785,15 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
||||
return appInstallRepo.Save(context.Background(), &install)
|
||||
}
|
||||
|
||||
func skipCheckStatus(service types.ServiceConfig) bool {
|
||||
for key := range service.Labels {
|
||||
if key == "skipStatusCheck" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getContainerNames(install model.AppInstall) ([]string, error) {
|
||||
envStr, err := coverEnvJsonToStr(install.Env)
|
||||
if err != nil {
|
||||
@@ -797,7 +805,7 @@ func getContainerNames(install model.AppInstall) ([]string, error) {
|
||||
}
|
||||
containerMap := make(map[string]struct{})
|
||||
for _, service := range project.AllServices() {
|
||||
if service.ContainerName == "${CONTAINER_NAME}" || service.ContainerName == "" {
|
||||
if service.ContainerName == "${CONTAINER_NAME}" || service.ContainerName == "" || skipCheckStatus(service) {
|
||||
continue
|
||||
}
|
||||
containerMap[service.ContainerName] = struct{}{}
|
||||
@@ -1363,7 +1371,7 @@ func doNotNeedSync(installed model.AppInstall) bool {
|
||||
installed.Status == constant.StatusSyncing || installed.Status == constant.StatusUninstalling || installed.Status == constant.StatusInstallErr
|
||||
}
|
||||
|
||||
func synAppInstall(containers map[string]types.Container, appInstall *model.AppInstall, force bool) {
|
||||
func synAppInstall(containers map[string]container.Summary, appInstall *model.AppInstall, force bool) {
|
||||
oldStatus := appInstall.Status
|
||||
containerNames := strings.Split(appInstall.ContainerName, ",")
|
||||
if len(containers) == 0 {
|
||||
@@ -1437,7 +1445,7 @@ func synAppInstall(containers map[string]types.Container, appInstall *model.AppI
|
||||
func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool) ([]response.AppInstallDTO, error) {
|
||||
var (
|
||||
res []response.AppInstallDTO
|
||||
containersMap map[string]types.Container
|
||||
containersMap map[string]container.Summary
|
||||
)
|
||||
if sync {
|
||||
cli, err := docker.NewClient()
|
||||
@@ -1445,7 +1453,7 @@ func handleInstalled(appInstallList []model.AppInstall, updated bool, sync bool)
|
||||
defer cli.Close()
|
||||
containers, err := cli.ListAllContainers()
|
||||
if err == nil {
|
||||
containersMap = make(map[string]types.Container, len(containers))
|
||||
containersMap = make(map[string]container.Summary, len(containers))
|
||||
for _, contain := range containers {
|
||||
containersMap[contain.Names[0]] = contain
|
||||
}
|
||||
|
||||
@@ -83,6 +83,8 @@ type IContainerService interface {
|
||||
ComposeUpdate(req dto.ComposeUpdate) error
|
||||
Prune(req dto.ContainerPrune) (dto.ContainerPruneReport, error)
|
||||
|
||||
LoadUsers(req dto.OperationWithName) ([]string, error)
|
||||
|
||||
StreamLogs(ctx *gin.Context, params dto.StreamLog)
|
||||
}
|
||||
|
||||
@@ -882,16 +884,20 @@ func (u *ContainerService) ContainerLogClean(req dto.OperationWithName) error {
|
||||
func (u *ContainerService) StreamLogs(ctx *gin.Context, params dto.StreamLog) {
|
||||
messageChan := make(chan string, 1024)
|
||||
errorChan := make(chan error, 1)
|
||||
doneChan := make(chan struct{})
|
||||
|
||||
go collectLogs(params, messageChan, errorChan)
|
||||
|
||||
go func() {
|
||||
<-ctx.Request.Context().Done()
|
||||
close(doneChan)
|
||||
}()
|
||||
go collectLogs(doneChan, params, messageChan, errorChan)
|
||||
ctx.Stream(func(w io.Writer) bool {
|
||||
select {
|
||||
case msg, ok := <-messageChan:
|
||||
if !ok {
|
||||
return msg == ""
|
||||
return false
|
||||
}
|
||||
_, err := fmt.Fprintf(w, "data: %v\n\n", msg)
|
||||
_, err := fmt.Fprintf(w, "data: %s\n\n", msg)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -900,17 +906,14 @@ func (u *ContainerService) StreamLogs(ctx *gin.Context, params dto.StreamLog) {
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(w, "event: error\ndata: %v\n\n", err.Error())
|
||||
}
|
||||
return true
|
||||
case <-ctx.Request.Context().Done():
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func collectLogs(params dto.StreamLog, messageChan chan<- string, errorChan chan<- error) {
|
||||
func collectLogs(done <-chan struct{}, params dto.StreamLog, messageChan chan<- string, errorChan chan<- error) {
|
||||
defer close(messageChan)
|
||||
defer close(errorChan)
|
||||
|
||||
var cmdArgs []string
|
||||
if params.Type == "compose" {
|
||||
cmdArgs = []string{"compose", "-f", params.Compose}
|
||||
@@ -928,36 +931,61 @@ func collectLogs(params dto.StreamLog, messageChan chan<- string, errorChan chan
|
||||
if params.Container != "" {
|
||||
cmdArgs = append(cmdArgs, params.Container)
|
||||
}
|
||||
|
||||
dockerCmd := exec.Command("docker", cmdArgs...)
|
||||
dockerCmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
|
||||
stdout, err := dockerCmd.StdoutPipe()
|
||||
if err != nil {
|
||||
errorChan <- fmt.Errorf("failed to get stdout pipe: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
dockerCmd.Stderr = dockerCmd.Stdout
|
||||
|
||||
if err = dockerCmd.Start(); err != nil {
|
||||
errorChan <- fmt.Errorf("failed to start command: %v", err)
|
||||
errorChan <- fmt.Errorf("failed to start docker logs command: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if dockerCmd.Process != nil {
|
||||
if pgid, err := syscall.Getpgid(dockerCmd.Process.Pid); err == nil {
|
||||
_ = syscall.Kill(-pgid, syscall.SIGKILL)
|
||||
}
|
||||
_ = dockerCmd.Process.Kill()
|
||||
_ = dockerCmd.Wait()
|
||||
}
|
||||
}()
|
||||
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
|
||||
processKilled := false
|
||||
go func() {
|
||||
<-done
|
||||
if !processKilled && dockerCmd.Process != nil {
|
||||
processKilled = true
|
||||
if pgid, err := syscall.Getpgid(dockerCmd.Process.Pid); err == nil {
|
||||
_ = syscall.Kill(-pgid, syscall.SIGKILL)
|
||||
}
|
||||
_ = dockerCmd.Process.Kill()
|
||||
}
|
||||
}()
|
||||
|
||||
for scanner.Scan() {
|
||||
message := scanner.Text()
|
||||
select {
|
||||
case messageChan <- message:
|
||||
case <-time.After(5 * time.Second):
|
||||
errorChan <- fmt.Errorf("message channel blocked")
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = scanner.Err(); err != nil {
|
||||
|
||||
if err = scanner.Err(); err != nil && err != io.EOF {
|
||||
errorChan <- fmt.Errorf("scanner error: %v", err)
|
||||
return
|
||||
}
|
||||
if err = dockerCmd.Wait(); err != nil {
|
||||
errorChan <- fmt.Errorf("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
_ = dockerCmd.Wait()
|
||||
}
|
||||
|
||||
func (u *ContainerService) DownloadContainerLogs(containerType, container, since, tail string, c *gin.Context) error {
|
||||
@@ -1065,6 +1093,21 @@ func (u *ContainerService) ContainerStats(id string) (*dto.ContainerStats, error
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) LoadUsers(req dto.OperationWithName) ([]string, error) {
|
||||
std, err := cmd.RunDefaultWithStdoutBashCf("docker exec %s cat /etc/passwd", req.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var users []string
|
||||
lines := strings.Split(string(std), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, ":") {
|
||||
users = append(users, strings.Split(line, ":")[0])
|
||||
}
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func stringsToMap(list []string) map[string]string {
|
||||
var labelMap = make(map[string]string)
|
||||
for _, label := range list {
|
||||
|
||||
@@ -60,7 +60,7 @@ func (u *CronjobService) handleApp(cronjob model.Cronjob, startTime time.Time, t
|
||||
if err := doAppBackup(&app, task, backupDir, record.FileName, cronjob.ExclusionRules, cronjob.Secret); err != nil {
|
||||
return err
|
||||
}
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -101,7 +101,7 @@ func (u *CronjobService) handleWebsite(cronjob model.Cronjob, startTime time.Tim
|
||||
return err
|
||||
}
|
||||
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -149,7 +149,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti
|
||||
}
|
||||
}
|
||||
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, record.FileName))
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, record.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -193,7 +193,7 @@ func (u *CronjobService) handleDirectory(cronjob model.Cronjob, startTime time.T
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = cronjob.Name
|
||||
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(backupDir, fileName))
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(backupDir, fileName))
|
||||
if err != nil {
|
||||
taskItem.LogFailedWithErr("Upload backup file", err)
|
||||
return err
|
||||
@@ -228,7 +228,7 @@ func (u *CronjobService) handleSystemLog(cronjob model.Cronjob, startTime time.T
|
||||
record.CronjobID = cronjob.ID
|
||||
record.Name = cronjob.Name
|
||||
record.DownloadAccountID, record.SourceAccountIDs = cronjob.DownloadAccountID, cronjob.SourceAccountIDs
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, accountMap, path.Join(path.Dir(backupDir), fileName))
|
||||
downloadPath, err := u.uploadCronjobBackFile(cronjob, task, accountMap, path.Join(path.Dir(backupDir), fileName))
|
||||
if err != nil {
|
||||
taskItem.LogFailedWithErr("Upload backup file", err)
|
||||
return err
|
||||
|
||||
@@ -118,11 +118,21 @@ func (u *CronjobService) handleShell(cronjob model.Cronjob, taskItem *task.Task)
|
||||
cmdMgr := cmd.NewCommandMgr(cmd.WithTask(*taskItem))
|
||||
taskItem.AddSubTaskWithOps(i18n.GetWithName("HandleShell", cronjob.Name), func(t *task.Task) error {
|
||||
if len(cronjob.ContainerName) != 0 {
|
||||
scriptItem := cronjob.Script
|
||||
if cronjob.ScriptMode == "select" {
|
||||
scriptItem = path.Join("/tmp", path.Base(cronjob.Script))
|
||||
if err := cmdMgr.Run("docker", "cp", cronjob.Script, cronjob.ContainerName+":"+scriptItem); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
command := "sh"
|
||||
if len(cronjob.Command) != 0 {
|
||||
command = cronjob.Command
|
||||
}
|
||||
return cmdMgr.Run("docker", "exec", cronjob.ContainerName, command, "-c", strings.ReplaceAll(cronjob.Script, "\"", "\\\""))
|
||||
if len(cronjob.User) != 0 {
|
||||
return cmdMgr.Run("docker", "exec", "-u", cronjob.User, cronjob.ContainerName, command, "-c", strings.ReplaceAll(scriptItem, "\"", "\\\""))
|
||||
}
|
||||
return cmdMgr.Run("docker", "exec", cronjob.ContainerName, command, "-c", strings.ReplaceAll(scriptItem, "\"", "\\\""))
|
||||
}
|
||||
if len(cronjob.Executor) == 0 {
|
||||
cronjob.Executor = "bash"
|
||||
@@ -252,23 +262,28 @@ func (u *CronjobService) handleSystemClean(cronjob model.Cronjob, taskItem *task
|
||||
taskItem.AddSubTaskWithOps(i18n.GetMsgByKey("HandleSystemClean"), cleanTask, nil, int(cronjob.RetryTimes), time.Duration(cronjob.Timeout)*time.Second)
|
||||
}
|
||||
|
||||
func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, accountMap map[string]backupClientHelper, file string) (string, error) {
|
||||
func (u *CronjobService) uploadCronjobBackFile(cronjob model.Cronjob, task *task.Task, accountMap map[string]backupClientHelper, file string) (string, error) {
|
||||
defer func() {
|
||||
_ = os.Remove(file)
|
||||
}()
|
||||
var errItem error
|
||||
accounts := strings.Split(cronjob.SourceAccountIDs, ",")
|
||||
cloudSrc := strings.TrimPrefix(file, global.Dir.TmpDir+"/")
|
||||
for _, account := range accounts {
|
||||
if len(account) != 0 {
|
||||
global.LOG.Debugf("start upload file to %s, dir: %s", accountMap[account].name, pathUtils.Join(accountMap[account].backupPath, cloudSrc))
|
||||
if _, err := accountMap[account].client.Upload(file, pathUtils.Join(accountMap[account].backupPath, cloudSrc)); err != nil {
|
||||
global.LOG.Errorf("upload file to %s failed, err: %v", accountMap[account].name, err)
|
||||
_, err := accountMap[account].client.Upload(file, pathUtils.Join(accountMap[account].backupPath, cloudSrc))
|
||||
task.LogWithStatus(
|
||||
i18n.GetMsgWithMap("UploadFile", map[string]interface{}{
|
||||
"file": pathUtils.Join(accountMap[account].backupPath, cloudSrc),
|
||||
"backup": accountMap[account].name,
|
||||
}), err)
|
||||
if err != nil {
|
||||
errItem = err
|
||||
continue
|
||||
}
|
||||
global.LOG.Debugf("upload successful!")
|
||||
}
|
||||
}
|
||||
return cloudSrc, nil
|
||||
return cloudSrc, errItem
|
||||
}
|
||||
|
||||
func (u *CronjobService) removeExpiredBackup(cronjob model.Cronjob, accountMap map[string]backupClientHelper, record model.BackupRecord) {
|
||||
|
||||
@@ -246,18 +246,23 @@ func (u *DeviceService) UpdateSwap(req dto.SwapHelper) error {
|
||||
}
|
||||
return operateSwapWithFile(true, req)
|
||||
}
|
||||
std1, err := cmd.RunDefaultWithStdoutBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
|
||||
stdDD, err := cmd.RunDefaultWithStdoutBashCf("%s dd if=/dev/zero of=%s bs=1024 count=%d", cmd.SudoHandleCmd(), req.Path, req.Size)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std1)
|
||||
return fmt.Errorf("handle dd %s failed, std: %s, err: %s", req.Path, stdDD, err)
|
||||
}
|
||||
std2, err := cmd.RunDefaultWithStdoutBashCf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
|
||||
stdChmod, err := cmd.RunDefaultWithStdoutBashCf("%s chmod 0600 %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std2)
|
||||
return fmt.Errorf("handle chmod 0600 %s failed,std: %s, err: %s", req.Path, stdChmod, err)
|
||||
}
|
||||
std3, err := cmd.RunDefaultWithStdoutBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
|
||||
stdMkswap, err := cmd.RunDefaultWithStdoutBashCf("%s mkswap -f %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("handle mkswap -f %s failed, std: %s, err: %s", req.Path, stdMkswap, err)
|
||||
}
|
||||
|
||||
stdSwapon, err := cmd.RunDefaultWithStdoutBashCf("%s swapon %s", cmd.SudoHandleCmd(), req.Path)
|
||||
if err != nil {
|
||||
_, _ = cmd.RunDefaultWithStdoutBashCf("%s swapoff %s", cmd.SudoHandleCmd(), req.Path)
|
||||
return fmt.Errorf("handle dd path %s failed, err: %s", req.Path, std3)
|
||||
return fmt.Errorf("handle swapoff %s failed,std: %s, err: %s", req.Path, stdSwapon, err)
|
||||
}
|
||||
return operateSwapWithFile(false, req)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -50,7 +51,7 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
ID: uuid.NewString(),
|
||||
Label: "1panel_original",
|
||||
Size: uint64(originalSize),
|
||||
IsCheck: true,
|
||||
IsCheck: originalSize > 0,
|
||||
IsRecommend: true,
|
||||
Type: "1panel_original",
|
||||
Children: loadTreeWithDir(true, "1panel_original", originalPath, fileOp),
|
||||
@@ -85,7 +86,7 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
ID: uuid.NewString(),
|
||||
Label: "snapshot",
|
||||
Size: snapSize,
|
||||
IsCheck: true,
|
||||
IsCheck: snapSize > 0,
|
||||
IsRecommend: true,
|
||||
Type: "snapshot",
|
||||
Children: snapTree,
|
||||
@@ -100,7 +101,7 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
ID: uuid.NewString(),
|
||||
Label: "rollback",
|
||||
Size: rollbackSize,
|
||||
IsCheck: true,
|
||||
IsCheck: rollbackSize > 0,
|
||||
IsRecommend: true,
|
||||
Type: "rollback",
|
||||
Children: rollBackTree,
|
||||
@@ -126,7 +127,7 @@ func (u *DeviceService) Scan() dto.CleanData {
|
||||
ID: uuid.NewString(),
|
||||
Label: "unused",
|
||||
Size: unusedSize,
|
||||
IsCheck: true,
|
||||
IsCheck: unusedSize > 0,
|
||||
IsRecommend: true,
|
||||
Type: "unused",
|
||||
Children: unusedTree,
|
||||
@@ -338,7 +339,7 @@ func doSystemClean(taskItem *task.Task) func(t *task.Task) error {
|
||||
}
|
||||
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, tmpUploadPath), taskItem, &size, &fileCount)
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, uploadPath), taskItem, &size, &fileCount)
|
||||
dropWithExclude(path.Join(global.Dir.BaseDir, uploadPath), []string{"theme"}, taskItem, &size, &fileCount)
|
||||
dropWithTask(path.Join(global.Dir.BaseDir, downloadPath), taskItem, &size, &fileCount)
|
||||
|
||||
logFiles, _ := os.ReadDir(global.Dir.LogDir)
|
||||
@@ -714,6 +715,30 @@ func dropVolumes() {
|
||||
}
|
||||
}
|
||||
|
||||
func dropWithExclude(pathToDelete string, excludeSubDirs []string, taskItem *task.Task, size *int64, count *int) {
|
||||
entries, err := os.ReadDir(pathToDelete)
|
||||
if err != nil {
|
||||
taskItem.LogFailed(fmt.Sprintf("read dir %s failed: %v", pathToDelete, err))
|
||||
return
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
fullPath := filepath.Join(pathToDelete, name)
|
||||
excluded := false
|
||||
for _, ex := range excludeSubDirs {
|
||||
if name == ex {
|
||||
excluded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if excluded {
|
||||
continue
|
||||
}
|
||||
dropWithTask(fullPath, taskItem, size, count)
|
||||
}
|
||||
}
|
||||
|
||||
func dropWithTask(itemPath string, taskItem *task.Task, size *int64, count *int) {
|
||||
itemSize := int64(0)
|
||||
itemCount := 0
|
||||
|
||||
@@ -337,7 +337,7 @@ func (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo,
|
||||
}
|
||||
if !utf8.Valid(content) {
|
||||
_, decodeName, _ := charset.DetermineEncoding(content, "")
|
||||
if decodeName == "windows-1252" {
|
||||
if decodeName == "windows-1252" || strings.ToLower(decodeName) == "gbk" {
|
||||
reader := strings.NewReader(info.Content)
|
||||
item := transform.NewReader(reader, simplifiedchinese.GBK.NewDecoder())
|
||||
contents, err := io.ReadAll(item)
|
||||
|
||||
@@ -182,6 +182,7 @@ func (u *FirewallService) OperateFirewall(operation string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needRestartDocker := false
|
||||
switch operation {
|
||||
case "start":
|
||||
if err := client.Start(); err != nil {
|
||||
@@ -191,26 +192,30 @@ func (u *FirewallService) OperateFirewall(operation string) error {
|
||||
_ = client.Stop()
|
||||
return err
|
||||
}
|
||||
_, _ = cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
|
||||
return nil
|
||||
needRestartDocker = true
|
||||
case "stop":
|
||||
if err := client.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
|
||||
return nil
|
||||
needRestartDocker = true
|
||||
case "restart":
|
||||
if err := client.Restart(); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
|
||||
return nil
|
||||
needRestartDocker = true
|
||||
case "disablePing":
|
||||
return u.updatePingStatus("0")
|
||||
case "enablePing":
|
||||
return u.updatePingStatus("1")
|
||||
default:
|
||||
return fmt.Errorf("not supported operation: %s", operation)
|
||||
}
|
||||
return fmt.Errorf("not support such operation: %s", operation)
|
||||
if needRestartDocker {
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *FirewallService) OperatePortRule(req dto.PortRuleOperate, reload bool) error {
|
||||
|
||||
+13
-11
@@ -105,21 +105,23 @@ func (f *FtpService) Sync() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentData := make(map[string]model.Ftp)
|
||||
for _, item := range listsInDB {
|
||||
currentData[item.User] = item
|
||||
}
|
||||
sameData := make(map[string]struct{})
|
||||
for _, item := range lists {
|
||||
for _, itemInDB := range listsInDB {
|
||||
if item.User == itemInDB.User {
|
||||
sameData[item.User] = struct{}{}
|
||||
if item.Path != itemInDB.Path || item.Status != itemInDB.Status {
|
||||
_ = ftpRepo.Update(itemInDB.ID, map[string]interface{}{"path": item.Path, "status": item.Status})
|
||||
if itemInDB, ok := currentData[item.User]; ok {
|
||||
sameData[item.User] = struct{}{}
|
||||
if item.Path != itemInDB.Path || item.Status != itemInDB.Status {
|
||||
if err := ftpRepo.Update(itemInDB.ID, map[string]interface{}{"path": item.Path, "status": item.Status}); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, item := range lists {
|
||||
if _, ok := sameData[item.User]; !ok {
|
||||
_ = ftpRepo.Create(&model.Ftp{User: item.User, Path: item.Path, Status: item.Status})
|
||||
} else {
|
||||
if err := ftpRepo.Create(&model.Ftp{User: item.User, Path: item.Path, Status: item.Status}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, item := range listsInDB {
|
||||
|
||||
@@ -91,9 +91,8 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||
if err := u.handleRegistries(req.DownloadUrl, "", "create"); err != nil {
|
||||
return fmt.Errorf("create registry %s failed, err: %v", req.DownloadUrl, err)
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
|
||||
if err != nil {
|
||||
return errors.New(string(stdout))
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
}
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
@@ -682,7 +682,7 @@ func (r *RuntimeService) GetPHPExtensions(runtimeID uint) (response.PHPExtension
|
||||
}
|
||||
}
|
||||
var phpExtensions []response.SupportExtension
|
||||
if err = json.Unmarshal(nginx_conf.PHPExtensionsJson, &phpExtensions); err != nil {
|
||||
if err = json.Unmarshal(nginx_conf.GetWebsiteFile("php_extensions.json"), &phpExtensions); err != nil {
|
||||
return res, err
|
||||
}
|
||||
for _, ext := range phpExtensions {
|
||||
|
||||
@@ -657,7 +657,7 @@ func unInstallPHPExtension(runtime *model.Runtime, delExtensions []string) error
|
||||
dir := runtime.GetPath()
|
||||
fileOP := files.NewFileOp()
|
||||
var phpExtensions []response.SupportExtension
|
||||
if err := json.Unmarshal(nginx_conf.PHPExtensionsJson, &phpExtensions); err != nil {
|
||||
if err := json.Unmarshal(nginx_conf.GetWebsiteFile("php_extensions.json"), &phpExtensions); err != nil {
|
||||
return err
|
||||
}
|
||||
phpVersion := getRuntimeEnv(runtime.Env, "PHP_VERSION")
|
||||
|
||||
@@ -371,7 +371,9 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error {
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = cmd.RunDefaultWithStdoutBashC("systemctl restart docker")
|
||||
if err := restartDocker(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -985,7 +985,7 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = ChangeHSTSConfig(req.Hsts, website); err != nil {
|
||||
if err = ChangeHSTSConfig(req.Hsts, req.Http3, website); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Enable = req.Enable
|
||||
|
||||
@@ -436,14 +436,14 @@ func (w WebsiteCAService) DownloadFile(id uint) (*os.File, error) {
|
||||
if err = fileOp.CreateDir(dir, constant.DirPerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = fileOp.WriteFile(path.Join(dir, "ca.csr"), strings.NewReader(ca.CSR), constant.DirPerm); err != nil {
|
||||
if err = fileOp.WriteFile(path.Join(dir, "ca.crt"), strings.NewReader(ca.CSR), constant.DirPerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = fileOp.WriteFile(path.Join(dir, "private.key"), strings.NewReader(ca.PrivateKey), constant.DirPerm); err != nil {
|
||||
if err = fileOp.WriteFile(path.Join(dir, "ca.key"), strings.NewReader(ca.PrivateKey), constant.DirPerm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fileName := ca.Name + ".zip"
|
||||
if err = fileOp.Compress([]string{path.Join(dir, "ca.csr"), path.Join(dir, "private.key")}, dir, fileName, files.SdkZip, ""); err != nil {
|
||||
if err = fileOp.Compress([]string{path.Join(dir, "ca.crt"), path.Join(dir, "ca.key")}, dir, fileName, files.SdkZip, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.Open(path.Join(dir, fileName))
|
||||
|
||||
@@ -1162,7 +1162,7 @@ func UpdateSSLConfig(websiteSSL model.WebsiteSSL) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ChangeHSTSConfig(enable bool, website model.Website) error {
|
||||
func ChangeHSTSConfig(enable bool, http3Enable bool, website model.Website) error {
|
||||
includeDir := GetSitePath(website, SiteProxyDir)
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(includeDir) {
|
||||
@@ -1193,6 +1193,11 @@ func ChangeHSTSConfig(enable bool, website model.Website) error {
|
||||
} else {
|
||||
location.RemoveDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
|
||||
}
|
||||
if http3Enable {
|
||||
location.UpdateDirective("add_header", []string{"Alt-Svc", "'h3=\":443\"; ma=2592000'"})
|
||||
} else {
|
||||
location.RemoveDirective("add_header", []string{"Alt-Svc", "'h3=\":443\"; ma=2592000'"})
|
||||
}
|
||||
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr("ErrUpdateBuWebsite", err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
cpuProfile string
|
||||
memProfile string
|
||||
duration time.Duration
|
||||
showMemStats bool
|
||||
showGoroutine bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(debugCmd)
|
||||
|
||||
debugCmd.Flags().StringVar(&cpuProfile, "cpuprofile", "", "write cpu profile to file")
|
||||
debugCmd.Flags().StringVar(&memProfile, "memprofile", "", "write memory profile to file")
|
||||
debugCmd.Flags().DurationVar(&duration, "duration", 30*time.Second, "duration for cpu profiling")
|
||||
debugCmd.Flags().BoolVar(&showMemStats, "memstats", false, "show memory statistics")
|
||||
debugCmd.Flags().BoolVar(&showGoroutine, "goroutine", false, "show goroutine profile")
|
||||
}
|
||||
|
||||
var debugCmd = &cobra.Command{
|
||||
Use: "debug",
|
||||
Short: "Performance debugging with pprof",
|
||||
Long: `Debug command provides CPU and memory profiling capabilities using pprof.
|
||||
|
||||
Examples:
|
||||
# CPU profiling for 30 seconds
|
||||
1panel-agent debug --cpuprofile=cpu.prof --duration=30s
|
||||
|
||||
# Memory profiling
|
||||
1panel-agent debug --memprofile=mem.prof
|
||||
|
||||
# Show memory statistics
|
||||
1panel-agent debug --memstats
|
||||
|
||||
# Show goroutine information
|
||||
1panel-agent debug --goroutine
|
||||
|
||||
# Combined profiling
|
||||
1panel-agent debug --cpuprofile=cpu.prof --memprofile=mem.prof --memstats`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if cpuProfile != "" {
|
||||
if err := startCPUProfile(cpuProfile); err != nil {
|
||||
return fmt.Errorf("failed to start CPU profile: %v", err)
|
||||
}
|
||||
defer stopCPUProfile()
|
||||
time.Sleep(duration)
|
||||
fmt.Printf("CPU profile saved to: %s\n", cpuProfile)
|
||||
}
|
||||
|
||||
if memProfile != "" {
|
||||
if err := writeMemProfile(memProfile); err != nil {
|
||||
return fmt.Errorf("failed to write memory profile: %v", err)
|
||||
}
|
||||
fmt.Printf("Memory profile saved to: %s\n", memProfile)
|
||||
}
|
||||
|
||||
if showMemStats {
|
||||
printMemStats()
|
||||
}
|
||||
|
||||
if showGoroutine {
|
||||
printGoroutineInfo()
|
||||
}
|
||||
|
||||
if cpuProfile == "" && memProfile == "" && !showMemStats && !showGoroutine {
|
||||
printBasicInfo()
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func startCPUProfile(filename string) error {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := pprof.StartCPUProfile(f); err != nil {
|
||||
f.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func stopCPUProfile() {
|
||||
pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
func writeMemProfile(filename string) error {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
runtime.GC()
|
||||
|
||||
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printMemStats() {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
|
||||
fmt.Println("\n=== Memory Statistics ===")
|
||||
fmt.Printf("Allocated memory: %d KB (%d MB)\n", m.Alloc/1024, m.Alloc/1024/1024)
|
||||
fmt.Printf("Total allocated: %d KB (%d MB)\n", m.TotalAlloc/1024, m.TotalAlloc/1024/1024)
|
||||
fmt.Printf("System memory: %d KB (%d MB)\n", m.Sys/1024, m.Sys/1024/1024)
|
||||
fmt.Printf("Heap allocated: %d KB (%d MB)\n", m.HeapAlloc/1024, m.HeapAlloc/1024/1024)
|
||||
fmt.Printf("Heap system: %d KB (%d MB)\n", m.HeapSys/1024, m.HeapSys/1024/1024)
|
||||
fmt.Printf("Heap idle: %d KB (%d MB)\n", m.HeapIdle/1024, m.HeapIdle/1024/1024)
|
||||
fmt.Printf("Heap in use: %d KB (%d MB)\n", m.HeapInuse/1024, m.HeapInuse/1024/1024)
|
||||
fmt.Printf("Heap released: %d KB (%d MB)\n", m.HeapReleased/1024, m.HeapReleased/1024/1024)
|
||||
fmt.Printf("Heap objects: %d\n", m.HeapObjects)
|
||||
fmt.Printf("GC runs: %d\n", m.NumGC)
|
||||
fmt.Printf("GC pause total: %d ns\n", m.PauseTotalNs)
|
||||
fmt.Printf("Next GC: %d KB (%d MB)\n", m.NextGC/1024, m.NextGC/1024/1024)
|
||||
fmt.Printf("Last GC: %s\n", time.Unix(0, int64(m.LastGC)).Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
func printGoroutineInfo() {
|
||||
fmt.Println("\n=== Goroutine Information ===")
|
||||
fmt.Printf("Number of goroutines: %d\n", runtime.NumGoroutine())
|
||||
fmt.Printf("Number of CPUs: %d\n", runtime.NumCPU())
|
||||
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
|
||||
|
||||
buf := make([]byte, 1024*1024)
|
||||
stackSize := runtime.Stack(buf, true)
|
||||
fmt.Printf("\nGoroutine stack trace (first 2048 bytes):\n")
|
||||
if stackSize > 2048 {
|
||||
fmt.Printf("%s...\n", buf[:2048])
|
||||
fmt.Printf("(truncated, total size: %d bytes)\n", stackSize)
|
||||
} else {
|
||||
fmt.Printf("%s\n", buf[:stackSize])
|
||||
}
|
||||
}
|
||||
|
||||
func printBasicInfo() {
|
||||
fmt.Println("=== Performance Debug Info ===")
|
||||
fmt.Printf("Go version: %s\n", runtime.Version())
|
||||
fmt.Printf("OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
|
||||
fmt.Printf("CPUs: %d\n", runtime.NumCPU())
|
||||
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
|
||||
fmt.Printf("Goroutines: %d\n", runtime.NumGoroutine())
|
||||
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
fmt.Printf("Memory allocated: %d KB\n", m.Alloc/1024)
|
||||
fmt.Printf("Total allocations: %d KB\n", m.TotalAlloc/1024)
|
||||
fmt.Printf("GC runs: %d\n", m.NumGC)
|
||||
|
||||
fmt.Println("\nUse --help to see available profiling options.")
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/server"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "1panel-agent",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
server.Start()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "net/http/pprof"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/server"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/cmd/server/cmd"
|
||||
"os"
|
||||
)
|
||||
|
||||
// @title 1Panel
|
||||
@@ -15,5 +15,8 @@ import (
|
||||
// @host localhost
|
||||
// @BasePath /api/v2
|
||||
func main() {
|
||||
server.Start()
|
||||
if err := cmd.RootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package nginx_conf
|
||||
import (
|
||||
"embed"
|
||||
_ "embed"
|
||||
"io"
|
||||
)
|
||||
|
||||
//go:embed ssl.conf
|
||||
@@ -44,8 +45,18 @@ var PathAuth []byte
|
||||
//go:embed upstream.conf
|
||||
var Upstream []byte
|
||||
|
||||
//go:embed php_extensions.json
|
||||
var PHPExtensionsJson []byte
|
||||
|
||||
//go:embed sse.conf
|
||||
var SSE []byte
|
||||
|
||||
//go:embed *.json
|
||||
var websitesFiles embed.FS
|
||||
|
||||
func GetWebsiteFile(filename string) []byte {
|
||||
file, err := websitesFiles.Open(filename)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer file.Close()
|
||||
res, _ := io.ReadAll(file)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "Swoole",
|
||||
"name": "swoole",
|
||||
"check": "swoole",
|
||||
"file": "swoole.so",
|
||||
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
|
||||
@@ -369,5 +369,12 @@
|
||||
"file": "bcmath.so",
|
||||
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
|
||||
"installed": false
|
||||
},
|
||||
{
|
||||
"name": "pcntl",
|
||||
"check": "pcntl",
|
||||
"file": "pcntl.so",
|
||||
"versions": ["56","70", "71", "72", "73", "74", "80", "81", "82", "83","84"],
|
||||
"installed": false
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package global
|
||||
|
||||
type ServerConfig struct {
|
||||
Base Base `mapstructure:"base"`
|
||||
RemoteURL RemoteURL `mapstructure:"remote_url"`
|
||||
Log LogConfig `mapstructure:"log"`
|
||||
Base Base `mapstructure:"base"`
|
||||
RemoteURL RemoteURL `mapstructure:"remote_url"`
|
||||
Log LogConfig `mapstructure:"log"`
|
||||
DockerConfig DockerConfig
|
||||
}
|
||||
|
||||
type Base struct {
|
||||
@@ -51,3 +52,7 @@ type LogConfig struct {
|
||||
LogSuffix string `mapstructure:"log_suffix"`
|
||||
MaxBackup int `mapstructure:"max_backup"`
|
||||
}
|
||||
|
||||
type DockerConfig struct {
|
||||
Command string
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ require (
|
||||
github.com/alex-ant/gomath v0.0.0-20160516115720-89013a210a82 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/baidubce/bce-sdk-go v0.9.223 // indirect
|
||||
github.com/bodgit/plumbing v1.2.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.3.0 // indirect
|
||||
github.com/bodgit/windows v1.0.0 // indirect
|
||||
|
||||
@@ -90,6 +90,8 @@ github.com/aws/aws-sdk-go v1.55.0/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQ
|
||||
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
|
||||
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
|
||||
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
||||
github.com/baidubce/bce-sdk-go v0.9.223 h1:vvDeIemf7ePPP59nLHCntQ/vS++ok2HKbRPgmz1VZKU=
|
||||
github.com/baidubce/bce-sdk-go v0.9.223/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
|
||||
@@ -92,6 +92,7 @@ ErrCustomApps: 'There is an installed application, please uninstall it first'
|
||||
ErrCustomRuntimes: 'There is an installed runtime environment, please delete it first'
|
||||
ErrAppVersionDeprecated: "The {{ .name }} application is not compatible with the current 1Panel version, skipped"
|
||||
ErrDockerFailed: "The state of Docker is abnormal, please check the service status"
|
||||
ErrDockerComposeCmdNotFound: "The Docker Compose command does not exist, please install this command on the host machine first"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: 'This file does not support preview'
|
||||
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: 'Source directory does not exist'
|
||||
ErrFavoriteExist: 'This path has already been favorited'
|
||||
ErrInvalidChar: 'Illegal characters are not allowed'
|
||||
ErrPathNotDelete: 'The selected directory cannot be deleted'
|
||||
ErrLogFileToLarge: "The log file exceeds 500MB and cannot be opened"
|
||||
|
||||
#website
|
||||
ErrAliasIsExist: 'Alias already exists'
|
||||
@@ -202,6 +204,7 @@ HandleSystemClean: 'System cache cleanup'
|
||||
SystemLog: 'System Log'
|
||||
CutWebsiteLog: 'Rotate Website Log'
|
||||
FileOrDir: 'Directory / File'
|
||||
UploadFile: 'Uploading backup file {{ .file }} to {{ .backup }}'
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: 'The current user does not exist, please modify and try again!'
|
||||
|
||||
@@ -92,6 +92,7 @@ ErrCustomApps: 'インストールされているアプリケーションがあ
|
||||
ErrCustomRuntimes: 'ランタイム環境がインストールされています。まずそれを削除してください'
|
||||
ErrAppVersionDeprecated: "{{ .name }} アプリケーションは現在の 1Panel バージョンと互換性がありません、スキップしました"
|
||||
ErrDockerFailed: "Docker の状態が異常です。サービス状態を確認してください"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose コマンドは存在しません。ホストマシンにこのコマンドを先にインストールしてください"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: 'このファイルはプレビューをサポートしていません'
|
||||
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: 'ソース ディレクトリが存在しません'
|
||||
ErrFavoriteExist: 'このパスはすでにお気に入りに登録されています'
|
||||
ErrInvalidChar: '不正な文字は許可されません'
|
||||
ErrPathNotDelete: '選択されたディレクトリは削除できません'
|
||||
ErrLogFileToLarge: "ログファイルが500MBを超えており、開くことができません"
|
||||
|
||||
#website
|
||||
ErrAliasIsExist: 'エイリアスがすでに存在します'
|
||||
@@ -202,6 +204,7 @@ HandleSystemClean: 'システム キャッシュのクリーンアップ'
|
||||
SystemLog: 'システムログ'
|
||||
CutWebsiteLog: 'ウェブサイトログのローテーション'
|
||||
FileOrDir: 'ディレクトリ / ファイル'
|
||||
UploadFile: 'バックアップファイル {{ .file }} を {{ .backup }} にアップロード中'
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: '現在のユーザーは存在しません。変更してもう一度お試しください。'
|
||||
|
||||
@@ -92,6 +92,7 @@ ErrCustomApps: '설치된 애플리케이션이 있습니다. 먼저 제거해
|
||||
ErrCustomRuntimes: '설치된 런타임 환경이 있습니다. 먼저 삭제해 주세요'
|
||||
ErrAppVersionDeprecated: "{{ .name }} 응용 프로그램은 현재 1Panel 버전과 호환되지 않아 건너뛰었습니다"
|
||||
ErrDockerFailed: "Docker의 상태가 비정상입니다. 서비스 상태를 확인하세요"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose 명령이 없습니다. 호스트 머신에 먼저 이 명령을 설치하세요"
|
||||
|
||||
#파일
|
||||
ErrFileCanNotRead: '이 파일은 미리보기를 지원하지 않습니다'
|
||||
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: '소스 디렉토리가 존재하지 않습니다'
|
||||
ErrFavoriteExist: '이 경로는 이미 즐겨찾기되었습니다'
|
||||
ErrInvalidChar: '불법 문자는 허용되지 않습니다'
|
||||
ErrPathNotDelete: '선택한 디렉토리를 삭제할 수 없습니다'
|
||||
ErrLogFileToLarge: "로그 파일이 500MB를 초과하여 열 수 없습니다"
|
||||
|
||||
#웹사이트
|
||||
ErrAliasIsExist: '별칭이 이미 존재합니다'
|
||||
@@ -202,6 +204,7 @@ HandleSystemClean: '시스템 캐시 정리'
|
||||
SystemLog: '시스템 로그'
|
||||
CutWebsiteLog: '웹사이트 로그 회전'
|
||||
FileOrDir: '디렉터리 / 파일'
|
||||
UploadFile: '백업 파일 {{ .file }} 을(를) {{ .backup }}(으)로 업로드 중'
|
||||
|
||||
#도구상자
|
||||
ErrNotExistUser: '현재 사용자가 존재하지 않습니다. 수정한 후 다시 시도하세요!'
|
||||
|
||||
@@ -92,6 +92,7 @@ ErrCustomApps: 'Ada aplikasi yang dipasang, sila nyahpasangnya dahulu'
|
||||
ErrCustomRuntimes: 'Terdapat persekitaran masa jalan yang dipasang, sila padamkannya dahulu'
|
||||
ErrAppVersionDeprecated: "Aplikasi {{ .name }} tidak sesuai dengan versi 1Panel saat ini, dilewati"
|
||||
ErrDockerFailed: "Keadaan Docker tidak normal, sila periksa status perkhidmatan"
|
||||
ErrDockerComposeCmdNotFound: "Perintah Docker Compose tidak wujud, sila pasang perintah ini di mesin tuan terlebih dahulu"
|
||||
|
||||
#fail
|
||||
ErrFileCanNotRead: 'Fail ini tidak menyokong pratonton'
|
||||
@@ -202,6 +203,7 @@ HandleSystemClean: 'Pembersihan cache sistem'
|
||||
SystemLog: 'Log Sistem'
|
||||
CutWebsiteLog: 'Putar Log Laman Web'
|
||||
FileOrDir: 'Direktori / Fail'
|
||||
UploadFile: 'Muat naik fail sandaran {{ .file }} ke {{ .backup }}'
|
||||
|
||||
#kotak alat
|
||||
ErrNotExistUser: 'Pengguna semasa tidak wujud, sila ubah suai dan cuba lagi!'
|
||||
|
||||
@@ -92,6 +92,7 @@ ErrCustomApps: 'Há um aplicativo instalado, desinstale-o primeiro'
|
||||
ErrCustomRuntimes: 'Há um ambiente de execução instalado, exclua-o primeiro'
|
||||
ErrAppVersionDeprecated: "O aplicativo {{ .name }} não é compatível com a versão atual do 1Panel, ignorado"
|
||||
ErrDockerFailed: "O estado do Docker está anormal, verifique o status do serviço"
|
||||
ErrDockerComposeCmdNotFound: "O comando Docker Compose não existe, por favor, instale este comando na máquina host primeiro"
|
||||
|
||||
#arquivo
|
||||
ErrFileCanNotRead: 'Este arquivo não suporta visualização'
|
||||
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: 'O diretório de origem não existe'
|
||||
ErrFavoriteExist: 'Este caminho já foi favorito'
|
||||
ErrInvalidChar: 'Caracteres ilegais não são permitidos'
|
||||
ErrPathNotDelete: 'O diretório selecionado não pode ser excluído'
|
||||
ErrLogFileToLarge: "Fail log melebihi 500MB, tidak boleh dibuka"
|
||||
|
||||
#site
|
||||
ErrAliasIsExist: 'Alias já existe'
|
||||
@@ -202,6 +204,7 @@ HandleSystemClean: 'Limpeza de cache do sistema'
|
||||
SystemLog: 'Log do Sistema'
|
||||
CutWebsiteLog: 'Rotacionar Log do Website'
|
||||
FileOrDir: 'Diretório / Arquivo'
|
||||
UploadFile: 'Enviando arquivo de backup {{ .file }} para {{ .backup }}'
|
||||
|
||||
#caixa de ferramentas
|
||||
ErrNotExistUser: 'O usuário atual não existe, modifique e tente novamente!'
|
||||
|
||||
@@ -92,6 +92,7 @@ ErrCustomApps: 'Установлено приложение. Сначала уд
|
||||
ErrCustomRuntimes: 'Установлена среда выполнения. Сначала удалите ее'
|
||||
ErrAppVersionDeprecated: "Приложение {{ .name }} несовместимо с текущей версией 1Panel, пропущено"
|
||||
ErrDockerFailed: "Состояние Docker аномально, проверьте состояние сервиса"
|
||||
ErrDockerComposeCmdNotFound: "Команда Docker Compose отсутствует, пожалуйста, установите эту команду на хост-машине сначала"
|
||||
|
||||
#файл
|
||||
ErrFileCanNotRead: 'Этот файл не поддерживает предварительный просмотр'
|
||||
@@ -107,6 +108,7 @@ ErrSourcePathNotFound: 'Исходный каталог не существуе
|
||||
ErrFavoriteExist: 'Этот путь уже добавлен в избранное'
|
||||
ErrInvalidChar: 'Недопустимые символы не допускаются'
|
||||
ErrPathNotDelete: 'Выбранный каталог не может быть удален'
|
||||
ErrLogFileToLarge: "Файл журнала превышает 500MB и не может быть открыт"
|
||||
|
||||
#веб-сайт
|
||||
ErrAliasIsExist: 'Псевдоним уже существует'
|
||||
@@ -202,6 +204,7 @@ HandleSystemClean: 'Очистка системного кэша'
|
||||
SystemLog: 'Системный лог'
|
||||
CutWebsiteLog: 'Ротация логов сайта'
|
||||
FileOrDir: 'Каталог / Файл'
|
||||
UploadFile: 'Загрузка файла резервной копии {{ .file }} в {{ .backup }}'
|
||||
|
||||
#ящик для инструментов
|
||||
ErrNotExistUser: 'Текущий пользователь не существует, измените его и повторите попытку!'
|
||||
|
||||
@@ -91,6 +91,7 @@ ErrCustomApps: '存在已安裝的應用程式,請先解除安裝'
|
||||
ErrCustomRuntimes: '存在已安裝的運作環境,請先刪除'
|
||||
ErrAppVersionDeprecated: "{{ .name }} 應用不適配目前 1Panel 版本,跳過"
|
||||
ErrDockerFailed: "Docker 狀態異常,請檢查服務狀態"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose 命令不存在,請先在宿主機安裝此命令"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: '此檔案不支援預覽'
|
||||
@@ -106,6 +107,7 @@ ErrSourcePathNotFound: '來源目錄不存在'
|
||||
ErrFavoriteExist: '已收藏此路徑'
|
||||
ErrInvalidChar: '禁止使用非法字元'
|
||||
ErrPathNotDelete: '所選目錄不可刪除'
|
||||
ErrLogFileToLarge: "日志文件超过 500M,無法打開"
|
||||
|
||||
#website
|
||||
ErrAliasIsExist: '代號已存在'
|
||||
@@ -201,6 +203,7 @@ HandleSystemClean: '系統快取清理'
|
||||
SystemLog: '系統日誌'
|
||||
CutWebsiteLog: '切割網站日誌'
|
||||
FileOrDir: '目錄 / 檔案'
|
||||
UploadFile: '上傳備份文件 {{ .file }} 到 {{ .backup }}'
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: '目前使用者不存在,請修改後重試!'
|
||||
|
||||
@@ -91,6 +91,7 @@ ErrCustomApps: "存在已经安装的应用,请先卸载"
|
||||
ErrCustomRuntimes: "存在已经安装的运行环境,请先删除"
|
||||
ErrAppVersionDeprecated: " {{ .name }} 应用不适配当前 1Panel 版本,跳过"
|
||||
ErrDockerFailed: "Docker 状态异常,请检查服务状态"
|
||||
ErrDockerComposeCmdNotFound: "Docker Compose 命令不存在,请先在宿主机安装此命令"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "此文件不支持预览"
|
||||
@@ -106,6 +107,7 @@ ErrSourcePathNotFound: "源目录不存在"
|
||||
ErrFavoriteExist: "已收藏此路径"
|
||||
ErrInvalidChar: "禁止使用非法字符"
|
||||
ErrPathNotDelete: "所选目录不可删除"
|
||||
ErrLogFileToLarge: "日志文件超过 500M,无法打开"
|
||||
|
||||
#website
|
||||
ErrAliasIsExist: "代号已存在"
|
||||
@@ -201,6 +203,7 @@ HandleSystemClean: "系统缓存清理"
|
||||
SystemLog: "系统日志"
|
||||
CutWebsiteLog: "切割网站日志"
|
||||
FileOrDir: "目录 / 文件"
|
||||
UploadFile: "上传备份文件 {{ .file }} 到 {{ .backup }}"
|
||||
|
||||
#toolbox
|
||||
ErrNotExistUser: "当前用户不存在,请修改后重试!"
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/service"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
@@ -15,6 +16,7 @@ func Init() {
|
||||
go syncSSL()
|
||||
go syncTask()
|
||||
go initAcmeAccount()
|
||||
go checkDockerCompose()
|
||||
}
|
||||
|
||||
func syncApp() {
|
||||
@@ -71,3 +73,16 @@ func initAcmeAccount() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkDockerCompose() {
|
||||
dockerComposCmd := common.GetDockerComposeCommand()
|
||||
if dockerComposCmd == "" {
|
||||
global.LOG.Errorf("Docker Compose command not found, please install Docker Compose Plugin")
|
||||
return
|
||||
}
|
||||
global.CONF.DockerConfig.Command = dockerComposCmd
|
||||
if err := service.NewISettingService().Update("DockerComposeCommand", dockerComposCmd); err != nil {
|
||||
global.LOG.Errorf("update docker compose command error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var whiteUrlList = map[string]struct{}{
|
||||
"/api/v2/auth/login": {},
|
||||
"/api/v2/websites/config": {},
|
||||
"/api/v2/websites/waf/config": {},
|
||||
"/api/v2/files/loadfile": {},
|
||||
"/api/v2/files/size": {},
|
||||
"/api/v2/logs/operation": {},
|
||||
"/api/v2/logs/login": {},
|
||||
"/api/v2/auth/logout": {},
|
||||
"/api/v1/dashboard/current": {},
|
||||
|
||||
"/api/v2/apps/installed/loadport": {},
|
||||
"/api/v2/apps/installed/check": {},
|
||||
"/api/v2/apps/installed/conninfo": {},
|
||||
"/api/v2/databases/load/file": {},
|
||||
"/api/v2/databases/variables": {},
|
||||
"/api/v2/databases/status": {},
|
||||
"/api/v2/databases/baseinfo": {},
|
||||
|
||||
"/api/v2/waf/attack/stat": {},
|
||||
"/api/v2/waf/config/website": {},
|
||||
|
||||
"/api/v2/monitor/stat": {},
|
||||
"/api/v2/monitor/visitors": {},
|
||||
"/api/v2/monitor/visitors/loc": {},
|
||||
"/api/v2/monitor/qps": {},
|
||||
}
|
||||
|
||||
func DemoHandle() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if strings.Contains(c.Request.URL.Path, "search") || c.Request.Method == http.MethodGet {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
if _, ok := whiteUrlList[c.Request.URL.Path]; ok {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusInternalServerError, dto.Response{
|
||||
Code: http.StatusInternalServerError,
|
||||
Message: buserr.New("ErrDemoEnvironment").Error(),
|
||||
})
|
||||
c.Abort()
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,8 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
baRouter.POST("/operate", baseApi.ContainerOperation)
|
||||
baRouter.POST("/prune", baseApi.ContainerPrune)
|
||||
|
||||
baRouter.POST("/users", baseApi.LoadContainerUsers)
|
||||
|
||||
baRouter.GET("/repo", baseApi.ListRepo)
|
||||
baRouter.POST("/repo/status", baseApi.CheckRepoStatus)
|
||||
baRouter.POST("/repo/search", baseApi.SearchRepo)
|
||||
|
||||
@@ -497,6 +497,7 @@ func (a *aliClient) completeUpload(uploadID, fileID string) error {
|
||||
|
||||
type tokenResp struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
func loadToken(refresh_token string) (string, error) {
|
||||
@@ -524,7 +525,7 @@ func loadToken(refresh_token string) (string, error) {
|
||||
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return respItem.RefreshToken, nil
|
||||
return respItem.AccessToken, nil
|
||||
}
|
||||
|
||||
func RefreshALIToken(varMap map[string]interface{}) (string, error) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
cosSDK "github.com/tencentyun/cos-go-sdk-v5"
|
||||
)
|
||||
@@ -18,6 +19,7 @@ type cosClient struct {
|
||||
|
||||
func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
region := loadParamFromVars("region", vars)
|
||||
endpoint := loadParamFromVars("endpoint", vars)
|
||||
accessKey := loadParamFromVars("accessKey", vars)
|
||||
secretKey := loadParamFromVars("secretKey", vars)
|
||||
bucket := loadParamFromVars("bucket", vars)
|
||||
@@ -26,7 +28,15 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
scType = "Standard"
|
||||
}
|
||||
|
||||
u, _ := url.Parse(fmt.Sprintf("https://cos.%s.myqcloud.com", region))
|
||||
endpointType := "cos"
|
||||
if len(endpoint) != 0 {
|
||||
re := regexp.MustCompile(`.*cos-dualstack\..*`)
|
||||
if re.MatchString(endpoint) {
|
||||
endpointType = "cos-dualstack"
|
||||
}
|
||||
}
|
||||
|
||||
u, _ := url.Parse(fmt.Sprintf("https://%s.%s.myqcloud.com", endpointType, region))
|
||||
b := &cosSDK.BaseURL{BucketURL: u}
|
||||
client := cosSDK.NewClient(b, &http.Client{
|
||||
Transport: &cosSDK.AuthorizationTransport{
|
||||
@@ -36,7 +46,7 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
})
|
||||
|
||||
if len(bucket) != 0 {
|
||||
u2, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucket, region))
|
||||
u2, _ := url.Parse(fmt.Sprintf("https://%s.%s.%s.myqcloud.com", bucket, endpointType, region))
|
||||
b2 := &cosSDK.BaseURL{BucketURL: u2}
|
||||
clientWithBucket := cosSDK.NewClient(b2, &http.Client{
|
||||
Transport: &cosSDK.AuthorizationTransport{
|
||||
|
||||
@@ -393,7 +393,6 @@ func RefreshGoogleToken(grantType string, tokenType string, varMap map[string]in
|
||||
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Println(respItem)
|
||||
if tokenType == "accessToken" {
|
||||
return respItem.AccessToken, nil
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ func NewCloudStorageClient(backupType string, vars map[string]interface{}) (Clou
|
||||
return client.NewUpClient(vars)
|
||||
case constant.ALIYUN:
|
||||
return client.NewALIClient(vars)
|
||||
case constant.GoogleDrive:
|
||||
return client.NewGoogleDriveClient(vars)
|
||||
default:
|
||||
return nil, buserr.WithName("ErrNotSupportType", backupType)
|
||||
}
|
||||
|
||||
@@ -120,9 +120,9 @@ func (c *CommandHelper) run(name string, arg ...string) (string, error) {
|
||||
cmd.Stdout = file
|
||||
cmd.Stderr = file
|
||||
} else if len(c.scriptPath) != 0 {
|
||||
cmd = exec.Command("bash", c.scriptPath)
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
cmd = exec.Command("bash", c.scriptPath)
|
||||
} else {
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
mathRand "math/rand"
|
||||
"net"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
@@ -409,3 +410,19 @@ func GetSystemVersion(versionString string) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func isCommandAvailable(name string, args ...string) bool {
|
||||
execCmd := exec.Command(name, args...)
|
||||
err := execCmd.Run()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func GetDockerComposeCommand() string {
|
||||
if isCommandAvailable("docker", "compose", "version") {
|
||||
return "docker compose"
|
||||
}
|
||||
if isCommandAvailable("docker-compose", "version") {
|
||||
return "docker-compose"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -1,40 +1,72 @@
|
||||
package compose
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
func checkCmd() error {
|
||||
if global.CONF.DockerConfig.Command == "" {
|
||||
dockerComposCmd := common.GetDockerComposeCommand()
|
||||
if dockerComposCmd == "" {
|
||||
return buserr.New("ErrDockerComposeCmdNotFound")
|
||||
}
|
||||
global.CONF.DockerConfig.Command = dockerComposCmd
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Up(filePath string) (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut("docker compose -f %s up -d", 20*time.Minute, filePath)
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCfAndTimeOut(global.CONF.DockerConfig.Command+" -f %s up -d", 20*time.Minute, filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Down(filePath string) (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s down --remove-orphans", filePath)
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s down --remove-orphans", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Stop(filePath string) (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s stop", filePath)
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s stop", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Restart(filePath string) (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s restart", filePath)
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s restart", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func Operate(filePath, operation string) (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s %s", filePath, operation)
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s %s", filePath, operation)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
func DownAndUp(filePath string) (string, error) {
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf("docker compose -f %s down", filePath)
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s down", filePath)
|
||||
if err != nil {
|
||||
return stdout, err
|
||||
}
|
||||
stdout, err = cmd.RunDefaultWithStdoutBashCf("docker compose -f %s up -d", filePath)
|
||||
stdout, err = cmd.RunDefaultWithStdoutBashCf(global.CONF.DockerConfig.Command+" -f %s up -d", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
@@ -144,14 +144,14 @@ func (c Client) NetworkExist(name string) bool {
|
||||
func CreateDefaultDockerNetwork() error {
|
||||
cli, err := NewClient()
|
||||
if err != nil {
|
||||
global.LOG.Errorf("init docker client error %s", err.Error())
|
||||
global.LOG.Warnf("init docker client error %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
defer cli.Close()
|
||||
if !cli.NetworkExist("1panel-network") {
|
||||
if err := cli.CreateNetwork("1panel-network"); err != nil {
|
||||
global.LOG.Errorf("create default docker network error %s", err.Error())
|
||||
global.LOG.Warnf("create default docker network error %s", err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/req_helper"
|
||||
"io"
|
||||
@@ -103,6 +104,16 @@ func ReadFileByLine(filename string, page, pageSize int, latest bool) (lines []s
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
fi, err := file.Stat()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if fi.Size() > 500*1024*1024 {
|
||||
err = buserr.New("ErrLogFileToLarge")
|
||||
return
|
||||
}
|
||||
|
||||
totalLines, err := countLines(filename)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -33,3 +33,12 @@ type IptablesNatInfo struct {
|
||||
SrcPort string `json:"srcPort"`
|
||||
DestPort string `json:"destPort"`
|
||||
}
|
||||
|
||||
type IptablesFilterInfo struct {
|
||||
Num string `json:"num"`
|
||||
Target string `json:"target"`
|
||||
Protocol string `json:"protocol"`
|
||||
Opt string `json:"opt"`
|
||||
Source string `json:"source"`
|
||||
Destination string `json:"destination"`
|
||||
}
|
||||
|
||||
@@ -10,9 +10,22 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
)
|
||||
|
||||
const (
|
||||
PreRoutingChain = "1PANEL_PREROUTING"
|
||||
PostRoutingChain = "1PANEL_POSTROUTING"
|
||||
ForwardChain = "1PANEL_FORWARD"
|
||||
)
|
||||
|
||||
const (
|
||||
FilterTab = "filter"
|
||||
NatTab = "nat"
|
||||
)
|
||||
|
||||
const NatChain = "1PANEL"
|
||||
|
||||
var NatListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`)
|
||||
var (
|
||||
natListRegex = regexp.MustCompile(`^(\d+)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)\s+(.+?)(?:\s+(.+?) .+?:(\d{1,5}(?::\d+)?).+?[ :](.+-.+|(?:.+:)?\d{1,5}(?:-\d{1,5})?))?$`)
|
||||
)
|
||||
|
||||
type Iptables struct {
|
||||
CmdStr string
|
||||
@@ -37,8 +50,24 @@ func (iptables *Iptables) run(rule string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iptables *Iptables) runf(rule string, a ...any) error {
|
||||
return iptables.run(fmt.Sprintf(rule, a...))
|
||||
func (iptables *Iptables) outf(tab, rule string, a ...any) (stdout string, err error) {
|
||||
stdout, err = cmd.RunDefaultWithStdoutBashCf("%s iptables -t %s %s", iptables.CmdStr, tab, fmt.Sprintf(rule, a...))
|
||||
if err != nil && stdout != "" {
|
||||
global.LOG.Errorf("iptables failed, err: %s", stdout)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (iptables *Iptables) runf(tab, rule string, a ...any) error {
|
||||
stdout, err := iptables.outf(tab, rule, a...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s, %s", err, stdout)
|
||||
}
|
||||
if stdout != "" {
|
||||
return fmt.Errorf("iptables error: %s", stdout)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iptables *Iptables) Check() error {
|
||||
@@ -47,26 +76,30 @@ func (iptables *Iptables) Check() error {
|
||||
return fmt.Errorf("%s, %s", err, stdout)
|
||||
}
|
||||
if stdout == "0" {
|
||||
return fmt.Errorf("disable")
|
||||
return fmt.Errorf("ipv4 forward disable")
|
||||
}
|
||||
|
||||
chain, _ := iptables.outf(NatTab, "-L -n | grep 'Chain %s'", PreRoutingChain)
|
||||
if len(strings.ReplaceAll(chain, "\n", "")) != 0 {
|
||||
return fmt.Errorf("chain enabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iptables *Iptables) NatNewChain() error {
|
||||
return iptables.runf("-N %s", NatChain)
|
||||
func (iptables *Iptables) NewChain(tab, chain string) error {
|
||||
return iptables.runf(tab, "-N %s", chain)
|
||||
}
|
||||
|
||||
func (iptables *Iptables) NatAppendChain() error {
|
||||
return iptables.runf("-A PREROUTING -j %s", NatChain)
|
||||
func (iptables *Iptables) AppendChain(tab string, chain, chain1 string) error {
|
||||
return iptables.runf(tab, "-A %s -j %s", chain, chain1)
|
||||
}
|
||||
|
||||
func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
|
||||
rule := fmt.Sprintf("%s iptables -t nat -nL %s --line", iptables.CmdStr, NatChain)
|
||||
if len(chain) == 1 {
|
||||
rule = fmt.Sprintf("%s iptables -t nat -nL %s --line", iptables.CmdStr, chain[0])
|
||||
if len(chain) == 0 {
|
||||
chain = append(chain, PreRoutingChain)
|
||||
}
|
||||
stdout, err := cmd.RunDefaultWithStdoutBashC(rule)
|
||||
stdout, err := iptables.outf(NatTab, "-nL %s --line", chain[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -76,8 +109,8 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
|
||||
line = strings.TrimFunc(line, func(r rune) bool {
|
||||
return r <= 32
|
||||
})
|
||||
if NatListRegex.MatchString(line) {
|
||||
match := NatListRegex.FindStringSubmatch(line)
|
||||
if natListRegex.MatchString(line) {
|
||||
match := natListRegex.FindStringSubmatch(line)
|
||||
if !strings.Contains(match[9], ":") {
|
||||
match[9] = fmt.Sprintf(":%s", match[9])
|
||||
}
|
||||
@@ -97,43 +130,126 @@ func (iptables *Iptables) NatList(chain ...string) ([]IptablesNatInfo, error) {
|
||||
return forwardList, nil
|
||||
}
|
||||
|
||||
func (iptables *Iptables) NatAdd(protocol, src, destIp, destPort string, save bool) error {
|
||||
rule := fmt.Sprintf("-A %s -p %s --dport %s -j REDIRECT --to-port %s", NatChain, protocol, src, destPort)
|
||||
if destIp != "" && destIp != "127.0.0.1" && destIp != "localhost" {
|
||||
rule = fmt.Sprintf("-A %s -p %s --dport %s -j DNAT --to-destination %s:%s", NatChain, protocol, src, destIp, destPort)
|
||||
}
|
||||
if err := iptables.run(rule); err != nil {
|
||||
return err
|
||||
func (iptables *Iptables) NatAdd(protocol, srcPort, dest, destPort string, save bool) error {
|
||||
if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
|
||||
if err := iptables.runf(NatTab, fmt.Sprintf(
|
||||
"-A %s -p %s --dport %s -j DNAT --to-destination %s:%s",
|
||||
PreRoutingChain,
|
||||
protocol,
|
||||
srcPort,
|
||||
dest,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iptables.runf(NatTab, fmt.Sprintf(
|
||||
"-A %s -d %s -p %s --dport %s -j MASQUERADE",
|
||||
PostRoutingChain,
|
||||
dest,
|
||||
protocol,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iptables.runf(FilterTab, fmt.Sprintf(
|
||||
"-A %s -d %s -p %s --dport %s -j ACCEPT",
|
||||
ForwardChain,
|
||||
dest,
|
||||
protocol,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iptables.runf(FilterTab, fmt.Sprintf(
|
||||
"-A %s -s %s -p %s --sport %s -j ACCEPT",
|
||||
ForwardChain,
|
||||
dest,
|
||||
protocol,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := iptables.runf(NatTab, fmt.Sprintf(
|
||||
"-A %s -p %s --dport %s -j REDIRECT --to-port %s",
|
||||
PreRoutingChain,
|
||||
protocol,
|
||||
srcPort,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if save {
|
||||
return global.DB.Save(&model.Forward{
|
||||
Protocol: protocol,
|
||||
Port: src,
|
||||
TargetIP: destIp,
|
||||
Port: srcPort,
|
||||
TargetIP: dest,
|
||||
TargetPort: destPort,
|
||||
}).Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iptables *Iptables) NatRemove(num string, protocol, src, destIp, destPort string) error {
|
||||
if err := iptables.runf("-D %s %s", NatChain, num); err != nil {
|
||||
func (iptables *Iptables) NatRemove(num string, protocol, srcPort, dest, destPort string) error {
|
||||
if err := iptables.runf(NatTab, "-D %s %s", PreRoutingChain, num); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dest != "" && dest != "127.0.0.1" && dest != "localhost" {
|
||||
if err := iptables.runf(NatTab, fmt.Sprintf(
|
||||
"-D %s -d %s -p %s --dport %s -j MASQUERADE",
|
||||
PostRoutingChain,
|
||||
dest,
|
||||
protocol,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iptables.runf(FilterTab, fmt.Sprintf(
|
||||
"-D %s -d %s -p %s --dport %s -j ACCEPT",
|
||||
ForwardChain,
|
||||
dest,
|
||||
protocol,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := iptables.runf(FilterTab, fmt.Sprintf(
|
||||
"-D %s -s %s -p %s --sport %s -j ACCEPT",
|
||||
ForwardChain,
|
||||
dest,
|
||||
protocol,
|
||||
destPort,
|
||||
)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
global.DB.Where(
|
||||
"protocol = ? AND port = ? AND target_ip = ? AND target_port = ?",
|
||||
protocol,
|
||||
src,
|
||||
destIp,
|
||||
srcPort,
|
||||
dest,
|
||||
destPort,
|
||||
).Delete(&model.Forward{})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iptables *Iptables) Reload() error {
|
||||
if err := iptables.runf("-F %s", NatChain); err != nil {
|
||||
if err := iptables.runf(NatTab, "-F %s", PreRoutingChain); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.runf(NatTab, "-F %s", PostRoutingChain); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := iptables.runf(FilterTab, "-F %s", ForwardChain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
)
|
||||
|
||||
@@ -100,16 +99,11 @@ func (f *Ufw) ListPort() ([]FireInfo, error) {
|
||||
}
|
||||
|
||||
func (f *Ufw) ListForward() ([]FireInfo, error) {
|
||||
_ = f.EnableForward()
|
||||
iptables, err := NewIptables()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
panelChian, _ := cmd.RunDefaultWithStdoutBashCf("%s iptables -t nat -L -n | grep 'Chain 1PANEL'", iptables.CmdStr)
|
||||
if len(strings.ReplaceAll(panelChian, "\n", "")) == 0 {
|
||||
if err := f.EnableForward(); err != nil {
|
||||
global.LOG.Errorf("init port forward failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
rules, err := iptables.NatList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -304,21 +298,33 @@ func (f *Ufw) EnableForward() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = iptables.NatNewChain()
|
||||
if err = iptables.Check(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = iptables.NewChain(NatTab, PreRoutingChain)
|
||||
_ = iptables.NewChain(NatTab, PostRoutingChain)
|
||||
_ = iptables.NewChain(FilterTab, ForwardChain)
|
||||
|
||||
if err = f.enableChain(iptables); err != nil {
|
||||
return err
|
||||
}
|
||||
return iptables.Reload()
|
||||
}
|
||||
|
||||
func (f *Ufw) enableChain(iptables *Iptables) error {
|
||||
rules, err := iptables.NatList("PREROUTING")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rule := range rules {
|
||||
if rule.Target == NatChain {
|
||||
goto reload
|
||||
if rule.Target == PreRoutingChain {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if err = iptables.NatAppendChain(); err != nil {
|
||||
return err
|
||||
}
|
||||
reload:
|
||||
return iptables.Reload()
|
||||
_ = iptables.AppendChain(NatTab, "PREROUTING", PreRoutingChain)
|
||||
_ = iptables.AppendChain(NatTab, "POSTROUTING", PostRoutingChain)
|
||||
_ = iptables.AppendChain(FilterTab, "FORWARD", ForwardChain)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func NewPostgresqlClient(conn client.DBInfo) (PostgresqlClient, error) {
|
||||
}
|
||||
escapedUsername := url.QueryEscape(conn.Username)
|
||||
escapedPassword := url.QueryEscape(conn.Password)
|
||||
connArgs := fmt.Sprintf("postgres://%s:%s@%s:%d/?sslmode=disable", escapedUsername, escapedPassword, conn.Address, conn.Port)
|
||||
connArgs := fmt.Sprintf("postgres://%s:%s@%s:%d/postgres?sslmode=disable", escapedUsername, escapedPassword, conn.Address, conn.Port)
|
||||
db, err := sql.Open("pgx", connArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+144
-123
@@ -4,10 +4,12 @@ import (
|
||||
"encoding/json"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/providers/dns/alidns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/baiducloud"
|
||||
"github.com/go-acme/lego/v4/providers/dns/clouddns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
|
||||
"github.com/go-acme/lego/v4/providers/dns/cloudns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/dnspod"
|
||||
"github.com/go-acme/lego/v4/providers/dns/dynu"
|
||||
"github.com/go-acme/lego/v4/providers/dns/freemyip"
|
||||
"github.com/go-acme/lego/v4/providers/dns/godaddy"
|
||||
"github.com/go-acme/lego/v4/providers/dns/huaweicloud"
|
||||
@@ -45,6 +47,8 @@ const (
|
||||
WestCN DnsType = "WestCN"
|
||||
ClouDNS DnsType = "ClouDNS"
|
||||
RegRu DnsType = "RegRu"
|
||||
Dynu DnsType = "Dynu"
|
||||
BaiduCloud DnsType = "BaiduCloud"
|
||||
)
|
||||
|
||||
type DNSParam struct {
|
||||
@@ -85,146 +89,163 @@ func getDNSProviderConfig(dnsType DnsType, params string) (challenge.Provider, e
|
||||
}
|
||||
switch dnsType {
|
||||
case DnsPod:
|
||||
dnsPodConfig := dnspod.NewDefaultConfig()
|
||||
dnsPodConfig.LoginToken = param.ID + "," + param.Token
|
||||
dnsPodConfig.PropagationTimeout = propagationTimeout
|
||||
dnsPodConfig.PollingInterval = pollingInterval
|
||||
dnsPodConfig.TTL = ttl
|
||||
p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
|
||||
config := dnspod.NewDefaultConfig()
|
||||
config.LoginToken = param.ID + "," + param.Token
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = dnspod.NewDNSProviderConfig(config)
|
||||
case AliYun:
|
||||
alidnsConfig := alidns.NewDefaultConfig()
|
||||
alidnsConfig.SecretKey = param.SecretKey
|
||||
alidnsConfig.APIKey = param.AccessKey
|
||||
alidnsConfig.PropagationTimeout = propagationTimeout
|
||||
alidnsConfig.PollingInterval = pollingInterval
|
||||
alidnsConfig.TTL = ttl
|
||||
p, err = alidns.NewDNSProviderConfig(alidnsConfig)
|
||||
config := alidns.NewDefaultConfig()
|
||||
config.SecretKey = param.SecretKey
|
||||
config.APIKey = param.AccessKey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = alidns.NewDNSProviderConfig(config)
|
||||
case CloudFlare:
|
||||
cloudflareConfig := cloudflare.NewDefaultConfig()
|
||||
cloudflareConfig.AuthEmail = param.Email
|
||||
cloudflareConfig.AuthToken = param.APIkey
|
||||
cloudflareConfig.PropagationTimeout = propagationTimeout
|
||||
cloudflareConfig.PollingInterval = pollingInterval
|
||||
cloudflareConfig.TTL = ttl
|
||||
p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig)
|
||||
config := cloudflare.NewDefaultConfig()
|
||||
config.AuthEmail = param.Email
|
||||
config.AuthToken = param.APIkey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = cloudflare.NewDNSProviderConfig(config)
|
||||
case CloudDns:
|
||||
clouddnsConfig := clouddns.NewDefaultConfig()
|
||||
clouddnsConfig.ClientID = param.ClientID
|
||||
clouddnsConfig.Email = param.Email
|
||||
clouddnsConfig.Password = param.Password
|
||||
clouddnsConfig.PropagationTimeout = propagationTimeout
|
||||
clouddnsConfig.PollingInterval = pollingInterval
|
||||
clouddnsConfig.TTL = ttl
|
||||
p, err = clouddns.NewDNSProviderConfig(clouddnsConfig)
|
||||
config := clouddns.NewDefaultConfig()
|
||||
config.ClientID = param.ClientID
|
||||
config.Email = param.Email
|
||||
config.Password = param.Password
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = clouddns.NewDNSProviderConfig(config)
|
||||
case NameCheap:
|
||||
namecheapConfig := namecheap.NewDefaultConfig()
|
||||
namecheapConfig.APIKey = param.APIkey
|
||||
namecheapConfig.APIUser = param.APIUser
|
||||
namecheapConfig.PropagationTimeout = propagationTimeout
|
||||
namecheapConfig.PollingInterval = pollingInterval
|
||||
namecheapConfig.TTL = ttl
|
||||
p, err = namecheap.NewDNSProviderConfig(namecheapConfig)
|
||||
config := namecheap.NewDefaultConfig()
|
||||
config.APIKey = param.APIkey
|
||||
config.APIUser = param.APIUser
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = namecheap.NewDNSProviderConfig(config)
|
||||
case NameSilo:
|
||||
nameSiloConfig := namesilo.NewDefaultConfig()
|
||||
nameSiloConfig.APIKey = param.APIkey
|
||||
nameSiloConfig.PropagationTimeout = propagationTimeout
|
||||
nameSiloConfig.PollingInterval = pollingInterval
|
||||
nameSiloConfig.TTL = ttl
|
||||
p, err = namesilo.NewDNSProviderConfig(nameSiloConfig)
|
||||
config := namesilo.NewDefaultConfig()
|
||||
config.APIKey = param.APIkey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = namesilo.NewDNSProviderConfig(config)
|
||||
case Godaddy:
|
||||
godaddyConfig := godaddy.NewDefaultConfig()
|
||||
godaddyConfig.APIKey = param.APIkey
|
||||
godaddyConfig.APISecret = param.APISecret
|
||||
godaddyConfig.PropagationTimeout = propagationTimeout
|
||||
godaddyConfig.PollingInterval = pollingInterval
|
||||
godaddyConfig.TTL = ttl
|
||||
p, err = godaddy.NewDNSProviderConfig(godaddyConfig)
|
||||
config := godaddy.NewDefaultConfig()
|
||||
config.APIKey = param.APIkey
|
||||
config.APISecret = param.APISecret
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = godaddy.NewDNSProviderConfig(config)
|
||||
case NameCom:
|
||||
nameComConfig := namedotcom.NewDefaultConfig()
|
||||
nameComConfig.APIToken = param.Token
|
||||
nameComConfig.Username = param.APIUser
|
||||
nameComConfig.PropagationTimeout = propagationTimeout
|
||||
nameComConfig.PollingInterval = pollingInterval
|
||||
nameComConfig.TTL = ttl
|
||||
p, err = namedotcom.NewDNSProviderConfig(nameComConfig)
|
||||
config := namedotcom.NewDefaultConfig()
|
||||
config.APIToken = param.Token
|
||||
config.Username = param.APIUser
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = namedotcom.NewDNSProviderConfig(config)
|
||||
case TencentCloud:
|
||||
tencentCloudConfig := tencentcloud.NewDefaultConfig()
|
||||
tencentCloudConfig.SecretID = param.SecretID
|
||||
tencentCloudConfig.SecretKey = param.SecretKey
|
||||
tencentCloudConfig.PropagationTimeout = propagationTimeout
|
||||
tencentCloudConfig.PollingInterval = pollingInterval
|
||||
tencentCloudConfig.TTL = ttl
|
||||
p, err = tencentcloud.NewDNSProviderConfig(tencentCloudConfig)
|
||||
config := tencentcloud.NewDefaultConfig()
|
||||
config.SecretID = param.SecretID
|
||||
config.SecretKey = param.SecretKey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = tencentcloud.NewDNSProviderConfig(config)
|
||||
case RainYun:
|
||||
rainyunConfig := rainyun.NewDefaultConfig()
|
||||
rainyunConfig.APIKey = param.APIkey
|
||||
rainyunConfig.PropagationTimeout = propagationTimeout
|
||||
rainyunConfig.PollingInterval = pollingInterval
|
||||
rainyunConfig.TTL = ttl
|
||||
p, err = rainyun.NewDNSProviderConfig(rainyunConfig)
|
||||
config := rainyun.NewDefaultConfig()
|
||||
config.APIKey = param.APIkey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = rainyun.NewDNSProviderConfig(config)
|
||||
case Volcengine:
|
||||
volcConfig := volcengine.NewDefaultConfig()
|
||||
volcConfig.SecretKey = param.SecretKey
|
||||
volcConfig.AccessKey = param.AccessKey
|
||||
volcConfig.PropagationTimeout = propagationTimeout
|
||||
volcConfig.PollingInterval = pollingInterval
|
||||
volcConfig.TTL = ttl
|
||||
p, err = volcengine.NewDNSProviderConfig(volcConfig)
|
||||
config := volcengine.NewDefaultConfig()
|
||||
config.SecretKey = param.SecretKey
|
||||
config.AccessKey = param.AccessKey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = volcengine.NewDNSProviderConfig(config)
|
||||
case HuaweiCloud:
|
||||
huaweiCloudConfig := huaweicloud.NewDefaultConfig()
|
||||
huaweiCloudConfig.AccessKeyID = param.AccessKey
|
||||
huaweiCloudConfig.SecretAccessKey = param.SecretKey
|
||||
huaweiCloudConfig.Region = param.Region
|
||||
huaweiCloudConfig.PropagationTimeout = propagationTimeout
|
||||
huaweiCloudConfig.PollingInterval = pollingInterval
|
||||
huaweiCloudConfig.TTL = int32(ttl)
|
||||
p, err = huaweicloud.NewDNSProviderConfig(huaweiCloudConfig)
|
||||
config := huaweicloud.NewDefaultConfig()
|
||||
config.AccessKeyID = param.AccessKey
|
||||
config.SecretAccessKey = param.SecretKey
|
||||
config.Region = param.Region
|
||||
if config.Region == "" {
|
||||
config.Region = "cn-north-1"
|
||||
}
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = int32(ttl)
|
||||
p, err = huaweicloud.NewDNSProviderConfig(config)
|
||||
case FreeMyIP:
|
||||
freeMyIpConfig := freemyip.NewDefaultConfig()
|
||||
freeMyIpConfig.Token = param.Token
|
||||
freeMyIpConfig.PropagationTimeout = propagationTimeout
|
||||
freeMyIpConfig.PollingInterval = pollingInterval
|
||||
p, err = freemyip.NewDNSProviderConfig(freeMyIpConfig)
|
||||
config := freemyip.NewDefaultConfig()
|
||||
config.Token = param.Token
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
p, err = freemyip.NewDNSProviderConfig(config)
|
||||
case Vercel:
|
||||
vercelConfig := vercel.NewDefaultConfig()
|
||||
vercelConfig.AuthToken = param.Token
|
||||
vercelConfig.PropagationTimeout = propagationTimeout
|
||||
vercelConfig.PollingInterval = pollingInterval
|
||||
p, err = vercel.NewDNSProviderConfig(vercelConfig)
|
||||
config := vercel.NewDefaultConfig()
|
||||
config.AuthToken = param.Token
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
p, err = vercel.NewDNSProviderConfig(config)
|
||||
case Spaceship:
|
||||
spaceshipConfig := spaceship.NewDefaultConfig()
|
||||
spaceshipConfig.APIKey = param.APIkey
|
||||
spaceshipConfig.APISecret = param.APISecret
|
||||
spaceshipConfig.PropagationTimeout = propagationTimeout
|
||||
spaceshipConfig.PollingInterval = pollingInterval
|
||||
spaceshipConfig.TTL = ttl
|
||||
p, err = spaceship.NewDNSProviderConfig(spaceshipConfig)
|
||||
config := spaceship.NewDefaultConfig()
|
||||
config.APIKey = param.APIkey
|
||||
config.APISecret = param.APISecret
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = spaceship.NewDNSProviderConfig(config)
|
||||
case WestCN:
|
||||
westcnConfig := westcn.NewDefaultConfig()
|
||||
westcnConfig.Username = param.Username
|
||||
westcnConfig.Password = param.Password
|
||||
westcnConfig.PropagationTimeout = propagationTimeout
|
||||
westcnConfig.PollingInterval = pollingInterval
|
||||
westcnConfig.TTL = ttl
|
||||
p, err = westcn.NewDNSProviderConfig(westcnConfig)
|
||||
|
||||
config := westcn.NewDefaultConfig()
|
||||
config.Username = param.Username
|
||||
config.Password = param.Password
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = westcn.NewDNSProviderConfig(config)
|
||||
case ClouDNS:
|
||||
cloudnsConfig := cloudns.NewDefaultConfig()
|
||||
cloudnsConfig.AuthID = param.AuthID
|
||||
cloudnsConfig.SubAuthID = param.SubAuthID
|
||||
cloudnsConfig.AuthPassword = param.AuthPassword
|
||||
cloudnsConfig.PropagationTimeout = propagationTimeout
|
||||
cloudnsConfig.PollingInterval = pollingInterval
|
||||
cloudnsConfig.TTL = ttl
|
||||
p, err = cloudns.NewDNSProviderConfig(cloudnsConfig)
|
||||
config := cloudns.NewDefaultConfig()
|
||||
config.AuthID = param.AuthID
|
||||
config.SubAuthID = param.SubAuthID
|
||||
config.AuthPassword = param.AuthPassword
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = cloudns.NewDNSProviderConfig(config)
|
||||
case RegRu:
|
||||
regRuConfig := regru.NewDefaultConfig()
|
||||
regRuConfig.Username = param.Username
|
||||
regRuConfig.Password = param.Password
|
||||
regRuConfig.PropagationTimeout = propagationTimeout
|
||||
regRuConfig.PollingInterval = pollingInterval
|
||||
regRuConfig.TTL = ttl
|
||||
p, err = regru.NewDNSProviderConfig(regRuConfig)
|
||||
config := regru.NewDefaultConfig()
|
||||
config.Username = param.Username
|
||||
config.Password = param.Password
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = regru.NewDNSProviderConfig(config)
|
||||
case Dynu:
|
||||
config := dynu.NewDefaultConfig()
|
||||
config.APIKey = param.APIkey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = dynu.NewDNSProviderConfig(config)
|
||||
case BaiduCloud:
|
||||
config := baiducloud.NewDefaultConfig()
|
||||
config.AccessKeyID = param.AccessKey
|
||||
config.SecretAccessKey = param.SecretKey
|
||||
config.PropagationTimeout = propagationTimeout
|
||||
config.PollingInterval = pollingInterval
|
||||
config.TTL = ttl
|
||||
p, err = baiducloud.NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package systemctl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"os/exec"
|
||||
@@ -16,31 +17,65 @@ func RunSystemCtl(args ...string) (string, error) {
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
func isSnapService(serviceName string) bool {
|
||||
cmd := exec.Command("snap", "services")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
if err := cmd.Run(); err != nil {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(out.String(), serviceName)
|
||||
}
|
||||
|
||||
func isSnapServiceActive(serviceName string) bool {
|
||||
cmd := exec.Command("snap", "services")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
if err := cmd.Run(); err != nil {
|
||||
return false
|
||||
}
|
||||
lines := strings.Split(out.String(), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, serviceName) && strings.Contains(line, "active") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsActive(serviceName string) (bool, error) {
|
||||
out, err := RunSystemCtl("is-active", serviceName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
if err == nil {
|
||||
return strings.TrimSpace(out) == "active", nil
|
||||
}
|
||||
return out == "active\n", nil
|
||||
|
||||
if isSnapServiceActive(serviceName) {
|
||||
return true, nil
|
||||
}
|
||||
return false, fmt.Errorf("service %s is not active: %v", serviceName, err)
|
||||
}
|
||||
|
||||
func IsEnable(serviceName string) (bool, error) {
|
||||
out, err := RunSystemCtl("is-enabled", serviceName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
if err == nil {
|
||||
return strings.TrimSpace(out) == "enabled", nil
|
||||
}
|
||||
return out == "enabled\n", nil
|
||||
|
||||
if isSnapServiceActive(serviceName) {
|
||||
return true, nil
|
||||
}
|
||||
return false, fmt.Errorf("failed to determine if service %s is enabled: %v", serviceName, err)
|
||||
}
|
||||
|
||||
func IsExist(serviceName string) (bool, error) {
|
||||
out, err := RunSystemCtl("is-enabled", serviceName)
|
||||
if err != nil {
|
||||
if strings.Contains(out, "disabled") {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
if err == nil || strings.Contains(out, "disabled") {
|
||||
return true, nil
|
||||
}
|
||||
return true, nil
|
||||
if isSnapService(serviceName) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func handlerErr(out string, err error) error {
|
||||
@@ -55,10 +90,27 @@ func handlerErr(out string, err error) error {
|
||||
|
||||
func Restart(serviceName string) error {
|
||||
out, err := RunSystemCtl("restart", serviceName)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if isSnapService(serviceName) {
|
||||
cmd := exec.Command("snap", "restart", serviceName)
|
||||
output, snapErr := cmd.CombinedOutput()
|
||||
return handlerErr(string(output), snapErr)
|
||||
}
|
||||
return handlerErr(out, err)
|
||||
}
|
||||
|
||||
func Operate(operate, serviceName string) error {
|
||||
out, err := RunSystemCtl(operate, serviceName)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if isSnapService(serviceName) && (operate == "start" || operate == "stop" || operate == "restart") {
|
||||
cmd := exec.Command("snap", operate, serviceName)
|
||||
output, snapErr := cmd.CombinedOutput()
|
||||
return handlerErr(string(output), snapErr)
|
||||
}
|
||||
return handlerErr(out, err)
|
||||
}
|
||||
|
||||
+10
-1
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/core/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/core/app/model"
|
||||
"github.com/1Panel-dev/1Panel/core/constant"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/captcha"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -133,7 +134,15 @@ func (b *BaseApi) GetLoginSetting(c *gin.Context) {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, settingInfo)
|
||||
res := &dto.LoginSetting{
|
||||
IsDemo: global.CONF.Base.IsDemo,
|
||||
IsIntl: global.CONF.Base.IsIntl,
|
||||
Language: settingInfo.Language,
|
||||
MenuTabs: settingInfo.MenuTabs,
|
||||
PanelName: settingInfo.PanelName,
|
||||
Theme: settingInfo.Theme,
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
func saveLoginLogs(c *gin.Context, err error) {
|
||||
|
||||
@@ -219,3 +219,12 @@ type AppstoreConfig struct {
|
||||
UpgradeBackup string `json:"upgradeBackup"`
|
||||
UninstallDeleteBackup string `json:"uninstallDeleteBackup"`
|
||||
}
|
||||
|
||||
type LoginSetting struct {
|
||||
IsDemo bool `json:"isDemo"`
|
||||
IsIntl bool `json:"isIntl"`
|
||||
Language string `json:"language"`
|
||||
MenuTabs string `json:"menuTabs"`
|
||||
PanelName string `json:"panelName"`
|
||||
Theme string `json:"theme"`
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
|
||||
return nil, "", buserr.New("ErrRecordNotFound")
|
||||
}
|
||||
if nameSetting.Value != info.Name {
|
||||
return nil, "ErrAuth", nil
|
||||
return nil, "ErrAuth", buserr.New("ErrAuth")
|
||||
}
|
||||
if err = checkPassword(info.Password); err != nil {
|
||||
return nil, "ErrAuth", err
|
||||
@@ -47,7 +47,7 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d
|
||||
return nil, "", err
|
||||
}
|
||||
if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance {
|
||||
return nil, "ErrEntrance", nil
|
||||
return nil, "ErrEntrance", buserr.New("ErrEntrance")
|
||||
}
|
||||
mfa, err := settingRepo.Get(repo.WithByKey("MFAStatus"))
|
||||
if err != nil {
|
||||
|
||||
@@ -284,7 +284,15 @@ func (u *BackupService) RefreshToken(req dto.OperateByName) error {
|
||||
|
||||
varsItem, _ := json.Marshal(varMap)
|
||||
backup.Vars = string(varsItem)
|
||||
return backupRepo.Save(&backup)
|
||||
if err := backupRepo.Save(&backup); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
if err := xpack.Sync(constant.SyncBackupAccounts); err != nil {
|
||||
global.LOG.Errorf("sync backup account to node failed, err: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *BackupService) NewClient(backup *model.BackupAccount) (cloud_storage.CloudStorageClient, error) {
|
||||
|
||||
@@ -11,7 +11,6 @@ var (
|
||||
groupRepo = repo.NewIGroupRepo()
|
||||
upgradeLogRepo = repo.NewIUpgradeLogRepo()
|
||||
|
||||
taskRepo = repo.NewITaskRepo()
|
||||
agentRepo = repo.NewIAgentRepo()
|
||||
scriptRepo = repo.NewIScriptRepo()
|
||||
)
|
||||
|
||||
@@ -188,34 +188,37 @@ func (u *ScriptService) Sync(req dto.OperateByTaskID) error {
|
||||
}
|
||||
var scriptSetting model.Setting
|
||||
_ = global.DB.Where("key = ?", "ScriptVersion").First(&scriptSetting).Error
|
||||
if scriptSetting.Value == string(versionRes) {
|
||||
syncTask.Logf("The local-%s and remote-%s versions are detected to be consistent. Skip...", scriptSetting.Value, versionRes)
|
||||
localVersion := strings.ReplaceAll(string(versionRes), "\n", "")
|
||||
remoteVersion := strings.ReplaceAll(scriptSetting.Value, "\n", "")
|
||||
|
||||
if localVersion == remoteVersion {
|
||||
syncTask.Log(i18n.GetMsgByKey("ScriptSyncSkip"))
|
||||
return nil
|
||||
}
|
||||
|
||||
syncTask.Log("start to download data.yaml")
|
||||
dataUrl := fmt.Sprintf("%s/scripts/data.yaml", global.CONF.RemoteURL.ResourceURL)
|
||||
_, dataRes, err := req_helper.HandleRequestWithProxy(dataUrl, http.MethodGet, constant.TimeOut20s)
|
||||
syncTask.LogWithStatus(i18n.GetMsgByKey("DownloadData"), err)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load scripts data.yaml from remote failed, err: %v", err)
|
||||
}
|
||||
|
||||
syncTask.Log("download successful!")
|
||||
var scripts Scripts
|
||||
if err = yaml.Unmarshal(dataRes, &scripts); err != nil {
|
||||
return fmt.Errorf("the format of data.yaml is err: %v", err)
|
||||
}
|
||||
|
||||
syncTask.Log("start to download scripts.tar.gz")
|
||||
tmpDir := path.Join(global.CONF.Base.InstallDir, "1panel/tmp/script")
|
||||
if _, err := os.Stat(tmpDir); err != nil {
|
||||
_ = os.MkdirAll(tmpDir, 0755)
|
||||
}
|
||||
scriptsUrl := fmt.Sprintf("%s/scripts/scripts.tar.gz", global.CONF.RemoteURL.ResourceURL)
|
||||
if err := files.DownloadFileWithProxy(scriptsUrl, tmpDir+"/scripts.tar.gz"); err != nil {
|
||||
err = files.DownloadFileWithProxy(scriptsUrl, tmpDir+"/scripts.tar.gz")
|
||||
syncTask.LogWithStatus(i18n.GetMsgByKey("DownloadPackage"), err)
|
||||
if err != nil {
|
||||
return fmt.Errorf("download scripts.tar.gz failed, err: %v", err)
|
||||
}
|
||||
syncTask.Log("download successful! now start to decompress...")
|
||||
|
||||
if err := files.HandleUnTar(tmpDir+"/scripts.tar.gz", tmpDir, ""); err != nil {
|
||||
return fmt.Errorf("handle decompress scripts.tar.gz failed, err: %v", err)
|
||||
}
|
||||
@@ -234,7 +237,7 @@ func (u *ScriptService) Sync(req dto.OperateByTaskID) error {
|
||||
scriptsForDB = append(scriptsForDB, scriptItem)
|
||||
}
|
||||
|
||||
syncTask.Log("analytic completion! now start to refresh database...")
|
||||
syncTask.Log(i18n.GetMsgByKey("AnalyticCompletion"))
|
||||
if err := scriptRepo.SyncAll(scriptsForDB); err != nil {
|
||||
return fmt.Errorf("sync script with db failed, err: %v", err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
cpuProfile string
|
||||
memProfile string
|
||||
duration time.Duration
|
||||
showMemStats bool
|
||||
showGoroutine bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(debugCmd)
|
||||
|
||||
debugCmd.Flags().StringVar(&cpuProfile, "cpuprofile", "", "write cpu profile to file")
|
||||
debugCmd.Flags().StringVar(&memProfile, "memprofile", "", "write memory profile to file")
|
||||
debugCmd.Flags().DurationVar(&duration, "duration", 30*time.Second, "duration for cpu profiling")
|
||||
debugCmd.Flags().BoolVar(&showMemStats, "memstats", false, "show memory statistics")
|
||||
debugCmd.Flags().BoolVar(&showGoroutine, "goroutine", false, "show goroutine profile")
|
||||
}
|
||||
|
||||
var debugCmd = &cobra.Command{
|
||||
Use: "debug",
|
||||
Short: "Performance debugging with pprof",
|
||||
Long: `Debug command provides CPU and memory profiling capabilities using pprof.
|
||||
|
||||
Examples:
|
||||
# CPU profiling for 30 seconds
|
||||
1panel-core debug --cpuprofile=cpu.prof --duration=30s
|
||||
|
||||
# Memory profiling
|
||||
1panel-core debug --memprofile=mem.prof
|
||||
|
||||
# Show memory statistics
|
||||
1panel-core debug --memstats
|
||||
|
||||
# Show goroutine information
|
||||
1panel-core debug --goroutine
|
||||
|
||||
# Combined profiling
|
||||
1panel-core debug --cpuprofile=cpu.prof --memprofile=mem.prof --memstats`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if cpuProfile != "" {
|
||||
if err := startCPUProfile(cpuProfile); err != nil {
|
||||
return fmt.Errorf("failed to start CPU profile: %v", err)
|
||||
}
|
||||
defer stopCPUProfile()
|
||||
time.Sleep(duration)
|
||||
fmt.Printf("CPU profile saved to: %s\n", cpuProfile)
|
||||
}
|
||||
|
||||
if memProfile != "" {
|
||||
if err := writeMemProfile(memProfile); err != nil {
|
||||
return fmt.Errorf("failed to write memory profile: %v", err)
|
||||
}
|
||||
fmt.Printf("Memory profile saved to: %s\n", memProfile)
|
||||
}
|
||||
|
||||
if showMemStats {
|
||||
printMemStats()
|
||||
}
|
||||
|
||||
if showGoroutine {
|
||||
printGoroutineInfo()
|
||||
}
|
||||
|
||||
if cpuProfile == "" && memProfile == "" && !showMemStats && !showGoroutine {
|
||||
printBasicInfo()
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func startCPUProfile(filename string) error {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := pprof.StartCPUProfile(f); err != nil {
|
||||
f.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func stopCPUProfile() {
|
||||
pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
func writeMemProfile(filename string) error {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
runtime.GC()
|
||||
|
||||
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printMemStats() {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
|
||||
fmt.Println("\n=== Memory Statistics ===")
|
||||
fmt.Printf("Allocated memory: %d KB (%d MB)\n", m.Alloc/1024, m.Alloc/1024/1024)
|
||||
fmt.Printf("Total allocated: %d KB (%d MB)\n", m.TotalAlloc/1024, m.TotalAlloc/1024/1024)
|
||||
fmt.Printf("System memory: %d KB (%d MB)\n", m.Sys/1024, m.Sys/1024/1024)
|
||||
fmt.Printf("Heap allocated: %d KB (%d MB)\n", m.HeapAlloc/1024, m.HeapAlloc/1024/1024)
|
||||
fmt.Printf("Heap system: %d KB (%d MB)\n", m.HeapSys/1024, m.HeapSys/1024/1024)
|
||||
fmt.Printf("Heap idle: %d KB (%d MB)\n", m.HeapIdle/1024, m.HeapIdle/1024/1024)
|
||||
fmt.Printf("Heap in use: %d KB (%d MB)\n", m.HeapInuse/1024, m.HeapInuse/1024/1024)
|
||||
fmt.Printf("Heap released: %d KB (%d MB)\n", m.HeapReleased/1024, m.HeapReleased/1024/1024)
|
||||
fmt.Printf("Heap objects: %d\n", m.HeapObjects)
|
||||
fmt.Printf("GC runs: %d\n", m.NumGC)
|
||||
fmt.Printf("GC pause total: %d ns\n", m.PauseTotalNs)
|
||||
fmt.Printf("Next GC: %d KB (%d MB)\n", m.NextGC/1024, m.NextGC/1024/1024)
|
||||
fmt.Printf("Last GC: %s\n", time.Unix(0, int64(m.LastGC)).Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
func printGoroutineInfo() {
|
||||
fmt.Println("\n=== Goroutine Information ===")
|
||||
fmt.Printf("Number of goroutines: %d\n", runtime.NumGoroutine())
|
||||
fmt.Printf("Number of CPUs: %d\n", runtime.NumCPU())
|
||||
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
|
||||
|
||||
buf := make([]byte, 1024*1024)
|
||||
stackSize := runtime.Stack(buf, true)
|
||||
fmt.Printf("\nGoroutine stack trace (first 2048 bytes):\n")
|
||||
if stackSize > 2048 {
|
||||
fmt.Printf("%s...\n", buf[:2048])
|
||||
fmt.Printf("(truncated, total size: %d bytes)\n", stackSize)
|
||||
} else {
|
||||
fmt.Printf("%s\n", buf[:stackSize])
|
||||
}
|
||||
}
|
||||
|
||||
func printBasicInfo() {
|
||||
fmt.Println("=== Performance Debug Info ===")
|
||||
fmt.Printf("Go version: %s\n", runtime.Version())
|
||||
fmt.Printf("OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
|
||||
fmt.Printf("CPUs: %d\n", runtime.NumCPU())
|
||||
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
|
||||
fmt.Printf("Goroutines: %d\n", runtime.NumGoroutine())
|
||||
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
fmt.Printf("Memory allocated: %d KB\n", m.Alloc/1024)
|
||||
fmt.Printf("Total allocations: %d KB\n", m.TotalAlloc/1024)
|
||||
fmt.Printf("GC runs: %d\n", m.NumGC)
|
||||
|
||||
fmt.Println("\nUse --help to see available profiling options.")
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func port() {
|
||||
fmt.Println("\n" + i18n.GetMsgByKeyForCmd("UpdateSuccessful"))
|
||||
fmt.Println(i18n.GetMsgWithMapForCmd("UpdatePortResult", map[string]interface{}{"name": newPortStr}))
|
||||
|
||||
std, err := cmd.RunDefaultWithStdoutBashC("1pctl restart")
|
||||
std, err := cmd.RunDefaultWithStdoutBashC("1pctl restart core")
|
||||
if err != nil {
|
||||
fmt.Println(std)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ const (
|
||||
GoogleDrive = "GoogleDrive"
|
||||
|
||||
OneDriveRedirectURI = "http://localhost/login/authorized"
|
||||
GoogleRedirectURI = "http://localhost:8080"
|
||||
GoogleRedirectURI = "https://localhost:8080"
|
||||
|
||||
DaemonJsonPath = "/etc/docker/daemon.json"
|
||||
)
|
||||
|
||||
@@ -23,6 +23,7 @@ require (
|
||||
github.com/mojocn/base64Captcha v1.3.6
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.0
|
||||
github.com/oschwald/maxminddb-golang v1.13.1
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/sftp v1.13.6
|
||||
github.com/qiniu/go-sdk/v7 v7.21.1
|
||||
|
||||
@@ -362,6 +362,8 @@ github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7g
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
|
||||
@@ -93,6 +93,10 @@ SubTask: "Subtask"
|
||||
|
||||
#script
|
||||
ScriptLibrary: "Script Library"
|
||||
ScriptSyncSkip: "Current script library is already the latest version!"
|
||||
DownloadData: "Downloading script library file data.yaml"
|
||||
DownloadPackage: "Downloading script library package"
|
||||
AnalyticCompletion: "Analysis completed, now syncing to database..."
|
||||
|
||||
Node: "Node"
|
||||
SyncNode: "Sync node data"
|
||||
|
||||
@@ -94,6 +94,10 @@ SubTask: "サブタスク"
|
||||
|
||||
#script
|
||||
ScriptLibrary: "スクリプトライブラリ"
|
||||
ScriptSyncSkip: "現在のスクリプトライブラリは最新バージョンです!"
|
||||
DownloadData: "スクリプトライブラリファイル data.yaml をダウンロード中"
|
||||
DownloadPackage: "スクリプトライブラリパッケージをダウンロード中"
|
||||
AnalyticCompletion: "解析が完了しました、データベースに同期しています..."
|
||||
|
||||
Node: "ノード"
|
||||
SyncNode: "ノードデータ同期"
|
||||
|
||||
@@ -93,6 +93,10 @@ SubTask: "서브 작업"
|
||||
|
||||
#script
|
||||
ScriptLibrary: "스크립트 라이브러리"
|
||||
ScriptSyncSkip: "현재 스크립트 라이브러리가 이미 최신 버전입니다!"
|
||||
DownloadData: "스크립트 라이브러리 파일 data.yaml 다운로드 중"
|
||||
DownloadPackage: "스크립트 라이브러리 패키지 다운로드 중"
|
||||
AnalyticCompletion: "분석 완료, 데이터베이스에 동기화 중..."
|
||||
|
||||
Node: "노드"
|
||||
SyncNode: "노드 데이터 동기화"
|
||||
|
||||
@@ -93,6 +93,10 @@ SubTask: "Tugas Sub"
|
||||
|
||||
#script
|
||||
ScriptLibrary: "Pustaka Skrip"
|
||||
ScriptSyncSkip: "Pustaka skrip semasa sudah versi terkini!"
|
||||
DownloadData: "Muat turun fail pustaka skrip data.yaml"
|
||||
DownloadPackage: "Muat turun pakej pustaka skrip"
|
||||
AnalyticCompletion: "Analisis selesai, sedang menyegerakkan ke pangkalan data..."
|
||||
|
||||
Node: "Nod"
|
||||
SyncNode: "Segerakkan data nod"
|
||||
|
||||
@@ -93,6 +93,10 @@ SubTask: "Subtarefa"
|
||||
|
||||
#script
|
||||
ScriptLibrary: "Biblioteca de Scripts"
|
||||
ScriptSyncSkip: "A biblioteca de scripts atual já é a versão mais recente!"
|
||||
DownloadData: "Baixando arquivo data.yaml da biblioteca de scripts"
|
||||
DownloadPackage: "Baixando pacote da biblioteca de scripts"
|
||||
AnalyticCompletion: "Análise concluída, agora sincronizando com o banco de dados..."
|
||||
|
||||
Node: "Nó"
|
||||
SyncNode: "Sincronizar dados do nó"
|
||||
|
||||
@@ -93,6 +93,10 @@ SubTask: "Подзадача"
|
||||
|
||||
#script
|
||||
ScriptLibrary: "Библиотека скриптов"
|
||||
ScriptSyncSkip: "Текущая библиотека скриптов уже является последней версией!"
|
||||
DownloadData: "Загрузка файла библиотеки скриптов data.yaml"
|
||||
DownloadPackage: "Загрузка архива библиотеки скриптов"
|
||||
AnalyticCompletion: "Анализ завершён, начинается синхронизация с базой данных..."
|
||||
|
||||
Node: "Узел"
|
||||
SyncNode: "Синхронизация данных узла"
|
||||
|
||||
@@ -93,6 +93,10 @@ SubTask: "子任務"
|
||||
|
||||
#script
|
||||
ScriptLibrary: "腳本庫"
|
||||
ScriptSyncSkip: "當前腳本庫已是最新版本!"
|
||||
DownloadData: "下載腳本庫文件 data.yaml"
|
||||
DownloadPackage: "下載腳本庫壓縮包"
|
||||
AnalyticCompletion: "解析成功,現在開始同步到資料庫..."
|
||||
|
||||
#sync node
|
||||
Node: "節點"
|
||||
|
||||
@@ -93,6 +93,10 @@ SubTask: "子任务"
|
||||
|
||||
#script
|
||||
ScriptLibrary: "脚本库"
|
||||
ScriptSyncSkip: "检查到当前脚本库已是最新版本!"
|
||||
DownloadData: "下载脚本库文件 data.yaml"
|
||||
DownloadPackage: "下载脚本库压缩包"
|
||||
AnalyticCompletion: "解析成功,现在开始同步到数据库..."
|
||||
|
||||
#sync node
|
||||
Node: "节点"
|
||||
|
||||
@@ -29,17 +29,18 @@ func LoadMenus() string {
|
||||
{ID: "71", Disabled: false, Title: "menu.files", IsShow: true, Label: "File", Path: "/hosts/files"},
|
||||
{ID: "72", Disabled: false, Title: "menu.monitor", IsShow: true, Label: "Monitorx", Path: "/hosts/monitor/monitor"},
|
||||
{ID: "74", Disabled: false, Title: "menu.firewall", IsShow: true, Label: "FirewallPort", Path: "/hosts/firewall/port"},
|
||||
{ID: "75", Disabled: false, Title: "menu.supervisor", IsShow: true, Label: "Process", Path: "/hosts/process/process"},
|
||||
{ID: "75", Disabled: false, Title: "menu.processManage", IsShow: true, Label: "Process", Path: "/hosts/process/process"},
|
||||
{ID: "76", Disabled: false, Title: "menu.ssh", IsShow: true, Label: "SSH", Path: "/hosts/ssh/ssh"},
|
||||
}},
|
||||
{ID: "8", Disabled: false, Title: "menu.terminal", IsShow: true, Label: "Terminal-Menu", Path: "/hosts/terminal"},
|
||||
{ID: "9", Disabled: false, Title: "menu.toolbox", IsShow: true, Label: "Toolbox-Menu", Path: "/toolbox"},
|
||||
{ID: "10", Disabled: false, Title: "menu.cronjob", IsShow: true, Label: "Cronjob-Menu", Path: "/cronjobs"},
|
||||
{ID: "9", Disabled: false, Title: "menu.toolbox", IsShow: true, Label: "Toolbox-Menu", Path: "/toolbox"},
|
||||
{ID: "11", Disabled: false, Title: "xpack.menu", IsShow: true, Label: "Xpack-Menu",
|
||||
Children: []dto.ShowMenu{
|
||||
{ID: "118", Disabled: false, Title: "xpack.app.app", IsShow: true, Label: "XApp", Path: "/xpack/app"},
|
||||
{ID: "112", Disabled: false, Title: "xpack.waf.name", IsShow: true, Label: "Dashboard", Path: "/xpack/waf/dashboard"},
|
||||
{ID: "111", Disabled: false, Title: "xpack.node.nodeManagement", IsShow: true, Label: "Node", Path: "/xpack/node"},
|
||||
{ID: "119", Disabled: false, Title: "xpack.upage", IsShow: true, Label: "Upage", Path: "/xpack/upage"},
|
||||
{ID: "113", Disabled: false, Title: "xpack.monitor.name", IsShow: true, Label: "MonitorDashboard", Path: "/xpack/monitor/dashboard"},
|
||||
{ID: "114", Disabled: false, Title: "xpack.tamper.tamper", IsShow: true, Label: "Tamper", Path: "/xpack/tamper"},
|
||||
{ID: "115", Disabled: false, Title: "xpack.exchange.exchange", IsShow: true, Label: "FileExange", Path: "/xpack/exchange/file"},
|
||||
|
||||
@@ -17,6 +17,8 @@ func Init() {
|
||||
migrations.InitGoogle,
|
||||
migrations.AddTaskDB,
|
||||
migrations.AddXpackHideMenu,
|
||||
migrations.UpdateGoogle,
|
||||
migrations.UpdateXpackHideMenu,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
||||
@@ -312,7 +312,7 @@ var AddXpackHideMenu = &gormigrate.Migration{
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
menus[i].Children = append(menus[i].Children, newItem)
|
||||
menus[i].Children = append([]dto.ShowMenu{newItem}, menus[i].Children...)
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -328,3 +328,145 @@ var AddXpackHideMenu = &gormigrate.Migration{
|
||||
return tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Update("value", string(updatedJSON)).Error
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateXpackHideMenu = &gormigrate.Migration{
|
||||
ID: "20250617-update-xpack-hide-menu",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
var menuJSON string
|
||||
if err := tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Pluck("value", &menuJSON).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
var menus []dto.ShowMenu
|
||||
if err := json.Unmarshal([]byte(menuJSON), &menus); err != nil {
|
||||
return tx.Model(&model.Setting{}).
|
||||
Where("key = ?", "HideMenu").
|
||||
Update("value", helper.LoadMenus()).Error
|
||||
}
|
||||
newItem := dto.ShowMenu{
|
||||
ID: "119",
|
||||
Disabled: false,
|
||||
Title: "xpack.upage",
|
||||
IsShow: true,
|
||||
Label: "Upage",
|
||||
Path: "/xpack/upage",
|
||||
}
|
||||
|
||||
for i, menu := range menus {
|
||||
if menu.ID == "11" {
|
||||
exists := false
|
||||
for _, child := range menu.Children {
|
||||
if child.ID == newItem.ID {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if exists {
|
||||
break
|
||||
}
|
||||
|
||||
insertIndex := -1
|
||||
for j, child := range menu.Children {
|
||||
if child.ID == "111" {
|
||||
insertIndex = j
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if insertIndex != -1 {
|
||||
children := menu.Children
|
||||
menus[i].Children = append(children[:insertIndex+1], append([]dto.ShowMenu{newItem}, children[insertIndex+1:]...)...)
|
||||
} else {
|
||||
menus[i].Children = append([]dto.ShowMenu{newItem}, menus[i].Children...)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for i, menu := range menus {
|
||||
if menu.ID == "11" {
|
||||
existsIndex := -1
|
||||
for j, child := range menu.Children {
|
||||
if child.ID == "118" {
|
||||
existsIndex = j
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if existsIndex == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
var item118 dto.ShowMenu
|
||||
if existsIndex != -1 {
|
||||
item118 = menu.Children[existsIndex]
|
||||
menus[i].Children = append(menu.Children[:existsIndex], menu.Children[existsIndex+1:]...)
|
||||
} else {
|
||||
item118 = dto.ShowMenu{
|
||||
ID: "118",
|
||||
Disabled: false,
|
||||
Title: "xpack.app.app",
|
||||
IsShow: true,
|
||||
Label: "XApp",
|
||||
Path: "/xpack/app",
|
||||
}
|
||||
}
|
||||
|
||||
menus[i].Children = append([]dto.ShowMenu{item118}, menus[i].Children...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var idx9, idx10 = -1, -1
|
||||
for i, menu := range menus {
|
||||
if menu.ID == "9" && menu.Path == "/toolbox" {
|
||||
idx9 = i
|
||||
}
|
||||
if menu.ID == "10" && menu.Path == "/cronjobs" {
|
||||
idx10 = i
|
||||
}
|
||||
}
|
||||
if idx9 != -1 && idx10 != -1 && idx10 > idx9 {
|
||||
menus[idx9], menus[idx10] = menus[idx10], menus[idx9]
|
||||
}
|
||||
|
||||
for i, menu := range menus {
|
||||
if menu.ID == "7" {
|
||||
for j, child := range menu.Children {
|
||||
if child.ID == "75" {
|
||||
if child.Title != "menu.processManage" {
|
||||
menus[i].Children[j].Title = "menu.processManage"
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
updatedJSON, err := json.Marshal(menus)
|
||||
if err != nil {
|
||||
return tx.Model(&model.Setting{}).
|
||||
Where("key = ?", "HideMenu").
|
||||
Update("value", helper.LoadMenus()).Error
|
||||
}
|
||||
|
||||
return tx.Model(&model.Setting{}).Where("key = ?", "HideMenu").Update("value", string(updatedJSON)).Error
|
||||
},
|
||||
}
|
||||
|
||||
var UpdateGoogle = &gormigrate.Migration{
|
||||
ID: "20250616-update-google",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
if err := tx.Model(&model.Setting{}).
|
||||
Where("key = ?", "GoogleID").
|
||||
Update("value", "NTU2NTQ3NDYwMTQtY2Q0bGR0dDk2aGNsNWcxYWtwdmJhZTFmcjJlZ2Y0MXAuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20=").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Model(&model.Setting{}).
|
||||
Where("key = ?", "GoogleSc").
|
||||
Update("value", "R09DU1BYLXRibXg0QVdVZ3d3Ykc2QW1XTHQ3YUdaZElVeE4=").Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -17,6 +17,12 @@ var whiteUrlList = map[string]struct{}{
|
||||
"/api/v2/files/size": {},
|
||||
"/api/v2/runtimes/sync": {},
|
||||
"/api/v2/toolbox/device/base": {},
|
||||
"/api/v2/files/user/group": {},
|
||||
"/api/v2/files/mount": {},
|
||||
"/api/v2/hosts/ssh/log": {},
|
||||
"/api/v2/toolbox/clam/base": {},
|
||||
"/api/v2/hosts/too": {},
|
||||
"/api/v2/backups/record/size": {},
|
||||
|
||||
"/api/v2/core/auth/login": {},
|
||||
"/api/v2/core/logs/login": {},
|
||||
@@ -43,11 +49,14 @@ var whiteUrlList = map[string]struct{}{
|
||||
"/api/v2/xpack/monitor/websites": {},
|
||||
"/api/v2/xpack/monitor/trend": {},
|
||||
"/api/v2/xpack/monitor/rank": {},
|
||||
"/api/v2/xpack/waf/cdn": {},
|
||||
|
||||
"/api/v2/core/nodes/list": {},
|
||||
}
|
||||
|
||||
func DemoHandle() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if strings.Contains(c.Request.URL.Path, "search") || c.Request.Method == http.MethodGet {
|
||||
if strings.Contains(c.Request.URL.Path, "search") || (c.Request.Method == http.MethodGet && c.Request.URL.Path != "/api/v2/containers/exec") {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -20,9 +20,10 @@ type aliClient struct {
|
||||
}
|
||||
|
||||
func NewALIClient(vars map[string]interface{}) (*aliClient, error) {
|
||||
refresh_token := loadParamFromVars("refresh_token", vars)
|
||||
drive_id := loadParamFromVars("drive_id", vars)
|
||||
|
||||
token, err := RefreshALIToken(vars)
|
||||
token, err := loadToken(refresh_token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -355,6 +356,35 @@ func (a *aliClient) completeUpload(uploadID, fileID string) error {
|
||||
|
||||
type tokenResp struct {
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
func loadToken(refresh_token string) (string, error) {
|
||||
client := resty.New()
|
||||
client.SetTLSClientConfig(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
data := map[string]interface{}{
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": refresh_token,
|
||||
}
|
||||
|
||||
url := "https://api.aliyundrive.com/token/refresh"
|
||||
resp, err := client.R().
|
||||
SetBody(data).
|
||||
Post(url)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("load account token failed, err: %v", err)
|
||||
}
|
||||
if resp.StatusCode() != 200 {
|
||||
return "", fmt.Errorf("load account token failed, code: %v", resp.StatusCode())
|
||||
}
|
||||
var respItem tokenResp
|
||||
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return respItem.AccessToken, nil
|
||||
}
|
||||
|
||||
func RefreshALIToken(varMap map[string]interface{}) (string, error) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
cosSDK "github.com/tencentyun/cos-go-sdk-v5"
|
||||
)
|
||||
@@ -18,6 +19,7 @@ type cosClient struct {
|
||||
|
||||
func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
region := loadParamFromVars("region", vars)
|
||||
endpoint := loadParamFromVars("endpoint", vars)
|
||||
accessKey := loadParamFromVars("accessKey", vars)
|
||||
secretKey := loadParamFromVars("secretKey", vars)
|
||||
bucket := loadParamFromVars("bucket", vars)
|
||||
@@ -26,7 +28,15 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
scType = "Standard"
|
||||
}
|
||||
|
||||
u, _ := url.Parse(fmt.Sprintf("https://cos.%s.myqcloud.com", region))
|
||||
endpointType := "cos"
|
||||
if len(endpoint) != 0 {
|
||||
re := regexp.MustCompile(`.*cos-dualstack\..*`)
|
||||
if re.MatchString(endpoint) {
|
||||
endpointType = "cos-dualstack"
|
||||
}
|
||||
}
|
||||
|
||||
u, _ := url.Parse(fmt.Sprintf("https://%s.%s.myqcloud.com", endpointType, region))
|
||||
b := &cosSDK.BaseURL{BucketURL: u}
|
||||
client := cosSDK.NewClient(b, &http.Client{
|
||||
Transport: &cosSDK.AuthorizationTransport{
|
||||
@@ -36,7 +46,7 @@ func NewCosClient(vars map[string]interface{}) (*cosClient, error) {
|
||||
})
|
||||
|
||||
if len(bucket) != 0 {
|
||||
u2, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucket, region))
|
||||
u2, _ := url.Parse(fmt.Sprintf("https://%s.%s.%s.myqcloud.com", bucket, endpointType, region))
|
||||
b2 := &cosSDK.BaseURL{BucketURL: u2}
|
||||
clientWithBucket := cosSDK.NewClient(b2, &http.Client{
|
||||
Transport: &cosSDK.AuthorizationTransport{
|
||||
|
||||
@@ -66,6 +66,19 @@ func ScanPort(port int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func CheckPort(host string, port string, timeout time.Duration) bool {
|
||||
target := net.JoinHostPort(host, port)
|
||||
conn, err := net.DialTimeout("tcp", target, timeout)
|
||||
if err != nil {
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(err.Error(), "connection refused")
|
||||
}
|
||||
defer conn.Close()
|
||||
return true
|
||||
}
|
||||
|
||||
func ComparePanelVersion(version1, version2 string) bool {
|
||||
if version1 == version2 {
|
||||
return false
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"codemirror": "^6.0.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"echarts": "^5.5.0",
|
||||
"element-plus": "^2.9.7",
|
||||
"element-plus": "^2.9.9",
|
||||
"fit2cloud-ui-plus": "^1.2.2",
|
||||
"highlight.js": "^11.9.0",
|
||||
"js-base64": "^3.7.7",
|
||||
|
||||
@@ -9,6 +9,9 @@ export const searchContainer = (params: Container.ContainerSearch) => {
|
||||
export const listContainer = () => {
|
||||
return http.post<Array<string>>(`/containers/list`, {});
|
||||
};
|
||||
export const loadContainerUsers = (name: string) => {
|
||||
return http.post<Array<string>>(`/containers/users`, { name: name });
|
||||
};
|
||||
export const loadContainerStatus = () => {
|
||||
return http.get<Container.ContainerStatus>(`/containers/status`);
|
||||
};
|
||||
|
||||
@@ -40,8 +40,8 @@ export const changeBind = (id: number, nodeIDs: Array<number>) => {
|
||||
export const loadLicenseOptions = () => {
|
||||
return http.get<Array<Setting.LicenseOptions>>(`/core/licenses/options`);
|
||||
};
|
||||
export const listNodeOptions = () => {
|
||||
return http.get<Array<Setting.NodeItem>>(`/core/nodes/list`);
|
||||
export const listNodeOptions = (type: string) => {
|
||||
return http.post<Array<Setting.NodeItem>>(`/core/nodes/list`, { type: type });
|
||||
};
|
||||
|
||||
export const listAllNodes = () => {
|
||||
|
||||
@@ -25,7 +25,7 @@ export const updateDevicePasswd = (user: string, passwd: string) => {
|
||||
return http.post(`/toolbox/device/update/passwd`, { user: user, passwd: Base64.encode(passwd) }, TimeoutEnum.T_60S);
|
||||
};
|
||||
export const updateDeviceSwap = (params: Toolbox.SwapHelper) => {
|
||||
return http.post(`/toolbox/device/update/swap`, params, TimeoutEnum.T_60S);
|
||||
return http.post(`/toolbox/device/update/swap`, params, TimeoutEnum.T_10M);
|
||||
};
|
||||
export const updateDeviceByConf = (name: string, file: string) => {
|
||||
return http.post(`/toolbox/device/update/byconf`, { name: name, file: file }, TimeoutEnum.T_5M);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4776196 */
|
||||
src: url('iconfont.woff2?t=1745909790481') format('woff2'),
|
||||
url('iconfont.woff?t=1745909790481') format('woff'),
|
||||
url('iconfont.ttf?t=1745909790481') format('truetype'),
|
||||
url('iconfont.svg?t=1745909790481#iconfont') format('svg');
|
||||
src: url('iconfont.woff2?t=1750234328196') format('woff2'),
|
||||
url('iconfont.woff?t=1750234328196') format('woff'),
|
||||
url('iconfont.ttf?t=1750234328196') format('truetype'),
|
||||
url('iconfont.svg?t=1750234328196#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -14,6 +14,14 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.p-start:before {
|
||||
content: "\e688";
|
||||
}
|
||||
|
||||
.p-stop:before {
|
||||
content: "\e750";
|
||||
}
|
||||
|
||||
.p-node-4:before {
|
||||
content: "\e73c";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,20 @@
|
||||
"css_prefix_text": "p-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "15838584",
|
||||
"name": "arrow-right-filling",
|
||||
"font_class": "start",
|
||||
"unicode": "e688",
|
||||
"unicode_decimal": 59016
|
||||
},
|
||||
{
|
||||
"icon_id": "212329",
|
||||
"name": "stop",
|
||||
"font_class": "stop",
|
||||
"unicode": "e750",
|
||||
"unicode_decimal": 59216
|
||||
},
|
||||
{
|
||||
"icon_id": "7712749",
|
||||
"name": "node-4",
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
/>
|
||||
<missing-glyph />
|
||||
|
||||
<glyph glyph-name="start" unicode="" d="M755.2 352L390.4 21.333333c-17.066667-14.933333-44.8-14.933333-59.733333 2.133334-6.4 8.533333-10.666667 19.2-10.666667 29.866666v661.333334c0 23.466667 19.2 42.666667 42.666667 42.666666 10.666667 0 21.333333-4.266667 27.733333-10.666666l362.666667-330.666667c17.066667-14.933333 19.2-42.666667 2.133333-59.733333 2.133333-2.133333 0-2.133333 0-4.266667z" horiz-adv-x="1024" />
|
||||
|
||||
<glyph glyph-name="stop" unicode="" d="M291.065468 30.504749c-24.391172 0-44.186906 19.751547-44.186906 44.186907L246.878562 693.308344c0 24.391172 19.795734 44.186906 44.186906 44.186907s44.186906-19.795734 44.186907-44.186907l0-618.616688C335.252375 50.256296 315.456641 30.504749 291.065468 30.504749zM732.934532 30.504749c-24.435359 0-44.186906 19.751547-44.186907 44.186907L688.747625 693.308344c0 24.391172 19.751547 44.186906 44.186907 44.186907s44.186906-19.795734 44.186906-44.186907l0-618.616688C777.121438 50.256296 757.369891 30.504749 732.934532 30.504749z" horiz-adv-x="1024" />
|
||||
|
||||
<glyph glyph-name="node-4" unicode="" d="M607.934 478.143c-6.18 6.178-12.766 11.747-19.554 16.91l-0.012-0.011c-6.986 6.72-16.472 10.857-26.93 10.857-21.465 0-38.865-17.4-38.865-38.864a38.697 38.697 0 0 1 9.072-24.947h-0.001a39.02 39.02 0 0 1 9.59-8.256c3.665-3.022 7.262-5.998 10.625-9.361l3.204-3.205c40.28-40.23 28.255-109.54-12.025-149.82l-171.88-171.83c-40.279-40.23-105.762-40.23-146.042 0l-3.23 3.232c-40.281 40.278-40.281 105.81 0 145.99l75.936 75.91c9.742 7.734 15.997 19.67 15.997 33.073 0 23.313-18.899 42.211-42.212 42.211a42.01 42.01 0 0 1-23.725-7.297c-0.021 0.045-0.044 0.088-0.066 0.134l-0.81-0.757a42.455 42.455 0 0 1-8.026-7.51l-78.913-73.842c-74.178-74.23-74.178-195.633 0-269.759l3.204-3.203c74.179-74.127 195.53-74.127 269.708 0l171.83 171.88c74.075 74.175 80.356 191.185 6.281 265.312l-3.156 3.153zM855.62 730.196l-3.204 3.204c-74.178 74.178-195.529 74.178-269.707 0l-171.83-171.88c-74.178-74.178-78.263-181.296-4.085-255.523l3.153-3.104c3.369-3.368 6.866-6.543 10.435-9.589a36.872 36.872 0 0 1 8.993-7.31l0.077-0.062 0.012 0.01a36.508 36.508 0 0 1 18.258-4.87c20.263 0 36.69 16.428 36.69 36.69 0 5.719-1.31 11.132-3.646 15.958-4.85 10.89-13.93 17.52-20.21 23.802l-3.154 3.103c-40.278 40.278-24.983 98.796 15.295 139.074l171.931 171.83c40.18 40.281 105.685 40.281 145.966 0l3.206-3.152c40.279-40.282 40.279-105.839 0-146.068l-75.687-75.738c-10.297-7.629-16.974-19.865-16.974-33.663 0-23.123 18.746-41.87 41.87-41.87a41.668 41.668 0 0 1 21.946 6.211c0.048-0.082 0.093-0.157 0.14-0.24l1.175 1.083a42.09 42.09 0 0 1 9.529 8.793l79.766 73.603c74.233 74.177 74.233 195.53 0.055 269.708z" horiz-adv-x="1024" />
|
||||
|
||||
<glyph glyph-name="node-3" unicode="" d="M900.144 799.76H123.84a66.896 66.896 0 0 1-66.816-66.832v-541.152a66.896 66.896 0 0 1 66.816-66.832h467.744a24 24 0 0 1 0 48H123.84a18.848 18.848 0 0 0-18.816 18.832V732.96A18.864 18.864 0 0 0 123.84 751.792h776.304a18.848 18.848 0 0 0 18.816-18.832v-541.152a18.832 18.832 0 0 0-18.816-18.832H753.76a24 24 0 0 1 0-48h146.384a66.896 66.896 0 0 1 66.816 66.832V732.96a66.896 66.896 0 0 1-66.816 66.8zM795.808 16.24H213.168a24 24 0 0 1 0-48h582.64a24 24 0 0 1 0 48zM204.192 502.16v-192.608a24 24 0 0 1 48 0V502.16a24 24 0 0 1-48 0zM478.096 443.36v-133.856a24 24 0 0 1 48 0V443.36a24 24 0 0 1-48 0zM819.808 309.552V443.36a24 24 0 0 1-48 0v-133.856a24 24 0 0 1 48 0zM342.336 622.96v-313.408a24 24 0 0 1 48 0V622.96a24 24 0 0 1-48 0zM648.64 646.96a24 24 0 0 1-24-24v-313.408a24 24 0 0 1 48 0V622.96a24 24 0 0 1-24 24zM655.152 162.192a32.24 32.24 0 0 1-2.24-4c-0.48-1.44-0.96-3.04-1.28-4.48a24.416 24.416 0 0 1-0.48-4.8 25.008 25.008 0 0 1 1.76-9.12 26.048 26.048 0 0 1 5.28-7.84 22.288 22.288 0 0 1 7.68-5.12 23.504 23.504 0 0 1 9.28-1.92 22.192 22.192 0 0 1 9.12 1.92 22.912 22.912 0 0 1 7.84 5.12 26.048 26.048 0 0 1 5.28 7.84 25.008 25.008 0 0 1 1.76 9.12 24 24 0 0 1-7.04 16.96 24.752 24.752 0 0 1-33.92 0 23.264 23.264 0 0 1-3.04-3.68z" horiz-adv-x="1024" />
|
||||
|
||||
|
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 170 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -4,7 +4,8 @@
|
||||
@close="handleClose"
|
||||
:destroy-on-close="true"
|
||||
:size="size"
|
||||
:close-on-press-escape="true"
|
||||
:close-on-press-escape="autoClose"
|
||||
:close-on-click-modal="autoClose"
|
||||
>
|
||||
<template #header>
|
||||
<el-page-header @back="handleBack">
|
||||
@@ -73,6 +74,10 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
autoClose: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const slots = useSlots();
|
||||
|
||||
@@ -153,7 +153,9 @@ const searchLogs = async () => {
|
||||
};
|
||||
eventSource.onerror = (event: MessageEvent) => {
|
||||
stopListening();
|
||||
MsgError(event.data);
|
||||
if (event.data && event.data != '') {
|
||||
MsgError(event.data);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -205,7 +205,14 @@ const getContent = async (pre: boolean) => {
|
||||
isLoading.value = true;
|
||||
emit('update:isReading', true);
|
||||
|
||||
const res = await readByLine(readReq, props.config.operateNode || '');
|
||||
let res;
|
||||
try {
|
||||
res = await readByLine(readReq, props.config.operateNode || '');
|
||||
} catch (error) {
|
||||
isLoading.value = false;
|
||||
firstLoading.value = false;
|
||||
}
|
||||
|
||||
logPath.value = res.data.path;
|
||||
firstLoading.value = false;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-tooltip v-if="msg" effect="dark" placement="bottom">
|
||||
<el-tooltip v-if="msg && msg != ''" effect="dark" placement="bottom">
|
||||
<template #content>
|
||||
<div class="content">{{ msg }}</div>
|
||||
</template>
|
||||
@@ -12,19 +12,24 @@
|
||||
</span>
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tag size="small" v-else :type="getType(statusItem)" round effect="light">
|
||||
<span class="flx-align-center">
|
||||
<span v-else>
|
||||
<el-tag size="small" :type="getType(statusItem)" round effect="light" v-if="!operate">
|
||||
<span class="flx-align-center">
|
||||
{{ $t('commons.status.' + statusItem) }}
|
||||
<el-icon v-if="loadingIcon(statusItem)" class="is-loading">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
</span>
|
||||
</el-tag>
|
||||
<el-button size="small" v-else :type="getType(statusItem)" plain round>
|
||||
{{ $t('commons.status.' + statusItem) }}
|
||||
<el-icon v-if="loadingIcon(statusItem)" class="is-loading">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
<el-icon size="15" v-if="operate">
|
||||
<CaretRight v-if="statusItem == 'running'" />
|
||||
<CaretBottom v-if="statusItem == 'stopped'" />
|
||||
</el-icon>
|
||||
</span>
|
||||
</el-tag>
|
||||
<svg-icon iconName="p-stop" className="status-icon" v-if="stopIcon(statusItem)"></svg-icon>
|
||||
<svg-icon iconName="p-start" className="status-icon" v-if="runningIcon(statusItem)"></svg-icon>
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -33,6 +38,7 @@ import { computed } from 'vue';
|
||||
const props = defineProps({
|
||||
status: String,
|
||||
msg: String,
|
||||
hasIcon: Boolean,
|
||||
operate: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
@@ -64,9 +70,9 @@ const getType = (status: string) => {
|
||||
case 'unhealthy':
|
||||
case 'failed':
|
||||
case 'lost':
|
||||
case 'exited':
|
||||
return 'danger';
|
||||
case 'paused':
|
||||
case 'exited':
|
||||
case 'dead':
|
||||
case 'removing':
|
||||
case 'deleted':
|
||||
@@ -95,9 +101,18 @@ const loadingStatus = [
|
||||
'executing',
|
||||
];
|
||||
|
||||
const stopStatus = ['stopped', 'exited', 'disable'];
|
||||
const runningStatus = ['running', 'enable'];
|
||||
|
||||
const loadingIcon = (status: string): boolean => {
|
||||
return loadingStatus.indexOf(status) > -1;
|
||||
};
|
||||
const stopIcon = (status: string): boolean => {
|
||||
return stopStatus.indexOf(status.toLocaleLowerCase()) > -1;
|
||||
};
|
||||
const runningIcon = (status: string): boolean => {
|
||||
return runningStatus.indexOf(status.toLocaleLowerCase()) > -1;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -105,4 +120,9 @@ const loadingIcon = (status: string): boolean => {
|
||||
width: 300px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<script setup lang="ts">
|
||||
import { dateFormat } from '@/utils/util';
|
||||
import { searchTasks } from '@/api/modules/log';
|
||||
import { reactive, ref } from '@vue/runtime-core';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { Log } from '@/api/interface/log';
|
||||
import TaskLog from '@/components/log/task/index.vue';
|
||||
import bus from '@/global/bus';
|
||||
|
||||
@@ -211,6 +211,10 @@ export const DNSTypes = [
|
||||
label: 'Name.com',
|
||||
value: 'NameCom',
|
||||
},
|
||||
{
|
||||
label: 'Dynu',
|
||||
value: 'Dynu',
|
||||
},
|
||||
{
|
||||
label: 'reg.ru',
|
||||
value: 'RegRu',
|
||||
@@ -219,6 +223,10 @@ export const DNSTypes = [
|
||||
label: 'FreeMyIP',
|
||||
value: 'FreeMyIP',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('ssl.baiduCloud'),
|
||||
value: 'BaiduCloud',
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('website.rainyun'),
|
||||
value: 'RainYun',
|
||||
|
||||
@@ -2616,6 +2616,7 @@ const message = {
|
||||
'The default execution directory of the script is the 1Panel installation directory. If a certificate is pushed into local directory, the execution directory will be the certificate push directory. The default execution timeout is 30 minutes.',
|
||||
customAcme: 'Custom ACME Service',
|
||||
customAcmeURL: 'ACME Service URL',
|
||||
baiduCloud: 'Baidu Cloud',
|
||||
},
|
||||
firewall: {
|
||||
create: 'Create rule',
|
||||
@@ -3378,13 +3379,22 @@ const message = {
|
||||
nodeSyncHelper1: '1. Public backup account information',
|
||||
nodeSyncHelper2: '2. Connection information between the main node and sub-nodes',
|
||||
|
||||
nodeCheck: 'Availability check',
|
||||
checkSSH: 'Check node SSH connection',
|
||||
checkLicense: 'Check node license status',
|
||||
checkService: 'Check existing service information on node',
|
||||
checkPort: 'Check node port reachability',
|
||||
panelExist:
|
||||
'1Panel V1 service is running on this node. Please upgrade to V2 using the migration script before adding.',
|
||||
coreExist: 'This node has already been enabled as the main node and cannot be added as a slave node.',
|
||||
'Detected that this node is running 1Panel V1 service. Please upgrade to V2 using the migration script before adding.',
|
||||
coreExist: 'This node is already enabled as a master node and cannot be added as a slave node.',
|
||||
agentExist:
|
||||
'1panel-agent is already installed on this node. Continuing will preserve existing data and only replace the 1panel-agent service. Proceed?',
|
||||
dataExist: '1Panel historical data exists on this node. Confirm to continue adding?',
|
||||
'Detected that 1panel-agent is already installed on this node. Continuing will retain existing data and only replace the 1panel-agent service.',
|
||||
oldDataExist:
|
||||
'Detected historical 1Panel V2 data on this node. The following information will be used to overwrite current settings:',
|
||||
errLicense: 'The license bound to this node is unavailable. Please check and try again!',
|
||||
errNodePort:
|
||||
'Node port [ {0} ] is detected as inaccessible. Please check if the firewall or security group has allowed this port.',
|
||||
|
||||
reinstallHelper: 'Reinstall node {0}, do you want to continue?',
|
||||
unhealthyCheck: 'Abnormal Check',
|
||||
fixOperation: 'Fix Operation',
|
||||
|
||||
@@ -2513,6 +2513,7 @@ const message = {
|
||||
'スクリプトのデフォルトの実行ディレクトリは、1Panelインストールディレクトリです。証明書がローカルディレクトリにプッシュされた場合、実行ディレクトリは証明書プッシュディレクトリになります。デフォルトの実行タイムアウトは30分です。',
|
||||
customAcme: 'カスタム ACME サービス',
|
||||
customAcmeURL: 'ACME サービス URL',
|
||||
baiduCloud: '百度クラウド',
|
||||
},
|
||||
firewall: {
|
||||
create: 'ルールを作成します',
|
||||
@@ -3246,13 +3247,21 @@ const message = {
|
||||
nodeSyncHelper1: '1. 公共のバックアップアカウント情報',
|
||||
nodeSyncHelper2: '2. 主ノードとサブノードの接続情報',
|
||||
|
||||
nodeCheck: '可用性チェック',
|
||||
checkSSH: 'ノードSSH接続を確認',
|
||||
checkLicense: 'ノードライセンス状態を確認',
|
||||
checkService: 'ノードの既存サービス情報を確認',
|
||||
checkPort: 'ノードポート到達性を確認',
|
||||
panelExist:
|
||||
'このノードで1Panel V1サービスが実行中です。追加する前に移行スクリプトでV2にアップグレードしてください。',
|
||||
coreExist: 'このノードは既にメインノードとして有効になっており、スレーブノードとして追加できません。',
|
||||
'このノードで1Panel V1サービスが実行中です。追加前に移行スクリプトでV2へアップグレードしてください。',
|
||||
coreExist: 'このノードはマスターノードとして有効化済みのため、スレーブノードとして追加できません。',
|
||||
agentExist:
|
||||
'このノードには1panel-agentがインストール済みです。続行すると既存データは保持され、1panel-agentサービスのみ置き換えられます。続行しますか?',
|
||||
dataExist: 'このノードに1Panelの過去データが存在します。追加を続行しますか?',
|
||||
errLicense: 'このノードにバインドされたライセンスが利用できません。確認して再試行してください!',
|
||||
'このノードに1panel-agentが既にインストールされています。続行すると既存データを保持し、1panel-agentサービスのみを置換します。',
|
||||
oldDataExist: 'このノードに1Panel V2の過去データが検出されました。以下の情報で現在の設定を上書きします:',
|
||||
errLicense: 'このノードに紐づけられたライセンスが利用できません。確認して再試行してください!',
|
||||
errNodePort:
|
||||
'ノードポート[ {0} ]にアクセスできないことが検出されました。ファイアウォールまたはセキュリティグループでこのポートが開放されているか確認してください。',
|
||||
|
||||
reinstallHelper: 'ノード{0}を再インストールします。続行しますか?',
|
||||
unhealthyCheck: '異常チェック',
|
||||
fixOperation: '修正操作',
|
||||
|
||||
@@ -2470,6 +2470,7 @@ const message = {
|
||||
'스크립트의 기본 실행 디렉토리는 1Panel 설치 디렉토리입니다. 인증서가 로컬 디렉토리에 푸시되는 경우 실행 디렉토리는 인증서 푸시 디렉토리가 됩니다. 기본 실행 제한 시간은 30분입니다.',
|
||||
customAcme: '사용자 정의 ACME 서비스',
|
||||
customAcmeURL: 'ACME 서비스 URL',
|
||||
baiduCloud: '바이두 클라우드',
|
||||
},
|
||||
firewall: {
|
||||
create: '규칙 만들기',
|
||||
@@ -3189,13 +3190,22 @@ const message = {
|
||||
nodeSyncHelper1: '1. 공용 백업 계정 정보',
|
||||
nodeSyncHelper2: '2. 주 노드와 하위 노드 간의 연결 정보',
|
||||
|
||||
nodeCheck: '가용성 확인',
|
||||
checkSSH: '노드 SSH 연결 확인',
|
||||
checkLicense: '노드 라이선스 상태 확인',
|
||||
checkService: '노드의 기존 서비스 정보 확인',
|
||||
checkPort: '노드 포트 접근 가능 여부 확인',
|
||||
panelExist:
|
||||
'이 노드에서 1Panel V1 서비스가 실행 중입니다. 추가하기 전에 마이그레이션 스크립트로 V2로 업그레이드하세요.',
|
||||
coreExist: '이 노드는 이미 메인 노드로 활성화되어 있어 슬레이브 노드로 추가할 수 없습니다.',
|
||||
'이 노드에서 1Panel V1 서비스가 실행 중인 것으로 감지되었습니다. 추가 전에 마이그레이션 스크립트로 V2로 업그레이드하십시오.',
|
||||
coreExist: '이 노드는 이미 마스터 노드로 활성화되어 있어 슬레이브 노드로 추가할 수 없습니다.',
|
||||
agentExist:
|
||||
'이 노드에 1panel-agent가 이미 설치되어 있습니다. 계속하면 기존 데이터는 유지되고 1panel-agent 서비스만 교체됩니다. 계속하시겠습니까?',
|
||||
dataExist: '이 노드에 1Panel 이전 데이터가 존재합니다. 추가를 계속하시겠습니까?',
|
||||
errLicense: '이 노드에 연결된 라이선스를 사용할 수 없습니다. 확인 후 다시 시도하세요!',
|
||||
'이 노드에 1panel-agent가 이미 설치되어 있는 것으로 감지되었습니다. 계속하면 기존 데이터를 유지하고 1panel-agent 서비스만 교체됩니다.',
|
||||
oldDataExist:
|
||||
'이 노드에서 1Panel V2 기록 데이터가 감지되었습니다. 다음 정보를 사용하여 현재 설정을 덮어씁니다:',
|
||||
errLicense: '이 노드에 바인딩된 라이선스를 사용할 수 없습니다. 확인 후 다시 시도하십시오!',
|
||||
errNodePort:
|
||||
'노드 포트 [ {0} ]에 접근할 수 없는 것으로 감지되었습니다. 방화벽 또는 보안 그룹에서 해당 포트가 허용되었는지 확인하십시오.',
|
||||
|
||||
reinstallHelper: '노드 {0}를 재설치합니다. 계속하시겠습니까?',
|
||||
unhealthyCheck: '비정상 체크',
|
||||
fixOperation: '수정 작업',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user