diff --git a/app.example.ini b/app.example.ini index 9f228802..95a3dcc1 100644 --- a/app.example.ini +++ b/app.example.ini @@ -5,6 +5,11 @@ JwtSecret = Email = HTTPChallengePort = 9180 StartCmd = login +Database = database +CADir = +Demo = +GithubProxy = +NginxConfigDir = [nginx_log] AccessLogPath = /var/log/nginx/access.log diff --git a/main.go b/main.go index 083d4b52..f06271ad 100644 --- a/main.go +++ b/main.go @@ -48,7 +48,7 @@ func prog(state overseer.State) { gin.SetMode(settings.ServerSettings.RunMode) settings.Init(confPath) - log.Printf("Nginx config dir path: %s", nginx.GetNginxConfPath("")) + log.Printf("Nginx config dir path: %s", nginx.GetConfPath()) if "" != settings.ServerSettings.JwtSecret { model.Init() diff --git a/server/api/cert.go b/server/api/cert.go index 5a2c8cb1..6fd3fd36 100644 --- a/server/api/cert.go +++ b/server/api/cert.go @@ -1,352 +1,352 @@ package api import ( - "github.com/0xJacky/Nginx-UI/server/model" - "github.com/0xJacky/Nginx-UI/server/pkg/cert" - "github.com/0xJacky/Nginx-UI/server/pkg/nginx" - "github.com/gin-gonic/gin" - "github.com/gorilla/websocket" - "github.com/spf13/cast" - "log" - "net/http" - "os" - "path/filepath" - "strings" + "github.com/0xJacky/Nginx-UI/server/model" + "github.com/0xJacky/Nginx-UI/server/pkg/cert" + "github.com/0xJacky/Nginx-UI/server/pkg/nginx" + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "github.com/spf13/cast" + "log" + "net/http" + "os" + "path/filepath" + "strings" ) const ( - Success = "success" - Info = "info" - Error = "error" + Success = "success" + Info = "info" + Error = "error" ) type IssueCertResponse struct { - Status string `json:"status"` - Message string `json:"message"` - SSLCertificate string `json:"ssl_certificate,omitempty"` - SSLCertificateKey string `json:"ssl_certificate_key,omitempty"` + Status string `json:"status"` + Message string `json:"message"` + SSLCertificate string `json:"ssl_certificate,omitempty"` + SSLCertificateKey string `json:"ssl_certificate_key,omitempty"` } func handleIssueCertLogChan(conn *websocket.Conn, logChan chan string) { - defer func() { - if err := recover(); err != nil { - log.Println("api.handleIssueCertLogChan recover", err) - } - }() + defer func() { + if err := recover(); err != nil { + log.Println("api.handleIssueCertLogChan recover", err) + } + }() - for logString := range logChan { + for logString := range logChan { - err := conn.WriteJSON(IssueCertResponse{ - Status: Info, - Message: logString, - }) + err := conn.WriteJSON(IssueCertResponse{ + Status: Info, + Message: logString, + }) - if err != nil { - log.Println("Error handleIssueCertLogChan", err) - return - } + if err != nil { + log.Println("Error handleIssueCertLogChan", err) + return + } - } + } } func IssueCert(c *gin.Context) { - var upGrader = websocket.Upgrader{ - CheckOrigin: func(r *http.Request) bool { - return true - }, - } + var upGrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true + }, + } - // upgrade http to websocket - ws, err := upGrader.Upgrade(c.Writer, c.Request, nil) - if err != nil { - log.Println(err) - return - } + // upgrade http to websocket + ws, err := upGrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + log.Println(err) + return + } - defer func(ws *websocket.Conn) { - err := ws.Close() - if err != nil { - log.Println("defer websocket close err", err) - } - }(ws) + defer func(ws *websocket.Conn) { + err := ws.Close() + if err != nil { + log.Println("defer websocket close err", err) + } + }(ws) - // read - var buffer struct { - ServerName []string `json:"server_name"` - } + // read + var buffer struct { + ServerName []string `json:"server_name"` + } - err = ws.ReadJSON(&buffer) + err = ws.ReadJSON(&buffer) - if err != nil { - log.Println(err) - return - } + if err != nil { + log.Println(err) + return + } - logChan := make(chan string, 1) - errChan := make(chan error, 1) + logChan := make(chan string, 1) + errChan := make(chan error, 1) - go cert.IssueCert(buffer.ServerName, logChan, errChan) + go cert.IssueCert(buffer.ServerName, logChan, errChan) - domain := strings.Join(buffer.ServerName, "_") + domain := strings.Join(buffer.ServerName, "_") - go handleIssueCertLogChan(ws, logChan) + go handleIssueCertLogChan(ws, logChan) - // block, unless errChan closed - for err = range errChan { - log.Println("Error cert.IssueCert", err) + // block, unless errChan closed + for err = range errChan { + log.Println("Error cert.IssueCert", err) - err = ws.WriteJSON(IssueCertResponse{ - Status: Error, - Message: err.Error(), - }) + err = ws.WriteJSON(IssueCertResponse{ + Status: Error, + Message: err.Error(), + }) - if err != nil { - log.Println("Error WriteJSON", err) - return - } + if err != nil { + log.Println("Error WriteJSON", err) + return + } - return - } + return + } - close(logChan) + close(logChan) - sslCertificatePath := nginx.GetNginxConfPath("ssl/" + domain + "/fullchain.cer") - sslCertificateKeyPath := nginx.GetNginxConfPath("ssl/" + domain + "/private.key") + sslCertificatePath := nginx.GetConfPath("ssl", domain, "fullchain.cer") + sslCertificateKeyPath := nginx.GetConfPath("ssl", domain, "private.key") - certModel, err := model.FirstOrCreateCert(domain) + certModel, err := model.FirstOrCreateCert(domain) - if err != nil { - log.Println(err) - } + if err != nil { + log.Println(err) + } - err = certModel.Updates(&model.Cert{ - SSLCertificatePath: sslCertificatePath, - SSLCertificateKeyPath: sslCertificateKeyPath, - }) + err = certModel.Updates(&model.Cert{ + SSLCertificatePath: sslCertificatePath, + SSLCertificateKeyPath: sslCertificateKeyPath, + }) - if err != nil { - log.Println(err) - } + if err != nil { + log.Println(err) + } - err = ws.WriteJSON(IssueCertResponse{ - Status: Success, - Message: "Issued certificate successfully", - SSLCertificate: sslCertificatePath, - SSLCertificateKey: sslCertificateKeyPath, - }) + err = ws.WriteJSON(IssueCertResponse{ + Status: Success, + Message: "Issued certificate successfully", + SSLCertificate: sslCertificatePath, + SSLCertificateKey: sslCertificateKeyPath, + }) - if err != nil { - log.Println(err) - return - } + if err != nil { + log.Println(err) + return + } } func GetCertList(c *gin.Context) { - certList := model.GetCertList(c.Query("name"), c.Query("domain")) + certList := model.GetCertList(c.Query("name"), c.Query("domain")) - c.JSON(http.StatusOK, gin.H{ - "data": certList, - }) + c.JSON(http.StatusOK, gin.H{ + "data": certList, + }) } func getCert(c *gin.Context, certModel model.Cert) { - type resp struct { - model.Cert - SSLCertification string `json:"ssl_certification"` - SSLCertificationKey string `json:"ssl_certification_key"` - CertificateInfo *CertificateInfo `json:"certificate_info,omitempty"` - } - - var sslCertificationBytes, sslCertificationKeyBytes []byte - var certificateInfo *CertificateInfo - if certModel.SSLCertificatePath != "" { - if _, err := os.Stat(certModel.SSLCertificatePath); err == nil { - sslCertificationBytes, _ = os.ReadFile(certModel.SSLCertificatePath) - } - - pubKey, err := cert.GetCertInfo(certModel.SSLCertificatePath) - - if err != nil { - ErrHandler(c, err) - return - } - - certificateInfo = &CertificateInfo{ - SubjectName: pubKey.Subject.CommonName, - IssuerName: pubKey.Issuer.CommonName, - NotAfter: pubKey.NotAfter, - NotBefore: pubKey.NotBefore, - } - } - - if certModel.SSLCertificateKeyPath != "" { - if _, err := os.Stat(certModel.SSLCertificateKeyPath); err == nil { - sslCertificationKeyBytes, _ = os.ReadFile(certModel.SSLCertificateKeyPath) - } - } - - c.JSON(http.StatusOK, resp{ - certModel, - string(sslCertificationBytes), - string(sslCertificationKeyBytes), - certificateInfo, - }) + type resp struct { + model.Cert + SSLCertification string `json:"ssl_certification"` + SSLCertificationKey string `json:"ssl_certification_key"` + CertificateInfo *CertificateInfo `json:"certificate_info,omitempty"` + } + + var sslCertificationBytes, sslCertificationKeyBytes []byte + var certificateInfo *CertificateInfo + if certModel.SSLCertificatePath != "" { + if _, err := os.Stat(certModel.SSLCertificatePath); err == nil { + sslCertificationBytes, _ = os.ReadFile(certModel.SSLCertificatePath) + } + + pubKey, err := cert.GetCertInfo(certModel.SSLCertificatePath) + + if err != nil { + ErrHandler(c, err) + return + } + + certificateInfo = &CertificateInfo{ + SubjectName: pubKey.Subject.CommonName, + IssuerName: pubKey.Issuer.CommonName, + NotAfter: pubKey.NotAfter, + NotBefore: pubKey.NotBefore, + } + } + + if certModel.SSLCertificateKeyPath != "" { + if _, err := os.Stat(certModel.SSLCertificateKeyPath); err == nil { + sslCertificationKeyBytes, _ = os.ReadFile(certModel.SSLCertificateKeyPath) + } + } + + c.JSON(http.StatusOK, resp{ + certModel, + string(sslCertificationBytes), + string(sslCertificationKeyBytes), + certificateInfo, + }) } func GetCert(c *gin.Context) { - certModel, err := model.FirstCertByID(cast.ToInt(c.Param("id"))) + certModel, err := model.FirstCertByID(cast.ToInt(c.Param("id"))) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - getCert(c, certModel) + getCert(c, certModel) } func AddCert(c *gin.Context) { - var json struct { - Name string `json:"name"` - Domain string `json:"domain" binding:"required"` - SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"` - SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"` - SSLCertification string `json:"ssl_certification"` - SSLCertificationKey string `json:"ssl_certification_key"` - } - if !BindAndValid(c, &json) { - return - } - certModel, err := model.FirstOrCreateCert(json.Domain) - - if err != nil { - ErrHandler(c, err) - return - } - - err = certModel.Updates(&model.Cert{ - Name: json.Name, - Domain: json.Domain, - SSLCertificatePath: json.SSLCertificatePath, - SSLCertificateKeyPath: json.SSLCertificateKeyPath, - }) - - if err != nil { - ErrHandler(c, err) - return - } - - err = os.MkdirAll(filepath.Dir(json.SSLCertificatePath), 0644) - if err != nil { - ErrHandler(c, err) - return - } - - err = os.MkdirAll(filepath.Dir(json.SSLCertificateKeyPath), 0644) - if err != nil { - ErrHandler(c, err) - return - } - - if json.SSLCertification != "" { - err = os.WriteFile(json.SSLCertificatePath, []byte(json.SSLCertification), 0644) - if err != nil { - ErrHandler(c, err) - return - } - } - - if json.SSLCertificationKey != "" { - err = os.WriteFile(json.SSLCertificateKeyPath, []byte(json.SSLCertificationKey), 0644) - if err != nil { - ErrHandler(c, err) - return - } - } - - getCert(c, certModel) + var json struct { + Name string `json:"name"` + Domain string `json:"domain" binding:"required"` + SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"` + SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"` + SSLCertification string `json:"ssl_certification"` + SSLCertificationKey string `json:"ssl_certification_key"` + } + if !BindAndValid(c, &json) { + return + } + certModel, err := model.FirstOrCreateCert(json.Domain) + + if err != nil { + ErrHandler(c, err) + return + } + + err = certModel.Updates(&model.Cert{ + Name: json.Name, + Domain: json.Domain, + SSLCertificatePath: json.SSLCertificatePath, + SSLCertificateKeyPath: json.SSLCertificateKeyPath, + }) + + if err != nil { + ErrHandler(c, err) + return + } + + err = os.MkdirAll(filepath.Dir(json.SSLCertificatePath), 0644) + if err != nil { + ErrHandler(c, err) + return + } + + err = os.MkdirAll(filepath.Dir(json.SSLCertificateKeyPath), 0644) + if err != nil { + ErrHandler(c, err) + return + } + + if json.SSLCertification != "" { + err = os.WriteFile(json.SSLCertificatePath, []byte(json.SSLCertification), 0644) + if err != nil { + ErrHandler(c, err) + return + } + } + + if json.SSLCertificationKey != "" { + err = os.WriteFile(json.SSLCertificateKeyPath, []byte(json.SSLCertificationKey), 0644) + if err != nil { + ErrHandler(c, err) + return + } + } + + getCert(c, certModel) } func ModifyCert(c *gin.Context) { - id := cast.ToInt(c.Param("id")) - certModel, err := model.FirstCertByID(id) - - var json struct { - Name string `json:"name"` - Domain string `json:"domain" binding:"required"` - SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"` - SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"` - SSLCertification string `json:"ssl_certification"` - SSLCertificationKey string `json:"ssl_certification_key"` - } - - if !BindAndValid(c, &json) { - return - } - - if err != nil { - ErrHandler(c, err) - return - } - - err = certModel.Updates(&model.Cert{ - Name: json.Name, - Domain: json.Domain, - SSLCertificatePath: json.SSLCertificatePath, - SSLCertificateKeyPath: json.SSLCertificateKeyPath, - }) - - if err != nil { - ErrHandler(c, err) - return - } - - err = os.MkdirAll(filepath.Dir(json.SSLCertificatePath), 0644) - if err != nil { - ErrHandler(c, err) - return - } - - err = os.MkdirAll(filepath.Dir(json.SSLCertificateKeyPath), 0644) - if err != nil { - ErrHandler(c, err) - return - } - - if json.SSLCertification != "" { - err = os.WriteFile(json.SSLCertificatePath, []byte(json.SSLCertification), 0644) - if err != nil { - ErrHandler(c, err) - return - } - } - - if json.SSLCertificationKey != "" { - err = os.WriteFile(json.SSLCertificateKeyPath, []byte(json.SSLCertificationKey), 0644) - if err != nil { - ErrHandler(c, err) - return - } - } - - GetCert(c) + id := cast.ToInt(c.Param("id")) + certModel, err := model.FirstCertByID(id) + + var json struct { + Name string `json:"name"` + Domain string `json:"domain" binding:"required"` + SSLCertificatePath string `json:"ssl_certificate_path" binding:"required"` + SSLCertificateKeyPath string `json:"ssl_certificate_key_path" binding:"required"` + SSLCertification string `json:"ssl_certification"` + SSLCertificationKey string `json:"ssl_certification_key"` + } + + if !BindAndValid(c, &json) { + return + } + + if err != nil { + ErrHandler(c, err) + return + } + + err = certModel.Updates(&model.Cert{ + Name: json.Name, + Domain: json.Domain, + SSLCertificatePath: json.SSLCertificatePath, + SSLCertificateKeyPath: json.SSLCertificateKeyPath, + }) + + if err != nil { + ErrHandler(c, err) + return + } + + err = os.MkdirAll(filepath.Dir(json.SSLCertificatePath), 0644) + if err != nil { + ErrHandler(c, err) + return + } + + err = os.MkdirAll(filepath.Dir(json.SSLCertificateKeyPath), 0644) + if err != nil { + ErrHandler(c, err) + return + } + + if json.SSLCertification != "" { + err = os.WriteFile(json.SSLCertificatePath, []byte(json.SSLCertification), 0644) + if err != nil { + ErrHandler(c, err) + return + } + } + + if json.SSLCertificationKey != "" { + err = os.WriteFile(json.SSLCertificateKeyPath, []byte(json.SSLCertificationKey), 0644) + if err != nil { + ErrHandler(c, err) + return + } + } + + GetCert(c) } func RemoveCert(c *gin.Context) { - id := cast.ToInt(c.Param("id")) - certModel, err := model.FirstCertByID(id) + id := cast.ToInt(c.Param("id")) + certModel, err := model.FirstCertByID(id) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - err = certModel.Remove() + err = certModel.Remove() - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - c.JSON(http.StatusNoContent, nil) + c.JSON(http.StatusNoContent, nil) } diff --git a/server/api/config.go b/server/api/config.go index 062e3d68..caa9312d 100644 --- a/server/api/config.go +++ b/server/api/config.go @@ -1,14 +1,13 @@ package api import ( - "github.com/0xJacky/Nginx-UI/server/pkg/config_list" - "github.com/0xJacky/Nginx-UI/server/pkg/nginx" - "github.com/gin-gonic/gin" - "log" - "net/http" - "os" - "path/filepath" - "strings" + "github.com/0xJacky/Nginx-UI/server/pkg/config_list" + "github.com/0xJacky/Nginx-UI/server/pkg/nginx" + "github.com/gin-gonic/gin" + "log" + "net/http" + "os" + "strings" ) func GetConfigs(c *gin.Context) { @@ -22,7 +21,7 @@ func GetConfigs(c *gin.Context) { "is_dir": "bool", } - configFiles, err := os.ReadDir(nginx.GetNginxConfPath(dir)) + configFiles, err := os.ReadDir(nginx.GetConfPath(dir)) if err != nil { ErrHandler(c, err) @@ -42,7 +41,7 @@ func GetConfigs(c *gin.Context) { } case mode&os.ModeSymlink != 0: // is a symbol var targetPath string - targetPath, err = os.Readlink(nginx.GetNginxConfPath(file.Name())) + targetPath, err = os.Readlink(nginx.GetConfPath(file.Name())) if err != nil { log.Println("GetConfigs Read Symlink Error", targetPath, err) continue @@ -77,7 +76,7 @@ func GetConfigs(c *gin.Context) { func GetConfig(c *gin.Context) { name := c.Param("name") - path := filepath.Join(nginx.GetNginxConfPath("/"), name) + path := nginx.GetConfPath("/", name) content, err := os.ReadFile(path) @@ -108,7 +107,7 @@ func AddConfig(c *gin.Context) { name := request.Name content := request.Content - path := filepath.Join(nginx.GetNginxConfPath("/"), name) + path := nginx.GetConfPath("/", name) if _, err = os.Stat(path); err == nil { c.JSON(http.StatusNotAcceptable, gin.H{ @@ -125,7 +124,7 @@ func AddConfig(c *gin.Context) { } } - output := nginx.ReloadNginx() + output := nginx.Reload() if output != "" && strings.Contains(output, "error") { c.JSON(http.StatusInternalServerError, gin.H{ @@ -153,7 +152,7 @@ func EditConfig(c *gin.Context) { ErrHandler(c, err) return } - path := filepath.Join(nginx.GetNginxConfPath("/"), name) + path := nginx.GetConfPath("/", name) content := request.Content origContent, err := os.ReadFile(path) @@ -171,7 +170,7 @@ func EditConfig(c *gin.Context) { } } - output := nginx.ReloadNginx() + output := nginx.Reload() if output != "" && strings.Contains(output, "error") { c.JSON(http.StatusInternalServerError, gin.H{ diff --git a/server/api/domain.go b/server/api/domain.go index 2e04423c..60860a24 100644 --- a/server/api/domain.go +++ b/server/api/domain.go @@ -1,387 +1,386 @@ package api import ( - "github.com/0xJacky/Nginx-UI/server/model" - "github.com/0xJacky/Nginx-UI/server/pkg/cert" - "github.com/0xJacky/Nginx-UI/server/pkg/config_list" - "github.com/0xJacky/Nginx-UI/server/pkg/nginx" - "github.com/gin-gonic/gin" - "log" - "net/http" - "os" - "path/filepath" - "strings" - "time" + "github.com/0xJacky/Nginx-UI/server/model" + "github.com/0xJacky/Nginx-UI/server/pkg/cert" + "github.com/0xJacky/Nginx-UI/server/pkg/config_list" + "github.com/0xJacky/Nginx-UI/server/pkg/nginx" + "github.com/gin-gonic/gin" + "log" + "net/http" + "os" + "strings" + "time" ) func GetDomains(c *gin.Context) { - name := c.Query("name") - orderBy := c.Query("order_by") - sort := c.DefaultQuery("sort", "desc") - - mySort := map[string]string{ - "enabled": "bool", - "name": "string", - "modify": "time", - } - - configFiles, err := os.ReadDir(nginx.GetNginxConfPath("sites-available")) - - if err != nil { - ErrHandler(c, err) - return - } - - enabledConfig, err := os.ReadDir(filepath.Join(nginx.GetNginxConfPath("sites-enabled"))) - - if err != nil { - ErrHandler(c, err) - return - } - - enabledConfigMap := make(map[string]bool) - for i := range enabledConfig { - enabledConfigMap[enabledConfig[i].Name()] = true - } - - var configs []gin.H - - for i := range configFiles { - file := configFiles[i] - fileInfo, _ := file.Info() - if !file.IsDir() { - if name != "" && !strings.Contains(file.Name(), name) { - continue - } - configs = append(configs, gin.H{ - "name": file.Name(), - "size": fileInfo.Size(), - "modify": fileInfo.ModTime(), - "enabled": enabledConfigMap[file.Name()], - }) - } - } - - configs = config_list.Sort(orderBy, sort, mySort[orderBy], configs) - - c.JSON(http.StatusOK, gin.H{ - "data": configs, - }) + name := c.Query("name") + orderBy := c.Query("order_by") + sort := c.DefaultQuery("sort", "desc") + + mySort := map[string]string{ + "enabled": "bool", + "name": "string", + "modify": "time", + } + + configFiles, err := os.ReadDir(nginx.GetConfPath("sites-available")) + + if err != nil { + ErrHandler(c, err) + return + } + + enabledConfig, err := os.ReadDir(nginx.GetConfPath("sites-enabled")) + + if err != nil { + ErrHandler(c, err) + return + } + + enabledConfigMap := make(map[string]bool) + for i := range enabledConfig { + enabledConfigMap[enabledConfig[i].Name()] = true + } + + var configs []gin.H + + for i := range configFiles { + file := configFiles[i] + fileInfo, _ := file.Info() + if !file.IsDir() { + if name != "" && !strings.Contains(file.Name(), name) { + continue + } + configs = append(configs, gin.H{ + "name": file.Name(), + "size": fileInfo.Size(), + "modify": fileInfo.ModTime(), + "enabled": enabledConfigMap[file.Name()], + }) + } + } + + configs = config_list.Sort(orderBy, sort, mySort[orderBy], configs) + + c.JSON(http.StatusOK, gin.H{ + "data": configs, + }) } type CertificateInfo struct { - SubjectName string `json:"subject_name"` - IssuerName string `json:"issuer_name"` - NotAfter time.Time `json:"not_after"` - NotBefore time.Time `json:"not_before"` + SubjectName string `json:"subject_name"` + IssuerName string `json:"issuer_name"` + NotAfter time.Time `json:"not_after"` + NotBefore time.Time `json:"not_before"` } func GetDomain(c *gin.Context) { - rewriteName, ok := c.Get("rewriteConfigFileName") + rewriteName, ok := c.Get("rewriteConfigFileName") - name := c.Param("name") + name := c.Param("name") - // for modify filename - if ok { - name = rewriteName.(string) - } + // for modify filename + if ok { + name = rewriteName.(string) + } - path := filepath.Join(nginx.GetNginxConfPath("sites-available"), name) + path := nginx.GetConfPath("sites-available", name) - enabled := true - if _, err := os.Stat(filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name)); os.IsNotExist(err) { - enabled = false - } + enabled := true + if _, err := os.Stat(nginx.GetConfPath("sites-enabled", name)); os.IsNotExist(err) { + enabled = false + } - c.Set("maybe_error", "nginx_config_syntax_error") - config, err := nginx.ParseNgxConfig(path) + c.Set("maybe_error", "nginx_config_syntax_error") + config, err := nginx.ParseNgxConfig(path) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - c.Set("maybe_error", "") + c.Set("maybe_error", "") - certInfoMap := make(map[int]CertificateInfo) - var serverName string - for serverIdx, server := range config.Servers { - for _, directive := range server.Directives { + certInfoMap := make(map[int]CertificateInfo) + var serverName string + for serverIdx, server := range config.Servers { + for _, directive := range server.Directives { - if directive.Directive == "server_name" { - serverName = strings.ReplaceAll(directive.Params, " ", "_") - continue - } + if directive.Directive == "server_name" { + serverName = strings.ReplaceAll(directive.Params, " ", "_") + continue + } - if directive.Directive == "ssl_certificate" { + if directive.Directive == "ssl_certificate" { - pubKey, err := cert.GetCertInfo(directive.Params) + pubKey, err := cert.GetCertInfo(directive.Params) - if err != nil { - log.Println("Failed to get certificate information", err) - break - } + if err != nil { + log.Println("Failed to get certificate information", err) + break + } - certInfoMap[serverIdx] = CertificateInfo{ - SubjectName: pubKey.Subject.CommonName, - IssuerName: pubKey.Issuer.CommonName, - NotAfter: pubKey.NotAfter, - NotBefore: pubKey.NotBefore, - } + certInfoMap[serverIdx] = CertificateInfo{ + SubjectName: pubKey.Subject.CommonName, + IssuerName: pubKey.Issuer.CommonName, + NotAfter: pubKey.NotAfter, + NotBefore: pubKey.NotBefore, + } - break - } - } - } + break + } + } + } - certModel, _ := model.FirstCert(serverName) + certModel, _ := model.FirstCert(serverName) - c.Set("maybe_error", "nginx_config_syntax_error") + c.Set("maybe_error", "nginx_config_syntax_error") - c.JSON(http.StatusOK, gin.H{ - "enabled": enabled, - "name": name, - "config": config.FmtCode(), - "tokenized": config, - "auto_cert": certModel.AutoCert == model.AutoCertEnabled, - "cert_info": certInfoMap, - }) + c.JSON(http.StatusOK, gin.H{ + "enabled": enabled, + "name": name, + "config": config.FmtCode(), + "tokenized": config, + "auto_cert": certModel.AutoCert == model.AutoCertEnabled, + "cert_info": certInfoMap, + }) } func EditDomain(c *gin.Context) { - name := c.Param("name") - - if name == "" { - c.JSON(http.StatusNotAcceptable, gin.H{ - "message": "param name is empty", - }) - return - } - - var json struct { - Name string `json:"name" binding:"required"` - Content string `json:"content" binding:"required"` - } - - if !BindAndValid(c, &json) { - return - } - - path := filepath.Join(nginx.GetNginxConfPath("sites-available"), name) - - err := os.WriteFile(path, []byte(json.Content), 0644) - if err != nil { - ErrHandler(c, err) - return - } - enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name) - // rename the config file if needed - if name != json.Name { - newPath := filepath.Join(nginx.GetNginxConfPath("sites-available"), json.Name) - // recreate soft link - log.Println(enabledConfigFilePath) - if _, err = os.Stat(enabledConfigFilePath); err == nil { - log.Println(enabledConfigFilePath) - _ = os.Remove(enabledConfigFilePath) - enabledConfigFilePath = filepath.Join(nginx.GetNginxConfPath("sites-enabled"), json.Name) - err = os.Symlink(newPath, enabledConfigFilePath) - - if err != nil { - ErrHandler(c, err) - return - } - } - err = os.Rename(path, newPath) - if err != nil { - ErrHandler(c, err) - return - } - name = json.Name - c.Set("rewriteConfigFileName", name) - - } - - enabledConfigFilePath = filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name) - if _, err = os.Stat(enabledConfigFilePath); err == nil { - // Test nginx configuration - err = nginx.TestNginxConf() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{ - "message": err.Error(), - "error": "nginx_config_syntax_error", - }) - return - } - - output := nginx.ReloadNginx() - - if output != "" && strings.Contains(output, "error") { - c.JSON(http.StatusInternalServerError, gin.H{ - "message": output, - }) - return - } - } - - GetDomain(c) + name := c.Param("name") + + if name == "" { + c.JSON(http.StatusNotAcceptable, gin.H{ + "message": "param name is empty", + }) + return + } + + var json struct { + Name string `json:"name" binding:"required"` + Content string `json:"content" binding:"required"` + } + + if !BindAndValid(c, &json) { + return + } + + path := nginx.GetConfPath("sites-available", name) + + err := os.WriteFile(path, []byte(json.Content), 0644) + if err != nil { + ErrHandler(c, err) + return + } + enabledConfigFilePath := nginx.GetConfPath("sites-enabled", name) + // rename the config file if needed + if name != json.Name { + newPath := nginx.GetConfPath("sites-available", json.Name) + // recreate soft link + log.Println(enabledConfigFilePath) + if _, err = os.Stat(enabledConfigFilePath); err == nil { + log.Println(enabledConfigFilePath) + _ = os.Remove(enabledConfigFilePath) + enabledConfigFilePath = nginx.GetConfPath("sites-enabled", json.Name) + err = os.Symlink(newPath, enabledConfigFilePath) + + if err != nil { + ErrHandler(c, err) + return + } + } + err = os.Rename(path, newPath) + if err != nil { + ErrHandler(c, err) + return + } + name = json.Name + c.Set("rewriteConfigFileName", name) + + } + + enabledConfigFilePath = nginx.GetConfPath("sites-enabled", name) + if _, err = os.Stat(enabledConfigFilePath); err == nil { + // Test nginx configuration + err = nginx.TestConf() + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": err.Error(), + "error": "nginx_config_syntax_error", + }) + return + } + + output := nginx.Reload() + + if output != "" && strings.Contains(output, "error") { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": output, + }) + return + } + } + + GetDomain(c) } func EnableDomain(c *gin.Context) { - configFilePath := filepath.Join(nginx.GetNginxConfPath("sites-available"), c.Param("name")) - enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), c.Param("name")) - - _, err := os.Stat(configFilePath) - - if err != nil { - ErrHandler(c, err) - return - } - - if _, err = os.Stat(enabledConfigFilePath); os.IsNotExist(err) { - err = os.Symlink(configFilePath, enabledConfigFilePath) - - if err != nil { - ErrHandler(c, err) - return - } - } - - // Test nginx config, if not pass then rollback. - err = nginx.TestNginxConf() - if err != nil { - _ = os.Remove(enabledConfigFilePath) - c.JSON(http.StatusInternalServerError, gin.H{ - "message": err.Error(), - }) - return - } - - output := nginx.ReloadNginx() - - if output != "" && strings.Contains(output, "error") { - c.JSON(http.StatusInternalServerError, gin.H{ - "message": output, - }) - return - } - - c.JSON(http.StatusOK, gin.H{ - "message": "ok", - }) + configFilePath := nginx.GetConfPath("sites-available", c.Param("name")) + enabledConfigFilePath := nginx.GetConfPath("sites-enabled", c.Param("name")) + + _, err := os.Stat(configFilePath) + + if err != nil { + ErrHandler(c, err) + return + } + + if _, err = os.Stat(enabledConfigFilePath); os.IsNotExist(err) { + err = os.Symlink(configFilePath, enabledConfigFilePath) + + if err != nil { + ErrHandler(c, err) + return + } + } + + // Test nginx config, if not pass then rollback. + err = nginx.TestConf() + if err != nil { + _ = os.Remove(enabledConfigFilePath) + c.JSON(http.StatusInternalServerError, gin.H{ + "message": err.Error(), + }) + return + } + + output := nginx.Reload() + + if output != "" && strings.Contains(output, "error") { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": output, + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + }) } func DisableDomain(c *gin.Context) { - enabledConfigFilePath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), c.Param("name")) - - _, err := os.Stat(enabledConfigFilePath) - - if err != nil { - ErrHandler(c, err) - return - } - - err = os.Remove(enabledConfigFilePath) - - if err != nil { - ErrHandler(c, err) - return - } - - // delete auto cert record - certModel := model.Cert{Domain: c.Param("name")} - err = certModel.Remove() - if err != nil { - ErrHandler(c, err) - return - } - - output := nginx.ReloadNginx() - - if output != "" { - c.JSON(http.StatusInternalServerError, gin.H{ - "message": output, - }) - return - } - - c.JSON(http.StatusOK, gin.H{ - "message": "ok", - }) + enabledConfigFilePath := nginx.GetConfPath("sites-enabled", c.Param("name")) + + _, err := os.Stat(enabledConfigFilePath) + + if err != nil { + ErrHandler(c, err) + return + } + + err = os.Remove(enabledConfigFilePath) + + if err != nil { + ErrHandler(c, err) + return + } + + // delete auto cert record + certModel := model.Cert{Domain: c.Param("name")} + err = certModel.Remove() + if err != nil { + ErrHandler(c, err) + return + } + + output := nginx.Reload() + + if output != "" { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": output, + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + }) } func DeleteDomain(c *gin.Context) { - var err error - name := c.Param("name") - availablePath := filepath.Join(nginx.GetNginxConfPath("sites-available"), name) - enabledPath := filepath.Join(nginx.GetNginxConfPath("sites-enabled"), name) - - if _, err = os.Stat(availablePath); os.IsNotExist(err) { - c.JSON(http.StatusNotFound, gin.H{ - "message": "site not found", - }) - return - } - - if _, err = os.Stat(enabledPath); err == nil { - c.JSON(http.StatusNotAcceptable, gin.H{ - "message": "site is enabled", - }) - return - } - - certModel := model.Cert{Domain: name} - _ = certModel.Remove() - - err = os.Remove(availablePath) - - if err != nil { - ErrHandler(c, err) - return - } - - c.JSON(http.StatusOK, gin.H{ - "message": "ok", - }) + var err error + name := c.Param("name") + availablePath := nginx.GetConfPath("sites-available", name) + enabledPath := nginx.GetConfPath("sites-enabled", name) + + if _, err = os.Stat(availablePath); os.IsNotExist(err) { + c.JSON(http.StatusNotFound, gin.H{ + "message": "site not found", + }) + return + } + + if _, err = os.Stat(enabledPath); err == nil { + c.JSON(http.StatusNotAcceptable, gin.H{ + "message": "site is enabled", + }) + return + } + + certModel := model.Cert{Domain: name} + _ = certModel.Remove() + + err = os.Remove(availablePath) + + if err != nil { + ErrHandler(c, err) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + }) } func AddDomainToAutoCert(c *gin.Context) { - domain := c.Param("domain") - domain = strings.ReplaceAll(domain, " ", "_") - certModel, err := model.FirstOrCreateCert(domain) + domain := c.Param("domain") + domain = strings.ReplaceAll(domain, " ", "_") + certModel, err := model.FirstOrCreateCert(domain) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - err = certModel.Updates(&model.Cert{ - AutoCert: model.AutoCertEnabled, - }) + err = certModel.Updates(&model.Cert{ + AutoCert: model.AutoCertEnabled, + }) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - c.JSON(http.StatusOK, certModel) + c.JSON(http.StatusOK, certModel) } func RemoveDomainFromAutoCert(c *gin.Context) { - domain := c.Param("domain") - domain = strings.ReplaceAll(domain, " ", "_") - certModel := model.Cert{ - Domain: domain, - } - - err := certModel.Updates(&model.Cert{ - AutoCert: model.AutoCertDisabled, - }) - - if err != nil { - ErrHandler(c, err) - return - } - c.JSON(http.StatusOK, nil) + domain := c.Param("domain") + domain = strings.ReplaceAll(domain, " ", "_") + certModel := model.Cert{ + Domain: domain, + } + + err := certModel.Updates(&model.Cert{ + AutoCert: model.AutoCertDisabled, + }) + + if err != nil { + ErrHandler(c, err) + return + } + c.JSON(http.StatusOK, nil) } diff --git a/server/api/nginx_log.go b/server/api/nginx_log.go index c6ec32d7..9997df52 100644 --- a/server/api/nginx_log.go +++ b/server/api/nginx_log.go @@ -13,7 +13,6 @@ import ( "log" "net/http" "os" - "path/filepath" ) const ( @@ -107,7 +106,7 @@ func getLogPath(control *controlStruct) (logPath string, err error) { switch control.Type { case "site": var config *nginx.NgxConfig - path := filepath.Join(nginx.GetNginxConfPath("sites-available"), control.ConfName) + path := nginx.GetConfPath("sites-available", control.ConfName) config, err = nginx.ParseNgxConfig(path) if err != nil { err = errors.Wrap(err, "error parsing ngx config") diff --git a/server/model/cert.go b/server/model/cert.go index 86f49e3d..ece89584 100644 --- a/server/model/cert.go +++ b/server/model/cert.go @@ -3,7 +3,6 @@ package model import ( "github.com/0xJacky/Nginx-UI/server/pkg/nginx" "os" - "path/filepath" ) const ( @@ -38,7 +37,7 @@ func GetAutoCertList() (c []Cert) { db.Where("auto_cert", AutoCertEnabled).Find(&t) // check if this domain is enabled - enabledConfig, err := os.ReadDir(filepath.Join(nginx.GetNginxConfPath("sites-enabled"))) + enabledConfig, err := os.ReadDir(nginx.GetConfPath("sites-enabled")) if err != nil { return diff --git a/server/pkg/cert/cert.go b/server/pkg/cert/cert.go index c8a20b11..7979c52d 100644 --- a/server/pkg/cert/cert.go +++ b/server/pkg/cert/cert.go @@ -110,7 +110,7 @@ func IssueCert(domain []string, logChan chan string, errChan chan error) { return } name := strings.Join(domain, "_") - saveDir := nginx.GetNginxConfPath("ssl/" + name) + saveDir := nginx.GetConfPath("ssl/" + name) if _, err = os.Stat(saveDir); os.IsNotExist(err) { err = os.MkdirAll(saveDir, 0755) if err != nil { @@ -142,7 +142,7 @@ func IssueCert(domain []string, logChan chan string, errChan chan error) { close(errChan) logChan <- "Reloading nginx" - nginx.ReloadNginx() + nginx.Reload() logChan <- "Finished" } diff --git a/server/pkg/nginx/nginx.go b/server/pkg/nginx/nginx.go index 88396432..d13c4f75 100644 --- a/server/pkg/nginx/nginx.go +++ b/server/pkg/nginx/nginx.go @@ -1,56 +1,64 @@ package nginx import ( - "errors" - "log" - "os/exec" - "path/filepath" - "regexp" - "strings" + "errors" + "github.com/0xJacky/Nginx-UI/server/settings" + "log" + "os/exec" + "path/filepath" + "regexp" + "strings" ) -func TestNginxConf() error { - out, err := exec.Command("nginx", "-t").CombinedOutput() - if err != nil { - log.Println(err) - } - output := string(out) - log.Println(output) - if strings.Contains(output, "failed") { - return errors.New(output) - } - return nil +func TestConf() error { + out, err := exec.Command("nginx", "-t").CombinedOutput() + if err != nil { + log.Println(err) + } + output := string(out) + log.Println(output) + if strings.Contains(output, "failed") { + return errors.New(output) + } + return nil } -func ReloadNginx() string { - out, err := exec.Command("nginx", "-s", "reload").CombinedOutput() +func Reload() string { + out, err := exec.Command("nginx", "-s", "reload").CombinedOutput() - if err != nil { - log.Println(err) - return err.Error() - } + if err != nil { + log.Println(err) + return err.Error() + } - output := string(out) - log.Println(output) - if strings.Contains(output, "failed") { - return output - } - return "" + output := string(out) + log.Println(output) + if strings.Contains(output, "failed") { + return output + } + return "" } -func GetNginxConfPath(dir string) string { - out, err := exec.Command("nginx", "-V").CombinedOutput() - if err != nil { - log.Println(err) - return "" - } - // fmt.Printf("%s\n", out) +func GetConfPath(dir ...string) string { - r, _ := regexp.Compile("--conf-path=(.*)/(.*.conf)") + var confPath string - confPath := r.FindStringSubmatch(string(out))[1] + if settings.ServerSettings.NginxConfigDir == "" { + out, err := exec.Command("nginx", "-V").CombinedOutput() + if err != nil { + log.Println("nginx.GetConfPath exec.Command error", err) + return "" + } + r, _ := regexp.Compile("--conf-path=(.*)/(.*.conf)") + match := r.FindStringSubmatch(string(out)) + if len(match) < 1 { + log.Println("nginx.GetConfPath len(match) < 1") + return "" + } + confPath = r.FindStringSubmatch(string(out))[1] + } else { + confPath = settings.ServerSettings.NginxConfigDir + } - // fmt.Println(confPath) - - return filepath.Join(confPath, dir) + return filepath.Join(confPath, filepath.Join(dir...)) } diff --git a/server/settings/settings.go b/server/settings/settings.go index 059f3915..02657b1f 100644 --- a/server/settings/settings.go +++ b/server/settings/settings.go @@ -27,6 +27,7 @@ type Server struct { Demo bool `json:"demo"` PageSize int `json:"page_size"` GithubProxy string `json:"github_proxy"` + NginxConfigDir string `json:"nginx_config_dir"` } type NginxLog struct { diff --git a/server/test/acme_test.go b/server/test/acme_test.go index 3000e2d3..3832ed31 100644 --- a/server/test/acme_test.go +++ b/server/test/acme_test.go @@ -46,7 +46,7 @@ func TestAcme(t *testing.T) { "install", "--log", "--home", "/usr/local/acme.sh", - "--cert-home", nginx.GetNginxConfPath("ssl")). + "--cert-home", nginx.GetConfPath("ssl")). CombinedOutput() if err != nil { log.Println(err) diff --git a/server/test/cert_test.go b/server/test/cert_test.go index 7ca61207..6b9c8fac 100644 --- a/server/test/cert_test.go +++ b/server/test/cert_test.go @@ -20,14 +20,14 @@ func TestCert(t *testing.T) { } fmt.Printf("%s\n", out) - _, err = os.Stat(nginx.GetNginxConfPath("ssl/test.ojbk.me/fullchain.cer")) + _, err = os.Stat(nginx.GetConfPath("ssl/test.ojbk.me/fullchain.cer")) if err != nil { log.Println(err) return } log.Println("[found]", "fullchain.cer") - _, err = os.Stat(nginx.GetNginxConfPath("ssl/test.ojbk.me/test.ojbk.me.key")) + _, err = os.Stat(nginx.GetConfPath("ssl/test.ojbk.me/test.ojbk.me.key")) if err != nil { log.Println(err)