role) !== 'admin') { abort(403, 'Nur für Admins'); } } /** * Redirect old /admin to the new dashboard */ public function index(Request $request) { $this->ensureAdmin(); return redirect()->route('admin.casino'); } public function casinoDashboard(Request $request) { $this->ensureAdmin(); $stats = [ 'total_users' => User::count(), 'total_wagered' => GameBet::sum('wager_amount'), 'total_payout' => GameBet::sum('payout_amount'), 'active_bans' => User::where('is_banned', true)->count(), 'new_users_24h' => User::where('created_at', '>=', now()->subDay())->count(), ]; $stats['house_edge'] = $stats['total_wagered'] - $stats['total_payout']; // Get chart data for the last 7 days $chartData = []; for ($i = 6; $i >= 0; $i--) { $date = Carbon::today()->subDays($i); $nextDate = Carbon::today()->subDays($i - 1); $wagered = GameBet::whereBetween('created_at', [$date, $nextDate])->sum('wager_amount'); $payout = GameBet::whereBetween('created_at', [$date, $nextDate])->sum('payout_amount'); $newUsers = User::whereBetween('created_at', [$date, $nextDate])->count(); $chartData[] = [ 'date' => $date->format('Y-m-d'), 'label' => $date->format('D, d M'), 'wagered' => (float)$wagered, 'payout' => (float)$payout, 'profit' => (float)($wagered - $payout), 'new_users' => $newUsers, ]; } $recent_bets = GameBet::with('user:id,username') ->orderByDesc('id') ->limit(10) ->get(); $recent_users = User::orderByDesc('id') ->limit(10) ->get(); return Inertia::render('Admin/CasinoDashboard', [ 'stats' => $stats, 'chartData' => $chartData, 'recentBets' => $recent_bets, 'recentUsers' => $recent_users, ]); } public function usersIndex(Request $request) { $this->ensureAdmin(); $query = User::orderByDesc('id'); if ($request->has('search')) { $search = $request->input('search'); $query->where(function($q) use ($search) { $q->where('id', 'like', "%$search%") ->orWhere('username', 'like', "%$search%") ->orWhere('email', 'like', "%$search%") ->orWhere('first_name', 'like', "%$search%") ->orWhere('last_name', 'like', "%$search%"); }); } if ($request->has('role') && $request->input('role') !== '') { $query->where('role', $request->input('role')); } $users = $query->paginate(20)->withQueryString(); $roles = ['Admin', 'Moderator', 'User']; return Inertia::render('Admin/Users', [ 'users' => $users, 'roles' => $roles, 'filters' => $request->only(['search', 'role']) ]); } public function userShow(Request $request, $id) { $this->ensureAdmin(); $user = User::with('wallets')->findOrFail($id); $restrictions = UserRestriction::where('user_id', $user->id)->orderByDesc('id')->get(); $vaultTransfers = WalletTransfer::where('user_id', $user->id) ->whereIn('type', ['vault_deposit', 'vault_withdraw']) ->orderByDesc('id') ->get(); $deposits = CryptoPayment::where('user_id', $user->id) ->orderByDesc('id') ->get(); $kycDocuments = class_exists(KycDocument::class) ? KycDocument::where('user_id', $user->id)->get() : []; return Inertia::render('Admin/UserShow', [ 'user' => $user, 'restrictions' => $restrictions, 'wallets' => $user->wallets, 'vaultTransfers' => $vaultTransfers, 'deposits' => $deposits, 'kycDocuments' => $kycDocuments, ]); } public function updateUser(Request $request, $id) { $this->ensureAdmin(); $validated = $request->validate([ 'username' => 'required|string|max:255', 'email' => 'required|email|max:255', 'first_name' => 'nullable|string|max:255', 'last_name' => 'nullable|string|max:255', 'birthdate' => 'nullable|date', 'gender' => 'nullable|string|max:50', 'phone' => 'nullable|string|max:50', 'country' => 'nullable|string|max:10', 'address_line1' => 'nullable|string|max:255', 'address_line2' => 'nullable|string|max:255', 'city' => 'nullable|string|max:255', 'postal_code' => 'nullable|string|max:50', 'currency' => 'nullable|string|max:10', 'vip_level' => 'nullable|integer|min:0|max:100', 'balance' => 'nullable|numeric', 'vault_balance' => 'nullable|numeric', 'is_banned' => 'boolean', 'is_chat_banned' => 'boolean', 'ban_reason' => 'nullable|string|max:255', 'role' => 'nullable|string|max:50' ]); DB::transaction(function() use ($id, $validated, $request) { $user = User::where('id', $id)->lockForUpdate()->firstOrFail(); $user->fill($validated); // Update restrictions for ban if ($request->has('is_banned')) { $user->is_banned = $request->input('is_banned'); if ($user->is_banned) { UserRestriction::updateOrCreate( ['user_id' => $user->id, 'type' => 'account_ban'], [ 'active' => true, 'reason' => $request->input('ban_reason'), 'expires_at' => $request->input('ban_ends_at') ] ); } else { UserRestriction::where('user_id', $user->id)->where('type', 'account_ban')->update(['active' => false]); } } // Update restrictions for chat ban (via UserRestriction only, no column on users table) if ($request->has('is_chat_banned')) { if ($request->boolean('is_chat_banned')) { UserRestriction::updateOrCreate( ['user_id' => $user->id, 'type' => 'chat_ban'], [ 'active' => true, 'reason' => 'Admin intervention', 'ends_at' => $request->input('chat_ban_ends_at'), ] ); } else { UserRestriction::where('user_id', $user->id)->where('type', 'chat_ban')->update(['active' => false]); } } $user->save(); }); return back()->with('success', 'User updated successfully.'); } public function userHistory(Request $request, $id) { $this->ensureAdmin(); $user = User::findOrFail($id); // Fetch wallet transfers as history $transfers = WalletTransfer::where('user_id', $user->id) ->orderByDesc('created_at') ->limit(50) ->get(); return response()->json(['data' => $transfers]); } public function chatIndex(Request $request) { $this->ensureAdmin(); $aiEnabled = AppSetting::get('chat.ai_enabled', false); return Inertia::render('Admin/Chat', [ 'aiEnabled' => $aiEnabled ]); } public function toggleAi(Request $request) { $this->ensureAdmin(); $enabled = $request->input('enabled', false); AppSetting::put('chat.ai_enabled', $enabled); return back()->with('success', 'AI Chat ' . ($enabled ? 'enabled' : 'disabled')); } public function deleteChatMessage($id) { $this->ensureAdmin(); // implement chat deletion - typically this would proxy to the backend API // For now, we'll just mock it as success if we don't have direct DB access to it here // (ChatMessage model exists locally, but messages might be on the upstream) return back()->with('success', 'Message deleted (Mock)'); } public function chatReportShow(Request $request, $id) { $this->ensureAdmin(); $report = ChatMessageReport::with([ 'reporter:id,username,email,avatar,avatar_url,role,vip_level,is_banned,created_at', ])->findOrFail($id); $restrictionWith = fn ($q) => $q->withTrashed(false)->orderByDesc('created_at'); $senderUser = null; if ($report->sender_id) { $senderUser = User::with(['restrictions' => $restrictionWith]) ->find($report->sender_id); } $reporterUser = User::with(['restrictions' => $restrictionWith]) ->find($report->reporter_id); return Inertia::render('Admin/ChatReportShow', [ 'report' => $report, 'senderUser' => $senderUser, 'reporterUser' => $reporterUser, 'flash' => session('success'), ]); } public function punishFromChatReport(Request $request, $id) { $this->ensureAdmin(); $report = ChatMessageReport::findOrFail($id); $validated = $request->validate([ 'type' => 'required|in:chat_ban,account_ban', 'reason' => 'required|string|max:300', 'hours' => 'nullable|integer|min:1', // null = permanent ]); $targetId = $report->sender_id; if (!$targetId) { return back()->withErrors(['error' => 'Kein Nutzer mit dieser Nachricht verknüpft.']); } $user = User::findOrFail($targetId); $admin = Auth::user(); $endsAt = $validated['hours'] ? now()->addHours($validated['hours']) : null; UserRestriction::updateOrCreate( ['user_id' => $user->id, 'type' => $validated['type']], [ 'active' => true, 'reason' => $validated['reason'], 'notes' => "Via Chat-Report #$report->id", 'imposed_by' => $admin->id, 'starts_at' => now(), 'ends_at' => $endsAt, 'source' => 'admin_panel', 'metadata' => ['report_id' => $report->id], ] ); if ($validated['type'] === 'account_ban') { $user->update([ 'is_banned' => true, 'ban_reason' => $validated['reason'], ]); } $report->update(['status' => 'reviewed']); return back()->with('success', 'Strafe wurde erfolgreich verhängt.'); } public function chatReports(Request $request) { $this->ensureAdmin(); $query = ChatMessageReport::with('reporter:id,username,email,avatar,avatar_url') ->orderByDesc('id'); $status = $request->input('status', 'pending'); if ($status && $status !== 'all') { $query->where('status', $status); } $search = trim((string) $request->input('search', '')); if ($search !== '') { if (is_numeric($search)) { $query->where('id', $search); } else { $q = '%' . $search . '%'; $query->where(function ($sub) use ($q) { $sub->where('sender_username', 'like', $q) ->orWhereHas('reporter', fn($r) => $r->where('username', 'like', $q)); }); } } $stats = [ 'pending' => ChatMessageReport::where('status', 'pending')->count(), 'reviewed' => ChatMessageReport::where('status', 'reviewed')->count(), 'dismissed' => ChatMessageReport::where('status', 'dismissed')->count(), ]; $stats['total'] = $stats['pending'] + $stats['reviewed'] + $stats['dismissed']; $reports = $query->paginate(25)->withQueryString(); return Inertia::render('Admin/ChatReports', [ 'reports' => $reports, 'filters' => $request->only(['status', 'search']), 'stats' => $stats, ]); } public function updateChatReport(Request $request, $id) { $this->ensureAdmin(); $validated = $request->validate([ 'status' => 'required|in:pending,reviewed,dismissed', 'admin_note' => 'nullable|string|max:1000', ]); $report = ChatMessageReport::findOrFail($id); $report->update($validated); return back()->with('success', 'Report updated.'); } public function liftRestriction(Request $request, $id) { $this->ensureAdmin(); $restriction = UserRestriction::findOrFail($id); $restriction->update(['active' => false]); if ($restriction->type === 'account_ban') { User::where('id', $restriction->user_id)->update(['is_banned' => false]); } return back()->with('success', 'Sperre aufgehoben.'); } public function extendRestriction(Request $request, $id) { $this->ensureAdmin(); $validated = $request->validate([ 'hours' => 'required|integer|min:1', ]); $restriction = UserRestriction::findOrFail($id); $base = $restriction->ends_at && $restriction->ends_at->isFuture() ? $restriction->ends_at : now(); $restriction->update(['ends_at' => $base->addHours($validated['hours'])]); return back()->with('success', 'Sperre verlängert.'); } public function profileReports(Request $request) { $this->ensureAdmin(); $query = ProfileReport::with([ 'reporter:id,username,email,avatar,avatar_url', 'profile:id,username,email,avatar,avatar_url,role,vip_level,bio', ])->orderByDesc('id'); $status = $request->input('status', 'pending'); if ($status && $status !== 'all') { $query->where('status', $status); } $search = trim((string) $request->input('search', '')); if ($search !== '') { if (is_numeric($search)) { $query->where('id', $search); } else { $q = '%' . $search . '%'; $query->where(function ($sub) use ($q) { $sub->whereHas('reporter', fn($r) => $r->where('username', 'like', $q)) ->orWhereHas('profile', fn($r) => $r->where('username', 'like', $q)); }); } } $stats = [ 'pending' => ProfileReport::where('status', 'pending')->count(), 'reviewed' => ProfileReport::where('status', 'reviewed')->count(), 'dismissed' => ProfileReport::where('status', 'dismissed')->count(), ]; $stats['total'] = $stats['pending'] + $stats['reviewed'] + $stats['dismissed']; $reports = $query->paginate(25)->withQueryString(); return Inertia::render('Admin/ProfileReports', [ 'reports' => $reports, 'filters' => $request->only(['status', 'search']), 'stats' => $stats, ]); } public function profileReportShow(Request $request, $id) { $this->ensureAdmin(); $report = ProfileReport::with([ 'reporter:id,username,email,avatar,avatar_url,role,vip_level,is_banned,created_at', 'profile:id,username,email,avatar,avatar_url,role,vip_level,is_banned,created_at', ])->findOrFail($id); // Attach restrictions to both users $reporterUser = null; $profileUser = null; if ($report->reporter_id) { $reporterUser = User::with(['restrictions' => function ($q) { $q->orderByDesc('id'); }])->find($report->reporter_id); } if ($report->profile_id) { $profileUser = User::with(['restrictions' => function ($q) { $q->orderByDesc('id'); }])->find($report->profile_id); } // Load current live profile data from DB for comparison $currentProfile = null; if ($report->profile_id) { $u = User::find($report->profile_id); if ($u) { $likesCount = ProfileLike::where('profile_id', $u->id)->count(); $comments = ProfileComment::with(['user:id,username,avatar']) ->where('profile_id', $u->id) ->latest() ->limit(15) ->get() ->map(fn($c) => [ 'id' => $c->id, 'content' => $c->content, 'created_at' => $c->created_at, 'user' => [ 'id' => $c->user->id, 'username' => $c->user->username, 'avatar' => $c->user->avatar, ], ]); $currentProfile = [ 'id' => $u->id, 'username' => $u->username, 'avatar' => $u->avatar ?? $u->avatar_url, 'banner' => $u->banner, 'bio' => $u->bio, 'role' => $u->role ?? 'User', 'vip_level' => (int)($u->vip_level ?? 0), 'clan_tag' => $u->clan_tag, 'stats' => [ 'wagered' => (float)($u->stats?->total_wagered ?? 0), 'wins' => (int)($u->stats?->total_wins ?? 0), 'biggest_win' => (float)($u->stats?->biggest_win ?? 0), 'likes_count' => $likesCount, 'join_date' => optional($u->created_at)->toDateString(), ], 'comments' => $comments, ]; } } $screenshotUrl = $report->screenshot_path ? asset('storage/' . $report->screenshot_path) : null; return Inertia::render('Admin/ProfileReportShow', [ 'report' => $report, 'reporterUser' => $reporterUser, 'profileUser' => $profileUser, 'screenshotUrl' => $screenshotUrl, 'currentProfile' => $currentProfile, 'flash' => session('success'), ]); } public function updateProfileReport(Request $request, $id) { $this->ensureAdmin(); $validated = $request->validate([ 'status' => 'required|in:pending,reviewed,dismissed', 'admin_note' => 'nullable|string|max:1000', ]); $report = ProfileReport::findOrFail($id); $report->update($validated); return back()->with('success', 'Report updated.'); } public function punishFromProfileReport(Request $request, $id) { $this->ensureAdmin(); $data = $request->validate([ 'type' => 'required|in:chat_ban,account_ban', 'reason' => 'required|string|max:500', 'hours' => 'nullable|integer|min:1', ]); $report = ProfileReport::findOrFail($id); $target = User::findOrFail($report->profile_id); $startsAt = now(); $endsAt = $data['hours'] ? now()->addHours($data['hours']) : null; UserRestriction::create([ 'user_id' => $target->id, 'type' => $data['type'], 'reason' => $data['reason'], 'active' => true, 'starts_at' => $startsAt, 'ends_at' => $endsAt, ]); if ($data['type'] === 'account_ban') { $target->update(['is_banned' => true]); } $report->update(['status' => 'reviewed']); return back()->with('success', 'Strafe erfolgreich verhängt!'); } }