index.go 3.0 KB

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