@@ -18,31 +18,47 @@ import (
1818 "github.com/hhftechnology/middleware-manager/services"
1919)
2020
21+ // Plugin represents the structure of a plugin in the JSON file
22+ type Plugin struct {
23+ DisplayName string `json:"displayName"`
24+ Type string `json:"type"`
25+ IconPath string `json:"iconPath"`
26+ Import string `json:"import"`
27+ Summary string `json:"summary"`
28+ Author string `json:"author,omitempty"`
29+ Version string `json:"version,omitempty"`
30+ TestedWith string `json:"tested_with,omitempty"`
31+ Stars int `json:"stars,omitempty"`
32+ Homepage string `json:"homepage,omitempty"`
33+ Docs string `json:"docs,omitempty"`
34+ }
35+
2136// Configuration represents the application configuration
2237type Configuration struct {
23- PangolinAPIURL string
24- TraefikAPIURL string
25- TraefikConfDir string
26- DBPath string
27- Port string
28- UIPath string
29- ConfigDir string
30- CheckInterval time.Duration
31- GenerateInterval time.Duration
32- ServiceInterval time.Duration // New field for service check interval
33- Debug bool
34- AllowCORS bool
35- CORSOrigin string
36- ActiveDataSource string
38+ PangolinAPIURL string
39+ TraefikAPIURL string
40+ TraefikConfDir string
41+ DBPath string
42+ Port string
43+ UIPath string
44+ ConfigDir string
45+ CheckInterval time.Duration
46+ GenerateInterval time.Duration
47+ ServiceInterval time.Duration
48+ Debug bool
49+ AllowCORS bool
50+ CORSOrigin string
51+ ActiveDataSource string
52+ TraefikStaticConfigPath string
53+ PluginsJSONURL string
3754}
3855
3956// DiscoverTraefikAPI attempts to discover the Traefik API by trying common URLs
4057func DiscoverTraefikAPI () (string , error ) {
4158 client := & http.Client {
42- Timeout : 2 * time .Second , // Short timeout for discovery
59+ Timeout : 2 * time .Second ,
4360 }
4461
45- // Common URLs to try
4662 urls := []string {
4763 "http://host.docker.internal:8080" ,
4864 "http://localhost:8080" ,
@@ -62,169 +78,165 @@ func DiscoverTraefikAPI() (string, error) {
6278 resp .Body .Close ()
6379 }
6480 }
65-
66- return "" , nil // Return empty string without error to allow fallbacks
81+ return "" , nil
6782}
6883
6984func main () {
7085 log .Println ("Starting Middleware Manager..." )
7186
72- // Parse command line flags
7387 var debug bool
7488 flag .BoolVar (& debug , "debug" , false , "Enable debug mode" )
7589 flag .Parse ()
7690
77- // Load configuration
7891 cfg := loadConfiguration (debug )
7992
80- // Try to discover Traefik API URL if not set in environment
8193 if os .Getenv ("TRAEFIK_API_URL" ) == "" {
8294 if discoveredURL , err := DiscoverTraefikAPI (); err == nil && discoveredURL != "" {
8395 log .Printf ("Auto-discovered Traefik API URL: %s" , discoveredURL )
8496 cfg .TraefikAPIURL = discoveredURL
8597 }
8698 }
8799
88- // Initialize database
89100 db , err := database .InitDB (cfg .DBPath )
90101 if err != nil {
91102 log .Fatalf ("Failed to initialize database: %v" , err )
92103 }
93104 defer db .Close ()
94-
95- // Ensure config directory exists
105+
96106 configDir := cfg .ConfigDir
97107 if err := config .EnsureConfigDirectory (configDir ); err != nil {
98108 log .Printf ("Warning: Failed to create config directory: %v" , err )
99109 }
100-
101- // Save default templates file if it doesn't exist
110+
102111 if err := config .SaveTemplateFile (configDir ); err != nil {
103- log .Printf ("Warning: Failed to save default templates: %v" , err )
112+ log .Printf ("Warning: Failed to save default middleware templates: %v" , err )
104113 }
105-
106- // Load default middleware templates
114+
107115 if err := config .LoadDefaultTemplates (db ); err != nil {
108- log .Printf ("Warning: Failed to load default templates: %v" , err )
116+ log .Printf ("Warning: Failed to load default middleware templates: %v" , err )
117+ }
118+
119+ if err := config .SaveTemplateServicesFile (configDir ); err != nil {
120+ log .Printf ("Warning: Failed to save default service templates: %v" , err )
121+ }
122+
123+ if err := config .LoadDefaultServiceTemplates (db ); err != nil {
124+ log .Printf ("Warning: Failed to load default service templates: %v" , err )
109125 }
110126
111- // Initialize config manager
112127 configManager , err := services .NewConfigManager (filepath .Join (configDir , "config.json" ))
113128 if err != nil {
114129 log .Fatalf ("Failed to initialize config manager: %v" , err )
115130 }
116131
117- // Ensure default data sources are configured with potentially discovered URL
118132 configManager .EnsureDefaultDataSources (cfg .PangolinAPIURL , cfg .TraefikAPIURL )
119133
120- // Create stop channel for graceful shutdown
121134 stopChan := make (chan struct {})
122-
123- // Start resource watcher with config manager
135+
124136 resourceWatcher , err := services .NewResourceWatcher (db , configManager )
125137 if err != nil {
126138 log .Fatalf ("Failed to create resource watcher: %v" , err )
127139 }
128140 go resourceWatcher .Start (cfg .CheckInterval )
129141
130- // Start configuration generator
131142 configGenerator := services .NewConfigGenerator (db , cfg .TraefikConfDir , configManager )
132143 go configGenerator .Start (cfg .GenerateInterval )
133144
134- // Start API server
135145 serverConfig := api.ServerConfig {
136146 Port : cfg .Port ,
137147 UIPath : cfg .UIPath ,
138148 Debug : cfg .Debug ,
139149 AllowCORS : cfg .AllowCORS ,
140150 CORSOrigin : cfg .CORSOrigin ,
141151 }
142-
143- server := api .NewServer (db .DB , serverConfig , configManager )
152+
153+ server := api .NewServer (db .DB , serverConfig , configManager , cfg . TraefikStaticConfigPath , cfg . PluginsJSONURL )
144154 go func () {
145155 if err := server .Start (); err != nil {
146156 log .Printf ("Server error: %v" , err )
147157 close (stopChan )
148158 }
149159 }()
150160
151- // Wait for shutdown signal or server error
152161 signalChan := make (chan os.Signal , 1 )
153162 signal .Notify (signalChan , os .Interrupt , syscall .SIGTERM )
154-
163+
164+ serviceWatcher , err := services .NewServiceWatcher (db , configManager )
165+ if err != nil {
166+ log .Printf ("Warning: Failed to create service watcher: %v" , err )
167+ serviceWatcher = nil
168+ } else {
169+ go serviceWatcher .Start (cfg .ServiceInterval )
170+ }
171+
155172 select {
156173 case <- signalChan :
157174 log .Println ("Received shutdown signal" )
158175 case <- stopChan :
159176 log .Println ("Received stop signal from server" )
160177 }
161178
162- // Start service watcher with config manager
163- serviceWatcher , err := services .NewServiceWatcher (db , configManager )
164- if err != nil {
165- log .Printf ("Warning: Failed to create service watcher: %v" , err )
166- } else {
167- // Use the same interval as the resource watcher, or a different one if preferred
168- go serviceWatcher .Start (cfg .CheckInterval )
169- }
170-
171- // Graceful shutdown
172179 log .Println ("Shutting down..." )
173180 resourceWatcher .Stop ()
174- serviceWatcher .Stop () // Add this line
181+ if serviceWatcher != nil {
182+ serviceWatcher .Stop ()
183+ }
175184 configGenerator .Stop ()
176185 server .Stop ()
177186 log .Println ("Middleware Manager stopped" )
178187}
179188
180- // loadConfiguration loads configuration from environment variables
181189func loadConfiguration (debug bool ) Configuration {
182- // Default check interval is 30 seconds
183190 checkInterval := 30 * time .Second
184191 if intervalStr := getEnv ("CHECK_INTERVAL_SECONDS" , "30" ); intervalStr != "" {
185192 if interval , err := strconv .Atoi (intervalStr ); err == nil && interval > 0 {
186193 checkInterval = time .Duration (interval ) * time .Second
187194 }
188195 }
189-
190- // Default generate interval is 10 seconds
196+
191197 generateInterval := 10 * time .Second
192198 if intervalStr := getEnv ("GENERATE_INTERVAL_SECONDS" , "10" ); intervalStr != "" {
193199 if interval , err := strconv .Atoi (intervalStr ); err == nil && interval > 0 {
194200 generateInterval = time .Duration (interval ) * time .Second
195201 }
196202 }
197-
198- // Allow CORS if specified
203+
204+ parsedServiceInterval := 30 * time .Second
205+ if intervalStr := getEnv ("SERVICE_INTERVAL_SECONDS" , "30" ); intervalStr != "" {
206+ if interval , err := strconv .Atoi (intervalStr ); err == nil && interval > 0 {
207+ parsedServiceInterval = time .Duration (interval ) * time .Second
208+ }
209+ }
210+
199211 allowCORS := false
200212 if corsStr := getEnv ("ALLOW_CORS" , "false" ); corsStr != "" {
201213 allowCORS = strings .ToLower (corsStr ) == "true"
202214 }
203-
204- // Override debug mode from environment if specified
215+
205216 if debugStr := getEnv ("DEBUG" , "" ); debugStr != "" {
206217 debug = strings .ToLower (debugStr ) == "true"
207218 }
208-
219+
209220 return Configuration {
210- PangolinAPIURL : getEnv ("PANGOLIN_API_URL" , "http://pangolin:3001/api/v1" ),
211- // Changed to use host.docker.internal as first default to better support Docker environments
212- TraefikAPIURL : getEnv ("TRAEFIK_API_URL" , "http://host.docker.internal:8080" ),
213- TraefikConfDir : getEnv ("TRAEFIK_CONF_DIR" , "/conf" ),
214- DBPath : getEnv ("DB_PATH" , "/data/middleware.db" ),
215- Port : getEnv ("PORT" , "3456" ),
216- UIPath : getEnv ("UI_PATH" , "/app/ui/build" ),
217- ConfigDir : getEnv ("CONFIG_DIR" , "/app/config" ),
218- ActiveDataSource : getEnv ("ACTIVE_DATA_SOURCE" , "pangolin" ),
219- CheckInterval : checkInterval ,
220- GenerateInterval : generateInterval ,
221- Debug : debug ,
222- AllowCORS : allowCORS ,
223- CORSOrigin : getEnv ("CORS_ORIGIN" , "" ),
221+ PangolinAPIURL : getEnv ("PANGOLIN_API_URL" , "http://pangolin:3001/api/v1" ),
222+ TraefikAPIURL : getEnv ("TRAEFIK_API_URL" , "http://host.docker.internal:8080" ),
223+ TraefikConfDir : getEnv ("TRAEFIK_CONF_DIR" , "/conf" ),
224+ DBPath : getEnv ("DB_PATH" , "/data/middleware.db" ),
225+ Port : getEnv ("PORT" , "3456" ),
226+ UIPath : getEnv ("UI_PATH" , "/app/ui/build" ),
227+ ConfigDir : getEnv ("CONFIG_DIR" , "/app/config" ),
228+ ActiveDataSource : getEnv ("ACTIVE_DATA_SOURCE" , "pangolin" ),
229+ CheckInterval : checkInterval ,
230+ GenerateInterval : generateInterval ,
231+ ServiceInterval : parsedServiceInterval ,
232+ Debug : debug ,
233+ AllowCORS : allowCORS ,
234+ CORSOrigin : getEnv ("CORS_ORIGIN" , "" ),
235+ TraefikStaticConfigPath : getEnv ("TRAEFIK_STATIC_CONFIG_PATH" , "/etc/traefik/traefik.yml" ),
236+ PluginsJSONURL : getEnv ("PLUGINS_JSON_URL" , "https://raw.githubusercontent.com/hhftechnology/middleware-manager/traefik-int/plugin/plugins.json" ),
224237 }
225238}
226239
227- // getEnv gets an environment variable or returns a default value
228240func getEnv (key , fallback string ) string {
229241 if value , exists := os .LookupEnv (key ); exists {
230242 return value
0 commit comments