mirror of https://github.com/1Panel-dev/1Panel
feat: 网站增加重定向功能 (#1731)
Refs https://github.com/1Panel-dev/1Panel/issues/1654 Refs https://github.com/1Panel-dev/1Panel/issues/1387 Refs https://github.com/1Panel-dev/1Panel/issues/986pull/1744/head
parent
d31eacd049
commit
c14e192bee
|
@ -801,3 +801,70 @@ func (b *BaseApi) UpdateAntiLeech(c *gin.Context) {
|
|||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update redirect conf
|
||||
// @Description 修改重定向配置
|
||||
// @Accept json
|
||||
// @Param request body request.NginxRedirectReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/redirect/update [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"修改网站 [domain] 重定向理配置 ","formatEN":"Update domain [domain] redirect config"}
|
||||
func (b *BaseApi) UpdateRedirectConfig(c *gin.Context) {
|
||||
var req request.NginxRedirectReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
err := websiteService.OperateRedirect(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Get redirect conf
|
||||
// @Description 获取重定向配置
|
||||
// @Accept json
|
||||
// @Param request body request.WebsiteProxyReq true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/redirect [post]
|
||||
func (b *BaseApi) GetRedirectConfig(c *gin.Context) {
|
||||
var req request.WebsiteRedirectReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
res, err := websiteService.GetRedirect(req.WebsiteID)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, res)
|
||||
}
|
||||
|
||||
// @Tags Website
|
||||
// @Summary Update redirect file
|
||||
// @Description 更新重定向文件
|
||||
// @Accept json
|
||||
// @Param request body request.NginxRedirectUpdate true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /websites/redirect/file [post]
|
||||
// @x-panel-log {"bodyKeys":["websiteID"],"paramKeys":[],"BeforeFuntions":[{"input_column":"id","input_value":"websiteID","isList":false,"db":"websites","output_column":"primary_domain","output_value":"domain"}],"formatZH":"更新重定向文件 [domain]","formatEN":"Nginx conf redirect file update [domain]"}
|
||||
func (b *BaseApi) UpdateRedirectConfigFile(c *gin.Context) {
|
||||
var req request.NginxRedirectUpdate
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if err := websiteService.UpdateRedirectFile(req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
|
|
@ -66,3 +66,23 @@ type NginxAntiLeechUpdate struct {
|
|||
LogEnable bool `json:"logEnable"`
|
||||
Blocked bool `json:"blocked"`
|
||||
}
|
||||
|
||||
type NginxRedirectReq struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Domains []string `json:"domains"`
|
||||
KeepPath bool `json:"keepPath" validate:"required"`
|
||||
Enable bool `json:"enable" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
Redirect string `json:"redirect" validate:"required"`
|
||||
Path string `json:"path"`
|
||||
Target string `json:"target" validate:"required"`
|
||||
Operate string `json:"operate" validate:"required"`
|
||||
RedirectRoot bool `json:"redirectRoot"`
|
||||
}
|
||||
|
||||
type NginxRedirectUpdate struct {
|
||||
WebsiteID uint `json:"websiteID" validate:"required"`
|
||||
Content string `json:"content" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
|
|
@ -189,3 +189,7 @@ type WebsiteProxyConfig struct {
|
|||
type WebsiteProxyReq struct {
|
||||
ID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type WebsiteRedirectReq struct {
|
||||
WebsiteID uint `json:"websiteId" validate:"required"`
|
||||
}
|
||||
|
|
|
@ -34,3 +34,17 @@ type NginxAntiLeechRes struct {
|
|||
LogEnable bool `json:"logEnable"`
|
||||
Blocked bool `json:"blocked"`
|
||||
}
|
||||
|
||||
type NginxRedirectConfig struct {
|
||||
WebsiteID uint `json:"websiteID"`
|
||||
Name string `json:"name"`
|
||||
Domains []string `json:"domains"`
|
||||
KeepPath bool `json:"keepPath"`
|
||||
Enable bool `json:"enable"`
|
||||
Type string `json:"type"`
|
||||
Redirect string `json:"redirect"`
|
||||
Path string `json:"path"`
|
||||
Target string `json:"target"`
|
||||
FilePath string `json:"filePath"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
|
|
@ -79,6 +79,9 @@ type IWebsiteService interface {
|
|||
UpdateAuthBasic(req request.NginxAuthUpdate) (err error)
|
||||
GetAntiLeech(id uint) (*response.NginxAntiLeechRes, error)
|
||||
UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err error)
|
||||
OperateRedirect(req request.NginxRedirectReq) (err error)
|
||||
GetRedirect(id uint) (res []response.NginxRedirectConfig, err error)
|
||||
UpdateRedirectFile(req request.NginxRedirectUpdate) (err error)
|
||||
}
|
||||
|
||||
func NewIWebsiteService() IWebsiteService {
|
||||
|
@ -1690,7 +1693,7 @@ func (w WebsiteService) UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err e
|
|||
return
|
||||
}
|
||||
fileOp := files.NewFileOp()
|
||||
backpContent, err := fileOp.GetContent(nginxFull.SiteConfig.Config.FilePath)
|
||||
backupContent, err := fileOp.GetContent(nginxFull.SiteConfig.Config.FilePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -1761,7 +1764,7 @@ func (w WebsiteService) UpdateAntiLeech(req request.NginxAntiLeechUpdate) (err e
|
|||
return
|
||||
}
|
||||
if err = updateNginxConfig(constant.NginxScopeServer, nil, &website); err != nil {
|
||||
_ = fileOp.WriteFile(nginxFull.SiteConfig.Config.FilePath, bytes.NewReader(backpContent), 0755)
|
||||
_ = fileOp.WriteFile(nginxFull.SiteConfig.Config.FilePath, bytes.NewReader(backupContent), 0755)
|
||||
return
|
||||
}
|
||||
return
|
||||
|
@ -1844,3 +1847,320 @@ func (w WebsiteService) GetAntiLeech(id uint) (*response.NginxAntiLeechRes, erro
|
|||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (w WebsiteService) OperateRedirect(req request.NginxRedirectReq) (err error) {
|
||||
var (
|
||||
website model.Website
|
||||
nginxInstall model.AppInstall
|
||||
oldContent []byte
|
||||
)
|
||||
|
||||
website, err = websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nginxInstall, err = getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "redirect")
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(includeDir) {
|
||||
_ = fileOp.CreateDir(includeDir, 0755)
|
||||
}
|
||||
fileName := fmt.Sprintf("%s.conf", req.Name)
|
||||
includePath := path.Join(includeDir, fileName)
|
||||
backName := fmt.Sprintf("%s.bak", req.Name)
|
||||
backPath := path.Join(includeDir, backName)
|
||||
|
||||
if req.Operate == "create" && (fileOp.Stat(includePath) || fileOp.Stat(backPath)) {
|
||||
err = buserr.New(constant.ErrNameIsExist)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
switch req.Operate {
|
||||
case "create":
|
||||
_ = fileOp.DeleteFile(includePath)
|
||||
case "edit":
|
||||
_ = fileOp.WriteFile(includePath, bytes.NewReader(oldContent), 0755)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
config *components.Config
|
||||
oldPar *parser.Parser
|
||||
)
|
||||
|
||||
switch req.Operate {
|
||||
case "create":
|
||||
config = &components.Config{}
|
||||
case "edit":
|
||||
oldPar, err = parser.NewParser(includePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
config = oldPar.Parse()
|
||||
oldContent, err = fileOp.GetContent(includePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
case "delete":
|
||||
_ = fileOp.DeleteFile(includePath)
|
||||
_ = fileOp.DeleteFile(backPath)
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
case "disable":
|
||||
_ = fileOp.Rename(includePath, backPath)
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
case "enable":
|
||||
_ = fileOp.Rename(backPath, includePath)
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
}
|
||||
|
||||
target := req.Target
|
||||
block := &components.Block{}
|
||||
|
||||
switch req.Type {
|
||||
case "path":
|
||||
if req.KeepPath {
|
||||
target = req.Target + "$1"
|
||||
}
|
||||
redirectKey := "permanent"
|
||||
if req.Redirect == "302" {
|
||||
redirectKey = "redirect"
|
||||
}
|
||||
block = &components.Block{
|
||||
Directives: []components.IDirective{
|
||||
&components.Directive{
|
||||
Name: "rewrite",
|
||||
Parameters: []string{fmt.Sprintf("^%s(.*)", req.Path), target, redirectKey},
|
||||
},
|
||||
},
|
||||
}
|
||||
case "domain":
|
||||
if req.KeepPath {
|
||||
target = req.Target + "$request_uri"
|
||||
}
|
||||
returnBlock := &components.Block{
|
||||
Directives: []components.IDirective{
|
||||
&components.Directive{
|
||||
Name: "return",
|
||||
Parameters: []string{req.Redirect, target},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, domain := range req.Domains {
|
||||
block.Directives = append(block.Directives, &components.Directive{
|
||||
Name: "if",
|
||||
Parameters: []string{"($host", "~", fmt.Sprintf("'^%s')", domain)},
|
||||
Block: returnBlock,
|
||||
})
|
||||
}
|
||||
case "404":
|
||||
if req.KeepPath && !req.RedirectRoot {
|
||||
target = req.Target + "$request_uri"
|
||||
}
|
||||
if req.RedirectRoot {
|
||||
target = "/"
|
||||
} else {
|
||||
if req.KeepPath {
|
||||
target = req.Target + "$request_uri"
|
||||
}
|
||||
}
|
||||
block = &components.Block{
|
||||
Directives: []components.IDirective{
|
||||
&components.Directive{
|
||||
Name: "error_page",
|
||||
Parameters: []string{"404", "=", "@notfound"},
|
||||
},
|
||||
&components.Directive{
|
||||
Name: "location",
|
||||
Parameters: []string{"@notfound"},
|
||||
Block: &components.Block{
|
||||
Directives: []components.IDirective{
|
||||
&components.Directive{
|
||||
Name: "return",
|
||||
Parameters: []string{"301", target},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
config.FilePath = includePath
|
||||
config.Block = block
|
||||
|
||||
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
||||
return buserr.WithErr(constant.ErrUpdateBuWebsite, err)
|
||||
}
|
||||
|
||||
nginxInclude := fmt.Sprintf("/www/sites/%s/redirect/*.conf", website.Alias)
|
||||
if err = updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w WebsiteService) GetRedirect(id uint) (res []response.NginxRedirectConfig, err error) {
|
||||
var (
|
||||
website model.Website
|
||||
nginxInstall model.AppInstall
|
||||
fileList response.FileInfo
|
||||
)
|
||||
website, err = websiteRepo.GetFirst(commonRepo.WithByID(id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nginxInstall, err = getAppInstallByKey(constant.AppOpenresty)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "redirect")
|
||||
fileOp := files.NewFileOp()
|
||||
if !fileOp.Stat(includeDir) {
|
||||
return
|
||||
}
|
||||
fileList, err = NewIFileService().GetFileList(request.FileOption{FileOption: files.FileOption{Path: includeDir, Expand: true, Page: 1, PageSize: 100}})
|
||||
if len(fileList.Items) == 0 {
|
||||
return
|
||||
}
|
||||
var (
|
||||
content []byte
|
||||
config *components.Config
|
||||
)
|
||||
for _, configFile := range fileList.Items {
|
||||
redirectConfig := response.NginxRedirectConfig{
|
||||
WebsiteID: website.ID,
|
||||
}
|
||||
parts := strings.Split(configFile.Name, ".")
|
||||
redirectConfig.Name = parts[0]
|
||||
if parts[1] == "conf" {
|
||||
redirectConfig.Enable = true
|
||||
} else {
|
||||
redirectConfig.Enable = false
|
||||
}
|
||||
redirectConfig.FilePath = configFile.Path
|
||||
content, err = fileOp.GetContent(configFile.Path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
redirectConfig.Content = string(content)
|
||||
config = parser.NewStringParser(string(content)).Parse()
|
||||
|
||||
dirs := config.GetDirectives()
|
||||
if len(dirs) > 0 {
|
||||
firstName := dirs[0].GetName()
|
||||
switch firstName {
|
||||
case "if":
|
||||
for _, ifDir := range dirs {
|
||||
params := ifDir.GetParameters()
|
||||
if len(params) > 2 && params[0] == "($host" {
|
||||
domain := strings.Trim(strings.Trim(params[2], "'"), "^")
|
||||
redirectConfig.Domains = append(redirectConfig.Domains, domain)
|
||||
if len(redirectConfig.Domains) > 1 {
|
||||
continue
|
||||
}
|
||||
redirectConfig.Type = "domain"
|
||||
}
|
||||
childDirs := ifDir.GetBlock().GetDirectives()
|
||||
for _, dir := range childDirs {
|
||||
if dir.GetName() == "return" {
|
||||
dirParams := dir.GetParameters()
|
||||
if len(dirParams) > 1 {
|
||||
redirectConfig.Redirect = dirParams[0]
|
||||
if strings.HasSuffix(dirParams[1], "$request_uri") {
|
||||
redirectConfig.KeepPath = true
|
||||
redirectConfig.Target = strings.TrimSuffix(dirParams[1], "$request_uri")
|
||||
} else {
|
||||
redirectConfig.KeepPath = false
|
||||
redirectConfig.Target = dirParams[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case "rewrite":
|
||||
redirectConfig.Type = "path"
|
||||
for _, pathDir := range dirs {
|
||||
if pathDir.GetName() == "rewrite" {
|
||||
params := pathDir.GetParameters()
|
||||
if len(params) > 2 {
|
||||
redirectConfig.Path = strings.Trim(strings.Trim(params[0], "^"), "(.*)")
|
||||
if strings.HasSuffix(params[1], "$1") {
|
||||
redirectConfig.KeepPath = true
|
||||
redirectConfig.Target = strings.TrimSuffix(params[1], "$1")
|
||||
} else {
|
||||
redirectConfig.KeepPath = false
|
||||
redirectConfig.Target = params[1]
|
||||
}
|
||||
if params[2] == "permanent" {
|
||||
redirectConfig.Redirect = "301"
|
||||
} else {
|
||||
redirectConfig.Redirect = "302"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case "error_page":
|
||||
redirectConfig.Type = "404"
|
||||
for _, errDir := range dirs {
|
||||
if errDir.GetName() == "location" {
|
||||
childDirs := errDir.GetBlock().GetDirectives()
|
||||
for _, dir := range childDirs {
|
||||
if dir.GetName() == "return" {
|
||||
dirParams := dir.GetParameters()
|
||||
if len(dirParams) > 1 {
|
||||
redirectConfig.Redirect = dirParams[0]
|
||||
if strings.HasSuffix(dirParams[1], "$request_uri") {
|
||||
redirectConfig.KeepPath = true
|
||||
redirectConfig.Target = strings.TrimSuffix(dirParams[1], "$request_uri")
|
||||
} else {
|
||||
redirectConfig.KeepPath = false
|
||||
redirectConfig.Target = dirParams[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
res = append(res, redirectConfig)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w WebsiteService) UpdateRedirectFile(req request.NginxRedirectUpdate) (err error) {
|
||||
var (
|
||||
website model.Website
|
||||
nginxFull dto.NginxFull
|
||||
oldRewriteContent []byte
|
||||
)
|
||||
website, err = websiteRepo.GetFirst(commonRepo.WithByID(req.WebsiteID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nginxFull, err = getNginxFull(&website)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
includePath := fmt.Sprintf("/www/sites/%s/redirect/%s.conf", website.Alias, req.Name)
|
||||
absolutePath := path.Join(nginxFull.Install.GetPath(), includePath)
|
||||
fileOp := files.NewFileOp()
|
||||
oldRewriteContent, err = fileOp.GetContent(absolutePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), 0755)
|
||||
}
|
||||
}()
|
||||
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
||||
}
|
||||
|
|
|
@ -61,5 +61,9 @@ func (a *WebsiteRouter) InitWebsiteRouter(Router *gin.RouterGroup) {
|
|||
|
||||
groupRouter.POST("/leech", baseApi.GetAntiLeech)
|
||||
groupRouter.POST("/leech/update", baseApi.UpdateAntiLeech)
|
||||
|
||||
groupRouter.POST("/redirect/update", baseApi.UpdateRedirectConfig)
|
||||
groupRouter.POST("/redirect", baseApi.GetRedirectConfig)
|
||||
groupRouter.POST("/redirect/file", baseApi.UpdateRedirectConfigFile)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9997,6 +9997,141 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/websites/redirect": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取重定向配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Get proxy conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteProxyReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/redirect/file": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新重定向文件",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Update redirect file",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.NginxRedirectUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_column": "id",
|
||||
"input_value": "websiteID",
|
||||
"isList": false,
|
||||
"output_column": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"websiteID"
|
||||
],
|
||||
"formatEN": "Nginx conf redirect file update [domain]",
|
||||
"formatZH": "更新重定向文件 [domain]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/redirect/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "修改重定向配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Update redirect conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.NginxRedirectReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_column": "id",
|
||||
"input_value": "websiteID",
|
||||
"isList": false,
|
||||
"output_column": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"websiteID"
|
||||
],
|
||||
"formatEN": "Update domain [domain] redirect config",
|
||||
"formatZH": "修改网站 [domain] 重定向理配置 ",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/rewrite": {
|
||||
"post": {
|
||||
"security": [
|
||||
|
@ -14494,6 +14629,76 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"request.NginxRedirectReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable",
|
||||
"keepPath",
|
||||
"name",
|
||||
"operate",
|
||||
"redirect",
|
||||
"target",
|
||||
"type",
|
||||
"websiteID"
|
||||
],
|
||||
"properties": {
|
||||
"domains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"keepPath": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"operate": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"redirect": {
|
||||
"type": "string"
|
||||
},
|
||||
"redirectRoot": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"target": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteID": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxRedirectUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content",
|
||||
"name",
|
||||
"websiteID"
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteID": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxRewriteReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
@ -9990,6 +9990,141 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/websites/redirect": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "获取重定向配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Get proxy conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.WebsiteProxyReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/redirect/file": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "更新重定向文件",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Update redirect file",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.NginxRedirectUpdate"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_column": "id",
|
||||
"input_value": "websiteID",
|
||||
"isList": false,
|
||||
"output_column": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"websiteID"
|
||||
],
|
||||
"formatEN": "Nginx conf redirect file update [domain]",
|
||||
"formatZH": "更新重定向文件 [domain]",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/redirect/update": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"ApiKeyAuth": []
|
||||
}
|
||||
],
|
||||
"description": "修改重定向配置",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Website"
|
||||
],
|
||||
"summary": "Update redirect conf",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "request",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/request.NginxRedirectReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
},
|
||||
"x-panel-log": {
|
||||
"BeforeFuntions": [
|
||||
{
|
||||
"db": "websites",
|
||||
"input_column": "id",
|
||||
"input_value": "websiteID",
|
||||
"isList": false,
|
||||
"output_column": "primary_domain",
|
||||
"output_value": "domain"
|
||||
}
|
||||
],
|
||||
"bodyKeys": [
|
||||
"websiteID"
|
||||
],
|
||||
"formatEN": "Update domain [domain] redirect config",
|
||||
"formatZH": "修改网站 [domain] 重定向理配置 ",
|
||||
"paramKeys": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"/websites/rewrite": {
|
||||
"post": {
|
||||
"security": [
|
||||
|
@ -14487,6 +14622,76 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"request.NginxRedirectReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable",
|
||||
"keepPath",
|
||||
"name",
|
||||
"operate",
|
||||
"redirect",
|
||||
"target",
|
||||
"type",
|
||||
"websiteID"
|
||||
],
|
||||
"properties": {
|
||||
"domains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"keepPath": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"operate": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"redirect": {
|
||||
"type": "string"
|
||||
},
|
||||
"redirectRoot": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"target": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteID": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxRedirectUpdate": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content",
|
||||
"name",
|
||||
"websiteID"
|
||||
],
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteID": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"request.NginxRewriteReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
@ -2618,6 +2618,55 @@ definitions:
|
|||
- name
|
||||
- websiteID
|
||||
type: object
|
||||
request.NginxRedirectReq:
|
||||
properties:
|
||||
domains:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
enable:
|
||||
type: boolean
|
||||
keepPath:
|
||||
type: boolean
|
||||
name:
|
||||
type: string
|
||||
operate:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
redirect:
|
||||
type: string
|
||||
redirectRoot:
|
||||
type: boolean
|
||||
target:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
websiteID:
|
||||
type: integer
|
||||
required:
|
||||
- enable
|
||||
- keepPath
|
||||
- name
|
||||
- operate
|
||||
- redirect
|
||||
- target
|
||||
- type
|
||||
- websiteID
|
||||
type: object
|
||||
request.NginxRedirectUpdate:
|
||||
properties:
|
||||
content:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
websiteID:
|
||||
type: integer
|
||||
required:
|
||||
- content
|
||||
- name
|
||||
- websiteID
|
||||
type: object
|
||||
request.NginxRewriteReq:
|
||||
properties:
|
||||
name:
|
||||
|
@ -9911,6 +9960,92 @@ paths:
|
|||
formatEN: Nginx conf proxy file update [domain]
|
||||
formatZH: 更新反向代理文件 [domain]
|
||||
paramKeys: []
|
||||
/websites/redirect:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 获取重定向配置
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.WebsiteProxyReq'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Get proxy conf
|
||||
tags:
|
||||
- Website
|
||||
/websites/redirect/file:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 更新重定向文件
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.NginxRedirectUpdate'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update redirect file
|
||||
tags:
|
||||
- Website
|
||||
x-panel-log:
|
||||
BeforeFuntions:
|
||||
- db: websites
|
||||
input_column: id
|
||||
input_value: websiteID
|
||||
isList: false
|
||||
output_column: primary_domain
|
||||
output_value: domain
|
||||
bodyKeys:
|
||||
- websiteID
|
||||
formatEN: Nginx conf redirect file update [domain]
|
||||
formatZH: 更新重定向文件 [domain]
|
||||
paramKeys: []
|
||||
/websites/redirect/update:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 修改重定向配置
|
||||
parameters:
|
||||
- description: request
|
||||
in: body
|
||||
name: request
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/request.NginxRedirectReq'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
summary: Update redirect conf
|
||||
tags:
|
||||
- Website
|
||||
x-panel-log:
|
||||
BeforeFuntions:
|
||||
- db: websites
|
||||
input_column: id
|
||||
input_value: websiteID
|
||||
isList: false
|
||||
output_column: primary_domain
|
||||
output_value: domain
|
||||
bodyKeys:
|
||||
- websiteID
|
||||
formatEN: Update domain [domain] redirect config
|
||||
formatZH: '修改网站 [domain] 重定向理配置 '
|
||||
paramKeys: []
|
||||
/websites/rewrite:
|
||||
post:
|
||||
consumes:
|
||||
|
|
|
@ -389,4 +389,30 @@ export namespace Website {
|
|||
export interface LeechReq {
|
||||
websiteID: number;
|
||||
}
|
||||
|
||||
export interface WebsiteReq {
|
||||
websiteID: number;
|
||||
}
|
||||
|
||||
export interface RedirectConfig {
|
||||
operate: string;
|
||||
websiteID: number;
|
||||
domains?: string[];
|
||||
enable: boolean;
|
||||
name: string;
|
||||
keepPath: boolean;
|
||||
type: string;
|
||||
redirect: string;
|
||||
path?: string;
|
||||
target: string;
|
||||
redirectRoot?: boolean;
|
||||
filePath?: string;
|
||||
content?: string;
|
||||
}
|
||||
|
||||
export interface RedirectFileUpdate {
|
||||
websiteID: number;
|
||||
name: string;
|
||||
content: string;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -214,3 +214,15 @@ export const GetAntiLeech = (req: Website.LeechReq) => {
|
|||
export const UpdateAntiLeech = (req: Website.LeechConfig) => {
|
||||
return http.post<any>(`/websites/leech/update`, req);
|
||||
};
|
||||
|
||||
export const GetRedirectConfig = (req: Website.WebsiteReq) => {
|
||||
return http.post<Website.RedirectConfig[]>(`/websites/redirect`, req);
|
||||
};
|
||||
|
||||
export const OperateRedirectConfig = (req: Website.WebsiteReq) => {
|
||||
return http.post<any>(`/websites/redirect/update`, req);
|
||||
};
|
||||
|
||||
export const UpdateRedirectConfigFile = (req: Website.RedirectFileUpdate) => {
|
||||
return http.post<any>(`/websites/redirect/file`, req);
|
||||
};
|
||||
|
|
|
@ -1438,6 +1438,17 @@ const message = {
|
|||
privateKeyPath: 'Private key file',
|
||||
certificatePath: 'Certificate file',
|
||||
ipWhiteListHelper: 'The role of IP whitelist: all rules are invalid for IP whitelist',
|
||||
redirect: 'redirect',
|
||||
sourceDomain: 'source domain name/path',
|
||||
targetURL: 'Target URL address',
|
||||
keepPath: 'Keep URI parameters',
|
||||
path: 'path',
|
||||
redirectType: 'redirection type',
|
||||
redirectWay: 'way',
|
||||
keep: 'keep',
|
||||
notKeep: 'Do not keep',
|
||||
redirectRoot: 'Redirect to the homepage',
|
||||
redirectHelper: '301 permanent redirection, 302 temporary redirection',
|
||||
},
|
||||
php: {
|
||||
short_open_tag: 'Short tag support',
|
||||
|
|
|
@ -1373,6 +1373,17 @@ const message = {
|
|||
privateKeyPath: '私鑰文件',
|
||||
certificatePath: '證書文件',
|
||||
ipWhiteListHelper: 'IP白名單的作用:所有規則對IP白名單無效',
|
||||
redirect: '重定向',
|
||||
sourceDomain: '源域名/路徑',
|
||||
targetURL: '目標URL地址',
|
||||
keepPath: '保留URI參數',
|
||||
path: '路徑',
|
||||
redirectType: '重定向類型',
|
||||
redirectWay: '方式',
|
||||
keep: '保留',
|
||||
notKeep: '不保留',
|
||||
redirectRoot: '重定向到首頁',
|
||||
redirectHelper: '301永久重定向,302臨時重定向',
|
||||
},
|
||||
php: {
|
||||
short_open_tag: '短標簽支持',
|
||||
|
|
|
@ -1373,6 +1373,17 @@ const message = {
|
|||
privateKeyPath: '私钥文件',
|
||||
certificatePath: '证书文件',
|
||||
ipWhiteListHelper: 'IP 白名单的作用:所有规则对IP白名单无效',
|
||||
redirect: '重定向',
|
||||
sourceDomain: '源域名/路径',
|
||||
targetURL: '目标URL地址',
|
||||
keepPath: '保留URI参数',
|
||||
path: '路径',
|
||||
redirectType: '重定向类型',
|
||||
redirectWay: '方式',
|
||||
keep: '保留',
|
||||
notKeep: '不保留',
|
||||
redirectRoot: '重定向到首页',
|
||||
redirectHelper: '301永久重定向,302临时重定向',
|
||||
},
|
||||
php: {
|
||||
short_open_tag: '短标签支持',
|
||||
|
|
|
@ -27,8 +27,11 @@
|
|||
<el-tab-pane :label="$t('website.antiLeech')">
|
||||
<AntiLeech :id="id" v-if="tabIndex == '8'"></AntiLeech>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.redirect')">
|
||||
<Redirect :id="id" v-if="tabIndex == '9'"></Redirect>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('website.other')">
|
||||
<Other :id="id" v-if="tabIndex == '9'"></Other>
|
||||
<Other :id="id" v-if="tabIndex == '10'"></Other>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
@ -46,6 +49,7 @@ import Rewrite from './rewrite/index.vue';
|
|||
import Proxy from './proxy/index.vue';
|
||||
import AuthBasic from './auth-basic/index.vue';
|
||||
import AntiLeech from './anti-Leech/index.vue';
|
||||
import Redirect from './redirect/index.vue';
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
<template>
|
||||
<el-drawer v-model="open" :close-on-click-modal="false" size="40%" :before-close="handleClose">
|
||||
<template #header>
|
||||
<DrawerHeader
|
||||
:header="$t('commons.button.' + redirect.operate) + $t('website.redirect')"
|
||||
:back="handleClose"
|
||||
/>
|
||||
</template>
|
||||
<el-row v-loading="loading">
|
||||
<el-col :span="22" :offset="1">
|
||||
<el-form ref="redirectForm" label-position="top" :model="redirect" :rules="rules">
|
||||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||
<el-input
|
||||
v-model.trim="redirect.name"
|
||||
:disabled="redirect.operate === 'edit' || redirect.type == '404'"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.table.type')" prop="type">
|
||||
<el-select
|
||||
v-model="redirect.type"
|
||||
@change="changeType(redirect.type)"
|
||||
:disabled="redirect.operate === 'edit'"
|
||||
>
|
||||
<el-option :label="$t('website.domain')" :value="'domain'"></el-option>
|
||||
<el-option :label="$t('website.path')" :value="'path'"></el-option>
|
||||
<el-option :label="'404'" :value="'404'"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.redirectWay')" prop="redirect">
|
||||
<el-select v-model="redirect.redirect">
|
||||
<el-option :label="'301'" :value="'301'"></el-option>
|
||||
<el-option :label="'302'" :value="'302'"></el-option>
|
||||
</el-select>
|
||||
<span class="input-help">
|
||||
{{ $t('website.redirectHelper') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.path')" prop="path" v-if="redirect.type == 'path'">
|
||||
<el-input v-model.trim="redirect.path"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.domain')" prop="domains" v-if="redirect.type == 'domain'">
|
||||
<el-select v-model="redirect.domains" multiple>
|
||||
<el-option
|
||||
v-for="(item, index) in domains"
|
||||
:key="index"
|
||||
:value="item.domain"
|
||||
:label="item.domain"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.redirectRoot')" prop="redirectRoot" v-if="redirect.type == '404'">
|
||||
<el-switch v-model="redirect.redirectRoot"></el-switch>
|
||||
</el-form-item>
|
||||
<div v-if="!redirect.redirectRoot">
|
||||
<el-form-item :label="$t('website.targetURL')" prop="target">
|
||||
<el-input v-model.trim="redirect.target"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('website.keepPath')" prop="keepPath">
|
||||
<el-switch v-model="redirect.keepPath"></el-switch>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit(redirectForm)" :disabled="loading">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { ListDomains, OperateRedirectConfig, GetRedirectConfig } from '@/api/modules/website';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { ref } from 'vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { Website } from '@/api/interface/website';
|
||||
|
||||
const redirectForm = ref<FormInstance>();
|
||||
const rules = ref({
|
||||
name: [Rules.requiredInput, Rules.appName],
|
||||
type: [Rules.requiredSelect],
|
||||
redirect: [Rules.requiredSelect],
|
||||
domains: [Rules.requiredSelect],
|
||||
target: [Rules.requiredInput],
|
||||
path: [Rules.requiredInput],
|
||||
});
|
||||
const open = ref(false);
|
||||
const loading = ref(false);
|
||||
|
||||
const initData = (): Website.RedirectConfig => ({
|
||||
websiteID: 0,
|
||||
operate: 'create',
|
||||
enable: true,
|
||||
name: '',
|
||||
domains: [],
|
||||
keepPath: true,
|
||||
type: 'domain',
|
||||
redirect: '301',
|
||||
target: 'http://',
|
||||
redirectRoot: false,
|
||||
});
|
||||
let redirect = ref(initData());
|
||||
const em = defineEmits(['close']);
|
||||
const handleClose = () => {
|
||||
redirectForm.value?.resetFields();
|
||||
open.value = false;
|
||||
em('close', false);
|
||||
};
|
||||
const domains = ref([]);
|
||||
|
||||
const acceptParams = (redirectParam: Website.RedirectConfig) => {
|
||||
if (redirectParam.operate == 'edit') {
|
||||
redirect.value = redirectParam;
|
||||
} else {
|
||||
redirect.value = initData();
|
||||
redirect.value.websiteID = redirectParam.websiteID;
|
||||
}
|
||||
domains.value = [];
|
||||
getDomains();
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const changeType = (type: string) => {
|
||||
if (type != '404') {
|
||||
redirect.value.redirectRoot = false;
|
||||
} else {
|
||||
redirect.value.name = '404';
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
OperateRedirectConfig(redirect.value)
|
||||
.then(() => {
|
||||
if (redirect.value.operate == 'create') {
|
||||
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
|
||||
} else {
|
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||
}
|
||||
handleClose();
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getDomains = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await GetRedirectConfig({ websiteID: redirect.value.websiteID });
|
||||
let oldDomains = [];
|
||||
if (res.data) {
|
||||
for (const old of res.data) {
|
||||
if (old.type == 'domain') {
|
||||
oldDomains = oldDomains.concat(old.domains);
|
||||
}
|
||||
}
|
||||
}
|
||||
ListDomains(redirect.value.websiteID)
|
||||
.then((domainRes) => {
|
||||
if (domainRes.data) {
|
||||
if (oldDomains.length > 0) {
|
||||
for (const data of domainRes.data) {
|
||||
if (oldDomains.indexOf(data.domain) > -1) {
|
||||
continue;
|
||||
}
|
||||
domains.value.push(data);
|
||||
}
|
||||
} else {
|
||||
domains.value = domainRes.data || [];
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {});
|
||||
} catch (error) {
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,93 @@
|
|||
<template>
|
||||
<el-drawer v-model="open" :close-on-click-modal="false" size="40%" :before-close="handleClose">
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('website.proxyFile')" :back="handleClose" />
|
||||
</template>
|
||||
<el-row v-loading="loading">
|
||||
<el-col :span="22" :offset="1">
|
||||
<div class="redirect-editor">
|
||||
<codemirror
|
||||
:autofocus="true"
|
||||
placeholder=""
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
style="height: 600px"
|
||||
theme="cobalt"
|
||||
:styleActiveLine="true"
|
||||
:extensions="extensions"
|
||||
v-model="req.content"
|
||||
/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit()" :disabled="loading">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import i18n from '@/lang';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { UpdateRedirectConfigFile } from '@/api/modules/website';
|
||||
import { StreamLanguage } from '@codemirror/language';
|
||||
import { nginx } from '@codemirror/legacy-modes/mode/nginx';
|
||||
import { oneDark } from '@codemirror/theme-one-dark';
|
||||
|
||||
const extensions = [StreamLanguage.define(nginx), oneDark];
|
||||
const proxyForm = ref<FormInstance>();
|
||||
const open = ref(false);
|
||||
const loading = ref(false);
|
||||
const em = defineEmits(['close']);
|
||||
const handleClose = () => {
|
||||
proxyForm.value?.resetFields();
|
||||
open.value = false;
|
||||
em('close', false);
|
||||
};
|
||||
const req = reactive({
|
||||
name: '',
|
||||
websiteID: 0,
|
||||
content: '',
|
||||
});
|
||||
|
||||
const acceptParams = async (proxyreq: any) => {
|
||||
req.name = proxyreq.name;
|
||||
req.websiteID = proxyreq.websiteID;
|
||||
req.content = proxyreq.content;
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
loading.value = true;
|
||||
UpdateRedirectConfigFile(req)
|
||||
.then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||
handleClose();
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.redirect-editor {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,194 @@
|
|||
<template>
|
||||
<ComplexTable :data="data" @search="search" v-loading="loading">
|
||||
<template #toolbar>
|
||||
<el-button type="primary" plain @click="openCreate">
|
||||
{{ $t('commons.button.create') + $t('website.redirect') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<el-table-column :label="$t('commons.table.name')" prop="name"></el-table-column>
|
||||
<el-table-column :label="$t('website.sourceDomain')" prop="domain" min-width="150px">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.type === 'domain'">{{ row.domains.join(',') }}</span>
|
||||
<span v-else>{{ row.path }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.type')" prop="type" width="80px">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.type != 404">{{ $t('website.' + row.type) }}</span>
|
||||
<span v-else>{{ 404 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('website.redirectWay')" prop="redirect" width="80px"></el-table-column>
|
||||
<el-table-column :label="$t('website.targetURL')" prop="target"></el-table-column>
|
||||
<el-table-column :label="$t('website.keepPath')" prop="keepPath">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.type != '404'">{{ row.keepPath ? $t('website.keep') : $t('website.notKeep') }}</span>
|
||||
<span v-else></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.status')" prop="enable">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row.enable" link type="success" :icon="VideoPlay" @click="opProxy(row)">
|
||||
{{ $t('commons.status.running') }}
|
||||
</el-button>
|
||||
<el-button v-else link type="danger" :icon="VideoPause" @click="opProxy(row)">
|
||||
{{ $t('commons.status.stopped') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<fu-table-operations
|
||||
:ellipsis="10"
|
||||
width="260px"
|
||||
:buttons="buttons"
|
||||
:label="$t('commons.table.operate')"
|
||||
:fixed="mobile ? false : 'right'"
|
||||
fix
|
||||
/>
|
||||
</ComplexTable>
|
||||
<Create ref="createRef" @close="search()" />
|
||||
<File ref="fileRef" @close="search()" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="proxy">
|
||||
import { Website } from '@/api/interface/website';
|
||||
import { OperateRedirectConfig, GetRedirectConfig } from '@/api/modules/website';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import Create from './create/index.vue';
|
||||
import File from './file/index.vue';
|
||||
import { VideoPlay, VideoPause } from '@element-plus/icons-vue';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { GlobalStore } from '@/store';
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const mobile = computed(() => {
|
||||
return globalStore.isMobile();
|
||||
});
|
||||
const id = computed(() => {
|
||||
return props.id;
|
||||
});
|
||||
const loading = ref(false);
|
||||
const data = ref();
|
||||
const createRef = ref();
|
||||
const fileRef = ref();
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('website.proxyFile'),
|
||||
click: function (row: Website.RedirectConfig) {
|
||||
openEditFile(row);
|
||||
},
|
||||
disabled: (row: Website.RedirectConfig) => {
|
||||
return !row.enable;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.edit'),
|
||||
click: function (row: Website.RedirectConfig) {
|
||||
openEdit(row);
|
||||
},
|
||||
disabled: (row: Website.ProxyConfig) => {
|
||||
return !row.enable;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
click: function (row: Website.RedirectConfig) {
|
||||
deleteProxy(row);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const initData = (id: number): Website.RedirectConfig => ({
|
||||
websiteID: id,
|
||||
operate: 'create',
|
||||
enable: true,
|
||||
name: '',
|
||||
domains: [],
|
||||
keepPath: true,
|
||||
type: '',
|
||||
redirect: '',
|
||||
target: '',
|
||||
});
|
||||
|
||||
const openCreate = () => {
|
||||
createRef.value.acceptParams(initData(id.value));
|
||||
};
|
||||
|
||||
const openEdit = (proxyConfig: Website.RedirectConfig) => {
|
||||
let proxy = JSON.parse(JSON.stringify(proxyConfig));
|
||||
proxy.operate = 'edit';
|
||||
createRef.value.acceptParams(proxy);
|
||||
};
|
||||
|
||||
const openEditFile = (proxyConfig: Website.RedirectConfig) => {
|
||||
fileRef.value.acceptParams({
|
||||
name: proxyConfig.name,
|
||||
content: proxyConfig.content,
|
||||
websiteID: proxyConfig.websiteID,
|
||||
});
|
||||
};
|
||||
|
||||
const deleteProxy = async (redirectConfig: Website.RedirectConfig) => {
|
||||
redirectConfig.operate = 'delete';
|
||||
await useDeleteData(OperateRedirectConfig, redirectConfig, 'commons.msg.delete');
|
||||
search();
|
||||
};
|
||||
|
||||
const submit = async (redirectConfig: Website.RedirectConfig) => {
|
||||
loading.value = true;
|
||||
await OperateRedirectConfig(redirectConfig)
|
||||
.then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||
search();
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const opProxy = (redirectConfig: Website.RedirectConfig) => {
|
||||
let proxy = JSON.parse(JSON.stringify(redirectConfig));
|
||||
proxy.enable = !redirectConfig.enable;
|
||||
let message = '';
|
||||
if (proxy.enable) {
|
||||
proxy.operate = 'enable';
|
||||
message = i18n.global.t('commons.button.start') + i18n.global.t('website.redirect');
|
||||
} else {
|
||||
proxy.operate = 'disable';
|
||||
message = i18n.global.t('commons.button.stop') + i18n.global.t('website.redirect');
|
||||
}
|
||||
ElMessageBox.confirm(message, i18n.global.t('cronjob.changeStatus'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
})
|
||||
.then(async () => {
|
||||
await submit(proxy);
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await GetRedirectConfig({ websiteID: id.value });
|
||||
data.value = res.data || [];
|
||||
} catch (error) {
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
</script>
|
Loading…
Reference in New Issue