Implement Phase 1: Foundation & Core Setup

Complete foundation for the EZSCALE billing platform replacing WHMCS:

- Install Composer deps (Fortify, Passport, Cashier, PayPal, Spatie Permissions, Inertia)
- Install Vue 3 + Inertia.js with Vite, 3 layouts (App, Auth, Admin)
- Configure subdomain routing (marketing, account, admin) with domain-based route files
- Create 30 database migrations (15 custom tables + package defaults)
- Create 14 Eloquent models with relationships, factories, and encrypted casts
- Set up Fortify auth with 7 Vue pages (Login, Register, ForgotPassword, ResetPassword, VerifyEmail, ConfirmPassword, TwoFactorChallenge)
- Add 2FA TOTP setup page with QR code and recovery codes
- Configure middleware (Inertia, Spatie roles/permissions, EnsureUserNotSuspended)
- Create seeders for roles/permissions, sample plans, and admin user
- Build dashboard controllers and Vue pages for customer and admin panels
- Add 4 shared Vue components (Card, Button, NavLink, FlashMessages)
- Generate Passport OAuth2 keys for future SSO/API use
- Write 24 Pest tests (auth, role-based access, models) — all passing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude Dev
2026-02-09 02:50:46 -05:00
parent cf7669f270
commit 26704f9721
130 changed files with 6862 additions and 230 deletions

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;
class AdminUserSeeder extends Seeder
{
public function run(): void
{
$admin = User::firstOrCreate(
['email' => 'admin@ezscale.cloud'],
[
'name' => 'Admin User',
'password' => Hash::make('admin123!'),
'status' => 'active',
'email_verified_at' => now(),
]
);
if (! $admin->hasRole('admin')) {
$admin->assignRole('admin');
}
if (! $admin->profile) {
$admin->profile()->create([]);
}
$customer = User::firstOrCreate(
['email' => 'user@ezscale.dev'],
[
'name' => 'Test Customer',
'password' => Hash::make('user123!'),
'status' => 'active',
'email_verified_at' => now(),
]
);
if (! $customer->hasRole('customer')) {
$customer->assignRole('customer');
}
if (! $customer->profile) {
$customer->profile()->create([]);
}
}
}

View File

@@ -1,25 +1,19 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
use WithoutModelEvents;
/**
* Seed the application's database.
*/
public function run(): void
{
// User::factory(10)->create();
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
$this->call([
RoleAndPermissionSeeder::class,
PlanSeeder::class,
AdminUserSeeder::class,
]);
}
}

View File

@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\Plan;
use Illuminate\Database\Seeder;
class PlanSeeder extends Seeder
{
public function run(): void
{
$plans = [
// VPS Plans
[
'name' => 'VPS Starter',
'slug' => 'vps-starter',
'description' => 'Perfect for small projects and development.',
'service_type' => 'vps',
'price' => 5.99,
'billing_cycle' => 'monthly',
'features' => ['cpu' => '1 vCPU', 'ram' => '1GB', 'disk' => '25GB SSD', 'bandwidth' => '1TB'],
'sort_order' => 1,
],
[
'name' => 'VPS Pro',
'slug' => 'vps-pro',
'description' => 'Ideal for growing applications and websites.',
'service_type' => 'vps',
'price' => 19.99,
'billing_cycle' => 'monthly',
'features' => ['cpu' => '2 vCPU', 'ram' => '4GB', 'disk' => '80GB SSD', 'bandwidth' => '3TB'],
'sort_order' => 2,
],
[
'name' => 'VPS Enterprise',
'slug' => 'vps-enterprise',
'description' => 'High-performance VPS for demanding workloads.',
'service_type' => 'vps',
'price' => 49.99,
'billing_cycle' => 'monthly',
'features' => ['cpu' => '4 vCPU', 'ram' => '16GB', 'disk' => '200GB SSD', 'bandwidth' => '10TB'],
'sort_order' => 3,
],
// Dedicated Server Plans
[
'name' => 'Dedicated Starter',
'slug' => 'dedicated-starter',
'description' => 'Entry-level dedicated server.',
'service_type' => 'dedicated',
'price' => 99.99,
'billing_cycle' => 'monthly',
'features' => ['cpu' => 'Intel Xeon E-2236', 'ram' => '32GB DDR4', 'disk' => '2x 500GB SSD', 'bandwidth' => '10TB'],
'stock_quantity' => 5,
'sort_order' => 10,
],
// Web Hosting Plans
[
'name' => 'Hosting Basic',
'slug' => 'hosting-basic',
'description' => 'Shared hosting for small websites.',
'service_type' => 'hosting',
'price' => 3.99,
'billing_cycle' => 'monthly',
'features' => ['disk' => '10GB SSD', 'bandwidth' => '100GB', 'domains' => '1', 'email' => '5 accounts'],
'sort_order' => 20,
],
// Game Server Plans
[
'name' => 'Minecraft Standard',
'slug' => 'minecraft-standard',
'description' => 'Minecraft server for up to 20 players.',
'service_type' => 'game_server',
'price' => 9.99,
'billing_cycle' => 'monthly',
'features' => ['cpu' => '2 vCPU', 'ram' => '4GB', 'disk' => '30GB SSD', 'players' => '20'],
'sort_order' => 30,
],
];
foreach ($plans as $plan) {
Plan::create(array_merge([
'currency' => 'USD',
'status' => 'active',
], $plan));
}
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
class RoleAndPermissionSeeder extends Seeder
{
public function run(): void
{
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
$permissions = [
'manage users',
'manage services',
'manage plans',
'manage invoices',
'manage coupons',
'view audit logs',
'impersonate users',
];
foreach ($permissions as $permission) {
Permission::create(['name' => $permission]);
}
$adminRole = Role::create(['name' => 'admin']);
$adminRole->givePermissionTo(Permission::all());
Role::create(['name' => 'customer']);
}
}