Laravel 12 + React 18 (Inertia.js) application for managing VPN/proxy subscriptions. PHP 8.2+ backend, JavaScript/JSX frontend, Filament admin panel, Tailwind CSS.
If the working directory is on a Windows filesystem mount (e.g. path starts with /mnt/c/,
/mnt/d/, etc.), always use the Windows-native tools (php.exe, composer.bat,
node.exe, npm.cmd, etc.) via powershell.exe -Command "..." or cmd.exe /c "..."
instead of the WSL-local binaries. The WSL environment may be missing PHP extensions
(e.g. ext-yaml, ext-intl) that the Windows PHP installation has. Never use
--ignore-platform-reqs or --ignore-platform-req to work around missing extensions —
ask the user to run the command on Windows or install the missing extensions first.
# Backend
composer install # Install PHP dependencies
php artisan test # Run all tests (Pest PHP)
php artisan test --filter=ProfileTest # Run a single test file
php artisan test --filter="profile page" # Run a single test by name
php artisan test tests/Feature/Auth # Run tests in a directory
php artisan test --parallel # Run tests in parallel
./vendor/bin/pint # Format PHP (Laravel Pint)
./vendor/bin/pint --test # Check formatting without fixing
# Frontend
npm install # Install JS dependencies
npm run build # Production build (Vite)
npm run dev # Dev server with HMR
# Artisan
php artisan migrate # Run migrations
php artisan db:seed # Seed database
php artisan route:list # List all routes- Always use English for code comments, even when conversing in other languages
- No space-only lines; use empty newlines instead
- Match existing comment language in each file
- Never use foreign keys or cascades in any RDBMS
- Never reset or wipe any database; roll back modified parts only, ask user if not possible
- Never commit changes via git automatically; always review diffs first
- If frontend is changed, run
npm run buildbefore finishing
app/
Console/Commands/ # Artisan commands
Filament/Resources/ # Admin panel (Filament 3)
Http/Controllers/ # Web controllers (Inertia responses)
Http/Middleware/ # Custom middleware
Jobs/ # Queued/sync jobs
Models/ # Eloquent models (all use SoftDeletes)
Notifications/ # Mail/notification classes
Observers/ # Model observers (attribute-based registration)
Services/ # Business logic services
resources/js/
Components/ # Reusable React components (PascalCase files)
Layouts/ # AuthenticatedLayout, GuestLayout
Pages/ # Page components organized by feature
Utils/ # Utility functions (camelCase files)
tests/
Feature/ # Integration tests (use RefreshDatabase)
Unit/ # Unit tests
- 4-space indentation
- Single quotes for strings; double quotes only for interpolation
- Short array syntax
[]exclusively - One blank line between methods
- No blank line after the class opening brace
- Alphabetical within a single
useblock (no blank-line group separation) - App namespace first, then framework/vendor, then PHP builtins:
use App\Models\User;
use App\Services\V2rayService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use Throwable;- Variables:
snake_case($vmess_servers,$out_trade_no) - Methods:
camelCase - Classes:
PascalCase - Constants:
UPPER_SNAKE_CASEon classes (const STATUS_PAID = 'paid') - Status values: string constants, not enums
- Always add parameter type hints
- Return types on lifecycle methods (
boot(): void,up(): void,handle(): void) - Controller action methods and relationship methods typically omit return types
- Use
/** @var Type $var */inline doc for local variable type hints
- All models use
SoftDeletestrait - Use
Attribute::make()for accessors (modern API) Model::unguard()is set globally;$fillablearrays still present for documentation- Observer registration via PHP 8 attributes:
#[ObservedBy(UserObserver::class)]
abort_if()for HTTP guard clauses:abort_if(!$condition, 404)throw_if()for service-level errors- try-catch with non-capturing catches for external services:
catch (InvalidStateException) logger()->error()orlogger()->driver('job')->log()for background task errors- User-facing errors via
->withErrors(['error' => '...'])
- Return
Inertia::render('Page', compact(...))for page responses - Use
$request->user()with/** @var User $user */type hint - Guard clauses at top of methods with
abort_if()
- Anonymous class style:
return new class extends Migration { ... } $table->id()for primary keys$table->unsignedBigInteger('..._id')for references (NO foreign key constraints)$table->timestamps()and$table->softDeletes()on every table- Use
->comment()for column documentation down()usesSchema::dropIfExists()- Table names: plural snake_case (
user_packages,vmess_servers)
- Constructor promotion with
readonly:public function __construct(private readonly string $server) readonly classwhen fully immutable- Public methods for API, private for internals
- 2-space indentation (per .editorconfig for
resources/js/**) - Semicolons in component files
- Double quotes for JSX attributes, single quotes for JS strings
- Functional components only (no class components)
- Framework/library imports first, then local components, then utils
- Path alias
@/maps toresources/js/
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import {Head, Link, router} from '@inertiajs/react';
import {useState} from "react";
import {formatBytes} from "@/Utils/formatBytes";export default function ComponentName({prop1, prop2})for pages- Props always destructured in function signature
- Use inner render functions (
renderSection()) for complex JSX blocks - Tailwind utility classes inline (no extraction libraries)
useStatefor local state (no external state management)router.visit(),router.get(),router.post()from Inertia for navigationroute()helper from Ziggy for named routes
- Components/files:
PascalCase(AuthenticatedLayout.jsx) - Variables/functions:
camelCase - Page components:
Index.jsxinside feature folders (Package/Index.jsx) - Utility files:
camelCase(formatBytes.js)
- Test descriptions are lowercase human-readable sentences
- Use
test()function (preferred) orit()for assertions - Feature tests auto-apply
RefreshDatabaseviatests/Pest.php - Create users with
User::factory()->create() - Authenticate with
$this->actingAs($user) - Fluent response assertions:
$response->assertSessionHasNoErrors()->assertRedirect(...) - Mix of
$this->assertSame()and Pest'sexpect()->toBe()(preferexpect()for new tests)
test('profile page is displayed', function () {
$user = User::factory()->create();
$response = $this
->actingAs($user)
->get('/profile');
$response->assertOk();
});- Inertia.js bridges Laravel and React (no separate API routes)
- Data passed to React via
Inertia::render('Page', compact(...))in controllers - Payment gateways: Alipay, BEPUSDT (crypto)
- GitHub OAuth login + GitHub Sponsors webhook integration
- Scheduled commands in
routes/console.phpusingSchedule::command() BepusdtServiceregistered as singleton inAppServiceProvider- HTTPS forced globally via
URL::forceScheme('https')