Initial commit: Animexe Laravel platform

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 00:01:48 +03:00
co-authored by Claude Opus 4.8
commit a63515cfc6
366 changed files with 74773 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Handle X-XSRF-Token Header
RewriteCond %{HTTP:x-xsrf-token} .
RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
# php -- BEGIN cPanel-generated handler, do not edit
# Set the “ea-php84” package as the default “PHP” programming language.
<IfModule mime_module>
AddHandler application/x-httpd-ea-php84 .php .php8 .phtml
</IfModule>
# php -- END cPanel-generated handler, do not edit
Binary file not shown.
+113
View File
@@ -0,0 +1,113 @@
const WORKER_PATH = '/cf-proxy';
export default {
async fetch(request) {
const url = new URL(request.url);
if (request.method === 'OPTIONS') {
return new Response(null, { status: 204, headers: corsHeaders() });
}
const uParam = url.searchParams.get('u') || '';
const refParam = url.searchParams.get('ref') || '';
let targetUrl;
try {
targetUrl = atob(uParam);
if (!targetUrl.startsWith('http')) throw 0;
} catch {
return new Response('Missing or invalid u param', { status: 400 });
}
let referer;
try {
referer = refParam ? atob(refParam) : new URL(targetUrl).origin + '/';
if (!referer.endsWith('/')) referer += '/';
} catch {
referer = new URL(targetUrl).origin + '/';
}
let resp;
try {
resp = await fetch(targetUrl, {
headers: {
'Referer': referer,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
'Accept': '*/*',
'Accept-Language': 'tr-TR,tr;q=0.9,en;q=0.8',
},
});
} catch (e) {
return new Response('Upstream fetch failed: ' + e.message, { status: 502 });
}
if (!resp.ok && resp.status !== 206) {
// Referer ile de olmadıysa referer'sız dene (bazı CDN'ler Cloudflare'dan gelen Referer'ı reddeder)
try {
const fallbackResp = await fetch(targetUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
'Accept': '*/*',
},
});
if (fallbackResp.ok || fallbackResp.status === 206) {
resp = fallbackResp;
}
} catch {}
}
const ct = resp.headers.get('content-type') || '';
const isM3u8 = ct.includes('mpegurl') || targetUrl.toLowerCase().includes('.m3u8');
if (isM3u8) {
const body = await resp.text();
if (body.trimStart().startsWith('#EXTM3U')) {
const baseDir = targetUrl.substring(0, targetUrl.lastIndexOf('/') + 1);
const workerBase = url.origin + WORKER_PATH + '?u=';
const refSuffix = '&ref=' + btoa(referer);
const rewritten = body.split('\n').map(line => {
const t = line.trimEnd();
if (t === '') return '';
if (t.startsWith('#')) {
return t.replace(/URI="([^"]+)"/g, (_, uri) => {
const abs = uri.startsWith('http') ? uri : baseDir + uri;
return `URI="${workerBase}${btoa(abs)}${refSuffix}"`;
});
}
const abs = t.startsWith('http') ? t : baseDir + t;
return workerBase + btoa(abs) + refSuffix;
}).join('\n');
return new Response(rewritten, {
status: 200,
headers: { ...corsHeaders(), 'Content-Type': 'application/vnd.apple.mpegurl', 'Cache-Control': 'no-cache' },
});
}
// m3u8 gibi görünüp değil — binary olarak geçir
return new Response(body, {
status: resp.status,
headers: { ...corsHeaders(), 'Content-Type': ct || 'video/mp2t' },
});
}
// Segment (.ts, .key, .png vb.) — body'yi doğrudan stream et (belleğe alma)
let segCt = ct;
if (!segCt || segCt.startsWith('image/') || segCt === 'application/octet-stream') {
segCt = 'video/mp2t';
}
return new Response(resp.body, {
status: resp.status,
headers: { ...corsHeaders(), 'Content-Type': segCt, 'Cache-Control': 'public, max-age=3600' },
});
}
};
function corsHeaders() {
return {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Range, Origin',
'Access-Control-Expose-Headers': 'Content-Length, Content-Range, Content-Type',
};
}
+10
View File
@@ -0,0 +1,10 @@
<?php
define('LARAVEL_START', microtime(true));
require __DIR__.'/../vendor/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
\Illuminate\Support\Facades\Artisan::call('config:clear');
\Illuminate\Support\Facades\Artisan::call('route:clear');
\Illuminate\Support\Facades\Artisan::call('view:clear');
\Illuminate\Support\Facades\Artisan::call('cache:clear');
echo 'Cache cleared. ' . date('H:i:s');
File diff suppressed because it is too large Load Diff
+13
View File
@@ -0,0 +1,13 @@
[17-Apr-2026 03:26:39 UTC] PHP Parse error: syntax error, unexpected token "`" in /home/karaonth/animexe.com/public/migrate_run.php on line 136
[17-Apr-2026 03:26:40 UTC] PHP Parse error: syntax error, unexpected token "`" in /home/karaonth/animexe.com/public/migrate_run.php on line 136
[17-Apr-2026 03:27:21 UTC] PHP Parse error: syntax error, unexpected token "`" in /home/karaonth/animexe.com/public/migrate_run.php on line 138
[17-Apr-2026 03:27:22 UTC] PHP Parse error: syntax error, unexpected token "`" in /home/karaonth/animexe.com/public/migrate_run.php on line 138
[17-Apr-2026 11:19:53 UTC] PHP Fatal error: Uncaught Error: Class "Artisan" not found in /home/karaonth/animexe.com/public/clearcache.php:5
Stack trace:
#0 {main}
thrown in /home/karaonth/animexe.com/public/clearcache.php on line 5
[17-Apr-2026 11:19:54 UTC] PHP Fatal error: Uncaught Error: Class "Artisan" not found in /home/karaonth/animexe.com/public/clearcache.php:5
Stack trace:
#0 {main}
thrown in /home/karaonth/animexe.com/public/clearcache.php on line 5
[28-Apr-2026 20:58:12 UTC] PHP Warning: include(/home/karaonth/animexe.com/vendor/laravel/framework/src/Illuminate/Collections/Arr.php): Failed to open stream: No such file or directory in /home/karaonth/animexe.com/vendor/composer/ClassLoader.php on line 576
Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

