Low priority fixes: debug comments, Fortify cleanup, badge cost setting, profile query merge, User model fixes, VPN constructor cleanup, PayPal POST, PII removal, Dutch→English translations, duplicate rank check, CHANGELOG

This commit is contained in:
root
2026-06-04 19:57:01 +02:00
parent 66cbd46f37
commit 4b6872e5e0
13 changed files with 82 additions and 61 deletions
+37
View File
@@ -0,0 +1,37 @@
# Changelog
## V3 — 2026-06-04
### Added
- Commandocentrum with Nitro V3 one-click updater
- Configurable paths (9 settings) stored in DB via HK UI
- Emulator start/stop/restart from admin panel
- Live monitoring (online users, DB status, server load)
- Hotel alert system
- Emulator logs viewer
- Clothing sync from FigureMap
- Social login (Google, Discord, GitHub)
- Staff activity log
- Notification settings (email & Discord)
- PHP 8.5 + Ubuntu 26.04 support
- Dual .env system (Linux + Windows)
- Bulletproof 12-step installation guide
### Changed
- Complete README rewrite with badges, tables, quick start
- All Dutch comments translated to English
- XAMPP support removed (security warning added)
- `.env.example``.env.install` with step-by-step guide
- `.env.standard``.env.example.linux` + `.env.example.windows`
### Fixed
- Removed debug comments from config/app.php
- Removed commented Fortify features
- Badge cost now configurable via `setting('badge_cost', 150)`
- ProfileController: merged 2 setting queries into 1
- User model: removed `save()` override that broke Eloquent expectations
- User model: removed `id` from `$hidden` (was inconsistent with API exposure)
- VPNCheckerMiddleware: removed empty constructor argument
- PayPal process route: GET → POST
- Discord webhook: no longer sends PII (email/IP)
- API purchase: added rank duplicate check
+1 -3
View File
@@ -171,13 +171,11 @@ class CreateNewUser implements CreatesNewUsers
try { try {
Http::asJson()->post(is_string($discordWebhookUrl) ? $discordWebhookUrl : '', [ Http::asJson()->post(is_string($discordWebhookUrl) ? $discordWebhookUrl : '', [
'username' => sprintf('%s Bot', is_string($hotelNameSetting) ? $hotelNameSetting : 'Hotel'), 'username' => sprintf('%s Bot', is_string($hotelNameSetting) ? $hotelNameSetting : 'Hotel'),
'content' => "User: {$username} has just registered, with the IP: {$ip} and E-mail: {$email}", 'content' => "User: {$username} has just registered.",
]); ]);
} catch (\Exception $e) { } catch (\Exception $e) {
Log::error('Failed to send Discord webhook notification', [ Log::error('Failed to send Discord webhook notification', [
'username' => $username, 'username' => $username,
'ip' => $ip,
'email' => $email,
'error' => $e->getMessage(), 'error' => $e->getMessage(),
]); ]);
} }
@@ -317,6 +317,10 @@ class HotelApiController extends Controller
$user = $request->user(); $user = $request->user();
if ($package->give_rank && $user->rank >= $package->give_rank) {
return response()->json(['error' => 'You already have this or a higher rank'], 400);
}
$cost = $package->costs; $cost = $package->costs;
if ($user->credits < $cost) { if ($user->credits < $cost) {
@@ -12,7 +12,7 @@ class BadgeController extends Controller
{ {
public function show(): View public function show(): View
{ {
$cost = 150; $cost = (int) setting('badge_cost', 150);
$currencyType = 'credits'; $currencyType = 'credits';
$folderError = false; $folderError = false;
$errorMessage = ''; $errorMessage = '';
@@ -60,7 +60,7 @@ class BadgeController extends Controller
return redirect()->route('login')->with('error', 'You must be logged in to purchase badges.'); return redirect()->route('login')->with('error', 'You must be logged in to purchase badges.');
} }
$cost = 150; $cost = (int) setting('badge_cost', 150);
if (property_exists($user, 'credits') && $user->credits !== null && $user->credits < $cost) { if (property_exists($user, 'credits') && $user->credits !== null && $user->credits < $cost) {
return redirect()->back()->with('error', 'You don\'t have enough credits to purchase a badge.'); return redirect()->back()->with('error', 'You don\'t have enough credits to purchase a badge.');
@@ -20,8 +20,10 @@ class ProfileController extends Controller
'badges', 'badges',
]); ]);
$showStats = (bool) (WebsiteSetting::where('key', 'profile_show_stats')->first()?->value ?? '1'); $settings = WebsiteSetting::whereIn('key', ['profile_show_stats', 'profile_show_online_status'])
$showOnline = (bool) (WebsiteSetting::where('key', 'profile_show_online_status')->first()?->value ?? '1'); ->pluck('value', 'key');
$showStats = (bool) ($settings['profile_show_stats'] ?? '1');
$showOnline = (bool) ($settings['profile_show_online_status'] ?? '1');
return view('user.profile', [ return view('user.profile', [
'user' => $user, 'user' => $user,
+4 -4
View File
@@ -17,7 +17,7 @@ class RadioApiKey
if (empty($key)) { if (empty($key)) {
return response()->json([ return response()->json([
'error' => 'API key is verplicht. Gebruik Authorization: Bearer <key> of ?api_key=<key>', 'error' => 'API key is required. Use Authorization: Bearer <key> or ?api_key=<key>',
], 401); ], 401);
} }
@@ -25,19 +25,19 @@ class RadioApiKey
if (! $apiKey) { if (! $apiKey) {
return response()->json([ return response()->json([
'error' => 'API key is ongeldig of verlopen', 'error' => 'API key is invalid or expired',
], 401); ], 401);
} }
if (! $apiKey->isAllowedIp($request->ip())) { if (! $apiKey->isAllowedIp($request->ip())) {
return response()->json([ return response()->json([
'error' => 'IP-adres niet toegestaan voor deze API key', 'error' => 'IP address not allowed for this API key',
], 403); ], 403);
} }
if (! $apiKey->hasPermission($permission)) { if (! $apiKey->hasPermission($permission)) {
return response()->json([ return response()->json([
'error' => 'Geen toestemming voor deze actie', 'error' => 'No permission for this action',
], 403); ], 403);
} }
+1 -1
View File
@@ -34,7 +34,7 @@ class VPNCheckerMiddleware
return $this->denyAccess($request); return $this->denyAccess($request);
} }
$ipService = new IpLookupService(''); $ipService = new IpLookupService;
$countryInfo = $ipService->getCountryInfo($userIp); $countryInfo = $ipService->getCountryInfo($userIp);
+1 -14
View File
@@ -128,7 +128,7 @@ class User extends Authenticatable implements FilamentUser, HasName
protected $fillable = ['username', 'mail', 'password', 'account_created', 'last_login', 'motto', 'look', 'credits', 'last_username_change', 'auth_ticket', 'home_room', 'ip_register', 'ip_current', 'referral_code', 'preferences', 'team_id', 'avatar_background', 'home_background', 'pincode', 'secret_key', 'extra_rank', 'is_hidden', 'background_id', 'background_stand_id', 'background_overlay_id', 'radio_points', 'pixels', 'points', 'online', 'gender', 'rank', 'mail_verified', 'two_factor_secret', 'two_factor_recovery_codes', 'two_factor_confirmed_at']; protected $fillable = ['username', 'mail', 'password', 'account_created', 'last_login', 'motto', 'look', 'credits', 'last_username_change', 'auth_ticket', 'home_room', 'ip_register', 'ip_current', 'referral_code', 'preferences', 'team_id', 'avatar_background', 'home_background', 'pincode', 'secret_key', 'extra_rank', 'is_hidden', 'background_id', 'background_stand_id', 'background_overlay_id', 'radio_points', 'pixels', 'points', 'online', 'gender', 'rank', 'mail_verified', 'two_factor_secret', 'two_factor_recovery_codes', 'two_factor_confirmed_at'];
#[\Override] #[\Override]
protected $hidden = ['id', 'password', 'remember_token']; protected $hidden = ['password', 'remember_token'];
/** /**
* @return array<string, string> * @return array<string, string>
@@ -394,19 +394,6 @@ class User extends Authenticatable implements FilamentUser, HasName
->logOnlyDirty(); ->logOnlyDirty();
} }
/**
* @param array<string, mixed> $options
*/
#[\Override]
public function save(array $options = []): bool
{
if (! $this->isDirty()) {
return false;
}
return parent::save($options);
}
public function hasAppliedForTeam(int $teamId): bool public function hasAppliedForTeam(int $teamId): bool
{ {
if ($teamId === 0) { if ($teamId === 0) {
+1 -3
View File
@@ -2,7 +2,6 @@
declare(strict_types=1); declare(strict_types=1);
// Auto-push test
use App\Providers\AppServiceProvider; use App\Providers\AppServiceProvider;
use App\Providers\EventServiceProvider; use App\Providers\EventServiceProvider;
use App\Providers\Filament\AdminFilamentPanelProvider; use App\Providers\Filament\AdminFilamentPanelProvider;
@@ -247,5 +246,4 @@ return [
// 'ExampleClass' => App\Example\ExampleClass::class, // 'ExampleClass' => App\Example\ExampleClass::class,
])->toArray(), ])->toArray(),
]; ]
// test
-5
View File
@@ -147,14 +147,9 @@ return [
'features' => [ 'features' => [
Features::registration(), Features::registration(),
// Features::resetPasswords(),
// Features::emailVerification(),
// Features::updateProfileInformation(),
// Features::updatePasswords(),
Features::twoFactorAuthentication([ Features::twoFactorAuthentication([
'confirm' => true, 'confirm' => true,
'confirmPassword' => true, 'confirmPassword' => true,
// 'window' => 0,
]), ]),
], ],
+13 -13
View File
@@ -1,4 +1,4 @@
# Uitgebreide PHP locatie detector voor IIS web.config # Extended PHP location detector for IIS web.config
$searchPaths = @( $searchPaths = @(
"C:\PHP", "C:\PHP",
@@ -19,11 +19,11 @@ $searchPaths = @(
$phpPath = $null $phpPath = $null
# Methode 1: Check of php-cgi.exe in PATH staat # Method 1: Check if php-cgi.exe is in PATH
Write-Host "Zoeken naar php-cgi.exe..." -ForegroundColor Cyan Write-Host "Searching for php-cgi.exe..." -ForegroundColor Cyan
$phpPath = (Get-Command php-cgi.exe -ErrorAction SilentlyContinue).Source $phpPath = (Get-Command php-cgi.exe -ErrorAction SilentlyContinue).Source
# Methode 2: Zoek in standaard locaties # Method 2: Search in default locations
if (-not $phpPath) { if (-not $phpPath) {
foreach ($basePath in $searchPaths) { foreach ($basePath in $searchPaths) {
if (Test-Path $basePath) { if (Test-Path $basePath) {
@@ -37,9 +37,9 @@ if (-not $phpPath) {
} }
} }
# Methode 3: Zoek in alle C: schijf (alleen top-level folders voor snelheid) # Method 3: Search entire C: drive (top-level folders only for speed)
if (-not $phpPath) { if (-not $phpPath) {
Write-Host "Zoeken in C:\ schijf..." -ForegroundColor Yellow Write-Host "Searching C:\ drive..." -ForegroundColor Yellow
$rootFolders = Get-ChildItem -Path "C:\" -Directory -ErrorAction SilentlyContinue | $rootFolders = Get-ChildItem -Path "C:\" -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match 'php|wamp|laragon|inetpub' } Where-Object { $_.Name -match 'php|wamp|laragon|inetpub' }
foreach ($folder in $rootFolders) { foreach ($folder in $rootFolders) {
@@ -52,7 +52,7 @@ if (-not $phpPath) {
} }
} }
# Methode 4: Check Windows Registry voor PHP installaties # Method 4: Check Windows Registry for PHP installations
if (-not $phpPath) { if (-not $phpPath) {
$regPaths = @( $regPaths = @(
"HKLM:\SOFTWARE\PHP", "HKLM:\SOFTWARE\PHP",
@@ -73,7 +73,7 @@ if (-not $phpPath) {
} }
if ($phpPath) { if ($phpPath) {
Write-Host "`nGevonden: $phpPath" -ForegroundColor Green Write-Host "`nFound: $phpPath" -ForegroundColor Green
$webConfigPath = ".\web.config" $webConfigPath = ".\web.config"
if (Test-Path $webConfigPath) { if (Test-Path $webConfigPath) {
@@ -82,14 +82,14 @@ if ($phpPath) {
if ($content -ne $newContent) { if ($content -ne $newContent) {
$newContent | Set-Content $webConfigPath -NoNewline $newContent | Set-Content $webConfigPath -NoNewline
Write-Host "Handler succesvol geupdate in web.config!" -ForegroundColor Green Write-Host "Handler successfully updated in web.config!" -ForegroundColor Green
} else { } else {
Write-Host "Handler was al correct ingesteld." -ForegroundColor Yellow Write-Host "Handler was already set correctly." -ForegroundColor Yellow
} }
} else { } else {
Write-Host "web.config niet gevonden in huidige directory!" -ForegroundColor Red Write-Host "web.config not found in current directory!" -ForegroundColor Red
} }
} else { } else {
Write-Host "`nKon php-cgi.exe niet vinden." -ForegroundColor Red Write-Host "`nCould not find php-cgi.exe." -ForegroundColor Red
Write-Host "Controleer of PHP correct is geinstalleerd." -ForegroundColor Red Write-Host "Check if PHP is installed correctly." -ForegroundColor Red
} }
+1 -1
View File
@@ -14,7 +14,7 @@ Route::prefix('shop')->group(function () {
// PayPal routes // PayPal routes
Route::controller(PayPalController::class)->prefix('paypal')->group(function () { Route::controller(PayPalController::class)->prefix('paypal')->group(function () {
Route::get('/process-transaction', 'process')->name('paypal.process-transaction'); Route::post('/process-transaction', 'process')->name('paypal.process-transaction');
Route::get('/successful-transaction', 'successful')->name('paypal.successful-transaction'); Route::get('/successful-transaction', 'successful')->name('paypal.successful-transaction');
Route::get('/cancelled-transaction', 'cancelled')->name('paypal.cancelled-transaction'); Route::get('/cancelled-transaction', 'cancelled')->name('paypal.cancelled-transaction');
}); });
+13 -13
View File
@@ -3,7 +3,7 @@
# Exit immediately if a command exits with a non-zero status # Exit immediately if a command exits with a non-zero status
set -e set -e
# --- CONFIGURATION (overschrijfbaar via omgevingsvariabelen) --- # --- CONFIGURATION (overridable via environment variables) ---
DB_NAME="${NITRO_DB_NAME:-habbo}" DB_NAME="${NITRO_DB_NAME:-habbo}"
DB_HOST="${NITRO_DB_HOST:-127.0.0.1}" DB_HOST="${NITRO_DB_HOST:-127.0.0.1}"
DB_PORT="${NITRO_DB_PORT:-3306}" DB_PORT="${NITRO_DB_PORT:-3306}"
@@ -44,7 +44,7 @@ echo "--> Checking for new SQL files..."
if [ -d "$SQL_DIR" ]; then if [ -d "$SQL_DIR" ]; then
find "$SQL_DIR" -name "*.sql" -mmin -10 -print0 | while IFS= read -r -d '' sql_file; do find "$SQL_DIR" -name "*.sql" -mmin -10 -print0 | while IFS= read -r -d '' sql_file; do
echo "--> Importing new SQL file: $(basename "$sql_file")" echo "--> Importing new SQL file: $(basename "$sql_file")"
mariadb $MYSQL_CRED --force "$DB_NAME" < "$sql_file" || echo "--> Opmerking: Sommige SQL-regels overgeslagen (bestonden waarschijnlijk al)." mariadb $MYSQL_CRED --force "$DB_NAME" < "$sql_file" || echo "--> Note: Some SQL rules skipped (probably already exist)."
done done
else else
echo "--> SQL directory not found, skipping SQL import." echo "--> SQL directory not found, skipping SQL import."
@@ -156,25 +156,25 @@ fi
# ---------------------------------------- # ----------------------------------------
echo "--> Starting automated cleanup..." echo "--> Starting automated cleanup..."
# 1. Verwijder emulator logs ouder dan 14 dagen # 1. Remove emulator logs older than 14 days
echo "--> Removing emulator logs older than 14 days..." echo "--> Removing emulator logs older than 14 days..."
find "$EMULATOR_DIR/" -name "*.log" -mtime +14 -exec rm -f {} \; find "$EMULATOR_DIR/" -name "*.log" -mtime +14 -exec rm -f {} \;
# 2. Hou maximaal de 5 nieuwste database backups, gooi oudere weg # 2. Keep max 5 newest database backups, delete older ones
echo "--> Managing update backups (keeping max 5)..." echo "--> Managing update backups (keeping max 5)..."
if [ -d "$BACKUP_DIR" ]; then if [ -d "$BACKUP_DIR" ]; then
# Zoekt alle .sql bestanden in de backup map, sorteert op datum (nieuwste eerst), # Find all .sql files in backup dir, sort by date (newest first),
# slaat de eerste 5 over (tail -n +6) en verwijdert de rest. # skip the first 5 (tail -n +6) and delete the rest.
ls -t "$BACKUP_DIR"/*.sql 2>/dev/null | tail -n +6 | xargs -r rm -f || true ls -t "$BACKUP_DIR"/*.sql 2>/dev/null | tail -n +6 | xargs -r rm -f || true
fi fi
# 3. Clean Yarn cache om SSD ruimte te besparen # 3. Clean Yarn cache to save SSD space
echo "--> Cleaning Yarn cache..." echo "--> Cleaning Yarn cache..."
yarn cache clean yarn cache clean
# ---------------------------------------- # ----------------------------------------
# 6. Fix Permissions (www-data) — alleen als sudo beschikbaar is # 6. Fix Permissions (www-data) — only if sudo is available
# ---------------------------------------- # ----------------------------------------
if command -v sudo &> /dev/null; then if command -v sudo &> /dev/null; then
echo "--> Setting permissions to www-data:www-data..." echo "--> Setting permissions to www-data:www-data..."
@@ -183,7 +183,7 @@ if command -v sudo &> /dev/null; then
sudo chown -R www-data:www-data "$EMULATOR_DIR" 2>/dev/null || echo "--> chown voor EMULATOR_DIR overgeslagen" sudo chown -R www-data:www-data "$EMULATOR_DIR" 2>/dev/null || echo "--> chown voor EMULATOR_DIR overgeslagen"
sudo chown -R www-data:www-data "$GAMEDATA_CONF_DIR" 2>/dev/null || echo "--> chown voor GAMEDATA_CONF_DIR overgeslagen" sudo chown -R www-data:www-data "$GAMEDATA_CONF_DIR" 2>/dev/null || echo "--> chown voor GAMEDATA_CONF_DIR overgeslagen"
else else
echo "--> Sudo niet beschikbaar, overslaan van chown." echo "--> Sudo not available, skipping chown."
fi fi
@@ -193,14 +193,14 @@ fi
if systemctl list-units --type=service --all | grep -q "$EMULATOR_SERVICE.service"; then if systemctl list-units --type=service --all | grep -q "$EMULATOR_SERVICE.service"; then
echo "--> Restarting $EMULATOR_SERVICE service..." echo "--> Restarting $EMULATOR_SERVICE service..."
if command -v sudo &> /dev/null; then if command -v sudo &> /dev/null; then
sudo systemctl restart $EMULATOR_SERVICE 2>/dev/null || echo "--> Service restart via sudo mislukt (mogelijk geen sudo rechten)" sudo systemctl restart $EMULATOR_SERVICE 2>/dev/null || echo "--> Service restart via sudo failed (possible insufficient sudo rights)"
sudo systemctl status $EMULATOR_SERVICE --no-pager -n 5 2>/dev/null || true sudo systemctl status $EMULATOR_SERVICE --no-pager -n 5 2>/dev/null || true
else else
echo "--> Sudo niet beschikbaar. Herstart de service handmatig: sudo systemctl restart $EMULATOR_SERVICE" echo "--> Sudo not available. Restart the service manually: sudo systemctl restart $EMULATOR_SERVICE"
fi fi
else else
echo "--> Waarschuwing: Service '$EMULATOR_SERVICE' niet gevonden in systemd." echo "--> Warning: Service '$EMULATOR_SERVICE' not found in systemd."
echo "--> Als je PM2 gebruikt, herstart je processen dan handmatig met: pm2 restart all" echo "--> If you use PM2, restart your processes manually: pm2 restart all"
fi fi
echo "=== Update successfully completed! ===" echo "=== Update successfully completed! ==="