Browse Source

Merge branch 'embed' of https://github.com/0xJacky/nginx-ui into embed

0xJacky 3 years ago
parent
commit
72739fb07f

+ 93 - 0
README-zh_CN.md

@@ -0,0 +1,93 @@
+# Nginx UI
+Yet another Nginx Web UI
+
+Version: 1.1.0
+
+[For English](README.md)
+
+## 项目特色
+
+1. 可在线查看服务器 CPU、内存、load average、磁盘使用率等指标
+2. 可一键申请 Let's encrypt 证书
+3. 可自动续签 Let's encrypt 证书
+4. 在线编辑网站配置文件
+
+## 项目预览
+
+### 登录
+![](resources/screenshots/login.png)
+
+### 仪表盘
+![](resources/screenshots/dashboard.png)
+
+### 用户列表
+![](resources/screenshots/user-list.png)
+
+### 域名列表
+![](resources/screenshots/domain-list.png)
+
+### 域名编辑
+![](resources/screenshots/domain-edit.png)
+
+### 配置列表
+![](resources/screenshots/config-list.png)
+
+### 配置编辑
+![](resources/screenshots/config-edit.png)
+
+## 使用前注意
+
+Nginx UI 遵循 Nginx 的标准,创建的网站配置文件位于 Nginx 配置目录(自动检测)下的 `sites-available` 目录,
+启用后的网站的配置文件将会创建一份软连接到 `sites-enabled` 目录中。因此,您可能需要调整配置文件的组织方式。
+
+## 安装
+1. 克隆项目
+```
+git clone https://github.com/0xJacky/nginx-ui
+```
+2. 编译后端
+```
+cd server
+go build -o nginx-ui-server main.go
+```
+3. 启动后端
+    1. 前台启动 `./nginx-ui-server`
+    2. 后台启动 `nohup ./nginx-ui-server &`
+
+4. 添加配置文件到 nginx
+```
+server {
+    listen	80;
+    listen	[::]:80;
+
+    server_name	<your_server_name>;
+    rewrite ^(.*)$  https://$host$1 permanent;
+}
+
+server {
+    listen	443 ssl http2;
+    listen	[::]:443 ssl http2;
+
+    server_name	<your_server_name>;
+
+    ssl_certificate	/path/to/ssl_cert;
+    ssl_certificate_key	/path/to/ssl_cert_key;
+
+    location / {
+        proxy_set_header Host $host;
+        proxy_set_header   X-Real-IP            $remote_addr;
+        proxy_set_header   X-Forwarded-For      $proxy_add_x_forwarded_for;
+        proxy_set_header   X-Forwarded-Proto    $scheme;
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection upgrade;
+        proxy_pass http://127.0.0.1:9000/;
+    }
+}
+```
+
+4. 初始化系统
+
+在浏览器中访问 `https://<your_server_name>/install`
+
+输入用户名和密码创建初始账户。

+ 38 - 33
README.md

@@ -3,56 +3,60 @@ Yet another Nginx Web UI
 
 Version: 1.1.0
 
-## 项目特色
+*Note: Currently only available in Simplified Chinese.*
 
-1. 可在线查看服务器 CPU、内存、load average、磁盘使用率等指标
-2. 可一键申请 Let's encrypt 证书
-3. 可自动续签 Let's encrypt 证书
-4. 在线编辑网站配置文件
+[简体中文说明](README-zh_CN.md)
 
-## 项目预览
+## Features
 
-### 登录
-![](screenshots/login.png)
+1. Online view of server CPU, Memory, Load Average, Disk Usage and other indicators.
+2. One-click deployment Let's Encrypt certificates.
+3. Automatic renewal Let's Encrypt certificates.
+4. Online editing websites configuration files.
 
-### 仪表盘
-![](screenshots/dashboard.png)
+## Screenshots
 
-### 用户列表
-![](screenshots/user-list.png)
+### Login
+![](resources/screenshots/login.png)
 
-### 域名列表
-![](screenshots/domain-list.png)
+### Dashboard
+![](resources/screenshots/dashboard.png)
 
-### 域名编辑
-![](screenshots/domain-edit.png)
+### Users Management
+![](resources/screenshots/user-list.png)
 
-### 配置列表
-![](screenshots/config-list.png)
+### Domains Management
+![](resources/screenshots/domain-list.png)
 
-### 配置编辑
-![](screenshots/config-edit.png)
+### Domain Editor
+![](resources/screenshots/domain-edit.png)
 
-## 使用前注意
+### Configurations Management
+![](resources/screenshots/config-list.png)
 
