Монорепозиторий для продуктовой команды Esprit. Содержит:
apps/support-app— операционное приложение для саппорта (тикеты, авторизация)apps/login-app— кастомный стор авхода (редирект на внешнее приложение авторизации)packages/ui— UI-библиотека с Tailwind, Storybook и VitePresspackages/core— typed API client, AuthService, TokenManager, mock fetcher'ы
- Node.js 20+
- pnpm 9.12.0 (указан в
packageManager, Corepack активирует автоматически) - Git — для клонирования и conventional commits
esprit-mono/
├── apps/
│ ├── login-app/ # Приложение авторизации
│ ├── support-app/ # Приложение саппорта
│ ├── Dockerfile # Универсальный Dockerfile (APP_NAME=login-app|support-app)
│ └── nginx.conf
├── packages/
│ ├── ui/ # @esprit/ui — компоненты, Storybook, VitePress
│ └── core/ # @esprit/core — HttpClient, auth, mocks
├── deployment/ # Ansible деплой
│ ├── playbook.yml # Основной playbook деплоя
│ ├── rollback.yml # Откат к предыдущей версии
│ ├── monitoring.yml # Мониторинг здоровья
│ ├── post-deploy-check.yml
│ ├── inventory/ # staging.yml, production.yml
│ ├── templates/ # docker-compose.yml.j2, env.j2, nginx.conf.j2
│ ├── Makefile # make deploy-staging, make rollback-production, ...
│ └── env.template # cp env.template .env — для ручного деплоя
├── .gitlab-ci.yml # CI/CD pipeline (GitLab)
├── docker-compose.yml # Запуск в Docker (локально)
├── docker-build.sh
└── turbo.json
- Vue 3 +
<script setup>+ строгий TypeScript - Vite для dev/build, Vitest + Testing Library для unit-тестов
- Pinia, Vue Router, Suspense, Teleport, Provide/Inject
- Tailwind дизайн-система, Storybook 8, VitePress документация
- pnpm workspaces + Turborepo pipeline
- Husky, lint-staged, ESLint 9 (flat config), Prettier, Stylelint, Commitlint (conventional commits)
- Docker — контейнеризация приложений и Storybook
pnpm install
pnpm dev # support-app на http://localhost:8081
pnpm dev:login # login-app на http://localhost:8080
pnpm dev:all # Storybook + оба приложения (параллельно)Vite proxy (support-app): В dev-режиме запросы /auth/* и /support/* проксируются на внешние API (см. apps/support-app/vite.config.ts). Это устраняет проблемы CORS при локальной разработке.
docker-compose up -d --build| Сервис | URL |
|---|---|
| Login App | http://localhost:8080 |
| Support App | http://localhost:8081 |
| Storybook | http://localhost:6006 |
Health checks: http://localhost:8080/health, http://localhost:8081/health
Переменные Docker (опционально):
NODE_VERSION=20-alpine PNPM_VERSION=9.12.0 docker-compose up -d --buildРучная сборка образов:
./docker-build.sh
# или
docker build -f apps/Dockerfile --build-arg APP_NAME=login-app -t esprit-login-app:latest .
docker build -f apps/Dockerfile --build-arg APP_NAME=support-app -t esprit-support-app:latest .
docker build -f packages/ui/Dockerfile -t esprit-storybook:latest .# Staging (автоматически при push в develop)
git push origin develop
# Production (вручную в GitLab UI после push в main)
git push origin main
# → GitLab → CI/CD → Pipelines → deploy:production → Play ▶️- Task intake — заведите issue, определите acceptance criteria
- Branching —
feat/*,fix/*,chore/*; base branchmain - Commits — conventional commits (
feat:,fix:,docs:, и т.д.), валидируются commitlint - Tests —
pnpm test --filter <package>перед PR - Code review — создаём Merge Request, CI должен пройти, ревью минимум от 1 senior
| Команда | Описание |
|---|---|
pnpm lint |
ESLint, Stylelint, Prettier по всем пакетам |
pnpm lint:fix |
Автоисправление линтером |
pnpm test |
Vitest во всех рабочих пространствах (UI/Core/Apps) |
pnpm build |
Каскадная сборка (core → ui → apps) с Turborepo cache |
pnpm storybook |
Storybook UI-библиотеки (порт 6006) |
pnpm docs |
VitePress документация дизайн-системы |
pnpm ci |
lint + test + build (используется в CI) |
Husky + lint-staged следят за форматированием при коммите. Commitlint проверяет conventional commits.
@esprit/core предоставляет createSupportMockFetcher и createAuthMockFetcher. Для production задайте VITE_*_API_URL и VITE_*_USE_MOCKS=false.
- Философия: композиция через Composition API, единые дизайн-токены, Storybook + VitePress
- Storybook:
pnpm storybook(порт 6006) - Документация:
pnpm docs— гайды по компонентам и токенам - Использование:
import { Button, Modal, Alert, Input } from '@esprit/ui'
| Переменная | Описание |
|---|---|
VITE_SUPPORT_API_URL |
URL API саппорта |
VITE_SUPPORT_USE_MOCKS |
Использовать mock fetcher для support API |
VITE_AUTH_API_URL |
URL API авторизации |
VITE_AUTH_USE_MOCKS |
Использовать mock fetcher для auth API |
VITE_LOGIN_APP_URL |
URL login-app (для редиректов guard'ов) |
VITE_SUPPORT_APP_URL |
URL support-app (для редиректов) |
VITE_DEFAULT_LOCALE |
Базовая локаль (en), переопределяется доменом (.ru → ru) |
VITE_PORTAL_URL |
URL портала (support-app config) |
VITE_AUTH_BASE_URL |
Базовый URL внешнего приложения авторизации |
| Переменная | По умолчанию | Описание |
|---|---|---|
VITE_AUTH_MODE |
redirect |
redirect или api (OAuth PKCE) |
VITE_AUTH_URL |
/login |
Путь для авторизации |
VITE_AUTH_CONSUMER |
freescout-auth |
Идентификатор потребителя |
VITE_AUTH_THEME |
— | Тема интерфейса авторизации |
VITE_AUTH_LOCALE |
ru_RU |
Локаль (ru) |
VITE_AUTH_LOCALE_EN |
en_EN |
Локаль (en) |
VITE_ONE_TIME_CODE_PARAM |
one_time_code |
GET-параметр с one-time кодом |
VITE_MOBILE_CODE_PARAM |
mobile_code |
GET-параметр с mobile кодом |
| Переменная | Описание |
|---|---|
VITE_USE_HTTPONLY_REFRESH_TOKEN |
Использовать httpOnly cookie вместо localStorage |
| App | Port | Роуты | Описание |
|---|---|---|---|
| support-app | 8081 | /, /tickets, /ticket/:id, /new, /components-examples |
Мультиязычная панель саппорта, guard installAuthGuard, стор session. При отсутствии токена — редирект на login-app. |
| login-app | 8080 | / (AuthView) |
Авторизация через редирект на внешнее приложение. Точки входа: сайт (channel=site), мобильная игра (channel=game). Композиции useRedirectAuth, useOneTimeCode (legacy). |
Структура src: src/modules/* — бизнес-фичи, src/app/* — провайдеры, guards, config. Алиасы @support/*, @login/*, @esprit/ui, @esprit/core.
- @esprit/ui — Vite library mode, Tailwind дизайн-токены, Storybook 8, VitePress, Vitest
- @esprit/core —
HttpClientс интерсепторами,AuthService,TokenManager, mock fetcher'ы (createAuthMockFetcher,createSupportMockFetcher)
Turborepo, pnpm workspaces, глобальные алиасы (tsconfig.base.json), Husky + lint-staged, Commitlint. CI/CD: GitLab CI/CD (.gitlab-ci.yml) — install → lint → test → build → docker → security → deploy → verify.
| Этап | Описание | Время (с кешем) |
|---|---|---|
| Install | pnpm с кешированием по pnpm-lock.yaml | ~20s |
| Test | Lint + Vitest (параллельно) | ~1m |
| Build | turbo run build (core → ui → apps) | ~2m |
| Docker | login-app + support-app (параллельно, BuildKit cache) | ~1m |
| Security | Trivy (HIGH/CRITICAL блокируют production) | ~2m |
| Deploy | Staging (авто) / Production (вручную) | ~3m |
| Verify | Health checks, логи, ресурсы | ~1m |
Общее время: ~6–8 минут с кешем, ~12–15 минут cold start.
Ветки: develop → Staging (автоматически), main → Production (ручной запуск job в GitLab UI).
Settings → CI/CD → Variables:
| Переменная | Тип | Protected | Описание |
|---|---|---|---|
SSH_DEPLOY_KEY |
File | Yes | Приватный SSH ключ для deploy |
GitLab автоматически предоставляет: CI_REGISTRY, CI_REGISTRY_USER, CI_REGISTRY_PASSWORD.
ssh-keygen -t ed25519 -C "gitlab-ci-deploy" -f ~/.ssh/gitlab_deploy_key
cat ~/.ssh/gitlab_deploy_key.pub # → в authorized_keys на сервере
# Содержимое ~/.ssh/gitlab_deploy_key → в GitLab Variables как SSH_DEPLOY_KEYТребования: Ubuntu 20.04+ / Debian 11+, 2GB RAM, 20GB disk, порты 22, 80, 443.
# Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo systemctl enable docker && sudo systemctl start docker
# Пользователь deploy
sudo useradd -m -s /bin/bash deploy
sudo usermod -aG docker deploy
echo "deploy ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/deploy
sudo chmod 0440 /etc/sudoers.d/deploy
# SSH
sudo -u deploy mkdir -p /home/deploy/.ssh
sudo -u deploy nano /home/deploy/.ssh/authorized_keys # вставить публичный ключ
sudo chmod 600 /home/deploy/.ssh/authorized_keys
sudo chown -R deploy:deploy /home/deploy/.ssh
# Директории
sudo -u deploy mkdir -p /opt/esprit-apps/backups
sudo chown -R deploy:deploy /opt/esprit-apps
# Firewall
sudo ufw allow 22/tcp && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp
sudo ufw enable
# SSL (production) — Certbot
sudo apt install certbot
sudo certbot certonly --standalone -d login.yourdomain.com -d support.yourdomain.comОбновите deployment/inventory/staging.yml и production.yml:
| Переменная | Описание | Пример |
|---|---|---|
ansible_host |
IP или домен сервера | staging.example.com |
ansible_user |
SSH пользователь | deploy |
domain_login |
Домен login-app | login.esprit.example.com |
domain_support |
Домен support-app | support.esprit.example.com |
login_app_port |
Порт (внутри Docker) | 3001 |
support_app_port |
Порт (внутри Docker) | 3002 |
ssl_enabled |
SSL для production | true |
ssl_cert_path |
Путь к сертификату | /etc/letsencrypt/live/.../fullchain.pem |
ssl_key_path |
Путь к приватному ключу | /etc/letsencrypt/live/.../privkey.pem |
vite_auth_api_url |
URL API auth | https://api.../auth |
vite_support_api_url |
URL API support | https://api.../support |
vite_login_app_url |
URL login-app | https://login... |
vite_support_app_url |
URL support-app | https://support... |
vite_default_locale |
Локаль | ru |
A-записи: login.yourdomain.com, support.yourdomain.com → IP сервера.
cd deployment
ansible -i inventory/staging.yml app_servers -m ping
ansible -i inventory/production.yml app_servers -m pingcd deployment
cp env.template .env
nano .env # REGISTRY_USER, REGISTRY_PASSWORD, REGISTRY_URL, PROJECT_PATH, IMAGE_TAG
# Деплой
make deploy-staging # Staging
make deploy-production # Production (с подтверждением)cd deployment
# Деплой
make deploy-staging
make deploy-production
# Откат
make rollback-staging
make rollback-production
# Мониторинг
make monitoring-staging
make monitoring-production
# Проверка
make check-staging
make check-production
# Логи
make logs-staging
make logs-production
# Тест подключения
make ping-staging
make ping-production
make help # Все командыПеременные Makefile: REGISTRY_URL, REGISTRY_USER, REGISTRY_PASSWORD, PROJECT_PATH, IMAGE_TAG (из .env или окружения).
cd deployment
ansible-playbook -i inventory/production.yml \
-e "registry_url=registry.gitlab.com" \
-e "registry_user=your_user" \
-e "registry_password=your_token" \
-e "image_tag=latest" \
-e "project_path=your-group/esprit-mono" \
-e "environment=production" \
playbook.yml# Статус
docker ps
docker stats esprit-login-app esprit-support-app
# Логи
docker logs -f esprit-support-app
docker logs --tail 100 esprit-login-app 2>&1 | grep -i error
# Health check
docker inspect esprit-login-app | jq '.[].State.Health'
# Ручная проверка (стейдж)
curl -f https://test-login.espritgames.ru/ && echo "✅ Login OK"
curl -f https://test-support.espritgames.ru/ && echo "✅ Support OK"- Makefile:
make rollback-production - GitLab UI: Pipelines → предыдущий успешный pipeline → Run
rollback:productionилиdeploy:production - Ansible:
ansible-playbook -i inventory/production.yml rollback.yml
- SSH_DEPLOY_KEY в GitLab Variables
- Docker (>= 24.0), Docker Compose (>= 2.0) на сервере
- Пользователь
deploy, группаdocker,/opt/esprit-apps - Firewall: 22, 80, 443
- SSL сертификаты (production)
- Inventory обновлён
- DNS настроен
-
ansible ... -m pingуспешен - Тестовый деплой на staging прошёл
Приложение не отвечает:
ssh deploy@server "docker ps"ssh deploy@server "docker logs --tail 100 esprit-support-app"ssh deploy@server "docker restart esprit-support-app"- При необходимости:
make rollback-production
Высокая нагрузка: docker stats --no-stream, free -h на сервере.
Очистка места: docker image prune -af --filter "until=168h" или docker system prune -af.
| Endpoint | Method | Description |
|---|---|---|
/.well-known/openid-configuration |
GET | OpenID Connect конфигурация |
/.well-known/jwks.json |
GET | Публичные ключи для JWT |
/auth/register |
POST | Регистрация { email, password } |
/auth/login |
POST | Вход с OAuth 2.0 (client_id, redirect_uri, state, code_challenge) |
/auth/token |
POST | Обмен code на токены |
/refresh |
POST | Обновление access token |
/auth/userinfo |
GET | Информация о пользователе |
/auth/logout |
POST | Инвалидация сессии |
/auth/external/{platform}/auth |
GET | OAuth внешнего провайдера |
/auth/external/{platform}/callback |
GET | Callback от провайдера |
/exchange/onetime_code |
POST | Обмен one_time_code на токены (redirect flow) |
/auth/code-exchange |
POST | Legacy: обмен one_time_code + channel на accessToken |
Redirect flow: login-app → редирект на внешнее приложение → callback с one_time_code → POST /exchange/onetime_code → токены → редирект на support-app с access_token, refresh_token, expires_in в URL.
OAuth 2.0 PKCE (режим api): state, code_verifier, code_challenge → POST /auth/login → редирект с code → POST /auth/token.
| Endpoint | Method | Description |
|---|---|---|
/tickets |
GET | Список тикетов |
/tickets |
POST | Создание { subject, priority, message } |
/tickets/:id |
GET | Детали тикета |
/tickets/:id/conversation |
GET, POST | Сообщения диалога (POST: { body, author }) |
import { HttpClient } from '@esprit/core'
import { createSupportMockFetcher } from '@esprit/core/mocks'
const client = new HttpClient({
baseUrl: 'https://api.esprit.io',
fetcher: createSupportMockFetcher({ tickets }),
getAccessToken: () => tokenManager.getToken(),
refreshToken: () => authService.refresh(),
})При 401 клиент вызывает refreshToken; при провале — onUnauthorized (очистка токена, редирект).
Не используйте sudo для docker. При необходимости:
sudo chown -R "$USER":staff ~/.dockerПерезапустите Docker Desktop.
Измените маппинг в docker-compose.yml:
ports:
- '8082:80'Ошибка fatal: repository 'https://gitlab.espritgames.ru/frontend/esprit-mono.git/' not found возникает до запуска джобов — Runner не может склонировать репозиторий. Исправляется в настройках GitLab и Runner, не в .gitlab-ci.yml.
Что проверить:
-
Путь проекта
GitLab → проект → Settings → General — убедитесь, что путь репозитория именноfrontend/esprit-mono. Если проект переносили или переименовывали, URL мог измениться. -
Доступ Runner к проекту
Runnerdeploy(t3_C9N1Nz) должен быть зарегистрирован в этом проекте или в группеfrontend.- Settings → CI/CD → Runners: если используется shared runner — админу GitLab нужно выдать группе/проекту доступ к этому runner.
- На сервере с runner: в
config.tomlпроверьтеurl— должен бытьhttps://gitlab.espritgames.ru/.
-
CI_JOB_TOKEN (для приватных репо)
GitLab 15.9+ может ограничивать доступ CI job token.- Settings → CI/CD → Token Access: для проекта или группы не должно быть запрета доступа к этому репо (или включите разрешение для этого проекта).
-
Переменные CI/CD
Settings → CI/CD → Variables: убедитесь, что нет переменных вродеGIT_REPOSITORY_URLилиGIT_STRATEGY, которые подменяют URL или способ клонирования. -
Репозиторий не удалён
В Settings → General → Advanced проверьте, что проект не в процессе удаления и репозиторий доступен по HTTPS в браузере (логин при необходимости).
После изменений перезапустите pipeline (Retry).
Проверьте cache в GitLab UI, попробуйте очистить (CI/CD → Pipelines → Clear runner caches).
- Проверьте
SSH_DEPLOY_KEYв GitLab Variables - Тест:
ssh -i <path-to-key> deploy@your-server - Проверьте права:
~/.ssh700,authorized_keys600
- Проверьте логи контейнеров:
docker logs esprit-login-app - Увеличьте timeout в
deployment/playbook.yml - Убедитесь, что приложения отвечают на
/или/health
GitLab → Pipelines → найдите нужный pipeline → Retry job deploy:production.