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