Add caddy sync program
This commit is contained in:
		
							
								
								
									
										348
									
								
								coolify/proxy/caddy/dynamic/caddy-sync.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										348
									
								
								coolify/proxy/caddy/dynamic/caddy-sync.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,348 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"flag" | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | 	"text/template" | ||||||
|  |  | ||||||
|  | 	"git.site.quack-lab.dev/dave/cylogger" | ||||||
|  | 	"github.com/BurntSushi/toml" | ||||||
|  | 	"github.com/sergi/go-diff/diffmatchpatch" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type ServiceConfig struct { | ||||||
|  | 	Domains  []string `toml:"domains"` | ||||||
|  | 	Backend  string   `toml:"backend"` | ||||||
|  | 	IPRange  string   `toml:"ip_range"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type FileConfig struct { | ||||||
|  | 	Services []ServiceConfig `toml:"services"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Config struct { | ||||||
|  | 	IPRanges map[string]string `toml:"ip_ranges"` | ||||||
|  | 	Files    map[string]FileConfig `toml:"files"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Template for generating Caddy configuration | ||||||
|  | const caddyTemplate = `{{- range $service := .Services -}} | ||||||
|  | {{ join $service.Domains " " }} { | ||||||
|  |     @lan { | ||||||
|  |         remote_ip {{ $service.IPRange }} | ||||||
|  |     } | ||||||
|  |     handle @lan { | ||||||
|  |         reverse_proxy {{ $service.Backend }} | ||||||
|  |     } | ||||||
|  |     handle { | ||||||
|  |         respond "Njet Molotoff" 403 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | {{ end }}` | ||||||
|  |  | ||||||
|  | // Global variables | ||||||
|  | var ( | ||||||
|  | 	dryRun   = flag.Bool("dry-run", false, "Show what would be done without making changes") | ||||||
|  | 	force    = flag.Bool("force", false, "Force apply changes") | ||||||
|  | 	delete   = flag.Bool("d", false, "Delete files not in TOML config") | ||||||
|  | 	tomlFile = flag.String("config", "services.toml", "Path to TOML configuration file") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	flag.Parse() | ||||||
|  | 	cylogger.InitFlag() // Automatically reads -loglevel flag | ||||||
|  |  | ||||||
|  | 	logger := cylogger.Default.WithPrefix(fmt.Sprintf("config=%s", *tomlFile)) | ||||||
|  | 	logger.Info("Starting Caddy configuration sync") | ||||||
|  | 	logger.Debug("Flags: dry-run=%v, force=%v, delete=%v", *dryRun, *force, *delete) | ||||||
|  |  | ||||||
|  | 	if *delete { | ||||||
|  | 		logger.Warning("Delete mode enabled - will remove files not in TOML") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if *dryRun { | ||||||
|  | 		logger.Info("Dry-run mode enabled - no changes will be made") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Parse TOML configuration | ||||||
|  | 	logger.Debug("Loading configuration from TOML") | ||||||
|  | 	config, err := loadConfig(*tomlFile) | ||||||
|  | 	if err != nil { | ||||||
|  | 		logger.Error("Failed to load configuration: %v", err) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 	logger.Debug("Successfully loaded configuration") | ||||||
|  |  | ||||||
|  | 	// Validate configuration | ||||||
|  | 	logger.Debug("Validating configuration") | ||||||
|  | 	if err := validateConfig(config); err != nil { | ||||||
|  | 		logger.Error("Configuration validation failed: %v", err) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 	logger.Debug("Configuration validation passed") | ||||||
|  |  | ||||||
|  | 	// Perform synchronization | ||||||
|  | 	logger.Debug("Starting synchronization") | ||||||
|  | 	if err := syncConfigs(config); err != nil { | ||||||
|  | 		logger.Error("Synchronization failed: %v", err) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	logger.Info("Caddy configuration sync completed successfully") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func loadConfig(filename string) (*Config, error) { | ||||||
|  | 	logger := cylogger.Default.WithPrefix(fmt.Sprintf("file=%s", filename)) | ||||||
|  | 	logger.Debug("Loading TOML configuration") | ||||||
|  |  | ||||||
|  | 	var config Config | ||||||
|  |  | ||||||
|  | 	_, err := toml.DecodeFile(filename, &config) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("failed to decode TOML: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	logger.Trace("Loaded %d IP ranges and %d files", len(config.IPRanges), len(config.Files)) | ||||||
|  | 	return &config, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func validateConfig(config *Config) error { | ||||||
|  | 	// Validate IP ranges are defined | ||||||
|  | 	for filename, fileConfig := range config.Files { | ||||||
|  | 		for i, service := range fileConfig.Services { | ||||||
|  | 			if service.IPRange == "" { | ||||||
|  | 				return fmt.Errorf("service %d in file %s has no IP range specified", i+1, filename) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if _, exists := config.IPRanges[service.IPRange]; !exists { | ||||||
|  | 				return fmt.Errorf("IP range '%s' not defined for service %d in file %s", service.IPRange, i+1, filename) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if len(service.Domains) == 0 { | ||||||
|  | 				return fmt.Errorf("service %d in file %s has no domains specified", i+1, filename) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if service.Backend == "" { | ||||||
|  | 				return fmt.Errorf("service %d in file %s has no backend specified", i+1, filename) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func syncConfigs(config *Config) error { | ||||||
|  | 	// Get current directory | ||||||
|  | 	currentDir, err := os.Getwd() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("failed to get current directory: %w", err) | ||||||
|  | 	} | ||||||
|  | 	logger := cylogger.Default.WithPrefix(fmt.Sprintf("dir=%s", currentDir)) | ||||||
|  |  | ||||||
|  | 	// Get existing .caddy files | ||||||
|  | 	logger.Debug("Scanning for existing .caddy files") | ||||||
|  | 	existingFiles, err := getExistingCaddyFiles(currentDir) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("failed to get existing .caddy files: %w", err) | ||||||
|  | 	} | ||||||
|  | 	logger.Debug("Found %d existing .caddy files", len(existingFiles)) | ||||||
|  |  | ||||||
|  | 	// Track which files we've processed | ||||||
|  | 	processedFiles := make(map[string]bool) | ||||||
|  | 	var created, updated, unchanged int | ||||||
|  |  | ||||||
|  | 	// Process each file in configuration | ||||||
|  | 	logger.Debug("Processing %d configured files", len(config.Files)) | ||||||
|  | 	for filename, fileConfig := range config.Files { | ||||||
|  | 		caddyFilename := filename + ".caddy" | ||||||
|  | 		processedFiles[caddyFilename] = true | ||||||
|  | 		fileLogger := logger.WithPrefix(fmt.Sprintf("file=%s", caddyFilename)) | ||||||
|  | 		fileLogger.Trace("Processing") | ||||||
|  |  | ||||||
|  | 		// Generate expected content | ||||||
|  | 		expectedContent, err := generateCaddyContent(fileConfig, config.IPRanges) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return fmt.Errorf("failed to generate content for %s: %w", caddyFilename, err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Check if file exists and compare content | ||||||
|  | 		existingContent, exists := existingFiles[caddyFilename] | ||||||
|  |  | ||||||
|  | 		if !exists { | ||||||
|  | 			// File doesn't exist, create it | ||||||
|  | 			fileLogger.Info("Creating new file") | ||||||
|  | 			created++ | ||||||
|  | 			if !*dryRun { | ||||||
|  | 				if err := writeFile(caddyFilename, expectedContent); err != nil { | ||||||
|  | 					return fmt.Errorf("failed to create file %s: %w", caddyFilename, err) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} else if normalizeContent(existingContent) != normalizeContent(expectedContent) { | ||||||
|  | 			// File exists but content differs | ||||||
|  | 			fileLogger.Warning("File content differs, updating") | ||||||
|  | 			updated++ | ||||||
|  |  | ||||||
|  | 			// Show detailed diff of what's changing | ||||||
|  | 			generateAndLogDiff(fileLogger, caddyFilename, existingContent, expectedContent) | ||||||
|  |  | ||||||
|  | 			if !*dryRun { | ||||||
|  | 				if err := writeFile(caddyFilename, expectedContent); err != nil { | ||||||
|  | 					return fmt.Errorf("failed to update file %s: %w", caddyFilename, err) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			// File exists and content matches | ||||||
|  | 			fileLogger.Debug("File content matches, no action needed") | ||||||
|  | 			unchanged++ | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Handle orphaned files (files that exist but are not in config) | ||||||
|  | 	var orphanedCount int | ||||||
|  | 	for filename := range existingFiles { | ||||||
|  | 		if !processedFiles[filename] { | ||||||
|  | 			orphanedCount++ | ||||||
|  | 			orphanLogger := logger.WithPrefix(fmt.Sprintf("orphan=%s", filename)) | ||||||
|  | 			if *delete { | ||||||
|  | 				orphanLogger.Warning("Deleting orphaned file") | ||||||
|  | 				if !*dryRun { | ||||||
|  | 					if err := os.Remove(filename); err != nil { | ||||||
|  | 						return fmt.Errorf("failed to delete file %s: %w", filename, err) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				orphanLogger.Warning("Found orphaned file not in configuration (use -d to delete)") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	logger.Debug("Sync summary: created=%d, updated=%d, unchanged=%d, orphaned=%d", | ||||||
|  | 		created, updated, unchanged, orphanedCount) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func generateCaddyContent(fileConfig FileConfig, ipRanges map[string]string) (string, error) { | ||||||
|  | 	logger := cylogger.Default.WithPrefix(fmt.Sprintf("template services=%d", len(fileConfig.Services))) | ||||||
|  | 	logger.Debug("Generating Caddy content") | ||||||
|  |  | ||||||
|  | 	// Create template data with resolved IP ranges | ||||||
|  | 	templateData := struct { | ||||||
|  | 		Services []ServiceConfig | ||||||
|  | 	}{ | ||||||
|  | 		Services: make([]ServiceConfig, len(fileConfig.Services)), | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Resolve IP ranges for each service | ||||||
|  | 	for i, service := range fileConfig.Services { | ||||||
|  | 		logger.Trace("Resolving service %d: domains=%v, backend=%s, ip_range=%s", | ||||||
|  | 			i, service.Domains, service.Backend, service.IPRange) | ||||||
|  |  | ||||||
|  | 		templateData.Services[i] = ServiceConfig{ | ||||||
|  | 			Domains:  service.Domains, | ||||||
|  | 			Backend:  service.Backend, | ||||||
|  | 			IPRange:  ipRanges[service.IPRange], | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Create template with join function | ||||||
|  | 	funcMap := template.FuncMap{ | ||||||
|  | 		"join": strings.Join, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tmpl, err := template.New("caddy").Funcs(funcMap).Parse(caddyTemplate) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", fmt.Errorf("failed to parse template: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var result strings.Builder | ||||||
|  | 	if err := tmpl.Execute(&result, templateData); err != nil { | ||||||
|  | 		return "", fmt.Errorf("failed to execute template: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	content := result.String() | ||||||
|  | 	logger.Trace("Generated content:\n%s", content) | ||||||
|  | 	return content, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getExistingCaddyFiles(dir string) (map[string]string, error) { | ||||||
|  | 	logger := cylogger.Default.WithPrefix(fmt.Sprintf("scanner dir=%s", dir)) | ||||||
|  | 	logger.Debug("Scanning directory for .caddy files") | ||||||
|  |  | ||||||
|  | 	files := make(map[string]string) | ||||||
|  |  | ||||||
|  | 	entries, err := os.ReadDir(dir) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("failed to read directory: %w", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, entry := range entries { | ||||||
|  | 		if !entry.IsDir() && strings.HasSuffix(entry.Name(), ".caddy") { | ||||||
|  | 			// Skip services.toml and other non-caddy files | ||||||
|  | 			if entry.Name() == "services.toml" { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			logger.Trace("Found .caddy file: %s", entry.Name()) | ||||||
|  | 			content, err := os.ReadFile(filepath.Join(dir, entry.Name())) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, fmt.Errorf("failed to read file %s: %w", entry.Name(), err) | ||||||
|  | 			} | ||||||
|  | 			files[entry.Name()] = string(content) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	logger.Debug("Scanned %d files, found %d .caddy files", len(entries), len(files)) | ||||||
|  | 	return files, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func writeFile(filename, content string) error { | ||||||
|  | 	return os.WriteFile(filename, []byte(content), 0644) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func generateAndLogDiff(logger *cylogger.Logger, filename, existingContent, expectedContent string) { | ||||||
|  | 	logger.Info("Changes needed for %s:", filename) | ||||||
|  |  | ||||||
|  | 	// ANSI color codes | ||||||
|  | 	const ( | ||||||
|  | 		ColorRed     = "\033[31m" | ||||||
|  | 		ColorGreen   = "\033[32m" | ||||||
|  | 		ColorReset   = "\033[0m" | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	dmp := diffmatchpatch.New() | ||||||
|  | 	diffs := dmp.DiffMain(existingContent, expectedContent, false) | ||||||
|  |  | ||||||
|  | 	// Use the library's built-in pretty diff formatting | ||||||
|  | 	prettyDiff := dmp.DiffPrettyText(diffs) | ||||||
|  |  | ||||||
|  | 	// Print the pretty diff with colors | ||||||
|  | 	lines := strings.Split(prettyDiff, "\n") | ||||||
|  | 	for _, line := range lines { | ||||||
|  | 		if line == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if strings.HasPrefix(line, "+") { | ||||||
|  | 			fmt.Printf("%s%s%s\n", ColorGreen, line, ColorReset) | ||||||
|  | 		} else if strings.HasPrefix(line, "-") { | ||||||
|  | 			fmt.Printf("%s%s%s\n", ColorRed, line, ColorReset) | ||||||
|  | 		} else { | ||||||
|  | 			fmt.Printf("%s\n", line) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func normalizeContent(content string) string { | ||||||
|  | 	// Normalize content for comparison by trimming whitespace and standardizing line endings | ||||||
|  | 	lines := strings.Split(content, "\n") | ||||||
|  | 	var normalizedLines []string | ||||||
|  |  | ||||||
|  | 	for _, line := range lines { | ||||||
|  | 		normalizedLines = append(normalizedLines, strings.TrimSpace(line)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return strings.Join(normalizedLines, "\n") | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								coolify/proxy/caddy/dynamic/go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								coolify/proxy/caddy/dynamic/go.mod
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | module caddy-sync | ||||||
|  |  | ||||||
|  | go 1.23 | ||||||
|  |  | ||||||
|  | toolchain go1.23.6 | ||||||
|  |  | ||||||
|  | require ( | ||||||
|  | 	git.site.quack-lab.dev/dave/cylogger v1.5.0 | ||||||
|  | 	github.com/BurntSushi/toml v1.3.2 | ||||||
|  | 	github.com/sergi/go-diff v1.4.0 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | require ( | ||||||
|  | 	github.com/google/go-cmp v0.5.9 // indirect | ||||||
|  | 	github.com/hexops/valast v1.5.0 // indirect | ||||||
|  | 	golang.org/x/mod v0.7.0 // indirect | ||||||
|  | 	golang.org/x/sys v0.3.0 // indirect | ||||||
|  | 	golang.org/x/tools v0.4.0 // indirect | ||||||
|  | 	mvdan.cc/gofumpt v0.4.0 // indirect | ||||||
|  | ) | ||||||
							
								
								
									
										48
									
								
								coolify/proxy/caddy/dynamic/go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								coolify/proxy/caddy/dynamic/go.sum
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | git.site.quack-lab.dev/dave/cylogger v1.5.0 h1:9H/eEMD1dqJ9hEudwbszxrzE9lN0P0iCeYOzYRPMWOA= | ||||||
|  | git.site.quack-lab.dev/dave/cylogger v1.5.0/go.mod h1:wctgZplMvroA4X6p8f4B/LaCKtiBcT1Pp+L14kcS8jk= | ||||||
|  | github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= | ||||||
|  | github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= | ||||||
|  | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
|  | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||||
|  | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
|  | github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= | ||||||
|  | github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= | ||||||
|  | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||||||
|  | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||||
|  | github.com/hexops/autogold v0.8.1 h1:wvyd/bAJ+Dy+DcE09BoLk6r4Fa5R5W+O+GUzmR985WM= | ||||||
|  | github.com/hexops/autogold v0.8.1/go.mod h1:97HLDXyG23akzAoRYJh/2OBs3kd80eHyKPvZw0S5ZBY= | ||||||
|  | github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= | ||||||
|  | github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= | ||||||
|  | github.com/hexops/valast v1.5.0 h1:FBTuvVi0wjTngtXJRZXMbkN/Dn6DgsUsBwch2DUJU8Y= | ||||||
|  | github.com/hexops/valast v1.5.0/go.mod h1:Jcy1pNH7LNraVaAZDLyv21hHg2WBv9Nf9FL6fGxU7o4= | ||||||
|  | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||||||
|  | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= | ||||||
|  | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= | ||||||
|  | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||||
|  | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||||
|  | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||||
|  | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||||||
|  | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||||
|  | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||||
|  | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= | ||||||
|  | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= | ||||||
|  | github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= | ||||||
|  | github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= | ||||||
|  | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||||
|  | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= | ||||||
|  | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||||
|  | golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= | ||||||
|  | golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||||||
|  | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= | ||||||
|  | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
|  | golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= | ||||||
|  | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
|  | golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= | ||||||
|  | golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= | ||||||
|  | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
|  | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
|  | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
|  | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | ||||||
|  | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | ||||||
|  | mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM= | ||||||
|  | mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ= | ||||||
							
								
								
									
										61
									
								
								coolify/proxy/caddy/dynamic/services.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								coolify/proxy/caddy/dynamic/services.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | # IP ranges that can be referenced by name | ||||||
|  | [ip_ranges] | ||||||
|  | lan = "192.168.0.0/16 127.0.0.0/8 10.0.0.0/8 172.16.0.0/12" | ||||||
|  |  | ||||||
|  | # Caddy configuration files - each key creates a separate .caddy file | ||||||
|  | [files.actual] | ||||||
|  | services = [ | ||||||
|  |     { domains = ["actual.site.quack-lab.dev"], backend = "actual_server:5006", ip_range = "lan" } | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [files.meilisearch] | ||||||
|  | services = [ | ||||||
|  |     { domains = ["meili.site.quack-lab.dev"], backend = "meilisearch:7700", ip_range = "lan" } | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [files.meili-web] | ||||||
|  | services = [ | ||||||
|  |     { domains = ["meili-web.site.quack-lab.dev"], backend = "meili-web:24900", ip_range = "lan" } | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [files.grist] | ||||||
|  | services = [ | ||||||
|  |     { domains = ["grist.site.quack-lab.dev"], backend = "grist:8484", ip_range = "lan" } | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [files.nsq] | ||||||
|  | services = [ | ||||||
|  |     { domains = ["nsq.site.quack-lab.dev", "nsq-http.site.quack-lab.dev"], backend = "nsqd:4151", ip_range = "lan" }, | ||||||
|  |     { domains = ["nsqadmin.site.quack-lab.dev"], backend = "nsqadmin:4171", ip_range = "lan" } | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [files.monitoring] | ||||||
|  | services = [ | ||||||
|  |     { domains = ["prometheus.site.quack-lab.dev", "vmagent.site.quack-lab.dev"], backend = "host.docker.internal:43261", ip_range = "lan" }, | ||||||
|  |     { domains = ["victoria.site.quack-lab.dev"], backend = "host.docker.internal:8428", ip_range = "lan" }, | ||||||
|  |     { domains = ["grafana.site.quack-lab.dev"], backend = "grafana-jococcw004848ck4k0owwww0:43433", ip_range = "lan" }, | ||||||
|  |     { domains = ["nodeexporter-sparky.site.quack-lab.dev"], backend = "host.docker.internal:56546", ip_range = "lan" }, | ||||||
|  |     { domains = ["libre-metrics-exporter-dave.site.quack-lab.dev"], backend = "192.168.1.64:9646", ip_range = "lan" }, | ||||||
|  |     { domains = ["libre-metrics-exporter-jana.site.quack-lab.dev"], backend = "192.168.1.68:9646", ip_range = "lan" }, | ||||||
|  |     { domains = ["power-meter-reader.site.quack-lab.dev"], backend = "host.docker.internal:9646", ip_range = "lan" } | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [files.pdf] | ||||||
|  | services = [ | ||||||
|  |     { domains = ["pdf.site.quack-lab.dev"], backend = "stirling-pdf:8080", ip_range = "lan" } | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [files.torrent] | ||||||
|  | services = [ | ||||||
|  |     { domains = ["torrent.site.quack-lab.dev"], backend = "qbit:8080", ip_range = "lan" } | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [files.portainer] | ||||||
|  | services = [ | ||||||
|  |     { domains = ["portainer.site.quack-lab.dev"], backend = "portainer:9000", ip_range = "lan" } | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [files.webtop] | ||||||
|  | services = [ | ||||||
|  |     { domains = ["webtop.site.quack-lab.dev"], backend = "webtop:3000", ip_range = "lan" } | ||||||
|  | ] | ||||||
		Reference in New Issue
	
	Block a user