380 lines
12 KiB
PHP
380 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Paymenter\Extensions\Servers\Froxlor;
|
|
|
|
use App\Classes\Extension\Server;
|
|
use App\Models\Service;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\View;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class Froxlor extends Server
|
|
{
|
|
private $url;
|
|
private $key;
|
|
private $secret;
|
|
|
|
public function boot()
|
|
{
|
|
View::addNamespace('Froxlor', __DIR__ . '/views');
|
|
}
|
|
|
|
public function getConfig($values = []): array
|
|
{
|
|
return [
|
|
[
|
|
'name' => 'host',
|
|
'label' => 'Froxlor Host',
|
|
'type' => 'text',
|
|
'default' => 'https://your-froxlor-server.com',
|
|
'required' => true,
|
|
'validation' => 'url',
|
|
],
|
|
[
|
|
'name' => 'username',
|
|
'label' => 'API Key',
|
|
'type' => 'text',
|
|
'required' => true,
|
|
'encrypted' => true,
|
|
],
|
|
[
|
|
'name' => 'password',
|
|
'label' => 'Secret Key',
|
|
'type' => 'password',
|
|
'required' => true,
|
|
'encrypted' => true,
|
|
]
|
|
];
|
|
}
|
|
|
|
public function getCheckoutConfig(): array
|
|
{
|
|
return [
|
|
[
|
|
'name' => 'domain',
|
|
'label' => 'FQDN Domain Name/Optional Subdomain Name (Subdomain of .webhost.virtnet.bond)',
|
|
'type' => 'text',
|
|
'required' => true,
|
|
'default' => 'mywebsite.webhost.virtnet.bond',
|
|
'validation' => 'regex:/^[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',
|
|
'description' => 'Enter a valid domain (e.g., example.com). This may be a subdomain of webhost.virtnet.bond (e.g., example.webhost.virtnet.bond) and must be in its entirety and in a FQDN format (RFC4703, RFC1035, see https://www.cloudflare.com/learning/dns/glossary/what-is-a-domain-name/ for more information). We do not provide DNS (See https://www.cloudflare.com/learning/dns/what-is-dns/ for more information) or Registrar services through this service and do not offer domain registration beyond the stated subdomain if chosen. You will have to point any registered domains to the servers yourself, with the information directly available to you after ordering within the service area.','hi',
|
|
]
|
|
|
|
];
|
|
}
|
|
|
|
|
|
public function getProductConfig($values = []): array
|
|
{
|
|
return [
|
|
[
|
|
'name' => 'package',
|
|
'label' => 'Package ID',
|
|
'type' => 'text',
|
|
'required' => true,
|
|
]
|
|
];
|
|
}
|
|
|
|
public function testConfig(): bool|string
|
|
{
|
|
try {
|
|
$response = $this->request('Customers.listing');
|
|
return isset($response['data']['list']) ? true : 'Invalid API response';
|
|
} catch (\Exception $e) {
|
|
return $e->getMessage();
|
|
}
|
|
}
|
|
|
|
public function createServer(Service $service, $settings, $properties)
|
|
{
|
|
$this->loadConfig();
|
|
$user = $service->user;
|
|
|
|
// Generate clean username that meets Froxlor requirements
|
|
$cleanBase = preg_replace('/[^a-z0-9]/', '', strtolower($user->name));
|
|
if (empty($cleanBase)) {
|
|
$cleanBase = 'user'; // fallback if name contains no valid characters
|
|
}
|
|
|
|
// Ensure username starts with a letter (common requirement)
|
|
if (is_numeric($cleanBase[0])) {
|
|
$cleanBase = 'a' . $cleanBase;
|
|
}
|
|
|
|
// Combine with random 5-digit number (you wanted 5 digits instead of 4)
|
|
$username = substr($cleanBase, 0, 10) . rand(10000, 99999);
|
|
|
|
// Generate password without symbols (letters and numbers only, 8 chars)
|
|
$password = substr(str_shuffle(
|
|
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
|
), 0, 8);
|
|
|
|
// Create customer
|
|
$response = $this->request('Customers.add', [
|
|
'new_loginname' => $username,
|
|
'email' => $user->email,
|
|
'firstname' => $user->name,
|
|
'company' => (string)$service->id,
|
|
'new_customer_password' => $password,
|
|
'hosting_plan_id' => $settings['package'],
|
|
'api_allowed' => false,
|
|
'adminid' => 0,
|
|
]);
|
|
|
|
if (!isset($response['data']['customerid'])) {
|
|
throw new \Exception("Failed to create customer: " . json_encode($response));
|
|
}
|
|
|
|
// Store credentials
|
|
$service->properties()->updateOrCreate(
|
|
['key' => 'froxlor_username'],
|
|
['value' => $username]
|
|
);
|
|
|
|
$service->properties()->updateOrCreate(
|
|
['key' => 'froxlor_password'],
|
|
['value' => $password]
|
|
);
|
|
|
|
// Create domain
|
|
$this->request('Domains.add', [
|
|
'domain' => $properties['domain'],
|
|
'customerid' => $response['data']['customerid'],
|
|
'subcanemaildomain' => 2,
|
|
'selectserveralias' => 1,
|
|
'caneditdomain' => true,
|
|
'isbinddomain' => true,
|
|
'ssl_redirect' => true,
|
|
]);
|
|
|
|
return [
|
|
'success' => true,
|
|
'properties' => [
|
|
'froxlor_username' => $username,
|
|
'froxlor_password' => $password
|
|
]
|
|
];
|
|
}
|
|
|
|
public function suspendServer(Service $service, $settings, $properties)
|
|
{
|
|
$this->loadConfig();
|
|
$username = $this->getStoredUsername($service);
|
|
|
|
$response = $this->request('Customers.update', [
|
|
'loginname' => $username,
|
|
'deactivated' => 1, // Using 1 instead of true for API compatibility
|
|
]);
|
|
|
|
if (!isset($response['data']['customerid'])) {
|
|
throw new \Exception("Failed to suspend server: " . json_encode($response));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function unsuspendServer(Service $service, $settings, $properties)
|
|
{
|
|
$this->loadConfig();
|
|
$username = $this->getStoredUsername($service);
|
|
|
|
$response = $this->request('Customers.update', [
|
|
'loginname' => $username,
|
|
'deactivated' => 0, // Using 0 instead of false for API compatibility
|
|
]);
|
|
|
|
if (!isset($response['data']['customerid'])) {
|
|
throw new \Exception("Failed to unsuspend server: " . json_encode($response));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function terminateServer(Service $service, $settings, $properties)
|
|
{
|
|
$this->loadConfig();
|
|
$username = $this->getStoredUsername($service);
|
|
|
|
$this->request('Customers.delete', [
|
|
'loginname' => $username,
|
|
'delete_userfiles' => true,
|
|
]);
|
|
|
|
$service->properties()->where('key', 'froxlor_username')->delete();
|
|
return true;
|
|
}
|
|
|
|
public function getActions(Service $service)
|
|
{
|
|
return [
|
|
[
|
|
'type' => 'view',
|
|
'label' => 'Panel Info',
|
|
'name' => "panelinfo",
|
|
'icon' => 'fas fa-info-circle'
|
|
]
|
|
];
|
|
}
|
|
|
|
public function getView(Service $service, $settings, $properties, $view)
|
|
{
|
|
$this->loadConfig();
|
|
|
|
// Get both properties using the same method
|
|
$username = $service->properties()->where('key', 'froxlor_username')->value('value');
|
|
$password = $service->properties()->where('key', 'froxlor_password')->value('value');
|
|
|
|
return view("Froxlor::$view", [
|
|
'service' => $service,
|
|
'settings' => $settings,
|
|
'froxlor_url' => $this->url,
|
|
'panel_info' => [
|
|
'login_url' => rtrim($this->url, '/') . '/index.php',
|
|
'username' => $username,
|
|
'password' => $password
|
|
],
|
|
'error' => empty($username) ? "Account is being provisioned. Please check back shortly." : null
|
|
]);
|
|
}
|
|
|
|
private function request($command, array $data = [])
|
|
{
|
|
$this->loadConfig();
|
|
$response = Http::withHeaders(['Content-Type' => 'application/json'])
|
|
->withBasicAuth($this->key, $this->secret)
|
|
->post(rtrim($this->url, '/') . '/api.php', [
|
|
'command' => $command,
|
|
'params' => $data
|
|
]);
|
|
|
|
if (!$response->successful()) {
|
|
throw new \Exception($response->json()['message'] ?? 'API request failed');
|
|
}
|
|
|
|
return $response->json();
|
|
}
|
|
|
|
private function loadConfig()
|
|
{
|
|
$this->url = $this->config('host');
|
|
$this->key = $this->config('username');
|
|
$this->secret = $this->config('password');
|
|
}
|
|
|
|
private function getStoredUsername(Service $service): string
|
|
{
|
|
$username = $service->properties()->where('key', 'froxlor_username')->value('value');
|
|
|
|
if (!$username) {
|
|
throw new \Exception("Froxlor username not found for this service");
|
|
}
|
|
|
|
return $username;
|
|
}
|
|
|
|
|
|
public function upgradeServer(Service $service, $settings, $properties)
|
|
{
|
|
$this->loadConfig();
|
|
$username = $this->getStoredUsername($service);
|
|
|
|
// Get the new package ID with strict validation
|
|
$newPackageId = (int)($properties['package'] ?? $settings['package']);
|
|
if ($newPackageId <= 0) {
|
|
throw new \Exception("Invalid package ID provided for upgrade");
|
|
}
|
|
|
|
// Get current customer details for comparison
|
|
$customerInfo = $this->request('Customers.get', ['loginname' => $username]);
|
|
if (!isset($customerInfo['data']['customerid'])) {
|
|
throw new \Exception("Customer not found");
|
|
}
|
|
|
|
// Get new plan details
|
|
$planResponse = $this->request('HostingPlans.get', ['id' => $newPackageId]);
|
|
if (!isset($planResponse['data']['id'])) {
|
|
throw new \Exception("Plan not found with ID: $newPackageId");
|
|
}
|
|
|
|
// DECODE THE JSON PLAN CONFIGURATION
|
|
$planData = json_decode($planResponse['data']['value'], true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
throw new \Exception("Failed to decode plan configuration");
|
|
}
|
|
|
|
// Prepare update data
|
|
$updateData = ['loginname' => $username];
|
|
|
|
// Resource limits mapping
|
|
$resources = [
|
|
'diskspace' => ['type' => 'int', 'ul_field' => 'diskspace_ul'],
|
|
'traffic' => ['type' => 'int', 'ul_field' => 'traffic_ul'],
|
|
'subdomains' => ['type' => 'int', 'ul_field' => 'subdomains_ul'],
|
|
'emails' => ['type' => 'int', 'ul_field' => 'emails_ul'],
|
|
'email_accounts' => ['type' => 'int', 'ul_field' => 'email_accounts_ul'],
|
|
'email_forwarders' => ['type' => 'int', 'ul_field' => 'email_forwarders_ul'],
|
|
'ftps' => ['type' => 'int', 'ul_field' => 'ftps_ul'],
|
|
'mysqls' => ['type' => 'int', 'ul_field' => 'mysqls_ul'],
|
|
];
|
|
|
|
foreach ($resources as $field => $config) {
|
|
if (isset($planData[$field])) {
|
|
$updateData[$field] = ($config['type'] === 'int') ? (int)$planData[$field] : $planData[$field];
|
|
|
|
if (isset($config['ul_field']) && isset($planData[$config['ul_field']])) {
|
|
$updateData[$config['ul_field']] = (bool)$planData[$config['ul_field']];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Features
|
|
$features = [
|
|
'phpenabled' => 'bool',
|
|
'perlenabled' => 'bool',
|
|
'dnsenabled' => 'bool',
|
|
'logviewenabled' => 'bool',
|
|
'email_imap' => 'bool',
|
|
'email_pop3' => 'bool'
|
|
];
|
|
|
|
foreach ($features as $field => $type) {
|
|
if (isset($planData[$field])) {
|
|
$updateData[$field] = ($type === 'bool') ? (bool)$planData[$field] : $planData[$field];
|
|
}
|
|
}
|
|
|
|
// Special configurations
|
|
if (!empty($planData['allowed_phpconfigs'])) {
|
|
$updateData['allowed_phpconfigs'] = is_array($planData['allowed_phpconfigs'])
|
|
? $planData['allowed_phpconfigs']
|
|
: [$planData['allowed_phpconfigs']];
|
|
}
|
|
|
|
// Execute the update
|
|
$response = $this->request('Customers.update', $updateData);
|
|
|
|
if (!isset($response['data']['customerid'])) {
|
|
throw new \Exception("Failed to upgrade server: " . ($response['message'] ?? 'Unknown error'));
|
|
}
|
|
|
|
// Store the new package ID
|
|
$service->properties()->updateOrCreate(
|
|
['key' => 'froxlor_package'],
|
|
['value' => $newPackageId]
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
private function getPlanDetails($planId)
|
|
{
|
|
$response = $this->request('HostingPlans.get', ['id' => $planId]);
|
|
|
|
if (!isset($response['data']['id'])) {
|
|
throw new \Exception("Plan not found with ID: $planId");
|
|
}
|
|
|
|
return $response['data'];
|
|
}
|
|
} |