index.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. package site
  2. import (
  3. "net"
  4. "path/filepath"
  5. "regexp"
  6. "strconv"
  7. "strings"
  8. "github.com/0xJacky/Nginx-UI/internal/cache"
  9. )
  10. type SiteIndex struct {
  11. Path string
  12. Content string
  13. Urls []string
  14. }
  15. var (
  16. IndexedSites = make(map[string]*SiteIndex)
  17. )
  18. func GetIndexedSite(path string) *SiteIndex {
  19. if site, ok := IndexedSites[path]; ok {
  20. return site
  21. }
  22. return &SiteIndex{}
  23. }
  24. func init() {
  25. cache.RegisterCallback(scanForSite)
  26. }
  27. func scanForSite(configPath string, content []byte) error {
  28. // Regular expressions for server_name and listen directives
  29. serverNameRegex := regexp.MustCompile(`(?m)server_name\s+([^;]+);`)
  30. listenRegex := regexp.MustCompile(`(?m)listen\s+([^;]+);`)
  31. // Find server blocks
  32. serverBlockRegex := regexp.MustCompile(`(?ms)server\s*\{[^\{]*((.*?\{.*?\})*?[^\}]*)\}`)
  33. serverBlocks := serverBlockRegex.FindAllSubmatch(content, -1)
  34. siteIndex := SiteIndex{
  35. Path: configPath,
  36. Content: string(content),
  37. Urls: []string{},
  38. }
  39. // Map to track hosts, their SSL status and port
  40. type hostInfo struct {
  41. hasSSL bool
  42. port int
  43. }
  44. hostMap := make(map[string]hostInfo)
  45. for _, block := range serverBlocks {
  46. serverBlockContent := block[0]
  47. // Extract server_name values
  48. serverNameMatches := serverNameRegex.FindSubmatch(serverBlockContent)
  49. if len(serverNameMatches) < 2 {
  50. continue
  51. }
  52. // Get all server names
  53. serverNames := strings.Fields(string(serverNameMatches[1]))
  54. var validServerNames []string
  55. // Filter valid domain names and IPs
  56. for _, name := range serverNames {
  57. // Skip placeholder names
  58. if name == "_" || name == "localhost" {
  59. continue
  60. }
  61. // Check if it's a valid IP
  62. if net.ParseIP(name) != nil {
  63. validServerNames = append(validServerNames, name)
  64. continue
  65. }
  66. // Basic domain validation
  67. if isValidDomain(name) {
  68. validServerNames = append(validServerNames, name)
  69. }
  70. }
  71. if len(validServerNames) == 0 {
  72. continue
  73. }
  74. // Check if SSL is enabled and extract port
  75. listenMatches := listenRegex.FindAllSubmatch(serverBlockContent, -1)
  76. hasSSL := false
  77. port := 80 // Default HTTP port
  78. for _, match := range listenMatches {
  79. if len(match) >= 2 {
  80. listenValue := string(match[1])
  81. if strings.Contains(listenValue, "ssl") {
  82. hasSSL = true
  83. port = 443 // Default HTTPS port
  84. }
  85. // Extract port number if present
  86. portRegex := regexp.MustCompile(`^(?:(\d+)|.*:(\d+))`)
  87. portMatches := portRegex.FindStringSubmatch(listenValue)
  88. if len(portMatches) > 0 {
  89. // Check which capture group has the port
  90. portStr := ""
  91. if portMatches[1] != "" {
  92. portStr = portMatches[1]
  93. } else if portMatches[2] != "" {
  94. portStr = portMatches[2]
  95. }
  96. if portStr != "" {
  97. if extractedPort, err := strconv.Atoi(portStr); err == nil {
  98. port = extractedPort
  99. }
  100. }
  101. }
  102. }
  103. }
  104. // Update host map with SSL status and port
  105. for _, name := range validServerNames {
  106. // Only update if this host doesn't have SSL yet or we're adding SSL now
  107. info, exists := hostMap[name]
  108. if !exists || (!info.hasSSL && hasSSL) {
  109. hostMap[name] = hostInfo{hasSSL: hasSSL, port: port}
  110. }
  111. }
  112. }
  113. // Generate URLs from the host map
  114. for host, info := range hostMap {
  115. protocol := "http"
  116. defaultPort := 80
  117. if info.hasSSL {
  118. protocol = "https"
  119. defaultPort = 443
  120. }
  121. url := protocol + "://" + host
  122. // Add port to URL if non-standard
  123. if info.port != defaultPort {
  124. url += ":" + strconv.Itoa(info.port)
  125. }
  126. siteIndex.Urls = append(siteIndex.Urls, url)
  127. }
  128. // Only store if we found valid URLs
  129. if len(siteIndex.Urls) > 0 {
  130. IndexedSites[filepath.Base(configPath)] = &siteIndex
  131. }
  132. return nil
  133. }
  134. // isValidDomain performs a basic validation of domain names
  135. func isValidDomain(domain string) bool {
  136. // Basic validation: contains at least one dot and no spaces
  137. return strings.Contains(domain, ".") && !strings.Contains(domain, " ")
  138. }