Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ APP_DEMO=false
APP_DATE_TIME_FORMAT="Y-m-d H:i:s"

PLOI_TOKEN=
PLOI_CORE_TOKEN=

IMPERSONATION=false

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
php: [8.2]
php: [8.4]

runs-on: ${{ matrix.os }}
steps:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ yarn-error.log
rr
.rr.yaml
.DS_Store
.phpunit.cache
122 changes: 122 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Ploi Core is a Laravel-based webhosting management platform that allows users to launch their own webhosting service using ploi.io as the backend infrastructure.

## Essential Commands

### Development
```bash
# Start development server
npm run dev

# Build for production
npm run build

# Watch for changes
npm run watch

# Format PHP code
composer format

# Run tests
php artisan test
php artisan test --filter=TestName

# Run browser tests
php artisan dusk

# Clear all caches
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear

# Queue management
php artisan horizon
php artisan queue:work
```

### Database
```bash
# Run migrations
php artisan migrate

# Rollback migrations
php artisan migrate:rollback

# Fresh migration with seeders
php artisan migrate:fresh --seed
```

### Custom Artisan Commands
```bash
php artisan core:install # Initial installation
php artisan core:synchronize # Sync with Ploi API
php artisan core:cleanup # Clean up resources
php artisan core:trial # Manage trials
```

## Architecture Overview

### Technology Stack
- **Backend**: Laravel 11 (PHP 8.2+), Filament v3 admin panel
- **Frontend**: Vue 3 with Inertia.js, Tailwind CSS, Vite
- **Queue**: Laravel Horizon with Redis
- **Payments**: Laravel Cashier (Stripe)
- **Testing**: Pest PHP, Laravel Dusk

### Key Directories
- `app/Services/Ploi/` - Ploi.io API integration layer
- `app/Filament/` - Admin panel resources and pages
- `app/Http/Controllers/` - Web and API controllers
- `app/Jobs/` - Async queue jobs for Ploi API operations
- `resources/js/Pages/` - Inertia.js Vue pages
- `resources/js/components/` - Reusable Vue components

### Ploi API Integration
The application heavily integrates with the Ploi.io API. Key service class is at `app/Services/Ploi/Ploi.php`. All server/site management operations go through this API layer. Use queue jobs for long-running operations to avoid timeouts.

### Database Structure
Main entities: Users, Packages, Servers, Sites, Databases, Certificates, Cronjobs. Multi-tenancy through user-server-site relationships. Role-based access: admin, reseller, user.

### Frontend Architecture
- Inertia.js handles the Vue-Laravel bridge
- Pages are in `resources/js/Pages/` following Laravel route structure
- Shared data is passed via Inertia middleware
- Vuex store modules in `resources/js/store/`
- Form handling uses Inertia forms

### Testing Approach
- Feature tests use Pest PHP syntax
- Database tests use RefreshDatabase trait
- API calls should be mocked using Http::fake()
- Browser tests in `tests/Browser/` using Dusk

### Important Environment Variables
```
PLOI_TOKEN= # Ploi API token
APP_DEMO=false # Demo mode toggle
STRIPE_KEY= # Stripe public key
STRIPE_SECRET= # Stripe secret key
```

### Development Workflow
1. Always run `npm run dev` for frontend changes
2. Use queue workers for Ploi API operations
3. Clear caches when changing config or routes
4. Format code with `composer format` before commits
5. Test with `php artisan test` for unit/feature tests

### Common Patterns
- Use Actions (`app/Actions/`) for business logic
- API responses follow Laravel's resource pattern
- Filament resources handle admin CRUD operations
- Queue jobs for async Ploi API calls
- Service classes for external integrations

### Deployment
Production deployment uses the `update.sh` script which handles git pull, composer install, migrations, and cache clearing. Laravel Horizon manages queues in production.
12 changes: 3 additions & 9 deletions app/Console/Commands/Core/Install.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,11 @@ protected function askAboutDefaultPackages()
]);
}

protected function getCompany($ploiCoreKey, $token)
protected function getCompany($token)
{
$response = Http::withHeaders([
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'X-Ploi-Core-Key' => $ploiCoreKey
'Content-Type' => 'application/json'
])
->withToken($token)
->get((new Ploi)->url . 'ping');
Expand Down Expand Up @@ -279,11 +278,7 @@ protected function checkCredentials()
$ploiApiToken = $this->ask('Enter the Ploi API token', env('PLOI_TOKEN'));
} while (empty($ploiApiToken));

do {
$ploiCoreKey = $this->ask('Enter the Ploi Core key', env('PLOI_CORE_TOKEN'));
} while (empty($ploiCoreKey));

$this->company = $this->getCompany($ploiCoreKey, $ploiApiToken);
$this->company = $this->getCompany($ploiApiToken);

