id(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->foreignId('qr_code_id')->nullable()->constrained('qr_codes')->nullOnDelete(); $table->string('title'); $table->text('message')->nullable(); $table->string('password_hash')->nullable(); $table->unsignedSmallInteger('retention_days')->default(30); $table->timestamp('expires_at')->nullable(); $table->string('status', 20)->default('active'); $table->unsignedBigInteger('downloads_total')->default(0); $table->unsignedBigInteger('storage_bytes')->default(0); $table->timestamps(); $table->index(['user_id', 'status', 'created_at']); $table->index(['expires_at', 'status']); }); Schema::create('transfer_files', function (Blueprint $table) { $table->id(); $table->foreignId('transfer_id')->constrained()->cascadeOnDelete(); $table->string('original_name'); $table->string('disk', 32)->default('qr'); $table->string('path'); $table->string('mime_type', 128); $table->unsignedBigInteger('size_bytes')->default(0); $table->unsignedBigInteger('downloads_total')->default(0); $table->timestamps(); $table->index(['transfer_id', 'created_at']); }); Schema::create('transfer_download_events', function (Blueprint $table) { $table->id(); $table->foreignId('transfer_id')->constrained()->cascadeOnDelete(); $table->foreignId('transfer_file_id')->nullable()->constrained('transfer_files')->nullOnDelete(); $table->timestamp('downloaded_at'); $table->string('ip_hash', 64)->nullable(); $table->text('user_agent')->nullable(); $table->timestamps(); $table->index(['transfer_id', 'downloaded_at']); }); } public function down(): void { Schema::dropIfExists('transfer_download_events'); Schema::dropIfExists('transfer_files'); Schema::dropIfExists('transfers'); } };