Sunucu Modülü Yazma

1.8k görüntülenme Markdown

Sunucu modülü, ödenmiş bir siparişi hosting panelinde ya da bulut sağlayıcısında gerçek bir hesaba dönüştürür. Çekirdek yaşam döngüsü metotlarınızı adıyla çağırır; döndürdüğünüz şey hizmetin üzerine yazılır.

Genel Bakış

Servers en büyük modül tipidir: 50 modül, üçü paylaşımlı hosting, özel makine ve sanallaştırma için kum havuzu örneği.

Sınıfınız ServerModule'ü genişletir, o da ModuleBaseTrait'i içeri alır. İkisi birlikte sunucuyu, hizmeti, ürünü, alıcıyı, çözümlenmiş limitleri, eklenti cevaplarını ve araç düzeneğini zaten tutar.

Çekirdek sınıfınızı hiç kurmaz: örneği fabrikadan çözer ve fiili adıyla çağırır. Yazmadığınız bir fiil atlanır; tipin isteğe-bağlılık mekanizması budur.

Services::run_module() Tek kapı: örneği kurar, takma adları çözer, metodu çalıştırır, sonucu uygular.
Services::instance_module() Modül tipini hizmetten seçer, sonra fabrikayı çağırıp hizmeti ve siparişi doldurur. hosting ve server Servers'a gider.
Modules::getInstance() Kanonik fabrika: yapılandırma, dil, örnek önbelleği. Bir modül için new asla kullanılmaz.
ModuleQueue Yeniden deneyen arka plan işçisi: aynı kapı, sonra aksiyonun gerektirdiği durum.

Ön Koşullar

  • Önce Modül Anatomisi ve Modül Yapılandırması; bu makale yalnızca Servers tipini anlatır.
  • API erişimi olan bir sağlayıcı hesabı ve bağlantı testini geçen bir sunucu kaydı.
  • O sunucuya bağlı bir test ürünü; yoksa create() okuyacak bir plan bulamaz.
  • Hata, false döndürerek değil fırlatılarak bildirilir.

Yapı

Her modül için tek bir dizin, içindeki sınıfla birebir aynı adda.

yerleşim
coremio/modules/Servers/Acme/
├── Acme.php            sınıf: extends ServerModule
├── ApiClient.php       kendi HTTP sarmalayıcınız, düz new, WISECP modülü değil
├── config.php          künye, sunucu form alanları, desteklenen kartlar ve araçlar
├── logo.png            modül seçicide görünür
├── lang/en.php         $this->lang, dil başına bir dosya
└── pages/              get_page() ile render edilen isteğe bağlı şablonlar
sınıf zinciri
namespace WISECP\Modules\Servers;

use Exception;
use Language;
use ServerModule;

class Acme extends ServerModule
{
    private ApiClient $api;

    // set_server() tarafından çağrılır; onu hem yapıcı hem set_service() koşar,
    // $this->server dolduktan ve sırları çözüldükten sonra.
    protected function define_server_info(array $server = []): void
    {
        include_once __DIR__ . DS . 'ApiClient.php';
        $this->api = new ApiClient($server);
    }
}
EkranSınıfınızdaki giriş noktasıNereden ulaşılır
Sunucu ayar formuconfig.php alanları, ardından test_connect()Admin, Sunucular, Sunucu Yönet
Ürün ayarlarıproduct_configuration(), save_product_configuration()Admin, ürün detayı, Modül sekmesi
Sağlamacreate(), suspend(), unsuspend(), cancel()Sipariş onayı, admin aksiyonları, kuyruk
Müşteri paneli panosudashboard_data()Müşteri panelindeki hizmet detayı
Müşteri paneli araçlarıtool_data(), tool_action()Araç kenar çubuğu, araçlar makalesine bakın
Metrik faturalamametrics_usage() ya da metrics_usage_bulk()Kullanım toplama cron'u
İçe aktarmalist()Admin, panelden hesap içe aktarma

Adım Adım

Katmanları Bu Sırayla Yazın

