"""Windows service that runs the Intrusion Detection Flask web app. Serves the web UI on localhost using the configured defaults (``http://127.0.0.1:54666`` from ``app.utils.constants``; the ``WEB_HOST`` and ``WEB_PORT`` environment variables or a local ``.env`` override them). Manage with (from the project root, in an activated venv): python intrusion-detection-service.py install python intrusion-detection-service.py start python intrusion-detection-service.py stop python intrusion-detection-service.py remove """ import os import sys import threading import servicemanager import win32event import win32service import win32serviceutil # Ensure the project root is importable. When the SCM launches the service # the working directory may be C:\\Windows\\System32, not the project dir. sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from app.utils.constants import WEB_HOST, WEB_PORT # noqa: E402 class IntrusionDetectionService(win32serviceutil.ServiceFramework): _svc_name_ = "IntrusionDetectionWebUI" _svc_display_name_ = "Intrusion Detection Web UI" _svc_description_ = ( "Serves the Intrusion Detection Flask web app on localhost " f"(http://{WEB_HOST}:{WEB_PORT})." ) def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) self._flask_thread = None def SvcStop(self): # Report STOP_PENDING, then signal the main loop; the SCM marks the # service STOPPED once SvcDoRun returns (after Flask shuts down). self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) win32event.SetEvent(self.hWaitStop) def SvcDoRun(self): servicemanager.LogMsg( servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '') ) try: self.main() finally: servicemanager.LogMsg( servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED, (self._svc_name_, '') ) def main(self): from app.web import serve # Flask's app.run() blocks, so run it on a daemon thread and keep the # service alive here until SvcStop signals the stop event. serve() # passes use_reloader=False, which is required under the service host # (the reloader would spawn a child process and break the service). self._flask_thread = threading.Thread( target=serve, kwargs={"host": WEB_HOST, "port": WEB_PORT}, daemon=True, name="flask-web", ) self._flask_thread.start() win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE) if __name__ == '__main__': # Handles both the service-host launch and command-line control # (install / start / stop / remove). win32serviceutil.HandleCommandLine(IntrusionDetectionService)