Compare commits
2 Commits
3aba59ef84
...
34c1d8446a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34c1d8446a | ||
|
|
ecd95c5698 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,6 +8,7 @@ dist
|
||||
!.env.example
|
||||
docker-compose.override.yml
|
||||
.idp-install.json
|
||||
.idp-infra.secrets
|
||||
nginx/certs/*
|
||||
!nginx/certs/.gitkeep
|
||||
nginx/conf.d/*.conf
|
||||
|
||||
@@ -358,7 +358,6 @@ export default function LoginPage() {
|
||||
) : null}
|
||||
|
||||
<ProjectTagline className="mt-9 text-center text-sm font-semibold" />
|
||||
<p className="mt-2 text-center text-sm font-bold">Узнать больше</p>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
|
||||
@@ -69,10 +69,6 @@ export default function RegisterPage() {
|
||||
{isSubmitting ? 'Отправляем...' : 'Получить код'}
|
||||
</Button>
|
||||
</form>
|
||||
<div className="mt-5 flex items-start gap-3 rounded-[22px] bg-[#2a2c36] p-4 text-sm">
|
||||
<ShieldCheck className="mt-0.5 h-5 w-5 text-[#8ec5ff]" />
|
||||
<p>Первый зарегистрированный пользователь автоматически станет суперадминистратором.</p>
|
||||
</div>
|
||||
<Button asChild variant="ghost" className="mt-5 w-full text-white hover:bg-[#2a2c36]">
|
||||
<Link href="/auth/login">У меня уже есть ID</Link>
|
||||
</Button>
|
||||
|
||||
@@ -48,9 +48,7 @@ export function Sidebar({ active }: { active: string }) {
|
||||
<LogOut className="h-3.5 w-3.5" />
|
||||
Выйти
|
||||
</button>
|
||||
<p>Русский</p>
|
||||
<p>Справка</p>
|
||||
<p>© 2026 Lendry</p>
|
||||
<p>© 2026</p>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
|
||||
@@ -41,6 +41,9 @@ RUN npm --workspace @lendry/sso-core run prisma:generate \
|
||||
&& npm --workspace @lendry/sso-core run proto:generate \
|
||||
&& npm --workspace @lendry/sso-core run build
|
||||
|
||||
COPY apps/sso-core/docker-entrypoint.sh /app/apps/sso-core/docker-entrypoint.sh
|
||||
RUN chmod +x /app/apps/sso-core/docker-entrypoint.sh
|
||||
|
||||
EXPOSE 3001 50051
|
||||
|
||||
CMD ["sh", "-c", "npm --workspace @lendry/sso-core run prisma:push && node apps/sso-core/dist/main.js"]
|
||||
CMD ["/app/apps/sso-core/docker-entrypoint.sh"]
|
||||
|
||||
23
apps/sso-core/docker-entrypoint.sh
Normal file
23
apps/sso-core/docker-entrypoint.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "[sso-core] Применение схемы БД (Prisma)..."
|
||||
attempt=0
|
||||
max_attempts=30
|
||||
|
||||
while [ "$attempt" -lt "$max_attempts" ]; do
|
||||
attempt=$((attempt + 1))
|
||||
if npm --workspace @lendry/sso-core run prisma:push; then
|
||||
echo "[sso-core] Схема БД синхронизирована"
|
||||
break
|
||||
fi
|
||||
if [ "$attempt" -ge "$max_attempts" ]; then
|
||||
echo "[sso-core] ОШИБКА: не удалось подключиться к PostgreSQL после ${max_attempts} попыток." >&2
|
||||
echo "[sso-core] Проверьте POSTGRES_* в .env и пароль существующего volume postgres_data." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "[sso-core] PostgreSQL недоступен или неверный пароль, повтор ${attempt}/${max_attempts}..."
|
||||
sleep 3
|
||||
done
|
||||
|
||||
exec node apps/sso-core/dist/main.js
|
||||
@@ -110,10 +110,11 @@ services:
|
||||
ldap-auth:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "node -e \"fetch('http://localhost:3001/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""]
|
||||
test: ["CMD-SHELL", "node -e \"fetch('http://127.0.0.1:3001/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
timeout: 10s
|
||||
retries: 12
|
||||
start_period: 120s
|
||||
|
||||
api-gateway:
|
||||
pull_policy: build
|
||||
@@ -143,10 +144,11 @@ services:
|
||||
minio:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "node -e \"fetch('http://localhost:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""]
|
||||
test: ["CMD-SHELL", "node -e \"fetch('http://127.0.0.1:3000/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
timeout: 10s
|
||||
retries: 12
|
||||
start_period: 60s
|
||||
|
||||
media-ws:
|
||||
pull_policy: build
|
||||
|
||||
139
install.sh
139
install.sh
@@ -22,6 +22,7 @@ LOCAL_CONF_DIR="${ROOT_DIR}/nginx/conf.d"
|
||||
LOCAL_CERTS_DIR="${ROOT_DIR}/nginx/certs"
|
||||
COMPOSE_OVERRIDE="${ROOT_DIR}/docker-compose.override.yml"
|
||||
STATE_FILE="${ROOT_DIR}/.idp-install.json"
|
||||
INFRA_SECRETS_FILE="${ROOT_DIR}/.idp-infra.secrets"
|
||||
|
||||
NON_INTERACTIVE=0
|
||||
FORCE_ENV="${FORCE_ENV:-0}"
|
||||
@@ -62,6 +63,7 @@ ${BOLD}Lendry ID v${SCRIPT_VERSION}${NC} — установка и управл
|
||||
./install.sh --renew-ssl Обновить сертификаты Let's Encrypt
|
||||
./install.sh --restart Перезапуск контейнеров
|
||||
./install.sh --status Статус сервисов
|
||||
./install.sh --reset-db Сброс PostgreSQL (новый пароль из .env, данные удалятся)
|
||||
./install.sh --remove-nginx Удалить конфиги Nginx IdP
|
||||
|
||||
${BOLD}Режим локальной сети (без интернета / AD-домены .local .lpr):${NC}
|
||||
@@ -85,7 +87,7 @@ ${BOLD}Прочие опции:${NC}
|
||||
-h, --help Справка
|
||||
|
||||
${BOLD}Переменные окружения:${NC}
|
||||
FORCE_ENV=1 Пересоздать .env (секреты будут новые)
|
||||
FORCE_ENV=1 Новые JWT-секреты (пароли БД/MinIO/RabbitMQ сохраняются если volume уже есть)
|
||||
OFFLINE=1 То же, что --offline
|
||||
|
||||
${BOLD}Примеры:${NC}
|
||||
@@ -117,7 +119,7 @@ parse_args() {
|
||||
--renew-ssl) ACTION="renew-ssl"; shift ;;
|
||||
--restart) ACTION="restart"; shift ;;
|
||||
--status) ACTION="status"; shift ;;
|
||||
--remove-nginx) ACTION="remove-nginx"; shift ;;
|
||||
--reset-db) ACTION="reset-db"; shift ;;
|
||||
--local) INSTALL_LOCAL=1; shift ;;
|
||||
--intranet) INSTALL_INTRANET=1; shift ;;
|
||||
--offline) OFFLINE=1; shift ;;
|
||||
@@ -637,17 +639,139 @@ write_env_secrets_if_missing() {
|
||||
cp .env.example .env 2>/dev/null || touch .env
|
||||
fi
|
||||
|
||||
if [[ "$FORCE_ENV" == "1" ]] || [[ "$(env_get POSTGRES_PASSWORD change-me-postgres)" == "change-me-postgres" ]]; then
|
||||
load_infra_secrets_into_env
|
||||
|
||||
if docker_volume_exists postgres_data; then
|
||||
if [[ -n "$(env_get POSTGRES_PASSWORD "")" ]]; then
|
||||
warn "Volume PostgreSQL уже существует — POSTGRES_PASSWORD в .env не меняем"
|
||||
else
|
||||
fail "Volume PostgreSQL существует, но POSTGRES_PASSWORD не найден. Восстановите .env или выполните: ./install.sh --reset-db"
|
||||
fi
|
||||
pg_pass="$(env_get POSTGRES_PASSWORD "$pg_pass")"
|
||||
fi
|
||||
|
||||
if docker_volume_exists rabbitmq_data; then
|
||||
warn "Volume RabbitMQ уже существует — RABBITMQ_DEFAULT_PASS не меняем"
|
||||
rmq_pass="$(env_get RABBITMQ_DEFAULT_PASS "$rmq_pass")"
|
||||
fi
|
||||
|
||||
if docker_volume_exists minio_data; then
|
||||
warn "Volume MinIO уже существует — MINIO_ROOT_PASSWORD не меняем"
|
||||
minio_pass="$(env_get MINIO_ROOT_PASSWORD "$minio_pass")"
|
||||
fi
|
||||
|
||||
local need_secrets=0
|
||||
if [[ "$FORCE_ENV" == "1" ]]; then
|
||||
need_secrets=1
|
||||
elif [[ "$(env_get POSTGRES_PASSWORD change-me-postgres)" == "change-me-postgres" ]]; then
|
||||
need_secrets=1
|
||||
elif [[ "$(env_get JWT_ACCESS_SECRET change-me-access-secret)" == "change-me-access-secret" ]]; then
|
||||
need_secrets=1
|
||||
fi
|
||||
|
||||
if [[ "$need_secrets" -eq 1 ]]; then
|
||||
env_set POSTGRES_PASSWORD "$pg_pass"
|
||||
env_set RABBITMQ_DEFAULT_PASS "$rmq_pass"
|
||||
env_set MINIO_ROOT_PASSWORD "$minio_pass"
|
||||
env_set JWT_ACCESS_SECRET "$jwt_access"
|
||||
env_set JWT_REFRESH_SECRET "$jwt_refresh"
|
||||
env_set DATA_ENCRYPTION_KEY "$data_key"
|
||||
ok "Секреты сгенерированы"
|
||||
save_infra_secrets
|
||||
ok "Секреты сгенерированы / синхронизированы"
|
||||
fi
|
||||
}
|
||||
|
||||
compose_project_name() {
|
||||
load_env
|
||||
echo "${COMPOSE_PROJECT_NAME:-lendry-id}"
|
||||
}
|
||||
|
||||
docker_volume_name() {
|
||||
echo "$(compose_project_name)_$1"
|
||||
}
|
||||
|
||||
docker_volume_exists() {
|
||||
docker volume inspect "$(docker_volume_name "$1")" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
save_infra_secrets() {
|
||||
load_env
|
||||
cat >"$INFRA_SECRETS_FILE" <<EOF
|
||||
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-}
|
||||
RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS:-}
|
||||
MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-}
|
||||
EOF
|
||||
chmod 600 "$INFRA_SECRETS_FILE" 2>/dev/null || true
|
||||
}
|
||||
|
||||
load_infra_secrets_into_env() {
|
||||
[[ -f "$INFRA_SECRETS_FILE" ]] || return 0
|
||||
# shellcheck disable=SC1091
|
||||
set -a && source "$INFRA_SECRETS_FILE" && set +a
|
||||
[[ -n "${POSTGRES_PASSWORD:-}" ]] && env_set POSTGRES_PASSWORD "$POSTGRES_PASSWORD"
|
||||
[[ -n "${RABBITMQ_DEFAULT_PASS:-}" ]] && env_set RABBITMQ_DEFAULT_PASS "$RABBITMQ_DEFAULT_PASS"
|
||||
[[ -n "${MINIO_ROOT_PASSWORD:-}" ]] && env_set MINIO_ROOT_PASSWORD "$MINIO_ROOT_PASSWORD"
|
||||
}
|
||||
|
||||
verify_postgres_auth() {
|
||||
load_env
|
||||
load_infra_secrets_into_env
|
||||
[[ -f .env ]] || return 0
|
||||
docker_cmd compose ps postgres 2>/dev/null | grep -q postgres || return 0
|
||||
|
||||
local user pass db
|
||||
user="$(env_get POSTGRES_USER lendry)"
|
||||
pass="$(env_get POSTGRES_PASSWORD "")"
|
||||
db="$(env_get POSTGRES_DB lendry_id)"
|
||||
[[ -n "$pass" ]] || return 0
|
||||
|
||||
if docker_cmd compose exec -T -e PGPASSWORD="$pass" postgres \
|
||||
psql -U "$user" -d "$db" -c 'SELECT 1' >/dev/null 2>&1; then
|
||||
ok "PostgreSQL: пароль из .env подходит"
|
||||
save_infra_secrets
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
fail "PostgreSQL: пароль в .env не совпадает с данными в volume (ошибка P1000).
|
||||
|
||||
Это происходит после FORCE_ENV=1 или смены POSTGRES_PASSWORD при существующей БД.
|
||||
|
||||
Решения:
|
||||
1) Сбросить БД (удалит всех пользователей и данные):
|
||||
./install.sh --reset-db --yes && ./install.sh --restart
|
||||
|
||||
2) Вернуть старый POSTGRES_PASSWORD в .env (если сохранили)
|
||||
|
||||
3) Если есть файл ${INFRA_SECRETS_FILE} — скопируйте пароль оттуда в .env"
|
||||
}
|
||||
|
||||
action_reset_db() {
|
||||
load_env
|
||||
local vol
|
||||
vol="$(docker_volume_name postgres_data)"
|
||||
|
||||
if [[ "$NON_INTERACTIVE" -ne 1 ]]; then
|
||||
echo ""
|
||||
warn "Будет удалён volume ${vol} — все пользователи, сессии и настройки в PostgreSQL пропадут."
|
||||
read -r -p "Продолжить? [y/N]: " confirm
|
||||
[[ "$confirm" =~ ^[Yy]$ ]] || fail "Отменено"
|
||||
fi
|
||||
|
||||
log "Остановка сервисов..."
|
||||
docker_cmd compose --env-file .env --profile proxy down 2>/dev/null || docker_cmd compose --env-file .env down 2>/dev/null || true
|
||||
|
||||
log "Удаление ${vol}..."
|
||||
if docker volume inspect "$vol" >/dev/null 2>&1; then
|
||||
docker volume rm "$vol" || fail "Не удалось удалить ${vol}"
|
||||
else
|
||||
warn "Volume ${vol} не найден — возможно, уже удалён"
|
||||
fi
|
||||
|
||||
ok "PostgreSQL volume удалён. PostgreSQL создастся заново с паролем из .env."
|
||||
save_infra_secrets
|
||||
}
|
||||
|
||||
persist_domain_env() {
|
||||
write_env_secrets_if_missing
|
||||
|
||||
@@ -1319,6 +1443,7 @@ deploy_stack() {
|
||||
|
||||
write_compose_override
|
||||
load_env
|
||||
verify_postgres_auth
|
||||
|
||||
local compose_cmd=(compose --env-file .env)
|
||||
if [[ -n "$(env_get DOMAIN_API "")" && "$(env_get INSTALL_MODE local)" != "local" ]]; then
|
||||
@@ -1328,6 +1453,7 @@ deploy_stack() {
|
||||
log "Сборка и запуск контейнеров (10–20 минут при первом запуске)..."
|
||||
docker_cmd "${compose_cmd[@]}" up -d --build
|
||||
|
||||
wait_for_service "sso-core" 120
|
||||
wait_for_service "api-gateway" 90
|
||||
wait_for_service "frontend" 60
|
||||
wait_for_service "docs" 60
|
||||
@@ -1494,11 +1620,12 @@ show_menu() {
|
||||
echo " 4) Перезапуск / пересборка контейнеров"
|
||||
echo " 5) Статус сервисов"
|
||||
echo " 6) Удалить конфиги Nginx IdP"
|
||||
echo " 7) Сброс PostgreSQL (если ошибка P1000 / пароль БД)"
|
||||
echo " 0) Выход"
|
||||
echo ""
|
||||
|
||||
local choice
|
||||
read -r -p "Выберите пункт [0-6]: " choice
|
||||
read -r -p "Выберите пункт [0-7]: " choice
|
||||
|
||||
case "$choice" in
|
||||
1) NON_INTERACTIVE=0; action_full_install ;;
|
||||
@@ -1507,6 +1634,7 @@ show_menu() {
|
||||
4) action_restart ;;
|
||||
5) show_status ;;
|
||||
6) remove_nginx_configs; ok "Готово" ;;
|
||||
7) action_reset_db; action_restart ;;
|
||||
0) exit 0 ;;
|
||||
*) warn "Неверный пункт" ;;
|
||||
esac
|
||||
@@ -1527,6 +1655,7 @@ main() {
|
||||
renew-ssl) action_renew_ssl ;;
|
||||
restart) action_restart ;;
|
||||
status) show_status ;;
|
||||
reset-db) action_reset_db ;;
|
||||
remove-nginx) remove_nginx_configs ;;
|
||||
"")
|
||||
if [[ -f .env ]] && [[ "$NON_INTERACTIVE" -ne 1 ]]; then
|
||||
|
||||
Reference in New Issue
Block a user