605 lines
21 KiB
PHP
605 lines
21 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\ChatMessageReport;
|
|
use App\Models\ProfileComment;
|
|
use App\Models\ProfileLike;
|
|
use App\Models\ProfileReport;
|
|
use App\Models\User;
|
|
use App\Models\WalletTransfer;
|
|
use App\Models\GameBet;
|
|
use App\Models\UserRestriction;
|
|
use App\Models\KycDocument;
|
|
use App\Models\CryptoPayment;
|
|
use App\Models\AppSetting;
|
|
use Illuminate\Http\Request;
|
|
use Inertia\Inertia;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Carbon\Carbon;
|
|
|
|
class AdminController extends Controller
|
|
{
|
|
public function __construct()
|
|
{
|
|
}
|
|
|
|
private function ensureAdmin()
|
|
{
|
|
if (strtolower((string) Auth::user()->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!');
|
|
}
|
|
}
|