/
kochkatech
/
CorpGame
Обзор
Документация
Войти
/
kochkatech
/
CorpGame
Код
Запросы
1
Задачи
Вики
Пакеты
0
Релизы
1
CI/CD
Аналитика
dev
scripts/ensure-env.ps1
127 строк
7 KB
kochkareal
feat(devops): свод к одному процессу Node на SQLite, портативная сборка без Docker; аудит безопасности
06 авг 2026, 15:06
06 авг 2026, 15:06
3266bcd
Код
Авторство
О чём код?
# Гарантирует, что .env существует и не содержит "заглушечных"/устаревших значений. # Бэкенд (main.ts, assertRequiredEnv) отказывается стартовать в проде с пустыми или # placeholder-значениями ADMIN_PASSWORD/JWT_SECRET — это важно для безопасности, но # обычный пользователь не должен вручную редактировать .env, чтобы просто запустить игру. # Поэтому start.ps1 вызывает этот скрипт перед стартом сервера: если секретов нет или # они всё ещё "change-me" — генерируются случайные, автоматически, без участия пользователя. # # DATABASE_URL пересчитывается и перезаписывается КАЖДЫЙ запуск, а не только при первом # создании .env: путь к data/corpgame.db абсолютный и зависит от того, куда именно # распаковали игру. Если папку перенесли (или это первый запуск на этой машине) — путь # в .env всё равно окажется правильным без ручного вмешательства. param( [Parameter(Mandatory = $true)] [string]$EnvPath, [Parameter(Mandatory = $true)] [string]$ExamplePath, [Parameter(Mandatory = $true)] [string]$DbPath ) $ErrorActionPreference = 'Stop' [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $OutputEncoding = [System.Text.Encoding]::UTF8 if (-not (Test-Path $EnvPath)) { if (-not (Test-Path $ExamplePath)) { Write-Host "[ошибка] $ExamplePath не найден — нечего скопировать в .env." exit 1 } Copy-Item $ExamplePath $EnvPath Write-Host "[инфо] Создан .env из .env.example" } $placeholders = @('change-me', 'change-me-too', 'dev-secret-change-me', '') # Пул из 36 символов (10 цифр + 26 латинских букв). Раньше здесь стоял # `Get-Random -Count $length` — для коллекции это выборка БЕЗ ВОЗВРАЩЕНИЯ (перестановка), # поэтому при $length > 36 она молча возвращала не $length символов, а весь 36-символьный # пул перемешанным (JWT_SECRET получался 36 символов вместо 40, и всегда одна и та же # перестановка одного и того же алфавита — существенно меньше энтропии, чем задумано). # ForEach-Object с независимым Get-Random на каждой итерации — выборка С ВОЗВРАЩЕНИЕМ, # как и должно быть для секрета, и всегда даёт ровно $length символов. $secretChars = (48..57) + (97..122) | ForEach-Object { [char]$_ } function New-RandomSecret([int]$length) { -join (1..$length | ForEach-Object { $secretChars | Get-Random }) } # ADMIN_PASSWORD ведущий вводит руками на телефоне/ноутбуке в разгар игры — короткий и без # визуально похожих символов (0/o/1/l), чтобы не приходилось перечитывать по буквам с экрана. $adminChars = '23456789abcdefghjkmnpqrstuvwxyz'.ToCharArray() function New-AdminPassword { -join (1..10 | ForEach-Object { $adminChars | Get-Random }) } $lines = @(Get-Content $EnvPath) # Наследие Postgres/Redis-эпохи — если .env создавали ещё до перехода на SQLite/один процесс, # в нём могут быть POSTGRES_*/REDIS_URL. Они больше ничего не настраивают, но, оставшись, # только сбивают с толку при чтении файла. HTTP_PORT — тоже наследие (заменён PORT, # который start.ps1 подбирает заново при каждом запуске, а не хранит в .env). $obsoleteKeys = 'POSTGRES_USER', 'POSTGRES_PASSWORD', 'POSTGRES_DB', 'REDIS_URL', 'HTTP_PORT' $before = $lines.Count $lines = @($lines | Where-Object { $line = $_; -not ($obsoleteKeys | Where-Object { $line -match "^\s*$_\s*=" }) }) $changed = $before -ne $lines.Count function Set-EnvValue([string[]]$currentLines, [string]$key, [string]$value) { $idx = -1 for ($i = 0; $i -lt $currentLines.Count; $i++) { if ($currentLines[$i] -match "^\s*$key\s*=") { $idx = $i; break } } $newLine = "$key=$value" if ($idx -ge 0) { $currentLines[$idx] = $newLine } else { $currentLines += $newLine } return $currentLines } function Get-EnvValue([string[]]$currentLines, [string]$key) { foreach ($line in $currentLines) { if ($line -match "^\s*$key\s*=(.*)$") { return $Matches[1].Trim() } } return $null } $generatedAdminPassword = $null foreach ($key in @('ADMIN_PASSWORD', 'JWT_SECRET')) { $currentValue = Get-EnvValue $lines $key if ([string]::IsNullOrEmpty($currentValue) -or $placeholders -contains $currentValue) { if ($key -eq 'ADMIN_PASSWORD') { $newValue = New-AdminPassword $generatedAdminPassword = $newValue } else { $newValue = New-RandomSecret 40 } $lines = Set-EnvValue $lines $key $newValue $changed = $true } } # file: URL всегда с прямыми слэшами — Prisma использует стандартный парсер URL, который # на Windows-путях с обратными слэшами трактует их как часть имени, а не разделитель. $dbUrl = 'file:' + ($DbPath -replace '\\', '/') if ((Get-EnvValue $lines 'DATABASE_URL') -ne $dbUrl) { $lines = Set-EnvValue $lines 'DATABASE_URL' $dbUrl $changed = $true } if ((Get-EnvValue $lines 'NODE_ENV') -ne 'production') { $lines = Set-EnvValue $lines 'NODE_ENV' 'production' $changed = $true } if ($changed) { Set-Content -Path $EnvPath -Value $lines -Encoding utf8 Write-Host "[инфо] .env обновлён" } if ($generatedAdminPassword) { Write-Host "" Write-Host "==================================================" Write-Host " Пароль администратора (сохранён в .env):" Write-Host " $generatedAdminPassword" Write-Host "==================================================" Write-Host "" } # Явный exit 0 обязателен: скрипт не вызывает ни одной внешней программы, поэтому без # него $LASTEXITCODE в вызывающем start.ps1 остаётся $null — а `$null -ne 0` в PowerShell # истинно, и start.ps1 принял бы успешное завершение за ошибку. exit 0