123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- package site
- import (
- "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 names
- func isValidDomain(domain string) bool {
- // Basic validation: contains at least one dot and no spaces
- return strings.Contains(domain, ".") && !strings.Contains(domain, " ")
- }
|