preflight_service.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package nginx_log
  2. import (
  3. "fmt"
  4. "os"
  5. "github.com/0xJacky/Nginx-UI/internal/event"
  6. "github.com/0xJacky/Nginx-UI/internal/nginx"
  7. "github.com/0xJacky/Nginx-UI/internal/nginx_log/indexer"
  8. "github.com/uozi-tech/cosy/logger"
  9. )
  10. // FileInfo represents basic file information for internal use
  11. type FileInfo struct {
  12. Exists bool `json:"exists"`
  13. Readable bool `json:"readable"`
  14. Size int64 `json:"size,omitempty"`
  15. LastModified int64 `json:"last_modified,omitempty"`
  16. }
  17. // TimeRange represents a time range for log data for internal use
  18. type TimeRange struct {
  19. Start int64 `json:"start"`
  20. End int64 `json:"end"`
  21. }
  22. // PreflightResponse represents the response from preflight checks for internal use
  23. type PreflightResponse struct {
  24. Available bool `json:"available"`
  25. IndexStatus string `json:"index_status"`
  26. Message string `json:"message,omitempty"`
  27. TimeRange *TimeRange `json:"time_range,omitempty"`
  28. FileInfo *FileInfo `json:"file_info,omitempty"`
  29. }
  30. // PreflightService handles preflight checks for log files
  31. type PreflightService struct{}
  32. // NewPreflightService creates a new preflight service
  33. func NewPreflightService() *PreflightService {
  34. return &PreflightService{}
  35. }
  36. // CheckLogPreflight performs preflight checks for a log file
  37. func (ps *PreflightService) CheckLogPreflight(logPath string) (*PreflightResponse, error) {
  38. // Use default access log path if logPath is empty
  39. if logPath == "" {
  40. defaultLogPath := nginx.GetAccessLogPath()
  41. if defaultLogPath != "" {
  42. logPath = defaultLogPath
  43. logger.Debugf("Using default access log path for preflight: %s", logPath)
  44. }
  45. }
  46. // Get searcher to check index status
  47. searcherService := GetModernSearcher()
  48. if searcherService == nil {
  49. return nil, ErrModernSearcherNotAvailable
  50. }
  51. // Check if the specific file is currently being indexed
  52. processingManager := event.GetProcessingStatusManager()
  53. currentStatus := processingManager.GetCurrentStatus()
  54. // First check if the file exists and get file info
  55. var fileInfo *os.FileInfo
  56. if logPath != "" {
  57. if stat, err := os.Stat(logPath); os.IsNotExist(err) {
  58. // File doesn't exist - check for historical data
  59. return ps.handleMissingFile(logPath, searcherService)
  60. } else if err != nil {
  61. // Permission or other file system error - map to error status
  62. return &PreflightResponse{
  63. Available: false,
  64. IndexStatus: string(indexer.IndexStatusError),
  65. Message: fmt.Sprintf("Cannot access log file %s: %v", logPath, err),
  66. FileInfo: &FileInfo{
  67. Exists: true,
  68. Readable: false,
  69. },
  70. }, nil
  71. } else {
  72. fileInfo = &stat
  73. }
  74. }
  75. // Check if searcher is healthy
  76. searcherHealthy := searcherService.IsHealthy()
  77. // Get detailed file status from log file manager
  78. return ps.buildPreflightResponse(logPath, fileInfo, searcherHealthy, &currentStatus)
  79. }
  80. // handleMissingFile handles the case when a log file doesn't exist
  81. func (ps *PreflightService) handleMissingFile(logPath string, searcherService interface{}) (*PreflightResponse, error) {
  82. searcherHealthy := searcherService.(interface{ IsHealthy() bool }).IsHealthy()
  83. logFileManager := GetLogFileManager()
  84. if logFileManager != nil {
  85. logGroup, err := logFileManager.GetLogByPath(logPath)
  86. if err == nil && logGroup != nil && logGroup.LastIndexed > 0 {
  87. // File has historical index data
  88. response := &PreflightResponse{
  89. Available: searcherHealthy,
  90. IndexStatus: string(indexer.IndexStatusIndexed),
  91. Message: "File indexed (historical data available)",
  92. FileInfo: &FileInfo{
  93. Exists: false,
  94. Readable: false,
  95. },
  96. }
  97. if logGroup.HasTimeRange {
  98. response.TimeRange = &TimeRange{
  99. Start: logGroup.TimeRangeStart,
  100. End: logGroup.TimeRangeEnd,
  101. }
  102. }
  103. return response, nil
  104. }
  105. }
  106. // File doesn't exist and no historical data
  107. return &PreflightResponse{
  108. Available: false,
  109. IndexStatus: string(indexer.IndexStatusNotIndexed),
  110. Message: "Log file does not exist",
  111. FileInfo: &FileInfo{
  112. Exists: false,
  113. Readable: false,
  114. },
  115. }, nil
  116. }
  117. // buildPreflightResponse builds the preflight response for existing files
  118. func (ps *PreflightService) buildPreflightResponse(logPath string, fileInfo *os.FileInfo, searcherHealthy bool, currentStatus *event.ProcessingStatusData) (*PreflightResponse, error) {
  119. logFileManager := GetLogFileManager()
  120. var indexStatus string = string(indexer.IndexStatusNotIndexed)
  121. var available bool = false
  122. response := &PreflightResponse{}
  123. if logFileManager != nil && logPath != "" {
  124. logGroup, err := logFileManager.GetLogByPath(logPath)
  125. if err == nil && logGroup != nil {
  126. // Determine status based on indexing state
  127. if logGroup.LastIndexed > 0 {
  128. indexStatus = string(indexer.IndexStatusIndexed)
  129. available = searcherHealthy
  130. } else if currentStatus.NginxLogIndexing {
  131. indexStatus = string(indexer.IndexStatusIndexing)
  132. available = false
  133. } else {
  134. indexStatus = string(indexer.IndexStatusNotIndexed)
  135. available = false
  136. }
  137. response.Available = available
  138. response.IndexStatus = indexStatus
  139. // Add time range if available
  140. if logGroup.HasTimeRange {
  141. response.TimeRange = &TimeRange{
  142. Start: logGroup.TimeRangeStart,
  143. End: logGroup.TimeRangeEnd,
  144. }
  145. }
  146. } else {
  147. // File not in database or error getting it
  148. if currentStatus.NginxLogIndexing {
  149. indexStatus = string(indexer.IndexStatusQueued)
  150. } else {
  151. indexStatus = string(indexer.IndexStatusNotIndexed)
  152. }
  153. available = false
  154. response.Available = available
  155. response.IndexStatus = indexStatus
  156. response.Message = "Log file not indexed yet"
  157. }
  158. } else {
  159. // Fallback to basic status
  160. response.Available = searcherHealthy
  161. response.IndexStatus = string(indexer.IndexStatusNotIndexed)
  162. }
  163. // Add file information if available
  164. if fileInfo != nil {
  165. response.FileInfo = &FileInfo{
  166. Exists: true,
  167. Readable: true,
  168. Size: (*fileInfo).Size(),
  169. LastModified: (*fileInfo).ModTime().Unix(),
  170. }
  171. }
  172. logger.Debugf("Preflight response: log_path=%s, available=%v, index_status=%s",
  173. logPath, response.Available, response.IndexStatus)
  174. return response, nil
  175. }