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/Demo/DemoWiper.php
<?php

namespace Database\Seeders\Demo;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

/**
 * DemoWiper — completely wipes business data so DemoSeeder can rebuild from scratch.
 *
 * IMPORTANT: This is destructive. It removes ALL customers, lists, subscribers,
 * campaigns, etc.
 *
 * Preservation rule: only tables LISTED in `$tables` get truncated.
 * Everything else in the schema is preserved by omission. Notable preserved
 * tables include (but are NOT limited to):
 *   - languages                           (system reference, LanguageSeeder is idempotent)
 *   - settings                            (system config — Stage19 selectively re-seeds)
 *   - migrations                          (laravel state)
 *   - plugins                             (plugin registry — survives reseed)
 *   - ai_conversations / ai_messages / ai_requests / ai_tool_calls /
 *     ai_feedback / ai_raw_blobs         (AI observability audit tables)
 *
 * We DO wipe countries and admin_groups because DemoSeeder calls
 * DatabaseInit which re-seeds them (and they are NOT idempotent).
 *
 * It also removes admin users + the wiped admins' user rows so re-seeding
 * doesn't trip on unique-email constraints.
 *
 * Wipe order = reverse of foreign-key dependency. We disable FK checks once
 * for speed and re-enable at the end.
 */
class DemoWiper
{
    /** Tables to truncate, in safe order (children → parents). */
    protected static array $tables = [
        // tracking & log children
        'ip_locations', 'open_logs', 'click_logs', 'bounce_logs', 'feedback_logs',
        'unsubscribe_logs', 'reply_logs', 'tracking_logs',
        // campaign + automation graph
        'campaigns_sending_servers',
        'campaigns_lists_segments', 'campaign_links', 'campaign_headers',
        'campaign_webhooks', 'ab_test_assignments', 'ab_test_variants', 'ab_tests',
        'auto_triggers', 'trigger_sessions',
        'automation_emails', 'automation_elements', 'automation2s',
        'emails',
        'campaigns',
        // funnels
        'funnel_conversions', 'funnel_page_views', 'funnel_product_links',
        'funnel_products', 'funnel_steps', 'funnel_domains', 'funnels',
        // forms
        'forms',
        // subscribers + list children
        'segments', 'fields', 'field_options',
        'subscribers',
        'mail_list_templates', 'mail_lists',
        // billing (transactions table dropped — Transaction model removed)
        'order_items',
        'orders',
        'invoices',
        'payment_intents',
        'billing_addresses',
        // 4-concept subscription credit snapshot (FK → subscriptions; TRUNCATE
        // bypasses cascade, so we must wipe explicitly BEFORE subscriptions).
        'subscription_credit_state',
        'subscriptions',
        // sending infrastructure (per-customer copies)
        // sending_identities FK-cascades from sending_servers + customers,
        // but TRUNCATE bypasses FK cascade — wipe explicitly before parents.
        'sending_identities',
        'tracking_domains', 'verified_senders', 'signatures',
        'email_verification_servers', 'sending_servers',
        // misc per-customer
        'assets',
        'customer_email_templates', 'page_templates', 'form_templates',
        // templates — wipe ALL to force TemplateSeeder re-run + prevent
        // accumulation of orphan copies across reseeds (campaigns/emails
        // create template copies; without wipe they pile up: 12k+ rows)
        // ads module — leaf tables first, then parents (FK order)
        'ad_metrics', 'ad_conversions', 'ad_products', 'ad_leads',
        'ad_publish_logs', 'ad_activity_logs', 'ad_ab_variants',
        'ad_ab_tests', 'ad_automation_rules', 'ad_lead_form_configs',
        'ad_product_catalogs', 'ad_pixels', 'ad_audiences',
        'ad_campaign_platforms', 'ad_campaigns', 'ad_accounts',
        'ad_platforms',
        'ad_creatives', 'ad_templates',
        'templates_categories',
        'system_email_templates', 'system_templates', 'templates',
        'websites', 'wp_posts',
        'activity_logs', 'notifications',
        // admin-level catalogs
        'blacklists', 'senders', 'bounce_handlers', 'feedback_loop_handlers',
        'payment_methods', 'payment_gateways',
        'warmup_strategies',
        'plans_sending_servers',
        'plan_remote_mappings',
        // 4-concept plan feature tables (FK → plans with cascade; TRUNCATE
        // doesn't cascade, so wipe explicitly BEFORE plans).
        'plan_credits', 'plan_quotas', 'plan_entitlements', 'plan_rate_limits',
        // top-level
        'plans',
        'admins', 'customers',
        'users',
        // system reference data — DatabaseInit will re-seed these
        'currencies', 'countries', 'admin_groups',
    ];

    public static function wipe(): void
    {
        echo "[demo:wipe] starting full wipe...\n";
        DB::statement('SET FOREIGN_KEY_CHECKS=0');
        try {
            foreach (self::$tables as $table) {
                if (Schema::hasTable($table)) {
                    DB::table($table)->truncate();
                    echo "[demo:wipe]   truncated {$table}\n";
                }
            }

            // Drop dynamic custom-field columns from `subscribers` (they accumulate
            // across reseeds and would confuse field tag uniqueness).
            self::dropDynamicSubscriberColumns();
        } finally {
            DB::statement('SET FOREIGN_KEY_CHECKS=1');
        }

        // Clean up template thumbnail files from storage — they accumulate
        // across reseeds (one per Template::copy()) and are NOT on the repo.
        // TemplateSeeder will regenerate canonical thumbs from theme source SVGs.
        self::cleanTemplateThumbStorage();

        echo "[demo:wipe] done\n";
    }

    /**
     * Remove template + ad template thumbnail files from storage.
     * Without cleanup these accumulate across reseeds.
     */
    protected static function cleanTemplateThumbStorage(): void
    {
        $dirs = [
            storage_path('app/public/templates/thumb'),
            storage_path('app/public/ad-templates/thumb'),
        ];
        foreach ($dirs as $thumbDir) {
            if (!is_dir($thumbDir)) continue;
            $files = glob($thumbDir . '/*');
            $count = 0;
            foreach ($files as $file) {
                if (is_file($file)) {
                    unlink($file);
                    $count++;
                }
            }
            if ($count > 0) {
                $label = basename(dirname($thumbDir));
                echo "[demo:wipe]   removed {$count} {$label} thumbnail files\n";
            }
        }
    }

    /**
     * The Subscriber model dynamically appends columns named field_NNN.
     * Drop them so a fresh seed starts with a clean slate.
     */
    protected static function dropDynamicSubscriberColumns(): void
    {
        if (!Schema::hasTable('subscribers')) return;
        $cols = Schema::getColumnListing('subscribers');
        $dynamic = array_filter($cols, fn($c) => preg_match('/^field_\d+$/', $c));
        if (empty($dynamic)) return;
        Schema::table('subscribers', function ($table) use ($dynamic) {
            foreach ($dynamic as $col) {
                $table->dropColumn($col);
            }
        });
        echo "[demo:wipe]   dropped " . count($dynamic) . " dynamic field_* columns\n";
    }
}