| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 | package siteimport (	"net"	"path/filepath"	"regexp"	"strconv"	"strings"	"github.com/0xJacky/Nginx-UI/internal/cache")type SiteIndex struct {	Path    string	Content string	Urls    []string}var (	IndexedSites = make(map[string]*SiteIndex))func GetIndexedSite(path string) *SiteIndex {	if site, ok := IndexedSites[path]; ok {		return site	}	return &SiteIndex{}}func init() {	cache.RegisterCallback(scanForSite)}func scanForSite(configPath string, content []byte) error {	// Regular expressions for server_name and listen directives	serverNameRegex := regexp.MustCompile(`(?m)server_name\s+([^;]+);`)	listenRegex := regexp.MustCompile(`(?m)listen\s+([^;]+);`)	// Find server blocks	serverBlockRegex := regexp.MustCompile(`(?ms)server\s*\{[^\{]*((.*?\{.*?\})*?[^\}]*)\}`)	serverBlocks := serverBlockRegex.FindAllSubmatch(content, -1)	siteIndex := SiteIndex{		Path:    configPath,		Content: string(content),		Urls:    []string{},	}	// Map to track hosts, their SSL status and port	type hostInfo struct {		hasSSL bool		port   int	}	hostMap := make(map[string]hostInfo)	for _, block := range serverBlocks {		serverBlockContent := block[0]		// Extract server_name values		serverNameMatches := serverNameRegex.FindSubmatch(serverBlockContent)		if len(serverNameMatches) < 2 {			continue		}		// Get all server names		serverNames := strings.Fields(string(serverNameMatches[1]))		var validServerNames []string		// Filter valid domain names and IPs		for _, name := range serverNames {			// Skip placeholder names			if name == "_" || name == "localhost" {				continue			}			// Check if it's a valid IP			if net.ParseIP(name) != nil {				validServerNames = append(validServerNames, name)				continue			}			// Basic domain validation			if isValidDomain(name) {				validServerNames = append(validServerNames, name)			}		}		if len(validServerNames) == 0 {			continue		}		// Check if SSL is enabled and extract port		listenMatches := listenRegex.FindAllSubmatch(serverBlockContent, -1)		hasSSL := false		port := 80 // Default HTTP port		for _, match := range listenMatches {			if len(match) >= 2 {				listenValue := string(match[1])				if strings.Contains(listenValue, "ssl") {					hasSSL = true					port = 443 // Default HTTPS port				}				// Extract port number if present				portRegex := regexp.MustCompile(`^(?:(\d+)|.*:(\d+))`)				portMatches := portRegex.FindStringSubmatch(listenValue)				if len(portMatches) > 0 {					// Check which capture group has the port					portStr := ""					if portMatches[1] != "" {						portStr = portMatches[1]					} else if portMatches[2] != "" {						portStr = portMatches[2]					}					if portStr != "" {						if extractedPort, err := strconv.Atoi(portStr); err == nil {							port = extractedPort						}					}				}			}		}		// Update host map with SSL status and port		for _, name := range validServerNames {			// Only update if this host doesn't have SSL yet or we're adding SSL now			info, exists := hostMap[name]			if !exists || (!info.hasSSL && hasSSL) {				hostMap[name] = hostInfo{hasSSL: hasSSL, port: port}			}		}	}	// Generate URLs from the host map	for host, info := range hostMap {		protocol := "http"		defaultPort := 80		if info.hasSSL {			protocol = "https"			defaultPort = 443		}		url := protocol + "://" + host		// Add port to URL if non-standard		if info.port != defaultPort {			url += ":" + strconv.Itoa(info.port)		}		siteIndex.Urls = append(siteIndex.Urls, url)	}	// Only store if we found valid URLs	if len(siteIndex.Urls) > 0 {		IndexedSites[filepath.Base(configPath)] = &siteIndex	}	return nil}// isValidDomain performs a basic validation of domain namesfunc isValidDomain(domain string) bool {	// Basic validation: contains at least one dot and no spaces	return strings.Contains(domain, ".") && !strings.Contains(domain, " ")}
 |