Initial commit: Animexe Laravel platform
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+223
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\Api\ImportApiController;
|
||||
use App\Http\Controllers\Api\AuthApiController;
|
||||
use App\Http\Controllers\Api\AnimeApiController;
|
||||
use App\Http\Controllers\Api\UserApiController;
|
||||
use App\Http\Controllers\Api\CommentApiController;
|
||||
use App\Http\Controllers\Api\AiApiController;
|
||||
use App\Http\Controllers\Api\SocialApiController;
|
||||
use App\Http\Controllers\Api\TribunalApiController;
|
||||
use App\Http\Controllers\Api\MessageApiController;
|
||||
use App\Http\Controllers\Api\PlanApiController;
|
||||
|
||||
// ── Import API (internal Python scraper) ─────────────────────────────────────
|
||||
Route::middleware('import.api')->prefix('import')->group(function () {
|
||||
Route::get('test', [ImportApiController::class, 'test']);
|
||||
Route::get('stats', [ImportApiController::class, 'stats']);
|
||||
Route::get('settings', [ImportApiController::class, 'settings']);
|
||||
Route::get('next-job', [ImportApiController::class, 'nextJob']);
|
||||
Route::post('jobs', [ImportApiController::class, 'createJob']); // AUTO-IMPORT
|
||||
Route::get('imported-ids', [ImportApiController::class, 'importedIds']);
|
||||
Route::get('imported-titles', [ImportApiController::class, 'importedTitles']);
|
||||
Route::get('ongoing', [ImportApiController::class, 'ongoingAnimes']);
|
||||
Route::get('all-animes', [ImportApiController::class, 'allAniziumAnimes']);
|
||||
Route::get('jobs/{job}', [ImportApiController::class, 'getJob']);
|
||||
Route::post('jobs/{job}/status', [ImportApiController::class, 'updateStatus']);
|
||||
Route::post('jobs/{job}/episode', [ImportApiController::class, 'saveEpisode']);
|
||||
Route::post('jobs/{job}/subtitle', [ImportApiController::class, 'saveSubtitle']);
|
||||
Route::get('jobs/{job}/done-episodes', [ImportApiController::class, 'doneEpisodes']);
|
||||
|
||||
// Altyazı yenileme
|
||||
Route::get('anizium-episodes', [ImportApiController::class, 'aniziumEpisodes']);
|
||||
Route::post('subtitle-direct', [ImportApiController::class, 'saveSubtitleDirect']);
|
||||
|
||||
// Sağlık kontrolü + deaktivasyon
|
||||
Route::get('anizium-health', [ImportApiController::class, 'aniziumHealthData']);
|
||||
Route::post('deactivate-anime', [ImportApiController::class, 'deactivateAnime']);
|
||||
|
||||
// Animecix-specific
|
||||
Route::get('animecix/pending', [ImportApiController::class, 'animecixPendingJobs']);
|
||||
Route::post('episodes/{episode}/video-sources', [ImportApiController::class, 'saveVideoSources']);
|
||||
|
||||
// Çapraz kaynak desteği
|
||||
Route::get('anime/lookup', [ImportApiController::class, 'animeLookup']);
|
||||
Route::get('published-animes-with-jobs', [ImportApiController::class, 'publishedAnimesWithJobs']);
|
||||
});
|
||||
|
||||
// ── Mobile App API ────────────────────────────────────────────────────────────
|
||||
|
||||
// App status (public — called on startup to check maintenance/force-update)
|
||||
Route::get('app-status', function () {
|
||||
$minVersion = \App\Models\Setting::get('mobile_min_version', '1.0.0');
|
||||
$currentVersion = \App\Models\Setting::get('mobile_current_version', '1.0.0');
|
||||
$maintenance = \App\Models\Setting::get('mobile_maintenance_mode', '0') === '1';
|
||||
$maintMsg = \App\Models\Setting::get('mobile_maintenance_message', 'Uygulama şu anda bakımda.');
|
||||
$apkUrl = \App\Models\Setting::get('mobile_apk_url', '');
|
||||
$forceMsg = \App\Models\Setting::get('mobile_force_update_msg', 'Lütfen uygulamayı güncelleyin.');
|
||||
|
||||
// Determine force_update by comparing appVersion (sent as ?v=x.y.z) with minVersion
|
||||
$appVersion = request('v', '0.0.0');
|
||||
$forceUpdate = version_compare($appVersion, $minVersion, '<');
|
||||
|
||||
return response()->json([
|
||||
'maintenance' => $maintenance,
|
||||
'maintenance_message' => $maintMsg,
|
||||
'min_version' => $minVersion,
|
||||
'current_version' => $currentVersion,
|
||||
'apk_url' => $apkUrl ?: null,
|
||||
'force_update' => $forceUpdate,
|
||||
'force_update_message'=> $forceMsg,
|
||||
]);
|
||||
});
|
||||
|
||||
// Auth (public)
|
||||
Route::prefix('auth')->group(function () {
|
||||
Route::post('register', [AuthApiController::class, 'register']);
|
||||
Route::post('login', [AuthApiController::class, 'login']);
|
||||
});
|
||||
|
||||
// Public content
|
||||
Route::get('home', [AnimeApiController::class, 'home']);
|
||||
Route::get('animes', [AnimeApiController::class, 'index']);
|
||||
Route::get('animes/{slug}', [AnimeApiController::class, 'show']);
|
||||
Route::get('genres', [AnimeApiController::class, 'genres']);
|
||||
Route::get('genres/{slug}', [AnimeApiController::class, 'genre']);
|
||||
Route::get('watch/{slug}/{season}/{episode}', [AnimeApiController::class, 'watch']);
|
||||
|
||||
// AniSkip (public — no auth needed, does Jikan+AniSkip lookup with caching)
|
||||
Route::get('aniskip/{slug}/{season}/{episode}', [AnimeApiController::class, 'aniSkip']);
|
||||
|
||||
// Skip segments (public — no auth needed)
|
||||
Route::get('episodes/{episode}/skip-segments', [AnimeApiController::class, 'skipSegments']);
|
||||
Route::post('episodes/{episode}/skip-event', [AnimeApiController::class, 'recordSkipEvent'])
|
||||
->middleware('throttle:60,1'); // max 60 events per minute per IP
|
||||
|
||||
// HEVC flag — player tarafından HEVC hatası alınan kaynakları işaretler
|
||||
Route::post('sources/flag-hevc', [AnimeApiController::class, 'flagHevc'])
|
||||
->middleware('throttle:30,1');
|
||||
|
||||
// Reklam istatistikleri — path bilerek nötr ('ads' değil), adblocker /api/ads/ engelliyor
|
||||
Route::post('slot/{ad}/view', [\App\Http\Controllers\Api\AdApiController::class, 'impression'])
|
||||
->middleware('throttle:30,1');
|
||||
Route::post('slot/{ad}/hit', [\App\Http\Controllers\Api\AdApiController::class, 'click'])
|
||||
->middleware('throttle:30,1');
|
||||
|
||||
// Comments (public read, auth write)
|
||||
Route::get('comments', [CommentApiController::class, 'index']);
|
||||
|
||||
// NicoNico timestamp comments (public read)
|
||||
Route::get('episodes/{episode}/nico-comments', [SocialApiController::class, 'timestampComments']);
|
||||
|
||||
// Predictions (public read)
|
||||
Route::get('episodes/{episode}/predictions', [SocialApiController::class, 'predictions']);
|
||||
|
||||
// Spoiler boxes (public read)
|
||||
Route::get('episodes/{episode}/spoiler-boxes', [SocialApiController::class, 'spoilerBoxes']);
|
||||
|
||||
// Tribunal (public read)
|
||||
Route::get('tribunals', [TribunalApiController::class, 'index']);
|
||||
Route::get('tribunals/{tribunal}', [TribunalApiController::class, 'show']);
|
||||
|
||||
// Watch party info (public)
|
||||
Route::get('party/{roomCode}', [SocialApiController::class, 'partyInfo']);
|
||||
|
||||
// Mood engine (public)
|
||||
Route::post('mood/recommend', [SocialApiController::class, 'moodRecommend']);
|
||||
|
||||
// Anime requests (public list)
|
||||
Route::get('anime-request', [UserApiController::class, 'requestIndex']);
|
||||
|
||||
// Notifications count (optional auth)
|
||||
Route::get('notifications/count', [UserApiController::class, 'notificationsCount'])
|
||||
->middleware('auth:sanctum');
|
||||
|
||||
// ── Auth required ─────────────────────────────────────────────────────────────
|
||||
Route::middleware('auth:sanctum')->group(function () {
|
||||
// Auth
|
||||
Route::post('auth/logout', [AuthApiController::class, 'logout']);
|
||||
Route::get('auth/me', [AuthApiController::class, 'me']);
|
||||
Route::post('auth/profile', [AuthApiController::class, 'updateProfile']);
|
||||
Route::post('auth/fcm-token', [AuthApiController::class, 'saveFcmToken']);
|
||||
|
||||
// Profile stats
|
||||
Route::get('profile/stats', [UserApiController::class, 'profileStats']);
|
||||
|
||||
// Watchlist
|
||||
Route::get('watchlist', [UserApiController::class, 'watchlist']);
|
||||
Route::post('watchlist/{anime}/toggle', [UserApiController::class, 'watchlistToggle']);
|
||||
|
||||
// Continue watching
|
||||
Route::post('continue-watching', [UserApiController::class, 'continueWatchingUpdate']);
|
||||
|
||||
// Rate & Follow
|
||||
Route::post('anime/{anime}/rate', [UserApiController::class, 'animeRate']);
|
||||
Route::post('anime/{anime:slug}/follow', [UserApiController::class, 'followToggle']);
|
||||
|
||||
// Notifications
|
||||
Route::get('notifications', [UserApiController::class, 'notifications']);
|
||||
|
||||
// Achievements
|
||||
Route::get('achievements', [UserApiController::class, 'achievements']);
|
||||
|
||||
// Episode notes
|
||||
Route::post('episode/{episode}/note', [UserApiController::class, 'noteStore']);
|
||||
Route::delete('notes/{note}', [UserApiController::class, 'noteDelete']);
|
||||
Route::get('episode/{episode}/notes', [UserApiController::class, 'episodeNotesList']);
|
||||
|
||||
// Comments
|
||||
Route::post('comments', [CommentApiController::class, 'store']);
|
||||
Route::post('comments/{comment}/like', [CommentApiController::class, 'like']);
|
||||
|
||||
// Anime requests
|
||||
Route::post('anime-request', [UserApiController::class, 'requestStore']);
|
||||
Route::post('anime-request/{animeRequest}/vote', [UserApiController::class, 'requestVote']);
|
||||
|
||||
// AI
|
||||
Route::post('ai/chat', [AiApiController::class, 'chat']);
|
||||
Route::post('ai/recommend', [AiApiController::class, 'recommend']);
|
||||
Route::post('ai/similar', [AiApiController::class, 'similar']);
|
||||
Route::post('ai/search', [AiApiController::class, 'search']);
|
||||
|
||||
// NicoNico (auth write)
|
||||
Route::post('episodes/{episode}/nico-comments', [SocialApiController::class, 'timestampCommentStore']);
|
||||
|
||||
// Predictions (auth write)
|
||||
Route::post('episodes/{episode}/predictions', [SocialApiController::class, 'predictionStore']);
|
||||
Route::post('predictions/{prediction}/vote', [SocialApiController::class, 'predictionVote']);
|
||||
|
||||
// Spoiler boxes (auth write)
|
||||
Route::post('episodes/{episode}/spoiler-boxes', [SocialApiController::class, 'spoilerBoxStore']);
|
||||
Route::post('spoiler-boxes/{box}/like', [SocialApiController::class, 'spoilerBoxLike']);
|
||||
|
||||
// Time capsule
|
||||
Route::get('capsules', [SocialApiController::class, 'capsuleIndex']);
|
||||
Route::post('capsules', [SocialApiController::class, 'capsuleStore']);
|
||||
Route::post('capsules/{capsule}/open', [SocialApiController::class, 'capsuleOpen']);
|
||||
|
||||
// Tribunal (auth write)
|
||||
Route::post('tribunals', [TribunalApiController::class, 'store']);
|
||||
Route::post('tribunals/{tribunal}/vote', [TribunalApiController::class, 'vote']);
|
||||
Route::post('tribunals/{tribunal}/argue', [TribunalApiController::class, 'argue']);
|
||||
Route::post('tribunal-arguments/{argument}/vote', [TribunalApiController::class, 'argVote']);
|
||||
|
||||
// Watch party
|
||||
Route::post('party/create', [SocialApiController::class, 'partyCreate']);
|
||||
Route::post('party/{roomCode}/join', [SocialApiController::class, 'partyJoin']);
|
||||
Route::post('party/{roomCode}/sync', [SocialApiController::class, 'partySync']);
|
||||
Route::post('party/{roomCode}/leave', [SocialApiController::class, 'partyLeave']);
|
||||
|
||||
// User follow
|
||||
Route::post('users/{user}/follow', [SocialApiController::class, 'followToggle']);
|
||||
|
||||
// Plans
|
||||
Route::get('plans', [PlanApiController::class, 'index']);
|
||||
|
||||
// ── Mesajlar (Mobile Chat) — sabit rotalar önce gelmeli
|
||||
Route::get('messages', [MessageApiController::class, 'conversations']);
|
||||
Route::post('messages/start/{user}', [MessageApiController::class, 'startConversation']);
|
||||
Route::get('messages/{conversation}', [MessageApiController::class, 'show']);
|
||||
Route::post('messages/{conversation}', [MessageApiController::class, 'send']);
|
||||
Route::get('messages/{conversation}/poll', [MessageApiController::class, 'poll']);
|
||||
});
|
||||
Reference in New Issue
Block a user