Yaşam döngüsüyle başlamak boşa iştir. create(), alıcının seçimlerini hizmet seçeneklerinden okur ve o seçenekler ancak ürün formu tanımlıysa oluşur.

  1. define_server_info(), ardından test_connect(): sunucuyu ekleyin, kimlik bilgilerini kanıtlayın.
  2. product_configuration() ve save_product_configuration(), artı plan listesini dolduran çağrılabilir metot.
  3. create(), sonra suspend(), unsuspend(), cancel().
  4. change_password() ve upgrade().
  5. list(), dashboard_data(), tek oturum açma metotları, metrikler.
  6. configure_features() ve araçlar.

Her katmanı bir sonrakine geçmeden canlı sağlayıcıya karşı doğrulayın: sonda çıkan ilk hatanın altı olası nedeni olur.

Sağlayıcıya Bağlanma

Kimlik bilgileri sunucu kaydından gelir. Hangi alanların görüneceğine config.php karar verir; taban sınıf onları çözülmüş teslim eder.

config.php
return [
    'name'                => 'Acme Cloud',

    // 'hosting' = alan adı tabanlı hesaplar, 'server' = VPS ya da özel makineler.
    'type'                => 'hosting',

    // Uzun bir API jetonu parola alanına değil erişim anahtarı alanına yazılır:
    // servers.password bir varchar'dır ve şifrelenmiş uzun jeton oraya sığmaz.
    'use-access-hash'     => true,
    'require-access-hash' => true,

    'use-test-connection' => true,
    'use-port'            => true,
    'not-secure-port'     => 2082,
    'secure-port'         => 2083,

    // Ek alanlar $server['fields'] içine düşer. 'crypt' değeri şifreli saklar.
    'fields' => [
        'region'    => ['type' => 'text', 'name' => '{lang.region}', 'col_class' => 'col-md-6'],
        'sub_token' => ['type' => 'password', 'name' => '{lang.sub_token}', 'crypt' => true],
    ],

    // Hesabı sağlayıcı tarafında hangi hizmet seçeneğinin tanımladığı.
    'service-relationship' => 'domain',
];
Parola zaten çözülmüş gelir

Taban sınıf $server['password'] değerini tek bir noktada çözer; define_server_info() metoduna her zaman düz metin ulaşır. Yeniden çözerseniz API bozulmuş bir sır alır.

Ürün Alanları ve Plan Açılır Listesi

product_configuration() bir alan tanım haritası döndürür. Yöneticinin seçtiği şey ürünün modül verisi olarak kaydedilir ve sağlama kodunuza $this->options['creation_info'] içinde ulaşır.

Seçenek değeri olarak planın adını yazın, sağlayıcının sayısal kimliğini değil; adı kimliğe create() içinde çevirin.

Yaşam Döngüsü Gövdesi

Her sağlama fiili aynı üç vuruşu izler: alıcının seçtiğini oku, sağlayıcıyı çağır, saklanacağı döndür. Kalıcılaştırmanın yolu dizi döndürmektir.

  1. Planı creation_info içinden, limitleri get_limit() ile, eklenti cevaplarını addon_params, sipariş formu cevaplarını requirement_params içinden okuyun.
  2. Sağlayıcıyı çağırın. API hatasında istemcinin fırlatmasına izin verin ya da çevrilmiş bir mesajla kendiniz fırlatın.
  3. ['config' => [...]] döndürün; çekirdek bunu hizmet seçeneklerine birleştirir ve sonraki her fiil hesabı oradan okur.

Referans

Yaşam Döngüsü İmzaları

Hiçbiri taban sınıfta soyut bildirilmez. Çekirdek her birini method_exists() ile yoklar ve olmayanı atlar; sözleşme imzanın kendisidir.

tam imzalar
// Hazırlık. define_server_info, diğer her fiilden önce set_server() içinden koşar.
protected function define_server_info(array $server = []): void;
public function test_connect(): array|bool;
public function configure_features(): void;

// Ürün ve hizmet formları. İki save metodu da değerleri REFERANSLA alır.
public function product_configuration(array $data = []): array;
public function save_product_configuration(array &$values): void;
public function service_configuration(): array;
public function save_service_configuration(array &$values): void;

