From c99362a3e5bd2d84b8bcd6ebf01e02aba6b9a320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Wed, 20 May 2026 16:08:08 +0300 Subject: [PATCH] =?UTF-8?q?chore(demo):=20=D1=81=D0=BA=D1=80=D0=B8=D0=BF?= =?UTF-8?q?=D1=82=20=D1=80=D0=B0=D0=B7=D0=B1=D0=B8=D0=B2=D0=BA=D0=B8=205?= =?UTF-8?q?=20=D0=B4=D0=B5=D0=BC=D0=BE-=D1=83=D1=87=D1=91=D1=82=D0=BE?= =?UTF-8?q?=D0=BA=20=D0=BD=D0=B0=205=20=D0=B8=D0=B7=D0=BE=D0=BB=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D1=82=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=D0=BD=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Каждый логин (admin/manager1-4) → своя компания/тенант. Идемпотентный: firstOrCreate + reassign tenant_id. Запуск: php artisan tinker storage/_demo_split_tenants.php Co-Authored-By: Claude Sonnet 4.6 --- app/storage/_demo_split_tenants.php | 117 ++++++++++++++++++++++++++++ docs/observer/STATUS.md | 6 +- 2 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 app/storage/_demo_split_tenants.php diff --git a/app/storage/_demo_split_tenants.php b/app/storage/_demo_split_tenants.php new file mode 100644 index 00000000..f1ffd784 --- /dev/null +++ b/app/storage/_demo_split_tenants.php @@ -0,0 +1,117 @@ + 'admin@demo.local', + 'tenant_subdomain' => 'demo', // оставляем существующий тенант + 'org_name' => null, // null = взять из существующего + 'create_new_tenant' => false, + ], + [ + 'email' => 'manager1@demo.local', + 'tenant_subdomain' => 'ivan-demo', + 'org_name' => 'Компания Ивана', + 'create_new_tenant' => true, + ], + [ + 'email' => 'manager2@demo.local', + 'tenant_subdomain' => 'anna-demo', + 'org_name' => 'Компания Анны', + 'create_new_tenant' => true, + ], + [ + 'email' => 'manager3@demo.local', + 'tenant_subdomain' => 'petr-demo', + 'org_name' => 'Компания Петра', + 'create_new_tenant' => true, + ], + [ + 'email' => 'manager4@demo.local', + 'tenant_subdomain' => 'mariya-demo', + 'org_name' => 'Компания Марии', + 'create_new_tenant' => true, + ], +]; + +// ------------------------------------------------------------------- +// 3. Создаём тенанты и переназначаем пользователей +// ------------------------------------------------------------------- +foreach ($accounts as $a) { + $user = User::query()->where('email', $a['email'])->firstOrFail(); + + if (! $a['create_new_tenant']) { + // Demo Admin остаётся в tenant "demo" + $tenant = Tenant::query()->where('subdomain', $a['tenant_subdomain'])->firstOrFail(); + echo "SKIP {$user->email} → тенант «{$tenant->organization_name}» (id={$tenant->id}) — без изменений\n"; + + continue; + } + + // Создаём новый тенант, если ещё не существует + $tenant = Tenant::query()->firstOrCreate( + ['subdomain' => $a['tenant_subdomain']], + [ + 'organization_name' => $a['org_name'], + 'contact_email' => $user->email, + 'webhook_token' => Str::random(64), + 'timezone' => 'Europe/Moscow', + 'locale' => 'ru', + 'is_trial' => true, + 'api_key_limit' => 5, + ] + ); + + // Переназначаем пользователя в новый тенант + $user->tenant_id = $tenant->id; + $user->save(); + + echo "OK {$user->email} → новый тенант «{$tenant->organization_name}» (id={$tenant->id}, subdomain={$tenant->subdomain})\n"; +} + +// ------------------------------------------------------------------- +// 4. Итоговый отчёт +// ------------------------------------------------------------------- +echo "\n=== ИТОГО: изоляция тенантов ===\n"; +$tenants = Tenant::query() + ->whereIn('subdomain', ['demo', 'ivan-demo', 'anna-demo', 'petr-demo', 'mariya-demo']) + ->orderBy('id') + ->get(); + +foreach ($tenants as $t) { + $users = User::query()->where('tenant_id', $t->id)->pluck('email')->implode(', '); + $projects = Project::query()->where('tenant_id', $t->id)->count(); + echo sprintf( + " Тенант %-12s (id=%-2d) — пользователи: %-40s | проектов: %d\n", + $t->subdomain, + $t->id, + $users ?: '(нет)', + $projects + ); +} + +echo "\nГотово. Каждый логин теперь в отдельной компании.\n"; +echo "Пароль для всех: password\n"; diff --git a/docs/observer/STATUS.md b/docs/observer/STATUS.md index 56d46d08..a1a0aabc 100644 --- a/docs/observer/STATUS.md +++ b/docs/observer/STATUS.md @@ -1,6 +1,6 @@ # Brain Status (auto-generated) -Last updated: 2026-05-20T11:34:26.960Z +Last updated: 2026-05-20T13:07:59.792Z | Контролёр | Состояние | Детали | |---|---|---| @@ -8,11 +8,11 @@ Last updated: 2026-05-20T11:34:26.960Z | C2 Cross-ref consistency | ✅ | [cross-ref-checker] OK — 0 drift in 4 files | | C3 Observer-of-observer | ✅ | [observer-of-observer] OK — last read 0 week(s) ago | | C4 Сигнальный статус | ✅ | This file (self-reference) | -| C5 Observer-coverage | ✅ | 16 episode(s) this month · Stop-hook + post-commit OK | +| C5 Observer-coverage | ✅ | 29 episode(s) this month · Stop-hook + post-commit OK | ## Метрики (информационные, не алерты) -- Observer evidence: 16 episodes this month, 0 observer_error markers, 0 PII matches before filter +- Observer evidence: 29 episodes this month, 0 observer_error markers, 26 PII matches before filter - Legacy v1 episodes (not in factor analysis): 5 - Last /brain-retro: 1 day(s) ago - Использование узлов: см. `/brain-retro` (раз в спринт). **Неиспользованные узлы — не проблема** (capability-readiness; см. memory `feedback_brain_unused_tools_not_problem` — outside-repo memory store).