Files
drivers_bot/README.md
2026-05-12 19:14:21 +09:00

139 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Drivers Bot
Telegram bot + Telegram Mini App для учета автомобилей, заправок, сервиса, жидкостей, напоминаний и стоимости владения.
## Состав
- `app/` - FastAPI API, статика Mini App, бизнес-логика и Alembic.
- `bot/` - aiogram 3 бот, который открывает Mini App и работает с API через внутренний токен.
- `web/` - статический frontend Telegram WebApp.
- `alembic/` - миграции PostgreSQL.
- `tests/` - базовые security/API тесты.
## Production Mini App
Для production Mini App должен открываться только по публичному HTTPS-домену. Для текущего проекта:
```text
https://drivers.smartsoltech.kr
```
В BotFather нужно выполнить:
```text
/setdomain
@seoulmate_officialbot
drivers.smartsoltech.kr
```
Важно:
- в BotFather указывается домен без `https://`;
- `WEBAPP_URL` или `PUBLIC_WEBAPP_URL` в `.env` должны быть `https://drivers.smartsoltech.kr`;
- нельзя использовать `localhost`, `127.0.0.1`, внутренний IP или `http://` для Telegram Mini App в production;
- если появляется `Bot domain invalid`, сначала проверь `/setdomain` и значение `WEBAPP_URL` в контейнере бота.
## Production .env
```dotenv
POSTGRES_DB=drivers
POSTGRES_USER=drivers
POSTGRES_PASSWORD=change-this-db-password
POSTGRES_PORT=5433
DATABASE_URL=postgresql+asyncpg://drivers:change-this-db-password@db:5432/drivers
BOT_TOKEN=123456:telegram-token
BOT_USERNAME=seoulmate_officialbot
WEBAPP_URL=https://drivers.smartsoltech.kr
PUBLIC_WEBAPP_URL=https://drivers.smartsoltech.kr
API_BASE_URL=http://api:8000
CORS_ORIGINS=https://drivers.smartsoltech.kr,https://t.me
INTERNAL_API_TOKEN=change-this-long-random-token
APP_ENV=production
ALLOW_DEV_AUTH=false
VAPID_PUBLIC_KEY=
```
`BOT_TOKEN`, `DATABASE_URL`, `WEBAPP_URL`, `API_BASE_URL`, `CORS_ORIGINS`, `INTERNAL_API_TOKEN` читаются только из env. Секреты не хранятся в коде.
## Nginx
Пример reverse proxy:
```nginx
server {
listen 80;
server_name drivers.smartsoltech.kr;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name drivers.smartsoltech.kr;
ssl_certificate /etc/letsencrypt/live/drivers.smartsoltech.kr/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/drivers.smartsoltech.kr/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}
```
## Запуск
```bash
cp .env.example .env
docker compose up -d --build
```
API локально: `http://localhost:8000`.
Локальные проверки:
```bash
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest -q
.venv/bin/ruff check app bot tests
docker compose build
docker compose up -d db api
curl http://127.0.0.1:8000/health
docker compose down
```
## Авторизация API
Пользовательские endpoint-ы требуют Telegram WebApp `initData` в заголовке:
```http
X-Telegram-Init-Data: query_id=...&user=...&auth_date=...&hash=...
```
Backend проверяет подпись Telegram, создает/обновляет пользователя и разрешает операции только с объектами владельца. Бот использует `INTERNAL_API_TOKEN` и `X-Telegram-User-Id`.
Публичное `/api/users` закрыто внутренним токеном. Для Mini App создание пользователя выполняется через `/api/users/webapp-auth`.
## Основные endpoint-ы
- `GET /api/users/me`
- `POST /api/cars`, `GET /api/cars`, `GET/PATCH/DELETE /api/cars/{id}`
- `POST /api/fuel`, `GET /api/cars/{car_id}/fuel?limit=50&offset=0`
- `PATCH /api/fuel/{id}`, `DELETE /api/fuel/{id}`
- `POST /api/service`, `GET /api/cars/{car_id}/service?limit=50&offset=0`
- `PATCH /api/service/{id}`, `DELETE /api/service/{id}`
- `GET /api/cars/{car_id}/stats`
- `GET /api/users/{user_id}/reminders?limit=50&offset=0`
- `POST /api/ocr/parse-text-receipt`
Расход топлива считается по интервалам между полными баками (`is_full_tank=true`). Если данных мало, API возвращает `null`, а не выдуманную цифру.
## OCR
Настоящий OCR по фото/PDF пока не подключен. Endpoint `POST /api/ocr/parse-text-receipt` честно разбирает только текстовый чек. Старый `/api/ocr/fuel-receipt` оставлен как deprecated-совместимость.