Adds two sections to the project-level CLAUDE.md: - "Strategic Direction: WHMCS Replacement" — scopes the long-arc plan (storefront, billing, provisioning, customer area, marketing site, admin RBAC, notifications, marketing email automation, future registrar / SSL / multi-tenant / partner API) so future sessions don't accidentally re-litigate decisions. - "Sister Projects (Reference)" — calls out infrastructure/ (Ezra + capacity / costs source-of-truth) and ezscale_api/ (Battlelog/ACP SaaS) as separate Gitea repos with their own CLAUDE.md, plus the catalog-data flow (website pulls hardware inventory + IP availability from infrastructure/). Also clarifies cross-repo conventions: Gitea (not GitHub), no shared DBs, design docs under docs/superpowers/specs/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
240 lines
16 KiB
Markdown
240 lines
16 KiB
Markdown
# CLAUDE.md - EZSCALE Site
|
|
|
|
## Project
|
|
EZSCALE Site — Laravel 12 application for VPS/Dedicated Server hosting management (billing, subscriptions, provisioning, customer management, SSO).
|
|
|
|
## Phase Tracking (MANDATORY)
|
|
All work MUST be tracked against Gitea issues and `TASKS.md`. **Use Gitea MCP tools (`mcp__gitea__*`) — never `gh` CLI.**
|
|
|
|
1. **Before starting:** Check the relevant Gitea issue (`mcp__gitea__list_issues` for repo `EZSCALE/website`) and `TASKS.md` for current status.
|
|
2. **While working:** Update the Gitea issue with progress comments (`mcp__gitea__add_issue_comment`).
|
|
3. **After completing:** Update `TASKS.md` to check off completed items and close the corresponding Gitea issue.
|
|
4. **New tasks discovered:** Create sub-issues or add items to `TASKS.md` immediately.
|
|
5. **Commit messages:** Reference the Gitea issue number (e.g., `Fix checkout validation (#3)`).
|
|
|
|
Gitea repo: https://git.ezscale.cloud/EZSCALE/website
|
|
Gitea issues: https://git.ezscale.cloud/EZSCALE/website/issues
|
|
|
|
## Documentation Updates (MANDATORY)
|
|
When a phase or significant task is finished, update: `TASKS.md`, GitHub issues, `CLAUDE.md`, memory files, and `PROJECT_DEVELOPMENT.md` (if architecture changed).
|
|
|
|
### Visual Verification (MANDATORY)
|
|
After every successful `npm run build`:
|
|
1. Take headless Chrome screenshots of affected pages
|
|
2. Compare against the EZSCALE design system (navy blue palette, Plus Jakarta Sans, custom SCSS in `resources/styles/`)
|
|
3. Fix any visual discrepancies before considering the task complete
|
|
4. Key pages: Login/Register, Marketing homepage, Admin/Account dashboards, Pricing page
|
|
|
|
## Laravel App Location
|
|
The Laravel application is in **`website/`**. All artisan, composer, and npm commands run from there.
|
|
|
|
```
|
|
website/
|
|
├── app/
|
|
│ ├── Models/ # 29 Eloquent models
|
|
│ ├── Http/Controllers/ # Account/, Admin/, Api/V1/ controllers
|
|
│ ├── Http/Resources/ # API Resources
|
|
│ ├── Services/Billing/ # BillingServiceInterface, Stripe, PayPal, Dunning
|
|
│ ├── Events/ # Payment, Subscription events
|
|
│ ├── Listeners/ # Event handlers
|
|
│ └── Console/Commands/ # RetryProvisioning, SyncStripePrices
|
|
├── bootstrap/app.php # Middleware, exceptions, routing (Laravel 12 slim — no Kernel files)
|
|
├── database/ # 59 migrations, 24 factories, seeders
|
|
├── resources/
|
|
│ ├── ts/ # TypeScript source (Vue 3 + Inertia)
|
|
│ │ ├── types/ # Shared TypeScript interfaces
|
|
│ │ ├── Layouts/ # AccountLayout, AdminLayout, AuthLayout, MarketingLayout
|
|
│ │ ├── Components/ # Shared + marketing components
|
|
│ │ └── Pages/ # 85 Vue pages (Auth/7, Admin/41, Marketing/14, etc.)
|
|
│ └── styles/ # EZSCALE design system SCSS (8 files)
|
|
├── routes/ # web, account, admin, marketing, webhooks, api
|
|
└── tests/ # ~497 Pest tests
|
|
```
|
|
|
|
## Tech Stack
|
|
- **Framework:** Laravel 12 (PHP 8.3)
|
|
- **Frontend:** Vue 3 + Inertia.js v2 + TypeScript (REQUIRED) + Vuetify 3 + Vite 7
|
|
- **UI Theme:** Custom EZSCALE design system — navy blue `#1d4ed8`, Plus Jakarta Sans + JetBrains Mono (Bunny Fonts CDN), subdomain-aware theming (light for marketing, dark for admin/account)
|
|
- **Charts:** ECharts via `vue-echarts`
|
|
- **Utilities:** `@vueuse/core`
|
|
- **Testing:** Pest 4 + PHPUnit 12 (~497 tests)
|
|
- **Formatting:** Laravel Pint
|
|
- **Payments:** Laravel Cashier (Stripe) + srmklive/paypal (PayPal)
|
|
- **Database:** MySQL 8.x, database driver for sessions (Redis NOT installed)
|
|
- **Auth:** Laravel Fortify (login, register, 2FA, password reset, email verify) + Passport (OAuth2/SSO)
|
|
- **Roles:** spatie/laravel-permission (admin, customer)
|
|
- **Queue:** Laravel Horizon
|
|
|
|
## Commands
|
|
```bash
|
|
cd website
|
|
composer run dev # Start all dev servers (artisan serve + queue + pail + vite)
|
|
php artisan test --compact # Run Pest tests
|
|
npm run build # Production build
|
|
vendor/bin/pint --dirty --format agent # Format changed files
|
|
```
|
|
|
|
## TypeScript Requirement (MANDATORY)
|
|
**All frontend code MUST use TypeScript.**
|
|
|
|
- All `.vue` files must use `<script setup lang="ts">` (never plain `<script setup>`)
|
|
- Props: `interface Props` + `withDefaults(defineProps<Props>(), {...})`
|
|
- Emits: `defineEmits<{ event: [payload: Type] }>()`
|
|
- Explicit types for `ref<Type>()`, `computed<Type>()`, and function return types
|
|
- No `any` type — use proper interfaces or type aliases
|
|
- Shared types go in `resources/ts/types/`
|
|
- Inertia page props should be typed with interfaces
|
|
|
|
## Design System
|
|
|
|
### Colors
|
|
- **Primary:** Navy blue `#1d4ed8`, lighter variant `#3b82f6`
|
|
- **Subdomain theming:** Marketing = light mode, Admin/Account = dark mode by default
|
|
- **Theme persistence:** localStorage key `ezscale-theme`, read on Vuetify init
|
|
|
|
### Typography
|
|
- **UI font:** Plus Jakarta Sans (Bunny Fonts CDN)
|
|
- **Code font:** JetBrains Mono (Bunny Fonts CDN)
|
|
|
|
### Key Patterns
|
|
- **Component ordering:** `<script lang="ts" setup>` then props interface, state, computed, methods, watchers
|
|
- **Layout system:** AppSidebar (collapsible) + AppTopNavbar (sticky) + CommandPalette (Cmd+K)
|
|
- **Navigation types:** `NavLink`, `NavGroup`, `NavSectionTitle` (see `resources/ts/@layouts/types.ts`)
|
|
- **State management:** Pinia stores (e.g., `stores/toast.ts`)
|
|
- **Icons:** Tabler icons via `@iconify/vue` (e.g., `tabler-smart-home`)
|
|
|
|
### Gotchas
|
|
- **User::billingInvoices()** — renamed from `invoices()` to avoid conflict with Laravel Cashier's built-in `invoices()` method. All call sites use `billingInvoices()`. The `withCount` uses alias `billing_invoices_count`.
|
|
|
|
## Agent Usage (MANDATORY)
|
|
Maximize use of subagents (Task tool) to reduce context usage. Main conversation should orchestrate; heavy lifting goes to agents.
|
|
|
|
- **Parallel agents**: Launch independent tasks as parallel agents, not sequentially in main context.
|
|
- **Delegate research**: Use `Explore` agents for codebase exploration and file searches.
|
|
- **Delegate implementation**: Use `general-purpose` agents for self-contained tasks.
|
|
- **Delegate reviews**: Use `feature-dev:code-reviewer` agents to review code after writing it.
|
|
- **Delegate architecture**: Use `feature-dev:code-architect` or `Plan` agents for feature design.
|
|
- **Background agents**: Use `run_in_background: true` for long-running tasks.
|
|
- **Batch similar work**: 10+ file updates with the same pattern go to an agent.
|
|
- **Frontend Design Skill**: ALWAYS run in background (`run_in_background: true`).
|
|
|
|
### Headless Chrome
|
|
```bash
|
|
google-chrome --headless=new --disable-gpu --no-sandbox --screenshot=/tmp/screenshot.png --window-size=1920,1080 --virtual-time-budget=15000 "URL"
|
|
```
|
|
- Must use `--headless=new`, `--virtual-time-budget=15000`, and `--no-sandbox`
|
|
- dbus errors in output are harmless — ignore them
|
|
- Read the resulting PNG with the Read tool to view it
|
|
|
|
## Code Conventions
|
|
|
|
### PHP
|
|
- PSR-12, enforced by Pint
|
|
- `declare(strict_types=1);` in all PHP files
|
|
- Explicit return types and parameter type hints
|
|
- PHP 8 constructor property promotion
|
|
- Form Request classes for validation (not inline in controllers)
|
|
- Service classes for business logic (thin controllers)
|
|
- Events/Listeners for side effects (email, provisioning)
|
|
- Eloquent over raw queries; avoid `DB::`, prefer `Model::query()`
|
|
- Eager loading to prevent N+1 queries
|
|
- `config()` only, never `env()` outside config files
|
|
- Named routes with `route()` helper
|
|
- Pest tests for all new functionality
|
|
- Database transactions for multi-step operations
|
|
- Check sibling files for conventions before creating new files
|
|
- Run `vendor/bin/pint --dirty --format agent` before finalizing
|
|
|
|
### Frontend (Vue/TypeScript)
|
|
- Use Vuetify components directly (VCard, VBtn, VTextField, etc.) — not raw HTML
|
|
- Use Inertia `Link` component for navigation (not `<a>` tags)
|
|
- Use `useForm()` from `@inertiajs/vue3` for form submissions
|
|
- Status badges: VChip with `resolveStatusColor()` utilities
|
|
- Pinia stores for shared state
|
|
- ECharts via `vue-echarts` for charts
|
|
|
|
## Security
|
|
- All API endpoints require authentication
|
|
- Admin routes protected by role-based middleware
|
|
- CSRF on all forms (webhooks exempted via `bootstrap/app.php`)
|
|
- Rate limiting on auth and API endpoints
|
|
- Audit logging for admin actions and billing events
|
|
|
|
## Domains
|
|
- **ezscale.dev** (dev) / **ezscale.cloud** (prod) — marketing site
|
|
- **account.ezscale.dev** / **account.ezscale.cloud** — customer dashboard
|
|
- **admin.ezscale.dev** / **admin.ezscale.cloud** — admin panel (Cloudflare Zero Trust)
|
|
- Subdomain routing configured in `bootstrap/app.php` via `Route::domain()`
|
|
|
|
## Key Business Domains
|
|
1. **Billing** — Subscriptions, invoices, payments (Stripe + PayPal), dunning, coupons
|
|
2. **Provisioning** — VirtFusion (VPS), SynergyCP (Dedicated), Enhance (Hosting), Pterodactyl (Game) — idempotent with retry
|
|
3. **Customer Management** — Profiles, support tickets, notifications
|
|
4. **Admin Panel** — Dashboard, analytics, user/service management
|
|
5. **SSO** — Single sign-on via Laravel Passport
|
|
|
|
## Strategic Direction: WHMCS Replacement
|
|
This app is being built into a full WHMCS replacement for EZSCALE. Scope to track:
|
|
|
|
- **Storefront / catalog** — product browsing, configurable options, cart, checkout, multi-currency, quotes, coupons, tax (regional)
|
|
- **Billing** — subscriptions, invoices, dunning, credit balances, refunds, Stripe + PayPal (more gateways later)
|
|
- **Provisioning** — VirtFusion / SynergyCP / Enhance / Pterodactyl modules (idempotent, retry, suspension/termination hooks)
|
|
- **Customer area** — services list, KB, ticketing, login history, two-factor, affiliate dashboard, credits
|
|
- **Marketing site** — pricing, status page, blog, lead capture, affiliate landing pages
|
|
- **Admin panel** — RBAC staff, financial reports, fraud detection, dunning queue, manual invoice ops, refund flows
|
|
- **Notifications** — transactional email, Discord webhooks, Slack handoff to Ezra (see `infrastructure/`)
|
|
- **Marketing email automation** — drip campaigns, lifecycle/winback, broadcast announcements, segmented lists (lead/customer/lapsed), unsubscribe + preference center, deliverability hooks (Stalwart). *Active planning.*
|
|
- **Future** — domain registrar integration, SSL provisioning, reseller multi-tenancy (see `KASM_AND_MULTITENANCY.md`), API for partner integrations
|
|
|
|
When planning new work, check what's already shipped (recent commits cover KB, tickets v2, multi-currency, cart, quotes, affiliates, credits, staff RBAC, fraud detection, service panels, churn prevention, login history, financial reports, configurable checkout). Gap-analyze against WHMCS feature parity before building.
|
|
|
|
## Sister Projects (Reference)
|
|
The EZSCALE platform is split across three repositories, all on Gitea (`git.ezscale.cloud`). Read their `CLAUDE.md` files when touching cross-repo concerns; do not modify them from this repo.
|
|
|
|
**Catalog data model:** website/ owns the product catalog (SKUs, pricing, configurable options, descriptions). It *pulls* from various sources to populate inventory and capacity dynamically — most notably from `infrastructure/` for the VPS and dedicated server lineups. Treat infrastructure/ as the source-of-truth for hardware inventory, capacity, and rack reality; treat website/ as the source-of-truth for what's *for sale*, at what price, and to whom.
|
|
|
|
### `../infrastructure/` — Ops platform & Ezra AI agent
|
|
**Path:** `/home/andrew/local_projects/infrastructure/` · **Repo:** `git@git.ezscale.cloud:EZSCALE/infrastructure.git`
|
|
|
|
Houses everything that runs the *business* (not the storefront): K3s clusters (US/EU), Terraform-managed Cloudflare DNS, Ansible playbooks, the `web/` Laravel 13 admin panel that hosts **Ezra** (the AI ops agent — capacity scoring, MRR forecasting, ticket sync, customer intelligence, investor reports), plus deep reference docs.
|
|
|
|
**What website/ pulls from infrastructure/ (catalog inputs):**
|
|
- **VPS lineups** — VirtFusion package definitions, hypervisor capacity, plan availability per region. Source: `infrastructure/web/app/Services/ApiClients/VirtFusion*` + `Services/Capacity/`. Use to drive the `/vps` product pages and "stock available" indicators.
|
|
- **Dedicated server lineups** — SynergyCP inventory + the rack reality in `infrastructure/docs/reference/rack-atl.md` (server SKUs, specs, current allocations). Use to drive the `/dedicated` product pages.
|
|
- **Vendor costs / margins** — `infrastructure/docs/reference/business-costs.md` (Evocative, Hetzner, etc.). Reference when setting prices; never expose to customers.
|
|
- **IP availability** — `infrastructure/docs/reference/ip-resources.md` (ARIN vs Evocative leases). Drives whether IPv4 add-ons can be sold without a waitlist.
|
|
|
|
**Other intersections (non-catalog):**
|
|
- **API client patterns** — `infrastructure/web/app/Services/ApiClients/BaseApiClient` provides retry, rate-limit, and IPv4 DNS pinning. Port that base class here when our provisioning clients need the same hardening.
|
|
- **Customer intelligence** — `Services/Intelligence/` syncs scoring/segmentation. Website billing events should feed this; risk scores from it should drive fraud thresholds here.
|
|
- **Ticketing** — Ezra ingests SupportPal tickets and drafts AI responses. As website/ takes over ticketing, agree on a sync contract (or have Ezra read from our DB).
|
|
- **Reverse DNS / mail** — `Services/Rdns/` (PowerDNS) and Stalwart/SOGo. Customer rDNS requests in the account panel should flow through Ezra, not direct PowerDNS calls.
|
|
- **Identity** — Authentik IdP at `id.ezscale.cloud`. Staff SSO should integrate via SocialiteProviders/Authentik (already used in `infrastructure/web/`); customers stay on Fortify + Passport.
|
|
- **Other reference docs** — `powerdns.md`, `mail-server.md`, `authentik.md`, `llc-agreement.md`, `zfs-storage.md`, `uptime-kuma.md`, `hardware-monitoring.md`.
|
|
|
|
### `../ezscale_api/` — Battlelog/ACP SaaS API
|
|
**Path:** `/home/andrew/local_projects/ezscale_api/` · **Repo:** `git@git.ezscale.cloud:EZSCALE/api.git` · **Endpoint:** `api.ezscale.cloud`
|
|
|
|
Centralized Laravel 12 API at `api.ezscale.cloud` that fronts Battlelog (BF3/BF4/BFH) for the **ACP (Adkats Control Panel)** product — game-server admin SaaS sold to Battlefield community server operators. TimescaleDB-backed, single shared API key, multi-tenant ACP instances consume it.
|
|
|
|
**Key intersections with website/:**
|
|
- **As a product** — ACP is one SKU in the website catalog. When provisioning ACP, website calls into the ACP tenant orchestrator (separate from this API) but tenants in turn need the API key issued/revoked here.
|
|
- **API key lifecycle** — Currently a single static `EZSCALE_API_KEY`. When website starts selling ACP plans with usage limits, we'll likely need per-tenant keys here — coordinate before changing the auth model.
|
|
- **Real-time pattern** — Standalone Node.js Socket.IO + Redis pub/sub setup (`ezscale_api/socketio/`) is a reusable template if website needs live admin dashboards beyond polling.
|
|
- **Helm/K8s deployment template** — `ezscale_api/helm/ezscale-api/` is the cleanest deploy reference for our eventual K8s migration of website/.
|
|
|
|
### Cross-repo conventions
|
|
- **Git host:** Gitea at `git.ezscale.cloud` (NOT GitHub). Use `mcp__gitea__*` tools, never `gh` CLI for these repos.
|
|
- **DB hosts:** Each app has its own DB. Cross-repo data access is via HTTP APIs, never shared DB connections.
|
|
- **Docs format:** Each repo's `CLAUDE.md` is the canonical onboarding doc. `docs/superpowers/specs/` holds design docs; `docs/reference/` (in infrastructure) holds long-lived ops reference.
|
|
|
|
## Reference Docs
|
|
- `TASKS.md` — Task list and progress tracking
|
|
- `PROJECT_DEVELOPMENT.md` — Architecture decisions, database schema, API integrations
|
|
- `FEATURES.md` — Feature specifications
|
|
- `ADVANCED_FEATURES.md` — Advanced feature specs
|
|
- `KASM_AND_MULTITENANCY.md` — Kasm Workspaces + reseller multi-tenancy
|
|
- `GETTING_STARTED.md` — Development setup guide
|
|
- `docs/` — Deployment configs (`deployment/`), API specs (`integrations/`), scripts (`scripts/`), feature plans (`superpowers/`)
|
|
- `website/CLAUDE.md` — Laravel Boost guidelines (auto-generated)
|