Extract Ladill Servers as standalone app at servers.ladill.com.

VPS and dedicated server ordering, managed panels, SSO, billing, and launcher integration — forked from hosting infrastructure with server-focused routes and dashboard.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-06 19:18:30 +00:00
co-authored by Cursor
commit b6c8ac343f
382 changed files with 67315 additions and 0 deletions
+156
View File
@@ -0,0 +1,156 @@
<?php
namespace App\Services\Afia;
use Illuminate\Support\Facades\Http;
use RuntimeException;
/**
* Afia an AI assistant scoped to a Ladill product (hosting or email).
* Self-contained: builds a product-specific system prompt + live context
* and calls the configured LLM (OpenAI or Anthropic).
*/
class AfiaService
{
public function enabled(): bool
{
return (bool) config('afia.enabled', true) && (string) config('afia.api_key', '') !== '';
}
/**
* @param array<int,array{role:string,text:string}> $history
* @param array<string,mixed> $context
*/
public function chat(string $message, array $history, array $context): string
{
if (! $this->enabled()) {
throw new RuntimeException('Afia is not configured.');
}
$provider = (string) config('afia.provider', 'openai');
$model = (string) config('afia.model', 'gpt-4o-mini');
$apiKey = (string) config('afia.api_key');
$messages = [['role' => 'system', 'content' => $this->systemPrompt($context)]];
foreach (array_slice($history, -8) as $turn) {
$role = ($turn['role'] ?? 'user') === 'assistant' ? 'assistant' : 'user';
$text = trim((string) ($turn['text'] ?? ''));
if ($text !== '') {
$messages[] = ['role' => $role, 'content' => $text];
}
}
$messages[] = ['role' => 'user', 'content' => $message];
return $provider === 'anthropic'
? $this->viaAnthropic($model, $apiKey, $messages)
: $this->viaOpenAi($model, $apiKey, $messages);
}
private function viaOpenAi(string $model, string $apiKey, array $messages): string
{
$res = Http::withToken($apiKey)->acceptJson()->timeout(45)
->post('https://api.openai.com/v1/chat/completions', [
'model' => $model,
'temperature' => 0.3,
'max_tokens' => 600,
'messages' => $messages,
]);
if ($res->failed()) {
throw new RuntimeException('OpenAI request failed: '.$res->status());
}
return trim((string) $res->json('choices.0.message.content', ''));
}
private function viaAnthropic(string $model, string $apiKey, array $messages): string
{
$system = $messages[0]['content'];
$turns = array_values(array_filter($messages, fn ($m) => $m['role'] !== 'system'));
$res = Http::withHeaders([
'x-api-key' => $apiKey,
'anthropic-version' => '2023-06-01',
])->acceptJson()->timeout(45)->post('https://api.anthropic.com/v1/messages', [
'model' => $model,
'max_tokens' => 600,
'system' => $system,
'messages' => array_map(fn ($m) => ['role' => $m['role'], 'content' => $m['content']], $turns),
]);
if ($res->failed()) {
throw new RuntimeException('Anthropic request failed: '.$res->status());
}
return trim((string) $res->json('content.0.text', ''));
}
/** @param array<string,mixed> $context */
private function systemPrompt(array $context): string
{
$ctx = collect($context)->map(fn ($v, $k) => "- {$k}: {$v}")->implode("\n");
return match ((string) config('afia.product', 'hosting')) {
'email' => $this->emailSystemPrompt($ctx),
default => $this->hostingSystemPrompt($ctx),
};
}
private function hostingSystemPrompt(string $ctx): string
{
return <<<PROMPT
You are Afia, the assistant inside Ladill Hosting Ladill's shared web hosting product (hosting.ladill.com).
Help the user buy hosting, manage accounts, link domains, use the control panel, and understand billing. Be concise,
friendly, and actionable: give short step-by-step answers and point to the relevant section by name.
What Ladill Hosting does and where things live:
- Dashboard: overview of all hosting accounts, pending orders, and linked domains.
- Product pages: Single Domain, Multi Domain, and WordPress hosting order new plans here.
- Account overview: per-account summary (plan, expiry, linked domains, email usage) with a Control Panel button.
- Control Panel: file manager, domains, databases, SSL, PHP settings, cron, terminal, and one-click apps.
- Domains: link a Ladill domain or an external domain to a hosting account; DNS may need updating for external domains.
- Billing: hosting renewals and upgrades are paid from the Ladill wallet (account portal Wallet).
- Team: invite teammates to co-manage hosting (sidebar Team).
Rules: Only answer questions about Ladill Hosting plans, accounts, domains on hosting, the control panel,
SSL, file manager, renewals, and wallet billing. If asked about Ladill Email, domains registration, or VPS/servers,
briefly redirect to the right Ladill app. Never invent passwords, DNS values, or prices tell the user where to find them.
Current user context:
{$ctx}
PROMPT;
}
private function emailSystemPrompt(string $ctx): string
{
return <<<PROMPT
You are Afia, the assistant inside Ladill Email Ladill's mailbox product (email.ladill.com).
Help the user set up email domains, create and manage mailboxes, and understand billing. Be concise,
friendly, and actionable: give short step-by-step answers and point to the relevant section by name.
What Ladill Email does and where things live:
- Domains: add a domain you want email on, then verify it by adding the shown DNS records (MX, SPF,
DKIM, DMARC). Mailboxes can only be created on a verified domain.
- Mailboxes: create a mailbox (address + display name + password + quota) on a verified domain. The
first mailbox is free; extra mailboxes are billed monthly from your Ladill wallet. Manage a mailbox to
reset its password or see IMAP/SMTP connection settings.
- Webmail: read & send from a mailbox at mail.ladill.com (Open Ladill Mail).
- Account: Billing (wallet balance, add funds, monthly mailbox subscriptions), Team (invite teammates to
co-manage), Settings (default quota, notifications), Developers (API tokens for the Email API).
Connection settings: IMAP host mail.<domain> port 993 (SSL); SMTP host mail.<domain> port 587
(STARTTLS); username is the full email address.
Facts: Mail is delivered by Ladill's mail servers. Prices show in GHS. Login/identity is via the Ladill
account (SSO). DNS changes can take time to propagate, so verification may not be instant.
Rules: Only answer questions about Ladill Email domains for email, DNS verification, mailboxes, webmail,
IMAP/SMTP setup, team, and billing for mailboxes. If asked about unrelated topics, briefly redirect.
Never invent passwords, DNS values, or prices tell the user where to find them. If something needs a
verified domain or a funded wallet, say so.
Current user context:
{$ctx}
PROMPT;
}
}