From 56f4e5b87fc1ef7158e6963bb2e497f37a3a513d Mon Sep 17 00:00:00 2001 From: Jacky Date: Wed, 2 Apr 2025 18:56:15 +0800 Subject: [PATCH] enhance: nginx log --- api/nginx_log/nginx_log.go | 244 ++-------- api/nginx_log/router.go | 2 + api/nginx_log/sse.go | 50 ++ api/nginx_log/websocket.go | 189 ++++++++ app/src/api/nginx_log.ts | 25 +- app/src/language/ar/app.po | 54 ++- app/src/language/de_DE/app.po | 54 ++- app/src/language/en/app.po | 52 +- app/src/language/es/app.po | 54 ++- app/src/language/fr_FR/app.po | 54 ++- app/src/language/ko_KR/app.po | 53 +- app/src/language/messages.pot | 50 +- app/src/language/ru_RU/app.po | 54 ++- app/src/language/tr_TR/app.po | 54 ++- app/src/language/vi_VN/app.po | 53 +- app/src/language/zh_CN/app.po | 57 ++- app/src/language/zh_TW/app.po | 58 ++- app/src/routes/modules/nginx_log.ts | 7 + app/src/views/nginx_log/NginxLog.vue | 54 +-- app/src/views/nginx_log/NginxLogList.vue | 152 ++++++ app/src/views/site/ngx_conf/LogEntry.vue | 28 +- internal/cache/cache.go | 6 +- internal/cache/nginx_log.go | 585 +++++++++++++++++++++++ internal/nginx_log/log_list.go | 43 ++ 24 files changed, 1685 insertions(+), 347 deletions(-) create mode 100644 api/nginx_log/sse.go create mode 100644 api/nginx_log/websocket.go create mode 100644 app/src/views/nginx_log/NginxLogList.vue create mode 100644 internal/cache/nginx_log.go create mode 100644 internal/nginx_log/log_list.go diff --git a/api/nginx_log/nginx_log.go b/api/nginx_log/nginx_log.go index 192a12af..4b5ef8cc 100644 --- a/api/nginx_log/nginx_log.go +++ b/api/nginx_log/nginx_log.go @@ -1,21 +1,18 @@ package nginx_log import ( - "encoding/json" - "github.com/0xJacky/Nginx-UI/internal/helper" - "github.com/0xJacky/Nginx-UI/internal/nginx" - "github.com/0xJacky/Nginx-UI/internal/nginx_log" - "github.com/gin-gonic/gin" - "github.com/gorilla/websocket" - "github.com/hpcloud/tail" - "github.com/pkg/errors" - "github.com/spf13/cast" - "github.com/uozi-tech/cosy" - "github.com/uozi-tech/cosy/logger" "io" "net/http" "os" "strings" + + "github.com/0xJacky/Nginx-UI/internal/cache" + "github.com/0xJacky/Nginx-UI/internal/nginx_log" + "github.com/gin-gonic/gin" + "github.com/pkg/errors" + "github.com/spf13/cast" + "github.com/uozi-tech/cosy" + "github.com/uozi-tech/cosy/logger" ) const ( @@ -23,10 +20,8 @@ const ( ) type controlStruct struct { - Type string `json:"type"` - ConfName string `json:"conf_name"` - ServerIdx int `json:"server_idx"` - DirectiveIdx int `json:"directive_idx"` + Type string `json:"type"` + LogPath string `json:"log_path"` } type nginxLogPageResp struct { @@ -130,200 +125,35 @@ func GetNginxLogPage(c *gin.Context) { }) } -func getLogPath(control *controlStruct) (logPath string, err error) { - switch control.Type { - case "site": - var config *nginx.NgxConfig - path := nginx.GetConfPath("sites-available", control.ConfName) - config, err = nginx.ParseNgxConfig(path) - if err != nil { - err = errors.Wrap(err, "error parsing ngx config") - return - } +func GetLogList(c *gin.Context) { + filters := []func(*cache.NginxLogCache) bool{} - if control.ServerIdx >= len(config.Servers) { - err = nginx_log.ErrServerIdxOutOfRange - return - } - - if control.DirectiveIdx >= len(config.Servers[control.ServerIdx].Directives) { - err = nginx_log.ErrDirectiveIdxOutOfRange - return - } - - directive := config.Servers[control.ServerIdx].Directives[control.DirectiveIdx] - switch directive.Directive { - case "access_log", "error_log": - // ok - default: - err = nginx_log.ErrLogDirective - return - } - - if directive.Params == "" { - err = nginx_log.ErrDirectiveParamsIsEmpty - return - } - - // fix: access_log /var/log/test.log main; - p := strings.Split(directive.Params, " ") - if len(p) > 0 { - logPath = p[0] - } - - case "error": - path := nginx.GetErrorLogPath() - - if path == "" { - err = nginx_log.ErrErrorLogPathIsEmpty - return - } - - logPath = path - default: - path := nginx.GetAccessLogPath() - - if path == "" { - err = nginx_log.ErrAccessLogPathIsEmpty - return - } - - logPath = path + if c.Query("type") != "" { + filters = append(filters, func(cache *cache.NginxLogCache) bool { + return cache.Type == c.Query("type") + }) } - // check if logPath is under one of the paths in LogDirWhiteList - if !nginx_log.IsLogPathUnderWhiteList(logPath) { - return "", nginx_log.ErrLogPathIsNotUnderTheLogDirWhiteList - } - return -} - -func tailNginxLog(ws *websocket.Conn, controlChan chan controlStruct, errChan chan error) { - defer func() { - if err := recover(); err != nil { - logger.Error(err) - return - } - }() - - control := <-controlChan - - for { - logPath, err := getLogPath(&control) - - if err != nil { - errChan <- err - return - } - - seek := tail.SeekInfo{ - Offset: 0, - Whence: io.SeekEnd, - } - - stat, err := os.Stat(logPath) - if os.IsNotExist(err) { - errChan <- errors.New("[error] log path not exists " + logPath) - return - } - - if !stat.Mode().IsRegular() { - errChan <- errors.New("[error] " + logPath + " is not a regular file. " + - "If you are using nginx-ui in docker container, please refer to " + - "https://nginxui.com/zh_CN/guide/config-nginx-log.html for more information.") - return - } - - // Create a tail - t, err := tail.TailFile(logPath, tail.Config{Follow: true, - ReOpen: true, Location: &seek}) - - if err != nil { - errChan <- errors.Wrap(err, "error tailing log") - return - } - - for { - var next = false - select { - case line := <-t.Lines: - // Print the text of each received line - if line == nil { - continue - } - - err = ws.WriteMessage(websocket.TextMessage, []byte(line.Text)) - if err != nil { - if helper.IsUnexpectedWebsocketError(err) { - errChan <- errors.Wrap(err, "error tailNginxLog write message") - } - return - } - case control = <-controlChan: - next = true - break - } - if next { - break - } - } - } -} - -func handleLogControl(ws *websocket.Conn, controlChan chan controlStruct, errChan chan error) { - defer func() { - if err := recover(); err != nil { - logger.Error(err) - return - } - }() - - for { - msgType, payload, err := ws.ReadMessage() - if err != nil && websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure) { - errChan <- errors.Wrap(err, "error handleLogControl read message") - return - } - - if msgType != websocket.TextMessage { - errChan <- errors.New("error handleLogControl message type") - return - } - - var msg controlStruct - err = json.Unmarshal(payload, &msg) - if err != nil { - errChan <- errors.Wrap(err, "error ReadWsAndWritePty json.Unmarshal") - return - } - controlChan <- msg - } -} - -func Log(c *gin.Context) { - 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 { - logger.Error(err) - return - } - - defer ws.Close() - - errChan := make(chan error, 1) - controlChan := make(chan controlStruct, 1) - - go tailNginxLog(ws, controlChan, errChan) - go handleLogControl(ws, controlChan, errChan) - - if err = <-errChan; err != nil { - logger.Error(err) - _ = ws.WriteMessage(websocket.TextMessage, []byte(err.Error())) - return + if c.Query("name") != "" { + filters = append(filters, func(cache *cache.NginxLogCache) bool { + return strings.Contains(cache.Name, c.Query("name")) + }) } + + if c.Query("path") != "" { + filters = append(filters, func(cache *cache.NginxLogCache) bool { + return strings.Contains(cache.Path, c.Query("path")) + }) + } + + data := cache.GetAllLogPaths(filters...) + + orderBy := c.DefaultQuery("sort_by", "name") + sort := c.DefaultQuery("order", "desc") + + data = nginx_log.Sort(orderBy, sort, data) + + c.JSON(http.StatusOK, gin.H{ + "data": data, + }) } diff --git a/api/nginx_log/router.go b/api/nginx_log/router.go index 59540a63..5f91a450 100644 --- a/api/nginx_log/router.go +++ b/api/nginx_log/router.go @@ -4,4 +4,6 @@ import "github.com/gin-gonic/gin" func InitRouter(r *gin.RouterGroup) { r.GET("nginx_log", Log) + r.GET("nginx_logs", GetLogList) + r.GET("nginx_logs/index_status", GetNginxLogsLive) } diff --git a/api/nginx_log/sse.go b/api/nginx_log/sse.go new file mode 100644 index 00000000..79d812b9 --- /dev/null +++ b/api/nginx_log/sse.go @@ -0,0 +1,50 @@ +package nginx_log + +import ( + "io" + "time" + + "github.com/0xJacky/Nginx-UI/api" + "github.com/0xJacky/Nginx-UI/internal/cache" + "github.com/gin-gonic/gin" +) + +// GetNginxLogsLive is an SSE endpoint that sends real-time log scanning status updates +func GetNginxLogsLive(c *gin.Context) { + api.SetSSEHeaders(c) + notify := c.Writer.CloseNotify() + + // Subscribe to scanner status changes + statusChan := cache.SubscribeStatusChanges() + + // Ensure we unsubscribe when the handler exits + defer cache.UnsubscribeStatusChanges(statusChan) + + // Main event loop + for { + select { + case status, ok := <-statusChan: + // If channel closed, exit + if !ok { + return + } + + // Send status update + c.Stream(func(w io.Writer) bool { + c.SSEvent("message", gin.H{ + "scanning": status, + }) + return false + }) + case <-time.After(30 * time.Second): + // Send heartbeat to keep connection alive + c.Stream(func(w io.Writer) bool { + c.SSEvent("heartbeat", "") + return false + }) + case <-notify: + // Client disconnected + return + } + } +} diff --git a/api/nginx_log/websocket.go b/api/nginx_log/websocket.go new file mode 100644 index 00000000..a74a5e66 --- /dev/null +++ b/api/nginx_log/websocket.go @@ -0,0 +1,189 @@ +package nginx_log + +import ( + "encoding/json" + "io" + "net/http" + "os" + + "github.com/0xJacky/Nginx-UI/internal/helper" + "github.com/0xJacky/Nginx-UI/internal/nginx" + "github.com/0xJacky/Nginx-UI/internal/nginx_log" + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "github.com/nxadm/tail" + "github.com/pkg/errors" + "github.com/uozi-tech/cosy/logger" +) + +func getLogPath(control *controlStruct) (logPath string, err error) { + // If direct log path is provided, use it + if control.LogPath != "" { + logPath = control.LogPath + // Check if logPath is under one of the paths in LogDirWhiteList + if !nginx_log.IsLogPathUnderWhiteList(logPath) { + return "", nginx_log.ErrLogPathIsNotUnderTheLogDirWhiteList + } + return + } + + // Otherwise, use default log path based on type + switch control.Type { + case "error": + path := nginx.GetErrorLogPath() + + if path == "" { + err = nginx_log.ErrErrorLogPathIsEmpty + return + } + + logPath = path + case "access": + fallthrough + default: + path := nginx.GetAccessLogPath() + + if path == "" { + err = nginx_log.ErrAccessLogPathIsEmpty + return + } + + logPath = path + } + + // check if logPath is under one of the paths in LogDirWhiteList + if !nginx_log.IsLogPathUnderWhiteList(logPath) { + return "", nginx_log.ErrLogPathIsNotUnderTheLogDirWhiteList + } + return +} + +func tailNginxLog(ws *websocket.Conn, controlChan chan controlStruct, errChan chan error) { + defer func() { + if err := recover(); err != nil { + logger.Error(err) + return + } + }() + + control := <-controlChan + + for { + logPath, err := getLogPath(&control) + + if err != nil { + errChan <- err + return + } + + seek := tail.SeekInfo{ + Offset: 0, + Whence: io.SeekEnd, + } + + stat, err := os.Stat(logPath) + if os.IsNotExist(err) { + errChan <- errors.New("[error] log path not exists " + logPath) + return + } + + if !stat.Mode().IsRegular() { + errChan <- errors.New("[error] " + logPath + " is not a regular file. " + + "If you are using nginx-ui in docker container, please refer to " + + "https://nginxui.com/zh_CN/guide/config-nginx-log.html for more information.") + return + } + + // Create a tail + t, err := tail.TailFile(logPath, tail.Config{Follow: true, + ReOpen: true, Location: &seek}) + + if err != nil { + errChan <- errors.Wrap(err, "error tailing log") + return + } + + for { + var next = false + select { + case line := <-t.Lines: + // Print the text of each received line + if line == nil { + continue + } + + err = ws.WriteMessage(websocket.TextMessage, []byte(line.Text)) + if err != nil { + if helper.IsUnexpectedWebsocketError(err) { + errChan <- errors.Wrap(err, "error tailNginxLog write message") + } + return + } + case control = <-controlChan: + next = true + break + } + if next { + break + } + } + } +} + +func handleLogControl(ws *websocket.Conn, controlChan chan controlStruct, errChan chan error) { + defer func() { + if err := recover(); err != nil { + logger.Error(err) + return + } + }() + + for { + msgType, payload, err := ws.ReadMessage() + if err != nil && websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure) { + errChan <- errors.Wrap(err, "error handleLogControl read message") + return + } + + if msgType != websocket.TextMessage { + errChan <- errors.New("error handleLogControl message type") + return + } + + var msg controlStruct + err = json.Unmarshal(payload, &msg) + if err != nil { + errChan <- errors.Wrap(err, "error ReadWsAndWritePty json.Unmarshal") + return + } + controlChan <- msg + } +} + +func Log(c *gin.Context) { + 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 { + logger.Error(err) + return + } + + defer ws.Close() + + errChan := make(chan error, 1) + controlChan := make(chan controlStruct, 1) + + go tailNginxLog(ws, controlChan, errChan) + go handleLogControl(ws, controlChan, errChan) + + if err = <-errChan; err != nil { + logger.Error(err) + _ = ws.WriteMessage(websocket.TextMessage, []byte(err.Error())) + return + } +} diff --git a/app/src/api/nginx_log.ts b/app/src/api/nginx_log.ts index 6f9f7714..adb83eeb 100644 --- a/app/src/api/nginx_log.ts +++ b/app/src/api/nginx_log.ts @@ -1,16 +1,35 @@ import http from '@/lib/http' +import { useUserStore } from '@/pinia' +import { SSE } from 'sse.js' export interface INginxLogData { type: string - conf_name: string - server_idx: number - directive_idx: number + log_path?: string } const nginx_log = { page(page = 0, data: INginxLogData | undefined = undefined) { return http.post(`/nginx_log?page=${page}`, data) }, + + get_list(params: { + type?: string + name?: string + path?: string + }) { + return http.get(`/nginx_logs`, { params }) + }, + + logs_live() { + const { token } = useUserStore() + const url = `/api/nginx_logs/index_status` + + return new SSE(url, { + headers: { + Authorization: token, + }, + }) + }, } export default nginx_log diff --git a/app/src/language/ar/app.po b/app/src/language/ar/app.po index cc2a7724..2ad7b997 100644 --- a/app/src/language/ar/app.po +++ b/app/src/language/ar/app.po @@ -25,7 +25,12 @@ msgstr "إعدادات المصادقة الثنائية" msgid "About" msgstr "عن" -#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:75 +#: src/views/nginx_log/NginxLogList.vue:29 +#, fuzzy +msgid "Access Log" +msgstr "سجلات الدخول" + +#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:91 msgid "Access Logs" msgstr "سجلات الدخول" @@ -39,7 +44,8 @@ msgstr "مستخدم ACME" #: src/views/certificate/DNSCredential.vue:33 #: src/views/config/configColumns.tsx:42 #: src/views/environment/envColumns.tsx:97 -#: src/views/notification/notificationColumns.tsx:65 +#: src/views/nginx_log/NginxLogList.vue:51 +#: src/views/notification/notificationColumns.tsx:66 #: src/views/preference/AuthSettings.vue:30 #: src/views/site/site_category/columns.ts:28 #: src/views/site/site_list/columns.tsx:76 src/views/stream/StreamList.vue:49 @@ -244,7 +250,7 @@ msgstr "إعدادات المصادقة" msgid "Author" msgstr "الكاتب" -#: src/views/nginx_log/NginxLog.vue:152 +#: src/views/nginx_log/NginxLog.vue:149 msgid "Auto Refresh" msgstr "التحديث التلقائي" @@ -260,6 +266,10 @@ msgstr "تم تمكين التجديد التلقائي لـ‏%{name}" msgid "Automatic Restart" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:125 +msgid "Automatically indexed from site and stream configurations." +msgstr "" + #: src/views/certificate/CertificateEditor.vue:255 #: src/views/config/ConfigEditor.vue:213 src/views/config/ConfigList.vue:106 #: src/views/config/ConfigList.vue:180 src/views/nginx_log/NginxLog.vue:173 @@ -627,7 +637,7 @@ msgid "" "Backup files will be automatically downloaded to your computer." msgstr "" -#: src/views/notification/notificationColumns.tsx:58 +#: src/views/notification/notificationColumns.tsx:59 #: src/views/preference/components/Passkey.vue:95 #: src/views/site/site_category/columns.ts:16 src/views/user/userColumns.tsx:48 msgid "Created at" @@ -771,7 +781,7 @@ msgstr "وصف" msgid "Destination file already exists" msgstr "" -#: src/views/notification/notificationColumns.tsx:52 +#: src/views/notification/notificationColumns.tsx:53 msgid "Details" msgstr "تفاصيل" @@ -1113,7 +1123,12 @@ msgstr "البيئات" msgid "Error" msgstr "خطأ" -#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:83 +#: src/views/nginx_log/NginxLogList.vue:30 +#, fuzzy +msgid "Error Log" +msgstr "سجلات الأخطاء" + +#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:99 msgid "Error Logs" msgstr "سجلات الأخطاء" @@ -1542,6 +1557,12 @@ msgstr "" msgid "If left blank, the default CA Dir will be used." msgstr "إذا تُرك فارغًا، سيتم استخدام دليل CA الافتراضي." +#: src/views/nginx_log/NginxLogList.vue:127 +msgid "" +"If logs are not indexed, please check if the log file is under the directory " +"in Nginx.LogDirWhiteList." +msgstr "" + #: src/views/preference/AuthSettings.vue:145 msgid "" "If the number of login failed attempts from a ip reach the max attempts in " @@ -1571,6 +1592,14 @@ msgstr "استيراد" msgid "Import Certificate" msgstr "استيراد شهادة" +#: src/views/nginx_log/NginxLogList.vue:135 +msgid "Indexed" +msgstr "" + +#: src/views/nginx_log/NginxLogList.vue:132 +msgid "Indexing..." +msgstr "" + #: src/components/StdDesign/StdDetail/StdDetail.vue:81 #: src/constants/index.ts:18 src/views/notification/notificationColumns.tsx:29 msgid "Info" @@ -1793,6 +1822,11 @@ msgstr "أماكن" msgid "Log" msgstr "سجل" +#: src/routes/modules/nginx_log.ts:39 src/views/nginx_log/NginxLogList.vue:113 +#, fuzzy +msgid "Log List" +msgstr "قائمة" + #: src/routes/modules/auth.ts:14 src/views/other/Login.vue:222 msgid "Login" msgstr "تسجيل الدخول" @@ -1906,6 +1940,7 @@ msgstr "توجيه متعدد الأسطر" #: src/views/config/components/Mkdir.vue:64 #: src/views/config/configColumns.tsx:7 src/views/config/ConfigEditor.vue:256 #: src/views/environment/envColumns.tsx:9 +#: src/views/nginx_log/NginxLogList.vue:35 #: src/views/preference/components/AddPasskey.vue:75 #: src/views/site/ngx_conf/NgxUpstream.vue:177 #: src/views/site/site_category/columns.ts:7 @@ -2025,7 +2060,7 @@ msgstr "مسار سجل أخطاء Nginx" msgid "Nginx is not running" msgstr "Nginx لا يعمل" -#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:148 +#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:143 msgid "Nginx Log" msgstr "سجل Nginx" @@ -2282,6 +2317,7 @@ msgid "Password length cannot exceed 20 characters" msgstr "" #: src/views/config/ConfigEditor.vue:263 +#: src/views/nginx_log/NginxLogList.vue:43 #: src/views/site/ngx_conf/LocationEditor.vue:109 #: src/views/site/ngx_conf/LocationEditor.vue:137 msgid "Path" @@ -3369,7 +3405,7 @@ msgstr "كبح" msgid "Tips" msgstr "نصائح" -#: src/views/notification/notificationColumns.tsx:44 +#: src/views/notification/notificationColumns.tsx:45 msgid "Title" msgstr "عنوان" @@ -3447,6 +3483,7 @@ msgid "Two-factor authentication required" msgstr "يتطلب المصادقة الثنائية" #: src/views/certificate/CertificateList/certColumns.tsx:25 +#: src/views/nginx_log/NginxLogList.vue:20 #: src/views/notification/notificationColumns.tsx:9 msgid "Type" msgstr "نوع" @@ -3549,6 +3586,7 @@ msgid "Version" msgstr "إصدار" #: src/components/StdDesign/StdDataDisplay/StdTable.vue:488 +#: src/views/nginx_log/NginxLogList.vue:143 #: src/views/site/ngx_conf/config_template/ConfigTemplate.vue:103 msgid "View" msgstr "عرض" diff --git a/app/src/language/de_DE/app.po b/app/src/language/de_DE/app.po index f1706a81..51452435 100644 --- a/app/src/language/de_DE/app.po +++ b/app/src/language/de_DE/app.po @@ -21,7 +21,12 @@ msgstr "2FA-Einstellungen" msgid "About" msgstr "Über" -#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:75 +#: src/views/nginx_log/NginxLogList.vue:29 +#, fuzzy +msgid "Access Log" +msgstr "Zugriffslog" + +#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:91 msgid "Access Logs" msgstr "Zugriffslog" @@ -36,7 +41,8 @@ msgstr "Benutzername" #: src/views/certificate/DNSCredential.vue:33 #: src/views/config/configColumns.tsx:42 #: src/views/environment/envColumns.tsx:97 -#: src/views/notification/notificationColumns.tsx:65 +#: src/views/nginx_log/NginxLogList.vue:51 +#: src/views/notification/notificationColumns.tsx:66 #: src/views/preference/AuthSettings.vue:30 #: src/views/site/site_category/columns.ts:28 #: src/views/site/site_list/columns.tsx:76 src/views/stream/StreamList.vue:49 @@ -258,7 +264,7 @@ msgstr "Authentifizierungseinstellungen" msgid "Author" msgstr "Autor" -#: src/views/nginx_log/NginxLog.vue:152 +#: src/views/nginx_log/NginxLog.vue:149 msgid "Auto Refresh" msgstr "Automatische Aktualisierung" @@ -274,6 +280,10 @@ msgstr "Automatische Verlängerung aktiviert für %{name}" msgid "Automatic Restart" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:125 +msgid "Automatically indexed from site and stream configurations." +msgstr "" + #: src/views/certificate/CertificateEditor.vue:255 #: src/views/config/ConfigEditor.vue:213 src/views/config/ConfigList.vue:106 #: src/views/config/ConfigList.vue:180 src/views/nginx_log/NginxLog.vue:173 @@ -650,7 +660,7 @@ msgid "" "Backup files will be automatically downloaded to your computer." msgstr "" -#: src/views/notification/notificationColumns.tsx:58 +#: src/views/notification/notificationColumns.tsx:59 #: src/views/preference/components/Passkey.vue:95 #: src/views/site/site_category/columns.ts:16 src/views/user/userColumns.tsx:48 msgid "Created at" @@ -799,7 +809,7 @@ msgstr "Beschreibung" msgid "Destination file already exists" msgstr "" -#: src/views/notification/notificationColumns.tsx:52 +#: src/views/notification/notificationColumns.tsx:53 msgid "Details" msgstr "Details" @@ -1161,7 +1171,12 @@ msgstr "Kommentare" msgid "Error" msgstr "Fehler" -#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:83 +#: src/views/nginx_log/NginxLogList.vue:30 +#, fuzzy +msgid "Error Log" +msgstr "Feherlogs" + +#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:99 msgid "Error Logs" msgstr "Feherlogs" @@ -1592,6 +1607,12 @@ msgstr "ICP-Nummer" msgid "If left blank, the default CA Dir will be used." msgstr "Wenn leer, wird das Standard-CA-Verzeichnis verwendet." +#: src/views/nginx_log/NginxLogList.vue:127 +msgid "" +"If logs are not indexed, please check if the log file is under the directory " +"in Nginx.LogDirWhiteList." +msgstr "" + #: src/views/preference/AuthSettings.vue:145 msgid "" "If the number of login failed attempts from a ip reach the max attempts in " @@ -1625,6 +1646,14 @@ msgstr "Import" msgid "Import Certificate" msgstr "Zertifikatsstatus" +#: src/views/nginx_log/NginxLogList.vue:135 +msgid "Indexed" +msgstr "" + +#: src/views/nginx_log/NginxLogList.vue:132 +msgid "Indexing..." +msgstr "" + #: src/components/StdDesign/StdDetail/StdDetail.vue:81 #: src/constants/index.ts:18 src/views/notification/notificationColumns.tsx:29 msgid "Info" @@ -1859,6 +1888,11 @@ msgstr "Orte" msgid "Log" msgstr "Login" +#: src/routes/modules/nginx_log.ts:39 src/views/nginx_log/NginxLogList.vue:113 +#, fuzzy +msgid "Log List" +msgstr "Liste" + #: src/routes/modules/auth.ts:14 src/views/other/Login.vue:222 msgid "Login" msgstr "Login" @@ -1981,6 +2015,7 @@ msgstr "Einzelne Anweisung" #: src/views/config/components/Mkdir.vue:64 #: src/views/config/configColumns.tsx:7 src/views/config/ConfigEditor.vue:256 #: src/views/environment/envColumns.tsx:9 +#: src/views/nginx_log/NginxLogList.vue:35 #: src/views/preference/components/AddPasskey.vue:75 #: src/views/site/ngx_conf/NgxUpstream.vue:177 #: src/views/site/site_category/columns.ts:7 @@ -2104,7 +2139,7 @@ msgstr "Nginx Fehlerlog-Pfad" msgid "Nginx is not running" msgstr "Nginx läuft nicht" -#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:148 +#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:143 msgid "Nginx Log" msgstr "Nginx-Log" @@ -2370,6 +2405,7 @@ msgid "Password length cannot exceed 20 characters" msgstr "Passwort darf nicht länger als 20 Zeichen sein" #: src/views/config/ConfigEditor.vue:263 +#: src/views/nginx_log/NginxLogList.vue:43 #: src/views/site/ngx_conf/LocationEditor.vue:109 #: src/views/site/ngx_conf/LocationEditor.vue:137 msgid "Path" @@ -3511,7 +3547,7 @@ msgstr "Begrenzung" msgid "Tips" msgstr "Tipps" -#: src/views/notification/notificationColumns.tsx:44 +#: src/views/notification/notificationColumns.tsx:45 msgid "Title" msgstr "Titel" @@ -3585,6 +3621,7 @@ msgid "Two-factor authentication required" msgstr "Zwei-Faktor-Authentifizierung erforderlich" #: src/views/certificate/CertificateList/certColumns.tsx:25 +#: src/views/nginx_log/NginxLogList.vue:20 #: src/views/notification/notificationColumns.tsx:9 msgid "Type" msgstr "Typ" @@ -3692,6 +3729,7 @@ msgid "Version" msgstr "" #: src/components/StdDesign/StdDataDisplay/StdTable.vue:488 +#: src/views/nginx_log/NginxLogList.vue:143 #: src/views/site/ngx_conf/config_template/ConfigTemplate.vue:103 msgid "View" msgstr "Anzeigen" diff --git a/app/src/language/en/app.po b/app/src/language/en/app.po index 24a48071..cd90964b 100644 --- a/app/src/language/en/app.po +++ b/app/src/language/en/app.po @@ -21,7 +21,12 @@ msgstr "" msgid "About" msgstr "About" -#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:75 +#: src/views/nginx_log/NginxLogList.vue:29 +#, fuzzy +msgid "Access Log" +msgstr "Sites List" + +#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:91 #, fuzzy msgid "Access Logs" msgstr "Sites List" @@ -37,7 +42,8 @@ msgstr "Username" #: src/views/certificate/DNSCredential.vue:33 #: src/views/config/configColumns.tsx:42 #: src/views/environment/envColumns.tsx:97 -#: src/views/notification/notificationColumns.tsx:65 +#: src/views/nginx_log/NginxLogList.vue:51 +#: src/views/notification/notificationColumns.tsx:66 #: src/views/preference/AuthSettings.vue:30 #: src/views/site/site_category/columns.ts:28 #: src/views/site/site_list/columns.tsx:76 src/views/stream/StreamList.vue:49 @@ -255,7 +261,7 @@ msgstr "" msgid "Author" msgstr "" -#: src/views/nginx_log/NginxLog.vue:152 +#: src/views/nginx_log/NginxLog.vue:149 msgid "Auto Refresh" msgstr "" @@ -271,6 +277,10 @@ msgstr "Auto-renewal enabled for %{name}" msgid "Automatic Restart" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:125 +msgid "Automatically indexed from site and stream configurations." +msgstr "" + #: src/views/certificate/CertificateEditor.vue:255 #: src/views/config/ConfigEditor.vue:213 src/views/config/ConfigList.vue:106 #: src/views/config/ConfigList.vue:180 src/views/nginx_log/NginxLog.vue:173 @@ -642,7 +652,7 @@ msgid "" "Backup files will be automatically downloaded to your computer." msgstr "" -#: src/views/notification/notificationColumns.tsx:58 +#: src/views/notification/notificationColumns.tsx:59 #: src/views/preference/components/Passkey.vue:95 #: src/views/site/site_category/columns.ts:16 src/views/user/userColumns.tsx:48 msgid "Created at" @@ -790,7 +800,7 @@ msgstr "" msgid "Destination file already exists" msgstr "" -#: src/views/notification/notificationColumns.tsx:52 +#: src/views/notification/notificationColumns.tsx:53 msgid "Details" msgstr "" @@ -1147,7 +1157,11 @@ msgstr "Comments" msgid "Error" msgstr "" -#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:83 +#: src/views/nginx_log/NginxLogList.vue:30 +msgid "Error Log" +msgstr "" + +#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:99 msgid "Error Logs" msgstr "" @@ -1580,6 +1594,12 @@ msgstr "" msgid "If left blank, the default CA Dir will be used." msgstr "" +#: src/views/nginx_log/NginxLogList.vue:127 +msgid "" +"If logs are not indexed, please check if the log file is under the directory " +"in Nginx.LogDirWhiteList." +msgstr "" + #: src/views/preference/AuthSettings.vue:145 msgid "" "If the number of login failed attempts from a ip reach the max attempts in " @@ -1606,6 +1626,14 @@ msgstr "" msgid "Import Certificate" msgstr "Certificate Status" +#: src/views/nginx_log/NginxLogList.vue:135 +msgid "Indexed" +msgstr "" + +#: src/views/nginx_log/NginxLogList.vue:132 +msgid "Indexing..." +msgstr "" + #: src/components/StdDesign/StdDetail/StdDetail.vue:81 #: src/constants/index.ts:18 src/views/notification/notificationColumns.tsx:29 msgid "Info" @@ -1845,6 +1873,10 @@ msgstr "Locations" msgid "Log" msgstr "Login" +#: src/routes/modules/nginx_log.ts:39 src/views/nginx_log/NginxLogList.vue:113 +msgid "Log List" +msgstr "" + #: src/routes/modules/auth.ts:14 src/views/other/Login.vue:222 msgid "Login" msgstr "Login" @@ -1960,6 +1992,7 @@ msgstr "Single Directive" #: src/views/config/components/Mkdir.vue:64 #: src/views/config/configColumns.tsx:7 src/views/config/ConfigEditor.vue:256 #: src/views/environment/envColumns.tsx:9 +#: src/views/nginx_log/NginxLogList.vue:35 #: src/views/preference/components/AddPasskey.vue:75 #: src/views/site/ngx_conf/NgxUpstream.vue:177 #: src/views/site/site_category/columns.ts:7 @@ -2083,7 +2116,7 @@ msgstr "" msgid "Nginx is not running" msgstr "" -#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:148 +#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:143 msgid "Nginx Log" msgstr "" @@ -2340,6 +2373,7 @@ msgid "Password length cannot exceed 20 characters" msgstr "" #: src/views/config/ConfigEditor.vue:263 +#: src/views/nginx_log/NginxLogList.vue:43 #: src/views/site/ngx_conf/LocationEditor.vue:109 #: src/views/site/ngx_conf/LocationEditor.vue:137 msgid "Path" @@ -3459,7 +3493,7 @@ msgstr "" msgid "Tips" msgstr "" -#: src/views/notification/notificationColumns.tsx:44 +#: src/views/notification/notificationColumns.tsx:45 msgid "Title" msgstr "" @@ -3520,6 +3554,7 @@ msgid "Two-factor authentication required" msgstr "" #: src/views/certificate/CertificateList/certColumns.tsx:25 +#: src/views/nginx_log/NginxLogList.vue:20 #: src/views/notification/notificationColumns.tsx:9 msgid "Type" msgstr "" @@ -3628,6 +3663,7 @@ msgid "Version" msgstr "" #: src/components/StdDesign/StdDataDisplay/StdTable.vue:488 +#: src/views/nginx_log/NginxLogList.vue:143 #: src/views/site/ngx_conf/config_template/ConfigTemplate.vue:103 #, fuzzy msgid "View" diff --git a/app/src/language/es/app.po b/app/src/language/es/app.po index 3a2aa8c4..277723cb 100644 --- a/app/src/language/es/app.po +++ b/app/src/language/es/app.po @@ -28,7 +28,12 @@ msgstr "Configuración de 2FA" msgid "About" msgstr "Acerca de" -#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:75 +#: src/views/nginx_log/NginxLogList.vue:29 +#, fuzzy +msgid "Access Log" +msgstr "Logs de acceso" + +#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:91 msgid "Access Logs" msgstr "Logs de acceso" @@ -42,7 +47,8 @@ msgstr "Usuario ACME" #: src/views/certificate/DNSCredential.vue:33 #: src/views/config/configColumns.tsx:42 #: src/views/environment/envColumns.tsx:97 -#: src/views/notification/notificationColumns.tsx:65 +#: src/views/nginx_log/NginxLogList.vue:51 +#: src/views/notification/notificationColumns.tsx:66 #: src/views/preference/AuthSettings.vue:30 #: src/views/site/site_category/columns.ts:28 #: src/views/site/site_list/columns.tsx:76 src/views/stream/StreamList.vue:49 @@ -249,7 +255,7 @@ msgstr "Configuración de autenticación" msgid "Author" msgstr "Autor" -#: src/views/nginx_log/NginxLog.vue:152 +#: src/views/nginx_log/NginxLog.vue:149 msgid "Auto Refresh" msgstr "Actualización automática" @@ -265,6 +271,10 @@ msgstr "Renovación automática habilitada por %{name}" msgid "Automatic Restart" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:125 +msgid "Automatically indexed from site and stream configurations." +msgstr "" + #: src/views/certificate/CertificateEditor.vue:255 #: src/views/config/ConfigEditor.vue:213 src/views/config/ConfigList.vue:106 #: src/views/config/ConfigList.vue:180 src/views/nginx_log/NginxLog.vue:173 @@ -626,7 +636,7 @@ msgid "" "Backup files will be automatically downloaded to your computer." msgstr "" -#: src/views/notification/notificationColumns.tsx:58 +#: src/views/notification/notificationColumns.tsx:59 #: src/views/preference/components/Passkey.vue:95 #: src/views/site/site_category/columns.ts:16 src/views/user/userColumns.tsx:48 msgid "Created at" @@ -772,7 +782,7 @@ msgstr "Descripción" msgid "Destination file already exists" msgstr "" -#: src/views/notification/notificationColumns.tsx:52 +#: src/views/notification/notificationColumns.tsx:53 msgid "Details" msgstr "Detalles" @@ -1116,7 +1126,12 @@ msgstr "Entornos" msgid "Error" msgstr "Error" -#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:83 +#: src/views/nginx_log/NginxLogList.vue:30 +#, fuzzy +msgid "Error Log" +msgstr "Logs de error" + +#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:99 msgid "Error Logs" msgstr "Logs de error" @@ -1543,6 +1558,12 @@ msgstr "" msgid "If left blank, the default CA Dir will be used." msgstr "Si se deja en blanco, se utilizará el directorio CA predeterminado." +#: src/views/nginx_log/NginxLogList.vue:127 +msgid "" +"If logs are not indexed, please check if the log file is under the directory " +"in Nginx.LogDirWhiteList." +msgstr "" + #: src/views/preference/AuthSettings.vue:145 msgid "" "If the number of login failed attempts from a ip reach the max attempts in " @@ -1574,6 +1595,14 @@ msgstr "Importar" msgid "Import Certificate" msgstr "Importar Certificado" +#: src/views/nginx_log/NginxLogList.vue:135 +msgid "Indexed" +msgstr "" + +#: src/views/nginx_log/NginxLogList.vue:132 +msgid "Indexing..." +msgstr "" + #: src/components/StdDesign/StdDetail/StdDetail.vue:81 #: src/constants/index.ts:18 src/views/notification/notificationColumns.tsx:29 msgid "Info" @@ -1795,6 +1824,11 @@ msgstr "Ubicaciones" msgid "Log" msgstr "Registro" +#: src/routes/modules/nginx_log.ts:39 src/views/nginx_log/NginxLogList.vue:113 +#, fuzzy +msgid "Log List" +msgstr "Lista" + #: src/routes/modules/auth.ts:14 src/views/other/Login.vue:222 msgid "Login" msgstr "Acceso" @@ -1909,6 +1943,7 @@ msgstr "Directiva multilínea" #: src/views/config/components/Mkdir.vue:64 #: src/views/config/configColumns.tsx:7 src/views/config/ConfigEditor.vue:256 #: src/views/environment/envColumns.tsx:9 +#: src/views/nginx_log/NginxLogList.vue:35 #: src/views/preference/components/AddPasskey.vue:75 #: src/views/site/ngx_conf/NgxUpstream.vue:177 #: src/views/site/site_category/columns.ts:7 @@ -2029,7 +2064,7 @@ msgstr "Ruta de registro de errores de Nginx" msgid "Nginx is not running" msgstr "Nginx no se está ejecutando" -#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:148 +#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:143 msgid "Nginx Log" msgstr "Registro Nginx" @@ -2291,6 +2326,7 @@ msgid "Password length cannot exceed 20 characters" msgstr "" #: src/views/config/ConfigEditor.vue:263 +#: src/views/nginx_log/NginxLogList.vue:43 #: src/views/site/ngx_conf/LocationEditor.vue:109 #: src/views/site/ngx_conf/LocationEditor.vue:137 msgid "Path" @@ -3412,7 +3448,7 @@ msgstr "Acelerador" msgid "Tips" msgstr "Consejos" -#: src/views/notification/notificationColumns.tsx:44 +#: src/views/notification/notificationColumns.tsx:45 msgid "Title" msgstr "Título" @@ -3488,6 +3524,7 @@ msgid "Two-factor authentication required" msgstr "Se requiere autenticación de dos factores" #: src/views/certificate/CertificateList/certColumns.tsx:25 +#: src/views/nginx_log/NginxLogList.vue:20 #: src/views/notification/notificationColumns.tsx:9 msgid "Type" msgstr "Tipo" @@ -3591,6 +3628,7 @@ msgid "Version" msgstr "Versión" #: src/components/StdDesign/StdDataDisplay/StdTable.vue:488 +#: src/views/nginx_log/NginxLogList.vue:143 #: src/views/site/ngx_conf/config_template/ConfigTemplate.vue:103 msgid "View" msgstr "Ver" diff --git a/app/src/language/fr_FR/app.po b/app/src/language/fr_FR/app.po index a73cc586..ed4aaa3e 100644 --- a/app/src/language/fr_FR/app.po +++ b/app/src/language/fr_FR/app.po @@ -26,7 +26,12 @@ msgstr "Options 2FA" msgid "About" msgstr "À propos" -#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:75 +#: src/views/nginx_log/NginxLogList.vue:29 +#, fuzzy +msgid "Access Log" +msgstr "Journaux d'accès" + +#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:91 msgid "Access Logs" msgstr "Journaux d'accès" @@ -41,7 +46,8 @@ msgstr "Nom d'utilisateur" #: src/views/certificate/DNSCredential.vue:33 #: src/views/config/configColumns.tsx:42 #: src/views/environment/envColumns.tsx:97 -#: src/views/notification/notificationColumns.tsx:65 +#: src/views/nginx_log/NginxLogList.vue:51 +#: src/views/notification/notificationColumns.tsx:66 #: src/views/preference/AuthSettings.vue:30 #: src/views/site/site_category/columns.ts:28 #: src/views/site/site_list/columns.tsx:76 src/views/stream/StreamList.vue:49 @@ -262,7 +268,7 @@ msgstr "Options d'authentification" msgid "Author" msgstr "Autheur" -#: src/views/nginx_log/NginxLog.vue:152 +#: src/views/nginx_log/NginxLog.vue:149 msgid "Auto Refresh" msgstr "Actualisation automatique" @@ -278,6 +284,10 @@ msgstr "Renouvellement automatique activé pour %{name}" msgid "Automatic Restart" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:125 +msgid "Automatically indexed from site and stream configurations." +msgstr "" + #: src/views/certificate/CertificateEditor.vue:255 #: src/views/config/ConfigEditor.vue:213 src/views/config/ConfigList.vue:106 #: src/views/config/ConfigList.vue:180 src/views/nginx_log/NginxLog.vue:173 @@ -654,7 +664,7 @@ msgid "" "Backup files will be automatically downloaded to your computer." msgstr "" -#: src/views/notification/notificationColumns.tsx:58 +#: src/views/notification/notificationColumns.tsx:59 #: src/views/preference/components/Passkey.vue:95 #: src/views/site/site_category/columns.ts:16 src/views/user/userColumns.tsx:48 msgid "Created at" @@ -805,7 +815,7 @@ msgstr "Description" msgid "Destination file already exists" msgstr "Le fichier de destination existe déjà" -#: src/views/notification/notificationColumns.tsx:52 +#: src/views/notification/notificationColumns.tsx:53 msgid "Details" msgstr "Détails" @@ -1166,7 +1176,12 @@ msgstr "Commentaires" msgid "Error" msgstr "Erreur" -#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:83 +#: src/views/nginx_log/NginxLogList.vue:30 +#, fuzzy +msgid "Error Log" +msgstr "Journaux d'erreurs" + +#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:99 msgid "Error Logs" msgstr "Journaux d'erreurs" @@ -1604,6 +1619,12 @@ msgstr "" msgid "If left blank, the default CA Dir will be used." msgstr "Si vide, le répertoire CA sera utilisé." +#: src/views/nginx_log/NginxLogList.vue:127 +msgid "" +"If logs are not indexed, please check if the log file is under the directory " +"in Nginx.LogDirWhiteList." +msgstr "" + #: src/views/preference/AuthSettings.vue:145 #, fuzzy msgid "" @@ -1640,6 +1661,14 @@ msgstr "Exporter" msgid "Import Certificate" msgstr "État du certificat" +#: src/views/nginx_log/NginxLogList.vue:135 +msgid "Indexed" +msgstr "" + +#: src/views/nginx_log/NginxLogList.vue:132 +msgid "Indexing..." +msgstr "" + #: src/components/StdDesign/StdDetail/StdDetail.vue:81 #: src/constants/index.ts:18 src/views/notification/notificationColumns.tsx:29 msgid "Info" @@ -1876,6 +1905,11 @@ msgstr "Localisations" msgid "Log" msgstr "Connexion" +#: src/routes/modules/nginx_log.ts:39 src/views/nginx_log/NginxLogList.vue:113 +#, fuzzy +msgid "Log List" +msgstr "Liste" + #: src/routes/modules/auth.ts:14 src/views/other/Login.vue:222 msgid "Login" msgstr "Connexion" @@ -1989,6 +2023,7 @@ msgstr "Directive multiligne" #: src/views/config/components/Mkdir.vue:64 #: src/views/config/configColumns.tsx:7 src/views/config/ConfigEditor.vue:256 #: src/views/environment/envColumns.tsx:9 +#: src/views/nginx_log/NginxLogList.vue:35 #: src/views/preference/components/AddPasskey.vue:75 #: src/views/site/ngx_conf/NgxUpstream.vue:177 #: src/views/site/site_category/columns.ts:7 @@ -2112,7 +2147,7 @@ msgstr "Chemin du journal des erreurs Nginx" msgid "Nginx is not running" msgstr "" -#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:148 +#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:143 msgid "Nginx Log" msgstr "Journal Nginx" @@ -2368,6 +2403,7 @@ msgid "Password length cannot exceed 20 characters" msgstr "" #: src/views/config/ConfigEditor.vue:263 +#: src/views/nginx_log/NginxLogList.vue:43 #: src/views/site/ngx_conf/LocationEditor.vue:109 #: src/views/site/ngx_conf/LocationEditor.vue:137 msgid "Path" @@ -3499,7 +3535,7 @@ msgstr "" msgid "Tips" msgstr "" -#: src/views/notification/notificationColumns.tsx:44 +#: src/views/notification/notificationColumns.tsx:45 msgid "Title" msgstr "" @@ -3564,6 +3600,7 @@ msgid "Two-factor authentication required" msgstr "" #: src/views/certificate/CertificateList/certColumns.tsx:25 +#: src/views/nginx_log/NginxLogList.vue:20 #: src/views/notification/notificationColumns.tsx:9 msgid "Type" msgstr "Type" @@ -3670,6 +3707,7 @@ msgid "Version" msgstr "Version actuelle" #: src/components/StdDesign/StdDataDisplay/StdTable.vue:488 +#: src/views/nginx_log/NginxLogList.vue:143 #: src/views/site/ngx_conf/config_template/ConfigTemplate.vue:103 msgid "View" msgstr "Voir" diff --git a/app/src/language/ko_KR/app.po b/app/src/language/ko_KR/app.po index 508d595b..7e93dffb 100644 --- a/app/src/language/ko_KR/app.po +++ b/app/src/language/ko_KR/app.po @@ -26,7 +26,12 @@ msgstr "2FA 설정" msgid "About" msgstr "대하여" -#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:75 +#: src/views/nginx_log/NginxLogList.vue:29 +#, fuzzy +msgid "Access Log" +msgstr "접근 로그" + +#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:91 msgid "Access Logs" msgstr "접근 로그" @@ -40,7 +45,8 @@ msgstr "ACME 사용자" #: src/views/certificate/DNSCredential.vue:33 #: src/views/config/configColumns.tsx:42 #: src/views/environment/envColumns.tsx:97 -#: src/views/notification/notificationColumns.tsx:65 +#: src/views/nginx_log/NginxLogList.vue:51 +#: src/views/notification/notificationColumns.tsx:66 #: src/views/preference/AuthSettings.vue:30 #: src/views/site/site_category/columns.ts:28 #: src/views/site/site_list/columns.tsx:76 src/views/stream/StreamList.vue:49 @@ -246,7 +252,7 @@ msgstr "" msgid "Author" msgstr "저자" -#: src/views/nginx_log/NginxLog.vue:152 +#: src/views/nginx_log/NginxLog.vue:149 msgid "Auto Refresh" msgstr "자동 새로고침" @@ -262,6 +268,10 @@ msgstr "%{name}에 대한 자동 갱신 활성화됨" msgid "Automatic Restart" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:125 +msgid "Automatically indexed from site and stream configurations." +msgstr "" + #: src/views/certificate/CertificateEditor.vue:255 #: src/views/config/ConfigEditor.vue:213 src/views/config/ConfigList.vue:106 #: src/views/config/ConfigList.vue:180 src/views/nginx_log/NginxLog.vue:173 @@ -623,7 +633,7 @@ msgid "" "Backup files will be automatically downloaded to your computer." msgstr "" -#: src/views/notification/notificationColumns.tsx:58 +#: src/views/notification/notificationColumns.tsx:59 #: src/views/preference/components/Passkey.vue:95 #: src/views/site/site_category/columns.ts:16 src/views/user/userColumns.tsx:48 msgid "Created at" @@ -770,7 +780,7 @@ msgstr "설명" msgid "Destination file already exists" msgstr "" -#: src/views/notification/notificationColumns.tsx:52 +#: src/views/notification/notificationColumns.tsx:53 msgid "Details" msgstr "세부 사항" @@ -1115,7 +1125,12 @@ msgstr "환경" msgid "Error" msgstr "오류" -#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:83 +#: src/views/nginx_log/NginxLogList.vue:30 +#, fuzzy +msgid "Error Log" +msgstr "오류 로그" + +#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:99 msgid "Error Logs" msgstr "오류 로그" @@ -1543,6 +1558,12 @@ msgstr "" msgid "If left blank, the default CA Dir will be used." msgstr "" +#: src/views/nginx_log/NginxLogList.vue:127 +msgid "" +"If logs are not indexed, please check if the log file is under the directory " +"in Nginx.LogDirWhiteList." +msgstr "" + #: src/views/preference/AuthSettings.vue:145 msgid "" "If the number of login failed attempts from a ip reach the max attempts in " @@ -1569,6 +1590,14 @@ msgstr "가져오기" msgid "Import Certificate" msgstr "인증서 상태" +#: src/views/nginx_log/NginxLogList.vue:135 +msgid "Indexed" +msgstr "" + +#: src/views/nginx_log/NginxLogList.vue:132 +msgid "Indexing..." +msgstr "" + #: src/components/StdDesign/StdDetail/StdDetail.vue:81 #: src/constants/index.ts:18 src/views/notification/notificationColumns.tsx:29 msgid "Info" @@ -1801,6 +1830,10 @@ msgstr "위치들" msgid "Log" msgstr "로그인" +#: src/routes/modules/nginx_log.ts:39 src/views/nginx_log/NginxLogList.vue:113 +msgid "Log List" +msgstr "" + #: src/routes/modules/auth.ts:14 src/views/other/Login.vue:222 msgid "Login" msgstr "로그인" @@ -1921,6 +1954,7 @@ msgstr "단일 지시문" #: src/views/config/components/Mkdir.vue:64 #: src/views/config/configColumns.tsx:7 src/views/config/ConfigEditor.vue:256 #: src/views/environment/envColumns.tsx:9 +#: src/views/nginx_log/NginxLogList.vue:35 #: src/views/preference/components/AddPasskey.vue:75 #: src/views/site/ngx_conf/NgxUpstream.vue:177 #: src/views/site/site_category/columns.ts:7 @@ -2044,7 +2078,7 @@ msgstr "Nginx 오류 로그 경로" msgid "Nginx is not running" msgstr "" -#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:148 +#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:143 msgid "Nginx Log" msgstr "Nginx 로그" @@ -2301,6 +2335,7 @@ msgid "Password length cannot exceed 20 characters" msgstr "" #: src/views/config/ConfigEditor.vue:263 +#: src/views/nginx_log/NginxLogList.vue:43 #: src/views/site/ngx_conf/LocationEditor.vue:109 #: src/views/site/ngx_conf/LocationEditor.vue:137 msgid "Path" @@ -3421,7 +3456,7 @@ msgstr "" msgid "Tips" msgstr "팁" -#: src/views/notification/notificationColumns.tsx:44 +#: src/views/notification/notificationColumns.tsx:45 msgid "Title" msgstr "제목" @@ -3485,6 +3520,7 @@ msgid "Two-factor authentication required" msgstr "" #: src/views/certificate/CertificateList/certColumns.tsx:25 +#: src/views/nginx_log/NginxLogList.vue:20 #: src/views/notification/notificationColumns.tsx:9 msgid "Type" msgstr "유형" @@ -3593,6 +3629,7 @@ msgid "Version" msgstr "현재 버전" #: src/components/StdDesign/StdDataDisplay/StdTable.vue:488 +#: src/views/nginx_log/NginxLogList.vue:143 #: src/views/site/ngx_conf/config_template/ConfigTemplate.vue:103 msgid "View" msgstr "보기" diff --git a/app/src/language/messages.pot b/app/src/language/messages.pot index 4c88c431..4171456e 100644 --- a/app/src/language/messages.pot +++ b/app/src/language/messages.pot @@ -14,8 +14,12 @@ msgstr "" msgid "About" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:29 +msgid "Access Log" +msgstr "" + #: src/routes/modules/nginx_log.ts:17 -#: src/views/site/ngx_conf/LogEntry.vue:75 +#: src/views/site/ngx_conf/LogEntry.vue:91 msgid "Access Logs" msgstr "" @@ -30,7 +34,8 @@ msgstr "" #: src/views/certificate/DNSCredential.vue:33 #: src/views/config/configColumns.tsx:42 #: src/views/environment/envColumns.tsx:97 -#: src/views/notification/notificationColumns.tsx:65 +#: src/views/nginx_log/NginxLogList.vue:51 +#: src/views/notification/notificationColumns.tsx:66 #: src/views/preference/AuthSettings.vue:30 #: src/views/site/site_category/columns.ts:28 #: src/views/site/site_list/columns.tsx:76 @@ -232,7 +237,7 @@ msgstr "" msgid "Author" msgstr "" -#: src/views/nginx_log/NginxLog.vue:152 +#: src/views/nginx_log/NginxLog.vue:149 msgid "Auto Refresh" msgstr "" @@ -248,6 +253,10 @@ msgstr "" msgid "Automatic Restart" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:125 +msgid "Automatically indexed from site and stream configurations." +msgstr "" + #: src/views/certificate/CertificateEditor.vue:255 #: src/views/config/ConfigEditor.vue:213 #: src/views/config/ConfigList.vue:106 @@ -590,7 +599,7 @@ msgstr "" msgid "Create system backups including Nginx configuration and Nginx UI settings. Backup files will be automatically downloaded to your computer." msgstr "" -#: src/views/notification/notificationColumns.tsx:58 +#: src/views/notification/notificationColumns.tsx:59 #: src/views/preference/components/Passkey.vue:95 #: src/views/site/site_category/columns.ts:16 #: src/views/user/userColumns.tsx:48 @@ -731,7 +740,7 @@ msgstr "" msgid "Destination file already exists" msgstr "" -#: src/views/notification/notificationColumns.tsx:52 +#: src/views/notification/notificationColumns.tsx:53 msgid "Details" msgstr "" @@ -1060,8 +1069,12 @@ msgstr "" msgid "Error" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:30 +msgid "Error Log" +msgstr "" + #: src/routes/modules/nginx_log.ts:24 -#: src/views/site/ngx_conf/LogEntry.vue:83 +#: src/views/site/ngx_conf/LogEntry.vue:99 msgid "Error Logs" msgstr "" @@ -1449,6 +1462,10 @@ msgstr "" msgid "If left blank, the default CA Dir will be used." msgstr "" +#: src/views/nginx_log/NginxLogList.vue:127 +msgid "If logs are not indexed, please check if the log file is under the directory in Nginx.LogDirWhiteList." +msgstr "" + #: src/views/preference/AuthSettings.vue:145 msgid "If the number of login failed attempts from a ip reach the max attempts in ban threshold minutes, the ip will be banned for a period of time." msgstr "" @@ -1470,6 +1487,14 @@ msgstr "" msgid "Import Certificate" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:135 +msgid "Indexed" +msgstr "" + +#: src/views/nginx_log/NginxLogList.vue:132 +msgid "Indexing..." +msgstr "" + #: src/components/StdDesign/StdDetail/StdDetail.vue:81 #: src/constants/index.ts:18 #: src/views/notification/notificationColumns.tsx:29 @@ -1682,6 +1707,11 @@ msgstr "" msgid "Log" msgstr "" +#: src/routes/modules/nginx_log.ts:39 +#: src/views/nginx_log/NginxLogList.vue:113 +msgid "Log List" +msgstr "" + #: src/routes/modules/auth.ts:14 #: src/views/other/Login.vue:222 msgid "Login" @@ -1787,6 +1817,7 @@ msgstr "" #: src/views/config/configColumns.tsx:7 #: src/views/config/ConfigEditor.vue:256 #: src/views/environment/envColumns.tsx:9 +#: src/views/nginx_log/NginxLogList.vue:35 #: src/views/preference/components/AddPasskey.vue:75 #: src/views/site/ngx_conf/NgxUpstream.vue:177 #: src/views/site/site_category/columns.ts:7 @@ -1904,7 +1935,7 @@ msgid "Nginx is not running" msgstr "" #: src/routes/modules/nginx_log.ts:9 -#: src/views/nginx_log/NginxLog.vue:148 +#: src/views/nginx_log/NginxLog.vue:143 msgid "Nginx Log" msgstr "" @@ -2139,6 +2170,7 @@ msgid "Password length cannot exceed 20 characters" msgstr "" #: src/views/config/ConfigEditor.vue:263 +#: src/views/nginx_log/NginxLogList.vue:43 #: src/views/site/ngx_conf/LocationEditor.vue:109 #: src/views/site/ngx_conf/LocationEditor.vue:137 msgid "Path" @@ -3125,7 +3157,7 @@ msgstr "" msgid "Tips" msgstr "" -#: src/views/notification/notificationColumns.tsx:44 +#: src/views/notification/notificationColumns.tsx:45 msgid "Title" msgstr "" @@ -3172,6 +3204,7 @@ msgid "Two-factor authentication required" msgstr "" #: src/views/certificate/CertificateList/certColumns.tsx:25 +#: src/views/nginx_log/NginxLogList.vue:20 #: src/views/notification/notificationColumns.tsx:9 msgid "Type" msgstr "" @@ -3279,6 +3312,7 @@ msgid "Version" msgstr "" #: src/components/StdDesign/StdDataDisplay/StdTable.vue:488 +#: src/views/nginx_log/NginxLogList.vue:143 #: src/views/site/ngx_conf/config_template/ConfigTemplate.vue:103 msgid "View" msgstr "" diff --git a/app/src/language/ru_RU/app.po b/app/src/language/ru_RU/app.po index 869dd9dc..551bc2ab 100644 --- a/app/src/language/ru_RU/app.po +++ b/app/src/language/ru_RU/app.po @@ -28,7 +28,12 @@ msgstr "Настройки 2FA" msgid "About" msgstr "О проекте" -#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:75 +#: src/views/nginx_log/NginxLogList.vue:29 +#, fuzzy +msgid "Access Log" +msgstr "Журналы доступа" + +#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:91 msgid "Access Logs" msgstr "Журналы доступа" @@ -42,7 +47,8 @@ msgstr "Пользователь ACME" #: src/views/certificate/DNSCredential.vue:33 #: src/views/config/configColumns.tsx:42 #: src/views/environment/envColumns.tsx:97 -#: src/views/notification/notificationColumns.tsx:65 +#: src/views/nginx_log/NginxLogList.vue:51 +#: src/views/notification/notificationColumns.tsx:66 #: src/views/preference/AuthSettings.vue:30 #: src/views/site/site_category/columns.ts:28 #: src/views/site/site_list/columns.tsx:76 src/views/stream/StreamList.vue:49 @@ -243,7 +249,7 @@ msgstr "Настройки аутентификации" msgid "Author" msgstr "Автор" -#: src/views/nginx_log/NginxLog.vue:152 +#: src/views/nginx_log/NginxLog.vue:149 msgid "Auto Refresh" msgstr "Автообновление" @@ -259,6 +265,10 @@ msgstr "Автообновление включено для %{name}" msgid "Automatic Restart" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:125 +msgid "Automatically indexed from site and stream configurations." +msgstr "" + #: src/views/certificate/CertificateEditor.vue:255 #: src/views/config/ConfigEditor.vue:213 src/views/config/ConfigList.vue:106 #: src/views/config/ConfigList.vue:180 src/views/nginx_log/NginxLog.vue:173 @@ -613,7 +623,7 @@ msgid "" "Backup files will be automatically downloaded to your computer." msgstr "" -#: src/views/notification/notificationColumns.tsx:58 +#: src/views/notification/notificationColumns.tsx:59 #: src/views/preference/components/Passkey.vue:95 #: src/views/site/site_category/columns.ts:16 src/views/user/userColumns.tsx:48 msgid "Created at" @@ -757,7 +767,7 @@ msgstr "Описание" msgid "Destination file already exists" msgstr "" -#: src/views/notification/notificationColumns.tsx:52 +#: src/views/notification/notificationColumns.tsx:53 msgid "Details" msgstr "Детали" @@ -1100,7 +1110,12 @@ msgstr "Окружения" msgid "Error" msgstr "Ошибка" -#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:83 +#: src/views/nginx_log/NginxLogList.vue:30 +#, fuzzy +msgid "Error Log" +msgstr "Ошибка логирования" + +#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:99 msgid "Error Logs" msgstr "Ошибка логирования" @@ -1526,6 +1541,12 @@ msgstr "ICP номер" msgid "If left blank, the default CA Dir will be used." msgstr "Если оставить пустым, будет использоваться каталог CA по умолчанию." +#: src/views/nginx_log/NginxLogList.vue:127 +msgid "" +"If logs are not indexed, please check if the log file is under the directory " +"in Nginx.LogDirWhiteList." +msgstr "" + #: src/views/preference/AuthSettings.vue:145 msgid "" "If the number of login failed attempts from a ip reach the max attempts in " @@ -1557,6 +1578,14 @@ msgstr "Импорт" msgid "Import Certificate" msgstr "Импортировать сертификат" +#: src/views/nginx_log/NginxLogList.vue:135 +msgid "Indexed" +msgstr "" + +#: src/views/nginx_log/NginxLogList.vue:132 +msgid "Indexing..." +msgstr "" + #: src/components/StdDesign/StdDetail/StdDetail.vue:81 #: src/constants/index.ts:18 src/views/notification/notificationColumns.tsx:29 msgid "Info" @@ -1776,6 +1805,11 @@ msgstr "Локации" msgid "Log" msgstr "Журнал" +#: src/routes/modules/nginx_log.ts:39 src/views/nginx_log/NginxLogList.vue:113 +#, fuzzy +msgid "Log List" +msgstr "Список" + #: src/routes/modules/auth.ts:14 src/views/other/Login.vue:222 msgid "Login" msgstr "Логин" @@ -1889,6 +1923,7 @@ msgstr "Многострочная директива" #: src/views/config/components/Mkdir.vue:64 #: src/views/config/configColumns.tsx:7 src/views/config/ConfigEditor.vue:256 #: src/views/environment/envColumns.tsx:9 +#: src/views/nginx_log/NginxLogList.vue:35 #: src/views/preference/components/AddPasskey.vue:75 #: src/views/site/ngx_conf/NgxUpstream.vue:177 #: src/views/site/site_category/columns.ts:7 @@ -2009,7 +2044,7 @@ msgstr "Путь для Nginx Error Log" msgid "Nginx is not running" msgstr "Nginx не работает" -#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:148 +#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:143 msgid "Nginx Log" msgstr "Журнал" @@ -2263,6 +2298,7 @@ msgid "Password length cannot exceed 20 characters" msgstr "" #: src/views/config/ConfigEditor.vue:263 +#: src/views/nginx_log/NginxLogList.vue:43 #: src/views/site/ngx_conf/LocationEditor.vue:109 #: src/views/site/ngx_conf/LocationEditor.vue:137 msgid "Path" @@ -3375,7 +3411,7 @@ msgstr "" msgid "Tips" msgstr "Советы" -#: src/views/notification/notificationColumns.tsx:44 +#: src/views/notification/notificationColumns.tsx:45 msgid "Title" msgstr "Заголовок" @@ -3444,6 +3480,7 @@ msgid "Two-factor authentication required" msgstr "Требуется двухфакторная аутентификация" #: src/views/certificate/CertificateList/certColumns.tsx:25 +#: src/views/nginx_log/NginxLogList.vue:20 #: src/views/notification/notificationColumns.tsx:9 msgid "Type" msgstr "Тип" @@ -3547,6 +3584,7 @@ msgid "Version" msgstr "Версия" #: src/components/StdDesign/StdDataDisplay/StdTable.vue:488 +#: src/views/nginx_log/NginxLogList.vue:143 #: src/views/site/ngx_conf/config_template/ConfigTemplate.vue:103 msgid "View" msgstr "Просмотр" diff --git a/app/src/language/tr_TR/app.po b/app/src/language/tr_TR/app.po index 6f1079c9..5ddef04a 100644 --- a/app/src/language/tr_TR/app.po +++ b/app/src/language/tr_TR/app.po @@ -24,7 +24,12 @@ msgstr "2FA Ayarları" msgid "About" msgstr "Hakkında" -#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:75 +#: src/views/nginx_log/NginxLogList.vue:29 +#, fuzzy +msgid "Access Log" +msgstr "Erişim Günlükleri" + +#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:91 msgid "Access Logs" msgstr "Erişim Günlükleri" @@ -38,7 +43,8 @@ msgstr "ACME Kullanıcısı" #: src/views/certificate/DNSCredential.vue:33 #: src/views/config/configColumns.tsx:42 #: src/views/environment/envColumns.tsx:97 -#: src/views/notification/notificationColumns.tsx:65 +#: src/views/nginx_log/NginxLogList.vue:51 +#: src/views/notification/notificationColumns.tsx:66 #: src/views/preference/AuthSettings.vue:30 #: src/views/site/site_category/columns.ts:28 #: src/views/site/site_list/columns.tsx:76 src/views/stream/StreamList.vue:49 @@ -244,7 +250,7 @@ msgstr "Kimlik Doğrulama Ayarları" msgid "Author" msgstr "Yazar" -#: src/views/nginx_log/NginxLog.vue:152 +#: src/views/nginx_log/NginxLog.vue:149 msgid "Auto Refresh" msgstr "Otomatik Yenileme" @@ -260,6 +266,10 @@ msgstr "Otomatik yenileme %{name} için etkinleştirildi" msgid "Automatic Restart" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:125 +msgid "Automatically indexed from site and stream configurations." +msgstr "" + #: src/views/certificate/CertificateEditor.vue:255 #: src/views/config/ConfigEditor.vue:213 src/views/config/ConfigList.vue:106 #: src/views/config/ConfigList.vue:180 src/views/nginx_log/NginxLog.vue:173 @@ -619,7 +629,7 @@ msgid "" "Backup files will be automatically downloaded to your computer." msgstr "" -#: src/views/notification/notificationColumns.tsx:58 +#: src/views/notification/notificationColumns.tsx:59 #: src/views/preference/components/Passkey.vue:95 #: src/views/site/site_category/columns.ts:16 src/views/user/userColumns.tsx:48 msgid "Created at" @@ -768,7 +778,7 @@ msgstr "Açıklama" msgid "Destination file already exists" msgstr "" -#: src/views/notification/notificationColumns.tsx:52 +#: src/views/notification/notificationColumns.tsx:53 msgid "Details" msgstr "Detaylar" @@ -1129,7 +1139,12 @@ msgstr "Ortamlar" msgid "Error" msgstr "Hata" -#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:83 +#: src/views/nginx_log/NginxLogList.vue:30 +#, fuzzy +msgid "Error Log" +msgstr "Hata Günlükleri" + +#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:99 msgid "Error Logs" msgstr "Hata Günlükleri" @@ -1555,6 +1570,12 @@ msgstr "" msgid "If left blank, the default CA Dir will be used." msgstr "Boş bırakılırsa, varsayılan CA Dir kullanılır." +#: src/views/nginx_log/NginxLogList.vue:127 +msgid "" +"If logs are not indexed, please check if the log file is under the directory " +"in Nginx.LogDirWhiteList." +msgstr "" + #: src/views/preference/AuthSettings.vue:145 msgid "" "If the number of login failed attempts from a ip reach the max attempts in " @@ -1587,6 +1608,14 @@ msgstr "İçe Aktar" msgid "Import Certificate" msgstr "Sertifika İçe Aktar" +#: src/views/nginx_log/NginxLogList.vue:135 +msgid "Indexed" +msgstr "" + +#: src/views/nginx_log/NginxLogList.vue:132 +msgid "Indexing..." +msgstr "" + #: src/components/StdDesign/StdDetail/StdDetail.vue:81 #: src/constants/index.ts:18 src/views/notification/notificationColumns.tsx:29 msgid "Info" @@ -1807,6 +1836,11 @@ msgstr "Konumlar" msgid "Log" msgstr "Günlük" +#: src/routes/modules/nginx_log.ts:39 src/views/nginx_log/NginxLogList.vue:113 +#, fuzzy +msgid "Log List" +msgstr "Liste" + #: src/routes/modules/auth.ts:14 src/views/other/Login.vue:222 msgid "Login" msgstr "Giriş" @@ -1936,6 +1970,7 @@ msgstr "Çok Hatlı Direktif" #: src/views/config/components/Mkdir.vue:64 #: src/views/config/configColumns.tsx:7 src/views/config/ConfigEditor.vue:256 #: src/views/environment/envColumns.tsx:9 +#: src/views/nginx_log/NginxLogList.vue:35 #: src/views/preference/components/AddPasskey.vue:75 #: src/views/site/ngx_conf/NgxUpstream.vue:177 #: src/views/site/site_category/columns.ts:7 @@ -2071,7 +2106,7 @@ msgstr "Nginx Hata Günlüğü Yolu" msgid "Nginx is not running" msgstr "Nginx çalışmıyor" -#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:148 +#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:143 #, fuzzy msgid "Nginx Log" msgstr "Nginx Günlüğü" @@ -2360,6 +2395,7 @@ msgid "Password length cannot exceed 20 characters" msgstr "" #: src/views/config/ConfigEditor.vue:263 +#: src/views/nginx_log/NginxLogList.vue:43 #: src/views/site/ngx_conf/LocationEditor.vue:109 #: src/views/site/ngx_conf/LocationEditor.vue:137 #, fuzzy @@ -3603,7 +3639,7 @@ msgstr "" msgid "Tips" msgstr "İpuçları" -#: src/views/notification/notificationColumns.tsx:44 +#: src/views/notification/notificationColumns.tsx:45 #, fuzzy msgid "Title" msgstr "Başlık" @@ -3688,6 +3724,7 @@ msgid "Two-factor authentication required" msgstr "İki faktörlü kimlik doğrulama gerekiyor" #: src/views/certificate/CertificateList/certColumns.tsx:25 +#: src/views/nginx_log/NginxLogList.vue:20 #: src/views/notification/notificationColumns.tsx:9 #, fuzzy msgid "Type" @@ -3808,6 +3845,7 @@ msgid "Version" msgstr "Versiyon" #: src/components/StdDesign/StdDataDisplay/StdTable.vue:488 +#: src/views/nginx_log/NginxLogList.vue:143 #: src/views/site/ngx_conf/config_template/ConfigTemplate.vue:103 #, fuzzy msgid "View" diff --git a/app/src/language/vi_VN/app.po b/app/src/language/vi_VN/app.po index 27f8bb4c..05572bf6 100644 --- a/app/src/language/vi_VN/app.po +++ b/app/src/language/vi_VN/app.po @@ -21,7 +21,12 @@ msgstr "" msgid "About" msgstr "Tác giả" -#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:75 +#: src/views/nginx_log/NginxLogList.vue:29 +#, fuzzy +msgid "Access Log" +msgstr "Log truy cập" + +#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:91 msgid "Access Logs" msgstr "Log truy cập" @@ -36,7 +41,8 @@ msgstr "Người dùng" #: src/views/certificate/DNSCredential.vue:33 #: src/views/config/configColumns.tsx:42 #: src/views/environment/envColumns.tsx:97 -#: src/views/notification/notificationColumns.tsx:65 +#: src/views/nginx_log/NginxLogList.vue:51 +#: src/views/notification/notificationColumns.tsx:66 #: src/views/preference/AuthSettings.vue:30 #: src/views/site/site_category/columns.ts:28 #: src/views/site/site_list/columns.tsx:76 src/views/stream/StreamList.vue:49 @@ -255,7 +261,7 @@ msgstr "" msgid "Author" msgstr "Tác giả" -#: src/views/nginx_log/NginxLog.vue:152 +#: src/views/nginx_log/NginxLog.vue:149 msgid "Auto Refresh" msgstr "Tự động làm mới" @@ -271,6 +277,10 @@ msgstr "Đã bật tự động gia hạn SSL cho %{name}" msgid "Automatic Restart" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:125 +msgid "Automatically indexed from site and stream configurations." +msgstr "" + #: src/views/certificate/CertificateEditor.vue:255 #: src/views/config/ConfigEditor.vue:213 src/views/config/ConfigList.vue:106 #: src/views/config/ConfigList.vue:180 src/views/nginx_log/NginxLog.vue:173 @@ -645,7 +655,7 @@ msgid "" "Backup files will be automatically downloaded to your computer." msgstr "" -#: src/views/notification/notificationColumns.tsx:58 +#: src/views/notification/notificationColumns.tsx:59 #: src/views/preference/components/Passkey.vue:95 #: src/views/site/site_category/columns.ts:16 src/views/user/userColumns.tsx:48 msgid "Created at" @@ -794,7 +804,7 @@ msgstr "Mô tả" msgid "Destination file already exists" msgstr "" -#: src/views/notification/notificationColumns.tsx:52 +#: src/views/notification/notificationColumns.tsx:53 msgid "Details" msgstr "Chi tiết" @@ -1152,7 +1162,12 @@ msgstr "Environments" msgid "Error" msgstr "Lỗi" -#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:83 +#: src/views/nginx_log/NginxLogList.vue:30 +#, fuzzy +msgid "Error Log" +msgstr "Log lỗi" + +#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:99 msgid "Error Logs" msgstr "Log lỗi" @@ -1579,6 +1594,12 @@ msgstr "" msgid "If left blank, the default CA Dir will be used." msgstr "" +#: src/views/nginx_log/NginxLogList.vue:127 +msgid "" +"If logs are not indexed, please check if the log file is under the directory " +"in Nginx.LogDirWhiteList." +msgstr "" + #: src/views/preference/AuthSettings.vue:145 msgid "" "If the number of login failed attempts from a ip reach the max attempts in " @@ -1606,6 +1627,14 @@ msgstr "Xuất" msgid "Import Certificate" msgstr "Chứng chỉ" +#: src/views/nginx_log/NginxLogList.vue:135 +msgid "Indexed" +msgstr "" + +#: src/views/nginx_log/NginxLogList.vue:132 +msgid "Indexing..." +msgstr "" + #: src/components/StdDesign/StdDetail/StdDetail.vue:81 #: src/constants/index.ts:18 src/views/notification/notificationColumns.tsx:29 msgid "Info" @@ -1839,6 +1868,10 @@ msgstr "Locations" msgid "Log" msgstr "Log" +#: src/routes/modules/nginx_log.ts:39 src/views/nginx_log/NginxLogList.vue:113 +msgid "Log List" +msgstr "" + #: src/routes/modules/auth.ts:14 src/views/other/Login.vue:222 msgid "Login" msgstr "Đăng nhập" @@ -1953,6 +1986,7 @@ msgstr "Single Directive" #: src/views/config/components/Mkdir.vue:64 #: src/views/config/configColumns.tsx:7 src/views/config/ConfigEditor.vue:256 #: src/views/environment/envColumns.tsx:9 +#: src/views/nginx_log/NginxLogList.vue:35 #: src/views/preference/components/AddPasskey.vue:75 #: src/views/site/ngx_conf/NgxUpstream.vue:177 #: src/views/site/site_category/columns.ts:7 @@ -2076,7 +2110,7 @@ msgstr "Vị trí lưu log lỗi (Error log) của Nginx" msgid "Nginx is not running" msgstr "" -#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:148 +#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:143 msgid "Nginx Log" msgstr "" @@ -2331,6 +2365,7 @@ msgid "Password length cannot exceed 20 characters" msgstr "" #: src/views/config/ConfigEditor.vue:263 +#: src/views/nginx_log/NginxLogList.vue:43 #: src/views/site/ngx_conf/LocationEditor.vue:109 #: src/views/site/ngx_conf/LocationEditor.vue:137 msgid "Path" @@ -3443,7 +3478,7 @@ msgstr "" msgid "Tips" msgstr "" -#: src/views/notification/notificationColumns.tsx:44 +#: src/views/notification/notificationColumns.tsx:45 msgid "Title" msgstr "Tiêu đề" @@ -3508,6 +3543,7 @@ msgid "Two-factor authentication required" msgstr "" #: src/views/certificate/CertificateList/certColumns.tsx:25 +#: src/views/nginx_log/NginxLogList.vue:20 #: src/views/notification/notificationColumns.tsx:9 msgid "Type" msgstr "Loại" @@ -3616,6 +3652,7 @@ msgid "Version" msgstr "Phiên bản hiện tại" #: src/components/StdDesign/StdDataDisplay/StdTable.vue:488 +#: src/views/nginx_log/NginxLogList.vue:143 #: src/views/site/ngx_conf/config_template/ConfigTemplate.vue:103 msgid "View" msgstr "Xem" diff --git a/app/src/language/zh_CN/app.po b/app/src/language/zh_CN/app.po index db78fe35..1764cc5f 100644 --- a/app/src/language/zh_CN/app.po +++ b/app/src/language/zh_CN/app.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2025-04-02 11:07+0800\n" +"PO-Revision-Date: 2025-04-02 18:55+0800\n" "Last-Translator: 0xJacky \n" "Language-Team: Chinese (Simplified Han script) \n" @@ -27,7 +27,11 @@ msgstr "2FA 设置" msgid "About" msgstr "关于" -#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:75 +#: src/views/nginx_log/NginxLogList.vue:29 +msgid "Access Log" +msgstr "访问日志" + +#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:91 msgid "Access Logs" msgstr "访问日志" @@ -41,7 +45,8 @@ msgstr "ACME 用户" #: src/views/certificate/DNSCredential.vue:33 #: src/views/config/configColumns.tsx:42 #: src/views/environment/envColumns.tsx:97 -#: src/views/notification/notificationColumns.tsx:65 +#: src/views/nginx_log/NginxLogList.vue:51 +#: src/views/notification/notificationColumns.tsx:66 #: src/views/preference/AuthSettings.vue:30 #: src/views/site/site_category/columns.ts:28 #: src/views/site/site_list/columns.tsx:76 src/views/stream/StreamList.vue:49 @@ -240,7 +245,7 @@ msgstr "认证设置" msgid "Author" msgstr "作者" -#: src/views/nginx_log/NginxLog.vue:152 +#: src/views/nginx_log/NginxLog.vue:149 msgid "Auto Refresh" msgstr "自动刷新" @@ -256,6 +261,10 @@ msgstr "成功启用 %{name} 自动续签" msgid "Automatic Restart" msgstr "自动重启" +#: src/views/nginx_log/NginxLogList.vue:125 +msgid "Automatically indexed from site and stream configurations." +msgstr "自动索引站点和 Stream 的配置文件。" + #: src/views/certificate/CertificateEditor.vue:255 #: src/views/config/ConfigEditor.vue:213 src/views/config/ConfigList.vue:106 #: src/views/config/ConfigList.vue:180 src/views/nginx_log/NginxLog.vue:173 @@ -604,7 +613,7 @@ msgid "" msgstr "" "创建系统备份,包括 Nginx 配置和 Nginx UI 设置。备份文件将自动下载到你的电脑。" -#: src/views/notification/notificationColumns.tsx:58 +#: src/views/notification/notificationColumns.tsx:59 #: src/views/preference/components/Passkey.vue:95 #: src/views/site/site_category/columns.ts:16 src/views/user/userColumns.tsx:48 msgid "Created at" @@ -741,7 +750,7 @@ msgstr "描述" msgid "Destination file already exists" msgstr "目标文件已存在" -#: src/views/notification/notificationColumns.tsx:52 +#: src/views/notification/notificationColumns.tsx:53 msgid "Details" msgstr "详情" @@ -1063,7 +1072,11 @@ msgstr "环境" msgid "Error" msgstr "错误" -#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:83 +#: src/views/nginx_log/NginxLogList.vue:30 +msgid "Error Log" +msgstr "错误日志" + +#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:99 msgid "Error Logs" msgstr "错误日志" @@ -1451,6 +1464,13 @@ msgstr "ICP备案号" msgid "If left blank, the default CA Dir will be used." msgstr "如果留空,则使用默认 CA Dir。" +#: src/views/nginx_log/NginxLogList.vue:127 +msgid "" +"If logs are not indexed, please check if the log file is under the directory " +"in Nginx.LogDirWhiteList." +msgstr "" +"如果日志未被索引,请检查日志文件是否位于 Nginx.LogDirWhiteList 中的目录下。" + #: src/views/preference/AuthSettings.vue:145 msgid "" "If the number of login failed attempts from a ip reach the max attempts in " @@ -1478,6 +1498,14 @@ msgstr "导入" msgid "Import Certificate" msgstr "导入证书" +#: src/views/nginx_log/NginxLogList.vue:135 +msgid "Indexed" +msgstr "已索引" + +#: src/views/nginx_log/NginxLogList.vue:132 +msgid "Indexing..." +msgstr "索引中..." + #: src/components/StdDesign/StdDetail/StdDetail.vue:81 #: src/constants/index.ts:18 src/views/notification/notificationColumns.tsx:29 msgid "Info" @@ -1693,6 +1721,10 @@ msgstr "Locations" msgid "Log" msgstr "日志" +#: src/routes/modules/nginx_log.ts:39 src/views/nginx_log/NginxLogList.vue:113 +msgid "Log List" +msgstr "日志列表" + #: src/routes/modules/auth.ts:14 src/views/other/Login.vue:222 msgid "Login" msgstr "登录" @@ -1804,6 +1836,7 @@ msgstr "多行指令" #: src/views/config/components/Mkdir.vue:64 #: src/views/config/configColumns.tsx:7 src/views/config/ConfigEditor.vue:256 #: src/views/environment/envColumns.tsx:9 +#: src/views/nginx_log/NginxLogList.vue:35 #: src/views/preference/components/AddPasskey.vue:75 #: src/views/site/ngx_conf/NgxUpstream.vue:177 #: src/views/site/site_category/columns.ts:7 @@ -1919,7 +1952,7 @@ msgstr "Nginx 错误日志路径" msgid "Nginx is not running" msgstr "Nginx 未启动" -#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:148 +#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:143 msgid "Nginx Log" msgstr "Nginx 日志" @@ -2163,6 +2196,7 @@ msgid "Password length cannot exceed 20 characters" msgstr "密码长度不能超过 20 个字符" #: src/views/config/ConfigEditor.vue:263 +#: src/views/nginx_log/NginxLogList.vue:43 #: src/views/site/ngx_conf/LocationEditor.vue:109 #: src/views/site/ngx_conf/LocationEditor.vue:137 msgid "Path" @@ -3198,7 +3232,7 @@ msgstr "限流" msgid "Tips" msgstr "提示" -#: src/views/notification/notificationColumns.tsx:44 +#: src/views/notification/notificationColumns.tsx:45 msgid "Title" msgstr "标题" @@ -3265,6 +3299,7 @@ msgid "Two-factor authentication required" msgstr "需要两步验证" #: src/views/certificate/CertificateList/certColumns.tsx:25 +#: src/views/nginx_log/NginxLogList.vue:20 #: src/views/notification/notificationColumns.tsx:9 msgid "Type" msgstr "类型" @@ -3366,6 +3401,7 @@ msgid "Version" msgstr "版本" #: src/components/StdDesign/StdDataDisplay/StdTable.vue:488 +#: src/views/nginx_log/NginxLogList.vue:143 #: src/views/site/ngx_conf/config_template/ConfigTemplate.vue:103 msgid "View" msgstr "查看" @@ -3797,9 +3833,6 @@ msgstr "你的 Passkeys" #~ msgid "HTTPS Listen Port" #~ msgstr "HTTPS 监听端口" -#~ msgid "Index (index)" -#~ msgstr "网站首页 (index)" - #~ msgid "Private Key Path (ssl_certificate_key)" #~ msgstr "私钥路径 (ssl_certificate_key)" diff --git a/app/src/language/zh_TW/app.po b/app/src/language/zh_TW/app.po index ea9b6c15..443a75ec 100644 --- a/app/src/language/zh_TW/app.po +++ b/app/src/language/zh_TW/app.po @@ -31,7 +31,12 @@ msgstr "多重要素驗證設定" msgid "About" msgstr "關於" -#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:75 +#: src/views/nginx_log/NginxLogList.vue:29 +#, fuzzy +msgid "Access Log" +msgstr "訪問日誌" + +#: src/routes/modules/nginx_log.ts:17 src/views/site/ngx_conf/LogEntry.vue:91 msgid "Access Logs" msgstr "訪問日誌" @@ -45,7 +50,8 @@ msgstr "ACME 用戶" #: src/views/certificate/DNSCredential.vue:33 #: src/views/config/configColumns.tsx:42 #: src/views/environment/envColumns.tsx:97 -#: src/views/notification/notificationColumns.tsx:65 +#: src/views/nginx_log/NginxLogList.vue:51 +#: src/views/notification/notificationColumns.tsx:66 #: src/views/preference/AuthSettings.vue:30 #: src/views/site/site_category/columns.ts:28 #: src/views/site/site_list/columns.tsx:76 src/views/stream/StreamList.vue:49 @@ -245,7 +251,7 @@ msgstr "認證設定" msgid "Author" msgstr "作者" -#: src/views/nginx_log/NginxLog.vue:152 +#: src/views/nginx_log/NginxLog.vue:149 msgid "Auto Refresh" msgstr "自動重新整理" @@ -261,6 +267,10 @@ msgstr "已啟用 %{name} 的自動續簽" msgid "Automatic Restart" msgstr "" +#: src/views/nginx_log/NginxLogList.vue:125 +msgid "Automatically indexed from site and stream configurations." +msgstr "" + #: src/views/certificate/CertificateEditor.vue:255 #: src/views/config/ConfigEditor.vue:213 src/views/config/ConfigList.vue:106 #: src/views/config/ConfigList.vue:180 src/views/nginx_log/NginxLog.vue:173 @@ -614,7 +624,7 @@ msgid "" "Backup files will be automatically downloaded to your computer." msgstr "" -#: src/views/notification/notificationColumns.tsx:58 +#: src/views/notification/notificationColumns.tsx:59 #: src/views/preference/components/Passkey.vue:95 #: src/views/site/site_category/columns.ts:16 src/views/user/userColumns.tsx:48 msgid "Created at" @@ -751,7 +761,7 @@ msgstr "描述" msgid "Destination file already exists" msgstr "目的檔案已存在" -#: src/views/notification/notificationColumns.tsx:52 +#: src/views/notification/notificationColumns.tsx:53 msgid "Details" msgstr "詳細資料" @@ -1084,7 +1094,12 @@ msgstr "環境" msgid "Error" msgstr "錯誤" -#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:83 +#: src/views/nginx_log/NginxLogList.vue:30 +#, fuzzy +msgid "Error Log" +msgstr "錯誤日誌" + +#: src/routes/modules/nginx_log.ts:24 src/views/site/ngx_conf/LogEntry.vue:99 msgid "Error Logs" msgstr "錯誤日誌" @@ -1509,6 +1524,12 @@ msgstr "ICP 編號" msgid "If left blank, the default CA Dir will be used." msgstr "如果留空,將使用默認的 CA Dir。" +#: src/views/nginx_log/NginxLogList.vue:127 +msgid "" +"If logs are not indexed, please check if the log file is under the directory " +"in Nginx.LogDirWhiteList." +msgstr "" + #: src/views/preference/AuthSettings.vue:145 msgid "" "If the number of login failed attempts from a ip reach the max attempts in " @@ -1536,6 +1557,15 @@ msgstr "導入" msgid "Import Certificate" msgstr "導入憑證" +#: src/views/nginx_log/NginxLogList.vue:135 +#, fuzzy +msgid "Indexed" +msgstr "網站首頁 (index)" + +#: src/views/nginx_log/NginxLogList.vue:132 +msgid "Indexing..." +msgstr "" + #: src/components/StdDesign/StdDetail/StdDetail.vue:81 #: src/constants/index.ts:18 src/views/notification/notificationColumns.tsx:29 msgid "Info" @@ -1756,6 +1786,11 @@ msgstr "Locations" msgid "Log" msgstr "日誌" +#: src/routes/modules/nginx_log.ts:39 src/views/nginx_log/NginxLogList.vue:113 +#, fuzzy +msgid "Log List" +msgstr "列表" + #: src/routes/modules/auth.ts:14 src/views/other/Login.vue:222 msgid "Login" msgstr "登入" @@ -1866,6 +1901,7 @@ msgstr "多行指令" #: src/views/config/components/Mkdir.vue:64 #: src/views/config/configColumns.tsx:7 src/views/config/ConfigEditor.vue:256 #: src/views/environment/envColumns.tsx:9 +#: src/views/nginx_log/NginxLogList.vue:35 #: src/views/preference/components/AddPasskey.vue:75 #: src/views/site/ngx_conf/NgxUpstream.vue:177 #: src/views/site/site_category/columns.ts:7 @@ -1985,7 +2021,7 @@ msgstr "Nginx 錯誤日誌路徑" msgid "Nginx is not running" msgstr "Nginx 未執行" -#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:148 +#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:143 msgid "Nginx Log" msgstr "Nginx 日誌" @@ -2235,6 +2271,7 @@ msgid "Password length cannot exceed 20 characters" msgstr "密碼長度不能超過 20 個字元" #: src/views/config/ConfigEditor.vue:263 +#: src/views/nginx_log/NginxLogList.vue:43 #: src/views/site/ngx_conf/LocationEditor.vue:109 #: src/views/site/ngx_conf/LocationEditor.vue:137 msgid "Path" @@ -3306,7 +3343,7 @@ msgstr "節流" msgid "Tips" msgstr "提示" -#: src/views/notification/notificationColumns.tsx:44 +#: src/views/notification/notificationColumns.tsx:45 msgid "Title" msgstr "標題" @@ -3373,6 +3410,7 @@ msgid "Two-factor authentication required" msgstr "需要多重因素驗證" #: src/views/certificate/CertificateList/certColumns.tsx:25 +#: src/views/nginx_log/NginxLogList.vue:20 #: src/views/notification/notificationColumns.tsx:9 msgid "Type" msgstr "類型" @@ -3474,6 +3512,7 @@ msgid "Version" msgstr "版本" #: src/components/StdDesign/StdDataDisplay/StdTable.vue:488 +#: src/views/nginx_log/NginxLogList.vue:143 #: src/views/site/ngx_conf/config_template/ConfigTemplate.vue:103 msgid "View" msgstr "檢視" @@ -3866,9 +3905,6 @@ msgstr "您的通行密鑰" #~ msgid "HTTPS Listen Port" #~ msgstr "HTTPS 監聽埠" -#~ msgid "Index (index)" -#~ msgstr "網站首頁 (index)" - #~ msgid "Private Key Path (ssl_certificate_key)" #~ msgstr "私鑰路徑 (ssl_certificate_key)" diff --git a/app/src/routes/modules/nginx_log.ts b/app/src/routes/modules/nginx_log.ts index 63f9d89b..28f06d4f 100644 --- a/app/src/routes/modules/nginx_log.ts +++ b/app/src/routes/modules/nginx_log.ts @@ -31,6 +31,13 @@ export const nginxLogRoutes: RouteRecordRaw[] = [ name: () => $gettext('Site Logs'), hiddenInSidebar: true, }, + }, { + path: 'list', + name: 'Log List', + component: () => import('@/views/nginx_log/NginxLogList.vue'), + meta: { + name: () => $gettext('Log List'), + }, }], }, ] diff --git a/app/src/views/nginx_log/NginxLog.vue b/app/src/views/nginx_log/NginxLog.vue index d4c66de8..ba67607e 100644 --- a/app/src/views/nginx_log/NginxLog.vue +++ b/app/src/views/nginx_log/NginxLog.vue @@ -11,16 +11,15 @@ let websocket: ReconnectingWebSocket | WebSocket const route = useRoute() const buffer = ref('') const page = ref(0) -const auto_refresh = ref(true) +const autoRefresh = ref(true) const router = useRouter() const loading = ref(false) const filter = ref('') +// Setup log control data based on route params const control = reactive({ type: logType(), - conf_name: route.query.conf_name as string, - server_idx: Number.parseInt(route.query.server_idx as string), - directive_idx: Number.parseInt(route.query.directive_idx as string), + log_path: route.query.log_path as string, }) function logType() { @@ -33,9 +32,7 @@ function openWs() { websocket = ws('/api/nginx_log') websocket.onopen = () => { - websocket.send(JSON.stringify({ - ...control, - })) + websocket.send(JSON.stringify(control)) } websocket.onmessage = (m: { data: string }) => { @@ -68,7 +65,7 @@ function init() { } function clearLog() { - logContainer.value!.innerHTML = '' + buffer.value = '' } onMounted(() => { @@ -79,10 +76,12 @@ onUnmounted(() => { websocket?.close() }) -watch(auto_refresh, value => { +watch(autoRefresh, async value => { if (value) { - openWs() clearLog() + await nextTick() + await init() + openWs() } else { websocket.close() @@ -90,28 +89,24 @@ watch(auto_refresh, value => { }) watch(route, () => { - init() - + // Update control data when route changes control.type = logType() - control.directive_idx = Number.parseInt(route.query.server_idx as string) - control.server_idx = Number.parseInt(route.query.directive_idx as string) - clearLog() + control.log_path = route.query.log_path as string - nextTick(() => { - websocket.send(JSON.stringify(control)) - }) + clearLog() + init() }) watch(control, () => { clearLog() - auto_refresh.value = true + autoRefresh.value = true nextTick(() => { websocket.send(JSON.stringify(control)) }) }) -function on_scroll_log() { +function onScrollLog() { if (!loading.value && page.value > 0) { loading.value = true @@ -131,8 +126,8 @@ function on_scroll_log() { } } -function debounce_scroll_log() { - return debounce(on_scroll_log, 100)() +function debounceScrollLog() { + return debounce(onScrollLog, 100)() } const computedBuffer = computed(() => { @@ -148,10 +143,15 @@ const computedBuffer = computed(() => { :title="$gettext('Nginx Log')" :bordered="false" > + - - - { ref="logContainer" v-dompurify-html="computedBuffer" class="nginx-log-container" - @scroll="debounce_scroll_log" + @scroll="debounceScrollLog" /> - + {{ $gettext('Back') }} diff --git a/app/src/views/nginx_log/NginxLogList.vue b/app/src/views/nginx_log/NginxLogList.vue new file mode 100644 index 00000000..6c0729f2 --- /dev/null +++ b/app/src/views/nginx_log/NginxLogList.vue @@ -0,0 +1,152 @@ + + + + + diff --git a/app/src/views/site/ngx_conf/LogEntry.vue b/app/src/views/site/ngx_conf/LogEntry.vue index 6b6d1845..2d5372e5 100644 --- a/app/src/views/site/ngx_conf/LogEntry.vue +++ b/app/src/views/site/ngx_conf/LogEntry.vue @@ -11,6 +11,8 @@ const props = defineProps<{ const accessIdx = ref() const errorIdx = ref() +const accessLogPath = ref() +const errorLogPath = ref() const hasAccessLog = computed(() => { let flag = false @@ -18,6 +20,14 @@ const hasAccessLog = computed(() => { if (v.directive === 'access_log') { flag = true accessIdx.value = k + + // Extract log path from directive params + if (v.params) { + const params = v.params.split(' ') + if (params.length > 0) { + accessLogPath.value = params[0] + } + } } }) @@ -30,6 +40,14 @@ const hasErrorLog = computed(() => { if (v.directive === 'error_log') { flag = true errorIdx.value = k + + // Extract log path from directive params + if (v.params) { + const params = v.params.split(' ') + if (params.length > 0) { + errorLogPath.value = params[0] + } + } } }) @@ -42,9 +60,8 @@ function on_click_access_log() { router.push({ path: '/nginx_log/site', query: { - server_idx: props.currentServerIdx, - directive_idx: accessIdx.value, - conf_name: props.name, + type: 'site', + log_path: accessLogPath.value, }, }) } @@ -53,9 +70,8 @@ function on_click_error_log() { router.push({ path: '/nginx_log/site', query: { - server_idx: props.currentServerIdx, - directive_idx: errorIdx.value, - conf_name: props.name, + type: 'site', + log_path: errorLogPath.value, }, }) } diff --git a/internal/cache/cache.go b/internal/cache/cache.go index 16b11760..14055a98 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -1,9 +1,10 @@ package cache import ( + "time" + "github.com/dgraph-io/ristretto/v2" "github.com/uozi-tech/cosy/logger" - "time" ) var cache *ristretto.Cache[string, any] @@ -19,6 +20,9 @@ func Init() { if err != nil { logger.Fatal("initializing local cache err", err) } + + // Initialize the nginx log scanner + InitNginxLogScanner() } func Set(key string, value interface{}, ttl time.Duration) { diff --git a/internal/cache/nginx_log.go b/internal/cache/nginx_log.go new file mode 100644 index 00000000..428976c7 --- /dev/null +++ b/internal/cache/nginx_log.go @@ -0,0 +1,585 @@ +package cache + +import ( + "os" + "path/filepath" + "regexp" + "strings" + "sync" + "time" + + "github.com/0xJacky/Nginx-UI/internal/helper" + "github.com/0xJacky/Nginx-UI/internal/nginx" + "github.com/0xJacky/Nginx-UI/settings" + "github.com/fsnotify/fsnotify" + "github.com/uozi-tech/cosy/logger" +) + +// NginxLogCache represents a cached log entry from nginx configuration +type NginxLogCache struct { + Path string `json:"path"` // Path to the log file + Type string `json:"type"` // Type of log: "access" or "error" + Name string `json:"name"` // Name of the log file +} + +// NginxLogScanner is responsible for scanning and watching nginx config files for log directives +type NginxLogScanner struct { + logCache map[string]*NginxLogCache // Map of log path to cache entry + cacheMutex sync.RWMutex // Mutex for protecting the cache + watcher *fsnotify.Watcher // File system watcher + scanTicker *time.Ticker // Ticker for periodic scanning + initialized bool // Whether the scanner has been initialized + scanning bool // Whether a scan is currently in progress + scanMutex sync.RWMutex // Mutex for protecting the scanning state + statusChan chan bool // Channel to broadcast scanning status changes + subscribers map[chan bool]struct{} // Set of subscribers + subscriberMux sync.RWMutex // Mutex for protecting the subscribers map +} + +// Add regex constants at package level +var ( + // logScanner is the singleton instance of NginxLogScanner + logScanner *NginxLogScanner + scannerInitMux sync.Mutex +) + +// Compile the regular expressions for matching log directives +var ( + // This regex matches: access_log or error_log, followed by a path, and optional parameters ending with semicolon + logDirectiveRegex = regexp.MustCompile(`(?m)(access_log|error_log)\s+([^\s;]+)(?:\s+[^;]+)?;`) +) + +// InitNginxLogScanner initializes the nginx log scanner +func InitNginxLogScanner() { + scanner := GetNginxLogScanner() + err := scanner.Initialize() + if err != nil { + logger.Error("Failed to initialize nginx log scanner:", err) + } +} + +// GetNginxLogScanner returns the singleton instance of NginxLogScanner +func GetNginxLogScanner() *NginxLogScanner { + scannerInitMux.Lock() + defer scannerInitMux.Unlock() + + if logScanner == nil { + logScanner = &NginxLogScanner{ + logCache: make(map[string]*NginxLogCache), + statusChan: make(chan bool, 10), // Buffer to prevent blocking + subscribers: make(map[chan bool]struct{}), + } + + // Start broadcaster goroutine + go logScanner.broadcastStatus() + } + return logScanner +} + +// broadcastStatus listens for status changes and broadcasts to all subscribers +func (s *NginxLogScanner) broadcastStatus() { + for status := range s.statusChan { + s.subscriberMux.RLock() + for ch := range s.subscribers { + // Non-blocking send to prevent slow subscribers from blocking others + select { + case ch <- status: + default: + // Skip if channel buffer is full + } + } + s.subscriberMux.RUnlock() + } +} + +// SubscribeStatusChanges allows a client to subscribe to scanning status changes +func SubscribeStatusChanges() chan bool { + s := GetNginxLogScanner() + ch := make(chan bool, 5) // Buffer to prevent blocking + + // Add to subscribers + s.subscriberMux.Lock() + s.subscribers[ch] = struct{}{} + s.subscriberMux.Unlock() + + // Send current status immediately + s.scanMutex.RLock() + currentStatus := s.scanning + s.scanMutex.RUnlock() + + // Non-blocking send + select { + case ch <- currentStatus: + default: + } + + return ch +} + +// UnsubscribeStatusChanges removes a subscriber from receiving status updates +func UnsubscribeStatusChanges(ch chan bool) { + s := GetNginxLogScanner() + + s.subscriberMux.Lock() + delete(s.subscribers, ch) + s.subscriberMux.Unlock() + + // Close the channel so the client knows it's unsubscribed + close(ch) +} + +// Initialize sets up the log scanner and starts watching for file changes +func (s *NginxLogScanner) Initialize() error { + if s.initialized { + return nil + } + + // Create a new watcher + watcher, err := fsnotify.NewWatcher() + if err != nil { + return err + } + s.watcher = watcher + + // Scan for the first time + err = s.ScanAllConfigs() + if err != nil { + return err + } + + // Setup watcher for config directory + configDir := filepath.Dir(nginx.GetConfPath("", "")) + availableDir := nginx.GetConfPath("sites-available", "") + enabledDir := nginx.GetConfPath("sites-enabled", "") + streamAvailableDir := nginx.GetConfPath("stream-available", "") + streamEnabledDir := nginx.GetConfPath("stream-enabled", "") + + // Watch the main directories + err = s.watcher.Add(configDir) + if err != nil { + logger.Error("Failed to watch config directory:", err) + } + + // Watch sites-available and sites-enabled if they exist + if _, err := os.Stat(availableDir); err == nil { + err = s.watcher.Add(availableDir) + if err != nil { + logger.Error("Failed to watch sites-available directory:", err) + } + } + + if _, err := os.Stat(enabledDir); err == nil { + err = s.watcher.Add(enabledDir) + if err != nil { + logger.Error("Failed to watch sites-enabled directory:", err) + } + } + + // Watch stream-available and stream-enabled if they exist + if _, err := os.Stat(streamAvailableDir); err == nil { + err = s.watcher.Add(streamAvailableDir) + if err != nil { + logger.Error("Failed to watch stream-available directory:", err) + } + } + + if _, err := os.Stat(streamEnabledDir); err == nil { + err = s.watcher.Add(streamEnabledDir) + if err != nil { + logger.Error("Failed to watch stream-enabled directory:", err) + } + } + + // Start the watcher goroutine + go s.watchForChanges() + + // Setup a ticker for periodic scanning (every 5 minutes) + s.scanTicker = time.NewTicker(5 * time.Minute) + go func() { + for range s.scanTicker.C { + err := s.ScanAllConfigs() + if err != nil { + logger.Error("Periodic config scan failed:", err) + } + } + }() + + s.initialized = true + return nil +} + +// watchForChanges handles the fsnotify events and triggers rescans when necessary +func (s *NginxLogScanner) watchForChanges() { + for { + select { + case event, ok := <-s.watcher.Events: + if !ok { + return + } + + // Check if this is a relevant event (create, write, rename, remove) + if event.Has(fsnotify.Create) || event.Has(fsnotify.Write) || + event.Has(fsnotify.Rename) || event.Has(fsnotify.Remove) { + // If it's a directory, add it to the watch list + if event.Has(fsnotify.Create) { + fi, err := os.Stat(event.Name) + if err == nil && fi.IsDir() { + _ = s.watcher.Add(event.Name) + } + } + + // Process file changes - no .conf restriction anymore + if !event.Has(fsnotify.Remove) { + logger.Debug("Config file changed:", event.Name) + // Give the system a moment to finish writing the file + time.Sleep(100 * time.Millisecond) + // Only scan the changed file instead of all configs + err := s.scanSingleFile(event.Name) + if err != nil { + logger.Error("Failed to scan changed file:", err) + } + } else { + // For removed files, we need to clean up any log entries that came from this file + // This would require tracking which logs came from which config files + // For now, we'll do a full rescan which is simpler but less efficient + err := s.ScanAllConfigs() + if err != nil { + logger.Error("Failed to rescan configs after file removal:", err) + } + } + } + case err, ok := <-s.watcher.Errors: + if !ok { + return + } + logger.Error("Watcher error:", err) + } + } +} + +// scanSingleFile scans a single file and updates the log cache accordingly +func (s *NginxLogScanner) scanSingleFile(filePath string) error { + // Set scanning state to true + s.scanMutex.Lock() + wasScanning := s.scanning + s.scanning = true + if !wasScanning { + // Only broadcast if status changed from not scanning to scanning + s.statusChan <- true + } + s.scanMutex.Unlock() + + // Ensure we reset scanning state when done + defer func() { + s.scanMutex.Lock() + s.scanning = false + // Broadcast the completion + s.statusChan <- false + s.scanMutex.Unlock() + }() + + // Create a temporary cache for new entries from this file + newEntries := make(map[string]*NginxLogCache) + + // Scan the file + err := s.scanConfigFile(filePath, newEntries) + if err != nil { + return err + } + + // Update the main cache with new entries + s.cacheMutex.Lock() + for path, entry := range newEntries { + s.logCache[path] = entry + } + s.cacheMutex.Unlock() + + return nil +} + +// ScanAllConfigs scans all nginx config files for log directives +func (s *NginxLogScanner) ScanAllConfigs() error { + // Set scanning state to true + s.scanMutex.Lock() + wasScanning := s.scanning + s.scanning = true + if !wasScanning { + // Only broadcast if status changed from not scanning to scanning + s.statusChan <- true + } + s.scanMutex.Unlock() + + // Ensure we reset scanning state when done + defer func() { + s.scanMutex.Lock() + s.scanning = false + // Broadcast the completion + s.statusChan <- false + s.scanMutex.Unlock() + }() + + // Initialize a new cache to replace the old one + newCache := make(map[string]*NginxLogCache) + + // Get the main config file + mainConfigPath := nginx.GetConfPath("", "nginx.conf") + err := s.scanConfigFile(mainConfigPath, newCache) + if err != nil { + logger.Error("Failed to scan main config:", err) + } + + // Scan sites-available directory - no .conf restriction anymore + sitesAvailablePath := nginx.GetConfPath("sites-available", "") + sitesAvailableFiles, err := os.ReadDir(sitesAvailablePath) + if err == nil { + for _, file := range sitesAvailableFiles { + if !file.IsDir() { + configPath := filepath.Join(sitesAvailablePath, file.Name()) + err := s.scanConfigFile(configPath, newCache) + if err != nil { + logger.Error("Failed to scan config:", configPath, err) + } + } + } + } + + // Scan stream-available directory if it exists + streamAvailablePath := nginx.GetConfPath("stream-available", "") + streamAvailableFiles, err := os.ReadDir(streamAvailablePath) + if err == nil { + for _, file := range streamAvailableFiles { + if !file.IsDir() { + configPath := filepath.Join(streamAvailablePath, file.Name()) + err := s.scanConfigFile(configPath, newCache) + if err != nil { + logger.Error("Failed to scan stream config:", configPath, err) + } + } + } + } + + // Replace the old cache with the new one + s.cacheMutex.Lock() + s.logCache = newCache + s.cacheMutex.Unlock() + + return nil +} + +// scanConfigFile scans a single config file for log directives using regex +func (s *NginxLogScanner) scanConfigFile(configPath string, cache map[string]*NginxLogCache) error { + // Open the file + file, err := os.Open(configPath) + if err != nil { + return err + } + defer file.Close() + + // Read the entire file content + content, err := os.ReadFile(configPath) + if err != nil { + return err + } + + // Find all matches of log directives + matches := logDirectiveRegex.FindAllSubmatch(content, -1) + for _, match := range matches { + if len(match) >= 3 { + directiveType := string(match[1]) // "access_log" or "error_log" + logPath := string(match[2]) // The log file path + + // Validate the log path + if isValidLogPath(logPath) { + logType := "access" + if directiveType == "error_log" { + logType = "error" + } + + cache[logPath] = &NginxLogCache{ + Path: logPath, + Type: logType, + Name: filepath.Base(logPath), + } + } + } + } + + // Look for include directives to process included files + includeRegex := regexp.MustCompile(`include\s+([^;]+);`) + includeMatches := includeRegex.FindAllSubmatch(content, -1) + + for _, match := range includeMatches { + if len(match) >= 2 { + includePath := string(match[1]) + // Handle glob patterns in include directives + if strings.Contains(includePath, "*") { + // If it's a relative path, make it absolute based on nginx config dir + if !filepath.IsAbs(includePath) { + configDir := filepath.Dir(nginx.GetConfPath("", "")) + includePath = filepath.Join(configDir, includePath) + } + + // Expand the glob pattern + matchedFiles, err := filepath.Glob(includePath) + if err != nil { + logger.Error("Error expanding glob pattern:", includePath, err) + continue + } + + // Process each matched file + for _, matchedFile := range matchedFiles { + fileInfo, err := os.Stat(matchedFile) + if err == nil && !fileInfo.IsDir() { + err = s.scanConfigFile(matchedFile, cache) + if err != nil { + logger.Error("Failed to scan included file:", matchedFile, err) + } + } + } + } else { + // Handle single file include + // If it's a relative path, make it absolute based on nginx config dir + if !filepath.IsAbs(includePath) { + configDir := filepath.Dir(nginx.GetConfPath("", "")) + includePath = filepath.Join(configDir, includePath) + } + + fileInfo, err := os.Stat(includePath) + if err == nil && !fileInfo.IsDir() { + err = s.scanConfigFile(includePath, cache) + if err != nil { + logger.Error("Failed to scan included file:", includePath, err) + } + } + } + } + } + + return nil +} + +// isLogPathUnderWhiteList checks if the log path is under one of the paths in LogDirWhiteList +// This is a duplicate of the function in nginx_log package to avoid import cycle +func isLogPathUnderWhiteList(path string) bool { + // deep copy + logDirWhiteList := append([]string{}, settings.NginxSettings.LogDirWhiteList...) + + accessLogPath := nginx.GetAccessLogPath() + errorLogPath := nginx.GetErrorLogPath() + + if accessLogPath != "" { + logDirWhiteList = append(logDirWhiteList, filepath.Dir(accessLogPath)) + } + if errorLogPath != "" { + logDirWhiteList = append(logDirWhiteList, filepath.Dir(errorLogPath)) + } + + for _, whitePath := range logDirWhiteList { + if helper.IsUnderDirectory(path, whitePath) { + return true + } + } + return false +} + +// isValidLogPath checks if a log path is valid: +// 1. It must be a regular file or a symlink to a regular file +// 2. It must not point to a console or special device +// 3. It must be under the whitelist directories +func isValidLogPath(logPath string) bool { + // First check if the path is under the whitelist + if !isLogPathUnderWhiteList(logPath) { + logger.Warn("Log path is not under whitelist:", logPath) + return false + } + + // Check if the path exists + fileInfo, err := os.Lstat(logPath) + if err != nil { + // If file doesn't exist, it might be created later + // We'll assume it's valid for now + return true + } + + // If it's a symlink, follow it + if fileInfo.Mode()&os.ModeSymlink != 0 { + linkTarget, err := os.Readlink(logPath) + if err != nil { + return false + } + + // Make absolute path if the link target is relative + if !filepath.IsAbs(linkTarget) { + linkTarget = filepath.Join(filepath.Dir(logPath), linkTarget) + } + + // Check the target file + targetInfo, err := os.Stat(linkTarget) + if err != nil { + return false + } + + // Only accept regular files as targets + return targetInfo.Mode().IsRegular() + } + + // For non-symlinks, just check if it's a regular file + return fileInfo.Mode().IsRegular() +} + +// Shutdown cleans up resources used by the scanner +func (s *NginxLogScanner) Shutdown() { + if s.watcher != nil { + s.watcher.Close() + } + + if s.scanTicker != nil { + s.scanTicker.Stop() + } + + // Clean up subscriber resources + s.subscriberMux.Lock() + // Close all subscriber channels + for ch := range s.subscribers { + close(ch) + } + // Clear the map + s.subscribers = make(map[chan bool]struct{}) + s.subscriberMux.Unlock() + + // Close the status channel + close(s.statusChan) +} + +// GetAllLogPaths returns all cached log paths +func GetAllLogPaths(filters ...func(*NginxLogCache) bool) []*NginxLogCache { + s := GetNginxLogScanner() + s.cacheMutex.RLock() + defer s.cacheMutex.RUnlock() + + result := make([]*NginxLogCache, 0, len(s.logCache)) + for _, cache := range s.logCache { + flag := true + if len(filters) > 0 { + for _, filter := range filters { + if !filter(cache) { + flag = false + break + } + } + } + if flag { + result = append(result, cache) + } + } + + return result +} + +// IsScanning returns whether a scan is currently in progress +func IsScanning() bool { + s := GetNginxLogScanner() + s.scanMutex.RLock() + defer s.scanMutex.RUnlock() + return s.scanning +} diff --git a/internal/nginx_log/log_list.go b/internal/nginx_log/log_list.go new file mode 100644 index 00000000..008d6b2a --- /dev/null +++ b/internal/nginx_log/log_list.go @@ -0,0 +1,43 @@ +package nginx_log + +import ( + "slices" + + "github.com/0xJacky/Nginx-UI/internal/cache" +) + +func typeToInt(t string) int { + if t == "access" { + return 0 + } + return 1 +} + +func sortCompare(i, j *cache.NginxLogCache, key string, order string) bool { + flag := false + + switch key { + case "type": + flag = typeToInt(i.Type) > typeToInt(j.Type) + default: + fallthrough + case "name": + flag = i.Name > j.Name + } + + if order == "asc" { + flag = !flag + } + + return flag +} + +func Sort(key string, order string, configs []*cache.NginxLogCache) []*cache.NginxLogCache { + slices.SortStableFunc(configs, func(i, j *cache.NginxLogCache) int { + if sortCompare(i, j, key, order) { + return 1 + } + return -1 + }) + return configs +}