add no ssl install script

This commit is contained in:
lendry
2026-06-24 15:06:01 +03:00
parent d2bbf35d40
commit 1b689fabc2
7 changed files with 923 additions and 425 deletions

View File

@@ -1,9 +1,15 @@
# Lendry ID — переменные для docker compose # Lendry ID — переменные для docker compose
# Скопируйте в .env или запустите: ./install.sh # Скопируйте в .env или запустите: ./install.sh
# Режим: local (порты) | production (Nginx + SSL) # Режим: local | intranet | production
INSTALL_MODE=local INSTALL_MODE=local
# Nginx: auto (Windows → Docker) | host (Linux) | docker
NGINX_MODE=auto
# SSL: none (HTTP) | selfsigned (HTTPS локально) | letsencrypt (интернет)
SSL_TYPE=none
# Домены без протокола (install.sh подставит PUBLIC_* URL) # Домены без протокола (install.sh подставит PUBLIC_* URL)
DOMAIN_API= DOMAIN_API=
DOMAIN_FRONTEND= DOMAIN_FRONTEND=
@@ -19,7 +25,7 @@ PUBLIC_FRONTEND_URL=http://localhost:3002
PUBLIC_DOCS_URL=http://localhost:3003 PUBLIC_DOCS_URL=http://localhost:3003
PUBLIC_WS_URL=ws://localhost:8085/ws PUBLIC_WS_URL=ws://localhost:8085/ws
# Nginx + Let's Encrypt # Nginx (USE_NGINX_SSL=true только при SSL_TYPE=letsencrypt|selfsigned)
USE_NGINX_SSL=false USE_NGINX_SSL=false
CERTBOT_EMAIL= CERTBOT_EMAIL=

3
.gitignore vendored
View File

