فهرست منبع

feat: manage config support dir

0xJacky 2 سال پیش
والد
کامیت
65b192c8be

+ 11 - 1
frontend/src/routes/index.ts

@@ -74,7 +74,17 @@ export const routes = [
                 }
             },
             {
-                path: 'config/:name',
+                path: 'config/:dir*',
+                name: () => $gettext('Manage Configs'),
+                component: () => import('@/views/config/Config.vue'),
+                meta: {
+                    icon: FileOutlined,
+                    hideChildren: true,
+                    hiddenInSidebar: true
+                }
+            },
+            {
+                path: 'config/:name+/edit',
                 name: () => $gettext('Edit Configuration'),
                 component: () => import('@/views/config/ConfigEdit.vue'),
                 meta: {

+ 45 - 22
frontend/src/views/config/Config.vue

@@ -2,43 +2,66 @@
 import StdTable from '@/components/StdDataDisplay/StdTable.vue'
 import gettext from '@/gettext'
 import config from '@/api/config'
-import {datetime} from '@/components/StdDataDisplay/StdTableTransformer'
+import {customRender, datetime} from '@/components/StdDataDisplay/StdTableTransformer'
+import {computed, h, nextTick, ref, watch} from 'vue'
 
 const {$gettext} = gettext
 
 const api = config
 
-const columns = [{
-    title: () => $gettext('Name'),
-    dataIndex: 'name',
-    sorter: true,
-    pithy: true
-}, {
-    title: () => $gettext('Updated at'),
-    dataIndex: 'modify',
-    customRender: datetime,
-    datetime: true,
-    sorter: true,
-    pithy: true
-}, {
-    title: () => $gettext('Action'),
-    dataIndex: 'action'
-}]
+import configColumns from '@/views/config/config'
+import {useRoute} from 'vue-router'
+import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue'
+import router from '@/routes'
+
+const table = ref(null)
+const route = useRoute()
+
+const basePath = computed(() => {
+    let dir = route?.params?.dir ? (route?.params?.dir as string[])?.join('/') : ''
+    if (dir) dir += '/'
+    return dir
+})
+
+const get_params = computed(() => {
+    return {
+        dir: basePath.value
+    }
+})
+
+const update = ref(1)
+
+watch(get_params, () => {
+    update.value++
+})
 </script>
+
 <template>
     <a-card :title="$gettext('Configurations')">
         <std-table
+            :key="update"
+            ref="table"
             :api="api"
-            :columns="columns"
+            :columns="configColumns"
             :deletable="false"
             :disable_search="true"
             row-key="name"
-            @clickEdit="r => {
-                $router.push({
-                    path: '/config/' + r
-                })
+            :get_params="get_params"
+            @clickEdit="(r, row) => {
+                if (!row.is_dir) {
+                    $router.push({
+                        path: '/config/' + basePath + r + '/edit'
+                    })
+                } else {
+                    $router.push({
+                        path: '/config/' + basePath + r
+                    })
+                }
             }"
         />
+        <footer-tool-bar v-if="basePath">
+            <a-button @click="router.go(-1)">{{ $gettext('Back') }}</a-button>
+        </footer-tool-bar>
     </a-card>
 </template>
 

+ 9 - 3
frontend/src/views/config/ConfigEdit.vue

@@ -2,7 +2,7 @@
 import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue'
 import gettext from '@/gettext'
 import {useRoute} from 'vue-router'
-import {ref} from 'vue'
+import {computed, ref} from 'vue'
 import config from '@/api/config'
 import {message} from 'ant-design-vue'
 import CodeEditor from '@/components/CodeEditor/CodeEditor.vue'
@@ -10,7 +10,13 @@ import CodeEditor from '@/components/CodeEditor/CodeEditor.vue'
 const {$gettext, interpolate} = gettext
 const route = useRoute()
 
-const name = ref(route.params.name)
+const name = computed(() => {
+    const n = route.params.name
+    if (typeof n === 'string') {
+        return n
+    }
+    return n?.join('/')
+})
 
 const configText = ref('')
 
@@ -46,7 +52,7 @@ function save() {
         <footer-tool-bar>
             <a-space>
                 <a-button @click="$router.go(-1)">
-                    <translate>Cancel</translate>
+                    <translate>Back</translate>
                 </a-button>
                 <a-button type="primary" @click="save">
                     <translate>Save</translate>

+ 41 - 0
frontend/src/views/config/config.tsx

@@ -0,0 +1,41 @@
+import {customRender, datetime} from '@/components/StdDataDisplay/StdTableTransformer'
+import gettext from '@/gettext'
+
+const {$gettext} = gettext
+
+import {Badge} from 'ant-design-vue'
+import {h} from 'vue'
+
+const configColumns = [{
+    title: () => $gettext('Name'),
+    dataIndex: 'name',
+    sorter: true,
+    pithy: true
+}, {
+    title: () => $gettext('Type'),
+    dataIndex: 'is_dir',
+    customRender: (args: customRender) => {
+        const template: any = []
+        const {text, column} = args
+        if (text === true || text > 0) {
+            template.push($gettext('Dir'))
+        } else {
+            template.push($gettext('File'))
+        }
+        return h('div', template)
+    },
+    sorter: true,
+    pithy: true
+}, {
+    title: () => $gettext('Updated at'),
+    dataIndex: 'modify',
+    customRender: datetime,
+    datetime: true,
+    sorter: true,
+    pithy: true
+}, {
+    title: () => $gettext('Action'),
+    dataIndex: 'action'
+}]
+
+export default configColumns

+ 146 - 157
server/api/config.go

@@ -1,184 +1,173 @@
 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"
+    "path/filepath"
+    "strings"
 )
 
 func GetConfigs(c *gin.Context) {
-	orderBy := c.Query("order_by")
-	sort := c.DefaultQuery("sort", "desc")
-
-	mySort := map[string]string{
-		"name":   "string",
-		"modify": "time",
-	}
-
-	configFiles, err := os.ReadDir(nginx.GetNginxConfPath("/"))
-
-	if err != nil {
-		ErrHandler(c, err)
-		return
-	}
-
-	var configs []gin.H
-
-	for i := range configFiles {
-		file := configFiles[i]
-		fileInfo, _ := file.Info()
-
-		switch mode := fileInfo.Mode(); {
-		case mode.IsRegular(): // regular file, not a hidden file
-			if "." == file.Name()[0:1] {
-				continue
-			}
-		case mode&os.ModeSymlink != 0: // is a symbol
-			var targetPath string
-			targetPath, err = os.Readlink(nginx.GetNginxConfPath(file.Name()))
-			if err != nil {
-				log.Println("GetConfigs Read Symlink Error", targetPath, err)
-				continue
-			}
-
-			var targetInfo os.FileInfo
-			targetInfo, err = os.Stat(targetPath)
-			if err != nil {
-				log.Println("GetConfigs Stat Error", targetPath, err)
-				continue
-			}
-			// but target file is not a dir
-			if targetInfo.IsDir() {
-				continue
-			}
-		default:
-			continue
-		}
-
-		configs = append(configs, gin.H{
-			"name":   file.Name(),
-			"size":   fileInfo.Size(),
-			"modify": fileInfo.ModTime(),
-		})
-	}
-
-	configs = config_list.Sort(orderBy, sort, mySort[orderBy], configs)
-
-	c.JSON(http.StatusOK, gin.H{
-		"data": configs,
-	})
+    orderBy := c.Query("order_by")
+    sort := c.DefaultQuery("sort", "desc")
+    dir := c.DefaultQuery("dir", "/")
+
+    mySort := map[string]string{
+        "name":   "string",
+        "modify": "time",
+        "is_dir": "bool",
+    }
+
+    configFiles, err := os.ReadDir(nginx.GetNginxConfPath(dir))
+
+    if err != nil {
+        ErrHandler(c, err)
+        return
+    }
+
+    var configs []gin.H
+
+    for i := range configFiles {
+        file := configFiles[i]
+        fileInfo, _ := file.Info()
+
+        switch mode := fileInfo.Mode(); {
+        case mode.IsRegular(): // regular file, not a hidden file
+            if "." == file.Name()[0:1] {
+                continue
+            }
+        case mode&os.ModeSymlink != 0: // is a symbol
+            var targetPath string
+            targetPath, err = os.Readlink(nginx.GetNginxConfPath(file.Name()))
+            if err != nil {
+                log.Println("GetConfigs Read Symlink Error", targetPath, err)
+                continue
+            }
+        }
+
+        configs = append(configs, gin.H{
+            "name":   file.Name(),
+            "size":   fileInfo.Size(),
+            "modify": fileInfo.ModTime(),
+            "is_dir": file.IsDir(),
+        })
+    }
+
+    configs = config_list.Sort(orderBy, sort, mySort[orderBy], configs)
+
+    c.JSON(http.StatusOK, gin.H{
+        "data": configs,
+    })
 }
 
 func GetConfig(c *gin.Context) {
-	name := c.Param("name")
-	path := filepath.Join(nginx.GetNginxConfPath("/"), name)
+    name := c.Param("name")
+    path := filepath.Join(nginx.GetNginxConfPath("/"), name)
 
-	content, err := os.ReadFile(path)
+    content, err := os.ReadFile(path)
 
-	if err != nil {
-		ErrHandler(c, err)
-		return
-	}
+    if err != nil {
+        ErrHandler(c, err)
+        return
+    }
 
-	c.JSON(http.StatusOK, gin.H{
-		"config": string(content),
-	})
+    c.JSON(http.StatusOK, gin.H{
+        "config": string(content),
+    })
 
 }
 
 type AddConfigJson struct {
-	Name    string `json:"name" binding:"required"`
-	Content string `json:"content" binding:"required"`
+    Name    string `json:"name" binding:"required"`
+    Content string `json:"content" binding:"required"`
 }
 
 func AddConfig(c *gin.Context) {
-	var request AddConfigJson
-	err := c.BindJSON(&request)
-	if err != nil {
-		ErrHandler(c, err)
-		return
-	}
-
-	name := request.Name
-	content := request.Content
-
-	path := filepath.Join(nginx.GetNginxConfPath("/"), name)
-
-	log.Println(path)
-	if _, err = os.Stat(path); err == nil {
-		c.JSON(http.StatusNotAcceptable, gin.H{
-			"message": "config exist",
-		})
-		return
-	}
-
-	if content != "" {
-		err = os.WriteFile(path, []byte(content), 0644)
-		if err != nil {
-			ErrHandler(c, err)
-			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{
-		"name":    name,
-		"content": content,
-	})
+    var request AddConfigJson
+    err := c.BindJSON(&request)
+    if err != nil {
+        ErrHandler(c, err)
+        return
+    }
+
+    name := request.Name
+    content := request.Content
+
+    path := filepath.Join(nginx.GetNginxConfPath("/"), name)
+
+    if _, err = os.Stat(path); err == nil {
+        c.JSON(http.StatusNotAcceptable, gin.H{
+            "message": "config exist",
+        })
+        return
+    }
+
+    if content != "" {
+        err = os.WriteFile(path, []byte(content), 0644)
+        if err != nil {
+            ErrHandler(c, err)
+            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{
+        "name":    name,
+        "content": content,
+    })
 
 }
 
 type EditConfigJson struct {
-	Content string `json:"content" binding:"required"`
+    Content string `json:"content" binding:"required"`
 }
 
 func EditConfig(c *gin.Context) {
-	name := c.Param("name")
-	var request EditConfigJson
-	err := c.BindJSON(&request)
-	if err != nil {
-		ErrHandler(c, err)
-		return
-	}
-	path := filepath.Join(nginx.GetNginxConfPath("/"), name)
-	content := request.Content
-
-	origContent, err := os.ReadFile(path)
-	if err != nil {
-		ErrHandler(c, err)
-		return
-	}
-
-	if content != "" && content != string(origContent) {
-		// model.CreateBackup(path)
-		err = os.WriteFile(path, []byte(content), 0644)
-		if err != nil {
-			ErrHandler(c, err)
-			return
-		}
-	}
-
-	output := nginx.ReloadNginx()
-
-	if output != "" && strings.Contains(output, "error") {
-		c.JSON(http.StatusInternalServerError, gin.H{
-			"message": output,
-		})
-		return
-	}
-
-	GetConfig(c)
+    name := c.Param("name")
+    var request EditConfigJson
+    err := c.BindJSON(&request)
+    if err != nil {
+        ErrHandler(c, err)
+        return
+    }
+    path := filepath.Join(nginx.GetNginxConfPath("/"), name)
+    content := request.Content
+
+    origContent, err := os.ReadFile(path)
+    if err != nil {
+        ErrHandler(c, err)
+        return
+    }
+
+    if content != "" && content != string(origContent) {
+        // model.CreateBackup(path)
+        err = os.WriteFile(path, []byte(content), 0644)
+        if err != nil {
+            ErrHandler(c, err)
+            return
+        }
+    }
+
+    output := nginx.ReloadNginx()
+
+    if output != "" && strings.Contains(output, "error") {
+        c.JSON(http.StatusInternalServerError, gin.H{
+            "message": output,
+        })
+        return
+    }
+
+    GetConfig(c)
 }