mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-24 08:20:27 +08:00
Refs https://github.com/1Panel-dev/1Panel/issues/5135 Refs https://github.com/1Panel-dev/1Panel/issues/6976
408 lines
12 KiB
Go
408 lines
12 KiB
Go
package service
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
|
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
|
"github.com/1Panel-dev/1Panel/agent/app/dto/response"
|
|
"github.com/1Panel-dev/1Panel/agent/app/model"
|
|
"github.com/1Panel-dev/1Panel/agent/app/repo"
|
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
|
"github.com/1Panel-dev/1Panel/agent/cmd/server/nginx_conf"
|
|
"github.com/1Panel-dev/1Panel/agent/constant"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/files"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/nginx"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/nginx/components"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/nginx/parser"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/re"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func (w WebsiteService) OperateProxy(req request.WebsiteProxyConfig) (err error) {
|
|
var (
|
|
website model.Website
|
|
par *parser.Parser
|
|
oldContent []byte
|
|
)
|
|
|
|
website, err = websiteRepo.GetFirst(repo.WithByID(req.ID))
|
|
if err != nil {
|
|
return
|
|
}
|
|
fileOp := files.NewFileOp()
|
|
includeDir := GetSitePath(website, SiteProxyDir)
|
|
if !fileOp.Stat(includeDir) {
|
|
_ = fileOp.CreateDir(includeDir, constant.DirPerm)
|
|
}
|
|
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("ErrNameIsExist")
|
|
return
|
|
}
|
|
|
|
defer func() {
|
|
if err != nil {
|
|
switch req.Operate {
|
|
case "create":
|
|
_ = fileOp.DeleteFile(includePath)
|
|
case "edit":
|
|
_ = fileOp.WriteFile(includePath, bytes.NewReader(oldContent), constant.DirPerm)
|
|
}
|
|
}
|
|
}()
|
|
|
|
var config *components.Config
|
|
|
|
switch req.Operate {
|
|
case "create":
|
|
config, err = parser.NewStringParser(string(nginx_conf.GetWebsiteFile("proxy.conf"))).Parse()
|
|
if err != nil {
|
|
return
|
|
}
|
|
case "edit":
|
|
par, err = parser.NewParser(includePath)
|
|
if err != nil {
|
|
return
|
|
}
|
|
config, err = par.Parse()
|
|
if err != nil {
|
|
return
|
|
}
|
|
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)
|
|
}
|
|
|
|
config.FilePath = includePath
|
|
directives := config.Directives
|
|
|
|
var location *components.Location
|
|
for _, directive := range directives {
|
|
if loc, ok := directive.(*components.Location); ok {
|
|
location = loc
|
|
break
|
|
}
|
|
}
|
|
if location == nil {
|
|
err = errors.New("invalid proxy config, no location found")
|
|
return
|
|
}
|
|
location.UpdateDirective("proxy_pass", []string{req.ProxyPass})
|
|
location.UpdateDirective("proxy_set_header", []string{"Host", req.ProxyHost})
|
|
location.ChangePath(req.Modifier, req.Match)
|
|
// Server Cache Settings
|
|
if req.Cache {
|
|
if err = openProxyCache(website); err != nil {
|
|
return
|
|
}
|
|
location.AddServerCache(fmt.Sprintf("proxy_cache_zone_of_%s", website.Alias), req.ServerCacheTime, req.ServerCacheUnit)
|
|
} else {
|
|
location.RemoveServerCache(fmt.Sprintf("proxy_cache_zone_of_%s", website.Alias))
|
|
}
|
|
// Browser Cache Settings
|
|
if req.CacheTime != 0 {
|
|
location.AddBrowserCache(req.CacheTime, req.CacheUnit)
|
|
} else {
|
|
location.RemoveBrowserCache()
|
|
}
|
|
// Content Replace Settings
|
|
if len(req.Replaces) > 0 {
|
|
location.AddSubFilter(req.Replaces)
|
|
} else {
|
|
location.RemoveSubFilter()
|
|
}
|
|
// SSL Settings
|
|
if req.SNI {
|
|
location.UpdateDirective("proxy_ssl_server_name", []string{"on"})
|
|
if req.ProxySSLName != "" {
|
|
location.UpdateDirective("proxy_ssl_name", []string{req.ProxySSLName})
|
|
}
|
|
} else {
|
|
location.UpdateDirective("proxy_ssl_server_name", []string{"off"})
|
|
}
|
|
// CORS Settings
|
|
if req.Cors {
|
|
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Origin", req.AllowOrigins, "always"})
|
|
if req.AllowMethods != "" {
|
|
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Methods", req.AllowMethods, "always"})
|
|
} else {
|
|
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Methods"})
|
|
}
|
|
if req.AllowHeaders != "" {
|
|
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Headers", req.AllowHeaders, "always"})
|
|
} else {
|
|
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Headers"})
|
|
}
|
|
if req.AllowCredentials {
|
|
location.UpdateDirective("add_header", []string{"Access-Control-Allow-Credentials", "true", "always"})
|
|
} else {
|
|
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Credentials"})
|
|
}
|
|
if req.Preflight {
|
|
location.AddCorsOption()
|
|
} else {
|
|
location.RemoveCorsOption()
|
|
}
|
|
} else {
|
|
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Origin"})
|
|
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Methods"})
|
|
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Headers"})
|
|
location.RemoveDirective("add_header", []string{"Access-Control-Allow-Credentials"})
|
|
location.RemoveDirectiveByFullParams("if", []string{"(", "$request_method", "=", "'OPTIONS'", ")"})
|
|
}
|
|
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
|
|
return buserr.WithErr("ErrUpdateBuWebsite", err)
|
|
}
|
|
nginxInclude := fmt.Sprintf("/www/sites/%s/proxy/*.conf", website.Alias)
|
|
return updateNginxConfig(constant.NginxScopeServer, []dto.NginxParam{{Name: "include", Params: []string{nginxInclude}}}, &website)
|
|
}
|
|
|
|
func (w WebsiteService) UpdateProxyCache(req request.NginxProxyCacheUpdate) (err error) {
|
|
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
|
|
if err != nil {
|
|
return
|
|
}
|
|
cacheDir := GetSitePath(website, SiteCacheDir)
|
|
fileOp := files.NewFileOp()
|
|
if !fileOp.Stat(cacheDir) {
|
|
_ = fileOp.CreateDir(cacheDir, constant.DirPerm)
|
|
}
|
|
if req.Open {
|
|
proxyCachePath := fmt.Sprintf("/www/sites/%s/cache levels=1:2 keys_zone=proxy_cache_zone_of_%s:%d%s max_size=%d%s inactive=%d%s", website.Alias, website.Alias, req.ShareCache, req.ShareCacheUnit, req.CacheLimit, req.CacheLimitUnit, req.CacheExpire, req.CacheExpireUnit)
|
|
return updateNginxConfig("", []dto.NginxParam{{Name: "proxy_cache_path", Params: []string{proxyCachePath}}}, &website)
|
|
}
|
|
return deleteNginxConfig("", []dto.NginxParam{{Name: "proxy_cache_path"}}, &website)
|
|
}
|
|
|
|
func (w WebsiteService) GetProxyCache(id uint) (res response.NginxProxyCache, err error) {
|
|
var (
|
|
website model.Website
|
|
)
|
|
website, err = websiteRepo.GetFirst(repo.WithByID(id))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
parser, err := parser.NewParser(GetSitePath(website, SiteConf))
|
|
if err != nil {
|
|
return
|
|
}
|
|
config, err := parser.Parse()
|
|
if err != nil {
|
|
return
|
|
}
|
|
var params []string
|
|
for _, d := range config.GetDirectives() {
|
|
if d.GetName() == "proxy_cache_path" {
|
|
params = d.GetParameters()
|
|
}
|
|
}
|
|
if len(params) == 0 {
|
|
return
|
|
}
|
|
for _, param := range params {
|
|
if re.GetRegex(re.ProxyCacheZonePattern).MatchString(param) {
|
|
matches := re.GetRegex(re.ProxyCacheZonePattern).FindStringSubmatch(param)
|
|
if len(matches) > 0 {
|
|
res.ShareCache, _ = strconv.Atoi(matches[1])
|
|
res.ShareCacheUnit = matches[2]
|
|
}
|
|
}
|
|
|
|
if re.GetRegex(re.ProxyCacheMaxSizeValidationPattern).MatchString(param) {
|
|
matches := re.GetRegex(re.ProxyCacheMaxSizePattern).FindStringSubmatch(param)
|
|
if len(matches) > 0 {
|
|
res.CacheLimit, _ = strconv.ParseFloat(matches[1], 64)
|
|
res.CacheLimitUnit = matches[2]
|
|
}
|
|
}
|
|
if re.GetRegex(re.ProxyCacheInactivePattern).MatchString(param) {
|
|
matches := re.GetRegex(re.ProxyCacheInactivePattern).FindStringSubmatch(param)
|
|
if len(matches) > 0 {
|
|
res.CacheExpire, _ = strconv.Atoi(matches[1])
|
|
res.CacheExpireUnit = matches[2]
|
|
}
|
|
}
|
|
}
|
|
res.Open = true
|
|
return
|
|
}
|
|
|
|
func (w WebsiteService) GetProxies(id uint) (res []request.WebsiteProxyConfig, err error) {
|
|
var (
|
|
website model.Website
|
|
fileList response.FileInfo
|
|
)
|
|
website, err = websiteRepo.GetFirst(repo.WithByID(id))
|
|
if err != nil {
|
|
return
|
|
}
|
|
includeDir := GetSitePath(website, SiteProxyDir)
|
|
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 {
|
|
proxyConfig := request.WebsiteProxyConfig{
|
|
ID: website.ID,
|
|
}
|
|
parts := strings.Split(configFile.Name, ".")
|
|
proxyConfig.Name = parts[0]
|
|
if parts[1] == "conf" {
|
|
proxyConfig.Enable = true
|
|
} else {
|
|
proxyConfig.Enable = false
|
|
}
|
|
proxyConfig.FilePath = configFile.Path
|
|
content, err = fileOp.GetContent(configFile.Path)
|
|
if err != nil {
|
|
return
|
|
}
|
|
proxyConfig.Content = string(content)
|
|
config, err = parser.NewStringParser(string(content)).Parse()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
directives := config.GetDirectives()
|
|
|
|
var location *components.Location
|
|
for _, directive := range directives {
|
|
if loc, ok := directive.(*components.Location); ok {
|
|
location = loc
|
|
break
|
|
}
|
|
}
|
|
if location == nil {
|
|
err = errors.New("invalid proxy config, no location found")
|
|
return
|
|
}
|
|
proxyConfig.ProxyPass = location.ProxyPass
|
|
proxyConfig.Cache = location.Cache
|
|
if location.CacheTime > 0 {
|
|
proxyConfig.CacheTime = location.CacheTime
|
|
proxyConfig.CacheUnit = location.CacheUint
|
|
}
|
|
if location.ServerCacheTime > 0 {
|
|
proxyConfig.ServerCacheTime = location.ServerCacheTime
|
|
proxyConfig.ServerCacheUnit = location.ServerCacheUint
|
|
}
|
|
proxyConfig.Match = location.Match
|
|
proxyConfig.Modifier = location.Modifier
|
|
proxyConfig.ProxyHost = location.Host
|
|
proxyConfig.Replaces = location.Replaces
|
|
for _, directive := range location.Directives {
|
|
if directive.GetName() == "proxy_ssl_server_name" {
|
|
proxyConfig.SNI = directive.GetParameters()[0] == "on"
|
|
}
|
|
if directive.GetName() == "proxy_ssl_name" && len(directive.GetParameters()) > 0 {
|
|
proxyConfig.ProxySSLName = directive.GetParameters()[0]
|
|
}
|
|
}
|
|
proxyConfig.Cors = location.Cors
|
|
proxyConfig.AllowCredentials = location.AllowCredentials
|
|
proxyConfig.AllowHeaders = location.AllowHeaders
|
|
proxyConfig.AllowOrigins = location.AllowOrigins
|
|
proxyConfig.AllowMethods = location.AllowMethods
|
|
proxyConfig.Preflight = location.Preflight
|
|
res = append(res, proxyConfig)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (w WebsiteService) UpdateProxyFile(req request.NginxProxyUpdate) (err error) {
|
|
var (
|
|
website model.Website
|
|
oldRewriteContent []byte
|
|
)
|
|
website, err = websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
absolutePath := fmt.Sprintf("%s/%s.conf", GetSitePath(website, SiteProxyDir), req.Name)
|
|
fileOp := files.NewFileOp()
|
|
oldRewriteContent, err = fileOp.GetContent(absolutePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = fileOp.WriteFile(absolutePath, strings.NewReader(req.Content), constant.DirPerm); err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
_ = fileOp.WriteFile(absolutePath, bytes.NewReader(oldRewriteContent), constant.DirPerm)
|
|
}
|
|
}()
|
|
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
|
}
|
|
|
|
func (w WebsiteService) ClearProxyCache(req request.NginxCommonReq) error {
|
|
website, err := websiteRepo.GetFirst(repo.WithByID(req.WebsiteID))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cacheDir := GetSitePath(website, SiteCacheDir)
|
|
fileOp := files.NewFileOp()
|
|
if fileOp.Stat(cacheDir) {
|
|
if err = fileOp.CleanDir(cacheDir); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err = opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w WebsiteService) DeleteProxy(req request.WebsiteProxyDel) (err error) {
|
|
fileOp := files.NewFileOp()
|
|
website, err := websiteRepo.GetFirst(repo.WithByID(req.ID))
|
|
if err != nil {
|
|
return
|
|
}
|
|
nginxInstall, err := getAppInstallByKey(constant.AppOpenresty)
|
|
if err != nil {
|
|
return
|
|
}
|
|
includeDir := path.Join(nginxInstall.GetPath(), "www", "sites", website.Alias, "proxy")
|
|
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)
|
|
_ = fileOp.DeleteFile(includePath)
|
|
_ = fileOp.DeleteFile(backPath)
|
|
return updateNginxConfig(constant.NginxScopeServer, nil, &website)
|
|
}
|