No description
Find a file
Gros Frumos cb89a90771 fix: viewport safe-area-inset for iOS PWA + disable pinch zoom
Topbar (avatar, network indicator) was hidden behind iOS status bar
in standalone PWA mode. Added safe-area-inset-top padding to topbar.
Disabled user-scalable to prevent accidental zoom.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 16:40:30 +02:00
backend feat: test signal via avatar/indicator tap on main screen 2026-03-21 16:06:02 +02:00
deploy fix: add ExecStartPre pip install to baton.service — prevents manual package installs 2026-03-21 09:17:06 +00:00
docs kin: BATON-FIX-011 Скрыть BOT_TOKEN из httpx/journalctl логов 2026-03-21 09:21:25 +02:00
frontend fix: viewport safe-area-inset for iOS PWA + disable pinch zoom 2026-03-21 16:40:30 +02:00
nginx infra: add Docker setup for portable deployment 2026-03-21 16:23:08 +02:00
tests sec: server-side email domain check + IP block on violations 2026-03-21 15:58:16 +02:00
.dockerignore infra: add Docker setup for portable deployment 2026-03-21 16:23:08 +02:00
.env.example kin: BATON-FIX-016 [TECH DEBT] VAPID public key жёстко вшит как пустая строка в <meta>-тег — требует ручного заполнения при деплое 2026-03-21 13:49:57 +02:00
.gitignore kin: BATON-002 [Research] UX Designer 2026-03-20 20:44:00 +02:00
.pre-commit-config.yaml sec: pre-commit hook + httpx exception logging hardening 2026-03-21 10:55:34 +02:00
=2.0.0 kin: BATON-008-backend_dev 2026-03-21 09:19:50 +02:00
ARCHITECTURE.md kin: BATON-ARCH-014 Доработать ADR-002 и ADR-004 по замечаниям ревью 2026-03-20 22:05:04 +02:00
docker-compose.yml infra: add Docker setup for portable deployment 2026-03-21 16:23:08 +02:00
Dockerfile infra: add Docker setup for portable deployment 2026-03-21 16:23:08 +02:00
pytest.ini kin: BATON-002 [Research] UX Designer 2026-03-20 20:44:00 +02:00
README.md kin: BATON-ARCH-003 Rate limiting /api/register + timing-safe сравнение токенов 2026-03-20 21:11:04 +02:00
requirements-dev.txt kin: BATON-002 [Research] UX Designer 2026-03-20 20:44:00 +02:00
requirements.txt kin: BATON-008-backend_dev 2026-03-21 09:19:50 +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

Готовые файлы находятся в deploy/:

# 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 — бесплатный сервис мониторинга, который пингует ваш /health снаружи каждые 5 минут. В отличие от self-ping, он работает даже если платформа убила процесс.

Настройка (бесплатно, без регистрации кредитной карты):

  1. Зарегистрируйтесь на 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.

Применение

# 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