No description
Find a file
2026-03-20 21:09:33 +02:00
backend kin: BATON-ARCH-012 Добавить WEBHOOK_ENABLED флаг для локальной разработки 2026-03-20 21:03:45 +02:00
docs docs: rename ADR-002-offline-pattern → ADR-007-offline-queue-v2, update all refs 2026-03-20 21:00:51 +02:00
frontend kin: BATON-002 [Research] UX Designer 2026-03-20 20:44:00 +02:00
nginx kin: BATON-ARCH-011 Защитить BOT_TOKEN от утечки в nginx access.log 2026-03-20 21:07:25 +02:00
tests kin: BATON-ARCH-013-backend_dev 2026-03-20 21:09:33 +02:00
.env.example kin: BATON-ARCH-012-backend_dev 2026-03-20 21:01:48 +02:00
.gitignore kin: BATON-002 [Research] UX Designer 2026-03-20 20:44:00 +02:00
ARCHITECTURE.md docs: fix broken ADR-002-offline-pattern link in ARCHITECTURE.md table 2026-03-20 21:06:02 +02:00
pytest.ini kin: BATON-002 [Research] UX Designer 2026-03-20 20:44:00 +02:00
README.md kin: BATON-ARCH-011 Защитить BOT_TOKEN от утечки в nginx access.log 2026-03-20 21:07:25 +02:00
requirements-dev.txt kin: BATON-002 [Research] UX Designer 2026-03-20 20:44:00 +02:00
requirements.txt kin: BATON-002 [Research] UX Designer 2026-03-20 20:44:00 +02:00

Baton — Экстренный сигнал

PWA-приложение для отправки экстренных сигналов с геолокацией через Telegram-бота.

Стек

  • Backend: Python 3.12+, FastAPI, aiosqlite, httpx
  • Frontend: Vanilla JS PWA (Service Worker, Web Push)
  • База данных: SQLite (WAL mode)
  • Уведомления: Telegram Bot API

Запуск

# Зависимости
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": <unix_ts>}
POST /api/register Регистрация пользователя
POST /api/signal Отправка экстренного сигнала
POST /api/webhook/telegram Telegram webhook

Hosting & Keep-Alive

Проблема cold start

На бесплатных хостингах (Render, fly.io free tier, Railway и подобных) приложение засыпает после периода неактивности (обычно 1530 минут). Следующий входящий запрос ждёт пока процесс поднимется заново — cold start занимает 35 секунд. Для экстренного приложения это критично.

Решения по вариантам хостинга

Вариант Стоимость Cold start Рекомендация
fly.io Hobby $5/мес Нет (всегда активен) Оптимально для прода
fly.io free tier Бесплатно 35 сек Только для разработки
Render free Бесплатно 35 сек Только для разработки
Самохостинг (VPS) От $35/мес Нет Полный контроль

Финальный выбор хостинга зависит от решения по OQ-004 (открытый вопрос по бюджету и масштабированию проекта).

Keep-alive механизм (asyncio background task)

Приложение запускает фоновый asyncio-таск, который каждые 10 минут пингует собственный /health endpoint. Это предотвращает засыпание на платформах, которые реагируют на активность процесса.

Активация: установите переменную APP_URL:

APP_URL=https://your-app.fly.dev

Без APP_URL таск не запускается (keep-alive отключён).

Ограничение: self-ping работает пока процесс жив. Если платформа убивает процесс при нулевом трафике — нужен внешний пингер (см. ниже).

Keep-alive для самохостинга (cron / systemd timer)

Если приложение на VPS и нужен мониторинг извне:

Вариант 1 — crontab:

# Редактируем cron
crontab -e

# Добавляем запись (каждые 10 минут):
*/10 * * * * curl -sf https://your-app.example.com/health > /dev/null

Вариант 2 — systemd timer:

Создайте два файла:

/etc/systemd/system/baton-keepalive.service:

[Unit]
Description=Baton keep-alive ping

[Service]
Type=oneshot
ExecStart=curl -sf https://your-app.example.com/health

/etc/systemd/system/baton-keepalive.timer:

[Unit]
Description=Run Baton keep-alive every 10 minutes

[Timer]
OnBootSec=1min
OnUnitActiveSec=10min

[Install]
WantedBy=timers.target

Активация:

systemctl daemon-reload
systemctl enable --now baton-keepalive.timer
systemctl list-timers baton-keepalive.timer

Nginx deployment

Для проксирования через nginx используйте готовый шаблон nginx/baton.conf.

Применение

# 1. Скопировать шаблон и заменить домен
sudo cp nginx/baton.conf /etc/nginx/sites-available/baton
sudo sed -i 's/<YOUR_DOMAIN>/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<TOKEN>/sendMessage → GET /bot[REDACTED]/sendMessage

Это защита по принципу «defence in depth»: текущий webhook-эндпоинт (/api/webhook/telegram) токен в URL не содержит, но маскировка сработает, если в будущем появится маршрут вида /bot<TOKEN>/....

Заголовок X-Telegram-Bot-Api-Secret-Token не попадает в access_log — nginx не логирует заголовки запросов в стандартном log_format.

Тесты

pip install -r requirements-dev.txt
pytest