+154
View File
@@ -0,0 +1,154 @@
<?php
/**
* HLS Proxy sunucu taraflı CORS bypass
* Kullanım: /hls-proxy.php?url=https://u.aniziumserver.sbs/.../master.m3u8&ref=https://anizium.co/
*/
$ALLOWED_DOMAINS = [
'aniziumserver.sbs',
'aniziumserver.site',
'aniziumserver.com',
'misakina.asia',
'misakina.cfd',
'irtau1.online',
'tsuriko-1.asia', 'tsuriko-2.asia', 'tsuriko-3.asia',
'rhyzoku-2.asia', 'rhyzoku-4.asia',
'kamadotanjiro.asia',
'uryuishida.asia',
'tohru.icu', 'tohru.cyou',
'zappy-net.store',
'pixel-quirk.shop',
'tau-video.xyz',
];
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, OPTIONS');
header('Access-Control-Allow-Headers: Range, Origin');
header('Access-Control-Max-Age: 86400');
exit;
}
$url = trim($_GET['url'] ?? '');
if (!$url || !str_starts_with($url, 'http')) { http_response_code(400); die('url parametresi eksik'); }
$parsed = parse_url($url);
$host = strtolower($parsed['host'] ?? '');
$allowed = false;
foreach ($ALLOWED_DOMAINS as $d) {
if ($host === $d || str_ends_with($host, '.' . $d)) { $allowed = true; break; }
}
if (!$allowed) { http_response_code(403); die('Bu domain proxy edilmiyor: ' . htmlspecialchars($host)); }
$referer = trim($_GET['ref'] ?? 'https://anizium.co/');
if (!str_ends_with($referer, '/')) $referer .= '/';
$origin = rtrim($referer, '/'); // Origin header referer'dan türet (hotlink koruması Origin de kontrol edebilir)
// Tek istek yürüten yardımcı — 403'te farklı header setiyle tekrar denemek için
$doFetch = function (string $url, array $headers) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_ENCODING => '', // gzip/br otomatik çöz
]);
$raw = curl_exec($ch);
$info = [
'code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
'hsize' => curl_getinfo($ch, CURLINFO_HEADER_SIZE),
'ctype' => curl_getinfo($ch, CURLINFO_CONTENT_TYPE) ?? '',
'final' => curl_getinfo($ch, CURLINFO_EFFECTIVE_URL),
'err' => curl_error($ch),
];
curl_close($ch);
return [$raw, $info];
};
$UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
$baseHeaders = [
'User-Agent: ' . $UA,
'Accept: */*',
'Accept-Language: tr-TR,tr;q=0.9,en;q=0.8',
'Accept-Encoding: identity',
'Referer: ' . $referer,
'Origin: ' . $origin,
'Sec-Fetch-Dest: empty',
'Sec-Fetch-Mode: cors',
'Sec-Fetch-Site: cross-site',
];
if (!empty($_SERVER['HTTP_RANGE'])) {
$baseHeaders[] = 'Range: ' . $_SERVER['HTTP_RANGE'];
}
[$raw, $info] = $doFetch($url, $baseHeaders);
// 403/401 → hotlink koruması olabilir; Origin'siz ve sadece Referer ile bir kez daha dene
if (in_array($info['code'], [401, 403], true)) {
$retryHeaders = array_values(array_filter($baseHeaders, function ($h) {
return !preg_match('/^(Origin|Sec-Fetch-):/i', $h);
}));
[$raw2, $info2] = $doFetch($url, $retryHeaders);
if (!in_array($info2['code'], [401, 403], true) && $raw2 !== false) {
$raw = $raw2; $info = $info2;
}
}
$httpCode = $info['code'];
$hdrSize = $info['hsize'];
$ctype = $info['ctype'];
$finalUrl = $info['final'];
$curlErr = $info['err'];
if ($raw === false || $curlErr) { http_response_code(502); die('Upstream hatası: ' . $curlErr); }
$body = substr($raw, $hdrSize);
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, OPTIONS');
header('Access-Control-Allow-Headers: Range');
header('Access-Control-Expose-Headers: Content-Length, Content-Range, Content-Type');
http_response_code($httpCode);
$isM3u8 = str_contains($ctype, 'mpegurl')
|| preg_match('/\.m3u8(\?|$)/i', strtok($url, '#'));
if ($isM3u8 && str_starts_with(ltrim($body), '#EXTM3U')) {
header('Content-Type: application/vnd.apple.mpegurl');
header('Cache-Control: no-cache');
$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$proxyBase = $scheme . '://' . $_SERVER['HTTP_HOST'] . '/hls-proxy.php?ref=' . urlencode($referer) . '&url=';
$baseDir = dirname($finalUrl) . '/';
$output = [];
foreach (explode("\n", $body) as $line) {
$line = rtrim($line, "\r");
if ($line === '') { $output[] = ''; continue; }
if (str_starts_with($line, '#')) {
$line = preg_replace_callback('/URI="([^"]+)"/', function ($m) use ($baseDir, $proxyBase) {
$uri = $m[1];
if (!str_starts_with($uri, 'http')) $uri = $baseDir . $uri;
return 'URI="' . $proxyBase . urlencode($uri) . '"';
}, $line);
$output[] = $line;
} else {
if (!str_starts_with($line, 'http')) $line = $baseDir . $line;
$output[] = $proxyBase . urlencode($line);
}
}
echo implode("\n", $output);
} else {
if ($ctype) header('Content-Type: ' . $ctype);
$len = strlen($body);
if ($len) header('Content-Length: ' . $len);
echo $body;
}
+20
View File
@@ -0,0 +1,20 @@
<?php
use Illuminate\Foundation\Application;
use Illuminate\Http\Request;
define('LARAVEL_START', microtime(true));
// Determine if the application is in maintenance mode...
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
require $maintenance;
}
// Register the Composer autoloader...
require __DIR__.'/../vendor/autoload.php';
// Bootstrap Laravel and handle the request...
/** @var Application $app */
$app = require_once __DIR__.'/../bootstrap/app.php';
$app->handleRequest(Request::capture());
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+44
View File
@@ -0,0 +1,44 @@
{
"name": "Animexe",
"short_name": "Animexe",
"description": "Türkçe anime izle — ücretsiz HD kalitede",
"start_url": "/",
"display": "standalone",
"background_color": "#06060d",
"theme_color": "#06060d",
"orientation": "any",
"lang": "tr",
"scope": "/",
"icons": [
{
"src": "/logo.jpg",
"sizes": "192x192",
"type": "image/jpeg",
"purpose": "any maskable"
},
{
"src": "/logo.jpg",
"sizes": "512x512",
"type": "image/jpeg",
"purpose": "any maskable"
}
],
"categories": ["entertainment"],
"screenshots": [],
"shortcuts": [
{
"name": "Ara",
"short_name": "Ara",
"description": "Anime ara",
"url": "/search",
"icons": [{ "src": "/logo.jpg", "sizes": "96x96" }]
},
{
"name": "AI Hub",
"short_name": "AI",
"description": "AI anime asistanı",
"url": "/ai",
"icons": [{ "src": "/logo.jpg", "sizes": "96x96" }]
}
]
}
+724
View File
@@ -0,0 +1,724 @@
<?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>';
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
xmlns:moz="http://www.mozilla.org/2006/browser/search/">
<ShortName>Animexe</ShortName>
<Description>Animexe'de Türkçe anime ara ve izle</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16" type="image/x-icon">/favicon.ico</Image>
<Url type="text/html" method="get" template="/search?q={searchTerms}"/>
<Url type="application/opensearchdescription+xml" rel="self" template="/opensearch.xml"/>
<moz:SearchForm>/search</moz:SearchForm>
<Language>tr-TR</Language>
<Tags>anime izle türkçe altyazı</Tags>
</OpenSearchDescription>
+58
View File
@@ -0,0 +1,58 @@
# Animexe — robots.txt
# Otomatik yönetim: Admin > SEO Paneli üzerinden düzenlenebilir.
User-agent: *
Allow: /
Disallow: /admin/
Disallow: /api/
Disallow: /profile
Disallow: /profile/settings
Disallow: /watchlist
Disallow: /notifications
Disallow: /ai
Disallow: /watch/
Disallow: /vtt-proxy
Disallow: /track/
Disallow: /comments
Crawl-delay: 1
User-agent: Googlebot
Allow: /
Disallow: /admin/
Disallow: /api/
User-agent: Bingbot
Allow: /
Disallow: /admin/
Disallow: /api/
# AI eğitim botlarını engelle (standart User-agent yöntemi)
User-agent: GPTBot
Disallow: /
User-agent: ChatGPT-User
Disallow: /
User-agent: CCBot
Disallow: /
User-agent: anthropic-ai
Disallow: /
User-agent: Claude-Web
Disallow: /
User-agent: cohere-ai
Disallow: /
User-agent: Omgili
Disallow: /
User-agent: FacebookBot
Disallow: /
Sitemap: https://animexe.com/sitemap.xml
Sitemap: https://animexe.com/sitemap-main.xml
Sitemap: https://animexe.com/sitemap-animes.xml
Sitemap: https://animexe.com/sitemap-videos.xml
Sitemap: https://animexe.com/sitemap-blog.xml
+30
View File
@@ -0,0 +1,30 @@
<?php
// KURULUM SONRASI HEMEN SİL!
if (($_GET['key'] ?? '') !== 'animexe-setup-2024') {
die('Yetkisiz erişim.');
}
define('LARAVEL_START', microtime(true));
require __DIR__.'/../vendor/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
echo '<pre>';
$kernel->call('migrate', ['--force' => true]);
echo "MIGRATE:\n" . $kernel->output() . "\n";
$kernel->call('storage:link');
echo "STORAGE LINK:\n" . $kernel->output() . "\n";
$kernel->call('config:cache');
echo "CONFIG CACHE:\n" . $kernel->output() . "\n";
$kernel->call('route:cache');
echo "ROUTE CACHE:\n" . $kernel->output() . "\n";
$kernel->call('view:cache');
echo "VIEW CACHE:\n" . $kernel->output() . "\n";
echo '</pre>';
echo '<h3 style="color:red">BU DOSYAYI HEMEN SİL! /public/setup.php</h3>';