HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/ai.naniguide.com/database/seeders/DemoSeeder.php
<?php

namespace Database\Seeders;

use Database\Seeders\Demo\DemoConfig;
use Database\Seeders\Demo\DemoWiper;
use Database\Seeders\Demo\Stage01_AdminsSeeder;
use Database\Seeders\Demo\Stage03_CustomersSeeder;
use Database\Seeders\Demo\Stage04_SubscriptionsSeeder;
use Database\Seeders\Demo\Stage05_AdminInfraSeeder;
use Database\Seeders\Demo\Stage06_CustomerInfraSeeder;
use Database\Seeders\Demo\Stage07_MailListsSeeder;
use Database\Seeders\Demo\Stage08_SubscribersSeeder;
use Database\Seeders\Demo\Stage09_TemplatesSeeder;
use Database\Seeders\Demo\Stage10_CampaignsSeeder;
use Database\Seeders\Demo\Stage11_TrackingLogsSeeder;
use Database\Seeders\Demo\Stage12_FormsSeeder;
use Database\Seeders\Demo\Stage14_AutomationsSeeder;
use Database\Seeders\Demo\Stage15_AbTestsSeeder;
use Database\Seeders\Demo\Stage16_FunnelsSeeder;
use Database\Seeders\Demo\Stage17_WebsitesSeeder;
use Database\Seeders\Demo\Stage18_NotificationsSeeder;
use Database\Seeders\Demo\Stage19_SettingsSeeder;
use Database\Seeders\Demo\Stage20_ManifestWriter;
use Database\Seeders\Demo\Stage21_CacheWarmer;
use Database\Seeders\Demo\Stage22_AdsSeeder;
use Database\Seeders\Demo\Stage23_CustomerAssetsSeeder;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;

/**
 * DemoSeeder — full reproducible demo dataset for the entire Acelle app.
 *
 * Usage:
 *   php artisan db:seed --class=DemoSeeder              # full wipe + rebuild
 *   php artisan db:seed --class=DemoSeeder --no-wipe    # additive (rare)
 *
 * Run order: wipe → DatabaseInit (languages, countries, admin_groups, plans,
 * sending server) → extras (roles, templates) → Stage01..22.
 *
 * Each stage class lives in database/seeders/Demo/Stage{NN}_*.php and
 * is responsible for one slice of the data. Sub-seeders share state via
 * static `$created` arrays so later stages can look up earlier results.
 *
 * Configuration: see Database\Seeders\Demo\DemoConfig.
 * Output:        docs/demo/DEMO_ACCOUNTS.json (manifest written by Stage20).
 * Documentation: docs/demo/DEMO.md (bot skill) + docs/demo/DEMO_USAGE.md (full guide)
 */
class DemoSeeder extends Seeder
{
    public function run(): void
    {
        // Fail fast if faker (dev dependency) is missing
        if (!class_exists(\Faker\Factory::class)) {
            echo "\n";
            echo "================================================================\n";
            echo " ERROR: fakerphp/faker is not installed!\n";
            echo " DemoSeeder requires dev dependencies.\n";
            echo " Run:  composer install\n";
            echo "================================================================\n\n";
            throw new \RuntimeException('fakerphp/faker is not installed. Run: composer install');
        }

        $startedAt = microtime(true);
        // Symfony console rejects unknown flags — use ENV vars instead of CLI flags.
        // Set on the artisan call: `DEMO_NO_WIPE=1 php artisan db:seed ...` etc.
        $noWipe    = !empty(getenv('DEMO_NO_WIPE'))    || in_array('--no-wipe',    $_SERVER['argv'] ?? [], true);
        $keepCache = !empty(getenv('DEMO_KEEP_CACHE')) || in_array('--keep-cache', $_SERVER['argv'] ?? [], true);

        echo "\n";
        echo "================================================================\n";
        echo " DEMO SEEDER — full app dataset\n";
        echo "================================================================\n";

        // ─── 1. Wipe ────────────────────────────────────────────────────────
        if (!$noWipe) {
            DemoWiper::wipe();
            // Optionally flush the application cache so stale HasCache entries
            // from a previous demo dataset don't bleed through. Skipped under
            // --keep-cache (used by demo-reset.sh blue/green: target DB seeds
            // run with new UIDs while webapp on the OTHER DB still serves
            // requests — flushing would needlessly cold-cache the live DB
            // during the seed window. Stale entries are orphan UIDs anyway,
            // never queried by the new dataset.)
            if (!$keepCache) {
                try {
                    \Illuminate\Support\Facades\Cache::flush();
                    echo "[demo:wipe]   flushed application cache\n";
                } catch (\Throwable $e) {
                    echo "[demo:wipe]   cache flush failed (non-fatal): " . $e->getMessage() . "\n";
                }
            } else {
                echo "[demo:wipe]   --keep-cache: skipping Cache::flush() (orphan entries by random UID will not collide)\n";
            }
        } else {
            echo "[demo] --no-wipe: skipping destructive wipe\n";
        }

        // ─── 2. Bootstrap system reference data via DatabaseInit ──────────
        // DatabaseInit seeds: languages, countries, admin_groups, plans,
        // and a default sending server. It is idempotent (uses firstOrCreate
        // patterns), so calling it after a wipe ensures all system reference
        // data exists before the demo stages run.
        $this->call(DatabaseInit::class);
        $this->bootstrapExtrasIfNeeded();

        // ─── 3. Demo stages (in dependency order) ──────────────────────────
        $stages = [
            Stage01_AdminsSeeder::class,
            Stage03_CustomersSeeder::class,
            Stage04_SubscriptionsSeeder::class,
            Stage05_AdminInfraSeeder::class,
            Stage06_CustomerInfraSeeder::class,
            Stage07_MailListsSeeder::class,
            Stage08_SubscribersSeeder::class,
            Stage09_TemplatesSeeder::class,
            Stage10_CampaignsSeeder::class,
            Stage11_TrackingLogsSeeder::class,
            Stage12_FormsSeeder::class,
            Stage14_AutomationsSeeder::class,   // "My Sales Pipeline" automation under Emma + 500 trigger rows
            Stage15_AbTestsSeeder::class,
            Stage16_FunnelsSeeder::class,
            Stage17_WebsitesSeeder::class,
            Stage18_NotificationsSeeder::class,
            Stage19_SettingsSeeder::class,
            Stage22_AdsSeeder::class,     // MUST run after customers/lists exist
            Stage23_CustomerAssetsSeeder::class, // dynamic upload from database/seeders/Demo/Photos
            Stage21_CacheWarmer::class,   // MUST run before manifest so totals reflect warm caches
            Stage20_ManifestWriter::class,
        ];

        foreach ($stages as $stage) {
            if (!class_exists($stage)) {
                echo "[demo] ⚠ {$stage} not yet implemented — skipping\n";
                continue;
            }
            $this->call($stage);
        }

        $elapsed = round(microtime(true) - $startedAt, 1);
        echo "================================================================\n";
        echo " DEMO SEEDER DONE — elapsed {$elapsed}s\n";
        echo " Manifest: " . DemoConfig::manifestPath() . "\n";
        echo "================================================================\n\n";

        // ─── Print login credentials ───────────────────────────────────
        $this->printLoginCredentials();
    }

