new changes

This commit is contained in:
Niranjan
2026-04-07 20:29:49 +05:30
parent 8fe63c7cf4
commit 31fe556bb0
79 changed files with 2917 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class ResolveTenantContext
{
public function handle(Request $request, Closure $next): Response
{
$tenantId = (string) $request->header('X-Tenant-Id', '');
$request->attributes->set('tenant_id', $tenantId);
return $next($request);
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace App\Modules\Agents;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class AgentController extends Controller
{
public function __construct(private readonly AgentSessionRepository $sessionsRepo)
{
}
public function issueEnrollmentToken(string $server): JsonResponse
{
return response()->json([
'data' => [
'server_id' => $server,
'token_type' => 'bootstrap',
'expires_in_sec' => 900,
],
]);
}
public function capabilities(string $server): JsonResponse
{
return response()->json([
'data' => [
'server_id' => $server,
'capabilities' => [],
],
]);
}
public function sessions(string $server): JsonResponse
{
return response()->json([
'data' => [
'server_id' => $server,
'sessions' => $this->sessionsRepo->listByServer($server),
],
]);
}
public function heartbeat(Request $request, string $server): JsonResponse
{
$agentUid = (string) $request->input('agent_uid', '');
$session = $this->sessionsRepo->touchHeartbeat($server, $agentUid, $request->all());
return response()->json([
'data' => $session,
]);
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Modules\Agents;
class AgentSessionRepository
{
public function listByServer(string $serverId): array
{
return [];
}
public function touchHeartbeat(string $serverId, string $agentUid, array $payload): array
{
return [
'server_id' => $serverId,
'agent_uid' => $agentUid,
'status' => 'online',
'last_seen_at' => now()->toISOString(),
'payload' => $payload,
];
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Modules\Auth;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class AuthController extends Controller
{
public function login(Request $request): JsonResponse
{
return response()->json([
'data' => [
'access_token' => 'placeholder-token',
'token_type' => 'Bearer',
'expires_in' => 3600,
],
]);
}
public function refresh(Request $request): JsonResponse
{
return response()->json([
'data' => [
'access_token' => 'placeholder-token-refreshed',
'token_type' => 'Bearer',
'expires_in' => 3600,
],
]);
}
public function me(Request $request): JsonResponse
{
return response()->json([
'data' => [
'id' => null,
'email' => null,
'tenants' => [],
],
]);
}
public function logout(Request $request): JsonResponse
{
return response()->json([], 204);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Modules\Jobs;
class CommandOrchestrator
{
private array $acceptedTypes = [
'SITE_CREATE',
'SITE_UPDATE',
'SITE_DELETE',
'DOMAIN_ADD',
'DOMAIN_REMOVE',
'SSL_ISSUE',
'SSL_APPLY',
'SSL_RENEW',
'WEBSERVER_RELOAD',
'FILE_LIST',
'FILE_READ',
'FILE_WRITE',
'CRON_LIST',
'CRON_CREATE',
'CRON_DELETE',
'FIREWALL_RULE_LIST',
'DOCKER_DEPLOY',
'PLUGIN_INSTALL',
'PLUGIN_UPDATE',
'PLUGIN_REMOVE',
'FIREWALL_RULE_ADD',
'FIREWALL_RULE_DELETE',
'BACKUP_RUN',
'BACKUP_RESTORE',
];
public function dispatch(array $commandEnvelope): array
{
$type = (string) ($commandEnvelope['type'] ?? '');
if (!in_array($type, $this->acceptedTypes, true)) {
return [
'job_status' => 'rejected',
'error' => 'Unsupported command type',
];
}
return [
'job_status' => 'queued',
'job_id' => uniqid('job_', true),
'tenant_id' => $commandEnvelope['tenant_id'] ?? null,
'type' => $type,
'idempotency_key' => $commandEnvelope['idempotency_key'] ?? null,
'args' => $commandEnvelope['args'] ?? [],
'message' => 'Command accepted for execution plane dispatch',
];
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Modules\Jobs;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class JobController extends Controller
{
public function __construct(private readonly CommandOrchestrator $orchestrator)
{
}
public function dispatch(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
$payload = $request->all();
$payload['tenant_id'] = $tenantId;
$result = $this->orchestrator->dispatch($payload);
return response()->json(['data' => $result], 202);
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Modules\Marketplace;
class MarketplaceCommandFactory
{
public function installPlugin(string $tenantId, array $input): array
{
return [
'tenant_id' => $tenantId,
'type' => 'PLUGIN_INSTALL',
'idempotency_key' => $input['idempotency_key'] ?? uniqid('plugin_install_', true),
'args' => $input,
];
}
public function updatePlugin(string $tenantId, array $input): array
{
return [
'tenant_id' => $tenantId,
'type' => 'PLUGIN_UPDATE',
'idempotency_key' => $input['idempotency_key'] ?? uniqid('plugin_update_', true),
'args' => $input,
];
}
public function removePlugin(string $tenantId, array $input): array
{
return [
'tenant_id' => $tenantId,
'type' => 'PLUGIN_REMOVE',
'idempotency_key' => $input['idempotency_key'] ?? uniqid('plugin_remove_', true),
'args' => $input,
];
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Modules\Marketplace;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class MarketplaceController extends Controller
{
public function __construct(private readonly MarketplaceWorkflowService $workflow)
{
}
public function catalog(Request $request): JsonResponse
{
return response()->json([
'data' => [
'items' => [],
'meta' => ['source' => 'placeholder-market-catalog'],
],
]);
}
public function install(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->install($tenantId, $request->all())], 202);
}
public function update(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->update($tenantId, $request->all())], 202);
}
public function remove(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->remove($tenantId, $request->all())], 202);
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Modules\Marketplace;
use App\Modules\Jobs\CommandOrchestrator;
class MarketplaceWorkflowService
{
public function __construct(
private readonly CommandOrchestrator $orchestrator,
private readonly MarketplaceCommandFactory $commands
) {
}
public function install(string $tenantId, array $input): array
{
return $this->orchestrator->dispatch($this->commands->installPlugin($tenantId, $input));
}
public function update(string $tenantId, array $input): array
{
return $this->orchestrator->dispatch($this->commands->updatePlugin($tenantId, $input));
}
public function remove(string $tenantId, array $input): array
{
return $this->orchestrator->dispatch($this->commands->removePlugin($tenantId, $input));
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Modules\Monitoring;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class MetricsIngestController extends Controller
{
public function ingest(Request $request): JsonResponse
{
return response()->json([
'data' => [
'accepted' => true,
'points' => is_array($request->input('points')) ? count($request->input('points')) : 0,
],
], 202);
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Modules\Monitoring;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class MonitoringController extends Controller
{
public function liveServerMetrics(string $serverId): JsonResponse
{
return response()->json([
'data' => [
'server_id' => $serverId,
'cpu_percent' => null,
'memory_percent' => null,
'disk_percent' => null,
'network' => ['in' => null, 'out' => null],
'status' => 'streaming',
],
]);
}
public function createAlertRule(Request $request): JsonResponse
{
return response()->json([
'data' => [
'status' => 'created',
'rule' => $request->all(),
],
], 201);
}
public function listAlerts(Request $request): JsonResponse
{
return response()->json([
'data' => [
'items' => [],
],
]);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Modules\Ops;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class BackupController extends Controller
{
public function __construct(private readonly OpsWorkflowService $workflow)
{
}
public function run(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->backupRun($tenantId, $request->all())], 202);
}
public function restore(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->backupRestore($tenantId, $request->all())], 202);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Modules\Ops;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class CronController extends Controller
{
public function __construct(private readonly OpsWorkflowService $workflow)
{
}
public function index(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->cronList($tenantId, $request->all())], 202);
}
public function store(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->cronCreate($tenantId, $request->all())], 202);
}
public function destroy(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->cronDelete($tenantId, $request->all())], 202);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Modules\Ops;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class FileOpsController extends Controller
{
public function __construct(private readonly OpsWorkflowService $workflow)
{
}
public function list(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->fileList($tenantId, $request->all())], 202);
}
public function read(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->fileRead($tenantId, $request->all())], 202);
}
public function write(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->fileWrite($tenantId, $request->all())], 202);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Modules\Ops;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class FirewallController extends Controller
{
public function __construct(private readonly OpsWorkflowService $workflow)
{
}
public function index(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->firewallList($tenantId, $request->all())], 202);
}
public function store(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->firewallAdd($tenantId, $request->all())], 202);
}
public function destroy(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json(['data' => $this->workflow->firewallDelete($tenantId, $request->all())], 202);
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace App\Modules\Ops;
class OpsCommandFactory
{
public function fileList(string $tenantId, array $input): array
{
return $this->cmd($tenantId, 'FILE_LIST', 'file_list_', $input);
}
public function fileRead(string $tenantId, array $input): array
{
return $this->cmd($tenantId, 'FILE_READ', 'file_read_', $input);
}
public function fileWrite(string $tenantId, array $input): array
{
return $this->cmd($tenantId, 'FILE_WRITE', 'file_write_', $input);
}
public function cronList(string $tenantId, array $input): array
{
return $this->cmd($tenantId, 'CRON_LIST', 'cron_list_', $input);
}
public function cronCreate(string $tenantId, array $input): array
{
return $this->cmd($tenantId, 'CRON_CREATE', 'cron_create_', $input);
}
public function cronDelete(string $tenantId, array $input): array
{
return $this->cmd($tenantId, 'CRON_DELETE', 'cron_delete_', $input);
}
public function firewallList(string $tenantId, array $input): array
{
return $this->cmd($tenantId, 'FIREWALL_RULE_LIST', 'fw_list_', $input);
}
public function firewallAdd(string $tenantId, array $input): array
{
return $this->cmd($tenantId, 'FIREWALL_RULE_ADD', 'fw_add_', $input);
}
public function firewallDelete(string $tenantId, array $input): array
{
return $this->cmd($tenantId, 'FIREWALL_RULE_DELETE', 'fw_delete_', $input);
}
public function backupRun(string $tenantId, array $input): array
{
return $this->cmd($tenantId, 'BACKUP_RUN', 'backup_run_', $input);
}
public function backupRestore(string $tenantId, array $input): array
{
return $this->cmd($tenantId, 'BACKUP_RESTORE', 'backup_restore_', $input);
}
private function cmd(string $tenantId, string $type, string $prefix, array $input): array
{
return [
'tenant_id' => $tenantId,
'type' => $type,
'idempotency_key' => $input['idempotency_key'] ?? uniqid($prefix, true),
'args' => $input,
];
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Modules\Ops;
use App\Modules\Jobs\CommandOrchestrator;
class OpsWorkflowService
{
public function __construct(
private readonly CommandOrchestrator $orchestrator,
private readonly OpsCommandFactory $commands
) {
}
public function fileList(string $tenantId, array $input): array { return $this->orchestrator->dispatch($this->commands->fileList($tenantId, $input)); }
public function fileRead(string $tenantId, array $input): array { return $this->orchestrator->dispatch($this->commands->fileRead($tenantId, $input)); }
public function fileWrite(string $tenantId, array $input): array { return $this->orchestrator->dispatch($this->commands->fileWrite($tenantId, $input)); }
public function cronList(string $tenantId, array $input): array { return $this->orchestrator->dispatch($this->commands->cronList($tenantId, $input)); }
public function cronCreate(string $tenantId, array $input): array { return $this->orchestrator->dispatch($this->commands->cronCreate($tenantId, $input)); }
public function cronDelete(string $tenantId, array $input): array { return $this->orchestrator->dispatch($this->commands->cronDelete($tenantId, $input)); }
public function firewallList(string $tenantId, array $input): array { return $this->orchestrator->dispatch($this->commands->firewallList($tenantId, $input)); }
public function firewallAdd(string $tenantId, array $input): array { return $this->orchestrator->dispatch($this->commands->firewallAdd($tenantId, $input)); }
public function firewallDelete(string $tenantId, array $input): array { return $this->orchestrator->dispatch($this->commands->firewallDelete($tenantId, $input)); }
public function backupRun(string $tenantId, array $input): array { return $this->orchestrator->dispatch($this->commands->backupRun($tenantId, $input)); }
public function backupRestore(string $tenantId, array $input): array { return $this->orchestrator->dispatch($this->commands->backupRestore($tenantId, $input)); }
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Modules\PublicApi;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class PublicApiController extends Controller
{
public function __construct(private readonly PublicTokenService $tokens)
{
}
public function issueToken(Request $request): JsonResponse
{
return response()->json([
'data' => $this->tokens->issue($request->all()),
], 201);
}
public function introspect(Request $request): JsonResponse
{
$token = (string) $request->input('token', '');
return response()->json([
'data' => $this->tokens->introspect($token),
]);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Modules\PublicApi;
class PublicTokenService
{
public function issue(array $input): array
{
return [
'token' => 'public-token-placeholder',
'scopes' => $input['scopes'] ?? [],
'expires_in' => 3600,
];
}
public function introspect(string $token): array
{
return [
'active' => $token !== '',
'scopes' => [],
];
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace App\Modules\Rbac;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class RbacController extends Controller
{
public function __construct(private readonly ScopeEvaluator $evaluator)
{
}
public function roles(): JsonResponse
{
return response()->json(['data' => []]);
}
public function createRole(Request $request): JsonResponse
{
return response()->json(['data' => ['status' => 'created']], 201);
}
public function attachPermissions(Request $request, string $role): JsonResponse
{
return response()->json(['data' => ['role_id' => $role, 'status' => 'updated']]);
}
public function assignRoles(Request $request, string $user): JsonResponse
{
return response()->json(['data' => ['user_id' => $user, 'status' => 'updated']]);
}
public function checkAccess(Request $request): JsonResponse
{
$payload = $request->all();
$allowed = $this->evaluator->isAllowed(
$payload['grants'] ?? [],
(string) ($payload['action'] ?? ''),
(string) ($payload['resource_type'] ?? ''),
isset($payload['resource_id']) ? (string) $payload['resource_id'] : null
);
return response()->json([
'data' => ['allowed' => $allowed],
]);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Modules\Rbac;
class ScopeEvaluator
{
public function isAllowed(array $grants, string $action, string $resourceType, ?string $resourceId = null): bool
{
foreach ($grants as $grant) {
$grantAction = $grant['action'] ?? null;
$grantResourceType = $grant['resource_type'] ?? null;
$grantResourceId = $grant['resource_id'] ?? null;
$effect = $grant['effect'] ?? 'allow';
$matches = ($grantAction === '*' || $grantAction === $action)
&& ($grantResourceType === '*' || $grantResourceType === $resourceType)
&& ($grantResourceId === null || $grantResourceId === $resourceId);
if ($matches && $effect === 'deny') {
return false;
}
if ($matches && $effect === 'allow') {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Modules\SaaS;
class BillingHooksService
{
public function emitUsageEvent(string $tenantId, string $eventType, array $payload): array
{
return [
'tenant_id' => $tenantId,
'event_type' => $eventType,
'status' => 'queued',
'payload' => $payload,
];
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Modules\SaaS;
class QuotaService
{
public function check(string $tenantId, string $resource, int $requested = 1): array
{
return [
'tenant_id' => $tenantId,
'resource' => $resource,
'requested' => $requested,
'allowed' => true,
'limit' => null,
'current_usage' => null,
];
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Modules\SaaS;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class SaaSController extends Controller
{
public function __construct(
private readonly QuotaService $quota,
private readonly BillingHooksService $billing
) {
}
public function checkQuota(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
$resource = (string) $request->input('resource', '');
$requested = (int) $request->input('requested', 1);
return response()->json([
'data' => $this->quota->check($tenantId, $resource, $requested),
]);
}
public function usageEvent(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
$eventType = (string) $request->input('event_type', 'generic');
$payload = (array) $request->input('payload', []);
return response()->json([
'data' => $this->billing->emitUsageEvent($tenantId, $eventType, $payload),
], 202);
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Modules\Server;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class ServerController extends Controller
{
public function __construct(private readonly ServerRepository $servers)
{
}
public function index(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
return response()->json([
'data' => $this->servers->listByTenant($tenantId),
'meta' => ['tenant_id' => $tenantId],
]);
}
public function store(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
$server = $this->servers->create([
'tenant_id' => $tenantId,
'name' => (string) $request->input('name', ''),
'id' => (string) $request->input('id', ''),
]);
return response()->json([
'data' => $server,
], 201);
}
public function show(string $server): JsonResponse
{
return response()->json([
'data' => ['id' => $server],
]);
}
public function update(Request $request, string $server): JsonResponse
{
return response()->json([
'data' => ['id' => $server, 'status' => 'updated'],
]);
}
public function destroy(string $server): JsonResponse
{
return response()->json([], 204);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Modules\Server;
class ServerRepository
{
/**
* Placeholder persistence adapter.
* Replace with Eloquent model queries in full Laravel bootstrap.
*/
public function listByTenant(string $tenantId): array
{
return [];
}
public function create(array $attributes): array
{
return [
'id' => $attributes['id'] ?? null,
'tenant_id' => $attributes['tenant_id'] ?? null,
'name' => $attributes['name'] ?? null,
'status' => 'provisioning',
];
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace App\Modules\Site;
class HostingCommandFactory
{
public function buildCreateSite(string $tenantId, array $input, string $webserver): array
{
return [
'tenant_id' => $tenantId,
'type' => 'SITE_CREATE',
'idempotency_key' => $input['idempotency_key'] ?? uniqid('site_create_', true),
'args' => [
'server_id' => $input['server_id'] ?? null,
'site_name' => $input['site_name'] ?? null,
'domain' => $input['domain'] ?? null,
'root_path' => $input['root_path'] ?? null,
'webserver' => $webserver,
],
];
}
public function buildUpdateSite(string $tenantId, string $siteId, array $input): array
{
return [
'tenant_id' => $tenantId,
'type' => 'SITE_UPDATE',
'idempotency_key' => $input['idempotency_key'] ?? uniqid('site_update_', true),
'args' => [
'site_id' => $siteId,
'server_id' => $input['server_id'] ?? null,
'changes' => $input['changes'] ?? [],
],
];
}
public function buildDeleteSite(string $tenantId, string $siteId, array $input): array
{
return [
'tenant_id' => $tenantId,
'type' => 'SITE_DELETE',
'idempotency_key' => $input['idempotency_key'] ?? uniqid('site_delete_', true),
'args' => [
'site_id' => $siteId,
'server_id' => $input['server_id'] ?? null,
],
];
}
public function buildAddDomain(string $tenantId, string $siteId, array $input): array
{
return [
'tenant_id' => $tenantId,
'type' => 'DOMAIN_ADD',
'idempotency_key' => $input['idempotency_key'] ?? uniqid('domain_add_', true),
'args' => [
'site_id' => $siteId,
'server_id' => $input['server_id'] ?? null,
'domain' => $input['domain'] ?? null,
],
];
}
public function buildRemoveDomain(string $tenantId, string $siteId, string $domainId, array $input): array
{
return [
'tenant_id' => $tenantId,
'type' => 'DOMAIN_REMOVE',
'idempotency_key' => $input['idempotency_key'] ?? uniqid('domain_remove_', true),
'args' => [
'site_id' => $siteId,
'domain_id' => $domainId,
'server_id' => $input['server_id'] ?? null,
],
];
}
public function buildIssueSsl(string $tenantId, array $input): array
{
return [
'tenant_id' => $tenantId,
'type' => 'SSL_ISSUE',
'idempotency_key' => $input['idempotency_key'] ?? uniqid('ssl_issue_', true),
'args' => $input,
];
}
public function buildApplySsl(string $tenantId, array $input): array
{
return [
'tenant_id' => $tenantId,
'type' => 'SSL_APPLY',
'idempotency_key' => $input['idempotency_key'] ?? uniqid('ssl_apply_', true),
'args' => $input,
];
}
public function buildRenewSsl(string $tenantId, array $input): array
{
return [
'tenant_id' => $tenantId,
'type' => 'SSL_RENEW',
'idempotency_key' => $input['idempotency_key'] ?? uniqid('ssl_renew_', true),
'args' => $input,
];
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace App\Modules\Site;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class SiteController extends Controller
{
public function __construct(private readonly SiteWorkflowService $workflow)
{
}
public function index(): JsonResponse { return response()->json(['data' => []]); }
public function store(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
$result = $this->workflow->createSite($tenantId, $request->all());
return response()->json(['data' => $result], 202);
}
public function show(string $site): JsonResponse { return response()->json(['data' => ['id' => $site]]); }
public function update(Request $request, string $site): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
$result = $this->workflow->updateSite($tenantId, $site, $request->all());
return response()->json(['data' => $result], 202);
}
public function destroy(Request $request, string $site): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
$result = $this->workflow->deleteSite($tenantId, $site, $request->all());
return response()->json(['data' => $result], 202);
}
public function addDomain(Request $request, string $site): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
$result = $this->workflow->addDomain($tenantId, $site, $request->all());
return response()->json(['data' => $result], 202);
}
public function removeDomain(Request $request, string $site, string $domain): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
$result = $this->workflow->removeDomain($tenantId, $site, $domain, $request->all());
return response()->json(['data' => $result], 202);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace App\Modules\Site;
use App\Modules\Jobs\CommandOrchestrator;
use InvalidArgumentException;
class SiteWorkflowService
{
public function __construct(
private readonly CommandOrchestrator $orchestrator,
private readonly HostingCommandFactory $commandFactory,
private readonly WebserverProfileResolver $webserverResolver
)
{
}
public function createSite(string $tenantId, array $input): array
{
$webserver = $this->webserverResolver->normalize((string) ($input['webserver'] ?? 'nginx'));
$command = $this->commandFactory->buildCreateSite($tenantId, $input, $webserver);
return $this->orchestrator->dispatch($command);
}
public function updateSite(string $tenantId, string $siteId, array $input): array
{
$command = $this->commandFactory->buildUpdateSite($tenantId, $siteId, $input);
return $this->orchestrator->dispatch($command);
}
public function deleteSite(string $tenantId, string $siteId, array $input = []): array
{
$command = $this->commandFactory->buildDeleteSite($tenantId, $siteId, $input);
return $this->orchestrator->dispatch($command);
}
public function addDomain(string $tenantId, string $siteId, array $input): array
{
if (empty($input['domain'])) {
throw new InvalidArgumentException('Domain is required');
}
$command = $this->commandFactory->buildAddDomain($tenantId, $siteId, $input);
return $this->orchestrator->dispatch($command);
}
public function removeDomain(string $tenantId, string $siteId, string $domainId, array $input = []): array
{
$command = $this->commandFactory->buildRemoveDomain($tenantId, $siteId, $domainId, $input);
return $this->orchestrator->dispatch($command);
}
public function issueSsl(string $tenantId, array $input): array
{
$command = $this->commandFactory->buildIssueSsl($tenantId, $input);
return $this->orchestrator->dispatch($command);
}
public function applySsl(string $tenantId, array $input): array
{
$command = $this->commandFactory->buildApplySsl($tenantId, $input);
return $this->orchestrator->dispatch($command);
}
public function renewSsl(string $tenantId, array $input): array
{
$command = $this->commandFactory->buildRenewSsl($tenantId, $input);
return $this->orchestrator->dispatch($command);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Modules\Site;
use InvalidArgumentException;
class WebserverProfileResolver
{
/**
* Normalize webserver name to an allowed adapter key.
*/
public function normalize(string $webserver): string
{
$normalized = strtolower(trim($webserver));
return match ($normalized) {
'nginx' => 'nginx',
'apache', 'httpd' => 'apache',
'openlitespeed', 'ols' => 'openlitespeed',
default => throw new InvalidArgumentException('Unsupported webserver profile'),
};
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Modules\Ssl;
use App\Modules\Site\SiteWorkflowService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class SslController extends Controller
{
public function __construct(private readonly SiteWorkflowService $workflow)
{
}
public function issue(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
$result = $this->workflow->issueSsl($tenantId, $request->all());
return response()->json(['data' => $result], 202);
}
public function apply(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
$result = $this->workflow->applySsl($tenantId, $request->all());
return response()->json(['data' => $result], 202);
}
public function renew(Request $request): JsonResponse
{
$tenantId = (string) $request->attributes->get('tenant_id', '');
$result = $this->workflow->renewSsl($tenantId, $request->all());
return response()->json(['data' => $result], 202);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Modules\Tenant;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class TenantController extends Controller
{
public function index(Request $request): JsonResponse
{
return response()->json(['data' => []]);
}
public function store(Request $request): JsonResponse
{
return response()->json(['data' => ['status' => 'created']], 201);
}
public function show(string $tenant): JsonResponse
{
return response()->json(['data' => ['id' => $tenant]]);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Modules\Webhook;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class WebhookController extends Controller
{
public function index(Request $request): JsonResponse
{
return response()->json(['data' => ['items' => []]]);
}
public function store(Request $request): JsonResponse
{
return response()->json([
'data' => [
'status' => 'created',
'endpoint' => $request->all(),
],
], 201);
}
public function testDelivery(Request $request): JsonResponse
{
return response()->json([
'data' => [
'status' => 'queued',
'target' => $request->input('url'),
],
], 202);
}
}