diff --git a/backend/app/api/v1/setting.go b/backend/app/api/v1/setting.go index d987ff79a..bcf9fed5f 100644 --- a/backend/app/api/v1/setting.go +++ b/backend/app/api/v1/setting.go @@ -1,6 +1,7 @@ package v1 import ( + "encoding/base64" "errors" "os" "path" @@ -60,6 +61,37 @@ func (b *BaseApi) UpdateSetting(c *gin.Context) { helper.SuccessWithData(c, nil) } +// @Tags System Setting +// @Summary Update proxy setting +// @Description 服务器代理配置 +// @Accept json +// @Param request body dto.ProxyUpdate true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Router /settings/proxy/update [post] +// @x-panel-log {"bodyKeys":["proxyUrl","proxyPort"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"服务器代理配置 [proxyPort]:[proxyPort]","formatEN":"set proxy [proxyPort]:[proxyPort]."} +func (b *BaseApi) UpdateProxy(c *gin.Context) { + var req dto.ProxyUpdate + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + + if len(req.ProxyPasswd) != 0 && len(req.ProxyType) != 0 { + pass, err := base64.StdEncoding.DecodeString(req.ProxyPasswd) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + req.ProxyPasswd = string(pass) + } + + if err := settingService.UpdateProxy(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + // @Tags System Setting // @Summary Update system setting // @Description 隐藏高级功能菜单 diff --git a/backend/app/dto/setting.go b/backend/app/dto/setting.go index ee0897b73..866fbe347 100644 --- a/backend/app/dto/setting.go +++ b/backend/app/dto/setting.go @@ -58,6 +58,13 @@ type SettingInfo struct { SnapshotIgnore string `json:"snapshotIgnore"` XpackHideMenu string `json:"xpackHideMenu"` NoAuthSetting string `json:"noAuthSetting"` + + ProxyUrl string `json:"proxyUrl"` + ProxyType string `json:"proxyType"` + ProxyPort string `json:"proxyPort"` + ProxyUser string `json:"proxyUser"` + ProxyPasswd string `json:"proxyPasswd"` + ProxyPasswdKeep string `json:"proxyPasswdKeep"` } type SettingUpdate struct { @@ -160,6 +167,15 @@ type Upgrade struct { Version string `json:"version" validate:"required"` } +type ProxyUpdate struct { + ProxyUrl string `json:"proxyUrl"` + ProxyType string `json:"proxyType"` + ProxyPort string `json:"proxyPort"` + ProxyUser string `json:"proxyUser"` + ProxyPasswd string `json:"proxyPasswd"` + ProxyPasswdKeep string `json:"proxyPasswdKeep"` +} + type CleanData struct { SystemClean []CleanTree `json:"systemClean"` UploadClean []CleanTree `json:"uploadClean"` diff --git a/backend/app/service/app.go b/backend/app/service/app.go index 1ff2ad309..91d3211f8 100644 --- a/backend/app/service/app.go +++ b/backend/app/service/app.go @@ -26,6 +26,7 @@ import ( "github.com/1Panel-dev/1Panel/backend/utils/docker" "github.com/1Panel-dev/1Panel/backend/utils/files" http2 "github.com/1Panel-dev/1Panel/backend/utils/http" + httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http" "gopkg.in/yaml.v3" ) @@ -226,21 +227,16 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response. if appDetailDTO.DockerCompose == "" { filename := filepath.Base(appDetailDTO.DownloadUrl) dockerComposeUrl := fmt.Sprintf("%s%s", strings.TrimSuffix(appDetailDTO.DownloadUrl, filename), "docker-compose.yml") - composeRes, err := http.Get(dockerComposeUrl) + statusCode, composeRes, err := httpUtil.HandleGet(dockerComposeUrl, http.MethodGet) if err != nil { return appDetailDTO, buserr.WithDetail("ErrGetCompose", err.Error(), err) } - defer composeRes.Body.Close() - bodyContent, err := io.ReadAll(composeRes.Body) - if err != nil { - return appDetailDTO, buserr.WithDetail("ErrGetCompose", err.Error(), err) - } - if composeRes.StatusCode > 200 { - return appDetailDTO, buserr.WithDetail("ErrGetCompose", string(bodyContent), err) + if statusCode > 200 { + return appDetailDTO, buserr.WithDetail("ErrGetCompose", string(composeRes), err) } - detail.DockerCompose = string(bodyContent) + detail.DockerCompose = string(composeRes) _ = appDetailRepo.Update(context.Background(), detail) - appDetailDTO.DockerCompose = string(bodyContent) + appDetailDTO.DockerCompose = string(composeRes) } appDetailDTO.HostMode = isHostModel(appDetailDTO.DockerCompose) @@ -840,19 +836,14 @@ func (a AppService) SyncAppListFromRemote() (err error) { global.LOG.Infof("Starting synchronization of application details...") for _, l := range list.Apps { app := appsMap[l.AppProperty.Key] - iconRes, err := http.Get(l.Icon) - if err != nil { - return err - } - body, err := io.ReadAll(iconRes.Body) + _, iconRes, err := httpUtil.HandleGet(l.Icon, http.MethodGet) if err != nil { return err } iconStr := "" - if !strings.Contains(string(body), "") { - iconStr = base64.StdEncoding.EncodeToString(body) + if !strings.Contains(string(iconRes), "") { + iconStr = base64.StdEncoding.EncodeToString(iconRes) } - _ = iconRes.Body.Close() app.Icon = iconStr app.TagsKey = l.AppProperty.Tags @@ -872,16 +863,11 @@ func (a AppService) SyncAppListFromRemote() (err error) { if _, ok := InitTypes[app.Type]; ok { dockerComposeUrl := fmt.Sprintf("%s/%s", versionUrl, "docker-compose.yml") - composeRes, err := http.Get(dockerComposeUrl) - if err != nil { - return err - } - defer composeRes.Body.Close() - bodyContent, err := io.ReadAll(composeRes.Body) + _, composeRes, err := httpUtil.HandleGet(dockerComposeUrl, http.MethodGet) if err != nil { return err } - detail.DockerCompose = string(bodyContent) + detail.DockerCompose = string(composeRes) } else { detail.DockerCompose = "" } diff --git a/backend/app/service/app_utils.go b/backend/app/service/app_utils.go index f3c04c066..1ae1cc49e 100644 --- a/backend/app/service/app_utils.go +++ b/backend/app/service/app_utils.go @@ -15,6 +15,7 @@ import ( "strconv" "strings" + httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http" "github.com/1Panel-dev/1Panel/backend/utils/xpack" "github.com/docker/docker/api/types/container" @@ -504,7 +505,7 @@ func upgradeInstall(installID uint, detailID uint, backup, pullImage bool) error _ = appDetailRepo.Update(context.Background(), detail) } go func() { - _, _ = http.Get(detail.DownloadCallBackUrl) + _, _, _ = httpUtil.HandleGet(detail.DownloadCallBackUrl, http.MethodGet) }() } @@ -782,7 +783,7 @@ func copyData(app model.App, appDetail model.AppDetail, appInstall *model.AppIns return } go func() { - _, _ = http.Get(appDetail.DownloadCallBackUrl) + _, _, _ = httpUtil.HandleGet(appDetail.DownloadCallBackUrl, http.MethodGet) }() } appKey := app.Key diff --git a/backend/app/service/device_clean.go b/backend/app/service/device_clean.go index ffe42cb20..ca06e8415 100644 --- a/backend/app/service/device_clean.go +++ b/backend/app/service/device_clean.go @@ -3,15 +3,16 @@ package service import ( "context" "fmt" - "github.com/1Panel-dev/1Panel/backend/utils/docker" - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/filters" "os" "path" "sort" "strings" "time" + "github.com/1Panel-dev/1Panel/backend/utils/docker" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/utils/cmd" @@ -514,6 +515,9 @@ func loadLogTree(fileOp fileUtils.FileOp) []dto.CleanTree { func loadContainerTree() []dto.CleanTree { var treeData []dto.CleanTree client, err := docker.NewDockerClient() + if err != nil { + return treeData + } diskUsage, err := client.DiskUsage(context.Background(), types.DiskUsageOptions{}) if err != nil { return treeData diff --git a/backend/app/service/runtime_utils.go b/backend/app/service/runtime_utils.go index f24a080de..bcf2bcf56 100644 --- a/backend/app/service/runtime_utils.go +++ b/backend/app/service/runtime_utils.go @@ -19,6 +19,7 @@ import ( "github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/utils/docker" "github.com/1Panel-dev/1Panel/backend/utils/files" + httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http" "github.com/pkg/errors" "github.com/subosito/gotenv" "gopkg.in/yaml.v3" @@ -54,12 +55,10 @@ func handleNode(create request.RuntimeCreate, runtime *model.Runtime, fileOp fil } go func() { - res, err := http.Get(nodeDetail.DownloadCallBackUrl) - if err != nil { + if _ , _, err := httpUtil.HandleGet(nodeDetail.DownloadCallBackUrl, http.MethodGet); err != nil { global.LOG.Errorf("http request failed(handleNode), err: %v", err) return } - res.Body.Close() }() go startRuntime(runtime) diff --git a/backend/app/service/setting.go b/backend/app/service/setting.go index 06b688182..4f88f54fc 100644 --- a/backend/app/service/setting.go +++ b/backend/app/service/setting.go @@ -33,6 +33,7 @@ type ISettingService interface { GetSettingInfo() (*dto.SettingInfo, error) LoadInterfaceAddr() ([]string, error) Update(key, value string) error + UpdateProxy(req dto.ProxyUpdate) error UpdatePassword(c *gin.Context, old, new string) error UpdatePort(port uint) error UpdateBindInfo(req dto.BindInfo) error @@ -62,6 +63,12 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) { if err := json.Unmarshal(arr, &info); err != nil { return nil, err } + if info.ProxyPasswdKeep != constant.StatusEnable { + info.ProxyPasswd = "" + } else { + info.ProxyPasswd, _ = encrypt.StringDecrypt(info.ProxyPasswd) + } + info.LocalTime = time.Now().Format("2006-01-02 15:04:05 MST -0700") return &info, err } @@ -162,6 +169,29 @@ func (u *SettingService) UpdateBindInfo(req dto.BindInfo) error { return nil } +func (u *SettingService) UpdateProxy(req dto.ProxyUpdate) error { + if err := settingRepo.Update("ProxyUrl", req.ProxyUrl); err != nil { + return err + } + if err := settingRepo.Update("ProxyType", req.ProxyType); err != nil { + return err + } + if err := settingRepo.Update("ProxyPort", req.ProxyPort); err != nil { + return err + } + if err := settingRepo.Update("ProxyUser", req.ProxyUser); err != nil { + return err + } + pass, _ := encrypt.StringEncrypt(req.ProxyPasswd) + if err := settingRepo.Update("ProxyPasswd", pass); err != nil { + return err + } + if err := settingRepo.Update("ProxyPasswdKeep", req.ProxyPasswdKeep); err != nil { + return err + } + return nil +} + func (u *SettingService) UpdatePort(port uint) error { if common.ScanPort(int(port)) { return buserr.WithDetail(constant.ErrPortInUsed, port, nil) diff --git a/backend/app/service/upgrade.go b/backend/app/service/upgrade.go index 395c52e8e..e941399a3 100644 --- a/backend/app/service/upgrade.go +++ b/backend/app/service/upgrade.go @@ -3,7 +3,6 @@ package service import ( "encoding/json" "fmt" - "io" "net/http" "os" "path" @@ -16,6 +15,7 @@ import ( "github.com/1Panel-dev/1Panel/backend/utils/cmd" "github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/files" + httpUtil "github.com/1Panel-dev/1Panel/backend/utils/http" ) type UpgradeService struct{} @@ -258,15 +258,13 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion, mode string) if !isLatest { path = fmt.Sprintf("%s/%s/latest.current", global.CONF.System.RepoUrl, mode) } - latestVersionRes, err := http.Get(path) + _, latestVersionRes, err := httpUtil.HandleGet(path, http.MethodGet) if err != nil { global.LOG.Errorf("load latest version from oss failed, err: %v", err) return "" } - defer latestVersionRes.Body.Close() - versionByte, err := io.ReadAll(latestVersionRes.Body) - version := string(versionByte) - if err != nil || strings.Contains(version, "<") { + version := string(latestVersionRes) + if strings.Contains(version, "<") { global.LOG.Errorf("load latest version from oss failed, err: %v", version) return "" } @@ -275,7 +273,7 @@ func (u *UpgradeService) loadVersion(isLatest bool, currentVersion, mode string) } versionMap := make(map[string]string) - if err := json.Unmarshal(versionByte, &versionMap); err != nil { + if err := json.Unmarshal(latestVersionRes, &versionMap); err != nil { global.LOG.Errorf("load latest version from oss failed (error unmarshal), err: %v", err) return "" } @@ -321,16 +319,11 @@ func (u *UpgradeService) checkVersion(v2, v1 string) string { } func (u *UpgradeService) loadReleaseNotes(path string) (string, error) { - releaseNotes, err := http.Get(path) + _, releaseNotes, err := httpUtil.HandleGet(path, http.MethodGet) if err != nil { return "", err } - defer releaseNotes.Body.Close() - release, err := io.ReadAll(releaseNotes.Body) - if err != nil { - return "", err - } - return string(release), nil + return string(releaseNotes), nil } func loadArch() (string, error) { diff --git a/backend/init/migration/migrate.go b/backend/init/migration/migrate.go index 069a202f6..a84d17f24 100644 --- a/backend/init/migration/migrate.go +++ b/backend/init/migration/migrate.go @@ -87,6 +87,7 @@ func Init() { migrations.AddRedisCommand, migrations.AddMonitorMenu, migrations.AddFtp, + migrations.AddProxy, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/backend/init/migration/migrations/v_1_10.go b/backend/init/migration/migrations/v_1_10.go index 638cf3741..85c131bf8 100644 --- a/backend/init/migration/migrations/v_1_10.go +++ b/backend/init/migration/migrations/v_1_10.go @@ -213,3 +213,28 @@ var AddFtp = &gormigrate.Migration{ return nil }, } + +var AddProxy = &gormigrate.Migration{ + ID: "20240528-add-proxy", + Migrate: func(tx *gorm.DB) error { + if err := tx.Create(&model.Setting{Key: "ProxyType", Value: ""}).Error; err != nil { + return err + } + if err := tx.Create(&model.Setting{Key: "ProxyUrl", Value: ""}).Error; err != nil { + return err + } + if err := tx.Create(&model.Setting{Key: "ProxyPort", Value: ""}).Error; err != nil { + return err + } + if err := tx.Create(&model.Setting{Key: "ProxyUser", Value: ""}).Error; err != nil { + return err + } + if err := tx.Create(&model.Setting{Key: "ProxyPasswd", Value: ""}).Error; err != nil { + return err + } + if err := tx.Create(&model.Setting{Key: "ProxyPasswdKeep", Value: ""}).Error; err != nil { + return err + } + return nil + }, +} diff --git a/backend/router/ro_setting.go b/backend/router/ro_setting.go index b689d2437..e2a911e0a 100644 --- a/backend/router/ro_setting.go +++ b/backend/router/ro_setting.go @@ -24,6 +24,7 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) { settingRouter.POST("/update", baseApi.UpdateSetting) settingRouter.GET("/interface", baseApi.LoadInterfaceAddr) settingRouter.POST("/menu/update", baseApi.UpdateMenu) + settingRouter.POST("/proxy/update", baseApi.UpdateProxy) settingRouter.POST("/bind/update", baseApi.UpdateBindInfo) settingRouter.POST("/port/update", baseApi.UpdatePort) settingRouter.POST("/ssl/update", baseApi.UpdateSSL) diff --git a/backend/server/server.go b/backend/server/server.go index 4fdfbe81e..c16c62a36 100644 --- a/backend/server/server.go +++ b/backend/server/server.go @@ -4,12 +4,13 @@ import ( "crypto/tls" "encoding/gob" "fmt" - "github.com/1Panel-dev/1Panel/backend/i18n" "net" "net/http" "os" "path" + "github.com/1Panel-dev/1Panel/backend/i18n" + "github.com/1Panel-dev/1Panel/backend/init/app" "github.com/1Panel-dev/1Panel/backend/init/business" @@ -36,6 +37,7 @@ func Start() { log.Init() db.Init() migration.Init() + InitOthers() app.Init() validator.Init() gob.Register(psession.SessionUser{}) @@ -45,7 +47,6 @@ func Start() { cron.Run() business.Init() hook.Init() - InitOthers() rootRouter := router.Routers() diff --git a/backend/utils/http/get.go b/backend/utils/http/get.go index 66a9aed1b..9e0ae5ad7 100644 --- a/backend/utils/http/get.go +++ b/backend/utils/http/get.go @@ -10,22 +10,29 @@ import ( "time" "github.com/1Panel-dev/1Panel/backend/buserr" + "github.com/1Panel-dev/1Panel/backend/utils/xpack" ) func GetHttpRes(url string) (*http.Response, error) { client := &http.Client{ Timeout: time.Second * 300, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - DialContext: (&net.Dialer{ - Timeout: 60 * time.Second, - KeepAlive: 60 * time.Second, - }).DialContext, - TLSHandshakeTimeout: 5 * time.Second, - ResponseHeaderTimeout: 10 * time.Second, - IdleConnTimeout: 15 * time.Second, - }, } + transportItem := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + DialContext: (&net.Dialer{ + Timeout: 60 * time.Second, + KeepAlive: 60 * time.Second, + }).DialContext, + TLSHandshakeTimeout: 5 * time.Second, + ResponseHeaderTimeout: 10 * time.Second, + IdleConnTimeout: 15 * time.Second, + } + ok, transport := xpack.LoadRequestTransport() + if ok { + transportItem.DialContext = transport.DialContext + transportItem.Proxy = transport.Proxy + } + client.Transport = transportItem req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil) if err != nil { diff --git a/backend/utils/http/request.go b/backend/utils/http/request.go new file mode 100644 index 000000000..0afe7e260 --- /dev/null +++ b/backend/utils/http/request.go @@ -0,0 +1,44 @@ +package http + +import ( + "context" + "io" + "net/http" + "time" + + "github.com/1Panel-dev/1Panel/backend/global" + "github.com/1Panel-dev/1Panel/backend/utils/xpack" +) + +func HandleGet(url, method string) (int, []byte, error) { + defer func() { + if r := recover(); r != nil { + global.LOG.Errorf(" A panic occurred during handle request, error message: %v", r) + return + } + }() + + client := http.Client{Timeout: 10 * time.Second} + ok, transport := xpack.LoadRequestTransport() + if ok { + client.Transport = transport + } + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + request, err := http.NewRequestWithContext(ctx, method, url, nil) + if err != nil { + return 0, nil, err + } + request.Header.Set("Content-Type", "application/json") + resp, err := client.Do(request) + if err != nil { + return 0, nil, err + } + body, err := io.ReadAll(resp.Body) + if err != nil { + return 0, nil, err + } + defer resp.Body.Close() + + return resp.StatusCode, body, nil +} diff --git a/backend/utils/xpack/xpack.go b/backend/utils/xpack/xpack.go index 31a3b18fe..efbbf4004 100644 --- a/backend/utils/xpack/xpack.go +++ b/backend/utils/xpack/xpack.go @@ -2,8 +2,14 @@ package xpack +import "net/http" + func RemoveTamper(website string) {} +func LoadRequestTransport() (bool, *http.Transport) { + return false, nil +} + func LoadGpuInfo() []interface{} { return nil } diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index a84a97365..ec76f6230 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -10352,6 +10352,49 @@ const docTemplate = `{ } } }, + "/settings/proxy/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "服务器代理配置", + "consumes": [ + "application/json" + ], + "tags": [ + "System Setting" + ], + "summary": "Update proxy setting", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.ProxyUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-panel-log": { + "BeforeFunctions": [], + "bodyKeys": [ + "proxyUrl", + "proxyPort" + ], + "formatEN": "set proxy [proxyPort]:[proxyPort].", + "formatZH": "服务器代理配置 [proxyPort]:[proxyPort]", + "paramKeys": [] + } + } + }, "/settings/search": { "post": { "security": [ @@ -17817,6 +17860,29 @@ const docTemplate = `{ } } }, + "dto.ProxyUpdate": { + "type": "object", + "properties": { + "proxyPasswd": { + "type": "string" + }, + "proxyPasswdKeep": { + "type": "string" + }, + "proxyPort": { + "type": "string" + }, + "proxyType": { + "type": "string" + }, + "proxyUrl": { + "type": "string" + }, + "proxyUser": { + "type": "string" + } + } + }, "dto.RecordSearch": { "type": "object", "required": [ @@ -18468,6 +18534,24 @@ const docTemplate = `{ "port": { "type": "string" }, + "proxyPasswd": { + "type": "string" + }, + "proxyPasswdKeep": { + "type": "string" + }, + "proxyPort": { + "type": "string" + }, + "proxyType": { + "type": "string" + }, + "proxyUrl": { + "type": "string" + }, + "proxyUser": { + "type": "string" + }, "securityEntrance": { "type": "string" }, @@ -20252,7 +20336,6 @@ const docTemplate = `{ "request.NginxRewriteUpdate": { "type": "object", "required": [ - "content", "name", "websiteId" ], diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index a75e7e809..50f3d8581 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -10345,6 +10345,49 @@ } } }, + "/settings/proxy/update": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "服务器代理配置", + "consumes": [ + "application/json" + ], + "tags": [ + "System Setting" + ], + "summary": "Update proxy setting", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/dto.ProxyUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-panel-log": { + "BeforeFunctions": [], + "bodyKeys": [ + "proxyUrl", + "proxyPort" + ], + "formatEN": "set proxy [proxyPort]:[proxyPort].", + "formatZH": "服务器代理配置 [proxyPort]:[proxyPort]", + "paramKeys": [] + } + } + }, "/settings/search": { "post": { "security": [ @@ -17810,6 +17853,29 @@ } } }, + "dto.ProxyUpdate": { + "type": "object", + "properties": { + "proxyPasswd": { + "type": "string" + }, + "proxyPasswdKeep": { + "type": "string" + }, + "proxyPort": { + "type": "string" + }, + "proxyType": { + "type": "string" + }, + "proxyUrl": { + "type": "string" + }, + "proxyUser": { + "type": "string" + } + } + }, "dto.RecordSearch": { "type": "object", "required": [ @@ -18461,6 +18527,24 @@ "port": { "type": "string" }, + "proxyPasswd": { + "type": "string" + }, + "proxyPasswdKeep": { + "type": "string" + }, + "proxyPort": { + "type": "string" + }, + "proxyType": { + "type": "string" + }, + "proxyUrl": { + "type": "string" + }, + "proxyUser": { + "type": "string" + }, "securityEntrance": { "type": "string" }, @@ -20245,7 +20329,6 @@ "request.NginxRewriteUpdate": { "type": "object", "required": [ - "content", "name", "websiteId" ], diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index d1ef2bc53..c425b1c0f 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -2247,6 +2247,21 @@ definitions: - from - type type: object + dto.ProxyUpdate: + properties: + proxyPasswd: + type: string + proxyPasswdKeep: + type: string + proxyPort: + type: string + proxyType: + type: string + proxyUrl: + type: string + proxyUser: + type: string + type: object dto.RecordSearch: properties: detailName: @@ -2681,6 +2696,18 @@ definitions: type: string port: type: string + proxyPasswd: + type: string + proxyPasswdKeep: + type: string + proxyPort: + type: string + proxyType: + type: string + proxyUrl: + type: string + proxyUser: + type: string securityEntrance: type: string serverPort: @@ -3873,7 +3900,6 @@ definitions: websiteId: type: integer required: - - content - name - websiteId type: object @@ -11727,6 +11753,34 @@ paths: formatEN: update system port => [serverPort] formatZH: 修改系统端口 => [serverPort] paramKeys: [] + /settings/proxy/update: + post: + consumes: + - application/json + description: 服务器代理配置 + parameters: + - description: request + in: body + name: request + required: true + schema: + $ref: '#/definitions/dto.ProxyUpdate' + responses: + "200": + description: OK + security: + - ApiKeyAuth: [] + summary: Update proxy setting + tags: + - System Setting + x-panel-log: + BeforeFunctions: [] + bodyKeys: + - proxyUrl + - proxyPort + formatEN: set proxy [proxyPort]:[proxyPort]. + formatZH: 服务器代理配置 [proxyPort]:[proxyPort] + paramKeys: [] /settings/search: post: description: 加载系统配置信息 diff --git a/frontend/src/api/interface/setting.ts b/frontend/src/api/interface/setting.ts index 53d07fc7e..7dc0d3d4b 100644 --- a/frontend/src/api/interface/setting.ts +++ b/frontend/src/api/interface/setting.ts @@ -50,11 +50,26 @@ export namespace Setting { snapshotIgnore: string; xpackHideMenu: string; noAuthSetting: string; + + proxyUrl: string; + proxyType: string; + proxyPort: string; + proxyUser: string; + proxyPasswd: string; + proxyPasswdKeep: string; } export interface SettingUpdate { key: string; value: string; } + export interface ProxyUpdate { + proxyUrl: string; + proxyType: string; + proxyPort: string; + proxyUser: string; + proxyPasswd: string; + proxyPasswdKeep: string; + } export interface SSLUpdate { ssl: string; domain: string; diff --git a/frontend/src/api/modules/setting.ts b/frontend/src/api/modules/setting.ts index 57da56d2a..fdee4e3c7 100644 --- a/frontend/src/api/modules/setting.ts +++ b/frontend/src/api/modules/setting.ts @@ -40,6 +40,14 @@ export const updateMenu = (param: Setting.SettingUpdate) => { return http.post(`/settings/menu/update`, param); }; +export const updateProxy = (params: Setting.ProxyUpdate) => { + let request = deepCopy(params) as Setting.ProxyUpdate; + if (request.proxyPasswd) { + request.proxyPasswd = Base64.encode(request.proxyPasswd); + } + return http.post(`/settings/proxy/update`, request); +}; + export const updatePassword = (param: Setting.PasswordUpdate) => { return http.post(`/settings/password/update`, param); }; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 7b4b59d56..049a6ab0b 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1272,6 +1272,15 @@ const message = { sessionTimeoutHelper: 'If you do not operate the panel for more than {0} seconds, the panel automatically logs out', systemIP: 'System Address', + proxy: 'Server Proxy', + proxyHelper: 'After setting up the proxy server, it will be effective in the following scenarios:', + proxyHelper1: 'Downloading and synchronizing installation packages from the app store', + proxyHelper2: 'System version upgrades and retrieving update information', + proxyHelper3: 'Verification and synchronization of system licenses', + proxyType: 'Proxy Type', + proxyUrl: 'Proxy Address', + proxyPort: 'Proxy Port', + proxyPasswdKeep: 'Remember Password', systemIPWarning: 'The server address is not currently set. Please set it in the control panel first!', systemIPWarning1: 'The current server address is set to {0}, and quick redirection is not possible!', defaultNetwork: 'Network Card', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 2df1823d2..5b7408487 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -1204,7 +1204,16 @@ const message = { sessionTimeout: '超時時間', sessionTimeoutError: '最小超時時間為 300 秒', sessionTimeoutHelper: '如果用戶超過 {0} 秒未操作面板,面板將自動退出登錄', - systemIP: '服務器地址', + systemIP: '伺服器地址', + proxy: '伺服器代理', + proxyHelper: '設置代理伺服器後,將在以下場景中生效:', + proxyHelper1: '應用商店的安裝包下載和同步', + proxyHelper2: '系統版本升級及獲取更新說明', + proxyHelper3: '系統許可證的驗證和同步', + proxyType: '代理類型', + proxyUrl: '代理地址', + proxyPort: '代理端口', + proxyPasswdKeep: '記住密碼', systemIPWarning: '當前未設置服務器地址,請先在面板設置中設置!', systemIPWarning1: '當前服務器地址設置為 {0},無法快速跳轉!', defaultNetwork: '默認網卡', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 51e552e79..5c68e514d 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1206,6 +1206,15 @@ const message = { sessionTimeoutError: '最小超时时间为 300 秒', sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板,面板将自动退出登录', systemIP: '服务器地址', + proxy: '服务器代理', + proxyHelper: '设置代理服务器后,将在以下场景中生效:', + proxyHelper1: '应用商店的安装包下载和同步', + proxyHelper2: '系统版本升级及获取更新说明', + proxyHelper3: '系统许可证的验证和同步', + proxyType: '代理类型', + proxyUrl: '代理地址', + proxyPort: '代理端口', + proxyPasswdKeep: '记住密码', systemIPWarning: '当前未设置服务器地址,请先在面板设置中设置!', systemIPWarning1: '当前服务器地址设置为 {0},无法快速跳转!', defaultNetwork: '默认网卡', diff --git a/frontend/src/views/setting/panel/index.vue b/frontend/src/views/setting/panel/index.vue index 40b77258d..5844e3bfe 100644 --- a/frontend/src/views/setting/panel/index.vue +++ b/frontend/src/views/setting/panel/index.vue @@ -116,6 +116,16 @@ + + + + + + + @@ -169,6 +180,7 @@ import UserName from '@/views/setting/panel/username/index.vue'; import Timeout from '@/views/setting/panel/timeout/index.vue'; import PanelName from '@/views/setting/panel/name/index.vue'; import SystemIP from '@/views/setting/panel/systemip/index.vue'; +import Proxy from '@/views/setting/panel/proxy/index.vue'; import Network from '@/views/setting/panel/default-network/index.vue'; import HideMenu from '@/views/setting/panel/hidemenu/index.vue'; import { storeToRefs } from 'pinia'; @@ -200,6 +212,14 @@ const form = reactive({ defaultNetworkVal: '', developerMode: '', + proxyShow: '', + proxyUrl: '', + proxyType: '', + proxyPort: '', + proxyUser: '', + proxyPasswd: '', + proxyPasswdKeep: '', + proHideMenus: ref(i18n.t('setting.unSetting')), hideMenuList: '', }); @@ -210,6 +230,7 @@ const userNameRef = ref(); const passwordRef = ref(); const panelNameRef = ref(); const systemIPRef = ref(); +const proxyRef = ref(); const timeoutRef = ref(); const networkRef = ref(); const hideMenuRef = ref(); @@ -243,7 +264,14 @@ const search = async () => { form.hideMenuList = res.data.xpackHideMenu; form.developerMode = res.data.developerMode; - // 提取隐藏节点的 title 并显示 + form.proxyUrl = res.data.proxyUrl; + form.proxyType = res.data.proxyType; + form.proxyPort = res.data.proxyPort; + form.proxyShow = form.proxyUrl ? form.proxyUrl + ':' + form.proxyPort : unset.value; + form.proxyUser = res.data.proxyUser; + form.proxyPasswd = res.data.proxyPasswd; + form.proxyPasswdKeep = res.data.proxyPasswdKeep; + const json: Node = JSON.parse(res.data.xpackHideMenu); const checkedTitles = getCheckedTitles(json); form.proHideMenus = checkedTitles.toString(); @@ -297,6 +325,16 @@ const onChangeTimeout = () => { const onChangeSystemIP = () => { systemIPRef.value.acceptParams({ systemIP: form.systemIP }); }; +const onChangeProxy = () => { + proxyRef.value.acceptParams({ + url: form.proxyUrl, + type: form.proxyType, + port: form.proxyPort, + user: form.proxyUser, + passwd: form.proxyPasswd, + passwdKeep: form.proxyPasswdKeep, + }); +}; const onChangeNetwork = () => { networkRef.value.acceptParams({ defaultNetwork: form.defaultNetwork }); }; diff --git a/frontend/src/views/setting/panel/proxy/index.vue b/frontend/src/views/setting/panel/proxy/index.vue new file mode 100644 index 000000000..b06b4514b --- /dev/null +++ b/frontend/src/views/setting/panel/proxy/index.vue @@ -0,0 +1,151 @@ + + + diff --git a/frontend/src/views/toolbox/ftp/index.vue b/frontend/src/views/toolbox/ftp/index.vue index 9d2eac868..97188518e 100644 --- a/frontend/src/views/toolbox/ftp/index.vue +++ b/frontend/src/views/toolbox/ftp/index.vue @@ -295,7 +295,7 @@ const onSync = async () => { await syncFtp() .then(() => { loading.value = false; - MsgSuccess(i18n.global.t('toolbox.ftp.operationSuccess')); + MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); search(); }) .catch(() => {