Files
animexe/public/migrate_run.php
T
2026-07-14 00:01:48 +03:00

725 lines
34 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// Güvenlik: secret key olmadan çalışmasın
if (($_GET['key'] ?? '') !== 'animexe2025') {
die('Yetkisiz erişim.');
}
define('LARAVEL_START', microtime(true));
require __DIR__ . '/../vendor/autoload.php';
$app = require __DIR__ . '/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel->bootstrap();
echo '<pre style="background:#111;color:#0f0;padding:20px;font-size:13px">';
echo "=== Animexe Migration & Seed ===\n\n";
$db = app('db');
$sch = app('db')->getSchemaBuilder();
$batch = ($db->table('migrations')->max('batch') ?? 0) + 1;
// Üretimde zaten uygulanmış ama migrations tablosuna kayıt edilmemiş migration'ları işaretle
// [ migration_name => kontrol fonksiyonu (true = zaten uygulanmış) ]
$alreadyApplied = [
'2024_01_01_000150_add_trending_to_animes' => fn() =>
$sch->hasColumn('animes', 'is_trending'),
'2024_01_01_000200_add_profile_fields_to_users' => fn() =>
$sch->hasColumn('users', 'bio'),
'2026_04_17_000001_create_blog_posts_table' => fn() =>
$sch->hasTable('blog_posts'),
'2024_01_01_000200_add_intro_times_to_episodes' => fn() =>
$sch->hasColumn('episodes', 'intro_start'),
'2026_04_17_200001_create_social_features_tables' => fn() =>
$sch->hasTable('episode_timestamp_comments'),
'2026_04_17_300000_create_community_features_tables' => fn() =>
$sch->hasTable('tribunals'),
'2026_04_17_310000_add_extra_sides_to_tribunals' => fn() =>
$sch->hasColumn('tribunals', 'extra_sides'),
'2026_04_18_000001_create_bot_protection_tables' => fn() =>
$sch->hasTable('analytics_bot_logs'),
'2026_04_19_000001_create_moderator_permissions_table' => fn() =>
$sch->hasTable('moderator_permissions') && $sch->hasTable('user_activity_logs'),
'2026_04_19_000002_add_bot_columns_to_analytics_pageviews' => fn() =>
$sch->hasColumn('analytics_pageviews', 'is_bot'),
'2026_04_19_000010_create_seo_keyword_tracker_table' => fn() =>
$sch->hasTable('seo_keyword_tracker'),
'2026_04_19_000011_create_seo_redirects_table' => fn() =>
$sch->hasTable('seo_redirects'),
'2026_04_19_000012_add_seo_columns_to_animes' => fn() =>
$sch->hasColumn('animes', 'seo_title'),
];
// Kolon yoksa direkt SQL ile ekle (artisan fallback)
if (!$sch->hasColumn('episodes', 'intro_start')) {
echo "▶ intro_start / intro_end kolonları ekleniyor...\n";
try {
$db->statement("ALTER TABLE `episodes` ADD COLUMN `intro_start` SMALLINT UNSIGNED NULL DEFAULT NULL AFTER `duration`, ADD COLUMN `intro_end` SMALLINT UNSIGNED NULL DEFAULT NULL AFTER `intro_start`");
$db->table('migrations')->insertOrIgnore(['migration' => '2024_01_01_000200_add_intro_times_to_episodes', 'batch' => $batch]);
echo "✓ Kolonlar eklendi\n\n";
} catch (\Throwable $e) {
echo "✗ HATA: " . $e->getMessage() . "\n\n";
}
} else {
echo "✓ intro_start / intro_end kolonları zaten mevcut\n\n";
}
foreach ($alreadyApplied as $migration => $check) {
$alreadyInDb = $db->table('migrations')->where('migration', $migration)->exists();
if ($alreadyInDb) {
echo "✓ Kayıtlı: $migration\n";
continue;
}
if ($check()) {
$db->table('migrations')->insert(['migration' => $migration, 'batch' => $batch]);
echo "⚡ Zaten uygulanmış, atlandı: $migration\n";
} else {
echo "⏳ Beklemede (migrate ile çalışacak): $migration\n";
}
}
echo "\n";
// Kritik: blog_posts tablosu yoksa direkt SQL ile oluştur (migration başarısız olsa bile)
if (!$sch->hasTable('blog_posts')) {
echo "▶ blog_posts tablosu oluşturuluyor (fallback)...\n";
try {
$db->statement("CREATE TABLE IF NOT EXISTS `blog_posts` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`slug` varchar(255) NOT NULL UNIQUE,
`excerpt` text,
`content` longtext,
`cover_image` varchar(255) DEFAULT NULL,
`focus_keyword` varchar(255) DEFAULT NULL,
`meta_title` varchar(255) DEFAULT NULL,
`meta_description` text,
`meta_keywords` varchar(255) DEFAULT NULL,
`status` enum('draft','published','generating') NOT NULL DEFAULT 'draft',
`ai_generated` tinyint(1) NOT NULL DEFAULT 0,
`anime_id` bigint unsigned DEFAULT NULL,
`linked_anime_ids` json DEFAULT NULL,
`faq` json DEFAULT NULL,
`views` int unsigned NOT NULL DEFAULT 0,
`reading_time` smallint unsigned NOT NULL DEFAULT 5,
`published_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
// migrations tablosuna kaydet
$db->table('migrations')->insert(['migration' => '2026_04_17_000001_create_blog_posts_table', 'batch' => $batch]);
echo "✓ blog_posts tablosu oluşturuldu\n\n";
} catch (\Throwable $e) {
echo "✗ blog_posts fallback HATA: " . $e->getMessage() . "\n\n";
}
} else {
echo "✓ blog_posts tablosu zaten mevcut\n\n";
}
// Sosyal özellikler tablolarını oluştur (fallback)
if (!$sch->hasTable('episode_timestamp_comments')) {
echo "▶ Sosyal özellikler tabloları oluşturuluyor...\n";
try {
$db->statement("CREATE TABLE IF NOT EXISTS `episode_timestamp_comments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`episode_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`timestamp_sec` smallint unsigned NOT NULL,
`body` varchar(100) NOT NULL,
`color` varchar(7) NOT NULL DEFAULT '#ffffff',
`is_hidden` tinyint(1) NOT NULL DEFAULT 0,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_ep_ts` (`episode_id`, `timestamp_sec`),
KEY `idx_user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->statement("CREATE TABLE IF NOT EXISTS `episode_predictions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`episode_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned NOT NULL,
`body` varchar(280) NOT NULL,
`is_correct` tinyint(1) DEFAULT NULL,
`vote_count` smallint unsigned NOT NULL DEFAULT 0,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_ep_user` (`episode_id`, `user_id`),
KEY `idx_ep_votes` (`episode_id`, `vote_count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->statement("CREATE TABLE IF NOT EXISTS `prediction_votes` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`prediction_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_pred_user` (`prediction_id`, `user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->statement("CREATE TABLE IF NOT EXISTS `watch_parties` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`room_code` varchar(10) NOT NULL,
`host_user_id` bigint unsigned NOT NULL,
`episode_id` bigint unsigned NOT NULL,
`current_sec` int unsigned NOT NULL DEFAULT 0,
`is_playing` tinyint(1) NOT NULL DEFAULT 0,
`synced_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`max_members` tinyint unsigned NOT NULL DEFAULT 10,
`is_private` tinyint(1) NOT NULL DEFAULT 0,
`password` varchar(60) DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_code` (`room_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->statement("CREATE TABLE IF NOT EXISTS `watch_party_members` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`party_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned NOT NULL,
`joined_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_ping` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_party_user` (`party_id`, `user_id`),
KEY `idx_party_ping` (`party_id`, `last_ping`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->statement("CREATE TABLE IF NOT EXISTS `first_watch_sessions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`episode_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned DEFAULT NULL,
`session_id` varchar(64) DEFAULT NULL,
`is_first_time` tinyint(1) NOT NULL DEFAULT 1,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_seen` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_ep_seen` (`episode_id`, `last_seen`),
KEY `idx_user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->table('migrations')->insertOrIgnore(['migration' => '2026_04_17_200001_create_social_features_tables', 'batch' => $batch]);
echo "✓ Sosyal özellikler tabloları oluşturuldu\n\n";
} catch (\Throwable $e) {
echo "✗ HATA: " . $e->getMessage() . "\n\n";
}
} else {
echo "✓ Sosyal özellikler tabloları zaten mevcut\n\n";
}
// Topluluk özellikleri tabloları (Mahkeme, Kapsül, Spoiler)
if (!$sch->hasTable('tribunals')) {
echo "▶ Topluluk özellikleri tabloları oluşturuluyor...\n";
try {
$db->statement("CREATE TABLE IF NOT EXISTS `tribunals` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`anime_id` bigint unsigned NOT NULL,
`episode_id` bigint unsigned DEFAULT NULL,
`created_by` bigint unsigned NOT NULL,
`question` varchar(280) NOT NULL,
`side_a` varchar(100) NOT NULL,
`side_b` varchar(100) NOT NULL,
`extra_sides` json DEFAULT NULL,
`status` enum('open','closed') NOT NULL DEFAULT 'open',
`verdict` varchar(1) DEFAULT NULL,
`closes_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_anime` (`anime_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->statement("CREATE TABLE IF NOT EXISTS `tribunal_votes` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`tribunal_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned NOT NULL,
`side` varchar(1) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_t_user` (`tribunal_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->statement("CREATE TABLE IF NOT EXISTS `tribunal_arguments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`tribunal_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned NOT NULL,
`side` varchar(1) NOT NULL,
`body` varchar(500) NOT NULL,
`vote_count` smallint unsigned NOT NULL DEFAULT 0,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_t_arg_user` (`tribunal_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->statement("CREATE TABLE IF NOT EXISTS `tribunal_argument_votes` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`argument_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_av_user` (`argument_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->statement("CREATE TABLE IF NOT EXISTS `time_capsules` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`anime_id` bigint unsigned NOT NULL,
`message` text NOT NULL,
`unlock_at` timestamp NOT NULL,
`opened_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_user_unlock` (`user_id`,`unlock_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->statement("CREATE TABLE IF NOT EXISTS `spoiler_boxes` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`episode_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned NOT NULL,
`body` text NOT NULL,
`is_spoiler` tinyint(1) NOT NULL DEFAULT 0,
`spoiler_score` tinyint NOT NULL DEFAULT 0,
`likes` smallint unsigned NOT NULL DEFAULT 0,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_ep` (`episode_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->statement("CREATE TABLE IF NOT EXISTS `spoiler_box_likes` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`box_id` bigint unsigned NOT NULL,
`user_id` bigint unsigned NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_box_user` (`box_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->statement("CREATE TABLE IF NOT EXISTS `mood_logs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned DEFAULT NULL,
`episode_id` bigint unsigned NOT NULL,
`mood` varchar(20) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->table('migrations')->insertOrIgnore(['migration' => '2026_04_17_300000_create_community_features_tables', 'batch' => $batch]);
echo "✓ Topluluk özellikleri tabloları oluşturuldu\n\n";
} catch (\Throwable $e) {
echo "✗ HATA: " . $e->getMessage() . "\n\n";
}
} else {
echo "✓ Topluluk özellikleri tabloları zaten mevcut\n\n";
}
// ── Bot koruma & gelişmiş analitik tabloları ─────────────────────────────────
if (!$sch->hasTable('analytics_bot_logs') || !$sch->hasTable('analytics_sessions') || !$sch->hasTable('blocked_ips')) {
echo "▶ Bot koruma tabloları oluşturuluyor...\n";
try {
if (!$sch->hasTable('analytics_bot_logs')) {
$db->statement("CREATE TABLE `analytics_bot_logs` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`ip` VARCHAR(45) NOT NULL,
`user_agent` VARCHAR(500) NULL,
`path` VARCHAR(500) NULL,
`method` VARCHAR(10) NULL,
`action` VARCHAR(20) NOT NULL DEFAULT 'allowed',
`bot_name` VARCHAR(100) NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_ip` (`ip`),
INDEX `idx_action` (`action`),
INDEX `idx_created` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
echo " ✓ analytics_bot_logs\n";
}
if (!$sch->hasTable('blocked_ips')) {
$db->statement("CREATE TABLE `blocked_ips` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`ip` VARCHAR(45) NOT NULL,
`reason` VARCHAR(255) NULL,
`auto_blocked` TINYINT(1) DEFAULT 0,
`blocked_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`expires_at` TIMESTAMP NULL,
UNIQUE KEY `uk_ip` (`ip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
echo " ✓ blocked_ips\n";
}
if (!$sch->hasTable('analytics_sessions')) {
$db->statement("CREATE TABLE `analytics_sessions` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`session_id` VARCHAR(100) NOT NULL,
`user_id` BIGINT UNSIGNED NULL,
`ip` VARCHAR(45) NULL,
`country` VARCHAR(100) NULL,
`city` VARCHAR(100) NULL,
`device` VARCHAR(20) NULL,
`browser` VARCHAR(50) NULL,
`referrer` VARCHAR(500) NULL,
`landing_page` VARCHAR(500) NULL,
`pages_visited` INT DEFAULT 1,
`total_seconds` INT DEFAULT 0,
`is_bot` TINYINT(1) DEFAULT 0,
`bot_type` VARCHAR(50) NULL,
`user_agent` VARCHAR(500) NULL,
`started_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`last_seen_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_session` (`session_id`),
INDEX `idx_started` (`started_at`),
INDEX `idx_bot` (`is_bot`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
echo " ✓ analytics_sessions\n";
}
$db->table('migrations')->insertOrIgnore(['migration' => '2026_04_18_000001_create_bot_protection_tables', 'batch' => $batch]);
echo "✓ Bot koruma tabloları hazır\n\n";
} catch (\Throwable $e) {
echo "✗ HATA: " . $e->getMessage() . "\n\n";
}
} else {
echo "✓ Bot koruma tabloları zaten mevcut\n\n";
}
// analytics_pageviews: is_bot, user_agent, time_on_page kolonları ekle
if ($sch->hasTable('analytics_pageviews')) {
$cols = ['is_bot' => 'TINYINT(1) DEFAULT 0', 'user_agent' => 'VARCHAR(500) NULL', 'time_on_page' => 'INT DEFAULT 0'];
foreach ($cols as $col => $def) {
if (!$sch->hasColumn('analytics_pageviews', $col)) {
try {
$db->statement("ALTER TABLE `analytics_pageviews` ADD COLUMN `{$col}` {$def}");
echo " ✓ analytics_pageviews.{$col} eklendi\n";
} catch (\Throwable $e) {
echo " ✗ {$col}: " . $e->getMessage() . "\n";
}
}
}
}
// ── Moderatör izinleri & kullanıcı aktivite logları ──────────────────────────
if (!$sch->hasTable('moderator_permissions')) {
echo "▶ moderator_permissions tablosu oluşturuluyor...\n";
try {
$db->statement("CREATE TABLE IF NOT EXISTS `moderator_permissions` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`permission` varchar(80) NOT NULL,
`granted_by` bigint unsigned NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_user_perm` (`user_id`, `permission`),
KEY `idx_user_id` (`user_id`),
CONSTRAINT `fk_modperm_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
$db->table('migrations')->insertOrIgnore(['migration' => '2026_04_19_000001_create_moderator_permissions_table', 'batch' => $batch]);
echo "✓ moderator_permissions tablosu oluşturuldu\n\n";
} catch (\Throwable $e) {
echo "✗ HATA: " . $e->getMessage() . "\n\n";
}
} else {
echo "✓ moderator_permissions tablosu zaten mevcut\n\n";
}
if (!$sch->hasTable('user_activity_logs')) {
echo "▶ user_activity_logs tablosu oluşturuluyor...\n";
try {
$db->statement("CREATE TABLE IF NOT EXISTS `user_activity_logs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NULL,
`session_id` varchar(64) NULL,
`action` varchar(60) NOT NULL,
`subject_type` varchar(60) NULL,
`subject_id` bigint unsigned NULL,
`ip` varchar(45) NULL,
`country` varchar(80) NULL,
`city` varchar(80) NULL,
`device` varchar(20) NULL,
`browser` varchar(50) NULL,
`user_agent` varchar(500) NULL,
`is_bot` tinyint(1) NOT NULL DEFAULT 0,
`meta` json NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_created` (`user_id`, `created_at`),
KEY `idx_action_created` (`action`, `created_at`),
KEY `idx_country` (`country`),
KEY `idx_is_bot` (`is_bot`),
KEY `idx_session` (`session_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
$db->table('migrations')->insertOrIgnore(['migration' => '2026_04_19_000001_create_moderator_permissions_table', 'batch' => $batch]);
echo "✓ user_activity_logs tablosu oluşturuldu\n\n";
} catch (\Throwable $e) {
echo "✗ HATA: " . $e->getMessage() . "\n\n";
}
} else {
echo "✓ user_activity_logs tablosu zaten mevcut\n\n";
}
// analytics_pageviews: is_bot, user_agent, time_on_page kolonları (önceki bloktan bağımsız tekrar kontrol)
if ($sch->hasTable('analytics_pageviews')) {
foreach (['is_bot' => 'TINYINT(1) DEFAULT 0', 'user_agent' => 'VARCHAR(500) NULL', 'time_on_page' => 'SMALLINT UNSIGNED DEFAULT 0'] as $col => $def) {
if (!$sch->hasColumn('analytics_pageviews', $col)) {
try {
$db->statement("ALTER TABLE `analytics_pageviews` ADD COLUMN `{$col}` {$def}");
echo " ✓ analytics_pageviews.{$col} eklendi\n";
} catch (\Throwable $e) {
echo " ✗ {$col}: " . $e->getMessage() . "\n";
}
}
}
$db->table('migrations')->insertOrIgnore(['migration' => '2026_04_19_000002_add_bot_columns_to_analytics_pageviews', 'batch' => $batch]);
}
echo "\n";
// ── SEO tabloları ─────────────────────────────────────────────────────────
if (!$sch->hasTable('seo_keyword_tracker')) {
echo "▶ seo_keyword_tracker tablosu oluşturuluyor...\n";
try {
$db->statement("CREATE TABLE IF NOT EXISTS `seo_keyword_tracker` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`keyword` varchar(255) NOT NULL,
`target_url` varchar(500) NULL,
`search_volume` int DEFAULT NULL,
`difficulty` tinyint unsigned DEFAULT NULL,
`notes` text NULL,
`created_at` timestamp NULL,
`updated_at` timestamp NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
$db->table('migrations')->insertOrIgnore(['migration' => '2026_04_19_000010_create_seo_keyword_tracker_table', 'batch' => $batch]);
echo "✓ seo_keyword_tracker oluşturuldu\n\n";
} catch (\Throwable $e) { echo "✗ HATA: " . $e->getMessage() . "\n\n"; }
} else { echo "✓ seo_keyword_tracker zaten mevcut\n\n"; }
if (!$sch->hasTable('seo_redirects')) {
echo "▶ seo_redirects tablosu oluşturuluyor...\n";
try {
$db->statement("CREATE TABLE IF NOT EXISTS `seo_redirects` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`from_path` varchar(500) NOT NULL,
`to_path` varchar(500) NOT NULL,
`type` smallint NOT NULL DEFAULT 301,
`hits` int NOT NULL DEFAULT 0,
`is_active` tinyint(1) NOT NULL DEFAULT 1,
`created_at` timestamp NULL,
`updated_at` timestamp NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `seo_redirects_from_unique` (`from_path`(191))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
$db->table('migrations')->insertOrIgnore(['migration' => '2026_04_19_000011_create_seo_redirects_table', 'batch' => $batch]);
echo "✓ seo_redirects oluşturuldu\n\n";
} catch (\Throwable $e) { echo "✗ HATA: " . $e->getMessage() . "\n\n"; }
} else { echo "✓ seo_redirects zaten mevcut\n\n"; }
foreach (['seo_title' => 'VARCHAR(100) NULL', 'seo_meta_desc' => 'VARCHAR(320) NULL', 'seo_keywords' => 'VARCHAR(500) NULL'] as $col => $def) {
if (!$sch->hasColumn('animes', $col)) {
try {
$db->statement("ALTER TABLE `animes` ADD COLUMN `{$col}` {$def}");
echo " ✓ animes.{$col} eklendi\n";
} catch (\Throwable $e) { echo " ✗ {$col}: " . $e->getMessage() . "\n"; }
}
}
$db->table('migrations')->insertOrIgnore(['migration' => '2026_04_19_000012_add_seo_columns_to_animes', 'batch' => $batch]);
echo "\n";
// extra_sides kolonu yoksa ekle (tribunals tablosu önceden oluşturulmuşsa)
if ($sch->hasTable('tribunals') && !$sch->hasColumn('tribunals', 'extra_sides')) {
echo "▶ tribunals.extra_sides kolonu ekleniyor...\n";
try {
$db->statement("ALTER TABLE `tribunals` ADD COLUMN `extra_sides` json DEFAULT NULL AFTER `side_b`");
$db->table('migrations')->insertOrIgnore(['migration' => '2026_04_17_310000_add_extra_sides_to_tribunals', 'batch' => $batch]);
echo "✓ extra_sides eklendi\n\n";
} catch (\Throwable $e) {
echo "✗ HATA: " . $e->getMessage() . "\n\n";
}
} else {
echo "✓ tribunals.extra_sides zaten mevcut\n\n";
}
// ── Animecix scraper tabloları ────────────────────────────────────────────────
// 1. import_jobs: source, animecix_title_id, animecix_slug kolonları
if (!$sch->hasColumn('import_jobs', 'animecix_title_id')) {
echo "▶ import_jobs animecix kolonları ekleniyor...\n";
try {
if (!$sch->hasColumn('import_jobs', 'source')) {
$db->statement("ALTER TABLE `import_jobs` ADD COLUMN `source` VARCHAR(30) NOT NULL DEFAULT 'anizium' AFTER `id`");
}
$db->statement("ALTER TABLE `import_jobs` ADD COLUMN `animecix_title_id` VARCHAR(50) NULL AFTER `source`");
$db->statement("ALTER TABLE `import_jobs` ADD COLUMN `animecix_slug` VARCHAR(300) NULL AFTER `animecix_title_id`");
$db->table('migrations')->insertOrIgnore(['migration' => '2024_01_01_000120_add_animecix_fields_to_import_jobs', 'batch' => $batch]);
echo "✓ import_jobs animecix kolonları eklendi\n\n";
} catch (\Throwable $e) {
echo "✗ HATA: " . $e->getMessage() . "\n\n";
}
} else {
echo "✓ import_jobs animecix kolonları zaten mevcut\n\n";
}
// 2. video_sources tablosu
if (!$sch->hasTable('video_sources')) {
echo "▶ video_sources tablosu oluşturuluyor...\n";
try {
$db->statement("CREATE TABLE IF NOT EXISTS `video_sources` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`episode_id` bigint unsigned NOT NULL,
`label` varchar(120) NOT NULL DEFAULT '',
`url` varchar(2000) NOT NULL,
`type` enum('mp4','hls','embed') NOT NULL DEFAULT 'mp4',
`quality` varchar(20) NOT NULL DEFAULT '',
`translator_id` varchar(60) NULL,
`sort_order` smallint unsigned NOT NULL DEFAULT 0,
`is_default` tinyint(1) NOT NULL DEFAULT 0,
`source` varchar(30) NOT NULL DEFAULT 'animecix',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_episode_sort` (`episode_id`, `sort_order`),
KEY `idx_source` (`source`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->table('migrations')->insertOrIgnore(['migration' => '2024_01_01_000121_create_video_sources_table', 'batch' => $batch]);
echo "✓ video_sources tablosu oluşturuldu\n\n";
} catch (\Throwable $e) {
echo "✗ HATA: " . $e->getMessage() . "\n\n";
}
} else {
echo "✓ video_sources tablosu zaten mevcut\n\n";
}
// 3. episodes.source ENUM'a 'animecix' ekle
try {
$enumCheck = $db->select("SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'episodes' AND COLUMN_NAME = 'source'");
$enumDef = $enumCheck[0]->COLUMN_TYPE ?? '';
if (!str_contains($enumDef, 'animecix')) {
echo "▶ episodes.source ENUM'a animecix ekleniyor...\n";
$db->statement("ALTER TABLE `episodes` MODIFY COLUMN `source` ENUM('bunnycdn','external','direct','anizium','animecix') NOT NULL DEFAULT 'bunnycdn'");
$db->table('migrations')->insertOrIgnore(['migration' => '2024_01_01_000122_add_animecix_to_episodes_source_enum', 'batch' => $batch]);
echo "✓ episodes.source güncellendi\n\n";
} else {
echo "✓ episodes.source zaten animecix içeriyor\n\n";
}
} catch (\Throwable $e) {
echo "✗ HATA: " . $e->getMessage() . "\n\n";
}
// 4. nextJob() için: import_jobs.source kolonu varsa INDEX ekle (performans)
try {
$idxCheck = $db->select("SHOW INDEX FROM import_jobs WHERE Key_name = 'idx_source_status'");
if (empty($idxCheck) && $sch->hasColumn('import_jobs', 'source')) {
$db->statement("ALTER TABLE `import_jobs` ADD INDEX `idx_source_status` (`source`, `status`)");
echo "✓ import_jobs(source, status) index eklendi\n\n";
}
} catch (\Throwable $e) {}
// ── 2026_05_14_000001: membership_plans.is_public, visible_until / users.admin_badge ──
echo "▶ Plan görünürlük & admin rozet kolonları kontrol ediliyor...\n";
$planVis = [
'is_public' => "TINYINT(1) NOT NULL DEFAULT 1 AFTER `is_active`",
'visible_until' => "TIMESTAMP NULL DEFAULT NULL AFTER `is_public`",
];
foreach ($planVis as $col => $def) {
if (!$sch->hasColumn('membership_plans', $col)) {
try {
$db->statement("ALTER TABLE `membership_plans` ADD COLUMN `{$col}` {$def}");
echo " ✓ membership_plans.{$col} eklendi\n";
} catch (\Throwable $e) { echo " ✗ {$col}: " . $e->getMessage() . "\n"; }
} else {
echo " ✓ membership_plans.{$col} zaten mevcut\n";
}
}
if (!$sch->hasColumn('users', 'admin_badge')) {
try {
$db->statement("ALTER TABLE `users` ADD COLUMN `admin_badge` VARCHAR(32) NULL");
echo " ✓ users.admin_badge eklendi\n";
} catch (\Throwable $e) { echo " ✗ admin_badge: " . $e->getMessage() . "\n"; }
} else {
echo " ✓ users.admin_badge zaten mevcut\n";
}
$db->table('migrations')->insertOrIgnore(['migration' => '2026_05_14_000001_add_plan_visibility_and_admin_badge', 'batch' => $batch]);
echo "\n";
// ── 2026_05_14_000002: membership_plans.trial_days / users.profile_music_url ──
echo "▶ Deneme süresi & profil müzik kolonları kontrol ediliyor...\n";
if (!$sch->hasColumn('membership_plans', 'trial_days')) {
try {
$db->statement("ALTER TABLE `membership_plans` ADD COLUMN `trial_days` SMALLINT UNSIGNED NOT NULL DEFAULT 0 AFTER `duration_days`");
echo " ✓ membership_plans.trial_days eklendi\n";
} catch (\Throwable $e) { echo " ✗ trial_days: " . $e->getMessage() . "\n"; }
} else {
echo " ✓ membership_plans.trial_days zaten mevcut\n";
}
if (!$sch->hasColumn('users', 'profile_music_url')) {
try {
$db->statement("ALTER TABLE `users` ADD COLUMN `profile_music_url` VARCHAR(500) NULL");
echo " ✓ users.profile_music_url eklendi\n";
} catch (\Throwable $e) { echo " ✗ profile_music_url: " . $e->getMessage() . "\n"; }
} else {
echo " ✓ users.profile_music_url zaten mevcut\n";
}
$db->table('migrations')->insertOrIgnore(['migration' => '2026_05_14_000002_add_trial_days_and_profile_music', 'batch' => $batch]);
echo "\n";
// ── payments tablosu ──────────────────────────────────────────────────────────
if (!$sch->hasTable('payments')) {
echo "▶ payments tablosu oluşturuluyor...\n";
try {
$db->statement("CREATE TABLE IF NOT EXISTS `payments` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`plan_id` bigint unsigned NOT NULL,
`conversation_id` varchar(100) NOT NULL,
`token` varchar(200) NULL,
`amount` decimal(10,2) NOT NULL,
`status` enum('pending','success','failed') NOT NULL DEFAULT 'pending',
`iyzico_payment_id` varchar(100) NULL,
`error_message` text NULL,
`paid_at` timestamp NULL,
`created_at` timestamp NULL,
`updated_at` timestamp NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_conversation` (`conversation_id`),
KEY `idx_user` (`user_id`),
KEY `idx_token` (`token`(191)),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
$db->table('migrations')->insertOrIgnore(['migration' => '2026_05_14_000003_create_payments_table', 'batch' => $batch]);
echo "✓ payments tablosu oluşturuldu\n\n";
} catch (\Throwable $e) {
echo "✗ HATA: " . $e->getMessage() . "\n\n";
}
} else {
echo "✓ payments tablosu zaten mevcut\n\n";
}
$commands = [
['migrate', ['--force' => true]],
['db:seed', ['--class' => 'AchievementSeeder', '--force' => true]],
['config:clear', []],
['view:clear', []],
['route:clear', []],
];
foreach ($commands as [$cmd, $args]) {
echo "▶ php artisan $cmd " . implode(' ', array_keys($args)) . "\n";
try {
Illuminate\Support\Facades\Artisan::call($cmd, $args);
echo Illuminate\Support\Facades\Artisan::output();
echo "✓ Tamam\n\n";
} catch (\Throwable $e) {
echo "✗ HATA: " . $e->getMessage() . "\n\n";
}
}
echo "=== Tamamlandı ===\n";
echo '</pre>';