# Baton — Экстренный сигнал PWA-приложение для отправки экстренных сигналов с геолокацией через Telegram-бота. ## Стек - **Backend:** Python 3.12+, FastAPI, aiosqlite, httpx - **Frontend:** Vanilla JS PWA (Service Worker, Web Push) - **База данных:** SQLite (WAL mode) - **Уведомления:** Telegram Bot API ## Запуск ```bash # Зависимости pip install -r requirements.txt # Переменные окружения (см. .env.example) cp .env.example .env # Запуск uvicorn backend.main:app --host 0.0.0.0 --port 8000 ``` ## Переменные окружения | Переменная | Обязательна | Описание | |---|---|---| | `BOT_TOKEN` | да | Токен Telegram-бота | | `CHAT_ID` | да | ID чата для уведомлений | | `WEBHOOK_SECRET` | да | Секрет для верификации Telegram webhook | | `WEBHOOK_URL` | да | Публичный URL `/api/webhook/telegram` | | `DB_PATH` | нет | Путь к SQLite-файлу (по умолчанию `baton.db`) | | `FRONTEND_ORIGIN` | нет | Разрешённый origin для CORS (по умолчанию `http://localhost:3000`) | | `WEBHOOK_ENABLED` | нет | Регистрировать webhook при старте (по умолчанию `true`) | | `APP_URL` | нет | Публичный URL приложения для keep-alive (например `https://baton.fly.dev`) | ## API | Метод | Путь | Описание | |---|---|---| | `GET` | `/health` | Health check: `{"status": "ok", "timestamp": }` | | `POST` | `/api/register` | Регистрация пользователя | | `POST` | `/api/signal` | Отправка экстренного сигнала | | `POST` | `/api/webhook/telegram` | Telegram webhook | ## Hosting & Keep-Alive ### Проблема cold start На бесплатных хостингах (Render, fly.io free tier, Railway и подобных) приложение **засыпает** после периода неактивности (обычно 15–30 минут). Следующий входящий запрос ждёт пока процесс поднимется заново — **cold start занимает 3–5 секунд**. Для экстренного приложения это критично. ### Решения по вариантам хостинга | Вариант | Стоимость | Cold start | Рекомендация | |---|---|---|---| | **fly.io Hobby** | $5/мес | Нет (всегда активен) | Оптимально для прода | | **fly.io free tier** | Бесплатно | 3–5 сек | Только для разработки | | **Render free** | Бесплатно | 3–5 сек | Только для разработки | | **Самохостинг (VPS)** | От $3–5/мес | Нет | Полный контроль | > **Финальный выбор хостинга зависит от решения по OQ-004** (открытый вопрос по бюджету и масштабированию проекта). ### Keep-alive механизм (asyncio background task) Приложение запускает фоновый asyncio-таск, который каждые **10 минут** пингует собственный `/health` endpoint. Это предотвращает засыпание на платформах, которые реагируют на активность процесса. **Активация:** установите переменную `APP_URL`: ```bash APP_URL=https://your-app.fly.dev ``` Без `APP_URL` таск не запускается (keep-alive отключён). **Ограничение:** self-ping работает пока процесс жив. Если платформа убивает процесс при нулевом трафике — нужен внешний пингер (см. ниже). ### Keep-alive для самохостинга (cron / systemd timer) Если приложение на VPS и нужен мониторинг извне: **Вариант 1 — crontab:** ```bash # Редактируем cron crontab -e # Добавляем запись (каждые 10 минут): */10 * * * * curl -sf https://your-app.example.com/health > /dev/null ``` **Вариант 2 — systemd timer:** Создайте два файла: `/etc/systemd/system/baton-keepalive.service`: ```ini [Unit] Description=Baton keep-alive ping [Service] Type=oneshot ExecStart=curl -sf https://your-app.example.com/health ``` `/etc/systemd/system/baton-keepalive.timer`: ```ini [Unit] Description=Run Baton keep-alive every 10 minutes [Timer] OnBootSec=1min OnUnitActiveSec=10min [Install] WantedBy=timers.target ``` Готовые файлы находятся в `deploy/`: ```bash # 1. Скопировать файлы sudo cp deploy/baton-keepalive.service /etc/systemd/system/ sudo cp deploy/baton-keepalive.timer /etc/systemd/system/ # 2. Заменить URL на реальный sudo sed -i 's|https://your-app.example.com|https://YOUR_APP_URL|g' \ /etc/systemd/system/baton-keepalive.service # 3. Включить и запустить sudo systemctl daemon-reload sudo systemctl enable --now baton-keepalive.timer # 4. Проверить systemctl list-timers baton-keepalive.timer ``` ### Keep-alive через UptimeRobot (внешний сервис, рекомендуется) [UptimeRobot](https://uptimerobot.com) — бесплатный сервис мониторинга, который пингует ваш `/health` снаружи каждые 5 минут. В отличие от self-ping, он работает даже если платформа убила процесс. **Настройка (бесплатно, без регистрации кредитной карты):** 1. Зарегистрируйтесь на [uptimerobot.com](https://uptimerobot.com) 2. **Add New Monitor** → тип **HTTP(s)** 3. Заполните: - **Friendly Name:** `Baton Health` - **URL:** `https://your-app.example.com/health` - **Monitoring Interval:** `5 minutes` 4. Сохраните. UptimeRobot начнёт пинговать каждые 5 минут и пришлёт email при падении. **Плюсы:** работает независимо от хостинга, бесплатно до 50 мониторов, email/Telegram-уведомления. **Минусы:** требует публичный URL (для локальной разработки не подходит). > **Рекомендация:** для прода используйте UptimeRobot как внешний watchdog + self-ping (APP_URL) как запасной вариант. ## Nginx deployment Для проксирования через nginx используйте готовый шаблон `nginx/baton.conf`. ### Применение ```bash # 1. Скопировать шаблон и заменить домен sudo cp nginx/baton.conf /etc/nginx/sites-available/baton sudo sed -i 's//baton.example.com/g' /etc/nginx/sites-available/baton # 2. Получить TLS-сертификат (если ещё нет) sudo certbot certonly --nginx -d baton.example.com # 3. Включить конфиг sudo ln -s /etc/nginx/sites-available/baton /etc/nginx/sites-enabled/baton # 4. Проверить и применить sudo nginx -t && sudo systemctl reload nginx ``` ### Защита BOT_TOKEN в логах Конфиг включает `map`-блок, который автоматически маскирует токен бота в `access_log`: ``` # В логе вместо реального токена: GET /bot/sendMessage → GET /bot[REDACTED]/sendMessage ``` Это защита по принципу «defence in depth»: текущий webhook-эндпоинт (`/api/webhook/telegram`) токен в URL не содержит, но маскировка сработает, если в будущем появится маршрут вида `/bot/...`. Заголовок `X-Telegram-Bot-Api-Secret-Token` не попадает в `access_log` — nginx не логирует заголовки запросов в стандартном `log_format`. ## Тесты ```bash pip install -r requirements-dev.txt pytest ```