Files
pic/api/managers.py
T
roof c696ca9ef6
Unit Tests / test (push) Successful in 7m32s
fix: DNS split-horizon in DDNS mode, service access filter, health check, verbosity persistence
- DNS (critical): add _configured_dns_params() that returns (primary_domain,
  split_horizon_zones) from config_manager so all apply_all_dns_rules() callers
  pass the correct primary zone (e.g. 'pic.ngo') and split-horizon list
  (e.g. ['pic1.pic.ngo']) instead of the FQDN as the primary — fixes
  DNS_PROBE_FINISHED_BAD_CONFIG for all external domains when on VPN

- firewall_manager: add split_horizon_zones param to apply_all_dns_rules()
  and forward it to generate_corefile()

- Peers: filter service_access list to installed services only; peers.py
  derives valid services from config_manager.get_installed_services() with
  the email→mail ID mapping; Peers.jsx fetches from /api/store/installed
  and filters the checkboxes and defaults accordingly

- Health check: fix file_manager→'files' ID mapping so files service health
  is checked when installed (was silently skipped due to 'file' vs 'files')

- Verbosity persistence: move log_levels.json from non-mounted
  /app/api/config/ to CONFIG_DIR (/app/config/) which maps to config/api/
  on the host; both load (managers.py) and save (routes/services.py) updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 13:05:58 -04:00

149 lines
5.9 KiB
Python

"""
Manager singletons for the PIC API.
All service managers are instantiated here and imported by app.py. Routes in
app.py reference these by name from app's own namespace (so test patches via
`patch('app.email_manager', mock)` continue to work as before).
Directory/path env vars:
DATA_DIR — host-mapped persistent data directory (default: /app/data)
CONFIG_DIR — host-mapped config directory (default: /app/config)
"""
import os
from network_manager import NetworkManager
from wireguard_manager import WireGuardManager
from peer_registry import PeerRegistry
from email_manager import EmailManager
from calendar_manager import CalendarManager
from file_manager import FileManager
from routing_manager import RoutingManager
from vault_manager import VaultManager
from container_manager import ContainerManager
from config_manager import ConfigManager
from service_bus import ServiceBus, EventType
from log_manager import LogManager
from cell_link_manager import CellLinkManager
import firewall_manager
from auth_manager import AuthManager
from setup_manager import SetupManager
from caddy_manager import CaddyManager
from ddns_manager import DDNSManager
from connectivity_manager import ConnectivityManager
from service_registry import ServiceRegistry
from service_composer import ServiceComposer
from account_manager import AccountManager
DATA_DIR = os.environ.get('DATA_DIR', '/app/data')
CONFIG_DIR = os.environ.get('CONFIG_DIR', '/app/config')
config_manager = ConfigManager(
config_file=os.path.join(CONFIG_DIR, 'cell_config.json'),
data_dir=DATA_DIR,
)
service_bus = ServiceBus()
log_manager = LogManager(log_dir='./data/logs')
# ServiceRegistry depends only on config_manager; create it early so
# NetworkManager and CaddyManager can derive subdomains from manifests
# instead of hardcoding service names.
service_registry = ServiceRegistry(config_manager=config_manager)
network_manager = NetworkManager(data_dir=DATA_DIR, config_dir=CONFIG_DIR,
service_registry=service_registry)
wireguard_manager = WireGuardManager(data_dir=DATA_DIR, config_dir=CONFIG_DIR)
peer_registry = PeerRegistry(data_dir=DATA_DIR, config_dir=CONFIG_DIR)
email_manager = EmailManager(data_dir=DATA_DIR, config_dir=CONFIG_DIR, service_bus=service_bus)
calendar_manager = CalendarManager(data_dir=DATA_DIR, config_dir=CONFIG_DIR)
file_manager = FileManager(data_dir=DATA_DIR, config_dir=CONFIG_DIR)
routing_manager = RoutingManager(data_dir=DATA_DIR, config_dir=CONFIG_DIR)
vault_manager = VaultManager(data_dir=DATA_DIR, config_dir=CONFIG_DIR)
container_manager = ContainerManager(data_dir=DATA_DIR, config_dir=CONFIG_DIR)
cell_link_manager = CellLinkManager(
data_dir=DATA_DIR, config_dir=CONFIG_DIR,
wireguard_manager=wireguard_manager,
network_manager=network_manager,
)
auth_manager = AuthManager(data_dir=DATA_DIR, config_dir=CONFIG_DIR)
caddy_manager = CaddyManager(config_manager=config_manager, data_dir=DATA_DIR, config_dir=CONFIG_DIR,
service_bus=service_bus, service_registry=service_registry)
ddns_manager = DDNSManager(config_manager=config_manager, data_dir=DATA_DIR, config_dir=CONFIG_DIR)
connectivity_manager = ConnectivityManager(
config_manager=config_manager,
peer_registry=peer_registry,
data_dir=DATA_DIR,
config_dir=CONFIG_DIR,
)
service_composer = ServiceComposer(config_manager=config_manager, data_dir=DATA_DIR)
account_manager = AccountManager(
service_registry=service_registry,
data_dir=DATA_DIR,
config_manager=config_manager,
email_manager=email_manager,
calendar_manager=calendar_manager,
file_manager=file_manager,
)
from service_store_manager import ServiceStoreManager
service_store_manager = ServiceStoreManager(
config_manager=config_manager,
caddy_manager=caddy_manager,
container_manager=container_manager,
data_dir=DATA_DIR,
config_dir=CONFIG_DIR,
service_composer=service_composer,
)
from egress_manager import EgressManager
egress_manager = EgressManager(
config_manager=config_manager,
service_store_manager=service_store_manager,
data_dir=DATA_DIR,
config_dir=CONFIG_DIR,
)
service_store_manager.egress_manager = egress_manager
setup_manager = SetupManager(config_manager=config_manager, auth_manager=auth_manager)
# Service logger configuration
_service_log_configs = {
'network': {'level': 'INFO', 'formatter': 'json', 'console': False},
'wireguard': {'level': 'INFO', 'formatter': 'json', 'console': False},
'email': {'level': 'INFO', 'formatter': 'json', 'console': False},
'calendar': {'level': 'INFO', 'formatter': 'json', 'console': False},
'files': {'level': 'INFO', 'formatter': 'json', 'console': False},
'routing': {'level': 'INFO', 'formatter': 'json', 'console': False},
'vault': {'level': 'INFO', 'formatter': 'json', 'console': False},
'api': {'level': 'INFO', 'formatter': 'json', 'console': True},
}
for _svc, _cfg in _service_log_configs.items():
log_manager.add_service_logger(_svc, _cfg)
# Apply any persisted log level overrides (stored in the mounted config volume)
import json as _json
_levels_file = os.path.join(CONFIG_DIR, 'log_levels.json')
if os.path.exists(_levels_file):
try:
with open(_levels_file) as _lf:
for _s, _l in _json.load(_lf).items():
log_manager.set_service_level(_s, _l)
except Exception:
pass
service_bus.start()
__all__ = [
'config_manager', 'service_bus', 'log_manager',
'network_manager', 'wireguard_manager', 'peer_registry',
'email_manager', 'calendar_manager', 'file_manager',
'routing_manager', 'vault_manager', 'container_manager',
'cell_link_manager', 'auth_manager', 'setup_manager', 'caddy_manager',
'ddns_manager', 'service_store_manager', 'connectivity_manager',
'service_registry', 'service_composer', 'account_manager',
'egress_manager',
'firewall_manager', 'EventType',
'DATA_DIR', 'CONFIG_DIR',
]