mirror of
https://github.com/xiaoxinpro/nginx-proxy-manager-zh.git
synced 2025-01-23 13:18:14 -05:00
Write host template on save
This commit is contained in:
parent
5b6dbaf43e
commit
8d37f5df8d
@ -78,7 +78,7 @@ func CreateHost() func(http.ResponseWriter, *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = newHost.Save(); err != nil {
|
if err = newHost.Save(false); err != nil {
|
||||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Host: %s", err.Error()), nil)
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, fmt.Sprintf("Unable to save Host: %s", err.Error()), nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -111,7 +111,7 @@ func UpdateHost() func(http.ResponseWriter, *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = hostObject.Save(); err != nil {
|
if err = hostObject.Save(false); err != nil {
|
||||||
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"npm/internal/database"
|
"npm/internal/database"
|
||||||
"npm/internal/entity/certificate"
|
"npm/internal/entity/certificate"
|
||||||
|
"npm/internal/entity/hosttemplate"
|
||||||
"npm/internal/entity/user"
|
"npm/internal/entity/user"
|
||||||
"npm/internal/types"
|
"npm/internal/types"
|
||||||
"npm/internal/util"
|
"npm/internal/util"
|
||||||
@ -57,6 +58,7 @@ type Model struct {
|
|||||||
IsDeleted bool `json:"is_deleted,omitempty" db:"is_deleted"`
|
IsDeleted bool `json:"is_deleted,omitempty" db:"is_deleted"`
|
||||||
// Expansions
|
// Expansions
|
||||||
Certificate *certificate.Model `json:"certificate,omitempty"`
|
Certificate *certificate.Model `json:"certificate,omitempty"`
|
||||||
|
HostTemplate *hosttemplate.Model `json:"host_template,omitempty"`
|
||||||
User *user.Model `json:"user,omitempty"`
|
User *user.Model `json:"user,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,15 +84,17 @@ func (m *Model) Touch(created bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save will save this model to the DB
|
// Save will save this model to the DB
|
||||||
func (m *Model) Save() error {
|
func (m *Model) Save(skipConfiguration bool) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if m.UserID == 0 {
|
if m.UserID == 0 {
|
||||||
return fmt.Errorf("User ID must be specified")
|
return fmt.Errorf("User ID must be specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !skipConfiguration {
|
||||||
// Set this host as requiring reconfiguration
|
// Set this host as requiring reconfiguration
|
||||||
m.Status = StatusReady
|
m.Status = StatusReady
|
||||||
|
}
|
||||||
|
|
||||||
if m.ID == 0 {
|
if m.ID == 0 {
|
||||||
m.ID, err = create(m)
|
m.ID, err = create(m)
|
||||||
@ -105,7 +109,7 @@ func (m *Model) Save() error {
|
|||||||
func (m *Model) Delete() bool {
|
func (m *Model) Delete() bool {
|
||||||
m.Touch(false)
|
m.Touch(false)
|
||||||
m.IsDeleted = true
|
m.IsDeleted = true
|
||||||
if err := m.Save(); err != nil {
|
if err := m.Save(false); err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -127,5 +131,11 @@ func (m *Model) Expand(items []string) error {
|
|||||||
m.Certificate = &cert
|
m.Certificate = &cert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if util.SliceContainsItem(items, "hosttemplate") && m.HostTemplateID > 0 {
|
||||||
|
var templ hosttemplate.Model
|
||||||
|
templ, err = hosttemplate.GetByID(m.HostTemplateID)
|
||||||
|
m.HostTemplate = &templ
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,51 @@
|
|||||||
package nginx
|
package nginx
|
||||||
|
|
||||||
import "npm/internal/entity/host"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"npm/internal/config"
|
||||||
|
"npm/internal/entity/host"
|
||||||
|
"npm/internal/logger"
|
||||||
|
)
|
||||||
|
|
||||||
// ConfigureHost will attempt to write nginx conf and reload nginx
|
// ConfigureHost will attempt to write nginx conf and reload nginx
|
||||||
func ConfigureHost(h host.Model) error {
|
func ConfigureHost(h host.Model) error {
|
||||||
// nolint: errcheck, gosec
|
// nolint: errcheck, gosec
|
||||||
h.Expand([]string{"certificate"})
|
h.Expand([]string{"certificate", "hosttemplate"})
|
||||||
|
|
||||||
|
data := TemplateData{
|
||||||
|
ConfDir: fmt.Sprintf("%s/nginx/hosts", config.Configuration.DataFolder),
|
||||||
|
DataDir: config.Configuration.DataFolder,
|
||||||
|
CertsDir: config.Configuration.Acmesh.CertHome,
|
||||||
|
Host: &h,
|
||||||
|
Certificate: h.Certificate,
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := fmt.Sprintf("%s/host_%d.conf", data.ConfDir, h.ID)
|
||||||
|
|
||||||
|
// Write the config to disk
|
||||||
|
err := writeTemplate(filename, h.HostTemplate.Template, data)
|
||||||
|
if err != nil {
|
||||||
|
// this configuration failed somehow
|
||||||
|
h.Status = host.StatusError
|
||||||
|
h.ErrorMessage = fmt.Sprintf("Template generation failed: %s", err.Error())
|
||||||
|
logger.Debug(h.ErrorMessage)
|
||||||
|
return h.Save(true)
|
||||||
|
}
|
||||||
|
|
||||||
// nolint: errcheck, gosec
|
// nolint: errcheck, gosec
|
||||||
reloadNginx()
|
if err := reloadNginx(); err != nil {
|
||||||
return nil
|
// reloading nginx failed, likely due to this host having a problem
|
||||||
|
h.Status = host.StatusError
|
||||||
|
h.ErrorMessage = fmt.Sprintf("Nginx configuation error: %s", err.Error())
|
||||||
|
writeConfigFile(filename, fmt.Sprintf("# %s", h.ErrorMessage))
|
||||||
|
logger.Debug(h.ErrorMessage)
|
||||||
|
} else {
|
||||||
|
// All good
|
||||||
|
h.Status = host.StatusOK
|
||||||
|
h.ErrorMessage = ""
|
||||||
|
logger.Debug("ConfigureHost OK: %+v", h)
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.Save(true)
|
||||||
}
|
}
|
||||||
|
86
backend/internal/nginx/template_test.go
Normal file
86
backend/internal/nginx/template_test.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package nginx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"npm/internal/entity/certificate"
|
||||||
|
"npm/internal/entity/host"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWriteTemplate(t *testing.T) {
|
||||||
|
template := `
|
||||||
|
{{#if Host.IsDisabled}}
|
||||||
|
# Host is disabled
|
||||||
|
{{else}}
|
||||||
|
server {
|
||||||
|
{{#if Certificate}}
|
||||||
|
{{#if Certificate.CertificateAuthorityID}}
|
||||||
|
# Acme SSL
|
||||||
|
include {{ConfDir}}/npm/conf.d/acme-challenge.conf;
|
||||||
|
include {{ConfDir}}/npm/conf.d/include/ssl-ciphers.conf;
|
||||||
|
ssl_certificate {{CertsDir}}/npm-{{Certificate.ID}}/fullchain.pem;
|
||||||
|
ssl_certificate_key {{CertsDir}}/npm-{{Certificate.ID}}/privkey.pem;
|
||||||
|
{{else}}
|
||||||
|
# Custom SSL
|
||||||
|
ssl_certificate {{DataDir}}/custom_ssl/npm-{{Certificate.ID}}/fullchain.pem;
|
||||||
|
ssl_certificate_key {{DataDir}}/custom_ssl/npm-{{Certificate.ID}}/privkey.pem;
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
type want struct {
|
||||||
|
output string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
data TemplateData
|
||||||
|
want want
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Basic Template enabled",
|
||||||
|
data: TemplateData{
|
||||||
|
ConfDir: "/etc/nginx/conf.d",
|
||||||
|
Host: &host.Model{
|
||||||
|
IsDisabled: false,
|
||||||
|
},
|
||||||
|
Certificate: &certificate.Model{
|
||||||
|
CertificateAuthorityID: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: want{
|
||||||
|
output: "\nserver {\n # Custom SSL\n ssl_certificate /custom_ssl/npm-0/fullchain.pem;\n ssl_certificate_key /custom_ssl/npm-0/privkey.pem;\n \n}\n\n",
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Basic Template disabled",
|
||||||
|
data: TemplateData{
|
||||||
|
ConfDir: "/etc/nginx/conf.d",
|
||||||
|
DataDir: "/data",
|
||||||
|
CertsDir: "/acme.sh/certs",
|
||||||
|
Host: &host.Model{
|
||||||
|
IsDisabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: want{
|
||||||
|
output: "\n # Host is disabled\n\n",
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(st *testing.T) {
|
||||||
|
output, err := generateHostConfig(template, test.data)
|
||||||
|
assert.Equal(t, test.want.err, err)
|
||||||
|
assert.Equal(t, test.want.output, output)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,31 +1,46 @@
|
|||||||
package nginx
|
package nginx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/fs"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
"npm/embed"
|
"npm/internal/entity/certificate"
|
||||||
|
"npm/internal/entity/host"
|
||||||
|
"npm/internal/logger"
|
||||||
|
|
||||||
"github.com/aymerick/raymond"
|
"github.com/aymerick/raymond"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WriteTemplate will load, parse and write a template file
|
// TemplateData ...
|
||||||
func WriteTemplate(templateName, outputFilename string, data map[string]interface{}) error {
|
type TemplateData struct {
|
||||||
// get template file content
|
ConfDir string
|
||||||
subFs, _ := fs.Sub(embed.NginxFiles, "nginx")
|
DataDir string
|
||||||
template, err := fs.ReadFile(subFs, templateName)
|
CertsDir string
|
||||||
|
Host *host.Model
|
||||||
if err != nil {
|
Certificate *certificate.Model
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
|
func generateHostConfig(template string, data TemplateData) (string, error) {
|
||||||
// Render
|
return raymond.Render(template, data)
|
||||||
parsedFile, err := raymond.Render(string(template), data)
|
}
|
||||||
if err != nil {
|
|
||||||
return err
|
func writeTemplate(filename, template string, data TemplateData) error {
|
||||||
}
|
output, err := generateHostConfig(template, data)
|
||||||
|
if err != nil {
|
||||||
// Write it
|
output = fmt.Sprintf("# Template Error: %s", err.Error())
|
||||||
// nolint: gosec
|
}
|
||||||
return ioutil.WriteFile(outputFilename, []byte(parsedFile), 0644)
|
|
||||||
|
// Write it. This will also write an error comment if generation failed
|
||||||
|
// nolint: gosec
|
||||||
|
writeErr := writeConfigFile(filename, output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return writeErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeConfigFile(filename, content string) error {
|
||||||
|
logger.Debug("Writing %s with:\n%s", filename, content)
|
||||||
|
// nolint: gosec
|
||||||
|
return ioutil.WriteFile(filename, []byte(content), 0644)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user