You've already forked Epicnabbo-Catalogus-Updated-Daily
🆙 More fixes 🆙
This commit is contained in:
+1
@@ -33,6 +33,7 @@ class TwoFactorAuthenticatedSessionController extends Controller
|
||||
*/
|
||||
public function store(TwoFactorLoginRequest $request): TwoFactorLoginResponse
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = $request->challengedUser();
|
||||
|
||||
if ($code = $request->validRecoveryCode()) {
|
||||
|
||||
@@ -26,15 +26,15 @@ class CreateNewUser implements CreatesNewUsers
|
||||
/**
|
||||
* Validate and create a newly registered user.
|
||||
*/
|
||||
public function create(array $input)
|
||||
public function create(array $input): User
|
||||
{
|
||||
if (setting('disable_registration') ?: '0' === '1') {
|
||||
if ((setting('disable_registration') ?: '0') === '1') {
|
||||
throw ValidationException::withMessages([
|
||||
'registration' => __('Registration is disabled.'),
|
||||
]);
|
||||
}
|
||||
|
||||
$ip = request()?->ip();
|
||||
$ip = request()->ip();
|
||||
if (! filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6)) {
|
||||
throw ValidationException::withMessages([
|
||||
'registration' => __('Your IP address seems to be invalid'),
|
||||
@@ -57,7 +57,7 @@ class CreateNewUser implements CreatesNewUsers
|
||||
$user = User::create([
|
||||
'username' => $input['username'],
|
||||
'mail' => $input['mail'],
|
||||
'password' => Hash::make($input['password']),
|
||||
'password' => Hash::make((string) $input['password']),
|
||||
'account_created' => time(),
|
||||
'last_login' => time(),
|
||||
'motto' => setting('start_motto') ?: 'Welcome to the hotel!',
|
||||
@@ -87,12 +87,12 @@ class CreateNewUser implements CreatesNewUsers
|
||||
->first();
|
||||
|
||||
if (is_null($referralUser)) {
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
return $user;
|
||||
}
|
||||
|
||||
// If same IP skip referral incrementation
|
||||
if ($referralUser->ip_current == $user->ip_current || $referralUser->ip_register == $user->ip_register) {
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
return $user;
|
||||
}
|
||||
|
||||
$referralUser->referrals()->updateOrCreate(['user_id' => $referralUser->id], [
|
||||
|
||||
@@ -10,14 +10,20 @@ use App\Services\User\UserApiService;
|
||||
|
||||
class HotelApiController extends Controller
|
||||
{
|
||||
public function __construct(private readonly UserApiService $userApiService): void {}
|
||||
public function __construct(private readonly UserApiService $userApiService) {}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $columns
|
||||
*/
|
||||
public function fetchUser(string $username, array $columns = ['username', 'motto', 'look']): UserResource
|
||||
{
|
||||
return new UserResource($this->userApiService->fetchUser($username, $columns));
|
||||
}
|
||||
|
||||
public function onlineUsers($columns = ['username', 'motto', 'look'], bool $randomOrder = true): OnlineUsersResource
|
||||
/**
|
||||
* @param array<int, string> $columns
|
||||
*/
|
||||
public function onlineUsers(array $columns = ['username', 'motto', 'look'], bool $randomOrder = true): OnlineUsersResource
|
||||
{
|
||||
return new OnlineUsersResource($this->userApiService->onlineUsers($columns, $randomOrder));
|
||||
}
|
||||
|
||||
@@ -39,7 +39,10 @@ class ArticleController extends Controller
|
||||
|
||||
public function toggleReaction(WebsiteArticle $article, Request $request): JsonResponse
|
||||
{
|
||||
$response = $this->reactionService->toggleReaction($article, Auth::user(), $request);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = $request->user();
|
||||
|
||||
$response = $this->reactionService->toggleReaction($article, $user, $request);
|
||||
|
||||
return response()->json($response);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ use Illuminate\Http\RedirectResponse;
|
||||
|
||||
class WebsiteArticleCommentsController extends Controller
|
||||
{
|
||||
public function __construct(public readonly CommentService $commentService): void {}
|
||||
public function __construct(public readonly CommentService $commentService) {}
|
||||
|
||||
public function store(WebsiteArticle $article, ArticleCommentFormRequest $request): RedirectResponse
|
||||
{
|
||||
$this->commentService->store($request->get('comment'), $article);
|
||||
$this->commentService->store($request->string('comment')->toString(), $article);
|
||||
|
||||
return back()->with('success', __('You comment has been posted!'));
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class BadgeController extends Controller
|
||||
|
||||
public function show(SettingsService $settingsService): \Illuminate\Contracts\View\View
|
||||
{
|
||||
$cost = (int) $settingsService->getOrDefault('drawbadge_currency_value', 150);
|
||||
$cost = (int) $settingsService->getOrDefault('drawbadge_currency_value', '150');
|
||||
$currencyType = $settingsService->getOrDefault('drawbadge_currency_type', 'credits');
|
||||
$badgesPath = $settingsService->getOrDefault('badge_path_filesystem');
|
||||
|
||||
@@ -43,8 +43,14 @@ class BadgeController extends Controller
|
||||
|
||||
public function buy(Request $request, SendCurrency $sendCurrency, SettingsService $settingsService): \Illuminate\Http\JsonResponse
|
||||
{
|
||||
$user = Auth::user();
|
||||
$cost = (int) $settingsService->getOrDefault('drawbadge_currency_value', 150);
|
||||
/** @var \App\Models\User|null $user */
|
||||
$user = $request->user();
|
||||
|
||||
if (! $user) {
|
||||
return response()->json(['success' => false, 'message' => 'Unauthorized.'], 401);
|
||||
}
|
||||
|
||||
$cost = (int) $settingsService->getOrDefault('drawbadge_currency_value', '150');
|
||||
$currencyType = $settingsService->getOrDefault('drawbadge_currency_type', 'credits');
|
||||
|
||||
$currentAmount = match ($currencyType) {
|
||||
@@ -65,13 +71,13 @@ class BadgeController extends Controller
|
||||
return response()->json(['success' => false, 'message' => 'Failed to deduct ' . $currencyType . '.'], 500);
|
||||
}
|
||||
|
||||
$badgeData = $request->input('badge_data');
|
||||
if (! $badgeData) {
|
||||
$badgeData = $request->string('badge_data')->toString();
|
||||
if ($badgeData === '') {
|
||||
return response()->json(['success' => false, 'message' => 'No badge data provided.'], 400);
|
||||
}
|
||||
|
||||
$badgeData = preg_replace('#^data:image/\w+;base64,#i', '', (string) $badgeData);
|
||||
$decoded = base64_decode((string) $badgeData, true);
|
||||
$badgeData = (string) preg_replace('#^data:image/\w+;base64,#i', '', $badgeData);
|
||||
$decoded = base64_decode($badgeData, true);
|
||||
|
||||
if ($decoded === false) {
|
||||
return response()->json(['success' => false, 'message' => 'Invalid base64 data.'], 400);
|
||||
|
||||
@@ -10,12 +10,15 @@ class FlashController extends Controller
|
||||
{
|
||||
public function __invoke(): View
|
||||
{
|
||||
Auth::user()->update([
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
$user->update([
|
||||
'ip_current' => request()->ip(),
|
||||
]);
|
||||
|
||||
return view('client.flash', [
|
||||
'sso' => Auth::user()->ssoTicket(),
|
||||
'sso' => $user->ssoTicket(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,15 @@ class NitroController extends Controller
|
||||
{
|
||||
public function __invoke(): View
|
||||
{
|
||||
Auth::user()->update([
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
$user->update([
|
||||
'ip_current' => request()->ip(),
|
||||
]);
|
||||
|
||||
return view('client.nitro', [
|
||||
'sso' => Auth::user()->ssoTicket(),
|
||||
'sso' => $user->ssoTicket(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,10 @@ use Illuminate\View\View;
|
||||
|
||||
class LeaderboardController extends Controller
|
||||
{
|
||||
/** @var array<int, int> */
|
||||
protected array $staffIds = [];
|
||||
|
||||
public function __construct(private readonly StaffService $staffService): void
|
||||
public function __construct(private readonly StaffService $staffService)
|
||||
{
|
||||
$this->staffIds = $this->staffService->fetchEmployeeIds();
|
||||
}
|
||||
@@ -44,6 +45,9 @@ class LeaderboardController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Collection<int, UserSetting>
|
||||
*/
|
||||
private function retrieveSettings(string $column): \Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
return UserSetting::select('user_id', $column)
|
||||
|
||||
@@ -8,7 +8,7 @@ use Illuminate\View\View;
|
||||
|
||||
class PhotosController extends Controller
|
||||
{
|
||||
public function __construct(private readonly CameraService $cameraService): void {}
|
||||
public function __construct(private readonly CameraService $cameraService) {}
|
||||
|
||||
public function __invoke(): View
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ use Illuminate\Http\RedirectResponse;
|
||||
|
||||
class StaffApplicationsController extends Controller
|
||||
{
|
||||
public function __construct(private readonly StaffApplicationService $staffApplicationService): void {}
|
||||
public function __construct(private readonly StaffApplicationService $staffApplicationService) {}
|
||||
|
||||
public function index(): View
|
||||
{
|
||||
@@ -29,7 +29,16 @@ class StaffApplicationsController extends Controller
|
||||
|
||||
public function store(WebsiteOpenPosition $position, StaffApplicationFormRequest $request): RedirectResponse
|
||||
{
|
||||
if ($this->staffApplicationService->hasUserAppliedForPosition($request->user(), $position->permission->id)) {
|
||||
/** @var \App\Models\User $user */
|
||||
$user = $request->user();
|
||||
|
||||
if ($position->permission === null) {
|
||||
return back()->withErrors([
|
||||
'message' => __('Invalid position configuration.'),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($this->staffApplicationService->hasUserAppliedForPosition($user, $position->permission->id)) {
|
||||
return back()->withErrors([
|
||||
'message' => __('You have already applied for this position.'),
|
||||
]);
|
||||
@@ -41,7 +50,7 @@ class StaffApplicationsController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
$this->staffApplicationService->storeApplication($request->user(), $position->permission->id, $request->input('content'));
|
||||
$this->staffApplicationService->storeApplication($user, $position->permission->id, $request->string('content')->toString());
|
||||
|
||||
return to_route('staff-applications.index')->with('success', __('Your application has been submitted!'));
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use Illuminate\View\View;
|
||||
|
||||
class StaffController extends Controller
|
||||
{
|
||||
public function __construct(private readonly StaffService $staffService): void {}
|
||||
public function __construct(private readonly StaffService $staffService) {}
|
||||
|
||||
public function __invoke(): View
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ use Illuminate\View\View;
|
||||
|
||||
class WebsiteTeamsController extends Controller
|
||||
{
|
||||
public function __construct(private readonly TeamService $teamService): void {}
|
||||
public function __construct(private readonly TeamService $teamService) {}
|
||||
|
||||
public function __invoke(): View
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ use Illuminate\View\View;
|
||||
|
||||
class WebsiteRareValuesController extends Controller
|
||||
{
|
||||
public function __construct(private readonly RareValueCategoriesService $valueCategoriesService): void {}
|
||||
public function __construct(private readonly RareValueCategoriesService $valueCategoriesService) {}
|
||||
|
||||
public function index(): View
|
||||
{
|
||||
@@ -42,7 +42,7 @@ class WebsiteRareValuesController extends Controller
|
||||
|
||||
public function search(RareSearchFormRequest $request): View|RedirectResponse
|
||||
{
|
||||
$searchTerm = $request->input('search');
|
||||
$searchTerm = $request->string('search')->toString();
|
||||
|
||||
$categories = $this->valueCategoriesService->searchCategories($searchTerm);
|
||||
|
||||
@@ -64,13 +64,18 @@ class WebsiteRareValuesController extends Controller
|
||||
->where('item_id', $value->item_id)
|
||||
->get();
|
||||
|
||||
$itemsPerUser = $items->groupBy('user_id')->map(fn ($group) => [
|
||||
'user' => $group->first()->user,
|
||||
'item_count' => $group->count(),
|
||||
]);
|
||||
$itemsPerUser = $items->groupBy('user_id')->map(function ($group) {
|
||||
/** @var \App\Models\Game\Furniture\Item $firstItem */
|
||||
$firstItem = $group->first();
|
||||
|
||||
return [
|
||||
'user' => $firstItem->user,
|
||||
'item_count' => $group->count(),
|
||||
];
|
||||
});
|
||||
|
||||
if ((bool) setting('enable_caching')) {
|
||||
Cache::remember('allItems_' . $value->id, setting('cache_timer'), fn () => $items);
|
||||
Cache::remember('allItems_' . $value->id, (int) setting('cache_timer'), fn () => $items);
|
||||
}
|
||||
|
||||
return view('value', [
|
||||
|
||||
@@ -35,7 +35,9 @@ class TicketController extends Controller
|
||||
|
||||
public function store(WebsiteTicketFormRequest $request): RedirectResponse
|
||||
{
|
||||
Auth::user()->tickets()->create($request->validated());
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
$user->tickets()->create($request->validated());
|
||||
|
||||
return back()->with('success', __('Ticket submitted!'));
|
||||
}
|
||||
|
||||
@@ -24,9 +24,12 @@ class TicketReplyController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = $request->user();
|
||||
|
||||
$data = $request->validated();
|
||||
$ticket->replies()->create([
|
||||
'user_id' => $request->user()->id,
|
||||
'user_id' => $user->id,
|
||||
'content' => $data['content'],
|
||||
]);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ class InstallationController extends Controller
|
||||
'installation_key' => ['required', 'string', 'max:255', new ValidateInstallationKeyRule],
|
||||
]);
|
||||
|
||||
WebsiteInstallation::first()->update([
|
||||
WebsiteInstallation::first()?->update([
|
||||
'step' => 1,
|
||||
'user_ip' => $request->ip(),
|
||||
]);
|
||||
@@ -38,7 +38,10 @@ class InstallationController extends Controller
|
||||
{
|
||||
$settings = $this->getSettingsForStep($currentStep);
|
||||
|
||||
return view('installation.step-' . $currentStep, [
|
||||
/** @var view-string $view */
|
||||
$view = 'installation.step-' . (string) $currentStep;
|
||||
|
||||
return view($view, [
|
||||
'settings' => $settings,
|
||||
]);
|
||||
}
|
||||
@@ -47,21 +50,27 @@ class InstallationController extends Controller
|
||||
{
|
||||
$this->updateSettings($request);
|
||||
|
||||
WebsiteInstallation::increment('step');
|
||||
WebsiteInstallation::query()->increment('step');
|
||||
|
||||
/** @var \App\Models\Miscellaneous\WebsiteInstallation|null $installation */
|
||||
$installation = WebsiteInstallation::first();
|
||||
|
||||
return to_route('installation.show-step', WebsiteInstallation::first()->step);
|
||||
return to_route('installation.show-step', $installation->step ?? 1);
|
||||
}
|
||||
|
||||
public function previousStep(): RedirectResponse
|
||||
{
|
||||
WebsiteInstallation::decrement('step');
|
||||
WebsiteInstallation::query()->decrement('step');
|
||||
|
||||
return to_route('installation.show-step', WebsiteInstallation::first()->step);
|
||||
/** @var \App\Models\Miscellaneous\WebsiteInstallation|null $installation */
|
||||
$installation = WebsiteInstallation::first();
|
||||
|
||||
return to_route('installation.show-step', $installation->step ?? 1);
|
||||
}
|
||||
|
||||
public function restartInstallation(): RedirectResponse
|
||||
{
|
||||
WebsiteInstallation::first()->update([
|
||||
WebsiteInstallation::first()?->update([
|
||||
'step' => 0,
|
||||
'installation_key' => Str::uuid(),
|
||||
'user_ip' => null,
|
||||
@@ -76,7 +85,7 @@ class InstallationController extends Controller
|
||||
|
||||
public function completeInstallation(): RedirectResponse
|
||||
{
|
||||
WebsiteInstallation::latest()->first()->update([
|
||||
WebsiteInstallation::latest()->first()?->update([
|
||||
'completed' => true,
|
||||
]);
|
||||
|
||||
@@ -100,10 +109,20 @@ class InstallationController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Collection<int, \App\Models\Miscellaneous\WebsiteSetting>
|
||||
*/
|
||||
private function getSettingsForStep(int $step): \Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
$settingsData = array_chunk(WebsiteSetting::all()->pluck('key')->toArray(), ceil(WebsiteSetting::count() / 4));
|
||||
$count = WebsiteSetting::count();
|
||||
/** @var int<1, max> $chunkSize */
|
||||
$chunkSize = $count > 0 ? (int) ceil($count / 4) : 1;
|
||||
|
||||
/** @var array<int, string> $keys */
|
||||
$keys = WebsiteSetting::query()->pluck('key')->toArray();
|
||||
$settingsData = array_chunk($keys, $chunkSize);
|
||||
|
||||
/** @var array<int, string> $settings */
|
||||
$settings = match ($step) {
|
||||
1 => $settingsData[0] ?? [],
|
||||
2 => $settingsData[1] ?? [],
|
||||
|
||||
@@ -33,9 +33,11 @@ class LogoGeneratorController extends Controller
|
||||
|
||||
$setting = WebsiteSetting::where('key', 'cms_logo')->first();
|
||||
|
||||
$setting->update([
|
||||
'value' => sprintf('%s/%s', $path, $filename),
|
||||
]);
|
||||
if ($setting) {
|
||||
$setting->update([
|
||||
'value' => sprintf('%s/%s', $path, $filename),
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Logo updated!']);
|
||||
}
|
||||
|
||||
@@ -16,10 +16,12 @@ class PaypalController extends Controller
|
||||
|
||||
private const string STATUS_COMPLETED = 'COMPLETED';
|
||||
|
||||
public function __construct(private PayPalClient $provider): void
|
||||
public function __construct(private PayPalClient $provider)
|
||||
{
|
||||
$this->provider = new PayPalClient;
|
||||
$this->provider->setApiCredentials(config('habbo.paypal'));
|
||||
/** @var array<mixed> $config */
|
||||
$config = config('habbo.paypal');
|
||||
$this->provider->setApiCredentials($config);
|
||||
$this->provider->getAccessToken();
|
||||
}
|
||||
|
||||
@@ -46,6 +48,7 @@ class PaypalController extends Controller
|
||||
],
|
||||
];
|
||||
|
||||
/** @var array<string, mixed> $response */
|
||||
$response = $this->provider->createOrder($orderData);
|
||||
|
||||
if (isset($response['id']) === false) {
|
||||
@@ -56,14 +59,19 @@ class PaypalController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($response['links'] as $links) {
|
||||
if ($links['rel'] === 'approve') {
|
||||
$request->user()->transactions()->create([
|
||||
/** @var array<int, array<string, string>> $links */
|
||||
$links = $response['links'];
|
||||
|
||||
foreach ($links as $link) {
|
||||
if ($link['rel'] === 'approve') {
|
||||
/** @var \App\Models\User $user */
|
||||
$user = $request->user();
|
||||
$user->transactions()->create([
|
||||
'transaction_id' => $response['id'],
|
||||
'amount' => 0,
|
||||
]);
|
||||
|
||||
return redirect()->away($links['href']);
|
||||
return redirect()->away($link['href']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +86,7 @@ class PaypalController extends Controller
|
||||
'token' => ['required'],
|
||||
]);
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = $request->user();
|
||||
|
||||
$transaction = $user->transactions()->where('transaction_id', $request['token'])->first();
|
||||
@@ -85,41 +94,71 @@ class PaypalController extends Controller
|
||||
return to_route('shop.index')->withErrors(['message' => __('Something went wrong, please try again later')]);
|
||||
}
|
||||
|
||||
$response = $this->provider->capturePaymentOrder($request['token']);
|
||||
$paymentDetails = $response['purchase_units'][0]['payments']['captures'][0];
|
||||
/** @var array<string, mixed> $response */
|
||||
$response = $this->provider->capturePaymentOrder($request->string('token')->toString());
|
||||
|
||||
if (isset($response['error'])) {
|
||||
/** @var array<string, mixed> $error */
|
||||
$error = $response['error'];
|
||||
/** @var array<int, array<string, string>> $details */
|
||||
$details = $error['details'] ?? [];
|
||||
$issue = $details[0]['issue'] ?? 'Unknown';
|
||||
$description = $details[0]['description'] ?? 'Unknown';
|
||||
|
||||
$transaction->update([
|
||||
'status' => $response['name'] ?? 'ERROR',
|
||||
'description' => sprintf('%s - %s', $issue, $description),
|
||||
'amount' => 0,
|
||||
]);
|
||||
|
||||
if (! isset($response['status'], $paymentDetails)) {
|
||||
Log::error('Invalid response from PayPal', ['response' => $response]);
|
||||
|
||||
return to_route('shop.index')->withErrors(['message' => __('Something went wrong, please try again later')]);
|
||||
return to_route('shop.index')->withErrors(['message' => __('Something went wrong, please check your paypal account to make sure nothing was deducted and try again')]);
|
||||
}
|
||||
|
||||
if (($response['status'] ?? null) === null) {
|
||||
$details = $response['error']['details'][0];
|
||||
$transaction->update([
|
||||
'status' => $response['name'],
|
||||
'description' => sprintf('%s - %s', $details['issue'], $details['description']),
|
||||
'amount' => 0,
|
||||
]);
|
||||
/** @var array<int, mixed> $purchaseUnits */
|
||||
$purchaseUnits = $response['purchase_units'] ?? [];
|
||||
/** @var array<string, mixed> $unit */
|
||||
$unit = $purchaseUnits[0] ?? [];
|
||||
/** @var array<string, mixed> $payments */
|
||||
$payments = $unit['payments'] ?? [];
|
||||
/** @var array<int, mixed> $captures */
|
||||
$captures = $payments['captures'] ?? [];
|
||||
|
||||
return to_route('shop.index')->withErrors(['message' => __('Something went wrong, please check your paypal account to make sure nothing was deducted and try again')]);
|
||||
if (! isset($captures[0])) {
|
||||
Log::error('Invalid response from PayPal', ['response' => $response]);
|
||||
return to_route('shop.index')->withErrors(['message' => __('Something went wrong, please try again later')]);
|
||||
}
|
||||
|
||||
/** @var array<string, mixed> $paymentDetails */
|
||||
$paymentDetails = $captures[0];
|
||||
|
||||
/** @var array<string, mixed> $amountDetails */
|
||||
$amountDetails = $paymentDetails['amount'] ?? [];
|
||||
|
||||
$paymentDetails = $response['purchase_units'][0]['payments']['captures'][0];
|
||||
if (! isset($response['status'])) {
|
||||
Log::error('Invalid response from PayPal', ['response' => $response]);
|
||||
|
||||
return to_route('shop.index')->withErrors(['message' => __('Something went wrong, please try again later')]);
|
||||
}
|
||||
|
||||
$status = $response['status'];
|
||||
|
||||
$transaction->update([
|
||||
'status' => $paymentDetails['status'],
|
||||
'amount' => $paymentDetails['amount']['value'],
|
||||
'currency' => $paymentDetails['amount']['currency_code'],
|
||||
'amount' => $amountDetails['value'] ?? 0,
|
||||
'currency' => $amountDetails['currency_code'] ?? 'USD',
|
||||
]);
|
||||
|
||||
if ($response['status'] !== self::STATUS_COMPLETED) {
|
||||
if ($status !== self::STATUS_COMPLETED) {
|
||||
return to_route('shop.index')->withErrors(
|
||||
['message' => $response['message'] ?? __('Something went wrong')],
|
||||
);
|
||||
}
|
||||
|
||||
$user->increment('website_balance', $paymentDetails['amount']['value']);
|
||||
$value = $amountDetails['value'] ?? 0;
|
||||
if (! is_numeric($value)) {
|
||||
$value = 0;
|
||||
}
|
||||
$user->increment('website_balance', (int) $value);
|
||||
|
||||
return to_route('shop.index')->with('success', __('Transaction successful'));
|
||||
}
|
||||
@@ -130,7 +169,10 @@ class PaypalController extends Controller
|
||||
'token' => ['required'],
|
||||
]);
|
||||
|
||||
$transaction = $request->user()->transactions()->where('transaction_id', $request['token'])->first();
|
||||
/** @var \App\Models\User $user */
|
||||
$user = $request->user();
|
||||
|
||||
$transaction = $user->transactions()->where('transaction_id', $request['token'])->first();
|
||||
if ($transaction !== null) {
|
||||
$transaction->update([
|
||||
'status' => self::STATUS_CANCELLED,
|
||||
|
||||
@@ -15,7 +15,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ShopController extends Controller
|
||||
{
|
||||
public function __construct(private readonly RconService $rconService): void {}
|
||||
public function __construct(private readonly RconService $rconService) {}
|
||||
|
||||
public function __invoke(?WebsiteShopCategory $category): \Illuminate\Contracts\View\View
|
||||
{
|
||||
@@ -56,7 +56,9 @@ class ShopController extends Controller
|
||||
|
||||
public function purchase(WebsiteShopArticle $package, Request $request, SendCurrency $sendCurrency): Response
|
||||
{
|
||||
$user = Auth::user();
|
||||
/** @var \App\Models\User $currentUser */
|
||||
$currentUser = Auth::user();
|
||||
$user = $currentUser;
|
||||
|
||||
if ($request->has('receiver')) {
|
||||
if (! $package->is_giftable) {
|
||||
@@ -72,13 +74,12 @@ class ShopController extends Controller
|
||||
['message' => __('Recipient not found')],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($package->give_rank && $user->rank >= $package->give_rank) {
|
||||
$message = __('You are already this or a higher rank');
|
||||
|
||||
if ($user->username !== Auth::user()->username) {
|
||||
if ($user->username !== $currentUser->username) {
|
||||
$message = __('The recipient is already this or a higher rank');
|
||||
}
|
||||
|
||||
@@ -87,27 +88,27 @@ class ShopController extends Controller
|
||||
);
|
||||
}
|
||||
|
||||
if (! $this->rconService->isConnected && $user->online === '1') {
|
||||
if (! $this->rconService->isConnected && $user->online) {
|
||||
return to_route('shop.index')->withErrors(
|
||||
['message' => __('Please logout before purchasing a package')],
|
||||
);
|
||||
}
|
||||
|
||||
if (Auth::user()->website_balance < $package->price()) {
|
||||
if ($currentUser->website_balance < $package->price()) {
|
||||
return to_route('shop.index')->withErrors(
|
||||
['message' => __('You need to top-up your account with another $:amount to purchase this package', ['amount' => ($package->price() - Auth::user()->website_balance)])],
|
||||
['message' => __('You need to top-up your account with another $:amount to purchase this package', ['amount' => ($package->price() - $currentUser->website_balance)])],
|
||||
);
|
||||
}
|
||||
|
||||
Auth::user()?->decrement('website_balance', $package->price());
|
||||
$currentUser->decrement('website_balance', $package->price());
|
||||
|
||||
$sendCurrency->execute($user, 'credits', $package->credits);
|
||||
$sendCurrency->execute($user, 'duckets', $package->duckets);
|
||||
$sendCurrency->execute($user, 'diamonds', $package->diamonds);
|
||||
$sendCurrency->execute($user, 'credits', (int) $package->credits);
|
||||
$sendCurrency->execute($user, 'duckets', (int) $package->duckets);
|
||||
$sendCurrency->execute($user, 'diamonds', (int) $package->diamonds);
|
||||
|
||||
if ($package->give_rank) {
|
||||
if ($this->rconService->isConnected) {
|
||||
$this->rconService->setRank($user, $package->give_rank);
|
||||
$this->rconService->setRank($user, (int) $package->give_rank);
|
||||
$this->rconService->disconnectUser($user);
|
||||
} else {
|
||||
$user->update([
|
||||
@@ -121,22 +122,28 @@ class ShopController extends Controller
|
||||
}
|
||||
|
||||
if ($package->furniture) {
|
||||
$this->handleFurniture(json_decode($package->furniture, true));
|
||||
$furniture = json_decode($package->furniture, true);
|
||||
if (is_array($furniture)) {
|
||||
$this->handleFurniture($user, $furniture);
|
||||
}
|
||||
}
|
||||
|
||||
$message = __('You have successfully purchased the package :name', ['name' => $package->name]);
|
||||
|
||||
if ($user->username !== Auth::user()->username) {
|
||||
if ($user->username !== $currentUser->username) {
|
||||
$message = __('You have successfully purchased the package :name for :username', ['name' => $package->name, 'username' => $user->username]);
|
||||
}
|
||||
|
||||
return to_route('shop.index')->with('success', $message);
|
||||
}
|
||||
|
||||
public function handleFurniture(array $furniture)
|
||||
/**
|
||||
* @param array<mixed> $furniture
|
||||
*/
|
||||
public function handleFurniture(User $user, array $furniture): void
|
||||
{
|
||||
$sendFurniture = app(SendFurniture::class);
|
||||
|
||||
$sendFurniture->execute(Auth::user(), $furniture);
|
||||
$sendFurniture->execute($user, $furniture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@ class ShopVoucherController extends Controller
|
||||
{
|
||||
public function __invoke(ShopVoucherFormRequest $request): RedirectResponse
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = $request->user();
|
||||
$voucher = WebsiteShopVoucher::where('code', $request->string('code'))->first();
|
||||
$voucher = WebsiteShopVoucher::where('code', $request->string('code')->toString())->first();
|
||||
|
||||
if (is_null($voucher) || ($voucher->expires_at && $voucher->expires_at->lte(now()))) {
|
||||
return back()->withErrors([
|
||||
|
||||
@@ -18,12 +18,15 @@ class AccountSettingsController extends Controller
|
||||
private readonly SessionService $sessionService,
|
||||
private readonly UserService $userService,
|
||||
private readonly RconService $rconService
|
||||
): void {}
|
||||
) {}
|
||||
|
||||
public function edit(): View
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
return view('user.settings.account', [
|
||||
'user' => Auth::user()->load('settings:allow_name_change'),
|
||||
'user' => $user->load('settings:allow_name_change'),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -46,7 +49,7 @@ class AccountSettingsController extends Controller
|
||||
|
||||
// $allowedNameChange = $user->settings?->allow_name_change && $user->username !== $request->input('username');
|
||||
|
||||
if (! $this->rconService->isConnected() && Auth::user()->online === '1') {
|
||||
if (! $this->rconService->isConnected() && $user->online) {
|
||||
return back()->withErrors('You must be offline to change your account settings');
|
||||
}
|
||||
|
||||
@@ -55,12 +58,12 @@ class AccountSettingsController extends Controller
|
||||
$this->userService->updateField($user, 'username', $request->input('username'));
|
||||
} **/
|
||||
if ($user->mail !== $request->input('mail')) {
|
||||
$this->userService->updateField($user, 'mail', $request->input('mail'));
|
||||
$this->userService->updateField($user, 'mail', $request->string('mail')->toString());
|
||||
}
|
||||
|
||||
if ($user->motto !== $request->input('motto')) {
|
||||
$this->rconService->setMotto($user, $request->input('motto'));
|
||||
$this->userService->updateField($user, 'motto', $request->input('motto'));
|
||||
$this->rconService->setMotto($user, $request->string('motto')->toString());
|
||||
$this->userService->updateField($user, 'motto', $request->string('motto')->toString());
|
||||
}
|
||||
|
||||
return to_route('settings.account.show')->with('success', __('Your account settings has been updated'));
|
||||
|
||||
@@ -16,8 +16,11 @@ class BannedController extends Controller
|
||||
->orderByDesc('id')
|
||||
->first();
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
return view('banned', [
|
||||
'ban' => $ipBan ?? Auth::user()->ban,
|
||||
'ban' => $ipBan ?? $user->ban,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ class ForgotPasswordController extends Controller
|
||||
'token' => $token,
|
||||
]);
|
||||
|
||||
Mail::send('email.forgetPassword', ['token' => $token], function ($message) use ($request): void {
|
||||
$message->to($request->mail);
|
||||
Mail::send('email.forgetPassword', ['token' => $token], function (\Illuminate\Mail\Message $message) use ($request): void {
|
||||
$message->to($request->string('mail')->toString());
|
||||
$message->subject('Reset Password');
|
||||
});
|
||||
}
|
||||
@@ -47,7 +47,12 @@ class ForgotPasswordController extends Controller
|
||||
if ($prt === null) {
|
||||
return to_route('forgot.password.get')->withErrors('message', __('This token has expired!'));
|
||||
}
|
||||
$tokenExpiration = \Illuminate\Support\Facades\Date::now()->subMinutes(config('habbo.password_reset_token_time'));
|
||||
$resetTime = config('habbo.password_reset_token_time');
|
||||
if (! is_numeric($resetTime)) {
|
||||
$resetTime = 60;
|
||||
}
|
||||
|
||||
$tokenExpiration = \Illuminate\Support\Facades\Date::now()->subMinutes((int) $resetTime);
|
||||
if ($prt->created_at->gte($tokenExpiration)) {
|
||||
$prt->delete();
|
||||
|
||||
@@ -66,12 +71,14 @@ class ForgotPasswordController extends Controller
|
||||
'password_confirmation' => ['required'],
|
||||
]);
|
||||
|
||||
$prt = PasswordResetToken::select('email', 'token')->where('token', $token)->first();
|
||||
if ($prt === null) {
|
||||
$prt = PasswordResetToken::with('user')->select('email', 'token')->where('token', $token)->first();
|
||||
if ($prt === null || $prt->user === null) {
|
||||
return to_route('forgot.password.get')->withErrors('message', __('This token has expired!'));
|
||||
}
|
||||
|
||||
$prt->user->changePassword($request->password);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = $prt->user;
|
||||
$user->changePassword($request->string('password')->toString());
|
||||
$prt->delete();
|
||||
|
||||
return to_route('login')->with('success', __('Your password has been successfully reset!'));
|
||||
|
||||
@@ -28,7 +28,16 @@ class GuestbookController extends Controller
|
||||
|
||||
public function destroy(User $user, WebsiteUserGuestbook $guestbook): RedirectResponse
|
||||
{
|
||||
if ($guestbook->user_id !== Auth::id() && $guestbook->profile_id !== $user->id && Auth::user()->rank < (int) setting('min_staff_rank')) {
|
||||
/** @var \App\Models\User|null $currentUser */
|
||||
$currentUser = Auth::user();
|
||||
|
||||
if ($currentUser === null) {
|
||||
return back()->withErrors([
|
||||
'message' => __('You must be logged in.'),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($guestbook->user_id !== $currentUser->id && $guestbook->profile_id !== $user->id && $currentUser->rank < (int) setting('min_staff_rank')) {
|
||||
return back()->withErrors([
|
||||
'message' => __('Do do not have permission to delete this message'),
|
||||
]);
|
||||
@@ -41,12 +50,19 @@ class GuestbookController extends Controller
|
||||
|
||||
private function validateGuestbookPost(User $user, GuestbookFormRequest $request): ?RedirectResponse
|
||||
{
|
||||
if ($user->id === $request->user()->id) {
|
||||
/** @var \App\Models\User|null $currentUser */
|
||||
$currentUser = $request->user();
|
||||
|
||||
if ($currentUser === null) {
|
||||
return $this->redirectWithError(__('You must be logged in.'));
|
||||
}
|
||||
|
||||
if ($user->id === $currentUser->id) {
|
||||
return $this->redirectWithError(__('You cannot post a message on your own profile.'));
|
||||
}
|
||||
|
||||
$maxAllowedPostCount = in_array(setting('max_guestbook_posts_per_profile'), ['', '0'], true) ? 3 : (int) setting('max_guestbook_posts_per_profile');
|
||||
if ($user->profileGuestbook()->where('user_id', $request->user()->id)->count() >= $maxAllowedPostCount) {
|
||||
$maxAllowedPostCount = in_array((string) setting('max_guestbook_posts_per_profile'), ['', '0'], true) ? 3 : (int) setting('max_guestbook_posts_per_profile');
|
||||
if ($user->profileGuestbook()->where('user_id', $currentUser->id)->count() >= $maxAllowedPostCount) {
|
||||
return $this->redirectWithError(__('You have already posted :count messages on this profile.', ['count' => $maxAllowedPostCount]));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,12 @@ class MeController extends Controller
|
||||
{
|
||||
public function __invoke(): View
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
return view('user.me', [
|
||||
'onlineFriends' => Auth::user()?->getOnlineFriends(),
|
||||
'user' => Auth::user()?->load('permission:id,rank_name'),
|
||||
'onlineFriends' => $user->getOnlineFriends(),
|
||||
'user' => $user->load('permission:id,rank_name'),
|
||||
'articles' => WebsiteArticle::whereHas('user')->with('user:id,username,look')->latest()->take(5)->get(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,11 @@ class PasswordSettingsController extends Controller
|
||||
|
||||
public function update(PasswordSettingsFormRequest $request): RedirectResponse
|
||||
{
|
||||
Auth::user()->update([
|
||||
'password' => Hash::make($request->input('password')),
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
$user->update([
|
||||
'password' => Hash::make($request->string('password')->toString()),
|
||||
]);
|
||||
|
||||
return to_route('settings.password.show')->with('success', __('Your password has been changed!'));
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Models\Game\Player\MessengerFriendship;
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
@@ -30,12 +31,12 @@ class ProfileController extends Controller
|
||||
private function loadUserRelations(User $user): User
|
||||
{
|
||||
return $user->load([
|
||||
'badges' => function ($badges): void {
|
||||
'badges' => function (HasMany $badges): void {
|
||||
$badges->where('slot_id', '>', '0')
|
||||
->orderBy('slot_id')
|
||||
->take(5);
|
||||
},
|
||||
'rooms' => function ($rooms): void {
|
||||
'rooms' => function (HasMany $rooms): void {
|
||||
$rooms->select('id', 'owner_id', 'name', 'users')
|
||||
->orderByDesc('users')
|
||||
->orderBy('id');
|
||||
@@ -43,6 +44,9 @@ class ProfileController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, MessengerFriendship>
|
||||
*/
|
||||
private function getUserFriends(int $userId): Collection
|
||||
{
|
||||
return MessengerFriendship::select('user_two_id')
|
||||
@@ -54,6 +58,9 @@ class ProfileController extends Controller
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, GuildMember>
|
||||
*/
|
||||
private function getUserGroups(int $userId): Collection
|
||||
{
|
||||
return GuildMember::query()
|
||||
|
||||
@@ -11,17 +11,22 @@ class ReferralController extends Controller
|
||||
{
|
||||
public function __invoke(RconService $rcon): RedirectResponse
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
if (! $user->referrals || $user->referrals->referrals_total < setting('referrals_needed')) {
|
||||
|
||||
/** @var \App\Models\User\UserReferral|null $referrals */
|
||||
$referrals = $user->referrals;
|
||||
|
||||
if ($referrals === null || $referrals->referrals_total < (int) setting('referrals_needed')) {
|
||||
return back()->withErrors([
|
||||
'message' => __('You do not have enough referrals to claim your reward'),
|
||||
]);
|
||||
}
|
||||
|
||||
// Decrease the total amount of referrals with the amount needed to claim reward
|
||||
$user->referrals->decrement('referrals_total', setting('referrals_needed'));
|
||||
$referrals->decrement('referrals_total', (int) setting('referrals_needed'));
|
||||
|
||||
$rcon->giveDiamonds($user, setting('referral_reward_amount'));
|
||||
$rcon->giveDiamonds($user, (int) setting('referral_reward_amount'));
|
||||
|
||||
// Log the claim
|
||||
$user->claimedReferralLog()->create([
|
||||
|
||||
@@ -25,7 +25,10 @@ class TwoFactorAuthenticationController extends Controller
|
||||
|
||||
public function verify(Request $request): RedirectResponse
|
||||
{
|
||||
$confirmed = $request->user()->confirmTwoFactorAuthentication($request->input('code'));
|
||||
/** @var \App\Models\User $user */
|
||||
$user = $request->user();
|
||||
|
||||
$confirmed = $user->confirmTwoFactorAuthentication($request->string('code')->toString());
|
||||
if (! $confirmed) {
|
||||
return back()->withErrors('Invalid Two Factor Authentication code');
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ namespace App\Models\Community\Staff;
|
||||
use App\Models\Game\Permission;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class WebsiteOpenPosition extends Model
|
||||
{
|
||||
@@ -12,7 +14,7 @@ class WebsiteOpenPosition extends Model
|
||||
|
||||
protected $table = 'website_open_positions';
|
||||
|
||||
use HasFactory;
|
||||
// use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'permission_id',
|
||||
@@ -25,21 +27,31 @@ class WebsiteOpenPosition extends Model
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
static::deleting(function ($openPosition): void {
|
||||
static::deleting(function (WebsiteOpenPosition $openPosition): void {
|
||||
WebsiteStaffApplications::where('rank_id', $openPosition->permission_id)->delete();
|
||||
});
|
||||
}
|
||||
|
||||
public function permission()
|
||||
/**
|
||||
* @return BelongsTo<Permission, $this>
|
||||
*/
|
||||
public function permission(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Permission::class, 'permission_id', 'id');
|
||||
}
|
||||
|
||||
public function applications()
|
||||
/**
|
||||
* @return HasMany<WebsiteStaffApplications, $this>
|
||||
*/
|
||||
public function applications(): HasMany
|
||||
{
|
||||
return $this->hasMany(WebsiteStaffApplications::class, 'rank_id', 'permission_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Database\Eloquent\Builder<WebsiteOpenPosition> $query
|
||||
* @return \Illuminate\Database\Eloquent\Builder<WebsiteOpenPosition>
|
||||
*/
|
||||
#[\Illuminate\Database\Eloquent\Attributes\Scope]
|
||||
protected function canApply($query)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,14 @@ namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property string $id
|
||||
* @property int|null $user_id
|
||||
* @property string|null $ip_address
|
||||
* @property string|null $user_agent
|
||||
* @property string $payload
|
||||
* @property int $last_activity
|
||||
*/
|
||||
class Session extends Model
|
||||
{
|
||||
protected $guarded = ['id'];
|
||||
|
||||
@@ -8,6 +8,13 @@ class WebsiteShopVoucher extends Model
|
||||
{
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'expires_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -66,6 +66,7 @@ use Spatie\Activitylog\Traits\LogsActivity;
|
||||
* @property \Illuminate\Support\Carbon|null $two_factor_confirmed_at
|
||||
* @property string|null $remember_token
|
||||
* @property \Illuminate\Support\Carbon|null $email_verified_at
|
||||
* @property int $website_balance
|
||||
*/
|
||||
class User extends Authenticatable implements FilamentUser, HasName
|
||||
{
|
||||
@@ -260,23 +261,32 @@ class User extends Authenticatable implements FilamentUser, HasName
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Collection<int, MessengerFriendship>
|
||||
* @return \Illuminate\Database\Eloquent\Collection<int, \App\Models\Game\Player\MessengerFriendship>
|
||||
*/
|
||||
public function getOnlineFriends(int $total = 10): \Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
return $this->friends()
|
||||
/** @var \Illuminate\Database\Eloquent\Collection<int, \App\Models\Game\Player\MessengerFriendship> $friends */
|
||||
$friends = $this->friends()
|
||||
->select(['user_two_id', 'users.id', 'users.username', 'users.look', 'users.motto', 'users.last_online'])
|
||||
->join('users', 'users.id', '=', 'user_two_id')
|
||||
->where('users.online', '1')
|
||||
->inRandomOrder()
|
||||
->limit($total)
|
||||
->get();
|
||||
|
||||
return $friends;
|
||||
}
|
||||
|
||||
public function confirmTwoFactorAuthentication(string $code): void
|
||||
public function confirmTwoFactorAuthentication(string $code): bool
|
||||
{
|
||||
$secret = $this->two_factor_secret;
|
||||
|
||||
if (! is_string($secret)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$codeIsValid = app(TwoFactorAuthenticationProvider::class)
|
||||
->verify(decrypt($this->two_factor_secret), $code);
|
||||
->verify(decrypt($secret), $code);
|
||||
|
||||
if (! $codeIsValid) {
|
||||
return false;
|
||||
@@ -289,12 +299,12 @@ class User extends Authenticatable implements FilamentUser, HasName
|
||||
return true;
|
||||
}
|
||||
|
||||
public function hasAppliedForPosition(int $rankId)
|
||||
public function hasAppliedForPosition(int $rankId): bool
|
||||
{
|
||||
return $this->applications()->where('rank_id', '=', $rankId)->exists();
|
||||
}
|
||||
|
||||
public function changePassword(string $newPassword)
|
||||
public function changePassword(string $newPassword): void
|
||||
{
|
||||
$this->password = Hash::make($newPassword);
|
||||
$this->save();
|
||||
@@ -307,7 +317,7 @@ class User extends Authenticatable implements FilamentUser, HasName
|
||||
|
||||
public function canAccessPanel(Panel $panel): bool
|
||||
{
|
||||
return hasHousekeepingPermission('can_access_housekeeping');
|
||||
return (bool) hasHousekeepingPermission('can_access_housekeeping');
|
||||
}
|
||||
|
||||
public function getActivitylogOptions(): LogOptions
|
||||
|
||||
@@ -16,11 +16,17 @@ class Ban extends Model
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* @return BelongsTo<User, $this>
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<User, $this>
|
||||
*/
|
||||
public function staff(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_staff_id');
|
||||
|
||||
@@ -10,6 +10,9 @@ class ClaimedReferralLog extends Model
|
||||
{
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* @return BelongsTo<User, $this>
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
|
||||
@@ -6,10 +6,20 @@ use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $user_id
|
||||
* @property int $referrals_total
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
*/
|
||||
class UserReferral extends Model
|
||||
{
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* @return BelongsTo<User, $this>
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
|
||||
@@ -10,11 +10,17 @@ class WebsiteUserGuestbook extends Model
|
||||
{
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* @return BelongsTo<User, $this>
|
||||
*/
|
||||
public function profile(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'profile_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<User, $this>
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
|
||||
@@ -4,35 +4,41 @@ namespace App\Models;
|
||||
|
||||
use App\Services\SettingsService;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
* @property string $image
|
||||
*/
|
||||
class WebsiteAd extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'image',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Casts\Attribute<string, never>
|
||||
*/
|
||||
protected function imageUrl(): \Illuminate\Database\Eloquent\Casts\Attribute
|
||||
{
|
||||
return \Illuminate\Database\Eloquent\Casts\Attribute::make(get: function () {
|
||||
$settingsService = app(SettingsService::class);
|
||||
|
||||
/** @var string $adsPicturePath */
|
||||
$adsPicturePath = Cache::remember('ads_picture_path', 3600, fn () => $settingsService->getOrDefault('ads_picture_path'));
|
||||
if (! str_starts_with((string) $adsPicturePath, 'http')) {
|
||||
$adsPicturePath = rtrim((string) config('app.url'), '/') . '/' . ltrim((string) $adsPicturePath, '/');
|
||||
|
||||
if (! str_starts_with($adsPicturePath, 'http')) {
|
||||
$adsPicturePath = rtrim((string) config('app.url'), '/') . '/' . ltrim($adsPicturePath, '/');
|
||||
}
|
||||
return rtrim((string) $adsPicturePath, '/') . '/' . $this->image;
|
||||
return rtrim($adsPicturePath, '/') . '/' . $this->image;
|
||||
});
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
protected static function booted()
|
||||
{
|
||||
static::deleting(function ($websiteAd): void {
|
||||
static::deleting(function (WebsiteAd $websiteAd): void {
|
||||
try {
|
||||
$websiteAd->configureAdsDisk();
|
||||
|
||||
|
||||
@@ -2,13 +2,10 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class WebsiteBadge extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'badge_key',
|
||||
'badge_name',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class WebsiteDrawBadge extends Model
|
||||
{
|
||||
@@ -10,7 +11,10 @@ class WebsiteDrawBadge extends Model
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
public function user()
|
||||
/**
|
||||
* @return BelongsTo<User, $this>
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
}
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
|
||||
class Wordfilter extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use LogsActivity;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
@@ -38,11 +38,21 @@ class FortifyServiceProvider extends ServiceProvider
|
||||
{
|
||||
Fortify::createUsersUsing(CreateNewUser::class);
|
||||
|
||||
RateLimiter::for('login', fn (Request $request) => Limit::perMinute(5)->by($request->input('username') . $request->ip()));
|
||||
RateLimiter::for('login', function (Request $request) {
|
||||
$username = $request->input('username');
|
||||
$ip = $request->ip();
|
||||
|
||||
RateLimiter::for('two-factor', fn (Request $request) => Limit::perMinute(5)->by($request->session()->get('login.id')));
|
||||
return Limit::perMinute(5)->by((is_string($username) ? $username : '') . (is_string($ip) ? $ip : ''));
|
||||
});
|
||||
|
||||
Fortify::loginView(fn () => view(\Illuminate\Auth\Events\Login::class, [
|
||||
RateLimiter::for('two-factor', function (Request $request) {
|
||||
$loginId = $request->session()->get('login.id');
|
||||
|
||||
return Limit::perMinute(5)->by(is_string($loginId) ? $loginId : '');
|
||||
});
|
||||
|
||||
/** @phpstan-ignore argument.type */
|
||||
Fortify::loginView(fn () => view('auth.login', [
|
||||
'articles' => WebsiteArticle::latest('id')
|
||||
->take(4)
|
||||
->has('user')
|
||||
@@ -82,7 +92,7 @@ class FortifyServiceProvider extends ServiceProvider
|
||||
$this->authenticate();
|
||||
}
|
||||
|
||||
private function authenticate()
|
||||
private function authenticate(): void
|
||||
{
|
||||
Fortify::authenticateThrough(fn () => array_filter([
|
||||
config('fortify.limiters.login') ? null : EnsureLoginIsNotThrottled::class,
|
||||
|
||||
@@ -14,7 +14,10 @@ class CurrentPasswordRule implements InvokableRule
|
||||
*/
|
||||
public function __invoke(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
if (! Hash::check($value, Auth::user()->password)) {
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
if (! Hash::check(is_string($value) ? $value : '', $user->password)) {
|
||||
$fail('It seems like your current password is wrong.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ use Illuminate\Contracts\Validation\InvokableRule;
|
||||
|
||||
class GoogleRecaptchaRule implements InvokableRule
|
||||
{
|
||||
public function __invoke(string $attribute, mixed $value, Closure $fail)
|
||||
public function __invoke(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
// If recaptcha is disabled
|
||||
if ((int) setting('google_recaptcha_enabled') === 0) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
$client = new Client;
|
||||
@@ -29,10 +29,14 @@ class GoogleRecaptchaRule implements InvokableRule
|
||||
|
||||
if ($response->getStatusCode() !== 200) {
|
||||
$fail(__('The Google recaptcha was not successful.'));
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var \stdClass $body */
|
||||
$body = json_decode((string) $response->getBody());
|
||||
|
||||
return $body->success;
|
||||
if (! isset($body->success) || ! $body->success) {
|
||||
$fail(__('The Google recaptcha was not successful.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ class Password implements Rule
|
||||
/**
|
||||
* Set the minimum length of the password.
|
||||
*/
|
||||
public function length(int $length)
|
||||
public function length(int $length): static
|
||||
{
|
||||
$this->length = $length;
|
||||
|
||||
|
||||
@@ -10,10 +10,11 @@ class TurnstileCheck implements ValidationRule
|
||||
{
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
$response = LaravelTurnstile::validate($value);
|
||||
$response = LaravelTurnstile::validate(is_string($value) ? $value : '');
|
||||
|
||||
if (! $response['success'] && setting('cloudflare_turnstile_enabled')) {
|
||||
$fail(__(config('turnstile.error_messages.turnstile_check_message')));
|
||||
$message = config('turnstile.error_messages.turnstile_check_message');
|
||||
$fail(__(is_string($message) ? $message : 'Turnstile check failed.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,10 @@ class ValidateInstallationKeyRule implements ValidationRule
|
||||
{
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
if ($value !== WebsiteInstallation::first()->installation_key) {
|
||||
/** @var WebsiteInstallation|null $installation */
|
||||
$installation = WebsiteInstallation::first();
|
||||
|
||||
if (! $installation || $value !== $installation->installation_key) {
|
||||
$fail('The :attribute does not match');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,17 @@ use Illuminate\Support\Str;
|
||||
|
||||
class WebsiteWordfilterRule implements InvokableRule
|
||||
{
|
||||
public function __invoke(string $attribute, mixed $value, Closure $fail)
|
||||
public function __invoke(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
$words = WebsiteWordfilter::get()
|
||||
->pluck('word')
|
||||
->toArray();
|
||||
/** @var array<string> $words */
|
||||
$words = WebsiteWordfilter::pluck('word')->map(fn ($item) => is_scalar($item) ? (string) $item : '')->all();
|
||||
|
||||
if (setting('website_wordfilter_enabled') === '1' && in_array(strtolower((string) $value), $words) || Str::contains(strtolower((string) $value), $words)) {
|
||||
$fail(__('You entered something that is not allowed on :hotel', ['hotel' => setting('hotel_name')]));
|
||||
if (setting('website_wordfilter_enabled') === '1') {
|
||||
$stringValue = strtolower(is_scalar($value) ? (string) $value : '');
|
||||
|
||||
if (in_array($stringValue, $words) || Str::contains($stringValue, $words)) {
|
||||
$fail(__('You entered something that is not allowed on :hotel', ['hotel' => setting('hotel_name')]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,13 @@ use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class ArticleService
|
||||
{
|
||||
public function getArticles(bool $paginate = false, int $perPage = 8): array|Collection|LengthAwarePaginator
|
||||
/**
|
||||
* @return Collection<int, WebsiteArticle>|LengthAwarePaginator<int, WebsiteArticle>
|
||||
*/
|
||||
public function getArticles(bool $paginate = false, int $perPage = 8): Collection|LengthAwarePaginator
|
||||
{
|
||||
$query = WebsiteArticle::with(['user' => function ($query): void {
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $query */
|
||||
$query->select('id', 'username', 'look');
|
||||
}])->orderByDesc('id');
|
||||
|
||||
|
||||
@@ -9,11 +9,14 @@ use Illuminate\Http\Request;
|
||||
|
||||
class ReactionService
|
||||
{
|
||||
/**
|
||||
* @return array{success: bool, added?: bool, username?: string}
|
||||
*/
|
||||
public function toggleReaction(WebsiteArticle $article, User $user, Request $request): array
|
||||
{
|
||||
$reaction = $request->get('reaction');
|
||||
|
||||
if (! is_string($reaction) || ! in_array($reaction, config('habbo.reactions'))) {
|
||||
if (! is_string($reaction) || ! in_array($reaction, (array) config('habbo.reactions'))) {
|
||||
return ['success' => false];
|
||||
}
|
||||
|
||||
@@ -29,7 +32,7 @@ class ReactionService
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'added' => $existingReaction?->active ?? true,
|
||||
'added' => $existingReaction ? $existingReaction->active : true,
|
||||
'username' => $user->username,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -8,12 +8,18 @@ use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class RareValueCategoriesService
|
||||
{
|
||||
/**
|
||||
* @return Collection<int, WebsiteRareValueCategory>
|
||||
*/
|
||||
public function fetchAllCategories(): Collection
|
||||
{
|
||||
return WebsiteRareValueCategory::all();
|
||||
}
|
||||
|
||||
public function fetchCategoriesByPriority(): Builder|Collection
|
||||
/**
|
||||
* @return Collection<int, WebsiteRareValueCategory>
|
||||
*/
|
||||
public function fetchCategoriesByPriority(): Collection
|
||||
{
|
||||
return WebsiteRareValueCategory::orderBy('priority')->with('furniture')->get();
|
||||
}
|
||||
@@ -23,12 +29,16 @@ class RareValueCategoriesService
|
||||
return WebsiteRareValueCategory::orderBy('priority')->whereId($id)->with('furniture')->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, WebsiteRareValueCategory>
|
||||
*/
|
||||
public function searchCategories(string $searchTerm): Collection
|
||||
{
|
||||
return WebsiteRareValueCategory::orderBy('priority')->whereHas('furniture', function ($query) use ($searchTerm): void {
|
||||
$query->where('name', 'like', '%' . $searchTerm . '%');
|
||||
})
|
||||
->with(['furniture' => function ($query) use ($searchTerm): void {
|
||||
/** @var Builder $query */
|
||||
$query->where('name', 'like', '%' . $searchTerm . '%');
|
||||
}])
|
||||
->get();
|
||||
|
||||
@@ -16,17 +16,20 @@ class StaffApplicationService
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, WebsiteOpenPosition>
|
||||
*/
|
||||
public function fetchOpenPositions(): Collection
|
||||
{
|
||||
return WebsiteOpenPosition::canApply()->with('permission')->get();
|
||||
}
|
||||
|
||||
public function hasUserAppliedForPosition($user, $positionId): bool
|
||||
public function hasUserAppliedForPosition(User $user, int $positionId): bool
|
||||
{
|
||||
return $user->applications()->where('rank_id', $positionId)->exists();
|
||||
}
|
||||
|
||||
public function isPositionOpenForApplication($position): bool
|
||||
public function isPositionOpenForApplication(WebsiteOpenPosition $position): bool
|
||||
{
|
||||
$currentTime = now();
|
||||
|
||||
|
||||
@@ -10,22 +10,32 @@ use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class StaffService
|
||||
{
|
||||
/**
|
||||
* @return Collection<int, Permission>
|
||||
*/
|
||||
public function fetchStaffPositions(): Collection
|
||||
{
|
||||
$cacheEnabled = setting('enable_caching') === '1';
|
||||
|
||||
if ($cacheEnabled && Cache::has('staff_positions')) {
|
||||
/** @var Collection<int, Permission> */
|
||||
return Cache::get('staff_positions');
|
||||
}
|
||||
|
||||
/** @var \App\Models\User|null $user */
|
||||
$user = Auth::user();
|
||||
$userRank = $user ? $user->rank : 0;
|
||||
|
||||
/** @var Collection<int, Permission> $employees */
|
||||
$employees = Permission::query()
|
||||
->select('id', 'rank_name', 'badge', 'staff_color', 'job_description')
|
||||
->when(Auth::user()->rank < (int) setting('min_rank_to_see_hidden_staff'), fn ($query) => $query->where('hidden_rank', false))
|
||||
->when($userRank < (int) setting('min_rank_to_see_hidden_staff'), fn ($query) => $query->where('hidden_rank', false))
|
||||
->where('id', '>=', setting('min_staff_rank'))
|
||||
->orderByDesc('id')
|
||||
->with(['users' => function ($query): void {
|
||||
->with(['users' => function ($query) use ($userRank): void {
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $query */
|
||||
$query->select('id', 'username', 'rank', 'motto', 'look', 'hidden_staff', 'online')
|
||||
->when(Auth::user()->rank < (int) setting('min_rank_to_see_hidden_staff'), fn ($query) => $query->where('hidden_staff', false));
|
||||
->when($userRank < (int) setting('min_rank_to_see_hidden_staff'), fn ($query) => $query->where('hidden_staff', false));
|
||||
}])
|
||||
->get();
|
||||
|
||||
@@ -37,14 +47,19 @@ class StaffService
|
||||
return $employees;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, int>
|
||||
*/
|
||||
public function fetchEmployeeIds(): array
|
||||
{
|
||||
$cacheEnabled = setting('enable_caching') === '1';
|
||||
|
||||
if ($cacheEnabled && Cache::has('staff_ids')) {
|
||||
/** @var array<int, int> */
|
||||
return Cache::get('staff_ids');
|
||||
}
|
||||
|
||||
/** @var array<int, int> $staffIds */
|
||||
$staffIds = User::select('id')
|
||||
->where('rank', '>=', setting('min_staff_rank'))
|
||||
->get()
|
||||
|
||||
@@ -8,18 +8,24 @@ use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class TeamService
|
||||
{
|
||||
/**
|
||||
* @return Collection<int, WebsiteTeam>
|
||||
*/
|
||||
public function fetchTeams(): Collection
|
||||
{
|
||||
$cacheEnabled = setting('enable_caching') === '1';
|
||||
|
||||
if (Cache::has('hotel_teams') && $cacheEnabled) {
|
||||
/** @var Collection<int, WebsiteTeam> */
|
||||
return Cache::get('hotel_teams');
|
||||
}
|
||||
|
||||
/** @var Collection<int, WebsiteTeam> $employees */
|
||||
$employees = WebsiteTeam::select(['id', 'rank_name', 'badge', 'staff_color', 'staff_background', 'job_description'])
|
||||
->where('hidden_rank', false)
|
||||
->orderByDesc('id')
|
||||
->with(['users' => function ($query): void {
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $query */
|
||||
$query->select('id', 'username', 'look', 'motto', 'rank', 'team_id', 'online');
|
||||
}])
|
||||
->get();
|
||||
|
||||
@@ -29,7 +29,7 @@ class FindRetrosService
|
||||
/**
|
||||
* Initialise Find Retros Service
|
||||
*/
|
||||
public function __construct(): void
|
||||
public function __construct()
|
||||
{
|
||||
$this->client = new Client(['verify' => false]);
|
||||
}
|
||||
@@ -43,8 +43,9 @@ class FindRetrosService
|
||||
return true;
|
||||
}
|
||||
|
||||
$cacheKey = sprintf(self::FIND_RETROS_CACHE_KEY, request()->ip());
|
||||
if (request()->ip() === '127.0.0.1') {
|
||||
$ip = request()->ip();
|
||||
$cacheKey = sprintf(self::FIND_RETROS_CACHE_KEY, is_scalar($ip) ? (string) $ip : '');
|
||||
if ($ip === '127.0.0.1') {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -56,7 +57,14 @@ class FindRetrosService
|
||||
return true;
|
||||
}
|
||||
|
||||
$uri = sprintf(self::FIND_RETROS_VERIFY_URI, config('habbo.findretros.api'), config('habbo.findretros.name'), request()->ip());
|
||||
$api = config('habbo.findretros.api');
|
||||
$name = config('habbo.findretros.name');
|
||||
|
||||
$uri = sprintf(self::FIND_RETROS_VERIFY_URI,
|
||||
is_scalar($api) ? (string) $api : '',
|
||||
is_scalar($name) ? (string) $name : '',
|
||||
is_scalar($ip) ? (string) $ip : ''
|
||||
);
|
||||
$request = $this->client->get($uri);
|
||||
$response = $request->getBody()->getContents();
|
||||
|
||||
@@ -74,6 +82,12 @@ class FindRetrosService
|
||||
*/
|
||||
public function getRedirectUri(): string
|
||||
{
|
||||
return sprintf(self::FIND_RETROS_REDIRECT_URI, config('habbo.findretros.api'), config('habbo.findretros.name'));
|
||||
$api = config('habbo.findretros.api');
|
||||
$name = config('habbo.findretros.name');
|
||||
|
||||
return sprintf(self::FIND_RETROS_REDIRECT_URI,
|
||||
is_scalar($api) ? (string) $api : '',
|
||||
is_scalar($name) ? (string) $name : ''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,19 +7,29 @@ use Illuminate\Support\Collection;
|
||||
|
||||
class HousekeepingPermissionsService
|
||||
{
|
||||
/** @var Collection<string, int>|null */
|
||||
public ?Collection $permissions;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->permissions = WebsiteHousekeepingPermission::all()->pluck('min_rank', 'permission');
|
||||
/** @var Collection<string, int> */
|
||||
$permissions = WebsiteHousekeepingPermission::pluck('min_rank', 'permission');
|
||||
$this->permissions = $permissions;
|
||||
}
|
||||
|
||||
public function getOrDefault(string $permissionName, bool $default = false): bool
|
||||
{
|
||||
if (! array_key_exists($permissionName, $this->permissions->toArray())) {
|
||||
if (! $this->permissions instanceof Collection || ! $this->permissions->has($permissionName)) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return auth()->check() && auth()->user()->rank >= (int) $this->permissions->get($permissionName);
|
||||
if (! auth()->check()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->rank >= (int) $this->permissions->get($permissionName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,18 @@ class IpLookupService
|
||||
|
||||
public function __construct(private readonly string $apiKey) {}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function ipLookup(string $ip): array
|
||||
{
|
||||
$response = Http::acceptJson()->get(sprintf('%s/%s?api-key=%s', $this->baseUrl, $ip, $this->apiKey));
|
||||
|
||||
/** @var array<string, mixed> $json */
|
||||
$json = $response->json() ?? [];
|
||||
|
||||
if (! $response->ok()) {
|
||||
$message = array_key_exists('message', $response->json()) ? $response->json()['message'] : 'Unknown error';
|
||||
$message = array_key_exists('message', $json) ? $json['message'] : 'Unknown error';
|
||||
|
||||
return [
|
||||
'message' => $message,
|
||||
@@ -23,6 +29,6 @@ class IpLookupService
|
||||
];
|
||||
}
|
||||
|
||||
return $response->json();
|
||||
return $json;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,19 @@ use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class PermissionsService
|
||||
{
|
||||
/** @var Collection<string, int>|null */
|
||||
public private(set) ?Collection $permissions;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->permissions = Cache::remember(
|
||||
/** @var Collection<string, int> $permissions */
|
||||
$permissions = Cache::remember(
|
||||
key: 'website_permissions',
|
||||
ttl: now()->addMinutes(30),
|
||||
callback: fn () => WebsitePermission::all()->pluck('min_rank', 'permission')
|
||||
callback: fn () => WebsitePermission::pluck('min_rank', 'permission')
|
||||
);
|
||||
|
||||
$this->permissions = $permissions;
|
||||
}
|
||||
|
||||
public function getOrDefault(string $permissionName, bool $default = false): bool
|
||||
@@ -29,17 +33,23 @@ class PermissionsService
|
||||
return false;
|
||||
}
|
||||
|
||||
return auth()->user()->rank >= (int) $this->permissions->get($permissionName);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->rank >= (int) $this->permissions->get($permissionName);
|
||||
}
|
||||
|
||||
public function refresh(): void
|
||||
{
|
||||
Cache::forget('website_permissions');
|
||||
|
||||
$this->permissions = Cache::remember(
|
||||
/** @var Collection<string, int> $permissions */
|
||||
$permissions = Cache::remember(
|
||||
key: 'website_permissions',
|
||||
ttl: now()->addMinutes(30),
|
||||
callback: fn () => WebsitePermission::all()->pluck('min_rank', 'permission')
|
||||
callback: fn () => WebsitePermission::pluck('min_rank', 'permission')
|
||||
);
|
||||
|
||||
$this->permissions = $permissions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Services;
|
||||
|
||||
use App\Enums\CurrencyTypes;
|
||||
use App\Exceptions\RconConnectionException;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use JsonException;
|
||||
use Socket;
|
||||
@@ -14,13 +15,17 @@ class RconService
|
||||
|
||||
public private(set) bool $isConnected = false;
|
||||
|
||||
protected array $config = [];
|
||||
/** @var array{ip: string, port: int} */
|
||||
protected array $config;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$ip = setting('rcon_ip');
|
||||
$port = setting('rcon_port');
|
||||
|
||||
$this->config = [
|
||||
'ip' => setting('rcon_ip'),
|
||||
'port' => (int) setting('rcon_port'),
|
||||
'ip' => (string) $ip,
|
||||
'port' => is_numeric($port) ? (int) $port : 3001,
|
||||
];
|
||||
|
||||
$this->initialize();
|
||||
@@ -28,9 +33,9 @@ class RconService
|
||||
|
||||
private function initialize(): void
|
||||
{
|
||||
$this->socket = @socket_create(domain: AF_INET, type: SOCK_STREAM, protocol: SOL_TCP);
|
||||
$socket = @socket_create(domain: AF_INET, type: SOCK_STREAM, protocol: SOL_TCP);
|
||||
|
||||
if (! $this->socket) {
|
||||
if ($socket === false) {
|
||||
$error = socket_strerror(socket_last_error());
|
||||
Log::error("RCON initialization failed: {$error}");
|
||||
|
||||
@@ -39,6 +44,8 @@ class RconService
|
||||
return;
|
||||
}
|
||||
|
||||
$this->socket = $socket;
|
||||
|
||||
socket_set_option(
|
||||
socket: $this->socket,
|
||||
level: SOL_SOCKET,
|
||||
@@ -53,7 +60,7 @@ class RconService
|
||||
);
|
||||
|
||||
if (! @socket_connect($this->socket, $this->config['ip'], $this->config['port'])) {
|
||||
$error = socket_strerror(socket_last_error());
|
||||
$error = socket_strerror(socket_last_error($this->socket));
|
||||
Log::error("RCON connection failed: {$error}");
|
||||
|
||||
$this->closeConnection();
|
||||
@@ -80,12 +87,13 @@ class RconService
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed>|null $data
|
||||
* @throws RconConnectionException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function sendCommand(string $command, ?array $data = null): bool
|
||||
{
|
||||
if (! $this->isConnected) {
|
||||
if (! $this->isConnected || ! $this->socket) {
|
||||
Log::error('RCON command failed: Not connected');
|
||||
$this->closeConnection();
|
||||
return false;
|
||||
@@ -106,7 +114,7 @@ class RconService
|
||||
/**
|
||||
* @throws RconConnectionException|JsonException
|
||||
*/
|
||||
public function sendGift($user, int $item_id, string $message = 'Here is a gift.'): void
|
||||
public function sendGift(User $user, int $item_id, string $message = 'Here is a gift.'): void
|
||||
{
|
||||
$this->sendCommand('sendgift', [
|
||||
'user_id' => $user->id,
|
||||
@@ -118,7 +126,7 @@ class RconService
|
||||
/**
|
||||
* @throws RconConnectionException|JsonException
|
||||
*/
|
||||
public function giveCredits($user, int $credits): void
|
||||
public function giveCredits(User $user, int $credits): void
|
||||
{
|
||||
$this->sendCommand('givecredits', [
|
||||
'user_id' => $user->id,
|
||||
@@ -129,7 +137,7 @@ class RconService
|
||||
/**
|
||||
* @throws RconConnectionException|JsonException
|
||||
*/
|
||||
public function giveBadge($user, string $badge): void
|
||||
public function giveBadge(User $user, string $badge): void
|
||||
{
|
||||
$this->sendCommand('givebadge', [
|
||||
'user_id' => $user->id,
|
||||
@@ -140,7 +148,7 @@ class RconService
|
||||
/**
|
||||
* @throws RconConnectionException|JsonException
|
||||
*/
|
||||
public function setMotto($user, string $motto): void
|
||||
public function setMotto(User $user, string $motto): void
|
||||
{
|
||||
$this->sendCommand('setmotto', [
|
||||
'user_id' => $user->id,
|
||||
@@ -159,7 +167,7 @@ class RconService
|
||||
/**
|
||||
* @throws RconConnectionException|JsonException
|
||||
*/
|
||||
public function disconnectUser($user): void
|
||||
public function disconnectUser(User $user): void
|
||||
{
|
||||
$this->sendCommand('disconnect', [
|
||||
'user_id' => $user->id,
|
||||
@@ -170,7 +178,7 @@ class RconService
|
||||
/**
|
||||
* @throws RconConnectionException|JsonException
|
||||
*/
|
||||
public function givePoints($user, CurrencyTypes $type, int $amount): void
|
||||
public function givePoints(User $user, CurrencyTypes $type, int $amount): void
|
||||
{
|
||||
$this->sendCommand('givepoints', [
|
||||
'user_id' => $user->id,
|
||||
@@ -183,7 +191,7 @@ class RconService
|
||||
* @throws RconConnectionException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function giveGotw($user, int $amount): void
|
||||
public function giveGotw(User $user, int $amount): void
|
||||
{
|
||||
$this->givePoints($user, CurrencyTypes::Points, $amount);
|
||||
}
|
||||
@@ -192,7 +200,7 @@ class RconService
|
||||
* @throws RconConnectionException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function giveDiamonds($user, int $amount): void
|
||||
public function giveDiamonds(User $user, int $amount): void
|
||||
{
|
||||
$this->givePoints($user, CurrencyTypes::Diamonds, $amount);
|
||||
}
|
||||
@@ -201,16 +209,16 @@ class RconService
|
||||
* @throws RconConnectionException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function giveDuckets($user, int $amount): void
|
||||
public function giveDuckets(User $user, int $amount): void
|
||||
{
|
||||
$this->givePoints($user, CurrencyTypes::DUCKETS, $amount);
|
||||
$this->givePoints($user, CurrencyTypes::Duckets, $amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RconConnectionException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function setRank($user, int $rank): void
|
||||
public function setRank(User $user, int $rank): void
|
||||
{
|
||||
$this->sendCommand('setrank', [
|
||||
'user_id' => $user->id,
|
||||
@@ -231,7 +239,7 @@ class RconService
|
||||
* @throws RconConnectionException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function alertUser($user, string $message): void
|
||||
public function alertUser(User $user, string $message): void
|
||||
{
|
||||
$this->sendCommand('alertuser', [
|
||||
'user_id' => $user->id,
|
||||
@@ -243,7 +251,7 @@ class RconService
|
||||
* @throws RconConnectionException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function forwardUser($user, int $roomId): void
|
||||
public function forwardUser(User $user, int $roomId): void
|
||||
{
|
||||
$this->sendCommand('forwarduser', [
|
||||
'user_id' => $user->id,
|
||||
@@ -255,7 +263,7 @@ class RconService
|
||||
* @throws RconConnectionException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function updateConfig($user, string $command): void
|
||||
public function updateConfig(User $user, string $command): void
|
||||
{
|
||||
$this->sendCommand('executecommand', [
|
||||
'user_id' => $user->id,
|
||||
|
||||
@@ -10,46 +10,49 @@ use Throwable;
|
||||
|
||||
class SettingsService
|
||||
{
|
||||
public private(set) ?Collection $settings;
|
||||
/** @var Collection<string, string> */
|
||||
public private(set) Collection $settings;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
try {
|
||||
$this->settings = Cache::remember(
|
||||
key: 'website_settings',
|
||||
ttl: now()->addMinutes(5),
|
||||
callback: fn () => Schema::hasTable('website_settings')
|
||||
? WebsiteSetting::all()->pluck('value', 'key')
|
||||
: collect()
|
||||
);
|
||||
} catch (Throwable) {
|
||||
$this->settings = collect();
|
||||
}
|
||||
$this->refresh();
|
||||
}
|
||||
|
||||
public function getOrDefault(string $settingName, ?string $default = null): string
|
||||
{
|
||||
if (! $this->settings instanceof Collection) {
|
||||
return (string) $default;
|
||||
}
|
||||
|
||||
return (string) $this->settings->get($settingName, $default);
|
||||
}
|
||||
|
||||
public function refresh(): void
|
||||
{
|
||||
Cache::forget('website_settings');
|
||||
|
||||
|
||||
try {
|
||||
$this->settings = Cache::remember(
|
||||
/** @var mixed $result */
|
||||
$result = Cache::remember(
|
||||
key: 'website_settings',
|
||||
ttl: now()->addMinutes(5),
|
||||
callback: fn () => Schema::hasTable('website_settings')
|
||||
? WebsiteSetting::all()->pluck('value', 'key')
|
||||
callback: fn () => Schema::hasTable('website_settings')
|
||||
? WebsiteSetting::pluck('value', 'key')
|
||||
: collect()
|
||||
);
|
||||
|
||||
/** @var array<string, string> $data */
|
||||
$data = [];
|
||||
|
||||
if ($result instanceof Collection) {
|
||||
foreach ($result as $key => $value) {
|
||||
$data[(string) $key] = is_scalar($value) ? (string) $value : '';
|
||||
}
|
||||
} elseif (is_array($result)) {
|
||||
foreach ($result as $key => $value) {
|
||||
$data[(string) $key] = is_scalar($value) ? (string) $value : '';
|
||||
}
|
||||
}
|
||||
|
||||
$this->settings = new Collection($data);
|
||||
} catch (Throwable) {
|
||||
$this->settings = collect();
|
||||
$this->settings = new Collection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,26 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Jenssegers\Agent\Agent;
|
||||
|
||||
use App\Models\Session;
|
||||
|
||||
class SessionService
|
||||
{
|
||||
/**
|
||||
* @return Collection<int, \stdClass>
|
||||
*/
|
||||
public function fetchSessionLogs(Request $request): Collection
|
||||
{
|
||||
return collect(
|
||||
Auth::user()->sessions,
|
||||
)->map(function ($session) use ($request) {
|
||||
/** @var \App\Models\User $user */
|
||||
$user = Auth::user();
|
||||
|
||||
/** @var Collection<int, Session> $sessions */
|
||||
$sessions = $user->sessions;
|
||||
|
||||
return $sessions->map(function (Session $session) use ($request): \stdClass {
|
||||
$agent = $this->createAgent($session);
|
||||
|
||||
return (object) [
|
||||
/** @var \stdClass $obj */
|
||||
$obj = (object) [
|
||||
'agent' => [
|
||||
'is_desktop' => $agent->isDesktop(),
|
||||
'platform' => $agent->platform(),
|
||||
@@ -25,14 +35,16 @@ class SessionService
|
||||
],
|
||||
'ip_address' => $session->ip_address,
|
||||
'is_current_device' => $session->id === $request->session()->getId(),
|
||||
'last_active' => \Illuminate\Support\Facades\Date::createFromTimestamp($session->last_activity)->diffForHumans(),
|
||||
'last_active' => \Illuminate\Support\Facades\Date::createFromTimestamp((int) $session->last_activity)->diffForHumans(),
|
||||
];
|
||||
});
|
||||
|
||||
return $obj;
|
||||
})->values();
|
||||
}
|
||||
|
||||
protected function createAgent($session): Agent
|
||||
protected function createAgent(Session $session): Agent
|
||||
{
|
||||
return tap(new Agent, function ($agent) use ($session): void {
|
||||
return tap(new Agent, function (Agent $agent) use ($session): void {
|
||||
$agent->setUserAgent($session->user_agent);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,11 +7,18 @@ use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class UserApiService
|
||||
{
|
||||
/**
|
||||
* @param array<int, string> $columns
|
||||
*/
|
||||
public function fetchUser(string $username, array $columns): User
|
||||
{
|
||||
return User::select($columns)->where('username', '=', $username)->first();
|
||||
return User::select($columns)->where('username', '=', $username)->firstOrFail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string> $columns
|
||||
* @return Builder<User>
|
||||
*/
|
||||
public function onlineUsers($columns = ['username', 'motto', 'look'], bool $randomOrder = true): Builder
|
||||
{
|
||||
$query = User::select($columns)->where('online', '=', '1');
|
||||
|
||||
Reference in New Issue
Block a user