feat: initial WooAApanel plugin

Full WooCommerce plugin for aaPanel hosting management:
- aaPanel API client (all website + database endpoints)
- Admin: server management, site/DB assignments, full site/DB management panels
- Customer My Account: web hosting tab with sites and databases
- WooDomains PowerDNS integration for DNS management
- WooCommerce order auto-provisioning (product → server linking)
- Permission model: admin has all actions, customers have scoped access

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 12:48:06 +01:00
commit 8bb96b9048
10 changed files with 3458 additions and 0 deletions

View File

@@ -0,0 +1,399 @@
<?php
/**
* aaPanel API client.
*
* Authentication: every request appends request_time and request_token
* as query-string parameters.
* request_token = md5( time + md5( api_key ) )
*
* All endpoints use POST with multipart/form-data body.
*/
defined( 'ABSPATH' ) || exit;
class WooAApanel_API {
private string $base_url;
private string $api_key;
private int $timeout = 30;
public function __construct( string $url, string $api_key ) {
$this->base_url = rtrim( $url, '/' );
$this->api_key = $api_key;
}
// ── Auth helpers ─────────────────────────────────────────────────────────
private function auth_params(): array {
$now = time();
return [
'request_time' => $now,
'request_token' => md5( $now . md5( $this->api_key ) ),
];
}
// ── Core HTTP ────────────────────────────────────────────────────────────
/**
* POST to an aaPanel v2 endpoint.
*
* @param string $path e.g. '/v2/site?action=AddSite'
* @param array $body POST body fields (multipart)
*/
public function post( string $path, array $body = [] ): array {
$auth = $this->auth_params();
// Auth params go on the query string.
$url = $this->base_url . $path
. ( str_contains( $path, '?' ) ? '&' : '?' )
. http_build_query( $auth );
$response = wp_remote_post( $url, [
'timeout' => $this->timeout,
'sslverify' => apply_filters( 'wooaapanel_sslverify', false ),
'body' => $body,
] );
if ( is_wp_error( $response ) ) {
return [ 'success' => false, 'error' => $response->get_error_message() ];
}
$code = wp_remote_retrieve_response_code( $response );
$raw = wp_remote_retrieve_body( $response );
$data = json_decode( $raw, true );
return [
'success' => ( $code >= 200 && $code < 300 ),
'data' => $data,
'code' => $code,
];
}
// ── Connection test ───────────────────────────────────────────────────────
public function test_connection(): array {
return $this->post( '/v2/panel/public/get_soft_status', [ 'name' => 'nginx' ] );
}
// ═══════════════════════════════════════════════════════════════════════════
// WEBSITE / PHP PROJECT
// ═══════════════════════════════════════════════════════════════════════════
// ── Site listing ─────────────────────────────────────────────────────────
public function get_sites( int $page = 1, int $limit = 50, string $search = '' ): array {
return $this->post( '/v2/data?action=getData', [
'p' => $page,
'limit' => $limit,
'table' => 'sites',
'search' => $search,
'type' => -1,
] );
}
public function get_site_types(): array {
return $this->post( '/v2/site?action=get_site_types', [] );
}
// ── Site CRUD ────────────────────────────────────────────────────────────
public function add_site( array $data ): array {
return $this->post( '/v2/site?action=AddSite', $data );
}
public function check_delete_site( string $site_name ): array {
return $this->post( '/v2/site?action=check_del_data', [ 'name' => $site_name ] );
}
public function delete_site( string $site_name, bool $ftp = false, bool $database = false, bool $path = false ): array {
return $this->post( '/v2/site?action=DeleteSite', [
'webname' => $site_name,
'ftp' => $ftp ? 1 : 0,
'database' => $database ? 1 : 0,
'path' => $path ? 1 : 0,
] );
}
// ── Domains ──────────────────────────────────────────────────────────────
public function get_site_domains( string $site_name ): array {
return $this->post( '/v2/data?action=getData&table=domain', [
'search' => $site_name,
'limit' => 100,
'p' => 1,
] );
}
public function add_domain( string $site_name, string $domain ): array {
return $this->post( '/v2/site?action=AddDomain', [
'id' => 0, // will be resolved by panel
'webname' => $site_name,
'domain' => $domain,
] );
}
public function delete_domain( string $site_name, string $domain, int $site_id = 0 ): array {
return $this->post( '/v2/site?action=DelDomain', [
'id' => $site_id,
'webname' => $site_name,
'domain' => $domain,
'port' => 80,
] );
}
// ── XSS protection ───────────────────────────────────────────────────────
public function get_xss( string $site_name, string $site_path ): array {
return $this->post( '/v2/site?action=GetDirUserINI', [
'id' => 0,
'name' => $site_name,
'path' => $site_path,
] );
}
public function set_xss( string $site_name, string $site_path, bool $enable ): array {
return $this->post( '/v2/site?action=SetDirUserINI', [
'id' => 0,
'name' => $site_name,
'path' => $site_path,
'action' => $enable ? 'set' : 'close',
] );
}
// ── Site root ────────────────────────────────────────────────────────────
public function get_site_root( string $site_name ): array {
return $this->post( '/v2/data?action=getKey', [
'name' => $site_name,
'key' => 'path',
] );
}
public function set_run_path( string $site_name, string $run_path ): array {
return $this->post( '/v2/site?action=SetSiteRunPath', [
'id' => 0,
'runPath' => $run_path,
'siteName' => $site_name,
] );
}
// ── PHP version ──────────────────────────────────────────────────────────
public function get_site_php_version( string $site_name ): array {
return $this->post( '/v2/site?action=GetSitePHPVersion', [ 'siteName' => $site_name ] );
}
public function get_php_versions(): array {
return $this->post( '/v2/site?action=GetPHPVersion', [] );
}
public function set_site_php_version( string $site_name, string $version ): array {
return $this->post( '/v2/site?action=SetPHPVersion', [
'siteName' => $site_name,
'version' => $version,
] );
}
// ── URL rewrite ──────────────────────────────────────────────────────────
public function get_rewrite_list( string $site_name ): array {
return $this->post( '/v2/site?action=GetRewriteList', [ 'siteName' => $site_name ] );
}
public function get_rewrite_content( string $site_name, string $template ): array {
return $this->post( '/v2/files?action=GetFileBody', [
'path' => "/www/server/panel/vhost/rewrite/{$site_name}.conf",
] );
}
public function set_rewrite( string $site_name, string $content ): array {
return $this->post( '/v2/files?action=SaveFileBody', [
'path' => "/www/server/panel/vhost/rewrite/{$site_name}.conf",
'data' => $content,
'encoding' => 'utf-8',
] );
}
// ── SSL ──────────────────────────────────────────────────────────────────
public function get_ssl( string $site_name ): array {
return $this->post( '/v2/site?action=GetSSL', [ 'siteName' => $site_name ] );
}
public function close_ssl( string $site_name ): array {
return $this->post( '/v2/site?action=CloseSSLConf', [ 'updateOf' => 1, 'siteName' => $site_name ] );
}
public function list_ssl_certs(): array {
return $this->post( '/v2/ssl_domain?action=list_ssl_info', [] );
}
public function upload_cert( string $key, string $cert, string $domain ): array {
return $this->post( '/v2/ssl_domain?action=upload_cert', [
'privateKey' => $key,
'certPem' => $cert,
'bindDomain' => $domain,
] );
}
public function deploy_cert_to_site( string $site_name, int $cert_id ): array {
return $this->post( '/v2/ssl_domain?action=cert_deploy_sites', [
'siteName' => $site_name,
'certId' => $cert_id,
] );
}
// ── Server info ──────────────────────────────────────────────────────────
public function get_server_info(): array {
return $this->post( '/v2/panel/public/get_soft_status', [ 'name' => 'nginx' ] );
}
// ═══════════════════════════════════════════════════════════════════════════
// DATABASES / MYSQL
// ═══════════════════════════════════════════════════════════════════════════
// ── Listing ──────────────────────────────────────────────────────────────
public function get_databases( int $page = 1, int $limit = 50, string $search = '' ): array {
return $this->post( '/v2/data?action=getData&table=databases&type=MySQL', [
'p' => $page,
'limit' => $limit,
'search' => $search,
'type' => 'MySQL',
] );
}
public function get_db_info( string $db_name ): array {
return $this->post( '/v2/database?action=GetInfo', [ 'name' => $db_name ] );
}
// ── Add / Delete ─────────────────────────────────────────────────────────
public function add_database( string $db_name, string $db_user, string $password, string $codeing = 'utf8mb4' ): array {
return $this->post( '/v2/database?action=AddDatabase', [
'name' => $db_name,
'db_user' => $db_user,
'password' => $password,
'codeing' => $codeing,
'address' => '%',
'accept' => '%',
] );
}
public function check_delete_db( string $db_name ): array {
return $this->post( '/v2/database?action=check_del_data', [ 'name' => $db_name ] );
}
public function delete_database( string $db_name ): array {
return $this->post( '/v2/database?action=DeleteDatabase', [ 'name' => $db_name ] );
}
// ── Backup ───────────────────────────────────────────────────────────────
public function backup_database( string $db_name ): array {
return $this->post( '/v2/database?action=ToBackup', [ 'name' => $db_name ] );
}
public function get_db_backups( string $db_name ): array {
return $this->post( '/v2/data?action=getData', [
'p' => 1,
'limit' => 100,
'table' => 'backup',
'search' => $db_name,
'type' => 'database',
] );
}
public function delete_db_backup( int $backup_id, string $db_name ): array {
return $this->post( '/v2/database?action=DelBackup', [
'id' => $backup_id,
'name' => $db_name,
] );
}
public function get_recycle_bin(): array {
return $this->post( '/v2/files?action=Get_Recycle_bin', [ 'type' => 'database' ] );
}
public function restore_from_recycle( string $path ): array {
return $this->post( '/v2/files?action=Re_Recycle_bin', [ 'path' => $path ] );
}
// ── Optimization ─────────────────────────────────────────────────────────
public function optimize_table( string $db_name ): array {
return $this->post( '/v2/database?action=OpTable', [ 'name' => $db_name ] );
}
public function repair_table( string $db_name ): array {
return $this->post( '/v2/database?action=ReTable', [ 'name' => $db_name ] );
}
// ── Credentials / Access ─────────────────────────────────────────────────
public function get_db_access( string $db_name ): array {
return $this->post( '/v2/database?action=GetDatabaseAccess', [ 'name' => $db_name ] );
}
public function set_db_access( string $db_name, string $access ): array {
return $this->post( '/v2/database?action=SetDatabaseAccess', [
'name' => $db_name,
'access' => $access,
] );
}
public function reset_db_password( string $db_name, string $db_user, string $password ): array {
return $this->post( '/v2/database?action=ResDatabasePassword', [
'name' => $db_name,
'db_user' => $db_user,
'password' => $password,
] );
}
public function get_mysql_root_password(): array {
return $this->post( '/v2/data?action=getKey', [ 'name' => 'mysql' ] );
}
public function set_mysql_root_password( string $password ): array {
return $this->post( '/v2/database?action=SetupPassword', [ 'password' => $password ] );
}
// ── Sync ─────────────────────────────────────────────────────────────────
public function sync_to_databases( array $ids = [] ): array {
return $this->post( '/v2/database?action=SyncToDatabases&type=0', [
'ids' => wp_json_encode( $ids ),
] );
}
public function sync_get_databases(): array {
return $this->post( '/v2/database?action=SyncGetDatabases', [] );
}
// ── Import SQL ───────────────────────────────────────────────────────────
public function import_sql( string $db_name, string $sql_path ): array {
return $this->post( '/v2/database?action=InputSql', [
'name' => $db_name,
'file' => $sql_path,
] );
}
// ── Quota ────────────────────────────────────────────────────────────────
public function modify_db_quota( string $db_name, int $quota_mb ): array {
return $this->post( '/v2/project/quota/modify_database_quota', [
'name' => $db_name,
'quota' => $quota_mb,
] );
}
// ── Static factory ───────────────────────────────────────────────────────
public static function from_server( object $server ): self {
return new self( $server->url, $server->api_key );
}
public function get_base_url(): string {
return $this->base_url;
}
}