diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5cb807c --- /dev/null +++ b/.env.example @@ -0,0 +1,31 @@ +# --- Core metadata -------------------------------------------------------- +PROJECT_NAME="Test Task CRM" +VERSION="0.1.0" +API_V1_PREFIX="/api/v1" + +# --- Database (used when DATABASE_URL не задан) --------------------------- +DB_HOST="localhost" +DB_PORT="5432" +DB_NAME="test_task_crm" +DB_USER="postgres" +DB_PASSWORD="postgres" +# DATABASE_URL="postgresql+asyncpg://postgres:postgres@localhost:5432/test_task_crm" + +# --- SQLAlchemy ----------------------------------------------------------- +SQLALCHEMY_ECHO="false" + +# --- JWT ----------------------------------------------------------------- +JWT_SECRET_KEY="change-me" +JWT_ALGORITHM="HS256" +ACCESS_TOKEN_EXPIRE_MINUTES="30" +REFRESH_TOKEN_EXPIRE_DAYS="7" + +# --- Redis cache for analytics ------------------------------------------- +REDIS_ENABLED="false" +REDIS_URL="redis://localhost:6379/0" +ANALYTICS_CACHE_TTL_SECONDS="120" +ANALYTICS_CACHE_BACKOFF_MS="200" + +# --- Frontend (Vite exposes только VITE_*) -------------------------------- +VITE_API_URL="http://localhost:8000" +VITE_APP_URL="http://localhost:5173" diff --git a/README.md b/README.md index 82f1e66..fc915eb 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,160 @@ # Test Task CRM -FastAPI backend template that follows the architecture described in the system prompt: async SQLAlchemy ORM, Alembic-ready models, service/repository layers, JWT auth helpers, and Pydantic v2 schemas. +Многопользовательская mini-CRM на FastAPI + PostgreSQL c сервисно-репозиторной архитектурой, JWT-аутентификацией, Alembic-миграциями и готовым React/Vite фронтендом. -## Quick start +## Стек и особенности -1. Create a `.env` file based on variables in `app/core/config.py`. -2. Install dependencies: - ```bash - uv sync - ``` -3. Run the development server: - ```bash - uv run python main.py - ``` +- Python 3.10+, FastAPI, SQLAlchemy Async ORM, Alembic. +- Pydantic Settings для конфигурации, JWT access/refresh токены, кеш аналитики в Redis. +- Frontend: Vite + React + TypeScript (см. `frontend/`). +- Докер-окружение для разработки (`docker-compose-dev.yml`) и деплоя (`docker-compose-ci.yml`). -## Project layout +## Структура проекта -``` +```text app/ - api/ # FastAPI routers and dependencies - core/ # Settings, database, security helpers - models/ # SQLAlchemy models and Pydantic schemas - repositories/ # Data access layer (SQLAlchemy ORM usage) - services/ # Business logic (auth, users, etc.) + api/ # FastAPI-роуты и зависимости + core/ # Настройки, база, безопасность + models/ # SQLAlchemy-модели + repositories/ # Работа с БД + services/ # Бизнес-правила и сценарии +frontend/ # Vite + React SPA +migrations/ # Alembic-миграции +tests/ # Pytest (unit + интеграции) ``` -Add new routers under `app/api/v1`, repositories under `app/repositories`, and keep business rules inside `app/services`. +## Переменные окружения -## Redis analytics cache +1. Скопируйте шаблон: `cp .env.example .env`. +2. Обновите секреты (`DB_PASSWORD`, `JWT_SECRET_KEY`, и т.д.) перед запуском. +3. Все переменные описаны в `app/core/config.py`; Vite читает только ключи с префиксом `VITE_`. -Analytics endpoints can use a Redis cache (TTL 120 seconds). The cache is disabled by default, so the service falls back to the database. +### Backend + +| Переменная | Значение по умолчанию | Назначение | +| --- | --- | --- | +| `PROJECT_NAME` | `"Test Task CRM"` | Заголовки/метаданные API | +| `VERSION` | `"0.1.0"` | Версия приложения | +| `API_V1_PREFIX` | `/api/v1` | Базовый префикс REST | +| `DB_HOST` | `localhost` | Хост PostgreSQL | +| `DB_PORT` | `5432` | Порт PostgreSQL | +| `DB_NAME` | `test_task_crm` | Имя БД | +| `DB_USER` | `postgres` | Пользователь БД | +| `DB_PASSWORD` | `postgres` | Пароль пользователя | +| `DATABASE_URL` | — | Полный DSN (перекрывает `DB_*`), формат `postgresql+asyncpg://user:pass@host:port/db` | +| `SQLALCHEMY_ECHO` | `false` | Логирование SQL | +| `JWT_SECRET_KEY` | `change-me` | Секрет для подписи JWT | +| `JWT_ALGORITHM` | `HS256` | Алгоритм JWT | +| `ACCESS_TOKEN_EXPIRE_MINUTES` | `30` | TTL access-токена | +| `REFRESH_TOKEN_EXPIRE_DAYS` | `7` | TTL refresh-токена | +| `REDIS_ENABLED` | `false` | Включить кеш аналитики | +| `REDIS_URL` | `redis://localhost:6379/0` | Строка подключения к Redis | +| `ANALYTICS_CACHE_TTL_SECONDS` | `120` | TTL кэша аналитики | +| `ANALYTICS_CACHE_BACKOFF_MS` | `200` | max задержка при ретраях записи в кеш | + +### Frontend (Vite) + +| Переменная | Значение по умолчанию | Назначение | +| --- | --- | --- | +| `VITE_API_URL` | `http://localhost:8000` (в `.env.example`), в `src/config/env.ts` дефолт `https://kitchen-crm.k1nq.tech` | Базовый URL бекенда (без завершающего `/`) | +| `VITE_APP_URL` | `http://localhost:5173` | URL SPA, используется для deeplink'ов и редиректов | + +⚠️ В `frontend/src/config/env.ts` зашит production URL (`https://kitchen-crm.k1nq.tech`). При деплое в другое место обязательно обновите `VITE_API_URL`/`VITE_APP_URL` в `.env` или настройте переменные окружения на уровне хостинга. + +## Локальный запуск без Docker + +### Предварительные требования + +- Python 3.10+ и [uv](https://github.com/astral-sh/uv). +- PostgreSQL 16+ (локально или через Docker). +- (Опционально) Redis 7+ для кеша аналитики. + +### Backend (API) + +```bash +# 1. Готовим окружение +cp .env.example .env +# отредактируйте .env: базы, секреты, VITE_* (если нужен фронтенд) + +# 2. Устанавливаем зависимости +uv sync + +# 3. Применяем миграции +uv run alembic upgrade head + +# 4. Запускаем API +uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 +``` + +PostgreSQL/Redis можно поднять вручную или командой `docker compose -f docker-compose-dev.yml up postgres redis -d`. + +### Frontend + +```bash +cd frontend +npm install +npm run dev -- --host +``` + +Фронтенд ожидает, что `VITE_API_URL` указывает на работающий бекенд (например, `http://localhost:8000`). + +## Запуск через Docker Compose + +### Локальная разработка (`docker-compose-dev.yml`) + +- Собирает бекенд из `app/Dockerfile`, поднимает `postgres:16-alpine` и `redis:7-alpine`. +- Порты по умолчанию: API `8000`, Postgres `5432`, Redis `6379`. +- Все переменные берутся из `.env`. + +```bash +docker compose -f docker-compose-dev.yml up --build +# или в фоне +docker compose -f docker-compose-dev.yml up --build -d +``` + +### Прод/CI стек (`docker-compose-ci.yml`) + +1. Соберите образы и статику: + + ```bash + docker build -f app/Dockerfile -t /test-task-crm:app . + docker build -f migrations/Dockerfile -t /test-task-crm:migrations . + cd frontend && npm install && npm run build && cd .. + ``` + +2. Залейте `frontend/dist` (используется как read-only volume) и задайте `GIT_HOST`, `GIT_USER`, `GIT_REPO` в `.env`. +3. Запустите стек: + + ```bash + docker compose -f docker-compose-ci.yml up -d + ``` + +В этом режиме `migrations` прогоняется один раз, `app` слушает порт `80`, Postgres хранит данные на `/mnt/data/postgres` (смонтируйте хостовую директорию заранее). + +## Redis для аналитики + +Кеш аналитики выключен по умолчанию. Чтобы включить: + +1. Поднимите Redis (`docker compose ... redis` или любым другим способом). +2. В `.env` выставьте: + - `REDIS_ENABLED=true` + - `REDIS_URL=redis://:6379/0` + - (опционально) `ANALYTICS_CACHE_TTL_SECONDS` и `ANALYTICS_CACHE_BACKOFF_MS`. +3. При недоступности Redis сервис автоматически возвращается к прямым запросам в PostgreSQL и пишет предупреждения в лог. + +## Тестирование + +Все тесты находятся в каталоге `tests/` (unit на бизнес-правила и интеграционные сценарии API). Запуск: + +```bash +uv run pytest +``` + +Полезные варианты: + +- Запустить только юнит-тесты сервисов: `uv run pytest tests/services -k service`. +- Запустить конкретный сценарий API: `uv run pytest tests/api/v1/test_deals.py -k won`. + +Перед деплоем рекомендуется прогонять миграции на чистой БД и выполнять `uv run pytest` для проверки правил ролей/стадий. -1. Start Redis and set the following variables: - - `REDIS_ENABLED=true` - - `REDIS_URL=redis://localhost:6379/0` - - `ANALYTICS_CACHE_TTL_SECONDS` (optional, defaults to 120) - - `ANALYTICS_CACHE_BACKOFF_MS` (max delay for write/delete retries, defaults to 200) -2. When Redis becomes unavailable, middleware logs the degradation and responses transparently fall back to database queries until connectivity is restored.