6.1 KiB
Перенос арендатора Staff
scripts/rehome-tenant.mjs переносит все строки из одного tenant ID в другой в схеме public. Скрипт рассчитан на остановленное приложение и запускается только привилегированной ролью БД, способной обходить RLS и изменять таблицы. Строки пароля или подключения в отчёты не выводятся.
Сначала на восстановленной копии базы выполните dry-run:
$env:DATABASE_REHOME_URL = 'postgresql://…'
node scripts/rehome-tenant.mjs --dry-run --source <ULID> --target <ULID>
Dry-run открывает транзакцию READ ONLY и выводит только идентификаторы переноса и количество строк по таблицам. Он не блокирует записи; поэтому результат является снимком для планирования, а не гарантией актуальности на момент apply. На копии и в согласованное окно с остановленным приложением повторите dry-run непосредственно перед переносом.
Обязательное подтверждение восстановления
Apply требует --backup-evidence <путь> и читает JSON отчёт. Само имя файла и существование файла не считаются доказательством. JSON должен содержать сведения о зашифрованном off-host pg_dump -Fc и успешном restore drill именно этого backup:
{
"backup": {
"format": "pg_dump -Fc",
"encrypted": true,
"offHost": true,
"sha256": "<64 hex characters>",
"createdAt": "2026-09-26T08:00:00Z"
},
"restoreDrill": {
"status": "passed",
"backupSha256": "<same 64 hex characters>",
"restoredAt": "2026-09-26T08:30:00Z",
"verifiedAt": "2026-09-26T08:45:00Z",
"isolatedDatabase": "krona_staff_restore_check"
}
}
Этот файл удостоверяет результаты, предоставленные оператором; скрипт не проверяет доступ к хранилищу, шифрование или пригодность backup самостоятельно. Сохраните исходный backup, хеш и протокол restore drill вне сервера приложения.
Apply
Проверьте dry-run и backup evidence, остановите приложение, затем запускайте в согласованное окно. --actor — ULID уполномоченного пользователя Core, не пароль или секрет:
node scripts/rehome-tenant.mjs --apply `
--source <ULID> --target <ULID> --actor <CORE_USER_ULID> `
--backup-evidence .\evidence\restore-drill.json
Apply использует одну транзакцию, сериализуемый уровень изоляции, advisory lock и детерминированные ACCESS EXCLUSIVE locks всех таблиц public.staff_*. Скрипт отказывает, если целевой tenant уже содержит строки, таблица лишена tenant_id или FORCE RLS, обнаружен неизвестный пользовательский trigger либо внешний FK, или роль БД не может безопасно выполнить перенос. Существующие FK сохраняются с их определениями и состоянием validation; если проверка после пересоздания не совпала, транзакция откатывается.
Отчёт содержит количества перенесённых строк и суммы staff_payroll_ledger.amount_minor отдельно по валюте. В staff_audit_log перенос сам создаёт ровно одну новую запись tenant.rehomed под целевым tenant. Поэтому число строк staff_audit_log после commit равно исходному числу строк источника плюс одна; по всем остальным таблицам количество равно исходному. Запись аудита включает from/to, исходные количества, суммы реестра, Core actor, correlation ID и SHA-256/время backup и проверки restore.
При ошибке stdout/stderr не содержит URL подключения, значений строк или текста серверной ошибки: транзакция откатывается, а в stderr печатается только безопасное сообщение и, если есть, пятисимвольный PostgreSQL SQLSTATE. После успешного commit отдельно проверьте SELECT DISTINCT tenant_id по каждой staff таблице, количества строк, сумму реестра по валюте и наличие записи tenant.rehomed.
Интеграционный тест
Тест tests/integration/rehome-tenant.test.ts предназначен только для отдельной пустой PostgreSQL 17 базы, имя которой заканчивается на _rehome_test. Он требует DATABASE_REHOME_TEST_URL и явное DATABASE_REHOME_TEST_CONFIRM=I_ACKNOWLEDGE_DISPOSABLE_PG17_DATABASE, создаёт в ней роль приложения без пароля при необходимости и применяет миграции на чистой схеме. В конце тест очищает таблицы public.staff_*; проверка пустоты выполняется до вставки фикстур. Не указывайте production, pilot или общую базу.