Browse Source

feat: 增加服务器代理设置 (#5189)

pull/5196/head
ssongliu 6 months ago committed by GitHub
parent
commit
8fc708be97
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 32
      backend/app/api/v1/setting.go
  2. 16
      backend/app/dto/setting.go
  3. 36
      backend/app/service/app.go
  4. 5
      backend/app/service/app_utils.go
  5. 10
      backend/app/service/device_clean.go
  6. 5
      backend/app/service/runtime_utils.go
  7. 30
      backend/app/service/setting.go
  8. 21
      backend/app/service/upgrade.go
  9. 1
      backend/init/migration/migrate.go
  10. 25
      backend/init/migration/migrations/v_1_10.go
  11. 1
      backend/router/ro_setting.go
  12. 5
      backend/server/server.go
  13. 27
      backend/utils/http/get.go
  14. 44
      backend/utils/http/request.go
  15. 6
      backend/utils/xpack/xpack.go
  16. 85
      cmd/server/docs/docs.go
  17. 85
      cmd/server/docs/swagger.json
  18. 56
      cmd/server/docs/swagger.yaml
  19. 15
      frontend/src/api/interface/setting.ts
  20. 8
      frontend/src/api/modules/setting.ts
  21. 9
      frontend/src/lang/modules/en.ts
  22. 11
      frontend/src/lang/modules/tw.ts
  23. 9
      frontend/src/lang/modules/zh.ts
  24. 40
      frontend/src/views/setting/panel/index.vue
  25. 151
      frontend/src/views/setting/panel/proxy/index.vue
  26. 2
      frontend/src/views/toolbox/ftp/index.vue

32
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 隐藏高级功能菜单

16
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"`

36
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), "<xml>") {
iconStr = base64.StdEncoding.EncodeToString(body)
if !strings.Contains(string(iconRes), "<xml>") {
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 = ""
}

5
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

10
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

5
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)

30
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)

21
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) {

1
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)

25
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
},
}

1
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)

5
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()

27
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 {

44
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
}

6
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
}

85
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"
],

85
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"
],

56
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: 加载系统配置信息

15
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;

8
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);
};

9
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',

11
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: '默認網卡',

9
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: '默认网卡',

40
frontend/src/views/setting/panel/index.vue

@ -116,6 +116,16 @@
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.proxy')" prop="proxyShow">
<el-input disabled v-model="form.proxyShow">
<template #append>
<el-button @click="onChangeProxy" icon="Setting">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.developerMode')" prop="developerMode">
<el-radio-group
@change="onSave('DeveloperMode', form.developerMode)"
@ -150,6 +160,7 @@
<UserName ref="userNameRef" />
<PanelName ref="panelNameRef" @search="search()" />
<SystemIP ref="systemIPRef" @search="search()" />
<Proxy ref="proxyRef" @search="search()" />
<Timeout ref="timeoutRef" @search="search()" />
<Network ref="networkRef" @search="search()" />
<HideMenu ref="hideMenuRef" @search="search()" />
@ -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 });
};

151
frontend/src/views/setting/panel/proxy/index.vue

@ -0,0 +1,151 @@
<template>
<div>
<el-drawer
v-model="passwordVisible"
:destroy-on-close="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
size="50%"
>
<template #header>
<DrawerHeader :header="$t('setting.proxy')" :back="handleClose" />
</template>
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" v-loading="loading">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-alert class="common-prompt" :closable="false" type="warning">
<template #default>
{{ $t('setting.proxyHelper') }}
<ul style="margin-left: -20px">
<li v-if="isProductPro">{{ $t('setting.proxyHelper1') }}</li>
<li v-if="isProductPro">{{ $t('setting.proxyHelper2') }}</li>
<li>{{ $t('setting.proxyHelper3') }}</li>
</ul>
</template>
</el-alert>
<el-form-item :label="$t('setting.proxyType')" prop="proxyType">
<el-select v-model="form.proxyType" clearable>
<el-option value="socks5" label="SOCKS5" />
<el-option value="http" label="HTTP" />
<el-option value="https" label="HTTPS" />
</el-select>
</el-form-item>
<el-form-item :label="$t('setting.proxyUrl')" prop="proxyUrl">
<el-input clearable v-model.trim="form.proxyUrl" />
</el-form-item>
<el-form-item :label="$t('setting.proxyPort')" prop="proxyPortItem">
<el-input clearable type="number" v-model.number="form.proxyPortItem" />
</el-form-item>
<el-form-item :label="$t('commons.login.username')" prop="proxyUser">
<el-input clearable v-model.trim="form.proxyUser" />
</el-form-item>
<el-form-item :label="$t('commons.login.password')" prop="proxyPasswd">
<el-input type="password" show-password clearable v-model.trim="form.proxyPasswd" />
</el-form-item>
<el-form-item>
<el-checkbox v-model="form.proxyPasswdKeepItem" :label="$t('setting.proxyPasswdKeep')" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button :disabled="loading" @click="passwordVisible = false">
{{ $t('commons.button.cancel') }}
</el-button>
<el-button :disabled="loading" type="primary" @click="submitChangePassword(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { FormInstance } from 'element-plus';
import { reactive, ref } from 'vue';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { updateProxy } from '@/api/modules/setting';
import { GlobalStore } from '@/store';
import { storeToRefs } from 'pinia';
const globalStore = GlobalStore();
const emit = defineEmits<{ (e: 'search'): void }>();
const { isProductPro } = storeToRefs(globalStore);
const formRef = ref<FormInstance>();
const rules = reactive({
proxyUrl: [Rules.noSpace, Rules.requiredInput],
proxyPortItem: [Rules.port],
});
const loading = ref(false);
const passwordVisible = ref<boolean>(false);
const form = reactive({
proxyUrl: '',
proxyType: '',
proxyPort: '',
proxyPortItem: 7890,
proxyUser: '',
proxyPasswd: '',
proxyPasswdKeep: '',
proxyPasswdKeepItem: false,
});
interface DialogProps {
url: string;
type: string;
port: string;
user: string;
passwd: string;
passwdKeep: string;
}
const acceptParams = (params: DialogProps): void => {
form.proxyUrl = params.url || '127.0.0.1';
form.proxyType = params.type || 'socks5';
form.proxyPortItem = params.port ? Number(params.port) : 7890;
form.proxyUser = params.user;
form.proxyPasswd = params.passwd;
passwordVisible.value = true;
form.proxyPasswdKeepItem = params.passwdKeep === 'Enable';
};
const submitChangePassword = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!form.proxyType) {
form.proxyUrl = '';
form.proxyPort = '';
form.proxyUser = '';
form.proxyPasswd = '';
form.proxyPasswdKeep = '';
} else {
form.proxyPort = form.proxyPortItem + '';
form.proxyPasswdKeep = form.proxyPasswdKeepItem ? 'Enable' : 'Disable';
}
loading.value = true;
await updateProxy(form)
.then(async () => {
loading.value = false;
emit('search');
passwordVisible.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
loading.value = false;
});
});
};
const handleClose = () => {
passwordVisible.value = false;
};
defineExpose({
acceptParams,
});
</script>

2
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(() => {

Loading…
Cancel
Save