This commit is contained in:
hhftechnologies
2025-05-19 09:31:07 +05:30
parent f5f870559e
commit 8f777cdafa

172
main.go
View File

@@ -25,43 +25,40 @@ type Plugin struct {
IconPath string `json:"iconPath"`
Import string `json:"import"`
Summary string `json:"summary"`
// Add any other fields that might be in your JSON, e.g., author, version
Author string `json:"author,omitempty"`
Version string `json:"version,omitempty"`
TestedWith string `json:"tested_with,omitempty"` // Example: "Traefik v2.10+"
Stars int `json:"stars,omitempty"` // Example: GitHub stars
Homepage string `json:"homepage,omitempty"` // Example: Link to plugin's GitHub repo
Docs string `json:"docs,omitempty"` // Example: Link to plugin's documentation
TestedWith string `json:"tested_with,omitempty"`
Stars int `json:"stars,omitempty"`
Homepage string `json:"homepage,omitempty"`
Docs string `json:"docs,omitempty"`
}
// Configuration represents the application configuration
type Configuration struct {
PangolinAPIURL string
TraefikAPIURL string
TraefikConfDir string
DBPath string
Port string
UIPath string
ConfigDir string
CheckInterval time.Duration
GenerateInterval time.Duration
ServiceInterval time.Duration // New field for service check interval
Debug bool
AllowCORS bool
CORSOrigin string
ActiveDataSource string
TraefikStaticConfigPath string // New: Path to traefik_config.yml or traefik.yml
PluginsJSONURL string // New: URL to the plugins.json file
PangolinAPIURL string
TraefikAPIURL string
TraefikConfDir string
DBPath string
Port string
UIPath string
ConfigDir string
CheckInterval time.Duration
GenerateInterval time.Duration
ServiceInterval time.Duration
Debug bool
AllowCORS bool
CORSOrigin string
ActiveDataSource string
TraefikStaticConfigPath string
PluginsJSONURL string
}
// DiscoverTraefikAPI attempts to discover the Traefik API by trying common URLs
func DiscoverTraefikAPI() (string, error) {
client := &http.Client{
Timeout: 2 * time.Second, // Short timeout for discovery
Timeout: 2 * time.Second,
}
// Common URLs to try
urls := []string{
"http://host.docker.internal:8080",
"http://localhost:8080",
@@ -81,22 +78,18 @@ func DiscoverTraefikAPI() (string, error) {
resp.Body.Close()
}
}
return "", nil // Return empty string without error to allow fallbacks
return "", nil
}
func main() {
log.Println("Starting Middleware Manager...")
// Parse command line flags
var debug bool
flag.BoolVar(&debug, "debug", false, "Enable debug mode")
flag.Parse()
// Load configuration
cfg := loadConfiguration(debug)
// Try to discover Traefik API URL if not set in environment
if os.Getenv("TRAEFIK_API_URL") == "" {
if discoveredURL, err := DiscoverTraefikAPI(); err == nil && discoveredURL != "" {
log.Printf("Auto-discovered Traefik API URL: %s", discoveredURL)
@@ -104,53 +97,51 @@ func main() {
}
}
// Initialize database
db, err := database.InitDB(cfg.DBPath)
if err != nil {
log.Fatalf("Failed to initialize database: %v", err)
}
defer db.Close()
// Ensure config directory exists
configDir := cfg.ConfigDir
if err := config.EnsureConfigDirectory(configDir); err != nil {
log.Printf("Warning: Failed to create config directory: %v", err)
}
// Save default templates file if it doesn't exist
if err := config.SaveTemplateFile(configDir); err != nil {
log.Printf("Warning: Failed to save default templates: %v", err)
}
// Load default middleware templates
if err := config.LoadDefaultTemplates(db); err != nil {
log.Printf("Warning: Failed to load default templates: %v", err)
log.Printf("Warning: Failed to save default middleware templates: %v", err)
}
if err := config.LoadDefaultTemplates(db); err != nil {
log.Printf("Warning: Failed to load default middleware templates: %v", err)
}
if err := config.SaveTemplateServicesFile(configDir); err != nil {
log.Printf("Warning: Failed to save default service templates: %v", err)
}
if err := config.LoadDefaultServiceTemplates(db); err != nil {
log.Printf("Warning: Failed to load default service templates: %v", err)
}
// Initialize config manager
configManager, err := services.NewConfigManager(filepath.Join(configDir, "config.json"))
if err != nil {
log.Fatalf("Failed to initialize config manager: %v", err)
}
// Ensure default data sources are configured with potentially discovered URL
configManager.EnsureDefaultDataSources(cfg.PangolinAPIURL, cfg.TraefikAPIURL)
// Create stop channel for graceful shutdown
stopChan := make(chan struct{})
// Start resource watcher with config manager
resourceWatcher, err := services.NewResourceWatcher(db, configManager)
if err != nil {
log.Fatalf("Failed to create resource watcher: %v", err)
}
go resourceWatcher.Start(cfg.CheckInterval)
// Start configuration generator
configGenerator := services.NewConfigGenerator(db, cfg.TraefikConfDir, configManager)
go configGenerator.Start(cfg.GenerateInterval)
// Start API server
serverConfig := api.ServerConfig{
Port: cfg.Port,
UIPath: cfg.UIPath,
@@ -158,20 +149,26 @@ func main() {
AllowCORS: cfg.AllowCORS,
CORSOrigin: cfg.CORSOrigin,
}
// so handlers can access TraefikStaticConfigPath and PluginsJSONURL
server := api.NewServer(db.DB, serverConfig, configManager, cfg.TraefikStaticConfigPath, cfg.PluginsJSONURL)
go func() {
if err := server.Start(); err != nil {
log.Printf("Server error: %v", err)
close(stopChan) // Ensure stopChan is closed on server error
close(stopChan)
}
}()
// Wait for shutdown signal or server error
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
serviceWatcher, err := services.NewServiceWatcher(db, configManager)
if err != nil {
log.Printf("Warning: Failed to create service watcher: %v", err)
serviceWatcher = nil
} else {
go serviceWatcher.Start(cfg.ServiceInterval)
}
select {
case <-signalChan:
log.Println("Received shutdown signal")
@@ -179,79 +176,70 @@ func main() {
log.Println("Received stop signal from server")
}
// Start service watcher with config manager
serviceWatcher, err := services.NewServiceWatcher(db, configManager)
if err != nil {
log.Printf("Warning: Failed to create service watcher: %v", err)
} else {
// Use the same interval as the resource watcher, or a different one if preferred
go serviceWatcher.Start(cfg.CheckInterval)
}
// Graceful shutdown
log.Println("Shutting down...")
resourceWatcher.Stop()
configGenerator.Stop()
if serviceWatcher != nil { // Check if serviceWatcher was initialized
if serviceWatcher != nil {
serviceWatcher.Stop()
}
server.Stop() // Ensure server has a Stop method // Add this line
configGenerator.Stop()
server.Stop()
log.Println("Middleware Manager stopped")
}
// loadConfiguration loads configuration from environment variables
func loadConfiguration(debug bool) Configuration {
// Default check interval is 30 seconds
checkInterval := 30 * time.Second
if intervalStr := getEnv("CHECK_INTERVAL_SECONDS", "30"); intervalStr != "" {
if interval, err := strconv.Atoi(intervalStr); err == nil && interval > 0 {
checkInterval = time.Duration(interval) * time.Second
}
}
// Default generate interval is 10 seconds
generateInterval := 10 * time.Second
if intervalStr := getEnv("GENERATE_INTERVAL_SECONDS", "10"); intervalStr != "" {
if interval, err := strconv.Atoi(intervalStr); err == nil && interval > 0 {
generateInterval = time.Duration(interval) * time.Second
}
}
// Allow CORS if specified
parsedServiceInterval := 30 * time.Second
if intervalStr := getEnv("SERVICE_INTERVAL_SECONDS", "30"); intervalStr != "" {
if interval, err := strconv.Atoi(intervalStr); err == nil && interval > 0 {
parsedServiceInterval = time.Duration(interval) * time.Second
}
}
allowCORS := false
if corsStr := getEnv("ALLOW_CORS", "false"); corsStr != "" {
allowCORS = strings.ToLower(corsStr) == "true"
}
// Override debug mode from environment if specified
if debugStr := getEnv("DEBUG", ""); debugStr != "" {
debug = strings.ToLower(debugStr) == "true"
}
return Configuration{
PangolinAPIURL: getEnv("PANGOLIN_API_URL", "http://pangolin:3001/api/v1"),
// Changed to use host.docker.internal as first default to better support Docker environments
TraefikAPIURL: getEnv("TRAEFIK_API_URL", "http://host.docker.internal:8080"),
TraefikConfDir: getEnv("TRAEFIK_CONF_DIR", "/conf"),
DBPath: getEnv("DB_PATH", "/data/middleware.db"),
Port: getEnv("PORT", "3456"),
UIPath: getEnv("UI_PATH", "/app/ui/build"),
ConfigDir: getEnv("CONFIG_DIR", "/app/config"),
ActiveDataSource: getEnv("ACTIVE_DATA_SOURCE", "pangolin"),
CheckInterval: checkInterval,
GenerateInterval: generateInterval,
Debug: debug,
AllowCORS: allowCORS,
CORSOrigin: getEnv("CORS_ORIGIN", ""),
TraefikStaticConfigPath: getEnv("TRAEFIK_STATIC_CONFIG_PATH", "/etc/traefik/traefik.yml"), // New
PluginsJSONURL: getEnv("PLUGINS_JSON_URL", "https://raw.githubusercontent.com/hhftechnology/middleware-manager/traefik-int/plugin/plugins.json"), // New
PangolinAPIURL: getEnv("PANGOLIN_API_URL", "http://pangolin:3001/api/v1"),
TraefikAPIURL: getEnv("TRAEFIK_API_URL", "http://host.docker.internal:8080"),
TraefikConfDir: getEnv("TRAEFIK_CONF_DIR", "/conf"),
DBPath: getEnv("DB_PATH", "/data/middleware.db"),
Port: getEnv("PORT", "3456"),
UIPath: getEnv("UI_PATH", "/app/ui/build"),
ConfigDir: getEnv("CONFIG_DIR", "/app/config"),
ActiveDataSource: getEnv("ACTIVE_DATA_SOURCE", "pangolin"),
CheckInterval: checkInterval,
GenerateInterval: generateInterval,
ServiceInterval: parsedServiceInterval,
Debug: debug,
AllowCORS: allowCORS,
CORSOrigin: getEnv("CORS_ORIGIN", ""),
TraefikStaticConfigPath: getEnv("TRAEFIK_STATIC_CONFIG_PATH", "/etc/traefik/traefik.yml"),
PluginsJSONURL: getEnv("PLUGINS_JSON_URL", "https://raw.githubusercontent.com/hhftechnology/middleware-manager/traefik-int/plugin/plugins.json"),
}
}
// getEnv gets an environment variable or returns a default value
func getEnv(key, fallback string) string {
if value, exists := os.LookupEnv(key); exists {
return value
}
return fallback
}
}