// Sağlama.
public function create(): array|bool;
public function suspend(): bool;
public function unsuspend(): bool;
public function cancel(): bool;
public function renew(): bool;
public function upgrade(array $new_product = []): bool;
public function change_password(string $password): bool;
public function change_limits(array $limits): bool;
public function reset_limits(): array|bool;

// Eklentiler. $addon, hizmetin eklenti tablosundaki bir satırdır.
public function addon_create(array $addon = []): array|bool;
public function addon_suspend(array $addon = []): array|bool;
public function addon_unsuspend(array $addon = []): array|bool;
public function addon_cancel(array $addon = []): array|bool;
public function addon_upgrade(array $addon, array $new_addon): array|bool;

// Müşteri paneli.
public function dashboard_data(): array;
public function tool_data(string $tool, string $action = 'index', array $params = []): array;
public function tool_action(string $tool, string $action, array $data = []): array;
public function sso_panel_login(): string;
public function sso_root_panel_login(): string;

// Metrik faturalama ve içe aktarma.
public function metrics_usage(): array;
public function metrics_usage_bulk(array $services = []): array;
public function metric_enable(array $metric): void;
public function metric_disable(array $metric): void;
public function list(bool $rCount = false, array $filters = [], array $orders = [], int $start = 0, int $end = -1): array|int;
MetotZorunluNe zaman çağrılırSonraki durum
define_server_infoevetHer örneklemeyok
test_connectevetSunucu formunda Bağlantıyı Test Etyok
product_configurationevetÜrün detayı, Modül sekmesiyok
createevetSipariş onaylandığında ya da adminde Yeniden Oluşturactive
suspendevetVadesi geçen fatura ya da elle askıya almasuspended
unsuspendevetÖdeme alındığında ya da elle geri açmaactive
cancelevetİptal işlendiğindecancelled
change_passwordevetMüşteri ya da yönetici hesap parolasını değiştirdiğindedeğişmez
upgradeevetAynı modülde kalan plan değişikliğindedeğişmez
renewisteğe bağlıYenileme faturası ödendiğinde, vade zaten ilerlemişkendeğişmez
change_limits, reset_limitsisteğe bağlıYalnız sağlayıcı hesap başına limit ezmeye izin veriyorsadeğişmez
addon_*isteğe bağlıHizmetteki bir eklenti durum değiştirdiğindeyalnız eklenti
lististeğe bağlıYönetici Hesapları İçe Aktar ekranını açar; o ekranı görünür kılan şey bu metotturyok
metric_enable, metric_disableisteğe bağlıYalnız metrik faturalama artı sağlayıcı tarafı limit ezme varsayok

create() Ne Döndürebilir

Sade bir başarı için true, aksi halde bir dizi döndürün. Dizinin her anahtarı bir talimat sayılır; ayrılmış olanlar, üzerine yazmak yerine hizmet seçeneklerine özyinelemeli olarak birleştirilir.

config Hizmet seçeneklerinde config altına birleştirilir. Hesap kimliği burada yaşar: user, şifrelenmiş password, home_dir, sağlayıcı tarafındaki kimlik.
login login altına birleştirilir. Müşteri panelinin gösterdiği ya da tek oturum açmanın kullandığı kimlik bilgileri.
creation_info creation_info altına, yani ürün formunun yazdığı torbaya birleştirilir. Gerçekten neyin sağlandığını kaydetmek için kullanın.
options Seçenek köküne birleştirilir. Yukarıdaki dört anahtarın kapsamadığı her şey için kaçış kapısı.
status Kuyruk tarafından tüketilir, hiç saklanmaz. 'inprocess' ya da 'waiting', eşzamansız sağlama biterken hizmeti aktif durumdan uzak tutar.
diğer her anahtar Seçenek köküne olduğu gibi yazılır. hostname, ip ya da ftp_info iç içe koyulmadan böyle saklanır.

Elinizde Hazır Duran Durum

Hepsi metodunuz koşmadan önce, yapıcı ve set_service() tarafından doldurulur. Hiçbiri için sorgu atmayın.

