initial commit
linter / quality (push) Has been cancelled
tests / ci (8.3) (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-09 19:53:31 +02:00
commit c178fab470
307 changed files with 30669 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
<?php
namespace App\Http\Controllers;
use App\Models\Bonus;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Storage;
use Inertia\Inertia;
class BonusController extends Controller
{
public function index()
{
return Inertia::render('Admin/Bonuses/Index', [
'bonuses' => Bonus::orderBy('order')->get()
]);
}
public function create()
{
return Inertia::render('Admin/Bonuses/Create', [
'predefinedBadges' => Config::get('bonuses.predefined_badges', []),
'predefinedKeyFeatures' => Config::get('bonuses.predefined_key_features', []),
]);
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'description' => 'nullable|string',
'image_path' => 'nullable|string',
'image_file' => 'nullable|image|max:2048', // 2MB Max
'brand_color' => 'nullable|string',
'hover_color' => 'nullable|string',
'badges' => 'nullable|array',
'badges.*.label' => 'required_with:badges|string|max:255',
'badges.*.class' => 'nullable|string|max:255',
'min_deposit' => 'nullable|string',
'max_bet' => 'nullable|string',
'wagering' => 'nullable|string',
'free_spins' => 'nullable|string',
'button_link' => 'nullable|string',
'key_features' => 'nullable|array',
'key_features.*' => 'required_with:key_features|string|max:255',
'is_sticky' => 'boolean',
'is_no_deposit' => 'boolean',
'is_featured' => 'boolean',
'order' => 'integer',
]);
if ($request->hasFile('image_file')) {
$path = $request->file('image_file')->store('bonuses', 'public');
$validated['image_path'] = Storage::url($path);
}
Bonus::create($validated);
return redirect()->route('admin.bonuses.index')->with('success', 'Bonus created successfully.');
}
public function edit(Bonus $bonus)
{
return Inertia::render('Admin/Bonuses/Edit', [
'bonus' => $bonus,
'predefinedBadges' => Config::get('bonuses.predefined_badges', []),
'predefinedKeyFeatures' => Config::get('bonuses.predefined_key_features', []),
]);
}
public function update(Request $request, Bonus $bonus)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'description' => 'nullable|string',
'image_path' => 'nullable|string',
'image_file' => 'nullable|image|max:2048',
'brand_color' => 'nullable|string',
'hover_color' => 'nullable|string',
'badges' => 'nullable|array',
'badges.*.label' => 'required_with:badges|string|max:255',
'badges.*.class' => 'nullable|string|max:255',
'min_deposit' => 'nullable|string',
'max_bet' => 'nullable|string',
'wagering' => 'nullable|string',
'free_spins' => 'nullable|string',
'button_link' => 'nullable|string',
'key_features' => 'nullable|array',
'key_features.*' => 'required_with:key_features|string|max:255',
'is_sticky' => 'boolean',
'is_no_deposit' => 'boolean',
'is_featured' => 'boolean',
'order' => 'integer',
]);
if ($request->hasFile('image_file')) {
// Delete old file if exists and it was an uploaded one
if ($bonus->image_path && str_contains($bonus->image_path, '/storage/bonuses/')) {
$oldPath = str_replace('/storage/', '', $bonus->image_path);
Storage::disk('public')->delete($oldPath);
}
$path = $request->file('image_file')->store('bonuses', 'public');
$validated['image_path'] = Storage::url($path);
}
$bonus->update($validated);
return redirect()->route('admin.bonuses.index')->with('success', 'Bonus updated successfully.');
}
public function destroy(Bonus $bonus)
{
if ($bonus->image_path && str_contains($bonus->image_path, '/storage/bonuses/')) {
$oldPath = str_replace('/storage/', '', $bonus->image_path);
Storage::disk('public')->delete($oldPath);
}
$bonus->delete();
return redirect()->route('admin.bonuses.index')->with('success', 'Bonus deleted successfully.');
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
abstract class Controller
{
//
}
@@ -0,0 +1,38 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;
class LiveStatusController extends Controller
{
public function __invoke()
{
// Wir cachen das Ergebnis für 2 Minuten, um die APIs nicht zu spammen
return Cache::remember('live_status', 120, function () {
return [
'twitch' => $this->checkTwitch('bratander1ste'),
'kick' => $this->checkKick('Bratander1ste'),
];
});
}
private function checkTwitch($username)
{
// Hinweis: Benötigt TWITCH_CLIENT_ID und TWITCH_CLIENT_SECRET in der .env
// Für einen schnellen Test ohne API-Key kannst du hier 'false' zurückgeben
return false;
}
private function checkKick($username)
{
try {
$response = Http::get("https://kick.com/api/v1/channels/{$username}");
return $response->json()['livestream'] !== null;
} catch (\Exception $e) {
return false;
}
}
}
@@ -0,0 +1,60 @@
<?php
namespace App\Http\Controllers\Settings;
use App\Http\Controllers\Controller;
use App\Http\Requests\Settings\ProfileDeleteRequest;
use App\Http\Requests\Settings\ProfileUpdateRequest;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Inertia\Inertia;
use Inertia\Response;
class ProfileController extends Controller
{
/**
* Show the user's profile settings page.
*/
public function edit(Request $request): Response
{
return Inertia::render('settings/Profile', [
'mustVerifyEmail' => $request->user() instanceof MustVerifyEmail,
'status' => $request->session()->get('status'),
]);
}
/**
* Update the user's profile information.
*/
public function update(ProfileUpdateRequest $request): RedirectResponse
{
$request->user()->fill($request->validated());
if ($request->user()->isDirty('email')) {
$request->user()->email_verified_at = null;
}
$request->user()->save();
return to_route('profile.edit');
}
/**
* Delete the user's profile.
*/
public function destroy(ProfileDeleteRequest $request): RedirectResponse
{
$user = $request->user();
Auth::logout();
$user->delete();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
}
@@ -0,0 +1,58 @@
<?php
namespace App\Http\Controllers\Settings;
use App\Http\Controllers\Controller;
use App\Http\Requests\Settings\PasswordUpdateRequest;
use App\Http\Requests\Settings\TwoFactorAuthenticationRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
use Inertia\Inertia;
use Inertia\Response;
use Laravel\Fortify\Features;
class SecurityController extends Controller implements HasMiddleware
{
/**
* Get the middleware that should be assigned to the controller.
*/
public static function middleware(): array
{
return Features::canManageTwoFactorAuthentication()
&& Features::optionEnabled(Features::twoFactorAuthentication(), 'confirmPassword')
? [new Middleware('password.confirm', only: ['edit'])]
: [];
}
/**
* Show the user's security settings page.
*/
public function edit(TwoFactorAuthenticationRequest $request): Response
{
$props = [
'canManageTwoFactor' => Features::canManageTwoFactorAuthentication(),
];
if (Features::canManageTwoFactorAuthentication()) {
$request->ensureStateIsValid();
$props['twoFactorEnabled'] = $request->user()->hasEnabledTwoFactorAuthentication();
$props['requiresConfirmation'] = Features::optionEnabled(Features::twoFactorAuthentication(), 'confirm');
}
return Inertia::render('settings/Security', $props);
}
/**
* Update the user's password.
*/
public function update(PasswordUpdateRequest $request): RedirectResponse
{
$request->user()->update([
'password' => $request->password,
]);
return back();
}
}
@@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers;
use App\Models\BonusStat;
use Illuminate\Http\Request;
class TrackingController extends Controller
{
public function track(Request $request)
{
$validated = $request->validate([
'bonus_id' => 'required|exists:bonuses,id',
'type' => 'required|in:view,click',
]);
BonusStat::create([
'bonus_id' => $validated['bonus_id'],
'type' => $validated['type'],
'ip_address' => $request->ip(),
]);
return response()->json(['success' => true]);
}
public function trackSocial(Request $request)
{
$validated = $request->validate([
'platform' => 'required|in:twitch,instagram,kick',
]);
BonusStat::create([
'type' => 'social_' . $validated['platform'],
'ip_address' => $request->ip(),
]);
return response()->json(['success' => true]);
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\View;
use Symfony\Component\HttpFoundation\Response;
class HandleAppearance
{
/**
* Handle an incoming request.
*
* @param Closure(Request): (Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
View::share('appearance', $request->cookie('appearance') ?? 'system');
return $next($request);
}
}
@@ -0,0 +1,55 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
{
/**
* The root template that's loaded on the first page visit.
*
* @see https://inertiajs.com/server-side-setup#root-template
*
* @var string
*/
protected $rootView = 'app';
/**
* Determines the current asset version.
*
* @see https://inertiajs.com/asset-versioning
*/
public function version(Request $request): ?string
{
return parent::version($request);
}
/**
* Define the props that are shared by default.
*
* @see https://inertiajs.com/shared-data
*
* @return array<string, mixed>
*/
public function share(Request $request): array
{
$user = $request->user();
return [
...parent::share($request),
'name' => config('app.name'),
'auth' => [
'user' => $user ? array_merge($user->toArray(), [
'role' => $user->role ?? 'user'
]) : null,
],
'flash' => [
'success' => $request->session()->get('success'),
'error' => $request->session()->get('error'),
],
'sidebarOpen' => ! $request->hasCookie('sidebar_state') || $request->cookie('sidebar_state') === 'true',
];
}
}
@@ -0,0 +1,25 @@
<?php
namespace App\Http\Requests\Settings;
use App\Concerns\PasswordValidationRules;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class PasswordUpdateRequest extends FormRequest
{
use PasswordValidationRules;
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'current_password' => $this->currentPasswordRules(),
'password' => $this->passwordRules(),
];
}
}
@@ -0,0 +1,24 @@
<?php
namespace App\Http\Requests\Settings;
use App\Concerns\PasswordValidationRules;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class ProfileDeleteRequest extends FormRequest
{
use PasswordValidationRules;
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'password' => $this->currentPasswordRules(),
];
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Http\Requests\Settings;
use App\Concerns\ProfileValidationRules;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class ProfileUpdateRequest extends FormRequest
{
use ProfileValidationRules;
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return $this->profileRules($this->user()->id);
}
}
@@ -0,0 +1,22 @@
<?php
namespace App\Http\Requests\Settings;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Laravel\Fortify\InteractsWithTwoFactorState;
class TwoFactorAuthenticationRequest extends FormRequest
{
use InteractsWithTwoFactorState;
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [];
}
}