diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e08e90d --- /dev/null +++ b/CHANGELOG.md @@ -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 diff --git a/app/Actions/Fortify/CreateNewUser.php b/app/Actions/Fortify/CreateNewUser.php index bb67021..ba5ec92 100755 --- a/app/Actions/Fortify/CreateNewUser.php +++ b/app/Actions/Fortify/CreateNewUser.php @@ -171,13 +171,11 @@ class CreateNewUser implements CreatesNewUsers try { Http::asJson()->post(is_string($discordWebhookUrl) ? $discordWebhookUrl : '', [ '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) { Log::error('Failed to send Discord webhook notification', [ 'username' => $username, - 'ip' => $ip, - 'email' => $email, 'error' => $e->getMessage(), ]); } diff --git a/app/Http/Controllers/Api/HotelApiController.php b/app/Http/Controllers/Api/HotelApiController.php index 2ceb1f8..7357fc0 100755 --- a/app/Http/Controllers/Api/HotelApiController.php +++ b/app/Http/Controllers/Api/HotelApiController.php @@ -317,6 +317,10 @@ class HotelApiController extends Controller $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; if ($user->credits < $cost) { diff --git a/app/Http/Controllers/Badge/BadgeController.php b/app/Http/Controllers/Badge/BadgeController.php index 127480c..7501551 100755 --- a/app/Http/Controllers/Badge/BadgeController.php +++ b/app/Http/Controllers/Badge/BadgeController.php @@ -12,7 +12,7 @@ class BadgeController extends Controller { public function show(): View { - $cost = 150; + $cost = (int) setting('badge_cost', 150); $currencyType = 'credits'; $folderError = false; $errorMessage = ''; @@ -60,7 +60,7 @@ class BadgeController extends Controller 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) { return redirect()->back()->with('error', 'You don\'t have enough credits to purchase a badge.'); diff --git a/app/Http/Controllers/User/ProfileController.php b/app/Http/Controllers/User/ProfileController.php index 844d826..b434e18 100755 --- a/app/Http/Controllers/User/ProfileController.php +++ b/app/Http/Controllers/User/ProfileController.php @@ -20,8 +20,10 @@ class ProfileController extends Controller 'badges', ]); - $showStats = (bool) (WebsiteSetting::where('key', 'profile_show_stats')->first()?->value ?? '1'); - $showOnline = (bool) (WebsiteSetting::where('key', 'profile_show_online_status')->first()?->value ?? '1'); + $settings = WebsiteSetting::whereIn('key', ['profile_show_stats', 'profile_show_online_status']) + ->pluck('value', 'key'); + $showStats = (bool) ($settings['profile_show_stats'] ?? '1'); + $showOnline = (bool) ($settings['profile_show_online_status'] ?? '1'); return view('user.profile', [ 'user' => $user, diff --git a/app/Http/Middleware/RadioApiKey.php b/app/Http/Middleware/RadioApiKey.php index aee09fa..55e063d 100755 --- a/app/Http/Middleware/RadioApiKey.php +++ b/app/Http/Middleware/RadioApiKey.php @@ -17,7 +17,7 @@ class RadioApiKey if (empty($key)) { return response()->json([ - 'error' => 'API key is verplicht. Gebruik Authorization: Bearer of ?api_key=', + 'error' => 'API key is required. Use Authorization: Bearer or ?api_key=', ], 401); } @@ -25,19 +25,19 @@ class RadioApiKey if (! $apiKey) { return response()->json([ - 'error' => 'API key is ongeldig of verlopen', + 'error' => 'API key is invalid or expired', ], 401); } if (! $apiKey->isAllowedIp($request->ip())) { return response()->json([ - 'error' => 'IP-adres niet toegestaan voor deze API key', + 'error' => 'IP address not allowed for this API key', ], 403); } if (! $apiKey->hasPermission($permission)) { return response()->json([ - 'error' => 'Geen toestemming voor deze actie', + 'error' => 'No permission for this action', ], 403); } diff --git a/app/Http/Middleware/VPNCheckerMiddleware.php b/app/Http/Middleware/VPNCheckerMiddleware.php index 983fc01..83724ce 100755 --- a/app/Http/Middleware/VPNCheckerMiddleware.php +++ b/app/Http/Middleware/VPNCheckerMiddleware.php @@ -34,7 +34,7 @@ class VPNCheckerMiddleware return $this->denyAccess($request); } - $ipService = new IpLookupService(''); + $ipService = new IpLookupService; $countryInfo = $ipService->getCountryInfo($userIp); diff --git a/app/Models/User.php b/app/Models/User.php index 03625de..1e9aa16 100755 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -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']; #[\Override] - protected $hidden = ['id', 'password', 'remember_token']; + protected $hidden = ['password', 'remember_token']; /** * @return array @@ -394,19 +394,6 @@ class User extends Authenticatable implements FilamentUser, HasName ->logOnlyDirty(); } - /** - * @param array $options - */ - #[\Override] - public function save(array $options = []): bool - { - if (! $this->isDirty()) { - return false; - } - - return parent::save($options); - } - public function hasAppliedForTeam(int $teamId): bool { if ($teamId === 0) { diff --git a/config/app.php b/config/app.php index 9dd9f35..3864e26 100755 --- a/config/app.php +++ b/config/app.php @@ -2,7 +2,6 @@ declare(strict_types=1); -// Auto-push test use App\Providers\AppServiceProvider; use App\Providers\EventServiceProvider; use App\Providers\Filament\AdminFilamentPanelProvider; @@ -247,5 +246,4 @@ return [ // 'ExampleClass' => App\Example\ExampleClass::class, ])->toArray(), -]; -// test +] diff --git a/config/fortify.php b/config/fortify.php index a0f619f..0ba8629 100755 --- a/config/fortify.php +++ b/config/fortify.php @@ -147,14 +147,9 @@ return [ 'features' => [ Features::registration(), - // Features::resetPasswords(), - // Features::emailVerification(), - // Features::updateProfileInformation(), - // Features::updatePasswords(), Features::twoFactorAuthentication([ 'confirm' => true, 'confirmPassword' => true, - // 'window' => 0, ]), ], diff --git a/public/fix-php-handler.ps1 b/public/fix-php-handler.ps1 index 9d1c6c2..95563e8 100755 --- a/public/fix-php-handler.ps1 +++ b/public/fix-php-handler.ps1 @@ -1,4 +1,4 @@ -# Uitgebreide PHP locatie detector voor IIS web.config +# Extended PHP location detector for IIS web.config $searchPaths = @( "C:\PHP", @@ -19,11 +19,11 @@ $searchPaths = @( $phpPath = $null -# Methode 1: Check of php-cgi.exe in PATH staat -Write-Host "Zoeken naar php-cgi.exe..." -ForegroundColor Cyan +# Method 1: Check if php-cgi.exe is in PATH +Write-Host "Searching for php-cgi.exe..." -ForegroundColor Cyan $phpPath = (Get-Command php-cgi.exe -ErrorAction SilentlyContinue).Source -# Methode 2: Zoek in standaard locaties +# Method 2: Search in default locations if (-not $phpPath) { foreach ($basePath in $searchPaths) { 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) { - Write-Host "Zoeken in C:\ schijf..." -ForegroundColor Yellow + Write-Host "Searching C:\ drive..." -ForegroundColor Yellow $rootFolders = Get-ChildItem -Path "C:\" -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'php|wamp|laragon|inetpub' } 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) { $regPaths = @( "HKLM:\SOFTWARE\PHP", @@ -73,7 +73,7 @@ if (-not $phpPath) { } if ($phpPath) { - Write-Host "`nGevonden: $phpPath" -ForegroundColor Green + Write-Host "`nFound: $phpPath" -ForegroundColor Green $webConfigPath = ".\web.config" if (Test-Path $webConfigPath) { @@ -82,14 +82,14 @@ if ($phpPath) { if ($content -ne $newContent) { $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 { - Write-Host "Handler was al correct ingesteld." -ForegroundColor Yellow + Write-Host "Handler was already set correctly." -ForegroundColor Yellow } } else { - Write-Host "web.config niet gevonden in huidige directory!" -ForegroundColor Red + Write-Host "web.config not found in current directory!" -ForegroundColor Red } } else { - Write-Host "`nKon php-cgi.exe niet vinden." -ForegroundColor Red - Write-Host "Controleer of PHP correct is geinstalleerd." -ForegroundColor Red + Write-Host "`nCould not find php-cgi.exe." -ForegroundColor Red + Write-Host "Check if PHP is installed correctly." -ForegroundColor Red } diff --git a/routes/shop.php b/routes/shop.php index 4100e2c..fc4df2c 100755 --- a/routes/shop.php +++ b/routes/shop.php @@ -14,7 +14,7 @@ Route::prefix('shop')->group(function () { // PayPal routes 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('/cancelled-transaction', 'cancelled')->name('paypal.cancelled-transaction'); }); diff --git a/update-Nitrov3.sh b/update-Nitrov3.sh index bb0ba68..f71385a 100755 --- a/update-Nitrov3.sh +++ b/update-Nitrov3.sh @@ -3,7 +3,7 @@ # Exit immediately if a command exits with a non-zero status set -e -# --- CONFIGURATION (overschrijfbaar via omgevingsvariabelen) --- +# --- CONFIGURATION (overridable via environment variables) --- DB_NAME="${NITRO_DB_NAME:-habbo}" DB_HOST="${NITRO_DB_HOST:-127.0.0.1}" DB_PORT="${NITRO_DB_PORT:-3306}" @@ -44,7 +44,7 @@ echo "--> Checking for new SQL files..." if [ -d "$SQL_DIR" ]; then find "$SQL_DIR" -name "*.sql" -mmin -10 -print0 | while IFS= read -r -d '' sql_file; do 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 else echo "--> SQL directory not found, skipping SQL import." @@ -156,25 +156,25 @@ fi # ---------------------------------------- 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..." 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)..." if [ -d "$BACKUP_DIR" ]; then - # Zoekt alle .sql bestanden in de backup map, sorteert op datum (nieuwste eerst), - # slaat de eerste 5 over (tail -n +6) en verwijdert de rest. + # Find all .sql files in backup dir, sort by date (newest first), + # 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 fi -# 3. Clean Yarn cache om SSD ruimte te besparen +# 3. Clean Yarn cache to save SSD space echo "--> Cleaning Yarn cache..." 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 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 "$GAMEDATA_CONF_DIR" 2>/dev/null || echo "--> chown voor GAMEDATA_CONF_DIR overgeslagen" else - echo "--> Sudo niet beschikbaar, overslaan van chown." + echo "--> Sudo not available, skipping chown." fi @@ -193,14 +193,14 @@ fi if systemctl list-units --type=service --all | grep -q "$EMULATOR_SERVICE.service"; then echo "--> Restarting $EMULATOR_SERVICE service..." 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 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 else - echo "--> Waarschuwing: Service '$EMULATOR_SERVICE' niet gevonden in systemd." - echo "--> Als je PM2 gebruikt, herstart je processen dan handmatig met: pm2 restart all" + echo "--> Warning: Service '$EMULATOR_SERVICE' not found in systemd." + echo "--> If you use PM2, restart your processes manually: pm2 restart all" fi echo "=== Update successfully completed! ===" \ No newline at end of file