    /**
     * Bootstrap extras that DatabaseInit does NOT seed: roles, templates,
     * and enforce English as active default language.
     *
     * DatabaseInit already handles: languages, countries, admin_groups,
     * plans, sending server, and USD currency.
     */
    protected function bootstrapExtrasIfNeeded(): void
    {
        echo "[demo] checking extras beyond DatabaseInit...\n";

        // ─── 1. Force English as active default ───────────────────────────
        $en = \App\Model\Language::where('code', 'en')->first();
        if ($en) {
            $changed = false;
            if ($en->status !== \App\Model\Language::STATUS_ACTIVE) {
                $en->status = \App\Model\Language::STATUS_ACTIVE;
                $changed = true;
            }
            if (!$en->is_default) {
                \App\Model\Language::where('id', '!=', $en->id)->update(['is_default' => 0]);
                $en->is_default = true;
                $changed = true;
            }
            if ($changed) {
                $en->save();
                echo "[demo]   → languages: forced English as active default\n";
            }
        }

        // ─── 2. Roles ─────────────────────────────────────────────────────
        if (\App\Model\Role::count() === 0) {
            $this->bootstrapDefaultRoles();
            echo "[demo]   → roles: " . \App\Model\Role::count() . " rows\n";
        }

        // ─── 3. Templates — seed canonical system email/form templates ────
        if (\App\Model\SystemEmailTemplate::count() === 0) {
            (new \Database\Seeders\TemplateSeeder())->run();
            echo "[demo]   → templates: " . \App\Model\SystemEmailTemplate::count() . " system email templates\n";
        }

        echo "[demo] extras OK\n";
    }

    /**
     * Print the primary login credentials after seeding completes.
     */
    protected function printLoginCredentials(): void
    {
        $admin = DemoConfig::admins()[0] ?? null;
        $primary = null;
        foreach (DemoConfig::customers() as $c) {
            if ($c['key'] === 'primary') { $primary = $c; break; }
        }

        echo "┌─────────────────────────────────────────────────────────────┐\n";
        echo "│  LOGIN CREDENTIALS                                        │\n";
        echo "├──────────┬──────────────────────────────────┬──────────────┤\n";
        echo "│  Role    │  Email                           │  Password    │\n";
        echo "├──────────┼──────────────────────────────────┼──────────────┤\n";
        if ($admin) {
            printf("│  Admin   │  %-32s│  %-12s│\n", $admin['email'], $admin['password']);
        }
        if ($primary) {
            printf("│  Customer│  %-32s│  %-12s│\n", $primary['email'], $primary['password']);
        }
        echo "└──────────┴──────────────────────────────────┴──────────────┘\n";
        echo "\n";
        if ($admin) {
            echo "  Admin panel:  /admin\n";
        }
        if ($primary) {
            echo "  Customer app: /dashboard\n";
        }
        echo "\n";
    }

    /**
     * Recreate the default global roles (`organization_admin`, `organization_readonly`).
     * Mirrors the AddDefaultRoles migration so we don't depend on it having
     * been re-run. The customer/admin creation flow looks up the admin role
     * via `Role::whereCode('organization_admin')->first()`, so the code MUST
     * be exactly that string.
     */
    protected function bootstrapDefaultRoles(): void
    {
        if (\App\Model\Role::count() > 0) return;

        $adminRole = \App\Model\Role::newGlobalRole();
        $adminRole->code        = 'organization_admin';
        $adminRole->name        = 'Full Access';
        $adminRole->description = 'The default role for new users who have full access to the customer account.';
        $adminRole->is_global   = true;
        $adminRole->readonly    = true;
        $adminRole->save();
        try { $adminRole->assignDefaultAdminPermissions(); } catch (\Throwable $e) {}

        $readonlyRole = \App\Model\Role::newGlobalRole();
        $readonlyRole->code        = 'organization_readonly';
        $readonlyRole->name        = 'Read Only';
        $readonlyRole->description = 'The default role for new users who can view all customer account data without the ability to edit or change it.';
        $readonlyRole->is_global   = true;
        $readonlyRole->readonly    = true;
        $readonlyRole->save();
        try { $readonlyRole->assignDefaultReadonlyPermissions(); } catch (\Throwable $e) {}
    }
}