mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 10:25:52 +02:00
refactor: cache index
This commit is contained in:
parent
5d8d96fd4f
commit
269397e114
20 changed files with 532 additions and 364 deletions
68
internal/nginx_log/log_cache.go
Normal file
68
internal/nginx_log/log_cache.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
package nginx_log
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
var (
|
||||
// logCache is the map to store all found log files
|
||||
logCache map[string]*NginxLogCache
|
||||
cacheMutex sync.RWMutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Initialize the cache
|
||||
logCache = make(map[string]*NginxLogCache)
|
||||
}
|
||||
|
||||
// AddLogPath adds a log path to the log cache
|
||||
func AddLogPath(path, logType, name string) {
|
||||
cacheMutex.Lock()
|
||||
defer cacheMutex.Unlock()
|
||||
|
||||
logCache[path] = &NginxLogCache{
|
||||
Path: path,
|
||||
Type: logType,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllLogPaths returns all cached log paths
|
||||
func GetAllLogPaths(filters ...func(*NginxLogCache) bool) []*NginxLogCache {
|
||||
cacheMutex.RLock()
|
||||
defer cacheMutex.RUnlock()
|
||||
|
||||
result := make([]*NginxLogCache, 0, len(logCache))
|
||||
for _, cache := range 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
|
||||
}
|
||||
|
||||
// ClearLogCache clears all entries in the log cache
|
||||
func ClearLogCache() {
|
||||
cacheMutex.Lock()
|
||||
defer cacheMutex.Unlock()
|
||||
|
||||
// Clear the cache
|
||||
logCache = make(map[string]*NginxLogCache)
|
||||
}
|
|
@ -2,10 +2,10 @@ package nginx_log
|
|||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/cache"
|
||||
)
|
||||
|
||||
// typeToInt converts log type string to a sortable integer
|
||||
// "access" = 0, "error" = 1
|
||||
func typeToInt(t string) int {
|
||||
if t == "access" {
|
||||
return 0
|
||||
|
@ -13,7 +13,9 @@ func typeToInt(t string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
func sortCompare(i, j *cache.NginxLogCache, key string, order string) bool {
|
||||
// sortCompare compares two log entries based on the specified key and order
|
||||
// Returns true if i should come after j in the sorted list
|
||||
func sortCompare(i, j *NginxLogCache, key string, order string) bool {
|
||||
flag := false
|
||||
|
||||
switch key {
|
||||
|
@ -32,8 +34,11 @@ func sortCompare(i, j *cache.NginxLogCache, key string, order string) bool {
|
|||
return flag
|
||||
}
|
||||
|
||||
func Sort(key string, order string, configs []*cache.NginxLogCache) []*cache.NginxLogCache {
|
||||
slices.SortStableFunc(configs, func(i, j *cache.NginxLogCache) int {
|
||||
// Sort sorts a list of NginxLogCache entries by the specified key and order
|
||||
// Supported keys: "type", "name"
|
||||
// Supported orders: "asc", "desc"
|
||||
func Sort(key string, order string, configs []*NginxLogCache) []*NginxLogCache {
|
||||
slices.SortStableFunc(configs, func(i, j *NginxLogCache) int {
|
||||
if sortCompare(i, j, key, order) {
|
||||
return 1
|
||||
}
|
||||
|
|
|
@ -2,19 +2,114 @@ package nginx_log
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/cache"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/0xJacky/Nginx-UI/settings"
|
||||
"path/filepath"
|
||||
"github.com/uozi-tech/cosy/logger"
|
||||
)
|
||||
|
||||
// IsLogPathUnderWhiteList checks if the log path is under one of the paths in LogDirWhiteList
|
||||
// Regular expression for log directives - matches access_log or error_log
|
||||
var logDirectiveRegex = regexp.MustCompile(`(?m)(access_log|error_log)\s+([^\s;]+)(?:\s+[^;]+)?;`)
|
||||
|
||||
// Use init function to automatically register callback
|
||||
func init() {
|
||||
// Register the callback directly with the global registry
|
||||
cache.RegisterCallback(scanForLogDirectives)
|
||||
}
|
||||
|
||||
// scanForLogDirectives scans and parses configuration files for log directives
|
||||
func scanForLogDirectives(configPath string, content []byte) error {
|
||||
// Clear previous scan results when scanning the main config
|
||||
if configPath == nginx.GetConfPath("", "nginx.conf") {
|
||||
ClearLogCache()
|
||||
}
|
||||
|
||||
// Find log directives using regex
|
||||
matches := logDirectiveRegex.FindAllSubmatch(content, -1)
|
||||
|
||||
// Parse log paths
|
||||
for _, match := range matches {
|
||||
if len(match) >= 3 {
|
||||
directiveType := string(match[1]) // "access_log" or "error_log"
|
||||
logPath := string(match[2]) // Path to log file
|
||||
|
||||
// Validate log path
|
||||
if IsLogPathUnderWhiteList(logPath) && isValidLogPath(logPath) {
|
||||
logType := "access"
|
||||
if directiveType == "error_log" {
|
||||
logType = "error"
|
||||
}
|
||||
|
||||
// Add to cache
|
||||
AddLogPath(logPath, logType, filepath.Base(logPath))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAllLogs returns all log paths
|
||||
func GetAllLogs(filters ...func(*NginxLogCache) bool) []*NginxLogCache {
|
||||
return GetAllLogPaths(filters...)
|
||||
}
|
||||
|
||||
// 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 in 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 the 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 the link target path absolute if it's 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()
|
||||
}
|
||||
|
||||
// IsLogPathUnderWhiteList checks if a log path is under one of the paths in LogDirWhiteList
|
||||
func IsLogPathUnderWhiteList(path string) bool {
|
||||
cacheKey := fmt.Sprintf("isLogPathUnderWhiteList:%s", path)
|
||||
res, ok := cache.Get(cacheKey)
|
||||
|
||||
// deep copy
|
||||
// Deep copy the whitelist
|
||||
logDirWhiteList := append([]string{}, settings.NginxSettings.LogDirWhiteList...)
|
||||
|
||||
accessLogPath := nginx.GetAccessLogPath()
|
||||
|
@ -27,7 +122,7 @@ func IsLogPathUnderWhiteList(path string) bool {
|
|||
logDirWhiteList = append(logDirWhiteList, filepath.Dir(errorLogPath))
|
||||
}
|
||||
|
||||
// no cache, check it
|
||||
// No cache, check it
|
||||
if !ok {
|
||||
for _, whitePath := range logDirWhiteList {
|
||||
if helper.IsUnderDirectory(path, whitePath) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue