139 lines
5.0 KiB
Markdown
139 lines
5.0 KiB
Markdown
# 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-совместимость.
|