https://github.com/sibeardev/stina
MVP: Backend service for booking
https://github.com/sibeardev/stina
alembic fastapi postgres python3 redis taskiq
Last synced: about 5 hours ago
JSON representation
MVP: Backend service for booking
- Host: GitHub
- URL: https://github.com/sibeardev/stina
- Owner: sibeardev
- Created: 2026-06-17T10:59:45.000Z (12 days ago)
- Default Branch: main
- Last Pushed: 2026-06-19T10:11:44.000Z (10 days ago)
- Last Synced: 2026-06-19T12:11:58.413Z (10 days ago)
- Topics: alembic, fastapi, postgres, python3, redis, taskiq
- Language: Python
- Homepage:
- Size: 80.1 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Stina
Backend-сервис для записи на встречи. Пользователь создаёт бронь через REST API, система ставит задачу в очередь, воркер асинхронно подтверждает запись и логирует mock-уведомление.
> ⚠️ MVP. Проект реализован в рамках тестового задания.
## Стек
- **Python 3.14**, **FastAPI**, **SQLAlchemy 2 (async)**
- **PostgreSQL 18**, **Redis 7**
- **TaskIQ** + **taskiq-redis** (очередь и result backend)
- **Alembic** (миграции), **pytest** (тесты), **uv** (зависимости)
Документация после запуска: [http://localhost:8000/docs](http://localhost:8000/docs)
---
## Запуск сервиса
### Требования
- Docker и Docker Compose
- (опционально) [uv](https://docs.astral.sh/uv/) для локальной разработки и тестов
### 1. Клонирование и окружение
```bash
git clone https://github.com/sibeardev/stina.git
cd stina
cp .env.example .env
```
Переменные в `.env` (имена сервисов — из `docker-compose.yml`):
```env
POSTGRES_DB=db
POSTGRES_USER=user
POSTGRES_PASSWORD=password
REDIS_URL=redis://redis:6379/0
```
`POSTGRES_HOST` по умолчанию `db` — задаётся в [`app/core/config.py`](app/core/config.py) для контейнеров
### 2. Запуск
```bash
docker compose up --build
```
Поднимаются сервисы:
- **api** — FastAPI на порту `8000`
- **worker** — TaskIQ-воркер
- **db** — PostgreSQL
- **redis** — брокер и result backend
### 3. Миграции
При старте API автоматически выполняется `alembic upgrade head` (с повторными попытками, пока Postgres не готов).
Ручной запуск при необходимости:
```bash
docker compose exec api uv run alembic upgrade head
```
### 4. Пример запроса
```bash
curl -X POST http://localhost:8000/bookings \
-H "Content-Type: application/json" \
-d '{
"name": "Иван",
"datetime": "2026-12-01T15:00:00+03:00",
"service_type": "consultation"
}'
```
### 5. Остановка
```bash
docker compose down
```
Данные Postgres сохраняются в volume `postgres_data`. Полное удаление: `docker compose down -v`.
---
## Тесты
Тесты **не требуют Docker**: используется SQLite in-memory и мок постановки задачи в очередь.
```bash
uv sync --group dev
uv run pytest
```
С подробным выводом:
```bash
uv run pytest -v
```
Покрытие:
- API: создание, список, статус, отмена, валидация и пагинация
- воркер: подтверждение, имитация сбоя внешнего сервиса, идемпотентность
---
## Технические решения
### FastAPI + async SQLAlchemy
API полностью асинхронный (`asyncpg`). Согласуется с TaskIQ worker (тот же `BookingService` и та же async-сессия, без отдельного sync-слоя).
### TaskIQ вместо Celery
В задании Celery указан как базовый вариант. **TaskIQ** выбран как плюс за async-first подход:
Постановка задачи — в роуте **после** `commit` в `service.create()`, чтобы воркер в отдельном процессе уже видел запись в БД
### Идемпотентность
Повторный запуск `confirm_booking_task` с тем же `booking_id` безопасен:
1. Бронь читается с **`SELECT … FOR UPDATE`** — защита от гонок между воркерами.
2. Если записи нет или `status != pending` — **выход без изменений** (уже `confirmed` / `failed` / `cancelled`).
3. Повторное уведомление не отправляется.
Так задача переживает повторную доставку из Redis без дублей и без «отката» финального статуса.
### Домен и отмена
- Статусы вынесены в [`app/domain/enums.py`](app/domain/enums.py) — общий тип для API, ORM и воркера.
- `DELETE /bookings/{id}` — запись остаётся в истории со статусом `cancelled`.
### Миграции
**Alembic** + async-драйвер `asyncpg` для CLI