refactor: introduce two-user daemon architecture with socket-based communication
- Add daemon/ module with aiohttp server, sync client, and handler registry - Add daemon/handlers/ for privileged operations (acme, dnsmasq, firewall, logs, nginx, wireguard) - Add system/acme-deploy.py, vacuum-walld sudoers and systemd service - Update API routes to use daemon client instead of lib/ directly - Update lib/, tests/, and webui/ for new architecture - Update docs and deployment scripts
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
"""Logs daemon handler.
|
||||
|
||||
Reads system logs and journal entries.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from daemon.server import registry
|
||||
from lib.common import run_proc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
PROJECT_DIR = Path(__file__).resolve().parent.parent.parent
|
||||
APP_LOG_FILE = PROJECT_DIR / "data" / "logs" / "vacuum-wall.log"
|
||||
_MAX_LINES = 200
|
||||
|
||||
_LOG_TAGS = {"logs"}
|
||||
|
||||
|
||||
def _tail_file(path: str, n: int = _MAX_LINES, sudo: bool = False) -> str:
|
||||
try:
|
||||
if sudo:
|
||||
result = run_proc(["cat", path], sudo=True)
|
||||
lines = result.stdout.splitlines(keepends=True)
|
||||
else:
|
||||
with open(path) as f:
|
||||
lines = f.readlines()
|
||||
return "".join(lines[-n:])
|
||||
except FileNotFoundError:
|
||||
return "(log file not found)\n"
|
||||
except PermissionError:
|
||||
return "(permission denied)\n"
|
||||
|
||||
|
||||
def _sudo_journalctl(unit: str, n: int = _MAX_LINES) -> str:
|
||||
try:
|
||||
result = run_proc(
|
||||
["journalctl", "-u", unit, "--no-pager", "-n", str(n)],
|
||||
sudo=True,
|
||||
check=False,
|
||||
timeout=10,
|
||||
)
|
||||
output = result.stdout.strip()
|
||||
return output if output else f"(no journal entries for {unit})\n"
|
||||
except Exception as exc:
|
||||
return f"(error reading journal: {exc})\n"
|
||||
|
||||
|
||||
@registry.register("GET", "/logs/journal", cache_tags=_LOG_TAGS)
|
||||
def journal(_request, _body) -> str:
|
||||
return _sudo_journalctl("vacuum-wall")
|
||||
|
||||
|
||||
@registry.register("GET", "/logs/nginx/access", cache_tags=_LOG_TAGS)
|
||||
def nginx_access(_request, _body) -> str:
|
||||
return _tail_file("/var/log/nginx/access.log", sudo=True)
|
||||
|
||||
|
||||
@registry.register("GET", "/logs/nginx/error", cache_tags=_LOG_TAGS)
|
||||
def nginx_error(_request, _body) -> str:
|
||||
return _tail_file("/var/log/nginx/error.log", sudo=True)
|
||||
|
||||
|
||||
@registry.register("GET", "/logs/dnsmasq", cache_tags=_LOG_TAGS)
|
||||
def dnsmasq_log(_request, _body) -> str:
|
||||
return _sudo_journalctl("dnsmasq")
|
||||
|
||||
|
||||
@registry.register("GET", "/logs/app", cache_tags=_LOG_TAGS)
|
||||
def app_log(_request, _body) -> str:
|
||||
return _tail_file(str(APP_LOG_FILE))
|
||||
Reference in New Issue
Block a user