mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-10-31 15:53:33 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			227 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			227 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package nginx
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 
 | |
| 	"npm/internal/config"
 | |
| 	"npm/internal/entity/certificate"
 | |
| 	"npm/internal/entity/host"
 | |
| 	"npm/internal/entity/upstream"
 | |
| 	"npm/internal/logger"
 | |
| 	"npm/internal/status"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	DeletedSuffix  = ".deleted"
 | |
| 	DisabledSuffix = ".disabled"
 | |
| 	ErrorSuffix    = ".error"
 | |
| )
 | |
| 
 | |
| // ConfigureHost will attempt to write nginx conf and reload nginx
 | |
| // When a host is disabled or deleted, it will name the file with a suffix
 | |
| // that won't be used by nginx.
 | |
| func ConfigureHost(h host.Model) error {
 | |
| 	// nolint: errcheck, gosec
 | |
| 	h.Expand([]string{"certificate", "nginxtemplate", "upstream"})
 | |
| 
 | |
| 	var certificateTemplate certificate.Template
 | |
| 	if h.Certificate != nil {
 | |
| 		certificateTemplate = h.Certificate.GetTemplate()
 | |
| 	}
 | |
| 
 | |
| 	var ups upstream.Model
 | |
| 	if h.Upstream != nil {
 | |
| 		ups = *h.Upstream
 | |
| 	}
 | |
| 
 | |
| 	data := TemplateData{
 | |
| 		Certificate: certificateTemplate,
 | |
| 		ConfDir:     fmt.Sprintf("%s/nginx/hosts", config.Configuration.DataFolder),
 | |
| 		Config: Config{ // todo
 | |
| 			Ipv4: true,
 | |
| 			Ipv6: false,
 | |
| 		},
 | |
| 		DataDir:  config.Configuration.DataFolder,
 | |
| 		Host:     h.GetTemplate(),
 | |
| 		Upstream: ups,
 | |
| 	}
 | |
| 
 | |
| 	removeHostFiles(h)
 | |
| 	filename := getHostFilename(h, "")
 | |
| 	if h.IsDeleted {
 | |
| 		filename = getHostFilename(h, DeletedSuffix)
 | |
| 	} else if h.IsDisabled {
 | |
| 		filename = getHostFilename(h, DisabledSuffix)
 | |
| 	}
 | |
| 
 | |
| 	// Write the config to disk
 | |
| 	err := writeTemplate(filename, h.NginxTemplate.Template, data, "")
 | |
| 	if err != nil {
 | |
| 		// this configuration failed somehow
 | |
| 		h.Status = status.StatusError
 | |
| 		h.ErrorMessage = fmt.Sprintf("Template generation failed: %s", err.Error())
 | |
| 		logger.Debug(h.ErrorMessage)
 | |
| 		return h.Save(true)
 | |
| 	}
 | |
| 
 | |
| 	// Reload Nginx and check for errors
 | |
| 	if output, err := reloadNginx(); err != nil {
 | |
| 		// reloading nginx failed, likely due to this host having a problem
 | |
| 		h.Status = status.StatusError
 | |
| 		h.ErrorMessage = fmt.Sprintf("Nginx configuation error: %s - %s", err.Error(), output)
 | |
| 
 | |
| 		// Write the .error file, if this isn't a deleted or disabled host
 | |
| 		// as the reload will only fail because of this host, if it's enabled
 | |
| 		if !h.IsDeleted && !h.IsDisabled {
 | |
| 			filename = getHostFilename(h, ErrorSuffix)
 | |
| 			// Clear existing file(s) again
 | |
| 			removeHostFiles(h)
 | |
| 			// Write the template again, but with an error message at the end of the file
 | |
| 			// nolint: errcheck, gosec
 | |
| 			writeTemplate(filename, h.NginxTemplate.Template, data, h.ErrorMessage)
 | |
| 		}
 | |
| 
 | |
| 		logger.Debug(h.ErrorMessage)
 | |
| 	} else {
 | |
| 		// All good
 | |
| 		h.Status = status.StatusOK
 | |
| 		h.ErrorMessage = ""
 | |
| 		logger.Debug("ConfigureHost OK: %+v", h)
 | |
| 	}
 | |
| 
 | |
| 	return h.Save(true)
 | |
| }
 | |
| 
 | |
| // ConfigureUpstream will attempt to write nginx conf and reload nginx
 | |