-Nginx UI 遵循 Nginx 的标准,创建的网站配置文件位于 Nginx 配置目录(自动检测)下的 `sites-available` 目录,
-启用后的网站的配置文件将会创建一份软连接到 `sites-enabled` 目录中。因此,您可能需要调整配置文件的组织方式。
+### Configuration Editor
+![](resources/screenshots/config-edit.png)
 
-## 安装
-1. 克隆项目
+## Note Before Use
+
+The Nginx UI follows the Nginx standard of creating site configuration files in the `sites-available` directory under the Nginx configuration directory (auto-detected).
+The configuration files for an enabled site will create a soft link to the `sites-enabled` directory. Therefore, you may need to adjust the way the configuration files are organised.
+
+## Install
+1. Clone
 ```
 git clone https://github.com/0xJacky/nginx-ui
 ```
-2. 编译后端
+2. Compiling the backend
 ```
 cd server
 go build -o nginx-ui-server main.go
 ```
-3. 启动后端
-    1. 前台启动 `./nginx-ui-server`
-    2. 后台启动 `nohup ./nginx-ui-server &`
+3. Start up the backend
+    1.  `./nginx-ui-server` for direct run.
+    2.  `nohup ./nginx-ui-server &` for run as service.
 
-4. 添加配置文件到 nginx
+4. Adding a configuration file to nginx
 ```
 server {
     listen	80;
@@ -84,8 +88,9 @@ server {
 }
 ```
 
-4. 初始化系统
+4. Installation
+
+Visit `https://<your_server_name>/install` in your browser.
 
-在浏览器中访问 `https://<your_server_name>/install`
+Enter your username and password to create initial account.
 
-输入用户名和密码创建初始账户。

+ 1 - 1
frontend/src/lib/utils/index.js

@@ -36,7 +36,7 @@ export default {
             if (process.env.NODE_ENV === 'development' && process.env["VUE_APP_API_WSS_ROOT"]) {
                 return process.env["VUE_APP_API_WSS_ROOT"]
             }
-            return protocol + location.host + '/' + process.env["VUE_APP_API_WSS_ROOT"]
+            return protocol + location.host + process.env["VUE_APP_API_WSS_ROOT"]
         }
     }
 }

+ 15 - 4
frontend/src/views/other/Install.vue

@@ -22,7 +22,7 @@
                 message: 'Please input your E-mail!',
               },] },
         ]"
-                    placeholder="Email"
+                    placeholder="Email (*)"
                 >
                     <a-icon slot="prefix" type="mail" style="color: rgba(0,0,0,.25)"/>
                 </a-input>
@@ -33,7 +33,7 @@
           'username',
           { rules: [{ required: true, message: 'Please input your username!' }] },
         ]"
-                    placeholder="Username"
+                    placeholder="Username (*)"
                 >
                     <a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)"/>
                 </a-input>
@@ -45,11 +45,22 @@
           { rules: [{ required: true, message: 'Please input your Password!' }] },
         ]"
                     type="password"
-                    placeholder="Password"
+                    placeholder="Password (*)"
                 >
                     <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)"/>
                 </a-input>
             </a-form-item>
+            <a-form-item>
+                <a-input
+                    v-decorator="[
+          'database',
+          { rules: [{ pattern: /^[^\\/:*?\x22<>|]{1,120}$/, message: 'Please input a legal file name!'}] },
+        ]"
+                    placeholder="Database (Optional, default: database)"
+                >
+                    <a-icon slot="prefix" type="database" style="color: rgba(0,0,0,.25)"/>
+                </a-input>
+            </a-form-item>
             <a-form-item>
                 <a-button type="primary" :block="true" html-type="submit" :loading="loading">
                     安装
@@ -57,7 +68,7 @@
             </a-form-item>
         </a-form>
         <footer>
-            Copyright © 2020 - {{ thisYear }} 0xJacky
+            Copyright © 2020 - {{ thisYear }} Nginx UI
         </footer>
     </div>
 

+ 1 - 1
frontend/src/views/other/Login.vue

@@ -39,7 +39,7 @@
                 </a-form-item>
             </a-form>
             <div class="footer">
-                Copyright © 2020 - {{ thisYear }} 0xJacky
+                Copyright © 2020 - {{ thisYear }} Nginx UI
             </div>
         </div>
     </div>

+ 1 - 1
frontend/version.json

@@ -1 +1 @@
-{"version":"1.1.0","build_id":5,"total_build":22}
+{"version":"1.1.0","build_id":8,"total_build":25}

+ 13 - 8
main.go