@@ -8,6 +8,9 @@ dist
!.env.example !.env.example
docker-compose.override.yml docker-compose.override.yml
.idp-install.json .idp-install.json
nginx/certs/*
!nginx/certs/.gitkeep
nginx/conf.d/*.conf
coverage coverage
generated generated
*.tsbuildinfo *.tsbuildinfo

View File

@@ -63,6 +63,21 @@ export const docPages: DocPage[] = [
id: 'quick-start', id: 'quick-start',
title: 'Быстрый запуск', title: 'Быстрый запуск',
blocks: [ blocks: [
{
type: 'callout',
variant: 'tip',
title: 'Production на сервере',
text: 'Для развёртывания на Linux с Docker, Nginx и SSL используйте скрипт install.sh — подробности в разделе «Развёртывание на сервере».'
},
{
type: 'code',
language: 'bash',
title: 'Сервер (рекомендуется)',
code: `git clone https://git.lendry.ru/lendry/IdP.git
cd IdP
chmod +x install.sh
./install.sh`
},
{ {
type: 'code', type: 'code',
language: 'bash', language: 'bash',
@@ -158,159 +173,294 @@ curl http://localhost:3003/docs/getting-started`
{ {
slug: 'deployment', slug: 'deployment',
title: 'Развёртывание на сервере', title: 'Развёртывание на сервере',
description: 'Пошаговый гайд по production-развёртыванию на Linux-сервере с Docker, Nginx и TLS.', description: 'Автоматическая установка через install.sh: Docker, Nginx, SSL и отдельные домены для каждого сервиса.',
sections: [ sections: [
{
id: 'install-script',
title: '1. Автоматическая установка (install.sh)',
blocks: [
{
type: 'paragraph',
text: 'Скрипт install.sh разворачивает весь стек одной командой: Docker, Nginx, генерация .env, reverse proxy. Поддерживает локальную сеть без интернета (--intranet), самоподписанный HTTPS и Let\'s Encrypt для публичных серверов. На Windows Server Nginx запускается в Docker-контейнере.'
},
{
type: 'callout',
variant: 'tip',
title: 'Локальная сеть (AD, .lpr, .local)',
text: 'Если домены только внутри LAN и Let\'s Encrypt недоступен — используйте ./install.sh --intranet --ssl none. Доступ: http://api.idpmvk.lpr без выхода в интернет.'
},
{
type: 'code',
language: 'bash',
title: 'Быстрый старт',
code: `git clone https://git.lendry.ru/lendry/IdP.git
cd IdP
chmod +x install.sh
./install.sh`
},
{
type: 'code',
language: 'bash',
title: 'Production без вопросов',
code: `./install.sh --install --yes \\
--api-domain api.id.example.ru \\
--frontend-domain id.example.ru \\
--docs-domain docs.example.ru \\
--email admin@example.ru`
},
{
type: 'code',
language: 'bash',
title: 'Установка без клонирования',
code: `curl -fsSL https://git.lendry.ru/lendry/IdP/raw/branch/main/install.sh | bash`
}
]
},
{
id: 'intranet-network',
title: '2. Локальная сеть (без интернета)',
blocks: [
{
type: 'paragraph',
text: 'Для корпоративной LAN, Windows Server и внутренних доменов (например idpmvk.lpr) не нужен доступ из интернета и Let\'s Encrypt. Скрипт настраивает Nginx на HTTP или HTTPS с самоподписанным сертификатом (openssl). На Windows Server Nginx работает в Docker (профиль proxy).'
},
{
type: 'code',
language: 'bash',
title: 'Рекомендуемая команда (HTTP)',
code: `./install.sh --intranet --install --yes --offline \\
--api-domain api.idpmvk.lpr \\
--frontend-domain id.idpmvk.lpr \\
--docs-domain docs.idpmvk.lpr \\
--ssl none`
},
{
type: 'code',
language: 'bash',
title: 'HTTPS самоподписанный (опционально)',
code: `./install.sh --intranet --install --yes --offline \\
--api-domain api.idpmvk.lpr \\
--frontend-domain id.idpmvk.lpr \\
--docs-domain docs.idpmvk.lpr \\
--ssl selfsigned`
},
{
type: 'table',
headers: ['SSL_TYPE', 'Описание', 'Интернет'],
rows: [
['none', 'http://api.idpmvk.lpr — без сертификатов', 'Не нужен'],
['selfsigned', 'https://... — openssl, предупреждение браузера', 'Не нужен'],
['letsencrypt', 'Публичный HTTPS', 'Нужен']
]
},
{
type: 'callout',
variant: 'warning',
title: 'DNS на клиентах',
text: 'Пропишите A-записи во внутреннем DNS Active Directory или файл hosts на каждом ПК: IP-сервера → api.idpmvk.lpr, id.idpmvk.lpr, docs.idpmvk.lpr. Windows: C:\\Windows\\System32\\drivers\\etc\\hosts'
},
{
type: 'code',
language: 'text',
title: 'Пример hosts (Windows / Linux)',
code: `192.168.1.10 api.idpmvk.lpr
192.168.1.10 id.idpmvk.lpr
192.168.1.10 docs.idpmvk.lpr`
},
{
type: 'callout',
variant: 'info',
title: 'Windows Server',
text: 'Запускайте install.sh из Git Bash или WSL. Docker Desktop / Docker Engine должен быть установлен заранее (--offline). Nginx поднимается контейнером на портах 80 и 443.'
}
]
},
{ {
id: 'server-requirements', id: 'server-requirements',
title: '1. Требования к серверу', title: '3. Требования к серверу',
blocks: [ blocks: [
{ {
type: 'list', type: 'list',
items: [ items: [
'Ubuntu 22.04+ / Debian 12+ / аналогичный Linux', 'Ubuntu 22.04+ / Debian 12+ / Fedora / RHEL (Linux)',
'Минимум 4 GB RAM, 2 vCPU, 40 GB SSD', 'Минимум 4 GB RAM, 2 vCPU, 40 GB SSD',
'Домен с DNS A-записью (например id.lendry.ru)', 'Отдельные домены или поддомены для API, Frontend и Docs (A-запись → IP сервера)',
'Открытые порты 80 и 443 (остальное — только localhost / internal network)' 'Открытые порты 80 и 443 (остальные сервисы доступны только через Nginx на localhost)',
'Права sudo/root для установки Docker и Nginx'
] ]
} }
] ]
}, },
{ {
id: 'install-docker', id: 'domains',
title: '2. Установка Docker', title: '4. Отдельные домены для сервисов',
blocks: [ blocks: [
{
type: 'paragraph',
text: 'При установке и при повторной настройке (--configure-domains) можно указать свой домен для каждого компонента. Текущие значения подставляются по умолчанию — достаточно изменить только нужные.'
},
{
type: 'table',
headers: ['Сервис', 'Параметр', 'Пример', 'Nginx → порт'],
rows: [
['API / Swagger', '--api-domain', 'api.id.example.ru', '127.0.0.1:3000'],
['Frontend (UI)', '--frontend-domain', 'id.example.ru', '127.0.0.1:3002'],
['Документация', '--docs-domain', 'docs.example.ru', '127.0.0.1:3003'],
['WebSocket', '--ws-domain', 'пусто = API + /ws', '127.0.0.1:8085'],
['MinIO S3', '--minio-domain', 'опционально', '127.0.0.1:9000'],
['MinIO Console', '--minio-console-domain', 'опционально', '127.0.0.1:9001']
]
},
{
type: 'callout',
variant: 'info',
title: 'WebSocket',
text: 'Если --ws-domain не указан, WebSocket проксируется на том же домене, что и API, по пути /ws (wss://api.example.ru/ws). Для отдельного домена укажите --ws-domain ws.example.ru.'
}
]
},
{
id: 'install-menu',
title: '5. Меню управления (повторный запуск)',
blocks: [
{
type: 'paragraph',
text: 'Если файл .env уже существует, повторный вызов ./install.sh открывает интерактивное меню — как у панелей с автоматическим reverse proxy.'
},
{
type: 'table',
headers: ['Пункт', 'Действие'],
rows: [
['1', 'Полная установка / переустановка стека'],
['2', 'Настроить или изменить домены, Nginx и SSL'],
['3', 'Обновить SSL-сертификаты (Let\'s Encrypt)'],
['4', 'Перезапуск / пересборка контейнеров'],
['5', 'Статус сервисов и проверка Nginx'],
['6', 'Удалить конфиги Nginx IdP'],
['0', 'Выход']
]
},
{ {
type: 'code', type: 'code',
language: 'bash', language: 'bash',
code: `sudo apt update && sudo apt install -y ca-certificates curl gnupg title: 'Смена доменов после установки',
curl -fsSL https://get.docker.com | sudo sh code: `# Интерактивно
sudo usermod -aG docker $USER ./install.sh --configure-domains
docker compose version`
# Или через меню
./install.sh
# → пункт 2
# Пример: вынести Swagger на отдельный домен
./install.sh --configure-domains \\
--api-domain swagger.id.example.ru \\
--frontend-domain id.example.ru \\
--docs-domain docs.example.ru`
}
]
},
{
id: 'install-commands',
title: '6. Команды и флаги',
blocks: [
{
type: 'table',
headers: ['Команда / флаг', 'Описание'],
rows: [
['./install.sh', 'Интерактивное меню (если .env есть) или первая установка'],
['./install.sh --install', 'Полная установка'],
['./install.sh --configure-domains', 'Изменить домены и пересобрать frontend/docs'],
['./install.sh --renew-ssl', 'Обновить сертификаты Let\'s Encrypt'],
['./install.sh --restart', 'docker compose up -d --build'],
['./install.sh --status', 'Статус контейнеров и доменов'],
['./install.sh --intranet', 'Локальная сеть, HTTP по умолчанию'],
['./install.sh --ssl none', 'Только HTTP через Nginx'],
['./install.sh --ssl selfsigned', 'HTTPS самоподписанный'],
['./install.sh --offline', 'Не скачивать Docker из интернета'],
['./install.sh --nginx-mode docker', 'Nginx в контейнере (Windows)'],
['./install.sh --local', 'localhost:3000/3002/3003 без Nginx'],
['./install.sh --yes', 'Без интерактивных вопросов'],
['FORCE_ENV=1 ./install.sh', 'Пересоздать .env с новыми секретами']
]
},
{
type: 'code',
language: 'bash',
title: 'Локальная разработка без доменов',
code: `./install.sh --local
# или
./install.sh --install --local --yes`
}
]
},
{
id: 'install-automation',
title: '7. Что делает скрипт автоматически',
blocks: [
{
type: 'list',
items: [
'Устанавливает curl, git, openssl, jq (apt/dnf)',
'Устанавливает Docker и Docker Compose v2 (get.docker.com)',
'Добавляет пользователя в группу docker',
'Генерирует .env: пароли PostgreSQL, RabbitMQ, MinIO, JWT-секреты, DATA_ENCRYPTION_KEY',
'Создаёт docker-compose.override.yml — привязка портов к 127.0.0.1',
'Устанавливает Nginx и Certbot, настраивает UFW (80, 443)',
'Генерирует конфиги /etc/nginx/sites-available/lendry-id-*.conf',
'Получает SSL через certbot (webroot), включает certbot.timer',
'Запускает docker compose up -d --build и ждёт health сервисов',
'При смене доменов пересобирает frontend и docs с новыми NEXT_PUBLIC_* URL'
]
},
{
type: 'callout',
variant: 'warning',
title: 'Секреты',
text: 'Файл .env не коммитьте в git. Сохраните его в надёжном месте после первой установки. FORCE_ENV=1 генерирует новые секреты и сбрасывает старые.'
} }
] ]
}, },
{ {
id: 'clone-config', id: 'clone-config',
title: '3. Клонирование и переменные окружения', title: '8. Переменные .env (генерируются автоматически)',
blocks: [ blocks: [
{ {
type: 'paragraph', type: 'paragraph',
text: 'Создайте файл .env в корне проекта с production-секретами. Никогда не коммитьте его в git.' text: 'install.sh создаёт и обновляет .env на основе .env.example. При ручном редактировании не меняйте секреты без необходимости — для смены доменов используйте ./install.sh --configure-domains.'
}, },
{ {
type: 'code', type: 'code',
language: 'bash', language: 'bash',
title: '.env (пример)', title: 'Ключевые переменные .env',
code: `# Секреты JWT и шифрования — сгенерируйте: openssl rand -base64 48 code: `# Домены (без протокола)
JWT_ACCESS_SECRET=your-long-access-secret DOMAIN_API=api.id.example.ru
JWT_REFRESH_SECRET=your-long-refresh-secret DOMAIN_FRONTEND=id.example.ru
DATA_ENCRYPTION_KEY=your-32-byte-encryption-key-change-me DOMAIN_DOCS=docs.example.ru
DOMAIN_WS=
# PostgreSQL (можно оставить docker internal) # Публичные URL (скрипт подставляет https://)
POSTGRES_USER=lendry PUBLIC_API_URL=https://api.id.example.ru
POSTGRES_PASSWORD=strong-db-password PUBLIC_FRONTEND_URL=https://id.example.ru
POSTGRES_DB=lendry_id PUBLIC_DOCS_URL=https://docs.example.ru
PUBLIC_WS_URL=wss://api.id.example.ru/ws
# RabbitMQ USE_NGINX_SSL=true
RABBITMQ_DEFAULT_USER=lendry CERTBOT_EMAIL=admin@example.ru
RABBITMQ_DEFAULT_PASS=strong-rmq-password INSTALL_MODE=production
# MinIO # Секреты — генерируются install.sh
MINIO_ROOT_USER=minioadmin JWT_ACCESS_SECRET=...
MINIO_ROOT_PASSWORD=strong-minio-password` JWT_REFRESH_SECRET=...
}, DATA_ENCRYPTION_KEY=...
{ POSTGRES_PASSWORD=...
type: 'callout', RABBITMQ_DEFAULT_PASS=...
variant: 'warning', MINIO_ROOT_PASSWORD=...`
title: 'PUBLIC_API_URL',
text: 'В production задайте PUBLIC_API_URL=https://id.lendry.ru для sso-core и api-gateway, а также MINIO_PUBLIC_ENDPOINT=id.lendry.ru (или CDN-домен для MinIO).'
}
]
},
{
id: 'docker-compose-prod',
title: '4. Production docker-compose',
blocks: [
{
type: 'paragraph',
text: 'Для production рекомендуется не публиковать PostgreSQL, Redis и RabbitMQ наружу — оставьте только api-gateway (3000), frontend (3002) и docs (3003) за reverse proxy.'
},
{
type: 'code',
language: 'bash',
code: `export DOCKER_BUILDKIT=1
docker compose up -d --build
# Проверка health всех сервисов
docker compose ps
docker compose logs -f sso-core api-gateway`
}
]
},
{
id: 'nginx',
title: '5. Nginx + TLS (Let\'s Encrypt)',
blocks: [
{
type: 'code',
language: 'nginx',
title: '/etc/nginx/sites-available/id.lendry.ru',
code: `server {
listen 443 ssl http2;
server_name id.lendry.ru;
ssl_certificate /etc/letsencrypt/live/id.lendry.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/id.lendry.ru/privkey.pem;
# REST API + Swagger
location / {
proxy_pass http://127.0.0.1:3000;
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 $scheme;
}
# Frontend UI
location /app/ {
proxy_pass http://127.0.0.1:3002/;
proxy_set_header Host $host;
}
# WebSocket
location /ws {
proxy_pass http://127.0.0.1:8085;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
# Документация на docs.id.lendry.ru или /docs-proxy
server {
listen 443 ssl http2;
server_name docs.id.lendry.ru;
ssl_certificate /etc/letsencrypt/live/docs.id.lendry.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/docs.id.lendry.ru/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3003;
proxy_set_header Host $host;
}
}`
},
{
type: 'code',
language: 'bash',
code: `sudo certbot --nginx -d id.lendry.ru -d docs.id.lendry.ru
sudo nginx -t && sudo systemctl reload nginx`
} }
] ]
}, },
{ {
id: 'env-services', id: 'env-services',
title: '6. Переменные окружения сервисов', title: '9. Переменные окружения сервисов',
blocks: [ blocks: [
{ {
type: 'table', type: 'table',
@@ -318,17 +468,50 @@ sudo nginx -t && sudo systemctl reload nginx`
rows: [ rows: [
['sso-core', 'DATABASE_URL, JWT_*_SECRET, DATA_ENCRYPTION_KEY, REDIS_URL, RABBITMQ_URL, MINIO_*, LDAP_AUTH_URL, PUBLIC_API_URL'], ['sso-core', 'DATABASE_URL, JWT_*_SECRET, DATA_ENCRYPTION_KEY, REDIS_URL, RABBITMQ_URL, MINIO_*, LDAP_AUTH_URL, PUBLIC_API_URL'],
['api-gateway', 'SSO_CORE_GRPC_URL, JWT_ACCESS_SECRET, PUBLIC_API_URL, MINIO_*'], ['api-gateway', 'SSO_CORE_GRPC_URL, JWT_ACCESS_SECRET, PUBLIC_API_URL, MINIO_*'],
['frontend', 'NEXT_PUBLIC_API_URL, NEXT_PUBLIC_WS_URL'], ['frontend', 'NEXT_PUBLIC_API_URL, NEXT_PUBLIC_WS_URL (build args + runtime)'],
['docs', 'NEXT_PUBLIC_API_URL'], ['docs', 'NEXT_PUBLIC_API_URL, INTERNAL_API_URL'],
['media-ws', 'REDIS_ADDR, RABBITMQ_URL, JWT_ACCESS_SECRET'], ['media-ws', 'REDIS_ADDR, RABBITMQ_URL, JWT_ACCESS_SECRET'],
['ldap-auth', 'PORT=8086'] ['ldap-auth', 'PORT=8086']
] ]
} }
] ]
}, },
{
id: 'after-install',
title: '10. После установки',
blocks: [
{
type: 'table',
headers: ['Ресурс', 'URL (production)'],
rows: [
['API / Swagger', 'https://<DOMAIN_API>/api'],
['Frontend', 'https://<DOMAIN_FRONTEND>'],
['Документация', 'https://<DOMAIN_DOCS>/docs'],
['WebSocket', 'wss://<DOMAIN_API>/ws или wss://<DOMAIN_WS>/ws'],
['MinIO Console', 'только localhost:9001 (если не задан домен)'],
['RabbitMQ UI', 'только localhost:15672']
]
},
{
type: 'callout',
variant: 'tip',
title: 'Первый пользователь',
text: 'Первый зарегистрированный пользователь автоматически становится супер-администратором. Название проекта (PROJECT_NAME) и LDAP настраиваются в админке: /admin/settings.'
},
{
type: 'code',
language: 'bash',
title: 'Полезные команды',
code: `docker compose ps
docker compose logs -f api-gateway sso-core
./install.sh --status
./install.sh --renew-ssl`
}
]
},
{ {
id: 'migrations', id: 'migrations',
title: '7. Миграции и seed', title: '11. Миграции и seed',
blocks: [ blocks: [
{ {
type: 'code', type: 'code',
@@ -341,15 +524,15 @@ docker compose restart sso-core`
}, },
{ {
type: 'callout', type: 'callout',
variant: 'tip', variant: 'info',
title: 'SystemSetting', title: 'SystemSetting',
text: 'Название проекта (PROJECT_NAME), tagline и LDAP-параметры настраиваются в админке: /admin/settings. Frontend и docs подтягивают PROJECT_NAME через GET /settings/public.' text: 'Название проекта (PROJECT_NAME), tagline и LDAP-параметры настраиваются в админке. Frontend и docs подтягивают PROJECT_NAME через GET /settings/public.'
} }
] ]
}, },
{ {
id: 'backup', id: 'backup',
title: '8. Резервное копирование', title: '12. Резервное копирование',
blocks: [ blocks: [
{ {
type: 'list', type: 'list',
@@ -369,15 +552,16 @@ docker compose restart sso-core`
}, },
{ {
id: 'monitoring', id: 'monitoring',
title: '9. Мониторинг и обновления', title: '13. Мониторинг и обновления',
blocks: [ blocks: [
{ {
type: 'list', type: 'list',
items: [ items: [
'Health: GET /health на api-gateway и sso-core', 'Health: GET /health на api-gateway и sso-core',
'Логи: docker compose logs -f --tail=200', 'Логи: docker compose logs -f --tail=200',
'Обновление: git pull && docker compose up -d --build', 'Обновление кода: git pull && ./install.sh --restart',
'Zero-downtime: blue-green или rolling update через orchestrator (K8s / Swarm)' 'Смена доменов: ./install.sh --configure-domains',
'SSL: ./install.sh --renew-ssl или автоматически через certbot.timer'
] ]
} }
] ]

View File

@@ -245,6 +245,32 @@ services:
timeout: 5s timeout: 5s
retries: 10 retries: 10
nginx:
profiles: ["proxy"]
image: nginx:1.27-alpine
container_name: lendry-id-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/certs:/etc/nginx/certs:ro
depends_on:
api-gateway:
condition: service_healthy
frontend:
condition: service_healthy
docs:
condition: service_healthy
media-ws:
condition: service_started
healthcheck:
test: ["CMD", "nginx", "-t"]
interval: 30s
timeout: 5s
retries: 3
volumes: volumes:
postgres_data: postgres_data:
redis_data: redis_data:

File diff suppressed because it is too large Load Diff

0
nginx/certs/.gitkeep Normal file
View File

0
nginx/conf.d/.gitkeep Normal file
View File