Initialer Laravel Commit für BetiX
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled

This commit is contained in:
2026-04-04 18:01:50 +02:00
commit 0280278978
374 changed files with 65210 additions and 0 deletions

View File

@@ -0,0 +1,604 @@
<?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!');
}
}