From 65b192c8beab8dc74388b44e142023dd97fedfd1 Mon Sep 17 00:00:00 2001 From: 0xJacky Date: Mon, 2 Jan 2023 18:38:40 +0800 Subject: [PATCH] feat: manage config support dir --- frontend/src/routes/index.ts | 12 +- frontend/src/views/config/Config.vue | 67 ++++-- frontend/src/views/config/ConfigEdit.vue | 12 +- frontend/src/views/config/config.tsx | 41 ++++ server/api/config.go | 261 +++++++++++------------ 5 files changed, 231 insertions(+), 162 deletions(-) create mode 100644 frontend/src/views/config/config.tsx diff --git a/frontend/src/routes/index.ts b/frontend/src/routes/index.ts index 68cce2cd..2c843213 100644 --- a/frontend/src/routes/index.ts +++ b/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: { diff --git a/frontend/src/views/config/Config.vue b/frontend/src/views/config/Config.vue index ebe02dcf..6f09d4ae 100644 --- a/frontend/src/views/config/Config.vue +++ b/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++ +}) + diff --git a/frontend/src/views/config/ConfigEdit.vue b/frontend/src/views/config/ConfigEdit.vue index c3746edf..18c4dd96 100644 --- a/frontend/src/views/config/ConfigEdit.vue +++ b/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() { - Cancel + Back Save diff --git a/frontend/src/views/config/config.tsx b/frontend/src/views/config/config.tsx new file mode 100644 index 00000000..662a77e7 --- /dev/null +++ b/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 diff --git a/server/api/config.go b/server/api/config.go index cd2e395a..ebc24a92 100644 --- a/server/api/config.go +++ b/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") + orderBy := c.Query("order_by") + sort := c.DefaultQuery("sort", "desc") + dir := c.DefaultQuery("dir", "/") - mySort := map[string]string{ - "name": "string", - "modify": "time", - } + mySort := map[string]string{ + "name": "string", + "modify": "time", + "is_dir": "bool", + } - configFiles, err := os.ReadDir(nginx.GetNginxConfPath("/")) + configFiles, err := os.ReadDir(nginx.GetNginxConfPath(dir)) - if err != nil { - ErrHandler(c, err) - return - } + if err != nil { + ErrHandler(c, err) + return + } - var configs []gin.H + var configs []gin.H - for i := range configFiles { - file := configFiles[i] - fileInfo, _ := file.Info() + 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 - } + 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(), + "is_dir": file.IsDir(), + }) + } - configs = append(configs, gin.H{ - "name": file.Name(), - "size": fileInfo.Size(), - "modify": fileInfo.ModTime(), - }) - } + configs = config_list.Sort(orderBy, sort, mySort[orderBy], configs) - configs = config_list.Sort(orderBy, sort, mySort[orderBy], configs) - - c.JSON(http.StatusOK, gin.H{ - "data": 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 - } + var request AddConfigJson + err := c.BindJSON(&request) + if err != nil { + ErrHandler(c, err) + return + } - name := request.Name - content := request.Content + name := request.Name + content := request.Content - path := filepath.Join(nginx.GetNginxConfPath("/"), name) + 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 _, 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 - } - } + if content != "" { + err = os.WriteFile(path, []byte(content), 0644) + if err != nil { + ErrHandler(c, err) + return + } + } - output := nginx.ReloadNginx() + output := nginx.ReloadNginx() - if output != "" && strings.Contains(output, "error") { - c.JSON(http.StatusInternalServerError, gin.H{ - "message": output, - }) - return - } + 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, - }) + 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 + 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 - } + 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 - } - } + 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() + output := nginx.ReloadNginx() - if output != "" && strings.Contains(output, "error") { - c.JSON(http.StatusInternalServerError, gin.H{ - "message": output, - }) - return - } + if output != "" && strings.Contains(output, "error") { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": output, + }) + return + } - GetConfig(c) + GetConfig(c) }