Browse Source

feat: read sites access log and error log

0xJacky 2 năm trước cách đây
mục cha
commit
28168197b1

+ 7 - 0
frontend/src/routes/index.ts

@@ -102,6 +102,13 @@ export const routes = [
                     path: 'error',
                     name: () => $gettext('Error Logs'),
                     component: () => import('@/views/nginx_log/NginxLog.vue'),
+                }, {
+                    path: 'site',
+                    name: () => $gettext('Site Logs'),
+                    component: () => import('@/views/nginx_log/NginxLog.vue'),
+                    meta: {
+                        hiddenInSidebar: true
+                    },
                 }]
             },
             {

+ 75 - 0
frontend/src/views/domain/ngx_conf/LogEntry.vue

@@ -0,0 +1,75 @@
+<script setup lang="ts">
+import {FileTextOutlined, FileExclamationOutlined} from '@ant-design/icons-vue'
+import {computed, ref} from 'vue'
+import {useRouter} from 'vue-router'
+
+const props = defineProps(['ngx_config', 'current_server_idx', 'name'])
+
+const accessIdx = ref()
+const errorIdx = ref()
+
+const hasAccessLog = computed(() => {
+    let flag = false
+    props.ngx_config.servers[props.current_server_idx].directives.forEach((v: any, k: any) => {
+        if (v.directive === 'access_log') {
+            flag = true
+            accessIdx.value = k
+            return
+        }
+    })
+    return flag
+})
+
+const hasErrorLog = computed(() => {
+    let flag = false
+    props.ngx_config.servers[props.current_server_idx].directives.forEach((v: any, k: any) => {
+        if (v.directive === 'error_log') {
+            flag = true
+            errorIdx.value = k
+            return
+        }
+    })
+    return flag
+})
+
+const router = useRouter()
+
+function on_click_access_log() {
+    router.push({
+        path: '/nginx_log/site',
+        query: {
+            server_idx: props.current_server_idx,
+            directive_idx: accessIdx.value,
+            conf_name: props.name
+        }
+    })
+}
+
+function on_click_error_log() {
+    router.push({
+        path: '/nginx_log/site',
+        query: {
+            server_idx: props.current_server_idx,
+            directive_idx: errorIdx.value,
+            conf_name: props.name
+        }
+    })
+}
+</script>
+
+<template>
+    <a-space style="margin-left: -15px;margin-bottom: 5px" v-if="hasAccessLog||hasErrorLog">
+        <a-button type="link" v-if="hasAccessLog" @click="on_click_access_log">
+            <FileTextOutlined/>
+            Access Logs
+        </a-button>
+        <a-button type="link" v-if="hasErrorLog" @click="on_click_error_log">
+            <FileExclamationOutlined/>
+            Error Logs
+        </a-button>
+    </a-space>
+</template>
+
+<style lang="less" scoped>
+
+</style>

+ 6 - 0
frontend/src/views/domain/ngx_conf/NgxConfigEditor.vue

@@ -5,6 +5,7 @@ import {computed, ref} from 'vue'
 import {useRoute} from 'vue-router'
 import {useGettext} from 'vue3-gettext'
 import Cert from '@/views/domain/cert/Cert.vue'
+import LogEntry from '@/views/domain/ngx_conf/LogEntry.vue'
 
 const {$gettext} = useGettext()
 
@@ -138,6 +139,11 @@ const autoCertRef = computed({
 
         <a-tabs v-model:activeKey="current_server_index">
             <a-tab-pane :tab="'Server '+(k+1)" v-for="(v,k) in props.ngx_config.servers" :key="k">
+                <log-entry
+                    :ngx_config="props.ngx_config"
+                    :current_server_idx="current_server_index"
+                    :name="name"
+                />
 
                 <div class="tab-content">
                     <template v-if="current_support_ssl&&enabled">

+ 20 - 5
frontend/src/views/nginx_log/NginxLog.vue

@@ -1,9 +1,10 @@
 <script setup lang="ts">
 import {useGettext} from 'vue3-gettext'
 import ws from '@/lib/websocket'
-import {computed, nextTick, onMounted, onUnmounted, reactive, ref, watch} from 'vue'
+import {nextTick, onMounted, onUnmounted, reactive, ref, watch} from 'vue'
 import ReconnectingWebSocket from 'reconnecting-websocket'
-import {useRoute} from 'vue-router'
+import {useRoute, useRouter} from 'vue-router'
+import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue'
 
 const {$gettext} = useGettext()
 
@@ -13,17 +14,24 @@ let websocket: ReconnectingWebSocket | WebSocket
 const route = useRoute()
 
 function logType() {
-    return route.path.indexOf('access') > 0 ? 'access' : 'error'
+    return route.path.indexOf('access') > 0 ? 'access' : route.path.indexOf('error') > 0 ? 'error' : 'site'
 }
 
 const control = reactive({
     fetch: 'new',
-    type: logType()
+    type: logType(),
+    conf_name: route.query.conf_name,
+    server_idx: parseInt(route.query.server_idx as string),
+    directive_idx: parseInt(route.query.directive_idx as string),
 })
 
 function openWs() {
     websocket = ws('/api/nginx_log')
-    websocket.send(JSON.stringify(control))
+
+    websocket.onopen = () => {
+        websocket.send(JSON.stringify(control))
+    }
+
     websocket.onmessage = (m: any) => {
         const para = document.createElement('p')
         para.appendChild(document.createTextNode(m.data.trim()));
@@ -76,6 +84,8 @@ onUnmounted(() => {
     websocket.close()
 })
 
+const router = useRouter()
+
 </script>
 
 <template>
@@ -96,6 +106,11 @@ onUnmounted(() => {
             <pre class="nginx-log-container" ref="logContainer"></pre>
         </a-card>
     </a-card>
+    <footer-tool-bar v-if="control.type==='site'">
+        <a-button @click="router.go(-1)">
+            <translate>Back</translate>
+        </a-button>
+    </footer-tool-bar>
 </template>
 
 <style lang="less">

+ 39 - 5
server/api/nginx_log.go

@@ -2,6 +2,7 @@ package api
 
 import (
 	"encoding/json"
+	"github.com/0xJacky/Nginx-UI/server/pkg/nginx"
 	"github.com/0xJacky/Nginx-UI/server/settings"
 	"github.com/gin-gonic/gin"
 	"github.com/gorilla/websocket"
@@ -10,11 +11,15 @@ import (
 	"io"
 	"log"
 	"net/http"
+	"path/filepath"
 )
 
 type controlStruct struct {
-	Fetch string `json:"fetch"`
-	Type  string `json:"type"`
+	Fetch        string `json:"fetch"`
+	Type         string `json:"type"`
+	ConfName     string `json:"conf_name"`
+	ServerIdx    int    `json:"server_idx"`
+	DirectiveIdx int    `json:"directive_idx"`
 }
 
 func tailNginxLog(ws *websocket.Conn, controlChan chan controlStruct, errChan chan error) {
@@ -33,11 +38,40 @@ func tailNginxLog(ws *websocket.Conn, controlChan chan controlStruct, errChan ch
 			seek.Offset = 0
 			seek.Whence = io.SeekEnd
 		}
+		var logPath string
+		switch control.Type {
+		case "site":
+			path := filepath.Join(nginx.GetNginxConfPath("sites-available"), control.ConfName)
+			config, err := nginx.ParseNgxConfig(path)
+			if err != nil {
+				errChan <- errors.Wrap(err, "error parsing ngx config")
+				return
+			}
+
+			if control.ServerIdx >= len(config.Servers) {
+				errChan <- errors.New("serverIdx out of range")
+				return
+			}
+			if control.DirectiveIdx >= len(config.Servers[control.ServerIdx].Directives) {
+				errChan <- errors.New("DirectiveIdx out of range")
+				return
+			}
+			directive := config.Servers[control.ServerIdx].Directives[control.DirectiveIdx]
+
+			switch directive.Directive {
+			case "access_log", "error_log":
+				// ok
+			default:
+				errChan <- errors.New("directive.Params neither access_log nor error_log")
+				return
+			}
 
-		logPath := settings.NginxLogSettings.AccessLogPath
+			logPath = directive.Params
 
-		if control.Type == "error" {
+		case "error":
 			logPath = settings.NginxLogSettings.ErrorLogPath
+		default:
+			logPath = settings.NginxLogSettings.AccessLogPath
 		}
 
 		// Create a tail
@@ -61,7 +95,6 @@ func tailNginxLog(ws *websocket.Conn, controlChan chan controlStruct, errChan ch
 					return
 				}
 			case control = <-controlChan:
-				log.Println("control change")
 				next = true
 				break
 			}
@@ -125,6 +158,7 @@ func NginxLog(c *gin.Context) {
 
 	if err = <-errChan; err != nil {
 		log.Println(err)
+		_ = ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))
 		return
 	}
 }