diff --git a/backend/app/api/v1/website.go b/backend/app/api/v1/website.go index c641b11c6..d8b7a1458 100644 --- a/backend/app/api/v1/website.go +++ b/backend/app/api/v1/website.go @@ -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) +} diff --git a/backend/app/dto/request/nginx.go b/backend/app/dto/request/nginx.go index 07c3e79bc..42f984a2f 100644 --- a/backend/app/dto/request/nginx.go +++ b/backend/app/dto/request/nginx.go @@ -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"` +} diff --git a/backend/app/dto/request/website.go b/backend/app/dto/request/website.go index 395d30736..c459d81fb 100644 --- a/backend/app/dto/request/website.go +++ b/backend/app/dto/request/website.go @@ -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"` +} diff --git a/backend/app/dto/response/nginx.go b/backend/app/dto/response/nginx.go index 230337743..7f349726f 100644 --- a/backend/app/dto/response/nginx.go +++ b/backend/app/dto/response/nginx.go @@ -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"` +} diff --git a/backend/app/service/website.go b/backend/app/service/website.go index ee9b86dbc..ce3608723 100644 --- a/backend/app/service/website.go +++ b/backend/app/service/website.go @@ -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) +} diff --git a/backend/router/ro_website.go b/backend/router/ro_website.go index 1fbba67a7..aae9e5a64 100644 --- a/backend/router/ro_website.go +++ b/backend/router/ro_website.go @@ -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) } } diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index a1898dab1..7ca210f48 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -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": [ diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index 0f7822bea..0fc92d62a 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -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": [ diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index 02772bd22..8cd9ccfb4 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -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: diff --git a/frontend/src/api/interface/website.ts b/frontend/src/api/interface/website.ts index ad5bbed30..f08f6a08a 100644 --- a/frontend/src/api/interface/website.ts +++ b/frontend/src/api/interface/website.ts @@ -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; + } } diff --git a/frontend/src/api/modules/website.ts b/frontend/src/api/modules/website.ts index c3ec2e729..338ad8927 100644 --- a/frontend/src/api/modules/website.ts +++ b/frontend/src/api/modules/website.ts @@ -214,3 +214,15 @@ export const GetAntiLeech = (req: Website.LeechReq) => { export const UpdateAntiLeech = (req: Website.LeechConfig) => { return http.post(`/websites/leech/update`, req); }; + +export const GetRedirectConfig = (req: Website.WebsiteReq) => { + return http.post(`/websites/redirect`, req); +}; + +export const OperateRedirectConfig = (req: Website.WebsiteReq) => { + return http.post(`/websites/redirect/update`, req); +}; + +export const UpdateRedirectConfigFile = (req: Website.RedirectFileUpdate) => { + return http.post(`/websites/redirect/file`, req); +}; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 6c0c11858..2bf9788b7 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -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', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index aac0541da..a364981bd 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -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: '短標簽支持', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 35c66af32..aecaaa148 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -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: '短标签支持', diff --git a/frontend/src/views/website/website/config/basic/index.vue b/frontend/src/views/website/website/config/basic/index.vue index f76836c6d..4f1a5b32d 100644 --- a/frontend/src/views/website/website/config/basic/index.vue +++ b/frontend/src/views/website/website/config/basic/index.vue @@ -27,8 +27,11 @@ + + + - + @@ -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: { diff --git a/frontend/src/views/website/website/config/basic/redirect/create/index.vue b/frontend/src/views/website/website/config/basic/redirect/create/index.vue new file mode 100644 index 000000000..1f4658b48 --- /dev/null +++ b/frontend/src/views/website/website/config/basic/redirect/create/index.vue @@ -0,0 +1,198 @@ + + + diff --git a/frontend/src/views/website/website/config/basic/redirect/file/index.vue b/frontend/src/views/website/website/config/basic/redirect/file/index.vue new file mode 100644 index 000000000..83e654605 --- /dev/null +++ b/frontend/src/views/website/website/config/basic/redirect/file/index.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/frontend/src/views/website/website/config/basic/redirect/index.vue b/frontend/src/views/website/website/config/basic/redirect/index.vue new file mode 100644 index 000000000..8720b20f4 --- /dev/null +++ b/frontend/src/views/website/website/config/basic/redirect/index.vue @@ -0,0 +1,194 @@ + + +