$this->server Sunucu satırı: ip, hostname, kullanıcı adı, çözülmüş parola, erişim anahtarı ve yapılandırmanızdan gelen fields.
$this->service, $this->product, $this->user, $this->order Hizmet satırı, ürünü, satın alan müşteri ve kaynak sipariş; hepsi düz dizi olarak.
$this->options ve save_options() Hizmet seçenekleri canlı bir dizi olarak. Değiştirin, sonra metot ortasında kalıcılaştırmak için $this->save_options() çağırın; sonda dizi döndürmek de aynı işi yapar.
get_limit(string $key): mixed Çözümlenmiş tek bir kaynak limiti; hizmet seviyesi ürün seviyesini ezer. Anahtarlar: disk_limit, bandwidth_limit, email_limit, database_limit, addons_limit, subdomain_limit, ftp_limit, park_limit, max_email_per_hour.
$this->addon_params, $this->addon_params_by_id Aktif eklentilerin birleşik toplamları ve aynı değerlerin eklenti satırı kimliğine göre ayrılmış hali. Anahtarlar addon-params altında bildirilenlerdir.
$this->requirement_params Alıcının sipariş formunda verdiği cevaplar; requirement-params altında bildirilen adlarla anahtarlanır.
encode_str(), decode_str() Bir sırrı seçeneklere yazmadan önce şifreleyin, sağlayıcıya göndermeden önce çözün. Panel parolası hiçbir zaman açık saklanmaz.
username_generator(string|int|null $domain): string Statik. Alan adından panele uygun bir kullanıcı adı türetir. Boş alan adı boş kullanıcı adı verir, sağlayıcı da onu reddeder.

Çağrının Çekirdek Tarafı

imza
public static function run_module(int|array $service, string $action, array $params = []): mixed;
public static function instance_module(int|array $service): ?object;
çekirdekten gerçek çağrılar
Services::run_module($id, 'create');                       // argüman yok
Services::run_module($id, 'change_password', [$password]); // tek konumsal dize
Services::run_module($service, 'upgrade', [$newProduct]);  // yeni ürün dizisi
Services::run_module($service, 'addon_create', [$addon]);  // tek eklenti satırı
null döner Sınıfınızda böyle bir metot yok. Kuyruk "modül metodu bulunamadı" kaydı düşer ve aksiyon sessizce başarılı sayılmak yerine başarısız olur.
false döner Modül reddetti. Kuyruk öğeyi başarısız işaretler ve yeniden dener.
terminate, cancel'a çözülür terminate() yoksa çekirdek önce cancel(), sonra cancelled() dener. cancel() yazarsanız iki yol da çalışır.
gate:service.module_action Metodunuzdan önce çalışır. Boş olmayan bir dize döndüren dinleyici aksiyonu o mesajla veto eder.
filter:service.module_result Sonuç uygulanmadan önce referansla çalışır; dinleyici neyin saklanacağını yeniden yazabilir.
action:service.module_ran Sonuç uygulandıktan sonra çalışır: hizmet, örnek, aksiyon, sonuç ve hata ile. Dönüş kullanılmaz.

Örnek

Eksiksiz bir sağlama metodu ve hemen ardından dönüş değerini geri okuyan çekirdek kodu. İki yarı birbirine aittir: döndürdüğünüz anahtarlar çekirdeğin birleştirdiği anahtarlardır ve sonraki fiillerinizin okuduğu her şey aynı torbadan çıkar.

