"""Запуск сервиса: обновление, Caddyfile, HTTPS. Раньше вся эта последовательность жила в .bat-файлах, а Caddyfile правился руками. Оба файла лежат вне папки app, поэтому обновление их не доставляло: чтобы получить новый порядок запуска, приходилось переустанавливать сервис. Теперь .bat - это трёхстрочная заглушка, которая просто зовёт этот модуль, а Caddyfile создаётся здесь из настроек. Меняется только код внутри app, который обновление возит штатно. """ import subprocess import sys from pathlib import Path __all__ = ["render_caddyfile", "ensure_caddyfile", "main"] # По этой строке отличаем свой файл от отредактированного вручную. MARK = "# Создан автоматически talkscore-asr. Правки будут перезаписаны." CADDY_WAIT_SEC = 3 def render_caddyfile(domains: str, port: int, max_upload_mb: int) -> str: """Собирает конфигурацию Caddy из настроек сервиса.""" listed = ", ".join(d.strip() for d in domains.split(",") if d.strip()) return f"""{MARK} # Домены и предел размера берутся из config.toml, менять нужно там. {{ \t# Caddy очень подробно рассказывает про сертификаты. В консоли из-за этого \t# не видно сообщений сервиса, поэтому его вывод уходит в файл. \tlog {{ \t\toutput file logs/caddy.log {{ \t\t\troll_size 10MiB \t\t\troll_keep 5 \t\t}} \t\tformat console \t\tlevel INFO \t}} }} {listed} {{ \treverse_proxy 127.0.0.1:{port} {{ \t\t# Реальный адрес клиента - иначе список разрешённых адресов \t\t# увидит вместо него localhost и пустит кого угодно. \t\theader_up X-Forwarded-For {{remote_host}} \t}} \trequest_body {{ \t\tmax_size {max_upload_mb}MB \t}} }} """ def ensure_caddyfile(base_dir: Path, content: str) -> Path | None: """Записывает Caddyfile, не затирая ручные правки. Если файл создан не нами, откладываем новую версию рядом: молча стереть чужую настройку хуже, чем оставить её устаревшей. """ target = base_dir / "Caddyfile" if target.exists(): existing = target.read_text(encoding="utf-8", errors="replace") if MARK not in existing: spare = base_dir / "Caddyfile.new" if existing.strip() != content.strip(): spare.write_text(content, encoding="utf-8") return spare return None if existing == content: return None target.write_text(content, encoding="utf-8") return target def start_caddy(base_dir: Path) -> subprocess.Popen | None: """Поднимает Caddy отдельным процессом. Его отсутствие не должно ронять запуск.""" binary = base_dir / "bin" / "caddy.exe" if not binary.is_file(): binary = base_dir / "bin" / "caddy" if not binary.is_file(): print("\n Caddy не найден в папке bin - запускаюсь без HTTPS.\n") return None (base_dir / "logs").mkdir(exist_ok=True) return subprocess.Popen( [str(binary), "run", "--config", str(base_dir / "Caddyfile")], cwd=str(base_dir)) def main(argv: list[str] | None = None) -> int: argv = sys.argv[1:] if argv is None else argv https = "https" in argv from app import updater from app.config import ConfigError, load_settings try: settings = load_settings() except ConfigError as exc: print(f"\n {exc}\n") return 1 updater.main() caddy = None if https: if not settings.https_domains.strip(): print("\n В config.toml не заданы домены: раздел [https], ключ domains." "\n Без них сертификат выпустить не на что.\n") return 1 written = ensure_caddyfile( settings.base_dir, render_caddyfile(settings.https_domains, settings.port, settings.max_upload_mb)) if written is not None and written.name.endswith(".new"): print(f"\n Caddyfile правили вручную, поэтому не трогаю его." f"\n Новая версия отложена рядом: {written}\n") caddy = start_caddy(settings.base_dir) try: from app.main import run run() finally: if caddy is not None: caddy.terminate() return 0 if __name__ == "__main__": sys.exit(main())