Развёртывание
Docker Compose
Проект использует раздельные Compose-файлы для разработки и продакшна.
Разработка (docker-compose.yml) — только инфраструктура
Приложения запускаются на хосте. PostgreSQL, Redis, MinIO и другие сервисы — в Docker.
# docker-compose.yml
services:
postgres:
image: postgres:16-alpine
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
mailpit:
image: axllent/mailpit:latest
ports:
- "1025:1025"
- "8025:8025"
minio:
image: minio/minio:latest
ports:
- "9000:9000"
- "9001:9001"
livekit:
image: livekit/livekit-server:latest
network_mode: hostПродакшн (docker-compose.prod.yml) — только приложения
Инфраструктура — внешние управляемые сервисы (RDS, ElastiCache и т.д.).
# docker-compose.prod.yml
services:
migration:
image: ${REGISTRY_HOST}/planner-migration:latest
profiles: [migrate]
backend:
image: ${REGISTRY_HOST}/planner-backend:latest
expose:
- "4000"
frontend:
image: ${REGISTRY_HOST}/planner-frontend:latest
expose:
- "80"
website:
image: ${REGISTRY_HOST}/planner-website:latest
expose:
- "8080"
docs:
image: ${REGISTRY_HOST}/planner-docs:latestРаннер миграций
Лёгкий Docker-образ для запуска Prisma-миграций и заполнения данными в продакшне.
# Запуск миграций
docker compose --profile migrate run --rm migration
# Заполнение данными после миграций
docker compose --profile migrate run --rm migration seedОбраз миграций (docker/Dockerfile.migration) основан на node:22-alpine с prisma, tsx и dotenv. Точка входа (docker/entrypoint.sh) поддерживает две команды:
migrate(по умолчанию) —npx prisma migrate deployseed—npx prisma db seed(запускаетtsx prisma/seed.ts)
Бэкенд также запускает prisma migrate deploy при старте как страховочная сетка (идемпотентно).
Продакшн-развёртывание
docker compose -f docker-compose.prod.yml up -dФайл продакшн-сборки использует оптимизированные Dockerfile (Dockerfile.backend.prod) с многоэтапной сборкой для уменьшения размера образов.
Dockerfile
Все Dockerfile основаны на node:22-alpine с единой системой наименования этапов:
| Этап | Описание |
|---|---|
base | Базовый образ Node |
deps | Установка зависимостей |
dev | Режим разработки |
builder | Сборка приложения |
runner | Продакшн-среда выполнения |
Бэкенд Dockerfile (docker/Dockerfile.backend)
FROM node:22-alpine AS base
# ... стандартная настройка pnpm с corepack
WORKDIR /app
FROM base AS deps
COPY pnpm-lock.yaml package.json pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile
FROM base AS dev
COPY --from=deps /app/node_modules ./node_modules
COPY . .
CMD ["sh", "-c", "cd apps/backend && pnpm dev"]Переменные окружения
| Переменная | По умолчанию | Описание |
|---|---|---|
DATABASE_URL | postgresql://planner:planner@localhost:5432/planner | Подключение к PostgreSQL |
JWT_SECRET | (обязательно) | Секрет для подписи JWT |
REDIS_PASSWORD | planner_redis | Пароль Redis |
SMTP_HOST | (опционально) | SMTP сервер для писем |
SMTP_PORT | 1025 | SMTP порт |
SMTP_USER | (опционально) | SMTP пользователь |
SMTP_PASS | (опционально) | SMTP пароль |
SMTP_FROM | noreply@planner.app | Email отправителя |
CORS_ORIGINS | (обязательно) | Разрешённые источники (через запятую) |
UPLOAD_DIR | ./uploads | Директория загрузки файлов |
Безопасность
HTTPS / SSL
Продакшн-развёртывание использует Caddy reverse proxy с автоматическим Let's Encrypt SSL. Порт 80 должен быть доступен для валидации домена. Весь трафик шифруется через TLSv1.2/TLSv1.3.
Заголовки безопасности
- CSP: Content Security Policy ограничивает источники скриптов, стилей, шрифтов, изображений и подключений
- HSTS:
max-age=31536000; includeSubDomains; preload(только продакшн) - X-Content-Type-Options:
nosniffна всех ответах - CORS: Белый список через
CORS_ORIGINS,credentials: true
Ограничение запросов
- Глобально: 60 запросов/минуту через Redis
- На эндпоинты: регистрация (5/мин), вход (10/мин), обновление токена (20/мин), забыли пароль (3/мин), сброс пароля (5/мин), подтверждение email (5/мин), повторная отправка (3/мин)
- Блокировка: 5 неудачных попыток → блокировка на 15 минут
Загрузка файлов
- Макс. размер: 50MB (документы), 2MB (аватары)
- Валидация magic bytes (JPEG, PNG, PDF и т.д.)
- SVG файлы заблокированы (XSS риск)
- Документы отдаются с
Content-Disposition: attachment - S3 ACL: аватары
public-read, документыprivate
Документация API
Swagger UI доступен по /api/docs только в режиме разработки. Отключён в продакшне (NODE_ENV === 'production').
Порт маппинг
| Сервис | Внутренний порт | Внешний порт |
|---|---|---|
| Бэкенд | 4000 | 4000 |
| Фронтенд | 3000 | 3000 |
| Сайт | 8080 | 8080 |
| Документация | 8080 | 8081 |
| PostgreSQL | 5432 | 5432 |
Проверка здоровья
Все сервисы отвечают HTTP 200 при работе:
curl http://localhost:4000/api/health
curl http://localhost:3000
curl http://localhost:8080
curl http://localhost:8081Эндпоинт здоровья проверяет подключение к БД, Redis, LiveKit и S3. Детали ошибок скрыты в продакшне.