|
import asyncio
|
|
import logging
|
|
|
|
from devplacepy.database import get_setting, set_setting
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ServiceManager:
|
|
def __init__(self):
|
|
self._services = {}
|
|
self._owns_lock = False
|
|
|
|
def set_lock_owner(self, owns: bool) -> None:
|
|
self._owns_lock = owns
|
|
|
|
def owns_lock(self) -> bool:
|
|
return self._owns_lock
|
|
|
|
def register(self, service) -> None:
|
|
self._services[service.name] = service
|
|
logger.info(f"Registered service: {service.name}")
|
|
|
|
def get_service(self, name: str):
|
|
return self._services.get(name)
|
|
|
|
def describe_all(self) -> list[dict]:
|
|
return [svc.describe() for svc in self._services.values()]
|
|
|
|
def set_enabled(self, name: str, enabled: bool) -> bool:
|
|
svc = self.get_service(name)
|
|
if svc is None:
|
|
return False
|
|
set_setting(svc.enabled_key, "1" if enabled else "0")
|
|
logger.info(f"Service {name} enabled={enabled}")
|
|
return True
|
|
|
|
def send_command(self, name: str, verb: str) -> bool:
|
|
svc = self.get_service(name)
|
|
if svc is None:
|
|
return False
|
|
raw = get_setting(svc.command_key, "")
|
|
counter = 0
|
|
if raw and ":" in raw:
|
|
try:
|
|
counter = int(raw.split(":", 1)[1])
|
|
except ValueError:
|
|
counter = 0
|
|
set_setting(svc.command_key, f"{verb}:{counter + 1}")
|
|
logger.info(f"Service {name} command={verb}")
|
|
return True
|
|
|
|
def save_config(self, name: str, form: dict) -> dict | None:
|
|
svc = self.get_service(name)
|
|
if svc is None:
|
|
return None
|
|
errors = {}
|
|
valid = {}
|
|
for field in svc.all_fields():
|
|
if field.key not in form:
|
|
continue
|
|
raw = form[field.key]
|
|
if field.secret and raw == "":
|
|
continue
|
|
try:
|
|
valid[field.key] = field.to_storage(field.coerce(raw))
|
|
except ValueError as e:
|
|
errors[field.key] = str(e)
|
|
if errors:
|
|
return {"ok": False, "errors": errors}
|
|
for key, value in valid.items():
|
|
set_setting(key, value)
|
|
logger.info(f"Service {name} config saved ({len(valid)} fields)")
|
|
return {"ok": True, "errors": {}}
|
|
|
|
def supervise(self) -> None:
|
|
for svc in self._services.values():
|
|
svc.start_supervisor()
|
|
logger.info(f"Supervising service: {svc.name}")
|
|
|
|
async def shutdown_all(self) -> None:
|
|
tasks = []
|
|
for svc in self._services.values():
|
|
svc.request_shutdown()
|
|
if svc._task is not None:
|
|
svc._task.cancel()
|
|
tasks.append(svc._task)
|
|
if tasks:
|
|
await asyncio.gather(*tasks, return_exceptions=True)
|
|
logger.info("All services stopped")
|
|
|
|
|
|
service_manager = ServiceManager()
|