Files
tgit/app/health.py
ElebobaandClaude Opus 5.5 0ce5285da1 feat(bot): точка входа, конфиг, главное меню, доступ для ADMIN_IDS, healthcheck
- config: pydantic-settings по подсистемам, ADMIN_IDS через запятую
- texts.yaml + валидация при старте (лишний/пропущенный ключ — ошибка сразу)
- меню: reply-клавиатура разделов (Участники — только админам), инлайн «Назад»/«Отмена»
  с редактированием сообщения на месте, /start, /menu, /help
- AccessMiddleware (outer) на сообщениях и кнопках: до #25 пускает только ADMIN_IDS,
  постороннему — одна фраза в сутки, устаревшие кнопки молча гасятся
- heartbeat-файл + HEALTHCHECK в Dockerfile; JSON-логи
- FSM пока в памяти (фабрика), хранилище в БД — в #5
- тесты на фейковой сессии aiogram через настоящий Dispatcher

Refs #4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:53:52 +03:00

36 lines
949 B
Python

"""Liveness: a background task touches a file while the event loop runs; the Docker healthcheck checks its age.
A stuck loop stops touching the file, so the container turns unhealthy even though the process is still up.
`python -m app.health` exits 0 when healthy, 1 otherwise.
"""
import asyncio
import sys
import time
from pathlib import Path
from app.config import RuntimeSettings
async def heartbeat(path: Path, interval: float) -> None:
while True:
await asyncio.to_thread(path.touch)
await asyncio.sleep(interval)
def is_alive(path: Path, max_age: float, now: float | None = None) -> bool:
try:
age = (time.time() if now is None else now) - path.stat().st_mtime
except FileNotFoundError:
return False
return age <= max_age
def main() -> None:
rt = RuntimeSettings()
sys.exit(0 if is_alive(rt.heartbeat_file, rt.heartbeat_max_age) else 1)
if __name__ == "__main__":
main()