modül tarafı
public function create(): array|bool
{
    $domain = $this->options['domain'] ?? '';
    if (!$domain) throw new Exception($this->lang['error-domain-required']);

    // Yeniden sağlama: yeni bir kimlik üretmek yerine önceki koşudakini yeniden kullanın.
    $username = $this->options['config']['user'] ?? '';
    if (!$username) $username = self::username_generator($domain);

    $password = ($this->options['config']['password'] ?? '') !== ''
        ? $this->decode_str($this->options['config']['password'])
        : Utility::generate_hash(12);

    $creation = $this->options['creation_info'] ?? [];

    $parameters = [
        'username'  => $username,
        'password'  => $password,
        'domain'    => $domain,

        // Seçenek değerleri plan ADINI taşır, bu yüzden onu bu sunucuda çözün.
        'plan_id'   => $this->resolve_plan_id($creation['plan'] ?? ''),

        'disk'      => $this->get_limit('disk_limit'),
        'bandwidth' => $this->get_limit('bandwidth_limit'),

        // Anahtar alanı "0" ya da "1" dizesi gönderir; empty("0") true'dur, onun yerine cast edin.
        'shell'     => (int) ($creation['shell_access'] ?? 0) === 1,
    ];

    // Sipariş formu cevapları ve eklenti toplamları; config.php'de bildirilen adlarla anahtarlanır.
    foreach ($this->requirement_params as $key => $value) $parameters[$key] = $value;
    foreach ($this->addon_params as $key => $value) $parameters[$key] = $value;

    // Yeniden deneme güvenliği: kuyruk bu metodu bir hatadan sonra tekrar koşar.
    if (!$this->api->account_exists($username))
        $this->api->call('accounts', $parameters, 'POST');

    return [
        'config' => [
            'user'     => $username,
            'password' => $this->encode_str($password),
            'home_dir' => '/home/' . $username,
        ],
        // Ayrılmış bir anahtar değil, bu yüzden seçenek köküne yazılır.
        'ip' => $this->server['ip'] ?? '',
    ];
}
çekirdeğin geri okuması
// Services::apply_module_result, sizi ilgilendiren kısma indirgenmiş hali.
if (is_array($result)) {
    if (isset($result['config']) && is_array($result['config']))
        $options['config'] = array_replace_recursive($options['config'] ?? [], $result['config']);

    // Ayrılmış kümenin dışında kalan her şey seçenek köküne düşer.
    $reserved = ['config', 'login', 'creation_info', 'options', 'status'];
    foreach ($result as $rKey => $rVal)
        if (!in_array($rKey, $reserved, true)) $options[$rKey] = $rVal;
}

// Sonra ModuleQueue yeni hizmet durumuna karar verir.
$moduleStatus = is_array($result) ? ($result['status'] ?? null) : null;
$targetStatus = $moduleStatus ?: match ($action) {
    'create', 'unsuspend', 'register' => 'active',
    'suspend'                          => 'suspended',
    'cancel'                           => 'cancelled',
    default                            => null,
};

// Ve sonraki her fiilin okuduğu şey budur:
$username = $this->options['config']['user'] ?? '';
$password = $this->decode_str($this->options['config']['password'] ?? '');

Tuzaklar

Hatayı fırlatarak bildirin, hata dizesiyle false döndürerek değil

Hata özelliğine atama yapıp false döndürmek önceki neslin kalıntısıdır. cPanel 41 yerde fırlatıyor, hiçbir yerde atamıyor. Çevrilmiş bir exception fırlatın; çağıran taraf mesajı gösterir.

Kuyruk create() metodunu iki kez koşacak

Hesap oluştuktan sonraki bir hata baştan yeniden denenir ve kopyayı reddeden bir sağlayıcı bundan sonra hep başarısız olur. Önce hesabı denetleyin ve kimlik bilgilerini elinize geçer geçmez save_options() ile yazın ki ikinci koşu onları bulabilsin.

Plan adını saklayın, sağlayıcı kimliğini değil

Yük dengeli bir sunucu grubunda sipariş yeri olan makineye düşer ve aynı plan orada farklı bir sayısal kimlik taşır. Sabit olan addır: adı kaydedin, kimliğe çağrı anında çevirin.

Anahtar değerini asla empty() ile sınamayın

Onay ve anahtar alanları "0" ve "1" dizelerini gönderir; empty("0") ise true'dur. (int) ($data['key'] ?? 0) === 1 yazın; bu, o biçimlerin hepsinde aynı davranır.

Sunucu parolasını kendiniz çözmeyin

Taban sınıf bunu tek bir yerde zaten yaptı; böylece bağlantı testi akışı ile sağlama akışı aynı davranır. İkinci bir çözme düz metin yolunu bozar ve hata ancak gerçek bir sağlayıcıya karşı ortaya çıkar.

Faydalı oldu mu?

Geri bildiriminiz için teşekkürler!

Hâlâ Yardıma mı İhtiyacınız Var?

Yukarıda bulamadığınız her şey için destek ekibimiz her zaman yanınızda.