id(); // --- Public Identity --- $table->string('username')->unique(); $table->string('username_index')->unique(); // Blind index for case-insensitive lookups $table->string('name'); // --- Private Identity (Encrypted) --- $table->text('email'); // Encrypted via SafeEncryptedString cast $table->string('email_index')->unique(); // Blind index for login lookups $table->timestamp('email_verified_at')->nullable(); $table->string('password'); // --- Role & Status --- $table->string('role')->default('user'); // admin | mod | streamer | user $table->string('clan_tag')->nullable(); $table->string('avatar_url')->nullable(); // OAuth / external avatar $table->string('avatar')->nullable(); // Uploaded avatar $table->string('banner')->nullable(); // Uploaded banner $table->text('bio')->nullable(); $table->boolean('is_public')->default(false); $table->boolean('is_adult')->default(false); $table->boolean('is_banned')->default(false); // --- VIP & Balance --- $table->integer('vip_level')->default(0); $table->string('preferred_locale', 8)->nullable(); $table->decimal('balance', 16, 4)->default(0); // BTX main balance $table->string('currency', 3)->default('EUR'); // --- Vault --- $table->decimal('vault_balance', 16, 4)->default(0); // BTX vault balance (plaintext decimal) $table->text('vault_balances')->nullable(); // JSON map for multi-currency vault $table->string('vault_pin_hash')->nullable(); $table->timestamp('vault_pin_set_at')->nullable(); $table->unsignedSmallInteger('vault_pin_attempts')->default(0); $table->timestamp('vault_pin_locked_until')->nullable(); $table->timestamp('withdraw_cooldown_until')->nullable(); // --- Personal Details (Encrypted PII) --- $table->text('first_name')->nullable(); $table->text('last_name')->nullable(); $table->text('birthdate')->nullable(); $table->text('gender')->nullable(); $table->text('phone')->nullable(); // --- Address (Encrypted PII) --- $table->text('country')->nullable(); $table->text('address_line1')->nullable(); $table->text('address_line2')->nullable(); $table->text('city')->nullable(); $table->text('state')->nullable(); $table->text('postal_code')->nullable(); // --- 2FA --- $table->text('two_factor_secret')->nullable(); $table->text('two_factor_recovery_codes')->nullable(); $table->timestamp('two_factor_confirmed_at')->nullable(); // --- Security & Tracking --- $table->timestamp('last_login_at')->nullable(); $table->text('last_login_ip')->nullable(); $table->text('last_login_user_agent')->nullable(); $table->string('registration_ip', 45)->nullable(); $table->rememberToken(); $table->timestamps(); }); // --------------------------------------------------------------- // PASSWORD RESET TOKENS // --------------------------------------------------------------- Schema::create('password_reset_tokens', function (Blueprint $table) { $table->string('email')->primary(); $table->string('token'); $table->timestamp('created_at')->nullable(); }); // --------------------------------------------------------------- // SESSIONS // --------------------------------------------------------------- Schema::create('sessions', function (Blueprint $table) { $table->string('id')->primary(); $table->foreignId('user_id')->nullable()->index(); $table->string('ip_address', 45)->nullable(); $table->text('user_agent')->nullable(); $table->longText('payload'); $table->integer('last_activity')->index(); }); } public function down(): void { Schema::dropIfExists('sessions'); Schema::dropIfExists('password_reset_tokens'); Schema::dropIfExists('users'); } };