@@ -718,18 +718,24 @@ def get_icon_for_route(route_name, service_name, rule):
718718
719719 return default_icon , icon_url
720720
721- def get_category_for_route (route_name , service_name ):
722- """Determine category for a route based on rules."""
723- category_config = config .get ('categories' , {})
721+ def get_category_from_service (service_name , services_payload ):
722+ """Find category from the corresponding service labels."""
723+ if not service_name or not services_payload :
724+ return None
724725
725- for rule_config in category_config .get ('rules' , []):
726- pattern = rule_config .get ('pattern' )
727- category = rule_config .get ('category' )
728- if not pattern or not category :
729- continue
730- if re .search (pattern , route_name , re .IGNORECASE ) or (service_name and re .search (pattern , service_name , re .IGNORECASE )):
731- return category
732-
726+ service_data = services_payload .get (f"{ service_name } @swarm" )
727+ if not service_data :
728+ return None
729+
730+ labels_dict = service_data .get ('labels' , {})
731+ if not isinstance (labels_dict , dict ):
732+ return None
733+
734+ for key , value in labels_dict .items ():
735+ lowered = key .lower ()
736+ if any (lowered == candidate .lower () for candidate in CATEGORY_LABEL_KEYS ):
737+ if isinstance (value , str ) and value .strip ():
738+ return value .strip ()
733739 return None
734740
735741def extract_category (router_data ):
@@ -775,6 +781,12 @@ def get_routes():
775781 traefik_url = config ['traefik' ]['api_url' ]
776782 request_timeout = config ['traefik' ].get ('timeout' , 5 )
777783
784+ # Fetch services to get access to service-level labels
785+ services_endpoint = f"{ traefik_url } /http/services"
786+ services_response = requests .get (services_endpoint , timeout = request_timeout )
787+ services_response .raise_for_status ()
788+ services_payload = services_response .json ()
789+
778790 base_endpoint = f"{ traefik_url } /http/routers"
779791 payloads = fetch_router_payloads (base_endpoint , request_timeout )
780792
@@ -783,30 +795,29 @@ def get_routes():
783795 for router_name , router_data in iter_router_entries (payload ):
784796 safe_name = router_name or router_data .get ('name' ) or 'unknown'
785797 display_name = normalize_route_name (safe_name )
798+ service_name = router_data .get ('service' )
786799 icon_value , icon_url = get_icon_for_route (
787800 display_name ,
788- router_data . get ( 'service' ) ,
801+ service_name ,
789802 router_data .get ('rule' , '' )
790803 )
791804
792- labels = (router_data .get ('labels' )
793- or router_data .get ('metadata' , {}).get ('labels' )
794- or {})
795-
796- if not matches_label_filters (labels ):
797- continue
805+ route_category = extract_category (router_data ) or get_category_for_route (display_name , service_name )
806+ # Fallback to service-level labels if no router-level category is found
807+ if not route_category :
808+ route_category = get_category_from_service (service_name , services_payload )
798809
799810 route = {
800811 'name' : display_name ,
801812 'rule' : router_data .get ('rule' , 'N/A' ),
802- 'service' : router_data . get ( 'service' , 'N/A' ) ,
813+ 'service' : service_name ,
803814 'status' : router_data .get ('status' , 'unknown' ),
804815 'priority' : router_data .get ('priority' , 0 ),
805816 'entryPoints' : router_data .get ('entryPoints' , []),
806817 'icon' : icon_value ,
807818 'iconUrl' : icon_url ,
808819 'url' : infer_route_url (router_data .get ('rule' , '' ), router_data .get ('entryPoints' )),
809- 'category' : extract_category ( router_data ) or get_category_for_route ( display_name , router_data . get ( 'service' )) ,
820+ 'category' : route_category ,
810821 'rawName' : safe_name
811822 }
812823 if is_excluded (route ['name' ], raw_name = safe_name ):
0 commit comments