refactor: cache index

This commit is contained in:
Jacky 2025-04-04 02:00:18 +00:00
parent 5d8d96fd4f
commit 269397e114
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
20 changed files with 532 additions and 364 deletions

8
api/index/router.go Normal file
View file

@ -0,0 +1,8 @@
package index
import "github.com/gin-gonic/gin"
// InitRouter registers all the index related routes
func InitRouter(r *gin.RouterGroup) {
r.GET("index/status", GetIndexStatus)
}

50
api/index/sse.go Normal file
View file

@ -0,0 +1,50 @@
package index
import (
"io"
"time"
"github.com/0xJacky/Nginx-UI/api"
"github.com/0xJacky/Nginx-UI/internal/cache"
"github.com/gin-gonic/gin"
)
// GetIndexStatus is an SSE endpoint that sends real-time index status updates
func GetIndexStatus(c *gin.Context) {
api.SetSSEHeaders(c)
notify := c.Writer.CloseNotify()
// Subscribe to scanner status changes
statusChan := cache.SubscribeScanningStatus()
// Ensure we unsubscribe when the handler exits
defer cache.UnsubscribeScanningStatus(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
}
}
}

View file

@ -6,7 +6,6 @@ import (
"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"
@ -16,20 +15,24 @@ import (
)
const (
// PageSize defines the size of log chunks returned by the API
PageSize = 128 * 1024
)
// controlStruct represents the request parameters for getting log content
type controlStruct struct {
Type string `json:"type"`
LogPath string `json:"log_path"`
Type string `json:"type"` // Type of log: "access" or "error"
LogPath string `json:"log_path"` // Path to the log file
}
// nginxLogPageResp represents the response format for log content
type nginxLogPageResp struct {
Content string `json:"content"`
Page int64 `json:"page"`
Error string `json:"error,omitempty"`
Content string `json:"content"` // Log content
Page int64 `json:"page"` // Current page number
Error string `json:"error,omitempty"` // Error message if any
}
// GetNginxLogPage handles retrieving a page of log content from a log file
func GetNginxLogPage(c *gin.Context) {
page := cast.ToInt64(c.Query("page"))
if page < 0 {
@ -84,6 +87,7 @@ func GetNginxLogPage(c *gin.Context) {
logger.Error(err)
return
}
defer f.Close()
totalPage := logFileStat.Size() / PageSize
@ -100,7 +104,7 @@ func GetNginxLogPage(c *gin.Context) {
buf = make([]byte, PageSize)
offset = (page - 1) * PageSize
// seek
// seek to the correct position in the file
_, err = f.Seek(offset, io.SeekStart)
if err != nil && err != io.EOF {
c.JSON(http.StatusInternalServerError, nginxLogPageResp{
@ -125,28 +129,29 @@ func GetNginxLogPage(c *gin.Context) {
})
}
// GetLogList returns a list of Nginx log files
func GetLogList(c *gin.Context) {
filters := []func(*cache.NginxLogCache) bool{}
filters := []func(*nginx_log.NginxLogCache) bool{}
if c.Query("type") != "" {
filters = append(filters, func(entry *cache.NginxLogCache) bool {
filters = append(filters, func(entry *nginx_log.NginxLogCache) bool {
return entry.Type == c.Query("type")
})
}
if c.Query("name") != "" {
filters = append(filters, func(entry *cache.NginxLogCache) bool {
filters = append(filters, func(entry *nginx_log.NginxLogCache) bool {
return strings.Contains(entry.Name, c.Query("name"))
})
}
if c.Query("path") != "" {
filters = append(filters, func(entry *cache.NginxLogCache) bool {
filters = append(filters, func(entry *nginx_log.NginxLogCache) bool {
return strings.Contains(entry.Path, c.Query("path"))
})
}
data := cache.GetAllLogPaths(filters...)
data := nginx_log.GetAllLogs(filters...)
orderBy := c.DefaultQuery("sort_by", "name")
sort := c.DefaultQuery("order", "desc")

View file

@ -2,8 +2,8 @@ package nginx_log
import "github.com/gin-gonic/gin"
// InitRouter registers all the nginx log related routes
func InitRouter(r *gin.RouterGroup) {
r.GET("nginx_log", Log)
r.GET("nginx_logs", GetLogList)
r.GET("nginx_logs/index_status", GetNginxLogsLive)
}

View file

@ -15,10 +15,10 @@ func GetNginxLogsLive(c *gin.Context) {
notify := c.Writer.CloseNotify()
// Subscribe to scanner status changes
statusChan := cache.SubscribeStatusChanges()
statusChan := cache.SubscribeScanningStatus()
// Ensure we unsubscribe when the handler exits
defer cache.UnsubscribeStatusChanges(statusChan)
defer cache.UnsubscribeScanningStatus(statusChan)
// Main event loop
for {

View file

@ -16,6 +16,8 @@ import (
"github.com/uozi-tech/cosy/logger"
)
// getLogPath resolves the log file path based on the provided control parameters
// It checks if the path is under the whitelist directories
func getLogPath(control *controlStruct) (logPath string, err error) {
// If direct log path is provided, use it
if control.LogPath != "" {
@ -58,6 +60,7 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
return
}
// tailNginxLog tails the specified log file and sends each line to the websocket
func tailNginxLog(ws *websocket.Conn, controlChan chan controlStruct, errChan chan error) {
defer func() {
if err := recover(); err != nil {
@ -130,6 +133,7 @@ func tailNginxLog(ws *websocket.Conn, controlChan chan controlStruct, errChan ch
}
}
// handleLogControl processes websocket control messages
func handleLogControl(ws *websocket.Conn, controlChan chan controlStruct, errChan chan error) {
defer func() {
if err := recover(); err != nil {
@ -160,6 +164,7 @@ func handleLogControl(ws *websocket.Conn, controlChan chan controlStruct, errCha
}
}
// Log handles websocket connection for real-time log viewing
func Log(c *gin.Context) {
var upGrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {