@@ -844,6 +844,83 @@ def get_component_levels():
844844 logging .error (f"Error reading log configuration: { e } " )
845845 return {"status" : "error" , "message" : str (e )}
846846
847+ @app .route ("/logs/configs" )
848+ @auth_required
849+ def list_log_configs ():
850+ """Return all available logconf_*.json files with display names."""
851+ import glob
852+
853+ configs = []
854+ active = (
855+ os .path .realpath ("pifinder_logconf.json" )
856+ if os .path .exists ("pifinder_logconf.json" )
857+ else None
858+ )
859+ for path in sorted (glob .glob ("logconf_*.json" )):
860+ stem = path [len ("logconf_" ) : - len (".json" )]
861+ display = stem .replace ("_" , " " ).title ()
862+ configs .append (
863+ {
864+ "file" : path ,
865+ "name" : display ,
866+ "active" : os .path .realpath (path ) == active ,
867+ }
868+ )
869+ return {"configs" : configs }
870+
871+ @app .route ("/logs/switch_config" , method = "post" )
872+ @auth_required
873+ def switch_log_config ():
874+ """Atomically repoint pifinder_logconf.json to the chosen config, then restart."""
875+ logconf_file = request .forms .get ("logconf_file" , "" ).strip ()
876+ if (
877+ not logconf_file
878+ or not logconf_file .startswith ("logconf_" )
879+ or not logconf_file .endswith (".json" )
880+ ):
881+ return {"status" : "error" , "message" : "Invalid log config file name" }
882+ if not os .path .exists (logconf_file ):
883+ return {
884+ "status" : "error" ,
885+ "message" : f"Log config file not found: { logconf_file } " ,
886+ }
887+ try :
888+ link = "pifinder_logconf.json"
889+ tmp = link + ".tmp"
890+ os .symlink (logconf_file , tmp )
891+ os .replace (tmp , link )
892+ logger .info ("Switched log config to %s" , logconf_file )
893+ except Exception as e :
894+ logger .error ("Failed to switch log config: %s" , e )
895+ return {"status" : "error" , "message" : str (e )}
896+ return template ("restart_pifinder" )
897+
898+ @app .route ("/logs/upload_config" , method = "post" )
899+ @auth_required
900+ def upload_log_config ():
901+ """Upload a new logconf_*.json file."""
902+ upload = request .files .get ("config_file" )
903+ if not upload :
904+ return {"status" : "error" , "message" : "No file provided" }
905+ filename = upload .filename
906+ if not filename .startswith ("logconf_" ) or not filename .endswith (".json" ):
907+ return {
908+ "status" : "error" ,
909+ "message" : "File must be named logconf_<name>.json" ,
910+ }
911+ if os .path .exists (filename ):
912+ return {
913+ "status" : "error" ,
914+ "message" : f"File already exists: { filename } " ,
915+ }
916+ try :
917+ upload .save (filename , overwrite = False )
918+ logger .info ("Uploaded log config: %s" , filename )
919+ return {"status" : "ok" , "file" : filename }
920+ except Exception as e :
921+ logger .error ("Failed to save uploaded log config: %s" , e )
922+ return {"status" : "error" , "message" : str (e )}
923+
847924 @app .route ("/logs/download" )
848925 @auth_required
849926 def download_logs ():
0 commit comments