1Panel/backend/app/service/website_utils.go

505 lines
14 KiB
Go
Raw Normal View History

2022-10-28 17:04:57 +08:00
package service
import (
"fmt"
2022-11-03 18:02:07 +08:00
"github.com/1Panel-dev/1Panel/backend/app/dto"
2022-10-28 17:04:57 +08:00
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
2022-11-02 15:19:14 +08:00
"github.com/1Panel-dev/1Panel/backend/utils/files"
2022-10-28 17:04:57 +08:00
"github.com/1Panel-dev/1Panel/backend/utils/nginx"
2022-11-07 16:19:05 +08:00
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
2022-10-28 17:04:57 +08:00
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
"github.com/1Panel-dev/1Panel/cmd/server/nginx_conf"
"github.com/pkg/errors"
2022-11-02 15:19:14 +08:00
"gorm.io/gorm"
2022-11-03 17:06:48 +08:00
"os"
2022-10-28 17:04:57 +08:00
"path"
2022-11-16 10:31:35 +08:00
"reflect"
2022-10-28 17:04:57 +08:00
"strconv"
"strings"
)
func getDomain(domainStr string, websiteID uint) (model.WebSiteDomain, error) {
domain := model.WebSiteDomain{
WebSiteID: websiteID,
}
domainArray := strings.Split(domainStr, ":")
if len(domainArray) == 1 {
domain.Domain = domainArray[0]
2022-11-02 15:19:14 +08:00
domain.Port = 80
2022-10-28 17:04:57 +08:00
return domain, nil
}
if len(domainArray) > 1 {
domain.Domain = domainArray[0]
portStr := domainArray[1]
portN, err := strconv.Atoi(portStr)
if err != nil {
return model.WebSiteDomain{}, err
}
domain.Port = portN
return domain, nil
}
return model.WebSiteDomain{}, nil
}
2022-11-21 16:28:51 +08:00
func createStaticHtml(website *model.WebSite) error {
2022-11-30 21:40:05 +08:00
nginxInstall, err := getAppInstallByKey("nginx")
2022-10-28 17:04:57 +08:00
if err != nil {
return err
}
2022-11-30 21:40:05 +08:00
indexFolder := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "www", "sites", website.Alias)
2022-11-21 16:28:51 +08:00
indexPath := path.Join(indexFolder, "index.html")
indexContent := string(nginx_conf.Index)
fileOp := files.NewFileOp()
if !fileOp.Stat(indexFolder) {
if err := fileOp.CreateDir(indexFolder, 0755); err != nil {
return err
}
}
if !fileOp.Stat(indexPath) {
if err := fileOp.CreateFile(indexPath); err != nil {
return err
}
}
if err := fileOp.WriteFile(indexPath, strings.NewReader(indexContent), 0755); err != nil {
return err
}
return nil
}
2022-11-30 21:40:05 +08:00
func createWebsiteFolder(nginxInstall model.AppInstall, website *model.WebSite) error {
nginxFolder := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name)
siteFolder := path.Join(nginxFolder, "www", "sites", website.Alias)
fileOp := files.NewFileOp()
if !fileOp.Stat(siteFolder) {
if err := fileOp.CreateDir(siteFolder, 0755); err != nil {
return err
}
if err := fileOp.CreateDir(path.Join(siteFolder, "log"), 0755); err != nil {
return err
}
if err := fileOp.CreateFile(path.Join(siteFolder, "log", "access.log")); err != nil {
return err
}
if err := fileOp.CreateDir(path.Join(siteFolder, "waf", "rules"), 0755); err != nil {
return err
}
if err := fileOp.CreateDir(path.Join(siteFolder, "data"), 0755); err != nil {
return err
}
}
return fileOp.CopyDir(path.Join(nginxFolder, "www", "common", "waf", "rules"), path.Join(siteFolder, "waf", "rules"))
}
2022-11-21 16:28:51 +08:00
func configDefaultNginx(website *model.WebSite, domains []model.WebSiteDomain) error {
2022-11-30 21:40:05 +08:00
nginxInstall, err := getAppInstallByKey("nginx")
2022-11-21 16:28:51 +08:00
if err != nil {
return err
}
2022-11-30 21:40:05 +08:00
if err := createWebsiteFolder(nginxInstall, website); err != nil {
2022-10-28 17:04:57 +08:00
return err
}
nginxFileName := website.Alias + ".conf"
2022-10-28 17:04:57 +08:00
configPath := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "conf", "conf.d", nginxFileName)
nginxContent := string(nginx_conf.WebsiteDefault)
config := parser.NewStringParser(nginxContent).Parse()
servers := config.FindServers()
if len(servers) == 0 {
return errors.New("nginx config is not valid")
}
server := servers[0]
var serverNames []string
for _, domain := range domains {
serverNames = append(serverNames, domain.Domain)
2022-11-03 17:06:48 +08:00
server.UpdateListen(strconv.Itoa(domain.Port), false)
2022-10-28 17:04:57 +08:00
}
server.UpdateServerName(serverNames)
2022-11-30 21:40:05 +08:00
siteFolder := path.Join("/www", "sites", website.Alias)
commonFolder := path.Join("/www", "common")
server.UpdateDirective("access_log", []string{path.Join(siteFolder, "log", "access.log")})
server.UpdateDirective("access_by_lua_file", []string{path.Join(commonFolder, "waf", "access.lua")})
server.UpdateDirective("set", []string{"$RulePath", path.Join(siteFolder, "waf", "rules")})
server.UpdateDirective("set", []string{"$logdir", path.Join(siteFolder, "waf", "log")})
2022-11-21 16:28:51 +08:00
if website.Type == "deployment" {
appInstall, err := appInstallRepo.GetFirst(commonRepo.WithByID(website.AppInstallID))
if err != nil {
return err
}
proxy := fmt.Sprintf("http://127.0.0.1:%d", appInstall.HttpPort)
server.UpdateRootProxy([]string{proxy})
} else {
2022-11-30 21:40:05 +08:00
server.UpdateRoot(path.Join("/www/sites", website.Alias))
2022-11-21 16:28:51 +08:00
server.UpdateRootLocation()
}
2022-10-28 17:04:57 +08:00
config.FilePath = configPath
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
}
2022-11-30 21:40:05 +08:00
if err := opNginx(nginxInstall.ContainerName, constant.NginxCheck); err != nil {
2022-10-28 17:04:57 +08:00
return err
}
2022-11-30 21:40:05 +08:00
return opNginx(nginxInstall.ContainerName, constant.NginxReload)
2022-10-28 17:04:57 +08:00
}
func opNginx(containerName, operate string) error {
nginxCmd := fmt.Sprintf("docker exec -i %s %s", containerName, "nginx -s reload")
2022-11-30 21:40:05 +08:00
if operate == constant.NginxCheck {
2022-10-28 17:04:57 +08:00
nginxCmd = fmt.Sprintf("docker exec -i %s %s", containerName, "nginx -t")
}
2022-11-08 15:42:31 +08:00
if out, err := cmd.Exec(nginxCmd); err != nil {
return errors.New(out)
2022-10-28 17:04:57 +08:00
}
return nil
}
2022-11-02 15:19:14 +08:00
func delNginxConfig(website model.WebSite) error {
nginxApp, err := appRepo.GetFirst(appRepo.WithKey("nginx"))
if err != nil {
return err
}
nginxInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithAppId(nginxApp.ID))
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
return err
}
nginxFileName := website.Alias + ".conf"
2022-11-02 15:19:14 +08:00
configPath := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "conf", "conf.d", nginxFileName)
fileOp := files.NewFileOp()
if !fileOp.Stat(configPath) {
return nil
}
if err := fileOp.DeleteFile(configPath); err != nil {
return err
}
return opNginx(nginxInstall.ContainerName, "reload")
}
2022-11-03 17:06:48 +08:00
func nginxCheckAndReload(oldContent string, filePath string, containerName string) error {
2022-11-30 21:40:05 +08:00
if err := opNginx(containerName, constant.NginxCheck); err != nil {
2022-11-03 17:06:48 +08:00
_ = files.NewFileOp().WriteFile(filePath, strings.NewReader(oldContent), 0644)
return err
}
2022-11-30 21:40:05 +08:00
if err := opNginx(containerName, constant.NginxReload); err != nil {
2022-11-03 17:06:48 +08:00
_ = files.NewFileOp().WriteFile(filePath, strings.NewReader(oldContent), 0644)
return err
}
2022-11-02 15:19:14 +08:00
return nil
}
2022-11-03 17:06:48 +08:00
func getNginxConfig(alias string) (dto.NginxConfig, error) {
2022-11-03 18:02:07 +08:00
var nginxConfig dto.NginxConfig
2022-11-24 10:28:39 +08:00
nginxInstall, err := getAppInstallByKey("nginx")
2022-11-03 17:06:48 +08:00
if err != nil {
2022-11-03 18:02:07 +08:00
return nginxConfig, err
2022-11-03 17:06:48 +08:00
}
configPath := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "conf", "conf.d", alias+".conf")
2022-11-03 17:06:48 +08:00
content, err := os.ReadFile(configPath)
if err != nil {
2022-11-03 18:02:07 +08:00
return nginxConfig, err
2022-11-03 17:06:48 +08:00
}
config := parser.NewStringParser(string(content)).Parse()
2022-11-03 18:02:07 +08:00
config.FilePath = configPath
nginxConfig.Config = config
nginxConfig.OldContent = string(content)
nginxConfig.ContainerName = nginxInstall.ContainerName
nginxConfig.FilePath = configPath
return nginxConfig, nil
}
func addListenAndServerName(website model.WebSite, ports []int, domains []string) error {
nginxConfig, err := getNginxConfig(website.Alias)
2022-11-03 18:02:07 +08:00
if err != nil {
return nil
}
config := nginxConfig.Config
server := config.FindServers()[0]
for _, port := range ports {
server.AddListen(strconv.Itoa(port), false)
}
for _, domain := range domains {
server.AddServerName(domain)
}
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
}
return nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxConfig.ContainerName)
}
func deleteListenAndServerName(website model.WebSite, ports []int, domains []string) error {
nginxConfig, err := getNginxConfig(website.Alias)
2022-11-03 18:02:07 +08:00
if err != nil {
return nil
}
config := nginxConfig.Config
2022-11-03 17:06:48 +08:00
server := config.FindServers()[0]
for _, port := range ports {
server.DeleteListen(strconv.Itoa(port))
}
for _, domain := range domains {
server.DeleteServerName(domain)
}
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
}
2022-11-03 18:02:07 +08:00
return nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxConfig.ContainerName)
2022-11-03 17:06:48 +08:00
}
2022-11-07 16:19:05 +08:00
2022-11-08 15:42:31 +08:00
func getNginxConfigByKeys(website model.WebSite, keys []string) ([]dto.NginxParam, error) {
nginxConfig, err := getNginxConfig(website.Alias)
2022-11-07 16:19:05 +08:00
if err != nil {
return nil, err
}
config := nginxConfig.Config
server := config.FindServers()[0]
2022-11-08 15:42:31 +08:00
var res []dto.NginxParam
2022-11-07 16:19:05 +08:00
for _, key := range keys {
dirs := server.FindDirectives(key)
for _, dir := range dirs {
2022-11-08 15:42:31 +08:00
nginxParam := dto.NginxParam{
Name: dir.GetName(),
Params: dir.GetParameters(),
}
res = append(res, nginxParam)
2022-11-07 16:19:05 +08:00
}
}
return res, nil
}
2022-11-24 10:28:39 +08:00
func updateNginxConfig(website model.WebSite, params []dto.NginxParam, scope dto.NginxKey) error {
nginxConfig, err := getNginxConfig(website.Alias)
2022-11-07 16:19:05 +08:00
if err != nil {
return err
}
config := nginxConfig.Config
2022-11-08 15:42:31 +08:00
updateConfig(config, scope)
2022-11-07 16:19:05 +08:00
server := config.FindServers()[0]
2022-11-08 15:42:31 +08:00
for _, p := range params {
2022-11-30 21:40:05 +08:00
server.UpdateDirective(p.Name, p.Params)
2022-11-07 16:19:05 +08:00
}
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
}
return nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxConfig.ContainerName)
}
2022-11-24 10:28:39 +08:00
func updateConfig(config *components.Config, scope dto.NginxKey) {
2022-11-16 10:31:35 +08:00
newConfig := &components.Config{}
switch scope {
case dto.LimitConn:
newConfig = parser.NewStringParser(string(nginx_conf.Limit)).Parse()
}
if reflect.DeepEqual(newConfig, &components.Config{}) {
return
}
for _, dir := range newConfig.GetDirectives() {
2022-11-30 21:40:05 +08:00
config.UpdateDirective(dir.GetName(), dir.GetParameters())
2022-11-08 15:42:31 +08:00
}
}
2022-11-24 10:28:39 +08:00
func getNginxParamsFromStaticFile(scope dto.NginxKey) []dto.NginxParam {
2022-11-16 10:31:35 +08:00
var nginxParams []dto.NginxParam
newConfig := &components.Config{}
switch scope {
case dto.SSL:
newConfig = parser.NewStringParser(string(nginx_conf.SSL)).Parse()
}
for _, dir := range newConfig.GetDirectives() {
nginxParams = append(nginxParams, dto.NginxParam{
Name: dir.GetName(),
Params: dir.GetParameters(),
})
}
return nginxParams
}
2022-11-24 17:50:47 +08:00
func getKeysFromStaticFile(scope dto.NginxKey) []string {
var res []string
newConfig := &components.Config{}
switch scope {
case dto.SSL:
newConfig = parser.NewStringParser(string(nginx_conf.SSL)).Parse()
}
for _, dir := range newConfig.GetDirectives() {
res = append(res, dir.GetName())
}
return res
}
2022-11-30 21:40:05 +08:00
func deleteNginxConfig(website model.WebSite, scope string, keys []string) error {
nginxConfig, err := getNginxConfig(website.Alias)
2022-11-08 15:42:31 +08:00
if err != nil {
return err
}
config := nginxConfig.Config
2022-11-30 21:40:05 +08:00
if scope == constant.NginxScopeHttp {
http := config.FindHttp()
for _, key := range keys {
http.RemoveDirective(key, []string{})
}
}
if scope == constant.NginxScopeServer {
server := config.FindServers()[0]
for _, key := range keys {
server.RemoveDirective(key, []string{})
}
}
2022-11-08 15:42:31 +08:00
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
}
return nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxConfig.ContainerName)
}
2022-11-20 18:32:56 +08:00
func createPemFile(website model.WebSite, websiteSSL model.WebSiteSSL) error {
2022-11-16 10:31:35 +08:00
nginxApp, err := appRepo.GetFirst(appRepo.WithKey("nginx"))
if err != nil {
return err
}
nginxInstall, err := appInstallRepo.GetFirst(appInstallRepo.WithAppId(nginxApp.ID))
if err != nil {
return err
}
2022-11-20 18:32:56 +08:00
configDir := path.Join(constant.AppInstallDir, "nginx", nginxInstall.Name, "ssl", website.Alias)
2022-11-16 10:31:35 +08:00
fileOp := files.NewFileOp()
if !fileOp.Stat(configDir) {
if err := fileOp.CreateDir(configDir, 0775); err != nil {
return err
}
}
fullChainFile := path.Join(configDir, "fullchain.pem")
privatePemFile := path.Join(configDir, "privkey.pem")
if !fileOp.Stat(fullChainFile) {
if err := fileOp.CreateFile(fullChainFile); err != nil {
return err
}
}
if !fileOp.Stat(privatePemFile) {
if err := fileOp.CreateFile(privatePemFile); err != nil {
return err
}
}
if err := fileOp.WriteFile(fullChainFile, strings.NewReader(websiteSSL.Pem), 0644); err != nil {
return err
}
if err := fileOp.WriteFile(privatePemFile, strings.NewReader(websiteSSL.PrivateKey), 0644); err != nil {
return err
}
return nil
}
2022-11-20 18:32:56 +08:00
func applySSL(website model.WebSite, websiteSSL model.WebSiteSSL) error {
2022-11-24 17:50:47 +08:00
nginxConfig, err := getNginxConfig(website.Alias)
if err != nil {
return nil
}
config := nginxConfig.Config
server := config.FindServers()[0]
2022-11-28 15:11:39 +08:00
server.UpdateListen("443", false, "ssl")
2022-11-24 17:50:47 +08:00
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
}
2022-11-20 18:32:56 +08:00
if err := createPemFile(website, websiteSSL); err != nil {
return err
}
nginxParams := getNginxParamsFromStaticFile(dto.SSL)
for i, param := range nginxParams {
if param.Name == "ssl_certificate" {
nginxParams[i].Params = []string{path.Join("/etc/nginx/ssl", website.Alias, "fullchain.pem")}
}
if param.Name == "ssl_certificate_key" {
nginxParams[i].Params = []string{path.Join("/etc/nginx/ssl", website.Alias, "privkey.pem")}
}
}
if err := updateNginxConfig(website, nginxParams, dto.SSL); err != nil {
return err
}
return nil
}
2022-11-08 15:42:31 +08:00
func getParamArray(key string, param interface{}) []string {
2022-11-07 16:19:05 +08:00
var res []string
2022-11-18 18:02:14 +08:00
switch p := param.(type) {
2022-11-07 16:19:05 +08:00
case string:
if key == "index" {
2022-11-18 18:02:14 +08:00
res = strings.Split(p, "\n")
2022-11-08 15:42:31 +08:00
return res
2022-11-07 16:19:05 +08:00
}
2022-11-08 15:42:31 +08:00
2022-11-18 18:02:14 +08:00
res = strings.Split(p, " ")
2022-11-08 15:42:31 +08:00
return res
2022-11-07 16:19:05 +08:00
}
return res
}
2022-11-08 15:42:31 +08:00
func handleParamMap(paramMap map[string]string, keys []string) []dto.NginxParam {
var nginxParams []dto.NginxParam
for k, v := range paramMap {
for _, name := range keys {
if name == k {
param := dto.NginxParam{
Name: k,
Params: getParamArray(k, v),
}
nginxParams = append(nginxParams, param)
}
}
}
return nginxParams
}
func getNginxParams(params interface{}, keys []string) []dto.NginxParam {
var nginxParams []dto.NginxParam
2022-11-18 18:02:14 +08:00
switch p := params.(type) {
case map[string]interface{}:
2022-11-18 18:02:14 +08:00
return handleParamMap(toMapStr(p), keys)
2022-11-08 15:42:31 +08:00
case []interface{}:
2022-11-18 18:02:14 +08:00
for _, mA := range p {
if m, ok := mA.(map[string]interface{}); ok {
nginxParams = append(nginxParams, handleParamMap(toMapStr(m), keys)...)
2022-11-08 15:42:31 +08:00
}
}
}
return nginxParams
}
func toMapStr(m map[string]interface{}) map[string]string {
ret := make(map[string]string, len(m))
for k, v := range m {
ret[k] = fmt.Sprint(v)
}
return ret
}