diff --git a/app/config/database.php b/app/config/database.php index c00d329d..0bef9cb2 100644 --- a/app/config/database.php +++ b/app/config/database.php @@ -144,6 +144,22 @@ return [ ] ), + // Путь А (27.06.2026): dedicated PG connection для SaaS-admin зоны под + // ролью crm_admin_user (политика srv_bypass = видит все тенанты + GRANT на + // админ-таблицы). Используется через middleware UseAdminConnection (alias + // admin-db) на группе saas-admin: AdminTenantsController / AdminBillingController + // ходят под default → получают cross-tenant доступ. На dev fallback на + // DB_USERNAME/DB_PASSWORD (postgres superuser). На prod ОБЯЗАТЕЛЬНО задать + // DB_ADMIN_USERNAME=crm_admin_user + DB_ADMIN_PASSWORD. + // См. docs/superpowers/specs/2026-06-27-admin-db-connection-path-a-design.md + 'pgsql_admin' => array_merge( + $pgsqlConnection, + [ + 'username' => env('DB_ADMIN_USERNAME', env('DB_USERNAME', 'root')), + 'password' => env('DB_ADMIN_PASSWORD', env('DB_PASSWORD', '')), + ] + ), + 'sqlsrv' => [ 'driver' => 'sqlsrv', 'url' => env('DB_URL'), diff --git a/app/tests/Unit/Database/PgsqlAdminConnectionTest.php b/app/tests/Unit/Database/PgsqlAdminConnectionTest.php new file mode 100644 index 00000000..f99d1765 --- /dev/null +++ b/app/tests/Unit/Database/PgsqlAdminConnectionTest.php @@ -0,0 +1,26 @@ + $admin */ + $admin = config('database.connections.pgsql_admin'); + /** @var array $base */ + $base = config('database.connections.pgsql'); + + expect($admin)->toBeArray(); + expect($admin['driver'])->toBe('pgsql'); + // делит базовый pgsql-конфиг (host/database/sslmode), отличается только ролью + expect($admin['host'])->toBe($base['host']); + expect($admin['database'])->toBe($base['database']); + expect($admin['sslmode'])->toBe($base['sslmode']); + // на dev/test (DB_ADMIN_USERNAME/DB_ADMIN_PASSWORD не заданы) роль падает на + // default DB_USERNAME/DB_PASSWORD — поэтому совпадает с базовым pgsql. + // На prod env-ключи DB_ADMIN_* указывают на crm_admin_user (отличается). + expect($admin['username'])->toBe($base['username']); + expect($admin['password'])->toBe($base['password']); +}); diff --git a/docs/observer/STATUS.md b/docs/observer/STATUS.md index 5751814f..f93faeef 100644 --- a/docs/observer/STATUS.md +++ b/docs/observer/STATUS.md @@ -1,6 +1,6 @@ # Brain Status (auto-generated) -Last updated: 2026-06-27T03:14:09.920Z +Last updated: 2026-06-27T03:37:19.157Z | Контролёр | Состояние | Детали | |---|---|---| @@ -39,6 +39,7 @@ Last updated: 2026-06-27T03:14:09.920Z | Время | Действие | Причина | |---|---|---| +| 2026-06-27T03:14:37.381Z | bash:find "c:\моя\проекты\claude-brain" -name "*sessionstart*" -type f 2>/dev/null | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:find "c:\моя\проекты\c | | 2026-06-27T03:10:25.034Z | bash:grep -n "globals\\|include\\|exclude" vitest.config.tools.mjs 2>/dev/null \|\| echo "NO tools config at root" | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:grep -n "globals\\|inc | | 2026-06-27T03:09:50.695Z | bash:git commit -F "C:/Users/ADMINI~1/AppData/Local/Temp/2/claude/c--------------claude-brain/2c98d47c-4f4d-48cc-98a8-2e | [verify-gate] расписка отклонена (stale-fingerprint) — пере-прогоните verify (staged-diff изменился / битая подпись) | | 2026-06-27T03:07:30.844Z | bash:git commit -F "C:/Users/ADMINI~1/AppData/Local/Temp/2/claude/c--------------claude-brain/2c98d47c-4f4d-48cc-98a8-2e | [verify-gate] расписка отклонена (stale-fingerprint) — пере-прогоните verify (staged-diff изменился / битая подпись) | @@ -48,7 +49,6 @@ Last updated: 2026-06-27T03:14:09.920Z | 2026-06-26T16:51:07.498Z | bash:cd "c:/моя/проекты/claude-brain" && git add docs/superpowers/router-mentor-wall-GUIDE.md && git commit -F - <<'EOF' | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:cd "c:/моя/проекты/cla | | 2026-06-26T14:42:17.264Z | bash:ls -la ~/.claude/runtime/secretary-mode-*.json 2>/dev/null; echo "--- содержимое флагов ---"; for f in ~/.claude/ru | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:ls -la ~/.claude/runti | | 2026-06-26T12:58:47.855Z | bash:node -e "JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')) && console.log('valid JSON, bytes:', requir | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:node -e "JSON.parse(re | -| 2026-06-26T12:55:30.873Z | bash:f="C:/Users/Administrator/.claude/projects/c--------------claude-brain/memory/MEMORY.md"; echo "lines: $(wc -l < "$ | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:f="C:/Users/Administra | ## Метрики (информационные, не алерты) @@ -132,9 +132,9 @@ Episodes since last run: 542 / threshold: 10 | PID | Имя | CPU-время | Возраст | |---|---|---|---| -| 3440 | MsMpEng | 15.04ч | 0.0ч | -| 21928 | Code | 5.20ч | 0.0ч | -| 1212 | svchost | 3.83ч | 0.0ч | +| 3440 | MsMpEng | 15.11ч | NaNч | +| 21928 | Code | 5.32ч | 0.0ч | +| 1212 | svchost | 3.84ч | 1327899.6ч | ⚠️ Проверь, не «осиротевшие» ли это процессы от завершённых Claude-сессий.