if (!$this->company) {
$this->error('Could not authenticate with ploi.io, please retry by running this command again.');
Expand All @@ -304,7 +299,6 @@ protected function checkCredentials()
}

$this->writeToEnvironmentFile('PLOI_TOKEN', $ploiApiToken);
$this->writeToEnvironmentFile('PLOI_CORE_TOKEN', $ploiCoreKey);

$name = $this->ask('What is the name of your company? (Press enter to keep the name here)', $this->company['name']);
$this->writeToEnvironmentFile('APP_NAME', $name);
Expand Down
3 changes: 1 addition & 2 deletions app/Http/Middleware/InstallationComplete.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public function handle($request, Closure $next)
protected function isInstallationComplete()
{
return config('app.key') &&
config('services.ploi.token') &&
config('services.ploi.core-token');
config('services.ploi.token');
}
}
3 changes: 1 addition & 2 deletions app/Jobs/Core/Ping.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ public function handle()

$response = Http::withHeaders([
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'X-Ploi-Core-Key' => config('services.ploi.core-token')
'Content-Type' => 'application/json'
])
->withToken(config('services.ploi.token'))
->post((new Ploi)->url . 'ping', [
Expand Down
2 changes: 1 addition & 1 deletion app/Services/Ploi/Exceptions/Resource/RequiresId.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Services\Ploi\Exceptions\Resource;
namespace App\Services\Ploi\Exceptions\Resource;

use Exception;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<?php

namespace Services\Ploi\Exceptions\Resource\Server\Site;
namespace App\Services\Ploi\Exceptions\Resource\Server\Site;

use Exception;

/**
* Class DomainAlreadyExists
*
* @package Services\Ploi\Exceptions\Resource\Server\Site
* @package App\Services\Ploi\Exceptions\Resource\Server\Site
*/
class DomainAlreadyExists extends Exception
{
Expand Down
26 changes: 2 additions & 24 deletions app/Services/Ploi/Ploi.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,17 @@ class Ploi

private $apiToken;

private $apiCoreToken;

protected PendingRequest $client;

public function __construct(string $token = null, string $coreApiToken = null)
public function __construct(string $token = null)
{
$this->url = config('services.ploi-api.url');

if (!$token) {
$token = config('services.ploi.token');
}

if (!$coreApiToken) {
$coreApiToken = config('services.ploi.core-token');
}

$this->setApiToken($token);
$this->setCoreApiToken($coreApiToken);

$this->buildClient();
}

Expand All @@ -58,22 +50,13 @@ public function setApiToken($token): self
return $this;
}

public function setCoreApiToken($coreApiToken): self
{
// Set the token
$this->apiCoreToken = $coreApiToken;

return $this;
}

public function buildClient(): static
{
$this->client = Http::baseUrl($this->url)
->withHeaders([
'Authorization' => 'Bearer ' . $this->getApiToken(),
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'X-Ploi-Core-Key' => $this->getCoreApiToken(),
]);

if (app()->isLocal()) {
Expand All @@ -88,11 +71,6 @@ public function getApiToken(): string
return $this->apiToken;
}

public function getCoreApiToken(): string
{
return $this->apiCoreToken;
}

public function makeAPICall(string $url, string $method = 'get', array $options = []): Response
{
if (!in_array($method, ['get', 'post', 'patch', 'delete'])) {
Expand Down Expand Up @@ -134,7 +112,7 @@ public function makeAPICall(string $url, string $method = 'get', array $options
return new Response($response);
}

public function server(int $id = null)
public function server(int $id = null): Server
{
return new Server($this, $id);
}
Expand Down
2 changes: 1 addition & 1 deletion app/Services/Ploi/Resources/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use stdClass;
use App\Services\Ploi\Ploi;
use App\Services\Ploi\Exceptions\Http\NotValid;
use Services\Ploi\Exceptions\Resource\RequiresId;
use App\Services\Ploi\Exceptions\Resource\RequiresId;

class Server extends Resource
{
Expand Down
4 changes: 2 additions & 2 deletions app/Services/Ploi/Resources/Site.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use stdClass;
use Exception;
use App\Services\Ploi\Exceptions\Http\NotValid;
use Services\Ploi\Exceptions\Resource\RequiresId;
use Services\Ploi\Exceptions\Resource\Server\Site\DomainAlreadyExists;
use App\Services\Ploi\Exceptions\Resource\RequiresId;
use App\Services\Ploi\Exceptions\Resource\Server\Site\DomainAlreadyExists;

/**
* Class Site
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"require-dev": {
"barryvdh/laravel-debugbar": "^3.3",
"friendsofphp/php-cs-fixer": "^3.1.0",
"fzaninotto/faker": "^1.9.1",
"fakerphp/faker": "^1.23",
"laravel/dusk": "^8.0",
"mockery/mockery": "^1.3.1",
"nunomaduro/collision": "^8.1",
Expand Down
Loading