Initial commit: Animexe Laravel platform
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
*.sqlite*
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The current password being used by the factory.
|
||||
*/
|
||||
protected static ?string $password;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'email_verified_at' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('username')->unique()->nullable();
|
||||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password');
|
||||
$table->string('avatar')->nullable();
|
||||
$table->enum('role', ['user', 'moderator', 'admin'])->default('user');
|
||||
$table->enum('membership', ['free', 'premium'])->default('free');
|
||||
$table->dateTime('premium_expires_at')->nullable();
|
||||
$table->boolean('is_banned')->default(false);
|
||||
$table->string('ban_reason')->nullable();
|
||||
$table->timestamp('banned_at')->nullable();
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
||||
$table->string('email')->primary();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
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('users');
|
||||
Schema::dropIfExists('password_reset_tokens');
|
||||
Schema::dropIfExists('sessions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('cache', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->mediumText('value');
|
||||
$table->integer('expiration')->index();
|
||||
});
|
||||
|
||||
Schema::create('cache_locks', function (Blueprint $table) {
|
||||
$table->string('key')->primary();
|
||||
$table->string('owner');
|
||||
$table->integer('expiration')->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cache');
|
||||
Schema::dropIfExists('cache_locks');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('queue')->index();
|
||||
$table->longText('payload');
|
||||
$table->unsignedTinyInteger('attempts');
|
||||
$table->unsignedInteger('reserved_at')->nullable();
|
||||
$table->unsignedInteger('available_at');
|
||||
$table->unsignedInteger('created_at');
|
||||
});
|
||||
|
||||
Schema::create('job_batches', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->string('name');
|
||||
$table->integer('total_jobs');
|
||||
$table->integer('pending_jobs');
|
||||
$table->integer('failed_jobs');
|
||||
$table->longText('failed_job_ids');
|
||||
$table->mediumText('options')->nullable();
|
||||
$table->integer('cancelled_at')->nullable();
|
||||
$table->integer('created_at');
|
||||
$table->integer('finished_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('failed_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('uuid')->unique();
|
||||
$table->text('connection');
|
||||
$table->text('queue');
|
||||
$table->longText('payload');
|
||||
$table->longText('exception');
|
||||
$table->timestamp('failed_at')->useCurrent();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('jobs');
|
||||
Schema::dropIfExists('job_batches');
|
||||
Schema::dropIfExists('failed_jobs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('membership_plans', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->text('description')->nullable();
|
||||
$table->decimal('price', 8, 2);
|
||||
$table->integer('duration_days'); // 30, 90, 365
|
||||
$table->json('features')->nullable(); // ["4K izleme", "Reklamsız", ...]
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->integer('sort_order')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('membership_plans');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('genres', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('slug')->unique();
|
||||
$table->string('color', 7)->nullable(); // hex renk
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('genres');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('animes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('title');
|
||||
$table->string('title_en')->nullable();
|
||||
$table->string('title_jp')->nullable();
|
||||
$table->string('slug')->unique();
|
||||
$table->text('description')->nullable();
|
||||
$table->string('cover_image')->nullable();
|
||||
$table->string('banner_image')->nullable();
|
||||
$table->string('trailer_url')->nullable();
|
||||
$table->integer('release_year')->nullable();
|
||||
$table->enum('type', ['series', 'movie', 'ova', 'ona', 'special'])->default('series');
|
||||
$table->enum('status', ['ongoing', 'completed', 'upcoming'])->default('ongoing');
|
||||
$table->integer('episode_count')->default(0);
|
||||
$table->decimal('rating', 3, 1)->default(0);
|
||||
$table->string('studio')->nullable();
|
||||
$table->string('mal_id')->nullable(); // MyAnimeList ID
|
||||
$table->boolean('is_featured')->default(false);
|
||||
$table->boolean('is_published')->default(false);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('anime_genre', function (Blueprint $table) {
|
||||
$table->foreignId('anime_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('genre_id')->constrained()->onDelete('cascade');
|
||||
$table->primary(['anime_id', 'genre_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('anime_genre');
|
||||
Schema::dropIfExists('animes');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('seasons', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('anime_id')->constrained()->onDelete('cascade');
|
||||
$table->integer('season_number');
|
||||
$table->string('title')->nullable();
|
||||
$table->text('description')->nullable();
|
||||
$table->string('cover_image')->nullable();
|
||||
$table->integer('release_year')->nullable();
|
||||
$table->boolean('is_published')->default(true);
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['anime_id', 'season_number']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('seasons');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('episodes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('anime_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('season_id')->constrained()->onDelete('cascade');
|
||||
$table->integer('episode_number');
|
||||
$table->string('title')->nullable();
|
||||
$table->text('description')->nullable();
|
||||
$table->string('thumbnail')->nullable();
|
||||
$table->integer('duration')->nullable(); // saniye
|
||||
// BunnyCDN
|
||||
$table->string('bunny_video_id')->nullable();
|
||||
$table->string('bunny_library_id')->nullable();
|
||||
$table->string('video_url')->nullable(); // CDN pull URL
|
||||
$table->string('m3u8_url')->nullable();
|
||||
// Kaynak
|
||||
$table->string('source_url')->nullable(); // Anizium CDN URL
|
||||
$table->enum('source', ['bunnycdn', 'external', 'direct'])->default('bunnycdn');
|
||||
// Durum
|
||||
$table->enum('status', ['pending', 'processing', 'published', 'failed'])->default('pending');
|
||||
$table->integer('view_count')->default(0);
|
||||
$table->boolean('is_published')->default(false);
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['season_id', 'episode_number']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('episodes');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
// Global izin ayarları (tüm site geneli)
|
||||
Schema::create('permission_settings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('key')->unique(); // örn: "can_comment", "can_watch", "can_rate"
|
||||
$table->string('label'); // Yönetici panelinde görünen ad
|
||||
$table->enum('required_membership', ['free', 'premium'])->default('free');
|
||||
$table->text('description')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// İçeriğe özel izin override'ları
|
||||
Schema::create('content_permissions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('content_type'); // 'anime', 'episode', 'season'
|
||||
$table->unsignedBigInteger('content_id');
|
||||
$table->string('permission_key'); // 'can_watch', 'can_comment', vb.
|
||||
$table->enum('required_membership', ['free', 'premium']);
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['content_type', 'content_id', 'permission_key'], 'cp_unique');
|
||||
$table->index(['content_type', 'content_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('content_permissions');
|
||||
Schema::dropIfExists('permission_settings');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('comments', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->string('commentable_type'); // 'anime', 'episode'
|
||||
$table->unsignedBigInteger('commentable_id');
|
||||
$table->foreignId('parent_id')->nullable()->constrained('comments')->onDelete('cascade');
|
||||
$table->text('content');
|
||||
$table->enum('status', ['pending', 'approved', 'rejected', 'spam'])->default('approved');
|
||||
$table->boolean('is_pinned')->default(false);
|
||||
$table->integer('like_count')->default(0);
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['commentable_type', 'commentable_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('comments');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('subscriptions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('plan_id')->constrained('membership_plans')->onDelete('cascade');
|
||||
$table->enum('status', ['active', 'expired', 'cancelled'])->default('active');
|
||||
$table->timestamp('starts_at')->nullable();
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
$table->string('payment_method')->nullable(); // 'manual', 'stripe', vb.
|
||||
$table->string('payment_ref')->nullable();
|
||||
$table->text('notes')->nullable(); // admin notu
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('subscriptions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('banners', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('title');
|
||||
$table->string('image');
|
||||
$table->string('link')->nullable();
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->integer('sort_order')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('banners');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('settings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('key')->unique();
|
||||
$table->text('value')->nullable();
|
||||
$table->string('group')->default('general'); // 'general', 'appearance', 'payment', vb.
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('settings');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('import_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('source_url')->nullable();
|
||||
$table->string('watch_id')->nullable();
|
||||
$table->string('cdn_id')->nullable(); // 85937 — XDM'den alınan CDN ID
|
||||
$table->string('anime_title')->nullable();
|
||||
$table->foreignId('anime_id')->nullable()->constrained('animes')->nullOnDelete();
|
||||
// Sezon/bölüm aralıkları (JSON array: [{"season":1,"from":1,"to":24}])
|
||||
$table->json('season_ranges')->nullable();
|
||||
$table->enum('status', ['pending','fetching','downloading','uploading','done','failed'])->default('pending');
|
||||
$table->integer('total_episodes')->default(0);
|
||||
$table->integer('done_episodes')->default(0);
|
||||
$table->integer('failed_episodes')->default(0);
|
||||
$table->text('current_step')->nullable();
|
||||
$table->text('error_log')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('import_jobs');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('import_jobs', function (Blueprint $table) {
|
||||
$table->string('source', 30)->default('anizium')->after('id');
|
||||
$table->string('animecix_title_id')->nullable()->after('source');
|
||||
$table->string('animecix_slug')->nullable()->after('animecix_title_id');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('import_jobs', function (Blueprint $table) {
|
||||
$table->dropColumn(['source', 'animecix_title_id', 'animecix_slug']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('subtitles', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('episode_id')->constrained('episodes')->cascadeOnDelete();
|
||||
$table->string('language', 10)->default('tr'); // tr, en, jp
|
||||
$table->string('label')->default('Türkçe'); // Görünen ad
|
||||
$table->string('url'); // VTT dosya URL'si
|
||||
$table->boolean('is_default')->default(false);
|
||||
$table->timestamps();
|
||||
$table->unique(['episode_id', 'language']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('subtitles');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('video_sources', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('episode_id')->constrained('episodes')->cascadeOnDelete();
|
||||
$table->string('label', 120)->default('');
|
||||
$table->text('url');
|
||||
$table->enum('type', ['mp4', 'hls', 'embed'])->default('mp4');
|
||||
$table->string('quality', 20)->default('');
|
||||
$table->string('translator_id', 60)->nullable();
|
||||
$table->unsignedSmallInteger('sort_order')->default(0);
|
||||
$table->boolean('is_default')->default(false);
|
||||
$table->string('source', 30)->default('animecix');
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('episode_id');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('video_sources');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
DB::statement("ALTER TABLE episodes MODIFY COLUMN source ENUM('bunnycdn','external','direct','anizium','animecix') NOT NULL DEFAULT 'bunnycdn'");
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
DB::statement("ALTER TABLE episodes MODIFY COLUMN source ENUM('bunnycdn','external','direct','anizium') NOT NULL DEFAULT 'bunnycdn'");
|
||||
}
|
||||
};
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('comments', function (Blueprint $table) {
|
||||
$table->string('gif_url', 500)->nullable()->after('content');
|
||||
});
|
||||
|
||||
Schema::create('comment_likes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->foreignId('comment_id')->constrained()->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
$table->unique(['user_id', 'comment_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('comment_likes');
|
||||
Schema::table('comments', function (Blueprint $table) {
|
||||
$table->dropColumn('gif_url');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('episodes', function (Blueprint $table) {
|
||||
// JSON array of available dub keys, e.g. ["original","trdub"]
|
||||
$table->text('available_dubs')->nullable()->after('m3u8_url');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('episodes', function (Blueprint $table) {
|
||||
$table->dropColumn('available_dubs');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('animes', function (Blueprint $table) {
|
||||
$table->boolean('is_trending')->default(false)->after('is_featured');
|
||||
$table->unsignedSmallInteger('trending_order')->default(0)->after('is_trending');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('animes', function (Blueprint $table) {
|
||||
$table->dropColumn(['is_trending', 'trending_order']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// Sayfa görüntüleme logları
|
||||
Schema::create('analytics_pageviews', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('session_id', 64)->nullable();
|
||||
$table->string('url', 500)->nullable();
|
||||
$table->string('page_type', 30)->default('other'); // home, anime, player, search, ai, genre, other
|
||||
$table->foreignId('anime_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->foreignId('episode_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('ip', 45)->nullable();
|
||||
$table->string('country', 100)->nullable();
|
||||
$table->string('city', 100)->nullable();
|
||||
$table->string('device', 20)->default('unknown'); // mobile, desktop, tablet
|
||||
$table->string('browser', 50)->nullable();
|
||||
$table->string('referrer', 500)->nullable();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
|
||||
$table->index(['created_at']);
|
||||
$table->index(['page_type', 'created_at']);
|
||||
$table->index(['anime_id', 'created_at']);
|
||||
$table->index(['user_id', 'created_at']);
|
||||
$table->index(['ip']);
|
||||
});
|
||||
|
||||
// Video izleme olayları
|
||||
Schema::create('analytics_watch_events', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('session_id', 64)->nullable();
|
||||
$table->foreignId('anime_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->foreignId('episode_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->unsignedSmallInteger('season_number')->default(1);
|
||||
$table->unsignedSmallInteger('episode_number')->default(1);
|
||||
$table->unsignedInteger('seconds_watched')->default(0);
|
||||
$table->unsignedInteger('total_seconds')->default(0);
|
||||
$table->unsignedTinyInteger('percent_complete')->default(0);
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
|
||||
$table->index(['anime_id', 'created_at']);
|
||||
$table->index(['episode_id', 'created_at']);
|
||||
$table->index(['user_id', 'created_at']);
|
||||
$table->index(['created_at']);
|
||||
});
|
||||
|
||||
// AI sorgu logları
|
||||
Schema::create('analytics_ai_queries', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('query_type', 30); // chat, recommend, search, episode_info, similar
|
||||
$table->text('query_text')->nullable();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
|
||||
$table->index(['query_type', 'created_at']);
|
||||
$table->index(['user_id', 'created_at']);
|
||||
$table->index(['created_at']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('analytics_ai_queries');
|
||||
Schema::dropIfExists('analytics_watch_events');
|
||||
Schema::dropIfExists('analytics_pageviews');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// Kaldığın yerden devam et
|
||||
Schema::create('continue_watching', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('anime_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('episode_id')->constrained()->cascadeOnDelete();
|
||||
$table->unsignedSmallInteger('season_number')->default(1);
|
||||
$table->unsignedSmallInteger('episode_number')->default(1);
|
||||
$table->unsignedInteger('seconds_watched')->default(0);
|
||||
$table->unsignedInteger('total_seconds')->default(0);
|
||||
$table->unsignedTinyInteger('percent_complete')->default(0);
|
||||
$table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate();
|
||||
|
||||
$table->unique(['user_id', 'anime_id']);
|
||||
$table->index(['user_id', 'updated_at']);
|
||||
});
|
||||
|
||||
// İzleme listesi
|
||||
Schema::create('watchlists', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('anime_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('status', 20)->default('plan'); // plan, watching, completed, dropped
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
|
||||
$table->unique(['user_id', 'anime_id']);
|
||||
$table->index(['user_id', 'status']);
|
||||
});
|
||||
|
||||
// Bölüm like/dislike
|
||||
Schema::create('episode_votes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('episode_id')->constrained()->cascadeOnDelete();
|
||||
$table->tinyInteger('vote')->default(1); // 1=like, -1=dislike
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
|
||||
$table->unique(['user_id', 'episode_id']);
|
||||
$table->index(['episode_id', 'vote']);
|
||||
});
|
||||
|
||||
// Anime puanlama (kullanıcı bazlı 1-10)
|
||||
Schema::create('anime_ratings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('anime_id')->constrained()->cascadeOnDelete();
|
||||
$table->unsignedTinyInteger('rating'); // 1-10
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate();
|
||||
|
||||
$table->unique(['user_id', 'anime_id']);
|
||||
$table->index(['anime_id']);
|
||||
});
|
||||
|
||||
// Anime istekleri
|
||||
Schema::create('anime_requests', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('title', 200);
|
||||
$table->string('original_title', 200)->nullable();
|
||||
$table->text('note')->nullable();
|
||||
$table->string('status', 20)->default('pending'); // pending, approved, rejected, added
|
||||
$table->text('admin_note')->nullable();
|
||||
$table->unsignedInteger('vote_count')->default(1); // kaç kişi istedi
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['status', 'vote_count']);
|
||||
});
|
||||
|
||||
// Anime isteği oyları (aynı anime'yi birden fazla kişi isteyebilir)
|
||||
Schema::create('anime_request_votes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('anime_request_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('ip', 45)->nullable();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
|
||||
$table->unique(['anime_request_id', 'user_id']);
|
||||
});
|
||||
|
||||
// Başarım tanımları
|
||||
Schema::create('achievements', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('key', 50)->unique();
|
||||
$table->string('title', 100);
|
||||
$table->string('description', 255);
|
||||
$table->string('icon', 50)->default('bi-trophy');
|
||||
$table->string('color', 20)->default('#f0883e');
|
||||
$table->string('condition_type', 30); // episodes_watched, hours_watched, watchlist_count, login_streak, request_approved
|
||||
$table->unsignedInteger('condition_value')->default(1);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// Kullanıcı başarımları
|
||||
Schema::create('user_achievements', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('achievement_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamp('earned_at')->useCurrent();
|
||||
|
||||
$table->unique(['user_id', 'achievement_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_achievements');
|
||||
Schema::dropIfExists('achievements');
|
||||
Schema::dropIfExists('anime_request_votes');
|
||||
Schema::dropIfExists('anime_requests');
|
||||
Schema::dropIfExists('anime_ratings');
|
||||
Schema::dropIfExists('episode_votes');
|
||||
Schema::dropIfExists('watchlists');
|
||||
Schema::dropIfExists('continue_watching');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// Anime takip
|
||||
Schema::create('anime_follows', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('anime_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->unique(['user_id', 'anime_id']);
|
||||
$table->index(['anime_id']);
|
||||
});
|
||||
|
||||
// Kullanıcı bildirimleri
|
||||
Schema::create('user_notifications', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('type', 50)->default('episode');
|
||||
$table->json('data');
|
||||
$table->timestamp('read_at')->nullable();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->index(['user_id', 'read_at']);
|
||||
$table->index(['user_id', 'created_at']);
|
||||
});
|
||||
|
||||
// Bölüm notları
|
||||
Schema::create('episode_notes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('episode_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('anime_id')->constrained()->cascadeOnDelete();
|
||||
$table->text('content');
|
||||
$table->unsignedInteger('timestamp_at')->nullable();
|
||||
$table->timestamps();
|
||||
$table->index(['user_id', 'episode_id']);
|
||||
$table->index(['user_id', 'anime_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('episode_notes');
|
||||
Schema::dropIfExists('user_notifications');
|
||||
Schema::dropIfExists('anime_follows');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void {
|
||||
Schema::table('episodes', function (Blueprint $table) {
|
||||
$table->unsignedSmallInteger('intro_start')->nullable()->after('duration');
|
||||
$table->unsignedSmallInteger('intro_end')->nullable()->after('intro_start');
|
||||
});
|
||||
}
|
||||
public function down(): void {
|
||||
Schema::table('episodes', function (Blueprint $table) {
|
||||
$table->dropColumn(['intro_start', 'intro_end']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->text('bio')->nullable()->after('avatar');
|
||||
$table->string('banner_image')->nullable()->after('bio');
|
||||
$table->string('website', 200)->nullable()->after('banner_image');
|
||||
$table->string('twitter', 100)->nullable()->after('website');
|
||||
$table->string('instagram', 100)->nullable()->after('twitter');
|
||||
$table->string('discord', 100)->nullable()->after('instagram');
|
||||
$table->string('profile_color', 7)->nullable()->default('#00f5ff')->after('discord');
|
||||
$table->boolean('show_watchlist')->default(true)->after('profile_color');
|
||||
$table->boolean('show_activity')->default(true)->after('show_watchlist');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn([
|
||||
'bio', 'banner_image', 'website', 'twitter',
|
||||
'instagram', 'discord', 'profile_color',
|
||||
'show_watchlist', 'show_activity',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('fcm_token', 500)->nullable()->after('remember_token');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('fcm_token');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('watchlists', function (Blueprint $table) {
|
||||
$table->timestamp('updated_at')->nullable()->useCurrent()->useCurrentOnUpdate()->after('created_at');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('watchlists', function (Blueprint $table) {
|
||||
$table->dropColumn('updated_at');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('episode_skip_events', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('episode_id')->constrained()->cascadeOnDelete();
|
||||
$table->unsignedInteger('from_sec'); // seek FROM
|
||||
$table->unsignedInteger('to_sec'); // seek TO
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->index(['episode_id', 'from_sec']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('episode_skip_events');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('seasons', function (Blueprint $table) {
|
||||
$table->string('mal_id')->nullable()->after('season_number');
|
||||
});
|
||||
}
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('seasons', function (Blueprint $table) {
|
||||
$table->dropColumn('mal_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('tokenable');
|
||||
$table->text('name');
|
||||
$table->string('token', 64)->unique();
|
||||
$table->text('abilities')->nullable();
|
||||
$table->timestamp('last_used_at')->nullable();
|
||||
$table->timestamp('expires_at')->nullable()->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('personal_access_tokens');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('blog_posts', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('title');
|
||||
$table->string('slug')->unique();
|
||||
$table->text('excerpt')->nullable();
|
||||
$table->longText('content')->nullable();
|
||||
$table->string('cover_image')->nullable();
|
||||
$table->string('focus_keyword')->nullable();
|
||||
$table->string('meta_title')->nullable();
|
||||
$table->text('meta_description')->nullable();
|
||||
$table->string('meta_keywords')->nullable();
|
||||
$table->enum('status', ['draft', 'published', 'generating'])->default('draft');
|
||||
$table->boolean('ai_generated')->default(false);
|
||||
$table->foreignId('anime_id')->nullable()->constrained('animes')->nullOnDelete();
|
||||
$table->json('linked_anime_ids')->nullable();
|
||||
$table->json('faq')->nullable();
|
||||
$table->unsignedInteger('views')->default(0);
|
||||
$table->unsignedSmallInteger('reading_time')->default(5);
|
||||
$table->timestamp('published_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('blog_posts');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('user_follows', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('follower_id');
|
||||
$table->unsignedBigInteger('following_id');
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
|
||||
$table->unique(['follower_id', 'following_id']);
|
||||
$table->foreign('follower_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->foreign('following_id')->references('id')->on('users')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_follows');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('conversations', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('conversation_participants', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('conversation_id');
|
||||
$table->unsignedBigInteger('user_id');
|
||||
$table->timestamp('last_read_at')->nullable();
|
||||
|
||||
$table->unique(['conversation_id', 'user_id']);
|
||||
$table->foreign('conversation_id')->references('id')->on('conversations')->onDelete('cascade');
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
});
|
||||
|
||||
Schema::create('messages', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('conversation_id');
|
||||
$table->unsignedBigInteger('user_id');
|
||||
$table->text('body');
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
|
||||
$table->foreign('conversation_id')->references('id')->on('conversations')->onDelete('cascade');
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->index(['conversation_id', 'created_at']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('messages');
|
||||
Schema::dropIfExists('conversation_participants');
|
||||
Schema::dropIfExists('conversations');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// 1) NicoNico tarzı timestamp yorumları
|
||||
Schema::create('episode_timestamp_comments', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('episode_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->unsignedSmallInteger('timestamp_sec'); // videonun kaçıncı saniyesi
|
||||
$table->string('body', 100); // max 100 karakter, kısa kalasın
|
||||
$table->string('color', 7)->default('#ffffff'); // hex renk
|
||||
$table->boolean('is_hidden')->default(false); // mod silme
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
|
||||
$table->index(['episode_id', 'timestamp_sec']);
|
||||
$table->index(['user_id']);
|
||||
});
|
||||
|
||||
// 2) Tahmin oyunu — bölüm öncesi tahminler
|
||||
Schema::create('episode_predictions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('episode_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('body', 280); // tahmin metni
|
||||
$table->boolean('is_correct')->nullable(); // null=beklemede, true/false=sonuç
|
||||
$table->unsignedSmallInteger('vote_count')->default(0);
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['episode_id', 'user_id']); // bölüm başına 1 tahmin
|
||||
$table->index(['episode_id', 'vote_count']);
|
||||
});
|
||||
|
||||
// 3) Tahmin oyları
|
||||
Schema::create('prediction_votes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('prediction_id')->constrained('episode_predictions')->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
|
||||
$table->unique(['prediction_id', 'user_id']);
|
||||
});
|
||||
|
||||
// 4) Watch Party odaları
|
||||
Schema::create('watch_parties', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('room_code', 10)->unique();
|
||||
$table->foreignId('host_user_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->foreignId('episode_id')->constrained()->cascadeOnDelete();
|
||||
$table->unsignedInteger('current_sec')->default(0); // senkron zaman
|
||||
$table->boolean('is_playing')->default(false);
|
||||
$table->timestamp('synced_at')->useCurrent()->useCurrentOnUpdate();
|
||||
$table->unsignedTinyInteger('max_members')->default(10);
|
||||
$table->boolean('is_private')->default(false);
|
||||
$table->string('password', 60)->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['room_code']);
|
||||
$table->index(['episode_id']);
|
||||
});
|
||||
|
||||
// 5) Watch Party üyeleri
|
||||
Schema::create('watch_party_members', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('party_id')->constrained('watch_parties')->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamp('joined_at')->useCurrent();
|
||||
$table->timestamp('last_ping')->useCurrent()->useCurrentOnUpdate();
|
||||
|
||||
$table->unique(['party_id', 'user_id']);
|
||||
$table->index(['party_id', 'last_ping']);
|
||||
});
|
||||
|
||||
// 6) İlk kez izleyenler — aktif oturum kaydı (60dk TTL, cron temizler)
|
||||
Schema::create('first_watch_sessions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('episode_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('session_id', 64)->nullable(); // misafir desteği
|
||||
$table->boolean('is_first_time')->default(true); // kullanıcı "ilk kez" dedi mi
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('last_seen')->useCurrent()->useCurrentOnUpdate();
|
||||
|
||||
$table->index(['episode_id', 'last_seen']);
|
||||
$table->index(['user_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('first_watch_sessions');
|
||||
Schema::dropIfExists('watch_party_members');
|
||||
Schema::dropIfExists('watch_parties');
|
||||
Schema::dropIfExists('prediction_votes');
|
||||
Schema::dropIfExists('episode_predictions');
|
||||
Schema::dropIfExists('episode_timestamp_comments');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('tribunals', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('anime_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('episode_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->foreignId('created_by')->constrained('users')->cascadeOnDelete();
|
||||
$table->string('question', 280);
|
||||
$table->string('side_a', 100);
|
||||
$table->string('side_b', 100);
|
||||
$table->enum('status', ['open', 'closed'])->default('open');
|
||||
$table->string('verdict', 1)->nullable();
|
||||
$table->timestamp('closes_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('tribunal_votes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('tribunal_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('side', 1);
|
||||
$table->timestamp('created_at');
|
||||
$table->unique(['tribunal_id', 'user_id']);
|
||||
});
|
||||
|
||||
Schema::create('tribunal_arguments', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('tribunal_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('side', 1);
|
||||
$table->string('body', 500);
|
||||
$table->unsignedSmallInteger('vote_count')->default(0);
|
||||
$table->timestamps();
|
||||
$table->unique(['tribunal_id', 'user_id']);
|
||||
});
|
||||
|
||||
Schema::create('tribunal_argument_votes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('argument_id')->constrained('tribunal_arguments')->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamp('created_at');
|
||||
$table->unique(['argument_id', 'user_id']);
|
||||
});
|
||||
|
||||
Schema::create('time_capsules', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('anime_id')->constrained()->cascadeOnDelete();
|
||||
$table->text('message');
|
||||
$table->timestamp('unlock_at');
|
||||
$table->timestamp('opened_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('spoiler_boxes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('episode_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->text('body');
|
||||
$table->boolean('is_spoiler')->default(false);
|
||||
$table->tinyInteger('spoiler_score')->default(0);
|
||||
$table->unsignedSmallInteger('likes')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('spoiler_box_likes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('box_id')->constrained('spoiler_boxes')->cascadeOnDelete();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamp('created_at');
|
||||
$table->unique(['box_id', 'user_id']);
|
||||
});
|
||||
|
||||
Schema::create('mood_logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->foreignId('episode_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('mood', 20);
|
||||
$table->timestamp('created_at');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('mood_logs');
|
||||
Schema::dropIfExists('spoiler_box_likes');
|
||||
Schema::dropIfExists('spoiler_boxes');
|
||||
Schema::dropIfExists('time_capsules');
|
||||
Schema::dropIfExists('tribunal_argument_votes');
|
||||
Schema::dropIfExists('tribunal_arguments');
|
||||
Schema::dropIfExists('tribunal_votes');
|
||||
Schema::dropIfExists('tribunals');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('tribunals', function (Blueprint $table) {
|
||||
$table->json('extra_sides')->nullable()->after('side_b');
|
||||
});
|
||||
|
||||
// Oy sütununu genişlet (a-z yeterliydi zaten, varchar(1) → varchar(2) yine de)
|
||||
Schema::table('tribunal_votes', function (Blueprint $table) {
|
||||
$table->string('side', 2)->change();
|
||||
});
|
||||
|
||||
Schema::table('tribunal_arguments', function (Blueprint $table) {
|
||||
$table->string('side', 2)->change();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('tribunals', function (Blueprint $table) {
|
||||
$table->dropColumn('extra_sides');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('moderator_permissions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->string('permission', 80);
|
||||
$table->foreignId('granted_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
|
||||
$table->unique(['user_id', 'permission']);
|
||||
$table->index('user_id');
|
||||
});
|
||||
|
||||
// user_activity_logs — every significant action a logged-in user takes
|
||||
Schema::create('user_activity_logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('session_id', 64)->nullable()->index();
|
||||
$table->string('action', 60); // login, logout, comment, watchlist_add…
|
||||
$table->string('subject_type', 60)->nullable(); // Anime, Episode, Comment…
|
||||
$table->unsignedBigInteger('subject_id')->nullable();
|
||||
$table->string('ip', 45)->nullable()->index();
|
||||
$table->string('country', 80)->nullable()->index();
|
||||
$table->string('city', 80)->nullable();
|
||||
$table->string('device', 20)->nullable();
|
||||
$table->string('browser', 50)->nullable();
|
||||
$table->string('user_agent', 500)->nullable();
|
||||
$table->boolean('is_bot')->default(false)->index();
|
||||
$table->json('meta')->nullable(); // extra context
|
||||
$table->timestamp('created_at')->useCurrent()->index();
|
||||
|
||||
$table->index(['user_id', 'created_at']);
|
||||
$table->index(['action', 'created_at']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_activity_logs');
|
||||
Schema::dropIfExists('moderator_permissions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('analytics_pageviews', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('analytics_pageviews', 'is_bot')) {
|
||||
$table->boolean('is_bot')->default(false)->index()->after('referrer');
|
||||
}
|
||||
if (!Schema::hasColumn('analytics_pageviews', 'user_agent')) {
|
||||
$table->string('user_agent', 500)->nullable()->after('is_bot');
|
||||
}
|
||||
if (!Schema::hasColumn('analytics_pageviews', 'time_on_page')) {
|
||||
$table->unsignedSmallInteger('time_on_page')->default(0)->after('user_agent');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('analytics_pageviews', function (Blueprint $table) {
|
||||
$table->dropColumn(['is_bot', 'user_agent', 'time_on_page']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
DB::statement("UPDATE episodes SET source = 'bunnycdn' WHERE source NOT IN ('bunnycdn','external','direct','anizium')");
|
||||
DB::statement("ALTER TABLE episodes MODIFY COLUMN source ENUM('bunnycdn','external','direct','anizium') NOT NULL DEFAULT 'bunnycdn'");
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
DB::statement("UPDATE episodes SET source = 'external' WHERE source = 'anizium'");
|
||||
DB::statement("ALTER TABLE episodes MODIFY COLUMN source ENUM('bunnycdn','external','direct') NOT NULL DEFAULT 'bunnycdn'");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('membership_plans', function (Blueprint $table) {
|
||||
// Her plan için hangi premium özelliklerin aktif olduğunu saklar
|
||||
// Örnek: {"ad_free":true,"comment_bg":true,"gif_avatar":false,...}
|
||||
$table->json('perks')->nullable()->after('features');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('membership_plans', function (Blueprint $table) {
|
||||
$table->dropColumn('perks');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('gif_avatar', 500)->nullable()->after('avatar');
|
||||
$table->string('comment_bg', 32)->nullable()->after('gif_avatar'); // fire|aurora|stars|sakura|neon|galaxy|ice
|
||||
$table->string('username_color', 32)->nullable()->after('comment_bg'); // preset key
|
||||
$table->string('profile_frame', 32)->nullable()->after('username_color'); // neon|fire|sakura|galaxy|gold|ice
|
||||
$table->string('profile_badge', 64)->nullable()->after('profile_frame'); // özel unvan metni
|
||||
$table->string('profile_bg', 32)->nullable()->after('profile_badge'); // animasyon stili
|
||||
$table->string('profile_theme', 32)->nullable()->after('profile_bg'); // tema anahtarı
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn([
|
||||
'gif_avatar', 'comment_bg', 'username_color',
|
||||
'profile_frame', 'profile_badge', 'profile_bg', 'profile_theme',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('membership_plans', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('membership_plans', 'is_public')) {
|
||||
$table->boolean('is_public')->default(true)->after('is_active');
|
||||
}
|
||||
if (!Schema::hasColumn('membership_plans', 'visible_until')) {
|
||||
$table->timestamp('visible_until')->nullable()->after('is_public');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('users', 'admin_badge')) {
|
||||
$table->string('admin_badge', 32)->nullable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('membership_plans', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('membership_plans', 'visible_until')) {
|
||||
$table->dropColumn('visible_until');
|
||||
}
|
||||
if (Schema::hasColumn('membership_plans', 'is_public')) {
|
||||
$table->dropColumn('is_public');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('users', 'admin_badge')) {
|
||||
$table->dropColumn('admin_badge');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('membership_plans', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('membership_plans', 'trial_days')) {
|
||||
$table->unsignedSmallInteger('trial_days')->default(0)->after('duration_days');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('users', 'profile_music_url')) {
|
||||
$table->string('profile_music_url', 500)->nullable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('membership_plans', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('membership_plans', 'trial_days')) {
|
||||
$table->dropColumn('trial_days');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('users', 'profile_music_url')) {
|
||||
$table->dropColumn('profile_music_url');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('animes', function (Blueprint $table) {
|
||||
// YouTube-benzeri trend skoru — TrendingController::computeScores() hesaplar
|
||||
$table->float('trending_score', 10, 2)->default(0)->after('trending_order');
|
||||
$table->index('trending_score');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('animes', function (Blueprint $table) {
|
||||
$table->dropColumn('trending_score');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('import_jobs', function (Blueprint $table) {
|
||||
// 2 = Cross-fill (mevcut anime, eksik kaynak ekleniyor)
|
||||
// 1 = Ongoing güncelleme
|
||||
// 0 = Yeni anime keşfi (varsayılan)
|
||||
$table->tinyInteger('priority')->default(0)->after('status');
|
||||
$table->index(['status', 'priority', 'id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('import_jobs', function (Blueprint $table) {
|
||||
$table->dropColumn('priority');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('anime_swipes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('anime_id')->constrained()->cascadeOnDelete();
|
||||
$table->enum('direction', ['like', 'skip']);
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->unique(['user_id', 'anime_id']);
|
||||
$table->index('user_id');
|
||||
});
|
||||
|
||||
Schema::table('animes', function (Blueprint $table) {
|
||||
$table->text('discovery_hook')->nullable()->after('description');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('anime_swipes');
|
||||
Schema::table('animes', function (Blueprint $table) {
|
||||
$table->dropColumn('discovery_hook');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('membership_plans', function (Blueprint $table) {
|
||||
$table->string('purchase_link', 1000)->nullable()->after('price');
|
||||
$table->string('badge_label', 32)->nullable()->after('sort_order');
|
||||
$table->string('accent_color', 16)->nullable()->after('badge_label');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('membership_plans', function (Blueprint $table) {
|
||||
$table->dropColumn(['purchase_link', 'badge_label', 'accent_color']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('activation_codes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('code', 32)->unique();
|
||||
$table->foreignId('plan_id')->constrained('membership_plans')->onDelete('cascade');
|
||||
$table->foreignId('used_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->timestamp('used_at')->nullable();
|
||||
$table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
$table->string('batch', 64)->nullable()->index();
|
||||
$table->text('notes')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('activation_codes');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('entry_effect', 16)->nullable()->after('profile_bg');
|
||||
$table->boolean('animated_banner')->default(false)->after('entry_effect');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn(['entry_effect', 'animated_banner']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('animes', function (Blueprint $table) {
|
||||
$table->boolean('is_dubbed')->default(false)->after('is_featured');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('animes', function (Blueprint $table) {
|
||||
$table->dropColumn('is_dubbed');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('voice_calls', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('caller_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->foreignId('callee_id')->constrained('users')->cascadeOnDelete();
|
||||
$table->string('channel_name')->unique();
|
||||
$table->enum('status', ['ringing', 'active', 'ended', 'declined'])->default('ringing');
|
||||
$table->timestamp('answered_at')->nullable();
|
||||
$table->timestamp('ended_at')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['callee_id', 'status']);
|
||||
$table->index(['caller_id', 'status']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('voice_calls');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('social_provider')->nullable()->after('email');
|
||||
$table->string('social_id')->nullable()->after('social_provider');
|
||||
$table->string('password')->nullable()->change();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn(['social_provider', 'social_id']);
|
||||
$table->string('password')->nullable(false)->change();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('video_sources', function (Blueprint $table) {
|
||||
$table->boolean('is_hevc')->default(false)->after('type');
|
||||
$table->timestamp('hevc_checked_at')->nullable()->after('is_hevc');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('video_sources', function (Blueprint $table) {
|
||||
$table->dropColumn(['is_hevc', 'hevc_checked_at']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('ads', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name', 120); // Admin içi tanımlayıcı isim
|
||||
$table->enum('type', ['video', 'banner'])->default('video');
|
||||
$table->string('placement', 30)->default('preroll'); // preroll | home_mid | home_bottom
|
||||
$table->string('file_path')->nullable(); // Yüklenen dosya (storage/public)
|
||||
$table->text('external_url')->nullable(); // Dış URL (mp4 veya görsel)
|
||||
$table->text('click_url')->nullable(); // Tıklama hedefi
|
||||
$table->unsignedSmallInteger('skip_after')->default(5); // Video: kaç sn sonra geçilebilir
|
||||
$table->unsignedSmallInteger('weight')->default(10); // Rotasyon ağırlığı (yüksek = sık)
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->timestamp('starts_at')->nullable(); // Zamanlama (opsiyonel)
|
||||
$table->timestamp('ends_at')->nullable();
|
||||
$table->unsignedBigInteger('impressions')->default(0);
|
||||
$table->unsignedBigInteger('clicks')->default(0);
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['type', 'is_active']);
|
||||
$table->index('placement');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('ads');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Achievement;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class AchievementSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$achievements = [
|
||||
['key'=>'first_login', 'title'=>'Hoş Geldin!', 'description'=>'İlk kez giriş yaptın', 'icon'=>'bi-door-open', 'color'=>'#79c0ff', 'condition_type'=>'first_login', 'condition_value'=>1],
|
||||
['key'=>'ep_1', 'title'=>'İzlemeye Başladım', 'description'=>'İlk bölümü %70 izledin', 'icon'=>'bi-play-circle', 'color'=>'#3fb950', 'condition_type'=>'episodes_watched', 'condition_value'=>1],
|
||||
['key'=>'ep_10', 'title'=>'Anime Meraklısı', 'description'=>'10 bölüm izledin', 'icon'=>'bi-collection-play', 'color'=>'#3fb950', 'condition_type'=>'episodes_watched', 'condition_value'=>10],
|
||||
['key'=>'ep_50', 'title'=>'Gerçek Otaku', 'description'=>'50 bölüm izledin', 'icon'=>'bi-trophy', 'color'=>'#f0883e', 'condition_type'=>'episodes_watched', 'condition_value'=>50],
|
||||
['key'=>'ep_100', 'title'=>'Efsane İzleyici', 'description'=>'100 bölüm izledin', 'icon'=>'bi-trophy-fill', 'color'=>'#ffd700', 'condition_type'=>'episodes_watched', 'condition_value'=>100],
|
||||
['key'=>'ep_500', 'title'=>'Ölümsüz Otaku', 'description'=>'500 bölüm izledin', 'icon'=>'bi-stars', 'color'=>'#a371f7', 'condition_type'=>'episodes_watched', 'condition_value'=>500],
|
||||
['key'=>'hours_10', 'title'=>'10 Saatlik Mara', 'description'=>'10 saat anime izledin', 'icon'=>'bi-clock-fill', 'color'=>'#79c0ff', 'condition_type'=>'hours_watched', 'condition_value'=>10],
|
||||
['key'=>'hours_100', 'title'=>'100 Saat Kulübü', 'description'=>'100 saat anime izledin', 'icon'=>'bi-alarm-fill', 'color'=>'#e84393', 'condition_type'=>'hours_watched', 'condition_value'=>100],
|
||||
['key'=>'watchlist_5', 'title'=>'Listeci', 'description'=>'5 anime listeye ekledin', 'icon'=>'bi-bookmark-fill', 'color'=>'#79c0ff', 'condition_type'=>'watchlist_count', 'condition_value'=>5],
|
||||
['key'=>'watchlist_20', 'title'=>'Liste Ustası', 'description'=>'20 anime listeye ekledin', 'icon'=>'bi-bookmarks-fill', 'color'=>'#a371f7', 'condition_type'=>'watchlist_count', 'condition_value'=>20],
|
||||
['key'=>'rated_1', 'title'=>'Eleştirmen', 'description'=>'İlk anime puanını verdin', 'icon'=>'bi-star-fill', 'color'=>'#ffd700', 'condition_type'=>'anime_rated', 'condition_value'=>1],
|
||||
['key'=>'rated_10', 'title'=>'Puan Makinesi', 'description'=>'10 animei puanladın', 'icon'=>'bi-star-half', 'color'=>'#ffd700', 'condition_type'=>'anime_rated', 'condition_value'=>10],
|
||||
['key'=>'request_1', 'title'=>'Talep Eden', 'description'=>'Anime isteği gönderdin', 'icon'=>'bi-plus-circle-fill','color'=>'#3fb950', 'condition_type'=>'request_sent', 'condition_value'=>1],
|
||||
];
|
||||
|
||||
foreach ($achievements as $ach) {
|
||||
Achievement::firstOrCreate(['key' => $ach['key']], $ach);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\MembershipPlan;
|
||||
use App\Models\PermissionSetting;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
// Admin kullanıcısı
|
||||
User::updateOrCreate(
|
||||
['email' => 'admin@animexe.com'],
|
||||
[
|
||||
'name' => 'Admin',
|
||||
'password' => Hash::make('admin123'),
|
||||
'role' => 'admin',
|
||||
]
|
||||
);
|
||||
|
||||
// Global izin ayarları
|
||||
$permissions = [
|
||||
['key' => 'can_watch', 'label' => 'Video İzleme', 'required_membership' => 'free', 'description' => 'Bölümleri izleyebilme'],
|
||||
['key' => 'can_comment', 'label' => 'Yorum Yapma', 'required_membership' => 'free', 'description' => 'Anime ve bölümlere yorum yapabilme'],
|
||||
['key' => 'can_rate', 'label' => 'Puanlama', 'required_membership' => 'free', 'description' => 'Anime puanlayabilme'],
|
||||
['key' => 'can_watchlist', 'label' => 'İzleme Listesi', 'required_membership' => 'free', 'description' => 'İzleme listesine ekleyebilme'],
|
||||
['key' => 'watch_hd', 'label' => '1080p İzleme', 'required_membership' => 'premium', 'description' => 'HD kalitede izleyebilme'],
|
||||
['key' => 'watch_4k', 'label' => '4K İzleme', 'required_membership' => 'premium', 'description' => '4K kalitede izleyebilme'],
|
||||
['key' => 'no_ads', 'label' => 'Reklamsız İzleme', 'required_membership' => 'premium', 'description' => 'Reklam gösterilmez'],
|
||||
['key' => 'early_access', 'label' => 'Erken Erişim', 'required_membership' => 'premium', 'description' => 'Yeni bölümlere erken erişim'],
|
||||
['key' => 'download', 'label' => 'İndirme', 'required_membership' => 'premium', 'description' => 'Bölümleri indirebilme'],
|
||||
];
|
||||
|
||||
foreach ($permissions as $perm) {
|
||||
PermissionSetting::updateOrCreate(['key' => $perm['key']], $perm);
|
||||
}
|
||||
|
||||
// Üyelik planları
|
||||
$plans = [
|
||||
['name' => 'Aylık Premium', 'slug' => 'aylik-premium', 'price' => 49.99, 'duration_days' => 30,
|
||||
'features' => ['Reklamsız izleme', '1080p HD', 'Sınırsız bölüm', 'Erken erişim'], 'is_active' => true, 'sort_order' => 1],
|
||||
['name' => '3 Aylık Premium', 'slug' => '3-aylik-premium', 'price' => 129.99, 'duration_days' => 90,
|
||||
'features' => ['Reklamsız izleme', '1080p HD', '4K kalite', 'Erken erişim', 'İndirme'], 'is_active' => true, 'sort_order' => 2],
|
||||
['name' => 'Yıllık Premium', 'slug' => 'yillik-premium', 'price' => 399.99, 'duration_days' => 365,
|
||||
'features' => ['Reklamsız izleme', '1080p HD', '4K kalite', 'Erken erişim', 'İndirme', 'Öncelikli destek'], 'is_active' => true, 'sort_order' => 3],
|
||||
];
|
||||
|
||||
foreach ($plans as $plan) {
|
||||
MembershipPlan::updateOrCreate(['slug' => $plan['slug']], $plan);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user