| func ConfigureUpstream(u upstream.Model) error {
 | |
| 	logger.Debug("ConfigureUpstream: %+v)", u)
 | |
| 
 | |
| 	// nolint: errcheck, gosec
 | |
| 	u.Expand([]string{"nginxtemplate"})
 | |
| 
 | |
| 	data := TemplateData{
 | |
| 		ConfDir:  fmt.Sprintf("%s/nginx/upstreams", config.Configuration.DataFolder),
 | |
| 		DataDir:  config.Configuration.DataFolder,
 | |
| 		Upstream: u,
 | |
| 	}
 | |
| 
 | |
| 	removeUpstreamFiles(u)
 | |
| 	filename := getUpstreamFilename(u, "")
 | |
| 	if u.IsDeleted {
 | |
| 		filename = getUpstreamFilename(u, DeletedSuffix)
 | |
| 	}
 | |
| 
 | |
| 	// Write the config to disk
 | |
| 	err := writeTemplate(filename, u.NginxTemplate.Template, data, "")
 | |
| 	if err != nil {
 | |
| 		// this configuration failed somehow
 | |
| 		u.Status = status.StatusError
 | |
| 		u.ErrorMessage = fmt.Sprintf("Template generation failed: %s", err.Error())
 | |
| 		logger.Debug(u.ErrorMessage)
 | |
| 		return u.Save(true)
 | |
| 	}
 | |
| 
 | |
| 	// nolint: errcheck, gosec
 | |
| 	if output, err := reloadNginx(); err != nil {
 | |
| 		// reloading nginx failed, likely due to this host having a problem
 | |
| 		u.Status = status.StatusError
 | |
| 		u.ErrorMessage = fmt.Sprintf("Nginx configuation error: %s - %s", err.Error(), output)
 | |
| 
 | |
| 		// Write the .error file, if this isn't a deleted upstream
 | |
| 		// as the reload will only fail because of this upstream
 | |
| 		if !u.IsDeleted {
 | |
| 			filename = getUpstreamFilename(u, ErrorSuffix)
 | |
| 			// Clear existing file(s) again
 | |
| 			removeUpstreamFiles(u)
 | |
| 			// Write the template again, but with an error message at the end of the file
 | |
| 			// nolint: errcheck, gosec
 | |
| 			writeTemplate(filename, u.NginxTemplate.Template, data, u.ErrorMessage)
 | |
| 		}
 | |
| 
 | |
| 		logger.Debug(u.ErrorMessage)
 | |
| 	} else {
 | |
| 		// All good
 | |
| 		u.Status = status.StatusOK
 | |
| 		u.ErrorMessage = ""
 | |
| 		logger.Debug("ConfigureUpstream OK: %+v", u)
 | |
| 	}
 | |
| 
 | |
| 	return u.Save(true)
 | |
| }
 | |
| 
 | |
| func getHostFilename(h host.Model, append string) string {
 | |
| 	confDir := fmt.Sprintf("%s/nginx/hosts", config.Configuration.DataFolder)
 | |
| 	return fmt.Sprintf("%s/host_%d.conf%s", confDir, h.ID, append)
 | |
| }
 | |
| 
 | |
| func getUpstreamFilename(u upstream.Model, append string) string {
 | |
| 	confDir := fmt.Sprintf("%s/nginx/upstreams", config.Configuration.DataFolder)
 | |
| 	return fmt.Sprintf("%s/upstream_%d.conf%s", confDir, u.ID, append)
 | |
| }
 | |
| 
 | |
| func removeHostFiles(h host.Model) {
 | |
| 	removeFiles([]string{
 | |
| 		getHostFilename(h, ""),
 | |
| 		getHostFilename(h, DeletedSuffix),
 | |
| 		getHostFilename(h, DisabledSuffix),
 | |
| 		getHostFilename(h, ErrorSuffix),
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func removeUpstreamFiles(u upstream.Model) {
 | |
| 	removeFiles([]string{
 | |
| 		getUpstreamFilename(u, ""),
 | |
| 		getUpstreamFilename(u, DeletedSuffix),
 | |
| 		getUpstreamFilename(u, ErrorSuffix),
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func removeFiles(files []string) {
 | |
| 	for _, file := range files {
 | |
| 		if _, err := os.Stat(file); err == nil {
 | |
| 			// nolint: errcheck, gosec
 | |
| 			os.Remove(file)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // GetHostConfigContent returns nginx config as it exists on disk
 | |
| func GetHostConfigContent(h host.Model) (string, error) {
 | |
| 	filename := getHostFilename(h, "")
 | |
| 	if h.ErrorMessage != "" {
 | |
| 		filename = getHostFilename(h, ErrorSuffix)
 | |
| 	}
 | |
| 	if h.IsDisabled {
 | |
| 		filename = getHostFilename(h, DisabledSuffix)
 | |
| 	}
 | |
| 	if h.IsDeleted {
 | |
| 		filename = getHostFilename(h, DeletedSuffix)
 | |
| 	}
 | |
| 
 | |
| 	// nolint: gosec
 | |
| 	cnt, err := os.ReadFile(filename)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	return string(cnt), nil
 | |
| }
 | |
| 
 | |
| // GetUpstreamConfigContent returns nginx config as it exists on disk
 | |
| func GetUpstreamConfigContent(u upstream.Model) (string, error) {
 | |
| 	filename := getUpstreamFilename(u, "")
 | |
| 	if u.ErrorMessage != "" {
 | |
| 		filename = getUpstreamFilename(u, ErrorSuffix)
 | |
| 	}
 | |
| 	if u.IsDeleted {
 | |
| 		filename = getUpstreamFilename(u, DeletedSuffix)
 | |
| 	}
 | |
| 
 | |
| 	// nolint: gosec
 | |
| 	cnt, err := os.ReadFile(filename)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	return string(cnt), nil
 | |
| }
 |