Compare commits

...
46 Commits
Author SHA1 Message Date
ssongliu cf230ca6e7 fix: Fix the problem of the failure of 1pctl update port (#9199) 2025-06-20 06:45:13 +00:00
蘭 bb99853000 fix: File content support GBK encoding (#9198) 2025-06-20 06:43:13 +00:00
ssongliu 0c64c8ff52 fix: Fix the problem of incomplete printing of the cronjob (#9197) 2025-06-20 14:25:35 +08:00
ssongliu e3e2006c19 fix: Solve the problem of failed binding and synchronization of Ali Pan (#9195) 2025-06-20 11:12:49 +08:00
蘭 eddc1b6a8d fix: Fix hidden file flipping issue (#9189) 2025-06-19 11:51:00 +00:00
ssongliu 83e1db723e fix: Optimize the synchronization log information of the script library (#9188) 2025-06-19 10:42:59 +00:00
ssongliu 76c608c703 style: Adjust the style of the cronjob status button (#9185) 2025-06-19 09:49:01 +00:00
CityFun d61a77bec7 feat: add core debug cmd for cpu/mem analysis (#9186) 2025-06-19 17:48:51 +08:00
ssongliu a6a2d9991c fix: Remove the redis password restriction (#9184) 2025-06-19 08:36:59 +00:00
CityFun 060dd08167 fix: fix issue with http3 config not work in proxy website (#9183) 2025-06-19 15:12:29 +08:00
CityFun dd1d93e096 fix: fix issue with docker cmd not close (#9182) 2025-06-19 07:06:59 +00:00
蘭 e4b24f51fc feat: Add cache to query SMS records (#9180) 2025-06-19 07:01:00 +00:00
CityFun e472269a4e feat: add agent debug cmd for cpu/mem analysis (#9181) 2025-06-19 14:59:51 +08:00
CityFun 4032276556 feat: update demo handle (#9175) 2025-06-18 09:58:56 +00:00
ssongliu 420537f092 fix: Fix the problem of abnormal addition of remote connections to th… (#9174)
Refs #9151
2025-06-18 09:20:57 +00:00
蘭 3867e536b2 fix: Fix menu sorting (#9172) 2025-06-18 09:18:57 +00:00
CityFun 4bc7b96ae4 style: change button style (#9171) 2025-06-18 08:48:58 +00:00
ssongliu 9f0338382f feat: Script within the container support the selection of users (#9170)
Refs #8766
2025-06-18 08:46:58 +00:00
蘭 692c77eb1a fix: Service status acquisition compatible with snap (#9169) 2025-06-18 16:45:22 +08:00
CityFun 0778e3e81d feat: add baidu and rynu dns provider for ssl (#9167)
Refs https://github.com/1Panel-dev/1Panel/issues/9160
2025-06-18 07:20:57 +00:00
CityFun 80e217e839 feat: Adopt temporary container adaptation for app store status verification (#9165)
Refs https://github.com/1Panel-dev/1Panel/issues/8973
2025-06-18 07:18:57 +00:00
蘭 47547ad4e1 fix: Fix image loading issue (#9164) 2025-06-18 07:16:57 +00:00
CityFun 8b22acce27 fix: fix issue with open container log failed (#9163) 2025-06-18 07:14:56 +00:00
ssongliu 754ac64bf6 fix: Increase the timeout period of the swap setting (#9162)
Refs #9142
2025-06-18 03:50:56 +00:00
ssongliu 3b37d5ae72 fix: Upgrade the element version to 2.9.9 (#9158) 2025-06-18 03:48:57 +00:00
ssongliu 4e2f24a8f0 fix: Fix the problem of failed Google drive backup (#9156)
Refs #9129
2025-06-17 10:48:55 +00:00
蘭 23a1fbdd4b fix: Clear cache by specifying default values (#9154)
#8933
2025-06-17 10:42:54 +00:00
蘭 795223e7fa fix: Fix the issue of interface image loss caused by clearing cache (#9149)
Refs: #9050
2025-06-17 09:00:54 +00:00
ssongliu 10039c6173 fix: Optimize the script library to synchronize the task log (#9148) 2025-06-17 16:59:34 +08:00
CityFun 146b628cc1 feat: Restrict the size of log files to read. (#9147) 2025-06-17 16:59:23 +08:00
CityFun 0c31fcc8a3 fix: fix issue with local app can not open port (#9145) 2025-06-17 08:36:53 +00:00
蘭 2c290dbbf7 fix: hide menu and sorting (#9141) 2025-06-17 16:14:28 +08:00
ssongliu 0df8a33b9e fix: Fix the problem of failed start of swap in the toolbox (#9143)
Refs #9142
2025-06-17 08:08:53 +00:00
ssongliu 1278e910f5 fix: Fix the issue of abnormal login log recording (#9139)
Refs #9054
2025-06-17 08:06:55 +00:00
ssongliu 429e2c6cbe feat: Optimize the node to add inspection items (#9138) 2025-06-17 08:04:54 +00:00
CityFun 00a5cc654a fix: fix issue with run script with no return (#9137) 2025-06-17 16:04:02 +08:00
蘭 d68a0b076b feat: transfer some code (#9124)
refs #9067
2025-06-17 03:56:53 +00:00
CityFun 99ef8e9310 fix: fix issue with edit dotnet environment failed (#9121)
Refs https://github.com/1Panel-dev/1Panel/issues/9083
2025-06-16 08:50:51 +00:00
Process Xie 5e549fdb71 fix: terminate Docker logs process when client disconnects (#9091)
* Update container.go

* Update container.go
2025-06-16 16:07:13 +08:00
CityFun 8f7b026f0e feat: Add quick installation for PHP extension pcntl (#9119) 2025-06-16 07:38:51 +00:00
CityFun fabf33a83b feat: Ensure compatibility with docker-compose commands. (#9117) 2025-06-16 07:14:51 +00:00
CityFun 211c956c4b fix: Fix input field validation error when editing installed application (#9115) 2025-06-16 06:44:51 +00:00
CityFun 55d966a52e fix: fix issue with php install swoole failed (#9113)
Refs https://github.com/1Panel-dev/1Panel/issues/9080
2025-06-16 06:42:51 +00:00
ssongliu a0d5319cd9 fix: Modify the license binding node filtering (#9111) 2025-06-16 06:40:52 +00:00
ssongliu d9358b87c4 style: Optimize the closing method of the right drawer (#9108) 2025-06-16 06:38:51 +00:00
CityFun 89526d9075 feat: add docker status for appstore (#9058) 2025-06-13 10:00:42 +00:00
138 changed files with 1966 additions and 674 deletions
+23
View File
@@ -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
+8 -8
View File
@@ -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
+8 -8
View File
@@ -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 {
+6 -4
View File
@@ -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
}
+14 -6
View File
@@ -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
}
+60 -17
View File
@@ -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 {
+5 -5
View File
@@ -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
+22 -7
View File
@@ -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) {
+11 -6
View File
@@ -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)
}
+30 -5
View File
@@ -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
+1 -1
View File
@@ -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)
+12 -7
View File
@@ -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
View File
@@ -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 {
+2 -3
View File
@@ -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()
+1 -1
View File
@@ -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 {
+1 -1
View File
@@ -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")
+3 -1
View File
@@ -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
}
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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))
+6 -1
View File
@@ -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)
}
+169
View File
@@ -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.")
}
+14
View File
@@ -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
},
}
+7 -4
View File
@@ -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)
}
}
+14 -3
View File
@@ -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
}
]
+8 -3
View File
@@ -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
}
+1
View File
@@ -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
+2
View File
@@ -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=
+3
View File
@@ -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!'
+3
View File
@@ -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: '現在のユーザーは存在しません。変更してもう一度お試しください。'
+3
View File
@@ -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: '현재 사용자가 존재하지 않습니다. 수정한 후 다시 시도하세요!'
+2
View File
@@ -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!'
+3
View File
@@ -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!'
+3
View File
@@ -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: 'Текущий пользователь не существует, измените его и повторите попытку!'
+3
View File
@@ -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: '目前使用者不存在,請修改後重試!'
+3
View File
@@ -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: "当前用户不存在,请修改后重试!"
+15
View File
@@ -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
}
}
-57
View File
@@ -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()
}
}
+2
View File
@@ -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)
+2 -1
View File
@@ -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) {
+12 -2
View File
@@ -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)
}
+1 -1
View File
@@ -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
+17
View File
@@ -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 ""
}
+39 -7
View File
@@ -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
}
+2 -2
View File
@@ -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
}
}
+11
View File
@@ -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
+9
View File
@@ -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"`
}
+144 -28
View File
@@ -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
}
+21 -15
View File
@@ -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
}
+1 -1
View File
@@ -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
View File
@@ -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 {
+64 -12
View File
@@ -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
View File
@@ -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) {
+9
View File
@@ -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"`
}
+2 -2
View File
@@ -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 {
+9 -1
View File
@@ -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) {
-1
View File
@@ -11,7 +11,6 @@ var (
groupRepo = repo.NewIGroupRepo()
upgradeLogRepo = repo.NewIUpgradeLogRepo()
taskRepo = repo.NewITaskRepo()
agentRepo = repo.NewIAgentRepo()
scriptRepo = repo.NewIScriptRepo()
)
+11 -8
View File
@@ -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)
}
+169
View File
@@ -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.")
}
+1 -1
View File
@@ -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)
}
+1 -1
View File
@@ -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"
)
+1
View File
@@ -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
+2
View File
@@ -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=
+4
View File
@@ -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"
+4
View File
@@ -94,6 +94,10 @@ SubTask: "サブタスク"
#script
ScriptLibrary: "スクリプトライブラリ"
ScriptSyncSkip: "現在のスクリプトライブラリは最新バージョンです!"
DownloadData: "スクリプトライブラリファイル data.yaml をダウンロード中"
DownloadPackage: "スクリプトライブラリパッケージをダウンロード中"
AnalyticCompletion: "解析が完了しました、データベースに同期しています..."
Node: "ノード"
SyncNode: "ノードデータ同期"
+4
View File
@@ -93,6 +93,10 @@ SubTask: "서브 작업"
#script
ScriptLibrary: "스크립트 라이브러리"
ScriptSyncSkip: "현재 스크립트 라이브러리가 이미 최신 버전입니다!"
DownloadData: "스크립트 라이브러리 파일 data.yaml 다운로드 중"
DownloadPackage: "스크립트 라이브러리 패키지 다운로드 중"
AnalyticCompletion: "분석 완료, 데이터베이스에 동기화 중..."
Node: "노드"
SyncNode: "노드 데이터 동기화"
+4
View File
@@ -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"
+4
View File
@@ -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ó"
+4
View File
@@ -93,6 +93,10 @@ SubTask: "Подзадача"
#script
ScriptLibrary: "Библиотека скриптов"
ScriptSyncSkip: "Текущая библиотека скриптов уже является последней версией!"
DownloadData: "Загрузка файла библиотеки скриптов data.yaml"
DownloadPackage: "Загрузка архива библиотеки скриптов"
AnalyticCompletion: "Анализ завершён, начинается синхронизация с базой данных..."
Node: "Узел"
SyncNode: "Синхронизация данных узла"
+4
View File
@@ -93,6 +93,10 @@ SubTask: "子任務"
#script
ScriptLibrary: "腳本庫"
ScriptSyncSkip: "當前腳本庫已是最新版本!"
DownloadData: "下載腳本庫文件 data.yaml"
DownloadPackage: "下載腳本庫壓縮包"
AnalyticCompletion: "解析成功,現在開始同步到資料庫..."
#sync node
Node: "節點"
+4
View File
@@ -93,6 +93,10 @@ SubTask: "子任务"
#script
ScriptLibrary: "脚本库"
ScriptSyncSkip: "检查到当前脚本库已是最新版本!"
DownloadData: "下载脚本库文件 data.yaml"
DownloadPackage: "下载脚本库压缩包"
AnalyticCompletion: "解析成功,现在开始同步到数据库..."
#sync node
Node: "节点"
+3 -2
View File
@@ -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"},
+2
View 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)
+143 -1
View File
@@ -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
},
}
+10 -1
View File
@@ -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
}
+31 -1
View File
@@ -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) {
+12 -2
View File
@@ -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{
+13
View File
@@ -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
+1 -1
View File
@@ -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",
+3
View File
@@ -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`);
};
+2 -2
View File
@@ -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 = () => {
+1 -1
View File
@@ -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);
+12 -4
View File
@@ -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="&#59016;" 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="&#59216;" 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="&#59196;" 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="&#59351;" 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.
+6 -1
View File
@@ -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);
}
};
};
+8 -1
View File
@@ -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;
+31 -11
View File
@@ -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>
+1 -1
View File
@@ -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';
+8
View File
@@ -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',
+14 -4
View File
@@ -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',
+14 -5
View File
@@ -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: '修正操作',
+15 -5
View File
@@ -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