id(); $table->string('name')->unique(); $table->string('slug')->unique(); $table->text('description')->nullable(); $table->timestamps(); }); Schema::create('channels', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->unique()->constrained()->cascadeOnDelete(); $table->foreignId('category_id')->nullable()->constrained()->nullOnDelete(); $table->unsignedBigInteger('live_broadcast_id')->nullable()->index(); $table->string('slug')->unique(); $table->string('display_name'); $table->text('description')->nullable(); $table->string('avatar_path')->nullable(); $table->string('banner_path')->nullable(); $table->string('thumbnail_path')->nullable(); $table->boolean('is_live')->default(false)->index(); $table->unsignedInteger('viewer_count')->default(0); $table->timestamp('suspended_at')->nullable()->index(); $table->timestamps(); }); Schema::create('stream_keys', function (Blueprint $table) { $table->id(); $table->foreignId('channel_id')->unique()->constrained()->cascadeOnDelete(); $table->string('key_hash'); $table->timestamp('last_used_at')->nullable(); $table->timestamp('revoked_at')->nullable()->index(); $table->timestamps(); }); Schema::create('broadcasts', function (Blueprint $table) { $table->id(); $table->foreignId('channel_id')->constrained()->cascadeOnDelete(); $table->string('title'); $table->string('status')->default('pending')->index(); $table->boolean('recording_enabled')->default(false); $table->string('mediamtx_path')->nullable()->index(); $table->string('source_type')->nullable(); $table->string('source_id')->nullable(); $table->string('hls_url')->nullable(); $table->unsignedInteger('viewer_peak')->default(0); $table->timestamp('started_at')->nullable()->index(); $table->timestamp('ended_at')->nullable()->index(); $table->timestamps(); }); Schema::create('vods', function (Blueprint $table) { $table->id(); $table->foreignId('broadcast_id')->unique()->constrained()->cascadeOnDelete(); $table->foreignId('channel_id')->constrained()->cascadeOnDelete(); $table->string('title'); $table->string('storage_path'); $table->string('playback_url')->nullable(); $table->unsignedInteger('duration_seconds')->nullable(); $table->unsignedBigInteger('size_bytes')->nullable(); $table->timestamp('published_at')->nullable()->index(); $table->timestamp('expires_at')->nullable()->index(); $table->softDeletes(); $table->timestamps(); }); Schema::create('follows', function (Blueprint $table) { $table->id(); $table->foreignId('channel_id')->constrained()->cascadeOnDelete(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->timestamps(); $table->unique(['channel_id', 'user_id']); }); Schema::create('chat_messages', function (Blueprint $table) { $table->id(); $table->foreignId('channel_id')->constrained()->cascadeOnDelete(); $table->foreignId('broadcast_id')->nullable()->constrained()->nullOnDelete(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->string('body', 500); $table->softDeletes(); $table->timestamps(); }); Schema::create('admin_audit_logs', function (Blueprint $table) { $table->id(); $table->foreignId('admin_user_id')->nullable()->constrained('users')->nullOnDelete(); $table->string('subject_type'); $table->unsignedBigInteger('subject_id'); $table->string('action'); $table->json('metadata')->nullable(); $table->timestamp('created_at')->nullable(); $table->index(['subject_type', 'subject_id']); }); } public function down(): void { Schema::dropIfExists('admin_audit_logs'); Schema::dropIfExists('chat_messages'); Schema::dropIfExists('follows'); Schema::dropIfExists('vods'); Schema::dropIfExists('broadcasts'); Schema::dropIfExists('stream_keys'); Schema::dropIfExists('channels'); Schema::dropIfExists('categories'); } };