@@ -8,6 +8,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/server/settings"
 	tool2 "github.com/0xJacky/Nginx-UI/server/tool"
 	"log"
+	"mime"
 	"net/http"
 	"os/signal"
 	"syscall"
@@ -19,22 +20,26 @@ func main() {
 	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
 	defer stop()
 
-	var dataDir string
-	flag.StringVar(&dataDir, "d", ".", "Specify the data dir")
+	// Hack: fix wrong Content Type of .js file on some OS platforms
+	// See https://github.com/golang/go/issues/32350
+	_ = mime.AddExtensionType(".js", "text/javascript; charset=utf-8")
+
+	var confPath string
+	flag.StringVar(&confPath, "config", "app.ini", "Specify the configuration file")
 	flag.Parse()
 
-	settings.Init(dataDir)
-	model.Init()
+	settings.Init(confPath)
+	log.Printf("nginx config dir path: %s", tool2.GetNginxConfPath(""))
+	if "" != settings.ServerSettings.JwtSecret {
+		model.Init()
+		go tool2.AutoCert()
+	}
 
 	srv := &http.Server{
 		Addr:    ":" + settings.ServerSettings.HttpPort,
 		Handler: router.InitRouter(),
 	}
 
-	log.Printf("nginx config dir path: %s", tool2.GetNginxConfPath(""))
-
-	go tool2.AutoCert()
-
 	// Initializing the server in a goroutine so that
 	// it won't block the graceful shutdown handling below
 	go func() {

+ 1 - 1
nginx-ui.service

@@ -5,7 +5,7 @@ After=network.target
 
 [Service]
 Type=simple
-ExecStart=/usr/local/bin/nginx-ui -d /usr/local/etc/nginx-ui
+ExecStart=/usr/local/bin/nginx-ui -d /usr/local/etc/nginx-ui/app.ini
 Restart=on-failure
 TimeoutStopSec=5
 KillMode=mixed

+ 0 - 0
logo.png → resources/logo.png


+ 0 - 0
nginx-ui-logo-design.sketch → resources/nginx-ui-logo-design.sketch


+ 0 - 0
screenshots/config-edit.png → resources/screenshots/config-edit.png


+ 0 - 0
screenshots/config-list.png → resources/screenshots/config-list.png


+ 0 - 0
screenshots/dashboard.png → resources/screenshots/dashboard.png


+ 0 - 0
screenshots/domain-edit.png → resources/screenshots/domain-edit.png


+ 0 - 0
screenshots/domain-list.png → resources/screenshots/domain-list.png


+ 0 - 0
screenshots/login.png → resources/screenshots/login.png


+ 0 - 0
screenshots/user-list.png → resources/screenshots/user-list.png


+ 17 - 13
server/api/install.go

@@ -1,22 +1,17 @@
 package api
 
 import (
-	model2 "github.com/0xJacky/Nginx-UI/server/model"
+	"github.com/0xJacky/Nginx-UI/server/model"
 	"github.com/0xJacky/Nginx-UI/server/settings"
+	"github.com/0xJacky/Nginx-UI/server/tool"
 	"github.com/gin-gonic/gin"
 	"github.com/google/uuid"
 	"golang.org/x/crypto/bcrypt"
 	"net/http"
-	"os"
-	"path"
 )
 
 func installLockStatus() bool {
-	lockPath := path.Join(settings.DataDir, "app.ini")
-	_, err := os.Stat(lockPath)
-
-	return !os.IsNotExist(err)
-
+	return "" != settings.ServerSettings.JwtSecret
 }
 
 func InstallLockCheck(c *gin.Context) {
@@ -29,6 +24,7 @@ type InstallJson struct {
 	Email    string `json:"email" binding:"required,email"`
 	Username string `json:"username" binding:"required,max=255"`
 	Password string `json:"password" binding:"required,max=255"`
+	Database string `json:"database"`
 }
 
 func InstallNginxUI(c *gin.Context) {
@@ -45,18 +41,26 @@ func InstallNginxUI(c *gin.Context) {
 		return
 	}
 
-	serverSettings := settings.Conf.Section("server")
-	serverSettings.Key("JwtSecret").SetValue(uuid.New().String())
-	serverSettings.Key("Email").SetValue(json.Email)
+	settings.ServerSettings.JwtSecret = uuid.New().String()
+	settings.ServerSettings.Email = json.Email
+	if "" != json.Database {
+		settings.ServerSettings.Database = json.Database
+	}
+	settings.ReflectFrom()
+
 	err := settings.Save()
 	if err != nil {
 		ErrHandler(c, err)
 		return
 	}
 
-	curd := model2.NewCurd(&model2.Auth{})
+	// Init model and auto cert
+	model.Init()
+	go tool.AutoCert()
+
+	curd := model.NewCurd(&model.Auth{})
 	pwd, _ := bcrypt.GenerateFromPassword([]byte(json.Password), bcrypt.DefaultCost)
-	err = curd.Add(&model2.Auth{
+	err = curd.Add(&model.Auth{
 		Name:     json.Username,
 		Password: string(pwd),
 	})

+ 2 - 1
server/model/models.go

@@ -1,6 +1,7 @@
 package model
 
 import (
+	"fmt"
 	"github.com/0xJacky/Nginx-UI/server/settings"
 	"gorm.io/driver/sqlite"
 	"gorm.io/gorm"
@@ -20,7 +21,7 @@ type Model struct {
 }
 
 func Init() {
-	dbPath := path.Join(settings.DataDir, "database.db")
+	dbPath := path.Join(path.Dir(settings.ConfPath), fmt.Sprintf("%s.db", settings.ServerSettings.Database))
 	var err error
 	db, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{
 		Logger:      logger.Default.LogMode(logger.Info),

+ 2 - 1
server/router/routers.go

@@ -3,6 +3,7 @@ package router
 import (
 	"bufio"
 	api2 "github.com/0xJacky/Nginx-UI/server/api"
+	"github.com/gin-contrib/static"
 	"github.com/gin-gonic/gin"
 	"net/http"
 	"strings"
@@ -14,7 +15,7 @@ func InitRouter() *gin.Engine {
 
 	r.Use(gin.Recovery())
 
-	r.Use(tryStatic("/", mustFS("")))
+	r.Use(static.Serve("/", mustFS("")))
 
 	r.NoRoute(func(c *gin.Context) {
 		accept := c.Request.Header.Get("Accept")

+ 49 - 28
server/settings/settings.go

@@ -1,10 +1,8 @@
 package settings
 
 import (
-    "gopkg.in/ini.v1"
-    "log"
-    "os"
-    "path"
+	"gopkg.in/ini.v1"
+	"log"
 )
 
 var Conf *ini.File
@@ -16,30 +14,47 @@ type Server struct {
 	JwtSecret         string
 	HTTPChallengePort string
 	Email             string
+	Database          string
 }
 
-var ServerSettings = &Server{}
+var ServerSettings = &Server{
+	HttpPort:          "9000",
+	RunMode:           "debug",
+	HTTPChallengePort: "9180",
+	Database:          "database",
+}
+
+var ConfPath string
+
+var sections = map[string]interface{}{
+	"server": ServerSettings,
+}
 
-var DataDir string
-var confPath string
+func Init(confPath string) {
+	ConfPath = confPath
+	Setup()
+}
 
-func Init(dataDir string)  {
-    DataDir = dataDir
-    confPath = path.Join(dataDir, "app.ini")
-    if _, err := os.Stat(confPath); os.IsNotExist(err) {
-        confPath = path.Join(dataDir, "app.example.ini")
-    }
-    Setup()
+func Setup() {
+	var err error
+	Conf, err = ini.LooseLoad(ConfPath)
+	if err != nil {
+		log.Printf("setting.Setup: %v", err)
+	} else {
+		MapTo()
+	}
 }
 
-func Setup()  {
-    var err error
-    Conf, err = ini.Load(confPath)
-    if err != nil {
-        log.Fatalf("setting.Setup, fail to parse '%s': %v", confPath, err)
-    }
+func MapTo() {
+	for k, v := range sections {
+		mapTo(k, v)
+	}
+}
 
-    mapTo("server", ServerSettings)
+func ReflectFrom() {
+	for k, v := range sections {
+		reflectFrom(k, v)
+	}
 }
 
 func mapTo(section string, v interface{}) {
@@ -49,12 +64,18 @@ func mapTo(section string, v interface{}) {
 	}
 }
 
+func reflectFrom(section string, v interface{}) {
+	err := Conf.Section(section).ReflectFrom(v)
+	if err != nil {
+		log.Fatalf("Cfg.ReflectFrom %s err: %v", section, err)
+	}
+}
+
 func Save() (err error) {
-    confPath = path.Join(DataDir, "app.ini")
-    err = Conf.SaveTo(confPath)
-    if err != nil {
-        return
-    }
-    Setup()
-    return
+	err = Conf.SaveTo(ConfPath)
+	if err != nil {
+		return
+	}
+	Setup()
+	return
 }