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:
84
assets/css/wooaapanel-account.css
Normal file
84
assets/css/wooaapanel-account.css
Normal file
@@ -0,0 +1,84 @@
|
||||
/* WooAApanel Account (My Account) Styles */
|
||||
|
||||
#wooaapanel-account { margin-top: 16px; }
|
||||
|
||||
/* ── Panels ───────────────────────────────────────────────────── */
|
||||
.wap-site-panel, .wap-db-panel {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wap-panel-header {
|
||||
background: #f8f9fa;
|
||||
padding: 14px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.wap-site-name, .wap-db-name { font-weight: 700; font-size: 15px; }
|
||||
.wap-domain { color: #0073aa; }
|
||||
.wap-server-label { color: #888; font-size: 12px; margin-left: auto; }
|
||||
|
||||
/* ── Action buttons ──────────────────────────────────────────── */
|
||||
.wap-panel-actions { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.wap-btn-sm { font-size: 12px !important; padding: 4px 10px !important; height: auto !important; line-height: 1.5 !important; }
|
||||
|
||||
/* ── Sections ────────────────────────────────────────────────── */
|
||||
.wap-section { padding: 16px; border-top: 1px solid #f0f0f0; }
|
||||
.wap-section h4, .wap-section h5 { margin-top: 0; margin-bottom: 10px; }
|
||||
|
||||
/* ── Tables inside sections ──────────────────────────────────── */
|
||||
.wap-section table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
||||
.wap-section table th,
|
||||
.wap-section table td { padding: 6px 10px; border-bottom: 1px solid #f0f0f0; text-align: left; }
|
||||
.wap-section table th { background: #f8f8f8; font-weight: 600; }
|
||||
|
||||
/* ── Input rows ───────────────────────────────────────────────── */
|
||||
.wap-section input[type="text"],
|
||||
.wap-section input[type="password"],
|
||||
.wap-section select {
|
||||
padding: 6px 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 3px;
|
||||
font-size: 13px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.wap-section textarea {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* ── SSL info ─────────────────────────────────────────────────── */
|
||||
.wap-ssl-info p { margin: 4px 0; }
|
||||
.wap-ssl-info .wap-ssl-valid { color: #155724; font-weight: 600; }
|
||||
.wap-ssl-info .wap-ssl-invalid { color: #721c24; font-weight: 600; }
|
||||
|
||||
/* ── DNS ──────────────────────────────────────────────────────── */
|
||||
.wap-dns-add-form label { display: block; margin-bottom: 10px; }
|
||||
.wap-dns-add-form textarea { height: 70px; }
|
||||
|
||||
/* ── Notices ──────────────────────────────────────────────────── */
|
||||
.wap-notice-inline {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
.wap-notice-inline.ok { background: #d4edda; color: #155724; }
|
||||
.wap-notice-inline.error { background: #f8d7da; color: #721c24; }
|
||||
|
||||
/* ── DB action notice ─────────────────────────────────────────── */
|
||||
.wap-db-action-notice { padding: 0 16px; }
|
||||
.wap-db-action-notice.ok { background: #d4edda; color: #155724; padding: 8px 16px; }
|
||||
.wap-db-action-notice.error { background: #f8d7da; color: #721c24; padding: 8px 16px; }
|
||||
94
assets/css/wooaapanel-admin.css
Normal file
94
assets/css/wooaapanel-admin.css
Normal file
@@ -0,0 +1,94 @@
|
||||
/* WooAApanel Admin Styles */
|
||||
|
||||
.wooaapanel-page { max-width: 1200px; }
|
||||
|
||||
/* ── Cards / tables ─────────────────────────────────────────── */
|
||||
.wap-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.wap-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
}
|
||||
.wap-table th, .wap-table td {
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
.wap-table th { background: #f8f8f8; font-weight: 600; }
|
||||
.wap-table tr:hover td { background: #fafafa; }
|
||||
|
||||
/* ── Forms ───────────────────────────────────────────────────── */
|
||||
.wap-form { max-width: 600px; }
|
||||
.wap-form label { display: block; margin-bottom: 12px; }
|
||||
.wap-form label span { display: block; font-weight: 600; margin-bottom: 4px; font-size: 12px; }
|
||||
.wap-form input[type="text"],
|
||||
.wap-form input[type="url"],
|
||||
.wap-form input[type="password"],
|
||||
.wap-form select,
|
||||
.wap-form textarea {
|
||||
width: 100%;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 3px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.wap-form textarea { min-height: 100px; font-family: monospace; }
|
||||
|
||||
/* ── Toolbar ─────────────────────────────────────────────────── */
|
||||
.wap-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.wap-toolbar select, .wap-toolbar input { max-width: 200px; }
|
||||
|
||||
/* ── Status / badges ─────────────────────────────────────────── */
|
||||
.wap-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.wap-badge-active { background: #d4edda; color: #155724; }
|
||||
.wap-badge-inactive{ background: #f8d7da; color: #721c24; }
|
||||
|
||||
/* ── Action buttons ──────────────────────────────────────────── */
|
||||
.wap-action-btn { cursor: pointer; }
|
||||
.wap-action-btn + .wap-action-btn { margin-left: 4px; }
|
||||
|
||||
/* ── Inline edit form ────────────────────────────────────────── */
|
||||
.wap-inline-form { background: #f8f9fa; border: 1px solid #e0e0e0; border-radius: 4px; padding: 16px; margin-top: 16px; }
|
||||
.wap-inline-form h4 { margin-top: 0; }
|
||||
|
||||
/* ── Notices ─────────────────────────────────────────────────── */
|
||||
.wap-notice { padding: 8px 12px; border-radius: 3px; margin-top: 8px; display: none; }
|
||||
.wap-notice.success { background: #d4edda; color: #155724; display: block; }
|
||||
.wap-notice.error { background: #f8d7da; color: #721c24; display: block; }
|
||||
|
||||
/* ── Server selector ──────────────────────────────────────────── */
|
||||
.wap-server-picker { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; }
|
||||
.wap-server-picker label { font-weight: 600; margin: 0; }
|
||||
|
||||
/* ── Detail panels ───────────────────────────────────────────── */
|
||||
.wap-detail-panel { margin-top: 8px; padding: 12px; background: #fafafa; border: 1px solid #e8e8e8; border-radius: 3px; }
|
||||
.wap-detail-panel pre { margin: 0; white-space: pre-wrap; word-break: break-all; font-size: 12px; }
|
||||
|
||||
/* ── Tabs ─────────────────────────────────────────────────────── */
|
||||
.wap-tabs { display: flex; gap: 0; border-bottom: 2px solid #0073aa; margin-bottom: 16px; }
|
||||
.wap-tab { padding: 8px 16px; cursor: pointer; border: 1px solid transparent; border-bottom: none; font-size: 13px; border-radius: 3px 3px 0 0; }
|
||||
.wap-tab.active { background: #0073aa; color: #fff; }
|
||||
.wap-tab:hover:not(.active) { background: #e8f4fc; }
|
||||
|
||||
.wap-tab-content { display: none; }
|
||||
.wap-tab-content.active { display: block; }
|
||||
389
assets/js/wooaapanel-account.js
Normal file
389
assets/js/wooaapanel-account.js
Normal file
@@ -0,0 +1,389 @@
|
||||
/**
|
||||
* WooAApanel Account JS
|
||||
*
|
||||
* Customer My Account "Web Hosting" tab.
|
||||
* Handles site and database panels.
|
||||
* Integrates WooDomains PowerDNS for DNS management if active.
|
||||
*/
|
||||
(function ($) {
|
||||
'use strict';
|
||||
|
||||
var cfg = wooaapanelAcct;
|
||||
var ajaxUrl = cfg.ajax_url;
|
||||
var nonce = cfg.nonce;
|
||||
var pdnsActive = parseInt(cfg.pdns_active, 10) === 1;
|
||||
var pdnsNonce = cfg.pdns_nonce;
|
||||
|
||||
/* ── Utility ──────────────────────────────────────────────── */
|
||||
|
||||
function post(action, data, cb) {
|
||||
data = $.extend({ action: action, nonce: nonce }, data);
|
||||
$.post(ajaxUrl, data, cb).fail(function () {
|
||||
setNotice('#wap-acct-notices', 'Request failed. Please reload and try again.', 'error');
|
||||
});
|
||||
}
|
||||
|
||||
function pdnsPost(action, data, cb) {
|
||||
data = $.extend({ action: action, nonce: pdnsNonce }, data);
|
||||
$.post(ajaxUrl, data, cb);
|
||||
}
|
||||
|
||||
function setNotice(selector, msg, type) {
|
||||
var $el = $(selector);
|
||||
$el.attr('class', 'woocommerce-' + (type === 'error' ? 'error' : 'message') + ' woocommerce-info')
|
||||
.text(msg).show();
|
||||
setTimeout(function () { $el.fadeOut(); }, 5000);
|
||||
}
|
||||
|
||||
function inlineNotice($el, msg, type) {
|
||||
$el.text(msg).attr('class', 'wap-notice-inline ' + (type === 'error' ? 'error' : 'ok'));
|
||||
setTimeout(function () { $el.text('').removeClass('ok error'); }, 4000);
|
||||
}
|
||||
|
||||
function escHtml(s) {
|
||||
return String(s)
|
||||
.replace(/&/g,'&').replace(/</g,'<')
|
||||
.replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
|
||||
function siteOpts($panel) {
|
||||
return {
|
||||
server_id: $panel.data('server-id'),
|
||||
site_name: $panel.data('site-name')
|
||||
};
|
||||
}
|
||||
|
||||
function dbOpts($panel) {
|
||||
return {
|
||||
server_id: $panel.data('server-id'),
|
||||
db_name: $panel.data('db-name')
|
||||
};
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════
|
||||
SITE PANELS
|
||||
══════════════════════════════════════════════════════════ */
|
||||
|
||||
/* ── Domains ──────────────────────────────────────────────── */
|
||||
|
||||
$(document).on('click', '.wap-load-domains', function () {
|
||||
var $panel = $(this).closest('.wap-site-panel');
|
||||
var $sec = $panel.find('.wap-domains-section');
|
||||
$sec.toggle();
|
||||
if ($sec.is(':visible')) {
|
||||
loadDomains($panel);
|
||||
}
|
||||
});
|
||||
|
||||
function loadDomains($panel) {
|
||||
var opts = siteOpts($panel);
|
||||
var $list = $panel.find('.wap-domains-list').html('<em>Loading…</em>');
|
||||
post('wooaapanel_acct_site_domains', opts, function (res) {
|
||||
var domains = res.data && res.data.data ? res.data.data : [];
|
||||
if (!domains.length) { $list.html('<p>No extra domains.</p>'); return; }
|
||||
var html = '<table class="woocommerce-table"><thead><tr><th>Domain</th><th>Port</th><th></th></tr></thead><tbody>';
|
||||
$.each(domains, function (i, d) {
|
||||
html += '<tr><td>' + escHtml(d.name || d.domain || '') + '</td><td>' + (d.port || '') + '</td>'
|
||||
+ '<td><button class="button wap-del-domain wap-btn-sm" data-domain="' + escHtml(d.name || d.domain || '') + '" data-id="' + (d.id || 0) + '">Remove</button></td></tr>';
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$list.html(html);
|
||||
|
||||
$list.find('.wap-del-domain').on('click', function () {
|
||||
if (!confirm('Remove this domain?')) return;
|
||||
post('wooaapanel_acct_site_del_domain', $.extend({}, opts, {
|
||||
domain: $(this).data('domain'),
|
||||
site_id: $(this).data('id')
|
||||
}), function () { loadDomains($panel); });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('click', '.wap-add-domain-btn', function () {
|
||||
var $panel = $(this).closest('.wap-site-panel');
|
||||
var domain = $panel.find('.wap-new-domain').val();
|
||||
var $notice = $panel.find('.wap-domain-notice');
|
||||
if (!domain) { inlineNotice($notice, 'Enter a domain name.', 'error'); return; }
|
||||
post('wooaapanel_acct_site_add_domain', $.extend({}, siteOpts($panel), { domain: domain }), function (res) {
|
||||
inlineNotice($notice, res.success ? 'Domain added!' : (res.data || 'Error.'), res.success ? 'ok' : 'error');
|
||||
if (res.success) { $panel.find('.wap-new-domain').val(''); loadDomains($panel); }
|
||||
});
|
||||
});
|
||||
|
||||
/* ── PHP Version ──────────────────────────────────────────── */
|
||||
|
||||
$(document).on('click', '.wap-load-php', function () {
|
||||
var $panel = $(this).closest('.wap-site-panel');
|
||||
var $sec = $panel.find('.wap-php-section');
|
||||
$sec.toggle();
|
||||
if ($sec.is(':visible') && !$sec.data('loaded')) {
|
||||
$sec.data('loaded', 1);
|
||||
loadPhpSection($panel, $sec);
|
||||
}
|
||||
});
|
||||
|
||||
function loadPhpSection($panel, $sec) {
|
||||
var opts = siteOpts($panel);
|
||||
$sec.find('.wap-php-current').text('Loading…');
|
||||
post('wooaapanel_acct_site_php_get', opts, function (res) {
|
||||
var current = res.data && res.data.version ? res.data.version : '?';
|
||||
$sec.find('.wap-php-current').html('Current PHP version: <strong>' + escHtml(current) + '</strong>');
|
||||
post('wooaapanel_acct_site_php_versions', opts, function (res2) {
|
||||
var versions = res2.data && res2.data.version ? res2.data.version : [];
|
||||
var opts2 = '';
|
||||
$.each(versions, function (i, v) {
|
||||
var ver = v.version || v;
|
||||
opts2 += '<option value="' + escHtml(ver) + '"' + (ver == current ? ' selected' : '') + '>' + escHtml(ver) + '</option>';
|
||||
});
|
||||
$sec.find('.wap-php-select').html(opts2);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('click', '.wap-php-set-btn', function () {
|
||||
var $panel = $(this).closest('.wap-site-panel');
|
||||
var $sec = $panel.find('.wap-php-section');
|
||||
var $notice = $sec.find('.wap-php-notice');
|
||||
var version = $sec.find('.wap-php-select').val();
|
||||
post('wooaapanel_acct_site_php_set', $.extend({}, siteOpts($panel), { version: version }), function (res) {
|
||||
inlineNotice($notice, res.success ? 'PHP version updated!' : (res.data || 'Error.'), res.success ? 'ok' : 'error');
|
||||
if (res.success) {
|
||||
$sec.data('loaded', 0);
|
||||
loadPhpSection($panel, $sec);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* ── URL Rewrite ──────────────────────────────────────────── */
|
||||
|
||||
$(document).on('click', '.wap-load-rewrite', function () {
|
||||
var $panel = $(this).closest('.wap-site-panel');
|
||||
var $sec = $panel.find('.wap-rewrite-section');
|
||||
$sec.toggle();
|
||||
if ($sec.is(':visible') && !$sec.data('loaded')) {
|
||||
$sec.data('loaded', 1);
|
||||
post('wooaapanel_acct_site_rewrite_get', siteOpts($panel), function (res) {
|
||||
var content = res.data && res.data.data ? res.data.data : (res.data || '');
|
||||
$sec.find('.wap-rewrite-content').val(content);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '.wap-rewrite-save-btn', function () {
|
||||
var $panel = $(this).closest('.wap-site-panel');
|
||||
var $sec = $panel.find('.wap-rewrite-section');
|
||||
var $notice = $sec.find('.wap-rewrite-notice');
|
||||
var content = $sec.find('.wap-rewrite-content').val();
|
||||
post('wooaapanel_acct_site_rewrite_set', $.extend({}, siteOpts($panel), { content: content }), function (res) {
|
||||
inlineNotice($notice, res.success ? 'Saved!' : (res.data || 'Error.'), res.success ? 'ok' : 'error');
|
||||
});
|
||||
});
|
||||
|
||||
/* ── SSL ──────────────────────────────────────────────────── */
|
||||
|
||||
$(document).on('click', '.wap-load-ssl', function () {
|
||||
var $panel = $(this).closest('.wap-site-panel');
|
||||
var $sec = $panel.find('.wap-ssl-section');
|
||||
$sec.toggle();
|
||||
if ($sec.is(':visible') && !$sec.data('loaded')) {
|
||||
$sec.data('loaded', 1);
|
||||
post('wooaapanel_acct_site_ssl_get', siteOpts($panel), function (res) {
|
||||
var d = res.data || {};
|
||||
var html = '';
|
||||
if (d.status !== undefined) {
|
||||
var valid = d.status == 1 || d.status === true;
|
||||
html += '<p>Status: <span class="' + (valid ? 'wap-ssl-valid' : 'wap-ssl-invalid') + '">' + (valid ? 'Active' : 'Inactive') + '</span></p>';
|
||||
}
|
||||
if (d.notAfter) html += '<p>Expires: ' + escHtml(d.notAfter) + '</p>';
|
||||
if (d.issuer) html += '<p>Issuer: ' + escHtml(d.issuer) + '</p>';
|
||||
if (d.subject) html += '<p>Domain: ' + escHtml(d.subject) + '</p>';
|
||||
if (!html) html = '<pre>' + escHtml(JSON.stringify(d, null, 2)) + '</pre>';
|
||||
$sec.find('.wap-ssl-info').html(html);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* ── Server Info ──────────────────────────────────────────── */
|
||||
|
||||
$(document).on('click', '.wap-load-server-info', function () {
|
||||
var $panel = $(this).closest('.wap-site-panel');
|
||||
var $sec = $panel.find('.wap-server-section');
|
||||
$sec.toggle();
|
||||
if ($sec.is(':visible') && !$sec.data('loaded')) {
|
||||
$sec.data('loaded', 1);
|
||||
post('wooaapanel_acct_server_info', siteOpts($panel), function (res) {
|
||||
if (!res.success) { $sec.find('.wap-server-info').text(res.data || 'Error.'); return; }
|
||||
var d = res.data;
|
||||
var html = '<table class="woocommerce-table">'
|
||||
+ '<tr><th>Server Name</th><td>' + escHtml(d.server_name || '') + '</td></tr>'
|
||||
+ '<tr><th>Server Host / IP</th><td>' + escHtml(d.server_host || '') + '</td></tr>'
|
||||
+ '<tr><th>Panel URL</th><td>' + escHtml(d.panel_url || '') + '</td></tr>';
|
||||
if (d.panel_status) {
|
||||
html += '<tr><th>Panel Status</th><td><pre style="margin:0;font-size:11px">' + escHtml(JSON.stringify(d.panel_status, null, 2)) + '</pre></td></tr>';
|
||||
}
|
||||
html += '</table>';
|
||||
$sec.find('.wap-server-info').html(html);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* ── DNS (WooDomains PowerDNS integration) ────────────────── */
|
||||
|
||||
if (pdnsActive) {
|
||||
$(document).on('click', '.wap-load-dns', function () {
|
||||
var $panel = $(this).closest('.wap-site-panel');
|
||||
var zone = $(this).data('zone');
|
||||
var $sec = $panel.find('.wap-dns-section');
|
||||
$sec.toggle();
|
||||
if ($sec.is(':visible')) {
|
||||
loadDnsRecords($sec, zone);
|
||||
}
|
||||
});
|
||||
|
||||
function loadDnsRecords($sec, zone) {
|
||||
$sec.find('.wap-dns-records-list').html('<em>Loading DNS records…</em>');
|
||||
pdnsPost('woodomains_dns_get', { zone: zone }, function (res) {
|
||||
if (!res.success) {
|
||||
$sec.find('.wap-dns-records-list').html('<p>' + escHtml(res.data && res.data.message ? res.data.message : 'Error.') + '</p>');
|
||||
return;
|
||||
}
|
||||
var records = res.data.records || [];
|
||||
var html = '<table class="woocommerce-table shop_table"><thead><tr>'
|
||||
+ '<th>Name</th><th>Type</th><th>TTL</th><th>Content</th><th></th>'
|
||||
+ '</tr></thead><tbody>';
|
||||
$.each(records, function (i, rr) {
|
||||
var contents = $.map(rr.records, function (r) { return escHtml(r.content); }).join('<br>');
|
||||
html += '<tr><td>' + escHtml(rr.name) + '</td><td>' + escHtml(rr.type) + '</td>'
|
||||
+ '<td>' + rr.ttl + '</td><td>' + contents + '</td>'
|
||||
+ '<td><button class="button wap-btn-sm wap-dns-del" data-name="' + escHtml(rr.name) + '" data-type="' + escHtml(rr.type) + '" data-zone="' + escHtml(zone) + '">Del</button></td></tr>';
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$sec.find('.wap-dns-records-list').html(html);
|
||||
|
||||
$sec.find('.wap-dns-del').on('click', function () {
|
||||
if (!confirm('Delete this DNS record?')) return;
|
||||
pdnsPost('woodomains_dns_delete', {
|
||||
zone: $(this).data('zone'),
|
||||
name: $(this).data('name'),
|
||||
type: $(this).data('type')
|
||||
}, function () { loadDnsRecords($sec, zone); });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('click', '.wap-dns-save-btn', function () {
|
||||
var $panel = $(this).closest('.wap-site-panel');
|
||||
var $sec = $panel.find('.wap-dns-section');
|
||||
var zone = $sec.data('zone');
|
||||
var $notice = $sec.find('.wap-dns-notice');
|
||||
var contents = $sec.find('.wap-dns-content').val()
|
||||
.split('\n').filter(function (s) { return s.trim(); });
|
||||
|
||||
pdnsPost('woodomains_dns_upsert', {
|
||||
zone: zone,
|
||||
name: $sec.find('.wap-dns-name').val(),
|
||||
type: $sec.find('.wap-dns-type').val(),
|
||||
ttl: parseInt($sec.find('.wap-dns-ttl').val(), 10) || 3600,
|
||||
contents: contents
|
||||
}, function (res) {
|
||||
inlineNotice($notice, res.success ? 'Saved!' : (res.data && res.data.message ? res.data.message : 'Error.'), res.success ? 'ok' : 'error');
|
||||
if (res.success) loadDnsRecords($sec, zone);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════
|
||||
DATABASE PANELS
|
||||
══════════════════════════════════════════════════════════ */
|
||||
|
||||
/* ── Backup ───────────────────────────────────────────────── */
|
||||
|
||||
$(document).on('click', '.wap-db-backup-btn', function () {
|
||||
var $panel = $(this).closest('.wap-db-panel');
|
||||
var $notice = $panel.find('.wap-db-action-notice');
|
||||
post('wooaapanel_acct_db_backup', dbOpts($panel), function (res) {
|
||||
$notice.attr('class', 'wap-db-action-notice ' + (res.success ? 'ok' : 'error'))
|
||||
.text(res.success ? 'Backup started!' : (res.data || 'Error.'));
|
||||
});
|
||||
});
|
||||
|
||||
/* ── Backup list ──────────────────────────────────────────── */
|
||||
|
||||
$(document).on('click', '.wap-load-db-backups', function () {
|
||||
var $panel = $(this).closest('.wap-db-panel');
|
||||
var $sec = $panel.find('.wap-db-backups-section');
|
||||
$sec.toggle();
|
||||
if ($sec.is(':visible')) {
|
||||
loadDbBackups($panel, $sec);
|
||||
}
|
||||
});
|
||||
|
||||
function loadDbBackups($panel, $sec) {
|
||||
$sec.find('.wap-db-backups-list').html('<em>Loading…</em>');
|
||||
post('wooaapanel_acct_db_backups', dbOpts($panel), function (res) {
|
||||
var backups = res.data && res.data.data ? res.data.data : [];
|
||||
if (!backups.length) { $sec.find('.wap-db-backups-list').html('<p>No backups found.</p>'); return; }
|
||||
var html = '<table class="woocommerce-table"><thead><tr><th>File</th><th>Size</th><th>Date</th><th></th></tr></thead><tbody>';
|
||||
$.each(backups, function (i, b) {
|
||||
html += '<tr><td>' + escHtml(b.name || '') + '</td><td>' + escHtml(b.size || '') + '</td><td>' + escHtml(b.addtime || '') + '</td>'
|
||||
+ '<td><button class="button wap-btn-sm wap-del-db-backup" data-id="' + (b.id || '') + '">Del</button></td></tr>';
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$sec.find('.wap-db-backups-list').html(html);
|
||||
|
||||
$sec.find('.wap-del-db-backup').on('click', function () {
|
||||
if (!confirm('Delete this backup?')) return;
|
||||
post('wooaapanel_acct_db_backup_delete', $.extend({}, dbOpts($panel), { backup_id: $(this).data('id') }), function () {
|
||||
loadDbBackups($panel, $sec);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ── Optimize ─────────────────────────────────────────────── */
|
||||
|
||||
$(document).on('click', '.wap-db-optimize-btn', function () {
|
||||
var $panel = $(this).closest('.wap-db-panel');
|
||||
var $notice = $panel.find('.wap-db-action-notice');
|
||||
post('wooaapanel_acct_db_optimize', dbOpts($panel), function (res) {
|
||||
$notice.attr('class', 'wap-db-action-notice ' + (res.success ? 'ok' : 'error'))
|
||||
.text(res.success ? 'Database optimized!' : (res.data || 'Error.'));
|
||||
});
|
||||
});
|
||||
|
||||
/* ── Repair ───────────────────────────────────────────────── */
|
||||
|
||||
$(document).on('click', '.wap-db-repair-btn', function () {
|
||||
var $panel = $(this).closest('.wap-db-panel');
|
||||
var $notice = $panel.find('.wap-db-action-notice');
|
||||
post('wooaapanel_acct_db_repair', dbOpts($panel), function (res) {
|
||||
$notice.attr('class', 'wap-db-action-notice ' + (res.success ? 'ok' : 'error'))
|
||||
.text(res.success ? 'Database repaired!' : (res.data || 'Error.'));
|
||||
});
|
||||
});
|
||||
|
||||
/* ── Change Password ──────────────────────────────────────── */
|
||||
|
||||
$(document).on('click', '.wap-db-password-btn', function () {
|
||||
var $panel = $(this).closest('.wap-db-panel');
|
||||
$panel.find('.wap-db-password-section').toggle();
|
||||
});
|
||||
|
||||
$(document).on('click', '.wap-db-pass-save-btn', function () {
|
||||
var $panel = $(this).closest('.wap-db-panel');
|
||||
var $sec = $panel.find('.wap-db-password-section');
|
||||
var $notice = $sec.find('.wap-db-pass-notice');
|
||||
var db_user = $sec.find('.wap-db-user').val();
|
||||
var password = $sec.find('.wap-db-new-pass').val();
|
||||
|
||||
if (!db_user || !password) {
|
||||
inlineNotice($notice, 'Database user and password are required.', 'error'); return;
|
||||
}
|
||||
|
||||
post('wooaapanel_acct_db_password', $.extend({}, dbOpts($panel), { db_user: db_user, password: password }), function (res) {
|
||||
inlineNotice($notice, res.success ? 'Password updated!' : (res.data || 'Error.'), res.success ? 'ok' : 'error');
|
||||
if (res.success) { $sec.find('.wap-db-user, .wap-db-new-pass').val(''); }
|
||||
});
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
972
assets/js/wooaapanel-admin.js
Normal file
972
assets/js/wooaapanel-admin.js
Normal file
@@ -0,0 +1,972 @@
|
||||
/**
|
||||
* WooAApanel Admin JS
|
||||
*
|
||||
* Handles all admin pages: Servers, Site Assignments, DB Assignments,
|
||||
* Sites (full management), Databases (full management), WC Products.
|
||||
*/
|
||||
(function ($) {
|
||||
'use strict';
|
||||
|
||||
var ajaxUrl = wooaapanel.ajax_url;
|
||||
var nonce = wooaapanel.nonce;
|
||||
|
||||
/* ── Utility ──────────────────────────────────────────────────── */
|
||||
|
||||
function post(action, data, cb) {
|
||||
data = $.extend({ action: action, nonce: nonce }, data);
|
||||
$.post(ajaxUrl, data, cb).fail(function () {
|
||||
alert('Request failed. Check your network and try again.');
|
||||
});
|
||||
}
|
||||
|
||||
function notice($el, msg, type) {
|
||||
$el.removeClass('success error').addClass(type).text(msg).show();
|
||||
}
|
||||
|
||||
function confirm_action(msg) {
|
||||
return window.confirm(msg || 'Are you sure?');
|
||||
}
|
||||
|
||||
function pretty(obj) {
|
||||
return JSON.stringify(obj, null, 2);
|
||||
}
|
||||
|
||||
/* ── Page detection ───────────────────────────────────────────── */
|
||||
|
||||
var $body = $('body');
|
||||
var page_id = '';
|
||||
if ($('#wooaapanel-servers-page').length) page_id = 'servers';
|
||||
if ($('#wooaapanel-site-assignments-page').length) page_id = 'site-assignments';
|
||||
if ($('#wooaapanel-db-assignments-page').length) page_id = 'db-assignments';
|
||||
if ($('#wooaapanel-sites-page').length) page_id = 'sites';
|
||||
if ($('#wooaapanel-databases-page').length) page_id = 'databases';
|
||||
if ($('#wooaapanel-products-page').length) page_id = 'products';
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════
|
||||
SERVERS PAGE
|
||||
══════════════════════════════════════════════════════════════ */
|
||||
|
||||
if (page_id === 'servers') {
|
||||
var $wrap = $('#wap-servers-wrap');
|
||||
|
||||
function loadServers() {
|
||||
post('wooaapanel_servers_list', {}, function (res) {
|
||||
if (!res.success) return;
|
||||
var html = '<button class="button button-primary" id="wap-server-add-btn">+ Add Server</button>'
|
||||
+ '<div id="wap-server-form" class="wap-inline-form" style="display:none"></div>'
|
||||
+ '<table class="wap-table" style="margin-top:12px"><thead><tr>'
|
||||
+ '<th>ID</th><th>Name</th><th>URL</th><th>Active</th><th>Actions</th>'
|
||||
+ '</tr></thead><tbody>';
|
||||
$.each(res.data, function (i, s) {
|
||||
html += '<tr>'
|
||||
+ '<td>' + s.id + '</td>'
|
||||
+ '<td>' + escHtml(s.name) + '</td>'
|
||||
+ '<td><a href="' + escHtml(s.url) + '" target="_blank">' + escHtml(s.url) + '</a></td>'
|
||||
+ '<td><span class="wap-badge ' + (s.active ? 'wap-badge-active' : 'wap-badge-inactive') + '">' + (s.active ? 'Active' : 'Inactive') + '</span></td>'
|
||||
+ '<td>'
|
||||
+ '<button class="button wap-action-btn wap-server-edit" data-id="' + s.id + '" data-name="' + escAttr(s.name) + '" data-url="' + escAttr(s.url) + '" data-active="' + s.active + '">Edit</button> '
|
||||
+ '<button class="button wap-action-btn wap-server-test" data-id="' + s.id + '">Test</button> '
|
||||
+ '<button class="button wap-action-btn wap-server-del" data-id="' + s.id + '" style="color:red">Delete</button>'
|
||||
+ '</td></tr>';
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$wrap.html(html);
|
||||
bindServerEvents();
|
||||
});
|
||||
}
|
||||
|
||||
function serverForm(id, name, url, api_key, active) {
|
||||
return '<h4>' + (id ? 'Edit Server' : 'Add Server') + '</h4>'
|
||||
+ '<input type="hidden" class="wap-s-id" value="' + (id || 0) + '">'
|
||||
+ '<label><span>Name</span><input type="text" class="wap-s-name" value="' + escAttr(name || '') + '"></label>'
|
||||
+ '<label><span>Panel URL (e.g. http://1.2.3.4:8888)</span><input type="url" class="wap-s-url" value="' + escAttr(url || '') + '"></label>'
|
||||
+ '<label><span>API Key</span><input type="text" class="wap-s-key" value="' + escAttr(api_key || '') + '"></label>'
|
||||
+ '<label><span>Active</span><select class="wap-s-active"><option value="1"' + (active != 0 ? ' selected' : '') + '>Yes</option><option value="0"' + (active == 0 ? ' selected' : '') + '>No</option></select></label>'
|
||||
+ '<button class="button button-primary wap-s-save">Save</button> <button class="button wap-s-cancel">Cancel</button>'
|
||||
+ '<div class="wap-notice" id="wap-server-notice"></div>';
|
||||
}
|
||||
|
||||
function bindServerEvents() {
|
||||
$('#wap-server-add-btn').on('click', function () {
|
||||
$('#wap-server-form').html(serverForm()).show();
|
||||
});
|
||||
|
||||
$wrap.on('click', '.wap-server-edit', function () {
|
||||
var $btn = $(this);
|
||||
$('#wap-server-form').html(serverForm(
|
||||
$btn.data('id'), $btn.data('name'), $btn.data('url'), '', $btn.data('active')
|
||||
)).show();
|
||||
// API key intentionally left blank on edit for security
|
||||
});
|
||||
|
||||
$wrap.on('click', '.wap-s-save', function () {
|
||||
var $form = $(this).closest('.wap-inline-form, #wap-server-form');
|
||||
var data = {
|
||||
id: $form.find('.wap-s-id').val(),
|
||||
name: $form.find('.wap-s-name').val(),
|
||||
url: $form.find('.wap-s-url').val(),
|
||||
api_key: $form.find('.wap-s-key').val(),
|
||||
active: $form.find('.wap-s-active').val()
|
||||
};
|
||||
if (!data.name || !data.url) { alert('Name and URL are required.'); return; }
|
||||
if (!data.id || data.id == '0') {
|
||||
if (!data.api_key) { alert('API key is required for new servers.'); return; }
|
||||
}
|
||||
post('wooaapanel_server_save', data, function (res) {
|
||||
if (res.success) { loadServers(); } else { alert(res.data || 'Error saving.'); }
|
||||
});
|
||||
});
|
||||
|
||||
$wrap.on('click', '.wap-s-cancel', function () { $('#wap-server-form').hide(); });
|
||||
|
||||
$wrap.on('click', '.wap-server-del', function () {
|
||||
if (!confirm_action('Delete this server?')) return;
|
||||
post('wooaapanel_server_delete', { id: $(this).data('id') }, function (res) {
|
||||
if (res.success) loadServers(); else alert(res.data || 'Error.');
|
||||
});
|
||||
});
|
||||
|
||||
$wrap.on('click', '.wap-server-test', function () {
|
||||
var id = $(this).data('id');
|
||||
var $btn = $(this);
|
||||
$btn.text('Testing…').prop('disabled', true);
|
||||
post('wooaapanel_server_test', { id: id }, function (res) {
|
||||
$btn.text('Test').prop('disabled', false);
|
||||
alert(res.success ? 'Connection OK! Response: ' + JSON.stringify(res.data) : 'Failed: ' + (res.data || res.error));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
loadServers();
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════
|
||||
SITE ASSIGNMENTS PAGE
|
||||
══════════════════════════════════════════════════════════════ */
|
||||
|
||||
if (page_id === 'site-assignments') {
|
||||
var $wrap = $('#wap-site-assignments-wrap');
|
||||
|
||||
function loadSiteAssignments() {
|
||||
post('wooaapanel_site_assignments_list', {}, function (res) {
|
||||
if (!res.success) return;
|
||||
var html = '<button class="button button-primary" id="wap-sa-add-btn">+ Assign Site to Customer</button>'
|
||||
+ '<div id="wap-sa-form" class="wap-inline-form" style="display:none"></div>'
|
||||
+ '<table class="wap-table" style="margin-top:12px"><thead><tr>'
|
||||
+ '<th>ID</th><th>Customer</th><th>Server</th><th>Site Name</th><th>Domain</th><th>Actions</th>'
|
||||
+ '</tr></thead><tbody>';
|
||||
$.each(res.data, function (i, a) {
|
||||
html += '<tr>'
|
||||
+ '<td>' + a.id + '</td>'
|
||||
+ '<td>' + escHtml(a.customer_name || '') + '<br><small>' + escHtml(a.customer_email || '') + '</small></td>'
|
||||
+ '<td>' + escHtml(a.server_name || '') + '</td>'
|
||||
+ '<td>' + escHtml(a.site_name) + '</td>'
|
||||
+ '<td>' + escHtml(a.domain || '—') + '</td>'
|
||||
+ '<td>'
|
||||
+ '<button class="button wap-action-btn wap-sa-del" data-id="' + a.id + '" style="color:red">Remove</button>'
|
||||
+ '</td></tr>';
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$wrap.html(html);
|
||||
bindSAEvents();
|
||||
});
|
||||
}
|
||||
|
||||
function loadServersSelect(cb) {
|
||||
post('wooaapanel_servers_list', {}, function (res) {
|
||||
if (!res.success) return;
|
||||
cb(res.data);
|
||||
});
|
||||
}
|
||||
|
||||
function bindSAEvents() {
|
||||
$('#wap-sa-add-btn').on('click', function () {
|
||||
loadServersSelect(function (servers) {
|
||||
var opts = '<option value="">— Select server —</option>';
|
||||
$.each(servers, function (i, s) { opts += '<option value="' + s.id + '">' + escHtml(s.name) + '</option>'; });
|
||||
var html = '<h4>Assign Site to Customer</h4>'
|
||||
+ '<label><span>Server</span><select id="wap-sa-server">' + opts + '</select></label>'
|
||||
+ '<label><span>Site Name</span>'
|
||||
+ '<select id="wap-sa-site"><option value="">— Load sites —</option></select>'
|
||||
+ '<button class="button" id="wap-sa-load-sites" style="margin-left:4px">Load Sites</button>'
|
||||
+ '</label>'
|
||||
+ '<label><span>Domain (optional, for DNS management)</span><input type="text" id="wap-sa-domain"></label>'
|
||||
+ '<label><span>Customer</span>'
|
||||
+ '<input type="text" id="wap-sa-customer-q" placeholder="Search by name or email">'
|
||||
+ '<div id="wap-sa-customer-results" style="border:1px solid #ddd;max-height:120px;overflow-y:auto;display:none"></div>'
|
||||
+ '<input type="hidden" id="wap-sa-customer-id">'
|
||||
+ '</label>'
|
||||
+ '<button class="button button-primary" id="wap-sa-save">Save</button> <button class="button" id="wap-sa-cancel">Cancel</button>'
|
||||
+ '<div class="wap-notice" id="wap-sa-notice"></div>';
|
||||
$('#wap-sa-form').html(html).show();
|
||||
bindSAFormEvents();
|
||||
});
|
||||
});
|
||||
|
||||
$wrap.on('click', '.wap-sa-del', function () {
|
||||
if (!confirm_action('Remove this site assignment?')) return;
|
||||
post('wooaapanel_site_assignment_delete', { id: $(this).data('id') }, function (res) {
|
||||
if (res.success) loadSiteAssignments(); else alert(res.data || 'Error.');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function bindSAFormEvents() {
|
||||
$('#wap-sa-load-sites').on('click', function () {
|
||||
var sid = $('#wap-sa-server').val();
|
||||
if (!sid) { alert('Select a server first.'); return; }
|
||||
post('wooaapanel_remote_sites', { server_id: sid }, function (res) {
|
||||
var sites = res.data && res.data.data ? res.data.data : [];
|
||||
var opts = '<option value="">— Select site —</option>';
|
||||
$.each(sites, function (i, s) {
|
||||
var name = s.name || s.siteName || s.webname || JSON.stringify(s);
|
||||
opts += '<option value="' + escAttr(name) + '">' + escHtml(name) + '</option>';
|
||||
});
|
||||
$('#wap-sa-site').html(opts);
|
||||
});
|
||||
});
|
||||
|
||||
var searchTimer;
|
||||
$('#wap-sa-customer-q').on('keyup', function () {
|
||||
clearTimeout(searchTimer);
|
||||
var q = $(this).val();
|
||||
searchTimer = setTimeout(function () {
|
||||
if (!q) return;
|
||||
post('wooaapanel_customers_search', { q: q }, function (res) {
|
||||
if (!res.success) return;
|
||||
var $list = $('#wap-sa-customer-results').empty().show();
|
||||
$.each(res.data, function (i, u) {
|
||||
$('<div style="padding:4px 8px;cursor:pointer">' + escHtml(u.label) + '</div>')
|
||||
.on('click', function () {
|
||||
$('#wap-sa-customer-id').val(u.id);
|
||||
$('#wap-sa-customer-q').val(u.label);
|
||||
$list.hide();
|
||||
})
|
||||
.appendTo($list);
|
||||
});
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
|
||||
$('#wap-sa-save').on('click', function () {
|
||||
var data = {
|
||||
server_id: $('#wap-sa-server').val(),
|
||||
site_name: $('#wap-sa-site').val(),
|
||||
domain: $('#wap-sa-domain').val(),
|
||||
customer_id: $('#wap-sa-customer-id').val()
|
||||
};
|
||||
if (!data.server_id || !data.site_name || !data.customer_id) {
|
||||
alert('Server, site and customer are all required.'); return;
|
||||
}
|
||||
post('wooaapanel_site_assignment_save', data, function (res) {
|
||||
if (res.success) { loadSiteAssignments(); } else { alert(res.data || 'Error.'); }
|
||||
});
|
||||
});
|
||||
|
||||
$('#wap-sa-cancel').on('click', function () { $('#wap-sa-form').hide(); });
|
||||
}
|
||||
|
||||
loadSiteAssignments();
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════
|
||||
DB ASSIGNMENTS PAGE
|
||||
══════════════════════════════════════════════════════════════ */
|
||||
|
||||
if (page_id === 'db-assignments') {
|
||||
var $wrap = $('#wap-db-assignments-wrap');
|
||||
|
||||
function loadDbAssignments() {
|
||||
post('wooaapanel_db_assignments_list', {}, function (res) {
|
||||
if (!res.success) return;
|
||||
var html = '<button class="button button-primary" id="wap-da-add-btn">+ Assign Database to Customer</button>'
|
||||
+ '<div id="wap-da-form" class="wap-inline-form" style="display:none"></div>'
|
||||
+ '<table class="wap-table" style="margin-top:12px"><thead><tr>'
|
||||
+ '<th>ID</th><th>Customer</th><th>Server</th><th>Database</th><th>Actions</th>'
|
||||
+ '</tr></thead><tbody>';
|
||||
$.each(res.data, function (i, a) {
|
||||
html += '<tr>'
|
||||
+ '<td>' + a.id + '</td>'
|
||||
+ '<td>' + escHtml(a.customer_name || '') + '<br><small>' + escHtml(a.customer_email || '') + '</small></td>'
|
||||
+ '<td>' + escHtml(a.server_name || '') + '</td>'
|
||||
+ '<td>' + escHtml(a.db_name) + '</td>'
|
||||
+ '<td><button class="button wap-action-btn wap-da-del" data-id="' + a.id + '" style="color:red">Remove</button></td>'
|
||||
+ '</tr>';
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$wrap.html(html);
|
||||
bindDAEvents();
|
||||
});
|
||||
}
|
||||
|
||||
function bindDAEvents() {
|
||||
$('#wap-da-add-btn').on('click', function () {
|
||||
post('wooaapanel_servers_list', {}, function (res) {
|
||||
var opts = '<option value="">— Select server —</option>';
|
||||
$.each(res.data || [], function (i, s) { opts += '<option value="' + s.id + '">' + escHtml(s.name) + '</option>'; });
|
||||
var html = '<h4>Assign Database to Customer</h4>'
|
||||
+ '<label><span>Server</span><select id="wap-da-server">' + opts + '</select></label>'
|
||||
+ '<label><span>Database</span>'
|
||||
+ '<select id="wap-da-db"><option value="">— Load databases —</option></select>'
|
||||
+ '<button class="button" id="wap-da-load-dbs" style="margin-left:4px">Load Databases</button>'
|
||||
+ '</label>'
|
||||
+ '<label><span>Customer</span>'
|
||||
+ '<input type="text" id="wap-da-customer-q" placeholder="Search by name or email">'
|
||||
+ '<div id="wap-da-customer-results" style="border:1px solid #ddd;max-height:120px;overflow-y:auto;display:none"></div>'
|
||||
+ '<input type="hidden" id="wap-da-customer-id">'
|
||||
+ '</label>'
|
||||
+ '<button class="button button-primary" id="wap-da-save">Save</button> <button class="button" id="wap-da-cancel">Cancel</button>';
|
||||
$('#wap-da-form').html(html).show();
|
||||
|
||||
$('#wap-da-load-dbs').on('click', function () {
|
||||
var sid = $('#wap-da-server').val();
|
||||
if (!sid) { alert('Select a server first.'); return; }
|
||||
post('wooaapanel_remote_dbs', { server_id: sid }, function (res2) {
|
||||
var dbs = res2.data && res2.data.data ? res2.data.data : [];
|
||||
var opts2 = '<option value="">— Select database —</option>';
|
||||
$.each(dbs, function (i, d) {
|
||||
var name = d.name || d.database || JSON.stringify(d);
|
||||
opts2 += '<option value="' + escAttr(name) + '">' + escHtml(name) + '</option>';
|
||||
});
|
||||
$('#wap-da-db').html(opts2);
|
||||
});
|
||||
});
|
||||
|
||||
var searchTimer;
|
||||
$('#wap-da-customer-q').on('keyup', function () {
|
||||
clearTimeout(searchTimer);
|
||||
var q = $(this).val();
|
||||
searchTimer = setTimeout(function () {
|
||||
if (!q) return;
|
||||
post('wooaapanel_customers_search', { q: q }, function (r) {
|
||||
if (!r.success) return;
|
||||
var $list = $('#wap-da-customer-results').empty().show();
|
||||
$.each(r.data, function (i, u) {
|
||||
$('<div style="padding:4px 8px;cursor:pointer">' + escHtml(u.label) + '</div>')
|
||||
.on('click', function () {
|
||||
$('#wap-da-customer-id').val(u.id);
|
||||
$('#wap-da-customer-q').val(u.label);
|
||||
$list.hide();
|
||||
}).appendTo($list);
|
||||
});
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
|
||||
$('#wap-da-save').on('click', function () {
|
||||
var data = {
|
||||
server_id: $('#wap-da-server').val(),
|
||||
db_name: $('#wap-da-db').val(),
|
||||
customer_id: $('#wap-da-customer-id').val()
|
||||
};
|
||||
if (!data.server_id || !data.db_name || !data.customer_id) {
|
||||
alert('Server, database and customer are required.'); return;
|
||||
}
|
||||
post('wooaapanel_db_assignment_save', data, function (r) {
|
||||
if (r.success) { loadDbAssignments(); } else { alert(r.data || 'Error.'); }
|
||||
});
|
||||
});
|
||||
|
||||
$('#wap-da-cancel').on('click', function () { $('#wap-da-form').hide(); });
|
||||
});
|
||||
});
|
||||
|
||||
$wrap.on('click', '.wap-da-del', function () {
|
||||
if (!confirm_action('Remove this database assignment?')) return;
|
||||
post('wooaapanel_db_assignment_delete', { id: $(this).data('id') }, function (res) {
|
||||
if (res.success) loadDbAssignments(); else alert(res.data || 'Error.');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
loadDbAssignments();
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════
|
||||
SITES PAGE (admin full management)
|
||||
══════════════════════════════════════════════════════════════ */
|
||||
|
||||
if (page_id === 'sites') {
|
||||
var $wrap = $('#wap-sites-wrap');
|
||||
|
||||
function renderSitesToolbar(servers) {
|
||||
var opts = '<option value="">— Select server —</option>';
|
||||
$.each(servers, function (i, s) { opts += '<option value="' + s.id + '">' + escHtml(s.name) + '</option>'; });
|
||||
$wrap.html(
|
||||
'<div class="wap-toolbar">'
|
||||
+ '<label>Server: <select id="wap-sites-server">' + opts + '</select></label>'
|
||||
+ '<input type="text" id="wap-sites-search" placeholder="Search…" style="max-width:180px">'
|
||||
+ '<button class="button" id="wap-sites-load">Load Sites</button>'
|
||||
+ '<button class="button button-primary" id="wap-site-add-btn">+ Add Site</button>'
|
||||
+ '</div>'
|
||||
+ '<div id="wap-site-form" class="wap-inline-form" style="display:none"></div>'
|
||||
+ '<div id="wap-site-detail" style="display:none"></div>'
|
||||
+ '<div id="wap-sites-list"></div>'
|
||||
);
|
||||
|
||||
$('#wap-sites-load').on('click', loadSitesList);
|
||||
|
||||
$('#wap-site-add-btn').on('click', function () {
|
||||
var sid = $('#wap-sites-server').val();
|
||||
if (!sid) { alert('Select a server first.'); return; }
|
||||
$('#wap-site-form').html(siteAddForm(sid)).show();
|
||||
$('#wap-site-detail').hide();
|
||||
});
|
||||
|
||||
$wrap.on('click', '.wap-site-manage', function () {
|
||||
var $row = $(this).closest('tr');
|
||||
openSiteDetail({
|
||||
server_id: $row.data('server'),
|
||||
site_name: $row.data('site')
|
||||
});
|
||||
});
|
||||
|
||||
$wrap.on('click', '.wap-site-del', function () {
|
||||
if (!confirm_action('Delete this site? This is irreversible.')) return;
|
||||
var $row = $(this).closest('tr');
|
||||
post('wooaapanel_admin_site_delete', {
|
||||
server_id: $row.data('server'),
|
||||
site_name: $row.data('site')
|
||||
}, function (res) {
|
||||
alert(res.success ? 'Site deleted.' : 'Error: ' + JSON.stringify(res.data));
|
||||
loadSitesList();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadSitesList() {
|
||||
var sid = $('#wap-sites-server').val();
|
||||
var search = $('#wap-sites-search').val();
|
||||
if (!sid) { alert('Select a server.'); return; }
|
||||
post('wooaapanel_admin_site_list', { server_id: sid, search: search }, function (res) {
|
||||
var sites = res.data && res.data.data ? res.data.data : [];
|
||||
var html = '<table class="wap-table"><thead><tr>'
|
||||
+ '<th>ID</th><th>Name</th><th>Domain</th><th>PHP</th><th>Status</th><th>Actions</th>'
|
||||
+ '</tr></thead><tbody>';
|
||||
$.each(sites, function (i, s) {
|
||||
html += '<tr data-server="' + sid + '" data-site="' + escAttr(s.name || s.siteName || '') + '">'
|
||||
+ '<td>' + (s.id || '—') + '</td>'
|
||||
+ '<td>' + escHtml(s.name || s.siteName || '') + '</td>'
|
||||
+ '<td>' + escHtml(s.domain || '') + '</td>'
|
||||
+ '<td>' + escHtml(s.php_version || s.phpVersion || '') + '</td>'
|
||||
+ '<td>' + escHtml(s.status || '') + '</td>'
|
||||
+ '<td>'
|
||||
+ '<button class="button wap-btn-sm wap-site-manage">Manage</button> '
|
||||
+ '<button class="button wap-btn-sm wap-site-del" style="color:red">Delete</button>'
|
||||
+ '</td></tr>';
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$('#wap-sites-list').html(html);
|
||||
});
|
||||
}
|
||||
|
||||
function siteAddForm(server_id) {
|
||||
return '<h4>Add PHP Project Site</h4>'
|
||||
+ '<input type="hidden" class="wap-sf-server" value="' + server_id + '">'
|
||||
+ '<label><span>Site / Domain name</span><input type="text" class="wap-sf-webname"></label>'
|
||||
+ '<label><span>PHP version (e.g. 82)</span><input type="text" class="wap-sf-phpver" value="82"></label>'
|
||||
+ '<label><span>Port</span><input type="number" class="wap-sf-port" value="80"></label>'
|
||||
+ '<label><span>Remark</span><input type="text" class="wap-sf-ps"></label>'
|
||||
+ '<label><span>DB name (optional)</span><input type="text" class="wap-sf-sql"></label>'
|
||||
+ '<label><span>DB user (optional)</span><input type="text" class="wap-sf-datauser"></label>'
|
||||
+ '<label><span>DB password (optional)</span><input type="password" class="wap-sf-datapass"></label>'
|
||||
+ '<button class="button button-primary wap-sf-save">Create Site</button>'
|
||||
+ '<button class="button wap-sf-cancel" style="margin-left:6px">Cancel</button>'
|
||||
+ '<div class="wap-notice" id="wap-site-form-notice" style="margin-top:8px"></div>';
|
||||
}
|
||||
|
||||
$wrap.on('click', '.wap-sf-save', function () {
|
||||
var $f = $(this).closest('.wap-inline-form');
|
||||
post('wooaapanel_admin_site_add', {
|
||||
server_id: $f.find('.wap-sf-server').val(),
|
||||
webname: $f.find('.wap-sf-webname').val(),
|
||||
version: $f.find('.wap-sf-phpver').val(),
|
||||
port: $f.find('.wap-sf-port').val(),
|
||||
ps: $f.find('.wap-sf-ps').val(),
|
||||
sql: $f.find('.wap-sf-sql').val() ? 'MySQL' : '',
|
||||
datauser: $f.find('.wap-sf-datauser').val(),
|
||||
datapassword: $f.find('.wap-sf-datapass').val()
|
||||
}, function (res) {
|
||||
var $n = $f.find('#wap-site-form-notice');
|
||||
notice($n, res.success ? 'Site created!' : 'Error: ' + JSON.stringify(res.data), res.success ? 'success' : 'error');
|
||||
if (res.success) loadSitesList();
|
||||
});
|
||||
});
|
||||
|
||||
$wrap.on('click', '.wap-sf-cancel', function () { $('#wap-site-form').hide(); });
|
||||
|
||||
function openSiteDetail(opts) {
|
||||
var $d = $('#wap-site-detail').show();
|
||||
$d.html(
|
||||
'<h3>Manage: ' + escHtml(opts.site_name) + '</h3>'
|
||||
+ '<div class="wap-tabs">'
|
||||
+ '<div class="wap-tab active" data-tab="domains">Domains</div>'
|
||||
+ '<div class="wap-tab" data-tab="php">PHP</div>'
|
||||
+ '<div class="wap-tab" data-tab="rewrite">URL Rewrite</div>'
|
||||
+ '<div class="wap-tab" data-tab="ssl">SSL</div>'
|
||||
+ '<div class="wap-tab" data-tab="xss">Anti-XSS</div>'
|
||||
+ '</div>'
|
||||
+ '<div id="wap-site-tab-domains" class="wap-tab-content active"></div>'
|
||||
+ '<div id="wap-site-tab-php" class="wap-tab-content"></div>'
|
||||
+ '<div id="wap-site-tab-rewrite" class="wap-tab-content"></div>'
|
||||
+ '<div id="wap-site-tab-ssl" class="wap-tab-content"></div>'
|
||||
+ '<div id="wap-site-tab-xss" class="wap-tab-content"></div>'
|
||||
);
|
||||
|
||||
// Tab switching
|
||||
$d.find('.wap-tab').on('click', function () {
|
||||
$d.find('.wap-tab').removeClass('active');
|
||||
$d.find('.wap-tab-content').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
$d.find('#wap-site-tab-' + $(this).data('tab')).addClass('active');
|
||||
});
|
||||
|
||||
loadDomainsTab(opts, $d);
|
||||
$d.find('.wap-tab[data-tab="php"]').on('click', function () { loadPhpTab(opts, $d); });
|
||||
$d.find('.wap-tab[data-tab="rewrite"]').on('click', function () { loadRewriteTab(opts, $d); });
|
||||
$d.find('.wap-tab[data-tab="ssl"]').on('click', function () { loadSslTab(opts, $d); });
|
||||
$d.find('.wap-tab[data-tab="xss"]').on('click', function () { loadXssTab(opts, $d); });
|
||||
}
|
||||
|
||||
// ── Domains tab ──────────────────────────────────────────
|
||||
|
||||
function loadDomainsTab(opts, $d) {
|
||||
var $tab = $d.find('#wap-site-tab-domains');
|
||||
post('wooaapanel_admin_site_domains', opts, function (res) {
|
||||
var domains = res.data && res.data.data ? res.data.data : [];
|
||||
var html = '<table class="wap-table"><thead><tr><th>Domain</th><th>Port</th><th></th></tr></thead><tbody>';
|
||||
$.each(domains, function (i, dm) {
|
||||
html += '<tr><td>' + escHtml(dm.name || dm.domain || '') + '</td><td>' + (dm.port || '') + '</td>'
|
||||
+ '<td><button class="button wap-btn-sm wap-del-domain" data-domain="' + escAttr(dm.name || dm.domain || '') + '" data-id="' + (dm.id || 0) + '">Del</button></td></tr>';
|
||||
});
|
||||
html += '</tbody></table>'
|
||||
+ '<div style="margin-top:8px">'
|
||||
+ '<input type="text" class="wap-add-domain-input" placeholder="new-domain.com" style="width:200px;margin-right:4px">'
|
||||
+ '<button class="button wap-add-domain-btn">Add Domain</button>'
|
||||
+ '<span class="wap-domain-msg" style="margin-left:8px"></span>'
|
||||
+ '</div>';
|
||||
$tab.html(html);
|
||||
|
||||
$tab.find('.wap-del-domain').on('click', function () {
|
||||
if (!confirm_action('Delete this domain?')) return;
|
||||
post('wooaapanel_admin_site_del_domain', $.extend({}, opts, {
|
||||
domain: $(this).data('domain'),
|
||||
site_id: $(this).data('id')
|
||||
}), function () { loadDomainsTab(opts, $d); });
|
||||
});
|
||||
|
||||
$tab.find('.wap-add-domain-btn').on('click', function () {
|
||||
var domain = $tab.find('.wap-add-domain-input').val();
|
||||
post('wooaapanel_admin_site_add_domain', $.extend({}, opts, { domain: domain }), function (res) {
|
||||
$tab.find('.wap-domain-msg').text(res.success ? 'Added!' : JSON.stringify(res.data));
|
||||
if (res.success) loadDomainsTab(opts, $d);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── PHP tab ──────────────────────────────────────────────
|
||||
|
||||
function loadPhpTab(opts, $d) {
|
||||
var $tab = $d.find('#wap-site-tab-php');
|
||||
if ($tab.data('loaded')) return;
|
||||
$tab.data('loaded', 1);
|
||||
post('wooaapanel_admin_site_php_get', opts, function (res) {
|
||||
var current = res.data && res.data.version ? res.data.version : '?';
|
||||
post('wooaapanel_admin_site_php_versions', opts, function (res2) {
|
||||
var versions = res2.data && res2.data.version ? res2.data.version : [];
|
||||
var opts2 = '';
|
||||
$.each(versions, function (i, v) {
|
||||
var ver = v.version || v;
|
||||
opts2 += '<option value="' + escAttr(ver) + '" ' + (ver == current ? 'selected' : '') + '>' + escHtml(ver) + '</option>';
|
||||
});
|
||||
$tab.html(
|
||||
'<p>Current: <strong>' + escHtml(current) + '</strong></p>'
|
||||
+ '<select id="wap-php-select">' + opts2 + '</select>'
|
||||
+ ' <button class="button wap-php-set">Set PHP Version</button>'
|
||||
+ '<span id="wap-php-notice" style="margin-left:8px"></span>'
|
||||
);
|
||||
$tab.find('.wap-php-set').on('click', function () {
|
||||
post('wooaapanel_admin_site_php_set', $.extend({}, opts, { version: $tab.find('#wap-php-select').val() }), function (res) {
|
||||
$tab.find('#wap-php-notice').text(res.success ? 'Updated!' : JSON.stringify(res.data));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── Rewrite tab ──────────────────────────────────────────
|
||||
|
||||
function loadRewriteTab(opts, $d) {
|
||||
var $tab = $d.find('#wap-site-tab-rewrite');
|
||||
if ($tab.data('loaded')) return;
|
||||
$tab.data('loaded', 1);
|
||||
post('wooaapanel_admin_site_rewrite_get', opts, function (res) {
|
||||
var content = res.data && res.data.data ? res.data.data : (res.data || '');
|
||||
$tab.html(
|
||||
'<textarea id="wap-rewrite-ta" rows="16" style="width:100%;font-family:monospace">' + escHtml(content) + '</textarea>'
|
||||
+ '<button class="button button-primary wap-rewrite-save" style="margin-top:6px">Save Rewrite</button>'
|
||||
+ '<span id="wap-rewrite-notice" style="margin-left:8px"></span>'
|
||||
);
|
||||
$tab.find('.wap-rewrite-save').on('click', function () {
|
||||
post('wooaapanel_admin_site_rewrite_set', $.extend({}, opts, { content: $tab.find('#wap-rewrite-ta').val() }), function (res) {
|
||||
$tab.find('#wap-rewrite-notice').text(res.success ? 'Saved!' : JSON.stringify(res.data));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── SSL tab ──────────────────────────────────────────────
|
||||
|
||||
function loadSslTab(opts, $d) {
|
||||
var $tab = $d.find('#wap-site-tab-ssl');
|
||||
if ($tab.data('loaded')) return;
|
||||
$tab.data('loaded', 1);
|
||||
post('wooaapanel_admin_site_ssl_get', opts, function (res) {
|
||||
var d = res.data || {};
|
||||
$tab.html('<pre>' + escHtml(pretty(d)) + '</pre>'
|
||||
+ '<button class="button wap-ssl-close" style="margin-top:8px">Disable SSL</button>'
|
||||
+ ' <span id="wap-ssl-notice"></span>');
|
||||
$tab.find('.wap-ssl-close').on('click', function () {
|
||||
if (!confirm_action('Disable SSL for this site?')) return;
|
||||
post('wooaapanel_admin_site_ssl_close', opts, function (res) {
|
||||
$tab.find('#wap-ssl-notice').text(res.success ? 'SSL disabled.' : JSON.stringify(res.data));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── XSS tab ──────────────────────────────────────────────
|
||||
|
||||
function loadXssTab(opts, $d) {
|
||||
var $tab = $d.find('#wap-site-tab-xss');
|
||||
if ($tab.data('loaded')) return;
|
||||
$tab.data('loaded', 1);
|
||||
post('wooaapanel_admin_site_xss_get', $.extend({ site_path: '/www/wwwroot/' + opts.site_name }, opts), function (res) {
|
||||
var status = res.data;
|
||||
$tab.html('<pre>' + escHtml(pretty(status)) + '</pre>'
|
||||
+ '<button class="button wap-xss-on">Enable XSS</button>'
|
||||
+ ' <button class="button wap-xss-off">Disable XSS</button>'
|
||||
+ ' <span id="wap-xss-notice"></span>');
|
||||
$tab.find('.wap-xss-on').on('click', function () {
|
||||
post('wooaapanel_admin_site_xss_set', $.extend({ site_path: '/www/wwwroot/' + opts.site_name, enable: 1 }, opts), function (res) {
|
||||
$tab.find('#wap-xss-notice').text(res.success ? 'XSS enabled.' : JSON.stringify(res.data));
|
||||
});
|
||||
});
|
||||
$tab.find('.wap-xss-off').on('click', function () {
|
||||
post('wooaapanel_admin_site_xss_set', $.extend({ site_path: '/www/wwwroot/' + opts.site_name, enable: 0 }, opts), function (res) {
|
||||
$tab.find('#wap-xss-notice').text(res.success ? 'XSS disabled.' : JSON.stringify(res.data));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Bootstrap
|
||||
post('wooaapanel_servers_list', {}, function (res) {
|
||||
renderSitesToolbar(res.data || []);
|
||||
});
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════
|
||||
DATABASES PAGE (admin full management)
|
||||
══════════════════════════════════════════════════════════════ */
|
||||
|
||||
if (page_id === 'databases') {
|
||||
var $wrap = $('#wap-databases-wrap');
|
||||
|
||||
function renderDbToolbar(servers) {
|
||||
var opts = '<option value="">— Select server —</option>';
|
||||
$.each(servers, function (i, s) { opts += '<option value="' + s.id + '">' + escHtml(s.name) + '</option>'; });
|
||||
$wrap.html(
|
||||
'<div class="wap-toolbar">'
|
||||
+ '<label>Server: <select id="wap-dbs-server">' + opts + '</select></label>'
|
||||
+ '<input type="text" id="wap-dbs-search" placeholder="Search…" style="max-width:180px">'
|
||||
+ '<button class="button" id="wap-dbs-load">Load Databases</button>'
|
||||
+ '<button class="button button-primary" id="wap-db-add-btn">+ Add Database</button>'
|
||||
+ '<button class="button" id="wap-db-sync-btn">Sync</button>'
|
||||
+ '<button class="button" id="wap-db-recycle-btn">Recycle Bin</button>'
|
||||
+ '</div>'
|
||||
+ '<div id="wap-db-form" class="wap-inline-form" style="display:none"></div>'
|
||||
+ '<div id="wap-db-detail" style="display:none"></div>'
|
||||
+ '<div id="wap-dbs-list"></div>'
|
||||
);
|
||||
|
||||
$('#wap-dbs-load').on('click', loadDbList);
|
||||
|
||||
$('#wap-db-add-btn').on('click', function () {
|
||||
var sid = $('#wap-dbs-server').val();
|
||||
if (!sid) { alert('Select a server first.'); return; }
|
||||
$('#wap-db-form').html(dbAddForm(sid)).show();
|
||||
});
|
||||
|
||||
$('#wap-db-sync-btn').on('click', function () {
|
||||
var sid = $('#wap-dbs-server').val();
|
||||
if (!sid) { alert('Select a server.'); return; }
|
||||
post('wooaapanel_admin_db_sync', { server_id: sid }, function (res) {
|
||||
alert(res.success ? (res.data && res.data.message || 'Synced.') : 'Error: ' + JSON.stringify(res.data));
|
||||
});
|
||||
});
|
||||
|
||||
$('#wap-db-recycle-btn').on('click', function () {
|
||||
var sid = $('#wap-dbs-server').val();
|
||||
if (!sid) { alert('Select a server.'); return; }
|
||||
post('wooaapanel_admin_db_recycle', { server_id: sid }, function (res) {
|
||||
alert(res.success ? 'Recycle bin:\n' + pretty(res.data) : 'Error.');
|
||||
});
|
||||
});
|
||||
|
||||
$wrap.on('click', '.wap-db-manage', function () {
|
||||
openDbDetail({
|
||||
server_id: $(this).closest('tr').data('server'),
|
||||
db_name: $(this).closest('tr').data('db')
|
||||
});
|
||||
});
|
||||
|
||||
$wrap.on('click', '.wap-db-del-row', function () {
|
||||
if (!confirm_action('Delete this database? This is irreversible.')) return;
|
||||
var $row = $(this).closest('tr');
|
||||
post('wooaapanel_admin_db_delete', {
|
||||
server_id: $row.data('server'),
|
||||
db_name: $row.data('db')
|
||||
}, function (res) {
|
||||
alert(res.success ? 'Database deleted.' : 'Error: ' + JSON.stringify(res.data));
|
||||
loadDbList();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadDbList() {
|
||||
var sid = $('#wap-dbs-server').val();
|
||||
var search = $('#wap-dbs-search').val();
|
||||
if (!sid) { alert('Select a server.'); return; }
|
||||
post('wooaapanel_admin_db_list', { server_id: sid, search: search }, function (res) {
|
||||
var dbs = res.data && res.data.data ? res.data.data : [];
|
||||
var html = '<table class="wap-table"><thead><tr>'
|
||||
+ '<th>ID</th><th>Name</th><th>User</th><th>Type</th><th>Size</th><th>Actions</th>'
|
||||
+ '</tr></thead><tbody>';
|
||||
$.each(dbs, function (i, d) {
|
||||
html += '<tr data-server="' + sid + '" data-db="' + escAttr(d.name || '') + '">'
|
||||
+ '<td>' + (d.id || '—') + '</td>'
|
||||
+ '<td>' + escHtml(d.name || '') + '</td>'
|
||||
+ '<td>' + escHtml(d.username || d.db_user || '') + '</td>'
|
||||
+ '<td>' + escHtml(d.type || 'MySQL') + '</td>'
|
||||
+ '<td>' + escHtml(d.dataLength || '') + '</td>'
|
||||
+ '<td>'
|
||||
+ '<button class="button wap-btn-sm wap-db-manage">Manage</button> '
|
||||
+ '<button class="button wap-btn-sm wap-db-del-row" style="color:red">Delete</button>'
|
||||
+ '</td></tr>';
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$('#wap-dbs-list').html(html);
|
||||
});
|
||||
}
|
||||
|
||||
function dbAddForm(server_id) {
|
||||
return '<h4>Add Database</h4>'
|
||||
+ '<input type="hidden" class="wap-dbf-server" value="' + server_id + '">'
|
||||
+ '<label><span>Database Name</span><input type="text" class="wap-dbf-name"></label>'
|
||||
+ '<label><span>Database User</span><input type="text" class="wap-dbf-user"></label>'
|
||||
+ '<label><span>Password</span><input type="password" class="wap-dbf-pass"></label>'
|
||||
+ '<label><span>Encoding</span><select class="wap-dbf-enc"><option value="utf8mb4">utf8mb4</option><option value="utf8">utf8</option><option value="gbk">gbk</option></select></label>'
|
||||
+ '<button class="button button-primary wap-dbf-save">Create Database</button>'
|
||||
+ ' <button class="button wap-dbf-cancel">Cancel</button>'
|
||||
+ '<div class="wap-notice" id="wap-db-form-notice" style="margin-top:8px"></div>';
|
||||
}
|
||||
|
||||
$wrap.on('click', '.wap-dbf-save', function () {
|
||||
var $f = $(this).closest('.wap-inline-form');
|
||||
post('wooaapanel_admin_db_add', {
|
||||
server_id: $f.find('.wap-dbf-server').val(),
|
||||
db_name: $f.find('.wap-dbf-name').val(),
|
||||
db_user: $f.find('.wap-dbf-user').val(),
|
||||
password: $f.find('.wap-dbf-pass').val(),
|
||||
codeing: $f.find('.wap-dbf-enc').val()
|
||||
}, function (res) {
|
||||
notice($f.find('#wap-db-form-notice'), res.success ? 'Created!' : 'Error: ' + JSON.stringify(res.data), res.success ? 'success' : 'error');
|
||||
if (res.success) loadDbList();
|
||||
});
|
||||
});
|
||||
|
||||
$wrap.on('click', '.wap-dbf-cancel', function () { $('#wap-db-form').hide(); });
|
||||
|
||||
function openDbDetail(opts) {
|
||||
var $d = $('#wap-db-detail').show();
|
||||
$d.html(
|
||||
'<h3>Manage: ' + escHtml(opts.db_name) + '</h3>'
|
||||
+ '<div class="wap-tabs">'
|
||||
+ '<div class="wap-tab active" data-tab="backups">Backups</div>'
|
||||
+ '<div class="wap-tab" data-tab="access">Access</div>'
|
||||
+ '<div class="wap-tab" data-tab="password">Password</div>'
|
||||
+ '<div class="wap-tab" data-tab="quota">Quota</div>'
|
||||
+ '</div>'
|
||||
+ '<div id="wap-db-tab-backups" class="wap-tab-content active"></div>'
|
||||
+ '<div id="wap-db-tab-access" class="wap-tab-content"></div>'
|
||||
+ '<div id="wap-db-tab-password" class="wap-tab-content"></div>'
|
||||
+ '<div id="wap-db-tab-quota" class="wap-tab-content"></div>'
|
||||
+ '<div style="margin-top:12px">'
|
||||
+ '<button class="button wap-db-backup-now">Backup Now</button>'
|
||||
+ ' <button class="button wap-db-optimize">Optimize</button>'
|
||||
+ ' <button class="button wap-db-repair">Repair</button>'
|
||||
+ ' <span id="wap-db-quick-notice" style="margin-left:8px"></span>'
|
||||
+ '</div>'
|
||||
);
|
||||
|
||||
$d.find('.wap-tab').on('click', function () {
|
||||
$d.find('.wap-tab').removeClass('active');
|
||||
$d.find('.wap-tab-content').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
$d.find('#wap-db-tab-' + $(this).data('tab')).addClass('active');
|
||||
});
|
||||
|
||||
loadDbBackupsTab(opts, $d);
|
||||
$d.find('.wap-tab[data-tab="access"]').on('click', function () { loadDbAccessTab(opts, $d); });
|
||||
$d.find('.wap-tab[data-tab="password"]').on('click', function () { renderDbPasswordTab(opts, $d); });
|
||||
$d.find('.wap-tab[data-tab="quota"]').on('click', function () { renderDbQuotaTab(opts, $d); });
|
||||
|
||||
$d.find('.wap-db-backup-now').on('click', function () {
|
||||
post('wooaapanel_admin_db_backup', opts, function (res) {
|
||||
$d.find('#wap-db-quick-notice').text(res.success ? 'Backup started!' : JSON.stringify(res.data));
|
||||
loadDbBackupsTab(opts, $d);
|
||||
});
|
||||
});
|
||||
|
||||
$d.find('.wap-db-optimize').on('click', function () {
|
||||
post('wooaapanel_admin_db_optimize', opts, function (res) {
|
||||
$d.find('#wap-db-quick-notice').text(res.success ? 'Optimized!' : JSON.stringify(res.data));
|
||||
});
|
||||
});
|
||||
|
||||
$d.find('.wap-db-repair').on('click', function () {
|
||||
post('wooaapanel_admin_db_repair', opts, function (res) {
|
||||
$d.find('#wap-db-quick-notice').text(res.success ? 'Repaired!' : JSON.stringify(res.data));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadDbBackupsTab(opts, $d) {
|
||||
var $tab = $d.find('#wap-db-tab-backups');
|
||||
post('wooaapanel_admin_db_backups', opts, function (res) {
|
||||
var backups = res.data && res.data.data ? res.data.data : [];
|
||||
var html = '<table class="wap-table"><thead><tr><th>ID</th><th>Filename</th><th>Size</th><th>Date</th><th></th></tr></thead><tbody>';
|
||||
$.each(backups, function (i, b) {
|
||||
html += '<tr><td>' + (b.id || '') + '</td><td>' + escHtml(b.name || '') + '</td><td>' + escHtml(b.size || '') + '</td><td>' + escHtml(b.addtime || '') + '</td>'
|
||||
+ '<td><button class="button wap-btn-sm wap-del-backup" data-id="' + (b.id || '') + '">Del</button></td></tr>';
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$tab.html(html);
|
||||
$tab.find('.wap-del-backup').on('click', function () {
|
||||
if (!confirm_action('Delete this backup?')) return;
|
||||
post('wooaapanel_admin_db_backup_delete', $.extend({}, opts, { backup_id: $(this).data('id') }), function () { loadDbBackupsTab(opts, $d); });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadDbAccessTab(opts, $d) {
|
||||
var $tab = $d.find('#wap-db-tab-access');
|
||||
if ($tab.data('loaded')) return;
|
||||
$tab.data('loaded', 1);
|
||||
post('wooaapanel_admin_db_access_get', opts, function (res) {
|
||||
var acc = res.data && res.data.access ? res.data.access : '%';
|
||||
$tab.html(
|
||||
'<p>Current: <strong>' + escHtml(acc) + '</strong></p>'
|
||||
+ '<input type="text" id="wap-access-input" value="' + escAttr(acc) + '" style="width:200px;margin-right:6px">'
|
||||
+ '<button class="button wap-set-access">Set Access</button>'
|
||||
+ '<span id="wap-access-notice" style="margin-left:8px"></span>'
|
||||
);
|
||||
$tab.find('.wap-set-access').on('click', function () {
|
||||
post('wooaapanel_admin_db_access_set', $.extend({}, opts, { access: $tab.find('#wap-access-input').val() }), function (res) {
|
||||
$tab.find('#wap-access-notice').text(res.success ? 'Updated!' : JSON.stringify(res.data));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function renderDbPasswordTab(opts, $d) {
|
||||
var $tab = $d.find('#wap-db-tab-password');
|
||||
if ($tab.data('loaded')) return;
|
||||
$tab.data('loaded', 1);
|
||||
$tab.html(
|
||||
'<label>DB User: <input type="text" id="wap-pw-user" style="width:160px;margin:0 4px"></label>'
|
||||
+ '<label>New Password: <input type="password" id="wap-pw-pass" style="width:160px;margin:0 4px"></label>'
|
||||
+ '<button class="button wap-set-password">Set Password</button>'
|
||||
+ '<span id="wap-pw-notice" style="margin-left:8px"></span>'
|
||||
);
|
||||
$tab.find('.wap-set-password').on('click', function () {
|
||||
post('wooaapanel_admin_db_password', $.extend({}, opts, {
|
||||
db_user: $tab.find('#wap-pw-user').val(),
|
||||
password: $tab.find('#wap-pw-pass').val()
|
||||
}), function (res) {
|
||||
$tab.find('#wap-pw-notice').text(res.success ? 'Password updated!' : JSON.stringify(res.data));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function renderDbQuotaTab(opts, $d) {
|
||||
var $tab = $d.find('#wap-db-tab-quota');
|
||||
if ($tab.data('loaded')) return;
|
||||
$tab.data('loaded', 1);
|
||||
$tab.html(
|
||||
'<label>Quota (MB): <input type="number" id="wap-quota-input" value="1024" min="1" style="width:100px;margin:0 4px"></label>'
|
||||
+ '<button class="button wap-set-quota">Set Quota</button>'
|
||||
+ '<span id="wap-quota-notice" style="margin-left:8px"></span>'
|
||||
);
|
||||
$tab.find('.wap-set-quota').on('click', function () {
|
||||
post('wooaapanel_admin_db_quota', $.extend({}, opts, { quota_mb: $tab.find('#wap-quota-input').val() }), function (res) {
|
||||
$tab.find('#wap-quota-notice').text(res.success ? 'Quota set!' : JSON.stringify(res.data));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
post('wooaapanel_servers_list', {}, function (res) {
|
||||
renderDbToolbar(res.data || []);
|
||||
});
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════
|
||||
WC PRODUCTS PAGE
|
||||
══════════════════════════════════════════════════════════════ */
|
||||
|
||||
if (page_id === 'products') {
|
||||
var $wrap = $('#wap-products-wrap');
|
||||
|
||||
post('wooaapanel_products_list', {}, function (res) {
|
||||
if (!res.success) { $wrap.html('<p>Error loading products.</p>'); return; }
|
||||
var products = res.data.products || [];
|
||||
var servers = res.data.servers || [];
|
||||
|
||||
var serverOpts = '<option value="0">— None (no auto-provision) —</option>';
|
||||
$.each(servers, function (i, s) { serverOpts += '<option value="' + s.id + '">' + escHtml(s.name) + '</option>'; });
|
||||
|
||||
var html = '<p>Assign an aaPanel server to a product. When an order for that product completes, a site and database will be automatically created on the linked server.</p>'
|
||||
+ '<table class="wap-table"><thead><tr><th>Product</th><th>Linked Server</th><th></th></tr></thead><tbody>';
|
||||
$.each(products, function (i, p) {
|
||||
var selOpts = serverOpts.replace('value="' + p.server_id + '"', 'value="' + p.server_id + '" selected');
|
||||
html += '<tr><td>' + escHtml(p.name) + ' <small>(#' + p.id + ')</small></td>'
|
||||
+ '<td><select class="wap-prod-server" data-id="' + p.id + '">' + selOpts + '</select></td>'
|
||||
+ '<td><button class="button wap-prod-save" data-id="' + p.id + '">Save</button> <span class="wap-prod-msg"></span></td>'
|
||||
+ '</tr>';
|
||||
});
|
||||
html += '</tbody></table>';
|
||||
$wrap.html(html);
|
||||
|
||||
$wrap.on('click', '.wap-prod-save', function () {
|
||||
var $row = $(this).closest('tr');
|
||||
var prod_id = $(this).data('id');
|
||||
var server_id = $row.find('.wap-prod-server').val();
|
||||
var $msg = $row.find('.wap-prod-msg');
|
||||
post('wooaapanel_product_server_save', { product_id: prod_id, server_id: server_id }, function (res) {
|
||||
$msg.text(res.success ? '✓ Saved' : 'Error');
|
||||
setTimeout(function () { $msg.text(''); }, 2000);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* ── HTML escape helpers ──────────────────────────────────── */
|
||||
|
||||
function escHtml(s) {
|
||||
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
function escAttr(s) { return escHtml(s); }
|
||||
|
||||
})(jQuery);
|
||||
550
includes/class-wooaapanel-account.php
Normal file
550
includes/class-wooaapanel-account.php
Normal file
@@ -0,0 +1,550 @@
|
||||
<?php
|
||||
/**
|
||||
* WooAApanel Account – WooCommerce My Account integration.
|
||||
*
|
||||
* Adds a "Web Hosting" tab where customers can manage their assigned sites
|
||||
* and databases. DNS management is provided via WooDomains PowerDNS if
|
||||
* that plugin is active.
|
||||
*/
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class WooAApanel_Account {
|
||||
|
||||
const ENDPOINT = 'web-hosting';
|
||||
|
||||
public function __construct() {
|
||||
add_action( 'init', [ $this, 'register_endpoint' ] );
|
||||
add_filter( 'woocommerce_account_menu_items', [ $this, 'add_menu_item' ] );
|
||||
add_action( 'woocommerce_account_' . self::ENDPOINT . '_endpoint', [ $this, 'render_page' ] );
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
|
||||
$actions = [
|
||||
// Sites
|
||||
'wooaapanel_acct_sites',
|
||||
'wooaapanel_acct_site_domains',
|
||||
'wooaapanel_acct_site_add_domain',
|
||||
'wooaapanel_acct_site_del_domain',
|
||||
'wooaapanel_acct_site_xss_get',
|
||||
'wooaapanel_acct_site_xss_set',
|
||||
'wooaapanel_acct_site_php_get',
|
||||
'wooaapanel_acct_site_php_versions',
|
||||
'wooaapanel_acct_site_php_set',
|
||||
'wooaapanel_acct_site_rewrite_get',
|
||||
'wooaapanel_acct_site_rewrite_set',
|
||||
'wooaapanel_acct_site_ssl_get',
|
||||
'wooaapanel_acct_server_info',
|
||||
// Databases
|
||||
'wooaapanel_acct_dbs',
|
||||
'wooaapanel_acct_db_backup',
|
||||
'wooaapanel_acct_db_backups',
|
||||
'wooaapanel_acct_db_backup_delete',
|
||||
'wooaapanel_acct_db_optimize',
|
||||
'wooaapanel_acct_db_repair',
|
||||
'wooaapanel_acct_db_password',
|
||||
];
|
||||
|
||||
foreach ( $actions as $action ) {
|
||||
add_action( 'wp_ajax_' . $action, [ $this, 'ajax_' . $action ] );
|
||||
}
|
||||
}
|
||||
|
||||
public function register_endpoint(): void {
|
||||
add_rewrite_endpoint( self::ENDPOINT, EP_ROOT | EP_PAGES );
|
||||
}
|
||||
|
||||
public function add_menu_item( array $items ): array {
|
||||
$logout = $items['customer-logout'] ?? null;
|
||||
unset( $items['customer-logout'] );
|
||||
$items[ self::ENDPOINT ] = __( 'Web Hosting', 'wooaapanel' );
|
||||
if ( $logout ) {
|
||||
$items['customer-logout'] = $logout;
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function enqueue_assets(): void {
|
||||
if ( ! is_account_page() ) {
|
||||
return;
|
||||
}
|
||||
wp_enqueue_style( 'wooaapanel-account', WOOAAPANEL_PLUGIN_URL . 'assets/css/wooaapanel-account.css', [], WOOAAPANEL_VERSION );
|
||||
wp_enqueue_script( 'wooaapanel-account', WOOAAPANEL_PLUGIN_URL . 'assets/js/wooaapanel-account.js', [ 'jquery' ], WOOAAPANEL_VERSION, true );
|
||||
wp_localize_script( 'wooaapanel-account', 'wooaapanelAcct', [
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'wooaapanel_account' ),
|
||||
'pdns_nonce' => class_exists( 'WooDomains_PowerDNS_API' ) ? wp_create_nonce( 'woodomains_nonce' ) : '',
|
||||
'pdns_active' => class_exists( 'WooDomains_PowerDNS_API' ) ? 1 : 0,
|
||||
'ajax_url_woodomains' => admin_url( 'admin-ajax.php' ),
|
||||
] );
|
||||
}
|
||||
|
||||
// ── My Account page render ────────────────────────────────────────────────
|
||||
|
||||
public function render_page(): void {
|
||||
if ( ! is_user_logged_in() ) {
|
||||
echo '<p>' . esc_html__( 'Please log in to manage your hosting.', 'wooaapanel' ) . '</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
$customer_id = get_current_user_id();
|
||||
$site_assigns = $this->get_site_assignments( $customer_id );
|
||||
$db_assigns = $this->get_db_assignments( $customer_id );
|
||||
$pdns_active = class_exists( 'WooDomains_PowerDNS_API' );
|
||||
|
||||
if ( empty( $site_assigns ) && empty( $db_assigns ) ) {
|
||||
echo '<div class="woocommerce-info">' . esc_html__( 'You have no hosting resources assigned yet. Please contact support.', 'wooaapanel' ) . '</div>';
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<div class="wooaapanel-account" id="wooaapanel-account">
|
||||
<div id="wap-acct-notices"></div>
|
||||
|
||||
<?php if ( ! empty( $site_assigns ) ) : ?>
|
||||
<h2><?php esc_html_e( 'Websites', 'wooaapanel' ); ?></h2>
|
||||
<?php foreach ( $site_assigns as $assign ) : ?>
|
||||
<div class="wap-site-panel"
|
||||
data-assignment-id="<?php echo esc_attr( $assign->id ); ?>"
|
||||
data-server-id="<?php echo esc_attr( $assign->server_id ); ?>"
|
||||
data-site-name="<?php echo esc_attr( $assign->site_name ); ?>"
|
||||
data-domain="<?php echo esc_attr( $assign->domain ); ?>">
|
||||
|
||||
<div class="wap-panel-header">
|
||||
<span class="wap-site-name"><?php echo esc_html( $assign->site_name ); ?></span>
|
||||
<span class="wap-domain"><?php echo esc_html( $assign->domain ); ?></span>
|
||||
<span class="wap-server-label"><?php echo esc_html( $assign->server_name ); ?></span>
|
||||
|
||||
<div class="wap-panel-actions">
|
||||
<button class="button wap-btn-sm wap-load-domains"><?php esc_html_e( 'Domains', 'wooaapanel' ); ?></button>
|
||||
<button class="button wap-btn-sm wap-load-php"><?php esc_html_e( 'PHP', 'wooaapanel' ); ?></button>
|
||||
<button class="button wap-btn-sm wap-load-rewrite"><?php esc_html_e( 'URL Rewrite', 'wooaapanel' ); ?></button>
|
||||
<button class="button wap-btn-sm wap-load-ssl"><?php esc_html_e( 'SSL', 'wooaapanel' ); ?></button>
|
||||
<button class="button wap-btn-sm wap-load-server-info"><?php esc_html_e( 'Server Info', 'wooaapanel' ); ?></button>
|
||||
<?php if ( $pdns_active && $assign->domain ) : ?>
|
||||
<button class="button wap-btn-sm wap-load-dns"
|
||||
data-zone="<?php echo esc_attr( rtrim( $assign->domain, '.' ) . '.' ); ?>">
|
||||
<?php esc_html_e( 'DNS Records', 'wooaapanel' ); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div><!-- .wap-panel-header -->
|
||||
|
||||
<!-- Domains -->
|
||||
<div class="wap-section wap-domains-section" style="display:none">
|
||||
<h4><?php esc_html_e( 'Domain Names', 'wooaapanel' ); ?></h4>
|
||||
<div class="wap-domains-list"></div>
|
||||
<div class="wap-add-domain-form">
|
||||
<input type="text" class="wap-new-domain input-text" placeholder="newdomain.com">
|
||||
<button class="button wap-add-domain-btn"><?php esc_html_e( 'Add Domain', 'wooaapanel' ); ?></button>
|
||||
<span class="wap-domain-notice"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PHP Version -->
|
||||
<div class="wap-section wap-php-section" style="display:none">
|
||||
<h4><?php esc_html_e( 'PHP Version', 'wooaapanel' ); ?></h4>
|
||||
<div class="wap-php-current"></div>
|
||||
<select class="wap-php-select"></select>
|
||||
<button class="button wap-php-set-btn"><?php esc_html_e( 'Set PHP Version', 'wooaapanel' ); ?></button>
|
||||
<span class="wap-php-notice"></span>
|
||||
</div>
|
||||
|
||||
<!-- URL Rewrite -->
|
||||
<div class="wap-section wap-rewrite-section" style="display:none">
|
||||
<h4><?php esc_html_e( 'URL Rewrite', 'wooaapanel' ); ?></h4>
|
||||
<textarea class="wap-rewrite-content" rows="10" style="width:100%;font-family:monospace"></textarea>
|
||||
<button class="button wap-rewrite-save-btn"><?php esc_html_e( 'Save Rewrite', 'wooaapanel' ); ?></button>
|
||||
<span class="wap-rewrite-notice"></span>
|
||||
</div>
|
||||
|
||||
<!-- SSL Info -->
|
||||
<div class="wap-section wap-ssl-section" style="display:none">
|
||||
<h4><?php esc_html_e( 'SSL Certificate', 'wooaapanel' ); ?></h4>
|
||||
<div class="wap-ssl-info"></div>
|
||||
</div>
|
||||
|
||||
<!-- XSS -->
|
||||
<div class="wap-section wap-xss-section" style="display:none">
|
||||
<h4><?php esc_html_e( 'Anti-XSS Protection', 'wooaapanel' ); ?></h4>
|
||||
<div class="wap-xss-status"></div>
|
||||
<button class="button wap-xss-toggle-btn"><?php esc_html_e( 'Toggle XSS Protection', 'wooaapanel' ); ?></button>
|
||||
<span class="wap-xss-notice"></span>
|
||||
</div>
|
||||
|
||||
<!-- Server Info -->
|
||||
<div class="wap-section wap-server-section" style="display:none">
|
||||
<h4><?php esc_html_e( 'Server Information', 'wooaapanel' ); ?></h4>
|
||||
<div class="wap-server-info"></div>
|
||||
</div>
|
||||
|
||||
<!-- DNS (WooDomains PowerDNS integration) -->
|
||||
<?php if ( $pdns_active && $assign->domain ) : ?>
|
||||
<div class="wap-section wap-dns-section" style="display:none"
|
||||
data-zone="<?php echo esc_attr( rtrim( $assign->domain, '.' ) . '.' ); ?>">
|
||||
<h4><?php esc_html_e( 'DNS Records', 'wooaapanel' ); ?></h4>
|
||||
<div class="wap-dns-records-list"></div>
|
||||
<hr>
|
||||
<h5><?php esc_html_e( 'Add / Update Record', 'wooaapanel' ); ?></h5>
|
||||
<div class="wap-dns-add-form">
|
||||
<label><?php esc_html_e( 'Name', 'wooaapanel' ); ?> <input type="text" class="wap-dns-name input-text" placeholder="<?php echo esc_attr( $assign->domain . '.' ); ?>"></label>
|
||||
<label><?php esc_html_e( 'Type', 'wooaapanel' ); ?>
|
||||
<select class="wap-dns-type">
|
||||
<?php foreach ( [ 'A', 'AAAA', 'CNAME', 'MX', 'TXT', 'SRV', 'CAA' ] as $t ) : ?>
|
||||
<option><?php echo esc_html( $t ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label><?php esc_html_e( 'TTL', 'wooaapanel' ); ?> <input type="number" class="wap-dns-ttl input-text" value="3600" min="60" style="width:80px"></label>
|
||||
<label><?php esc_html_e( 'Content (one per line)', 'wooaapanel' ); ?><br>
|
||||
<textarea class="wap-dns-content input-text" rows="3"></textarea>
|
||||
</label>
|
||||
<button class="button wap-dns-save-btn"><?php esc_html_e( 'Save Record', 'wooaapanel' ); ?></button>
|
||||
<span class="wap-dns-notice"></span>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div><!-- .wap-site-panel -->
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ( ! empty( $db_assigns ) ) : ?>
|
||||
<h2><?php esc_html_e( 'Databases', 'wooaapanel' ); ?></h2>
|
||||
<?php foreach ( $db_assigns as $assign ) : ?>
|
||||
<div class="wap-db-panel"
|
||||
data-assignment-id="<?php echo esc_attr( $assign->id ); ?>"
|
||||
data-server-id="<?php echo esc_attr( $assign->server_id ); ?>"
|
||||
data-db-name="<?php echo esc_attr( $assign->db_name ); ?>">
|
||||
|
||||
<div class="wap-panel-header">
|
||||
<span class="wap-db-name"><strong><?php echo esc_html( $assign->db_name ); ?></strong></span>
|
||||
<span class="wap-server-label"><?php echo esc_html( $assign->server_name ); ?></span>
|
||||
|
||||
<div class="wap-panel-actions">
|
||||
<button class="button wap-btn-sm wap-db-backup-btn"><?php esc_html_e( 'Backup', 'wooaapanel' ); ?></button>
|
||||
<button class="button wap-btn-sm wap-load-db-backups"><?php esc_html_e( 'Backups', 'wooaapanel' ); ?></button>
|
||||
<button class="button wap-btn-sm wap-db-optimize-btn"><?php esc_html_e( 'Optimize', 'wooaapanel' ); ?></button>
|
||||
<button class="button wap-btn-sm wap-db-repair-btn"><?php esc_html_e( 'Repair', 'wooaapanel' ); ?></button>
|
||||
<button class="button wap-btn-sm wap-db-password-btn"><?php esc_html_e( 'Change Password', 'wooaapanel' ); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wap-db-action-notice"></div>
|
||||
|
||||
<!-- Backup list -->
|
||||
<div class="wap-section wap-db-backups-section" style="display:none">
|
||||
<h4><?php esc_html_e( 'Database Backups', 'wooaapanel' ); ?></h4>
|
||||
<div class="wap-db-backups-list"></div>
|
||||
</div>
|
||||
|
||||
<!-- Change password form -->
|
||||
<div class="wap-section wap-db-password-section" style="display:none">
|
||||
<h4><?php esc_html_e( 'Change Database Password', 'wooaapanel' ); ?></h4>
|
||||
<input type="text" class="wap-db-user input-text" placeholder="<?php esc_attr_e( 'Database user', 'wooaapanel' ); ?>">
|
||||
<input type="password" class="wap-db-new-pass input-text" placeholder="<?php esc_attr_e( 'New password', 'wooaapanel' ); ?>">
|
||||
<button class="button wap-db-pass-save-btn"><?php esc_html_e( 'Save Password', 'wooaapanel' ); ?></button>
|
||||
<span class="wap-db-pass-notice"></span>
|
||||
</div>
|
||||
|
||||
</div><!-- .wap-db-panel -->
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
</div><!-- #wooaapanel-account -->
|
||||
<?php
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
private function get_site_assignments( int $customer_id ): array {
|
||||
global $wpdb;
|
||||
return $wpdb->get_results( $wpdb->prepare( "
|
||||
SELECT a.id, a.server_id, a.site_name, a.domain,
|
||||
s.name AS server_name, s.url AS server_url
|
||||
FROM {$wpdb->prefix}wooaapanel_site_assignments a
|
||||
JOIN {$wpdb->prefix}wooaapanel_servers s ON s.id = a.server_id
|
||||
WHERE a.customer_id = %d AND s.active = 1
|
||||
ORDER BY a.site_name
|
||||
", $customer_id ) );
|
||||
}
|
||||
|
||||
private function get_db_assignments( int $customer_id ): array {
|
||||
global $wpdb;
|
||||
return $wpdb->get_results( $wpdb->prepare( "
|
||||
SELECT a.id, a.server_id, a.db_name,
|
||||
s.name AS server_name
|
||||
FROM {$wpdb->prefix}wooaapanel_db_assignments a
|
||||
JOIN {$wpdb->prefix}wooaapanel_servers s ON s.id = a.server_id
|
||||
WHERE a.customer_id = %d AND s.active = 1
|
||||
ORDER BY a.db_name
|
||||
", $customer_id ) );
|
||||
}
|
||||
|
||||
private function get_server( int $id ): ?object {
|
||||
global $wpdb;
|
||||
return $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}wooaapanel_servers WHERE id = %d AND active = 1",
|
||||
$id
|
||||
) );
|
||||
}
|
||||
|
||||
/** Verify nonce + login. */
|
||||
private function account_verify(): void {
|
||||
check_ajax_referer( 'wooaapanel_account', 'nonce' );
|
||||
if ( ! is_user_logged_in() ) {
|
||||
wp_send_json_error( 'Not logged in.', 401 );
|
||||
}
|
||||
}
|
||||
|
||||
/** Confirm the current user owns this site assignment. */
|
||||
private function verify_site_ownership( int $server_id, string $site_name ): bool {
|
||||
global $wpdb;
|
||||
return (bool) $wpdb->get_var( $wpdb->prepare( "
|
||||
SELECT id FROM {$wpdb->prefix}wooaapanel_site_assignments
|
||||
WHERE customer_id = %d AND server_id = %d AND site_name = %s
|
||||
", get_current_user_id(), $server_id, $site_name ) );
|
||||
}
|
||||
|
||||
/** Confirm the current user owns this DB assignment. */
|
||||
private function verify_db_ownership( int $server_id, string $db_name ): bool {
|
||||
global $wpdb;
|
||||
return (bool) $wpdb->get_var( $wpdb->prepare( "
|
||||
SELECT id FROM {$wpdb->prefix}wooaapanel_db_assignments
|
||||
WHERE customer_id = %d AND server_id = %d AND db_name = %s
|
||||
", get_current_user_id(), $server_id, $db_name ) );
|
||||
}
|
||||
|
||||
private function api_for_site( int $server_id, string $site_name ): WooAApanel_API {
|
||||
if ( ! $this->verify_site_ownership( $server_id, $site_name ) ) {
|
||||
wp_send_json_error( 'Access denied.', 403 );
|
||||
}
|
||||
$server = $this->get_server( $server_id );
|
||||
if ( ! $server ) {
|
||||
wp_send_json_error( 'Server unavailable.' );
|
||||
}
|
||||
return WooAApanel_API::from_server( $server );
|
||||
}
|
||||
|
||||
private function api_for_db( int $server_id, string $db_name ): WooAApanel_API {
|
||||
if ( ! $this->verify_db_ownership( $server_id, $db_name ) ) {
|
||||
wp_send_json_error( 'Access denied.', 403 );
|
||||
}
|
||||
$server = $this->get_server( $server_id );
|
||||
if ( ! $server ) {
|
||||
wp_send_json_error( 'Server unavailable.' );
|
||||
}
|
||||
return WooAApanel_API::from_server( $server );
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
// AJAX: Account – Sites
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public function ajax_wooaapanel_acct_sites(): void {
|
||||
$this->account_verify();
|
||||
$sites = $this->get_site_assignments( get_current_user_id() );
|
||||
$dbs = $this->get_db_assignments( get_current_user_id() );
|
||||
wp_send_json_success( [ 'sites' => $sites, 'databases' => $dbs ] );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_site_domains(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$api = $this->api_for_site( $server_id, $site_name );
|
||||
wp_send_json( $api->get_site_domains( $site_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_site_add_domain(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
|
||||
|
||||
if ( ! $domain ) {
|
||||
wp_send_json_error( 'Domain is required.' );
|
||||
}
|
||||
|
||||
$api = $this->api_for_site( $server_id, $site_name );
|
||||
wp_send_json( $api->add_domain( $site_name, $domain ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_site_del_domain(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
|
||||
$site_id = absint( $_POST['site_id'] ?? 0 );
|
||||
$api = $this->api_for_site( $server_id, $site_name );
|
||||
wp_send_json( $api->delete_domain( $site_name, $domain, $site_id ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_site_xss_get(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$site_path = sanitize_text_field( $_POST['site_path'] ?? "/www/wwwroot/{$site_name}" );
|
||||
$api = $this->api_for_site( $server_id, $site_name );
|
||||
wp_send_json( $api->get_xss( $site_name, $site_path ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_site_xss_set(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$site_path = sanitize_text_field( $_POST['site_path'] ?? "/www/wwwroot/{$site_name}" );
|
||||
$enable = ! empty( $_POST['enable'] );
|
||||
$api = $this->api_for_site( $server_id, $site_name );
|
||||
wp_send_json( $api->set_xss( $site_name, $site_path, $enable ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_site_php_get(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$api = $this->api_for_site( $server_id, $site_name );
|
||||
wp_send_json( $api->get_site_php_version( $site_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_site_php_versions(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$api = $this->api_for_site( $server_id, $site_name );
|
||||
wp_send_json( $api->get_php_versions() );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_site_php_set(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$version = sanitize_text_field( $_POST['version'] ?? '' );
|
||||
|
||||
if ( ! $version ) {
|
||||
wp_send_json_error( 'PHP version is required.' );
|
||||
}
|
||||
|
||||
$api = $this->api_for_site( $server_id, $site_name );
|
||||
wp_send_json( $api->set_site_php_version( $site_name, $version ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_site_rewrite_get(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$template = sanitize_text_field( $_POST['template'] ?? '' );
|
||||
$api = $this->api_for_site( $server_id, $site_name );
|
||||
wp_send_json( $api->get_rewrite_content( $site_name, $template ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_site_rewrite_set(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$content = wp_unslash( $_POST['content'] ?? '' );
|
||||
$api = $this->api_for_site( $server_id, $site_name );
|
||||
wp_send_json( $api->set_rewrite( $site_name, $content ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_site_ssl_get(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$api = $this->api_for_site( $server_id, $site_name );
|
||||
wp_send_json( $api->get_ssl( $site_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_server_info(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
|
||||
// Fetch the server row to expose only safe fields (name, IP visible via panel URL host).
|
||||
if ( ! $this->verify_site_ownership( $server_id, $site_name ) ) {
|
||||
wp_send_json_error( 'Access denied.', 403 );
|
||||
}
|
||||
|
||||
$server = $this->get_server( $server_id );
|
||||
if ( ! $server ) {
|
||||
wp_send_json_error( 'Server unavailable.' );
|
||||
}
|
||||
|
||||
// Parse IP from URL.
|
||||
$host = parse_url( $server->url, PHP_URL_HOST );
|
||||
|
||||
// External IP via aaPanel status endpoint.
|
||||
$api = WooAApanel_API::from_server( $server );
|
||||
$res = $api->get_server_info();
|
||||
|
||||
wp_send_json_success( [
|
||||
'server_name' => $server->name,
|
||||
'server_host' => $host,
|
||||
'panel_url' => $server->url,
|
||||
'panel_status' => $res['data'] ?? [],
|
||||
] );
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
// AJAX: Account – Databases
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public function ajax_wooaapanel_acct_dbs(): void {
|
||||
$this->account_verify();
|
||||
$dbs = $this->get_db_assignments( get_current_user_id() );
|
||||
wp_send_json_success( $dbs );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_db_backup(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$api = $this->api_for_db( $server_id, $db_name );
|
||||
wp_send_json( $api->backup_database( $db_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_db_backups(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$api = $this->api_for_db( $server_id, $db_name );
|
||||
wp_send_json( $api->get_db_backups( $db_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_db_backup_delete(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$backup_id = absint( $_POST['backup_id'] ?? 0 );
|
||||
$api = $this->api_for_db( $server_id, $db_name );
|
||||
wp_send_json( $api->delete_db_backup( $backup_id, $db_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_db_optimize(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$api = $this->api_for_db( $server_id, $db_name );
|
||||
wp_send_json( $api->optimize_table( $db_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_db_repair(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$api = $this->api_for_db( $server_id, $db_name );
|
||||
wp_send_json( $api->repair_table( $db_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_acct_db_password(): void {
|
||||
$this->account_verify();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$db_user = sanitize_text_field( $_POST['db_user'] ?? '' );
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if ( ! $db_user || ! $password ) {
|
||||
wp_send_json_error( 'Database user and new password are required.' );
|
||||
}
|
||||
|
||||
$api = $this->api_for_db( $server_id, $db_name );
|
||||
wp_send_json( $api->reset_db_password( $db_name, $db_user, $password ) );
|
||||
}
|
||||
}
|
||||
745
includes/class-wooaapanel-admin.php
Normal file
745
includes/class-wooaapanel-admin.php
Normal file
@@ -0,0 +1,745 @@
|
||||
<?php
|
||||
/**
|
||||
* WooAApanel Admin – menu pages and AJAX handlers.
|
||||
*/
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class WooAApanel_Admin {
|
||||
|
||||
public function __construct() {
|
||||
add_action( 'admin_menu', [ $this, 'register_menu' ] );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
|
||||
$actions = [
|
||||
// Servers
|
||||
'wooaapanel_servers_list',
|
||||
'wooaapanel_server_save',
|
||||
'wooaapanel_server_delete',
|
||||
'wooaapanel_server_test',
|
||||
// Site assignments
|
||||
'wooaapanel_site_assignments_list',
|
||||
'wooaapanel_site_assignment_save',
|
||||
'wooaapanel_site_assignment_delete',
|
||||
// DB assignments
|
||||
'wooaapanel_db_assignments_list',
|
||||
'wooaapanel_db_assignment_save',
|
||||
'wooaapanel_db_assignment_delete',
|
||||
// Customer search
|
||||
'wooaapanel_customers_search',
|
||||
// Remote site list
|
||||
'wooaapanel_remote_sites',
|
||||
'wooaapanel_remote_dbs',
|
||||
// === Admin site actions ===
|
||||
'wooaapanel_admin_site_list',
|
||||
'wooaapanel_admin_site_add',
|
||||
'wooaapanel_admin_site_delete',
|
||||
'wooaapanel_admin_site_domains',
|
||||
'wooaapanel_admin_site_add_domain',
|
||||
'wooaapanel_admin_site_del_domain',
|
||||
'wooaapanel_admin_site_xss_get',
|
||||
'wooaapanel_admin_site_xss_set',
|
||||
'wooaapanel_admin_site_php_get',
|
||||
'wooaapanel_admin_site_php_versions',
|
||||
'wooaapanel_admin_site_php_set',
|
||||
'wooaapanel_admin_site_rewrite_list',
|
||||
'wooaapanel_admin_site_rewrite_get',
|
||||
'wooaapanel_admin_site_rewrite_set',
|
||||
'wooaapanel_admin_site_ssl_get',
|
||||
'wooaapanel_admin_site_ssl_close',
|
||||
'wooaapanel_admin_site_ssl_upload',
|
||||
'wooaapanel_admin_site_ssl_deploy',
|
||||
'wooaapanel_admin_site_ssl_list',
|
||||
'wooaapanel_admin_site_run_path',
|
||||
// === Admin DB actions ===
|
||||
'wooaapanel_admin_db_list',
|
||||
'wooaapanel_admin_db_add',
|
||||
'wooaapanel_admin_db_delete',
|
||||
'wooaapanel_admin_db_backup',
|
||||
'wooaapanel_admin_db_backups',
|
||||
'wooaapanel_admin_db_backup_delete',
|
||||
'wooaapanel_admin_db_optimize',
|
||||
'wooaapanel_admin_db_repair',
|
||||
'wooaapanel_admin_db_access_get',
|
||||
'wooaapanel_admin_db_access_set',
|
||||
'wooaapanel_admin_db_password',
|
||||
'wooaapanel_admin_db_recycle',
|
||||
'wooaapanel_admin_db_restore',
|
||||
'wooaapanel_admin_db_sync',
|
||||
'wooaapanel_admin_db_quota',
|
||||
// WC product settings
|
||||
'wooaapanel_products_list',
|
||||
'wooaapanel_product_server_save',
|
||||
];
|
||||
|
||||
foreach ( $actions as $action ) {
|
||||
add_action( 'wp_ajax_' . $action, [ $this, 'ajax_' . $action ] );
|
||||
}
|
||||
}
|
||||
|
||||
// ── Menu ─────────────────────────────────────────────────────────────────
|
||||
|
||||
public function register_menu(): void {
|
||||
add_menu_page(
|
||||
'WooAApanel',
|
||||
'WooAApanel',
|
||||
'manage_woocommerce',
|
||||
'wooaapanel',
|
||||
[ $this, 'page_dashboard' ],
|
||||
'dashicons-admin-site-alt3',
|
||||
57
|
||||
);
|
||||
add_submenu_page( 'wooaapanel', 'Dashboard', 'Dashboard', 'manage_woocommerce', 'wooaapanel', [ $this, 'page_dashboard' ] );
|
||||
add_submenu_page( 'wooaapanel', 'Servers', 'Servers', 'manage_woocommerce', 'wooaapanel-servers', [ $this, 'page_servers' ] );
|
||||
add_submenu_page( 'wooaapanel', 'Site Assignments', 'Site Assignments', 'manage_woocommerce', 'wooaapanel-site-assignments',[ $this, 'page_site_assignments' ] );
|
||||
add_submenu_page( 'wooaapanel', 'DB Assignments', 'DB Assignments', 'manage_woocommerce', 'wooaapanel-db-assignments', [ $this, 'page_db_assignments' ] );
|
||||
add_submenu_page( 'wooaapanel', 'Sites', 'Sites', 'manage_woocommerce', 'wooaapanel-sites', [ $this, 'page_sites' ] );
|
||||
add_submenu_page( 'wooaapanel', 'Databases', 'Databases', 'manage_woocommerce', 'wooaapanel-databases', [ $this, 'page_databases' ] );
|
||||
add_submenu_page( 'wooaapanel', 'WC Products', 'WC Products', 'manage_woocommerce', 'wooaapanel-products', [ $this, 'page_products' ] );
|
||||
}
|
||||
|
||||
public function enqueue_assets( string $hook ): void {
|
||||
if ( strpos( $hook, 'wooaapanel' ) === false ) {
|
||||
return;
|
||||
}
|
||||
wp_enqueue_style( 'wooaapanel-admin', WOOAAPANEL_PLUGIN_URL . 'assets/css/wooaapanel-admin.css', [], WOOAAPANEL_VERSION );
|
||||
wp_enqueue_script( 'wooaapanel-admin', WOOAAPANEL_PLUGIN_URL . 'assets/js/wooaapanel-admin.js', [ 'jquery' ], WOOAAPANEL_VERSION, true );
|
||||
wp_localize_script( 'wooaapanel-admin', 'wooaapanel', [
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'wooaapanel_admin' ),
|
||||
] );
|
||||
}
|
||||
|
||||
// ── Pages ────────────────────────────────────────────────────────────────
|
||||
|
||||
public function page_dashboard(): void {
|
||||
echo '<div class="wrap"><h1>WooAApanel</h1>';
|
||||
echo '<p>' . esc_html__( 'Manage aaPanel servers, site and database assignments for your WooCommerce customers.', 'wooaapanel' ) . '</p>';
|
||||
echo '<ul>';
|
||||
$pages = [
|
||||
'wooaapanel-servers' => 'Servers',
|
||||
'wooaapanel-site-assignments' => 'Site Assignments',
|
||||
'wooaapanel-db-assignments' => 'DB Assignments',
|
||||
'wooaapanel-sites' => 'Sites',
|
||||
'wooaapanel-databases' => 'Databases',
|
||||
'wooaapanel-products' => 'WC Product Linking',
|
||||
];
|
||||
foreach ( $pages as $slug => $label ) {
|
||||
printf( '<li><a href="%s">%s</a></li>', esc_url( admin_url( 'admin.php?page=' . $slug ) ), esc_html( $label ) );
|
||||
}
|
||||
echo '</ul></div>';
|
||||
}
|
||||
|
||||
public function page_servers(): void {
|
||||
echo '<div class="wrap wooaapanel-page" id="wooaapanel-servers-page"><h1>' . esc_html__( 'aaPanel Servers', 'wooaapanel' ) . '</h1>';
|
||||
echo '<div id="wap-servers-wrap"></div></div>';
|
||||
}
|
||||
|
||||
public function page_site_assignments(): void {
|
||||
echo '<div class="wrap wooaapanel-page" id="wooaapanel-site-assignments-page"><h1>' . esc_html__( 'Site Assignments', 'wooaapanel' ) . '</h1>';
|
||||
echo '<div id="wap-site-assignments-wrap"></div></div>';
|
||||
}
|
||||
|
||||
public function page_db_assignments(): void {
|
||||
echo '<div class="wrap wooaapanel-page" id="wooaapanel-db-assignments-page"><h1>' . esc_html__( 'Database Assignments', 'wooaapanel' ) . '</h1>';
|
||||
echo '<div id="wap-db-assignments-wrap"></div></div>';
|
||||
}
|
||||
|
||||
public function page_sites(): void {
|
||||
echo '<div class="wrap wooaapanel-page" id="wooaapanel-sites-page"><h1>' . esc_html__( 'Sites', 'wooaapanel' ) . '</h1>';
|
||||
echo '<div id="wap-sites-wrap"></div></div>';
|
||||
}
|
||||
|
||||
public function page_databases(): void {
|
||||
echo '<div class="wrap wooaapanel-page" id="wooaapanel-databases-page"><h1>' . esc_html__( 'Databases', 'wooaapanel' ) . '</h1>';
|
||||
echo '<div id="wap-databases-wrap"></div></div>';
|
||||
}
|
||||
|
||||
public function page_products(): void {
|
||||
echo '<div class="wrap wooaapanel-page" id="wooaapanel-products-page"><h1>' . esc_html__( 'WC Product ↔ Server Linking', 'wooaapanel' ) . '</h1>';
|
||||
echo '<p>' . esc_html__( 'Link a WooCommerce product to an aaPanel server. When a customer purchases the product, a site and database will be automatically provisioned on the linked server.', 'wooaapanel' ) . '</p>';
|
||||
echo '<div id="wap-products-wrap"></div></div>';
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
private function verify_admin(): void {
|
||||
check_ajax_referer( 'wooaapanel_admin', 'nonce' );
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( 'Insufficient permissions.', 403 );
|
||||
}
|
||||
}
|
||||
|
||||
private function get_server( int $id ): ?object {
|
||||
global $wpdb;
|
||||
return $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}wooaapanel_servers WHERE id = %d",
|
||||
$id
|
||||
) );
|
||||
}
|
||||
|
||||
private function api( int $server_id ): WooAApanel_API {
|
||||
$server = $this->get_server( $server_id );
|
||||
if ( ! $server ) {
|
||||
wp_send_json_error( 'Server not found.', 404 );
|
||||
}
|
||||
return WooAApanel_API::from_server( $server );
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
// AJAX: Servers
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public function ajax_wooaapanel_servers_list(): void {
|
||||
$this->verify_admin();
|
||||
global $wpdb;
|
||||
$rows = $wpdb->get_results( "SELECT id, name, url, active, created_at FROM {$wpdb->prefix}wooaapanel_servers ORDER BY id DESC" );
|
||||
wp_send_json_success( $rows );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_server_save(): void {
|
||||
$this->verify_admin();
|
||||
|
||||
$id = absint( $_POST['id'] ?? 0 );
|
||||
$name = sanitize_text_field( $_POST['name'] ?? '' );
|
||||
$url = esc_url_raw( $_POST['url'] ?? '' );
|
||||
$api_key = sanitize_text_field( $_POST['api_key'] ?? '' );
|
||||
$active = absint( $_POST['active'] ?? 1 );
|
||||
|
||||
if ( ! $name || ! $url || ! $api_key ) {
|
||||
wp_send_json_error( 'Name, URL and API key are required.' );
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$data = compact( 'name', 'url', 'api_key', 'active' );
|
||||
|
||||
if ( $id ) {
|
||||
$wpdb->update( "{$wpdb->prefix}wooaapanel_servers", $data, [ 'id' => $id ] );
|
||||
} else {
|
||||
$wpdb->insert( "{$wpdb->prefix}wooaapanel_servers", $data );
|
||||
$id = $wpdb->insert_id;
|
||||
}
|
||||
|
||||
wp_send_json_success( [ 'id' => $id ] );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_server_delete(): void {
|
||||
$this->verify_admin();
|
||||
$id = absint( $_POST['id'] ?? 0 );
|
||||
global $wpdb;
|
||||
$wpdb->delete( "{$wpdb->prefix}wooaapanel_servers", [ 'id' => $id ] );
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_server_test(): void {
|
||||
$this->verify_admin();
|
||||
$id = absint( $_POST['id'] ?? 0 );
|
||||
$api = $this->api( $id );
|
||||
$res = $api->test_connection();
|
||||
wp_send_json( $res );
|
||||
}
|
||||
|
||||
// ── Remote site/db lists (used by assignment UI) ──────────────────────
|
||||
|
||||
public function ajax_wooaapanel_remote_sites(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$api = $this->api( $server_id );
|
||||
$res = $api->get_sites( 1, 200 );
|
||||
wp_send_json( $res );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_remote_dbs(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$api = $this->api( $server_id );
|
||||
$res = $api->get_databases( 1, 200 );
|
||||
wp_send_json( $res );
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
// AJAX: Site Assignments
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public function ajax_wooaapanel_site_assignments_list(): void {
|
||||
$this->verify_admin();
|
||||
global $wpdb;
|
||||
$rows = $wpdb->get_results( "
|
||||
SELECT a.id, a.customer_id, a.server_id, a.site_name, a.domain, a.created_at,
|
||||
s.name AS server_name,
|
||||
u.display_name AS customer_name, u.user_email AS customer_email
|
||||
FROM {$wpdb->prefix}wooaapanel_site_assignments a
|
||||
JOIN {$wpdb->prefix}wooaapanel_servers s ON s.id = a.server_id
|
||||
LEFT JOIN {$wpdb->users} u ON u.ID = a.customer_id
|
||||
ORDER BY a.id DESC
|
||||
" );
|
||||
wp_send_json_success( $rows );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_site_assignment_save(): void {
|
||||
$this->verify_admin();
|
||||
|
||||
$id = absint( $_POST['id'] ?? 0 );
|
||||
$customer_id = absint( $_POST['customer_id'] ?? 0 );
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
|
||||
|
||||
if ( ! $customer_id || ! $server_id || ! $site_name ) {
|
||||
wp_send_json_error( 'Customer, server and site name are required.' );
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$data = compact( 'customer_id', 'server_id', 'site_name', 'domain' );
|
||||
|
||||
if ( $id ) {
|
||||
$wpdb->update( "{$wpdb->prefix}wooaapanel_site_assignments", $data, [ 'id' => $id ] );
|
||||
} else {
|
||||
$wpdb->insert( "{$wpdb->prefix}wooaapanel_site_assignments", $data );
|
||||
$id = $wpdb->insert_id;
|
||||
}
|
||||
|
||||
wp_send_json_success( [ 'id' => $id ] );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_site_assignment_delete(): void {
|
||||
$this->verify_admin();
|
||||
$id = absint( $_POST['id'] ?? 0 );
|
||||
global $wpdb;
|
||||
$wpdb->delete( "{$wpdb->prefix}wooaapanel_site_assignments", [ 'id' => $id ] );
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
// AJAX: DB Assignments
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public function ajax_wooaapanel_db_assignments_list(): void {
|
||||
$this->verify_admin();
|
||||
global $wpdb;
|
||||
$rows = $wpdb->get_results( "
|
||||
SELECT a.id, a.customer_id, a.server_id, a.db_name, a.created_at,
|
||||
s.name AS server_name,
|
||||
u.display_name AS customer_name, u.user_email AS customer_email
|
||||
FROM {$wpdb->prefix}wooaapanel_db_assignments a
|
||||
JOIN {$wpdb->prefix}wooaapanel_servers s ON s.id = a.server_id
|
||||
LEFT JOIN {$wpdb->users} u ON u.ID = a.customer_id
|
||||
ORDER BY a.id DESC
|
||||
" );
|
||||
wp_send_json_success( $rows );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_db_assignment_save(): void {
|
||||
$this->verify_admin();
|
||||
|
||||
$id = absint( $_POST['id'] ?? 0 );
|
||||
$customer_id = absint( $_POST['customer_id'] ?? 0 );
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
|
||||
if ( ! $customer_id || ! $server_id || ! $db_name ) {
|
||||
wp_send_json_error( 'Customer, server and database name are required.' );
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$data = compact( 'customer_id', 'server_id', 'db_name' );
|
||||
|
||||
if ( $id ) {
|
||||
$wpdb->update( "{$wpdb->prefix}wooaapanel_db_assignments", $data, [ 'id' => $id ] );
|
||||
} else {
|
||||
$wpdb->insert( "{$wpdb->prefix}wooaapanel_db_assignments", $data );
|
||||
$id = $wpdb->insert_id;
|
||||
}
|
||||
|
||||
wp_send_json_success( [ 'id' => $id ] );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_db_assignment_delete(): void {
|
||||
$this->verify_admin();
|
||||
$id = absint( $_POST['id'] ?? 0 );
|
||||
global $wpdb;
|
||||
$wpdb->delete( "{$wpdb->prefix}wooaapanel_db_assignments", [ 'id' => $id ] );
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
// AJAX: Customer search
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public function ajax_wooaapanel_customers_search(): void {
|
||||
$this->verify_admin();
|
||||
|
||||
$q = sanitize_text_field( $_POST['q'] ?? '' );
|
||||
$users = get_users( [
|
||||
'role__in' => [ 'customer', 'subscriber', 'administrator' ],
|
||||
'search' => '*' . $q . '*',
|
||||
'number' => 20,
|
||||
'fields' => [ 'ID', 'display_name', 'user_email' ],
|
||||
] );
|
||||
|
||||
wp_send_json_success( array_map( fn( $u ) => [
|
||||
'id' => $u->ID,
|
||||
'label' => $u->display_name . ' <' . $u->user_email . '>',
|
||||
], $users ) );
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
// AJAX: Admin site actions (ALL operations)
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public function ajax_wooaapanel_admin_site_list(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$search = sanitize_text_field( $_POST['search'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->get_sites( 1, 100, $search ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_add(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$api = $this->api( $server_id );
|
||||
$data = [
|
||||
'webname' => sanitize_text_field( $_POST['webname'] ?? '' ),
|
||||
'path' => sanitize_text_field( $_POST['path'] ?? '' ),
|
||||
'type_id' => absint( $_POST['type_id'] ?? 0 ),
|
||||
'version' => sanitize_text_field( $_POST['version'] ?? '' ),
|
||||
'port' => absint( $_POST['port'] ?? 80 ),
|
||||
'ps' => sanitize_text_field( $_POST['ps'] ?? '' ),
|
||||
'ftp_username' => sanitize_text_field( $_POST['ftp_username'] ?? '' ),
|
||||
'ftp_password' => sanitize_text_field( $_POST['ftp_password'] ?? '' ),
|
||||
'sql' => sanitize_text_field( $_POST['sql'] ?? '' ),
|
||||
'codeing' => sanitize_text_field( $_POST['codeing'] ?? 'utf8mb4' ),
|
||||
'datauser' => sanitize_text_field( $_POST['datauser'] ?? '' ),
|
||||
'datapassword' => sanitize_text_field( $_POST['datapassword'] ?? '' ),
|
||||
];
|
||||
wp_send_json( $api->add_site( $data ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_delete(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$ftp = ! empty( $_POST['ftp'] );
|
||||
$database = ! empty( $_POST['database'] );
|
||||
$path = ! empty( $_POST['path'] );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->delete_site( $site_name, $ftp, $database, $path ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_domains(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->get_site_domains( $site_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_add_domain(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->add_domain( $site_name, $domain ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_del_domain(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
|
||||
$site_id = absint( $_POST['site_id'] ?? 0 );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->delete_domain( $site_name, $domain, $site_id ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_xss_get(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$site_path = sanitize_text_field( $_POST['site_path'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->get_xss( $site_name, $site_path ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_xss_set(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$site_path = sanitize_text_field( $_POST['site_path'] ?? '' );
|
||||
$enable = ! empty( $_POST['enable'] );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->set_xss( $site_name, $site_path, $enable ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_php_get(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->get_site_php_version( $site_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_php_versions(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->get_php_versions() );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_php_set(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$version = sanitize_text_field( $_POST['version'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->set_site_php_version( $site_name, $version ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_rewrite_list(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->get_rewrite_list( $site_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_rewrite_get(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$template = sanitize_text_field( $_POST['template'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->get_rewrite_content( $site_name, $template ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_rewrite_set(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$content = wp_unslash( $_POST['content'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->set_rewrite( $site_name, $content ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_ssl_get(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->get_ssl( $site_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_ssl_close(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->close_ssl( $site_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_ssl_list(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->list_ssl_certs() );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_ssl_upload(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$key = wp_unslash( $_POST['key'] ?? '' );
|
||||
$cert = wp_unslash( $_POST['cert'] ?? '' );
|
||||
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->upload_cert( $key, $cert, $domain ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_ssl_deploy(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$cert_id = absint( $_POST['cert_id'] ?? 0 );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->deploy_cert_to_site( $site_name, $cert_id ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_site_run_path(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$site_name = sanitize_text_field( $_POST['site_name'] ?? '' );
|
||||
$run_path = sanitize_text_field( $_POST['run_path'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->set_run_path( $site_name, $run_path ) );
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
// AJAX: Admin DB actions (ALL operations)
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public function ajax_wooaapanel_admin_db_list(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$search = sanitize_text_field( $_POST['search'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->get_databases( 1, 100, $search ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_add(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$db_user = sanitize_text_field( $_POST['db_user'] ?? '' );
|
||||
$password = sanitize_text_field( $_POST['password'] ?? '' );
|
||||
$codeing = sanitize_text_field( $_POST['codeing'] ?? 'utf8mb4' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->add_database( $db_name, $db_user, $password, $codeing ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_delete(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->delete_database( $db_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_backup(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->backup_database( $db_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_backups(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->get_db_backups( $db_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_backup_delete(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$backup_id = absint( $_POST['backup_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->delete_db_backup( $backup_id, $db_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_optimize(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->optimize_table( $db_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_repair(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->repair_table( $db_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_access_get(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->get_db_access( $db_name ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_access_set(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$access = sanitize_text_field( $_POST['access'] ?? '%' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->set_db_access( $db_name, $access ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_password(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$db_user = sanitize_text_field( $_POST['db_user'] ?? '' );
|
||||
$password = sanitize_text_field( $_POST['password'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->reset_db_password( $db_name, $db_user, $password ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_recycle(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->get_recycle_bin() );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_restore(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$path = sanitize_text_field( $_POST['path'] ?? '' );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->restore_from_recycle( $path ) );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_sync(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->sync_to_databases() );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_admin_db_quota(): void {
|
||||
$this->verify_admin();
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$db_name = sanitize_text_field( $_POST['db_name'] ?? '' );
|
||||
$quota_mb = absint( $_POST['quota_mb'] ?? 0 );
|
||||
$api = $this->api( $server_id );
|
||||
wp_send_json( $api->modify_db_quota( $db_name, $quota_mb ) );
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
// AJAX: WC Product server linking
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
|
||||
public function ajax_wooaapanel_products_list(): void {
|
||||
$this->verify_admin();
|
||||
|
||||
$products = wc_get_products( [ 'limit' => 200, 'status' => 'publish' ] );
|
||||
$servers = $this->get_all_servers();
|
||||
|
||||
$rows = [];
|
||||
foreach ( $products as $product ) {
|
||||
$rows[] = [
|
||||
'id' => $product->get_id(),
|
||||
'name' => $product->get_name(),
|
||||
'server_id' => (int) get_post_meta( $product->get_id(), '_wooaapanel_server_id', true ),
|
||||
];
|
||||
}
|
||||
|
||||
wp_send_json_success( [
|
||||
'products' => $rows,
|
||||
'servers' => $servers,
|
||||
] );
|
||||
}
|
||||
|
||||
public function ajax_wooaapanel_product_server_save(): void {
|
||||
$this->verify_admin();
|
||||
$product_id = absint( $_POST['product_id'] ?? 0 );
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
update_post_meta( $product_id, '_wooaapanel_server_id', $server_id );
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
// ── Internal helpers ──────────────────────────────────────────────────
|
||||
|
||||
private function get_all_servers(): array {
|
||||
global $wpdb;
|
||||
return $wpdb->get_results( "SELECT id, name FROM {$wpdb->prefix}wooaapanel_servers WHERE active = 1 ORDER BY name" );
|
||||
}
|
||||
}
|
||||
399
includes/class-wooaapanel-api.php
Normal file
399
includes/class-wooaapanel-api.php
Normal 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;
|
||||
}
|
||||
}
|
||||
56
includes/class-wooaapanel-installer.php
Normal file
56
includes/class-wooaapanel-installer.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class WooAApanel_Installer {
|
||||
|
||||
public static function install(): void {
|
||||
global $wpdb;
|
||||
|
||||
$charset = $wpdb->get_charset_collate();
|
||||
|
||||
// aaPanel server instances.
|
||||
$servers_sql = "CREATE TABLE {$wpdb->prefix}wooaapanel_servers (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
url VARCHAR(500) NOT NULL,
|
||||
api_key VARCHAR(500) NOT NULL,
|
||||
active TINYINT(1) NOT NULL DEFAULT 1,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id)
|
||||
) $charset;";
|
||||
|
||||
// Site (PHP project) assignments per customer.
|
||||
$sites_sql = "CREATE TABLE {$wpdb->prefix}wooaapanel_site_assignments (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
customer_id BIGINT NOT NULL,
|
||||
server_id INT NOT NULL,
|
||||
site_name VARCHAR(255) NOT NULL,
|
||||
domain VARCHAR(255) NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_customer (customer_id),
|
||||
KEY idx_server (server_id),
|
||||
UNIQUE KEY uniq_site (server_id, site_name)
|
||||
) $charset;";
|
||||
|
||||
// Database assignments per customer.
|
||||
$dbs_sql = "CREATE TABLE {$wpdb->prefix}wooaapanel_db_assignments (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
customer_id BIGINT NOT NULL,
|
||||
server_id INT NOT NULL,
|
||||
db_name VARCHAR(255) NOT NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_customer (customer_id),
|
||||
KEY idx_server (server_id),
|
||||
UNIQUE KEY uniq_db (server_id, db_name)
|
||||
) $charset;";
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
dbDelta( $servers_sql );
|
||||
dbDelta( $sites_sql );
|
||||
dbDelta( $dbs_sql );
|
||||
|
||||
update_option( 'wooaapanel_db_version', WOOAAPANEL_VERSION );
|
||||
}
|
||||
}
|
||||
124
includes/class-wooaapanel-orders.php
Normal file
124
includes/class-wooaapanel-orders.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
/**
|
||||
* WooAApanel Orders – auto-provision sites and databases when a WooCommerce
|
||||
* order is completed for a product linked to an aaPanel server.
|
||||
*
|
||||
* Product meta:
|
||||
* _wooaapanel_server_id (int) – which server to provision on
|
||||
*/
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class WooAApanel_Orders {
|
||||
|
||||
public function __construct() {
|
||||
add_action( 'woocommerce_order_status_completed', [ $this, 'provision_on_complete' ], 10, 1 );
|
||||
}
|
||||
|
||||
public function provision_on_complete( int $order_id ): void {
|
||||
$order = wc_get_order( $order_id );
|
||||
if ( ! $order ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$customer_id = $order->get_customer_id();
|
||||
if ( ! $customer_id ) {
|
||||
return; // guest orders – skip
|
||||
}
|
||||
|
||||
foreach ( $order->get_items() as $item ) {
|
||||
$product_id = $item->get_product_id();
|
||||
$server_id = (int) get_post_meta( $product_id, '_wooaapanel_server_id', true );
|
||||
|
||||
if ( ! $server_id ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Avoid double-provisioning if order is re-completed.
|
||||
$provisioned_key = "_wooaapanel_provisioned_{$product_id}";
|
||||
if ( $order->get_meta( $provisioned_key ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$server = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}wooaapanel_servers WHERE id = %d AND active = 1",
|
||||
$server_id
|
||||
) );
|
||||
|
||||
if ( ! $server ) {
|
||||
$order->add_order_note( sprintf(
|
||||
__( 'WooAApanel: server #%d not found or inactive – skipping provisioning for product #%d.', 'wooaapanel' ),
|
||||
$server_id,
|
||||
$product_id
|
||||
) );
|
||||
continue;
|
||||
}
|
||||
|
||||
$user = get_user_by( 'id', $customer_id );
|
||||
$username = $user ? sanitize_user( $user->user_login, true ) : 'user' . $customer_id;
|
||||
// Sanitise to aaPanel-safe names (alphanumeric + underscore, max 16 chars).
|
||||
$safe_name = preg_replace( '/[^a-z0-9_]/', '', strtolower( $username ) );
|
||||
$safe_name = substr( $safe_name ?: 'user', 0, 12 );
|
||||
$suffix = substr( md5( $order_id . $product_id ), 0, 4 );
|
||||
$site_name = $safe_name . '_' . $suffix;
|
||||
$db_name = $safe_name . '_' . $suffix;
|
||||
$db_user = $safe_name . '_' . $suffix;
|
||||
$db_pass = wp_generate_password( 16, false );
|
||||
|
||||
$api = WooAApanel_API::from_server( $server );
|
||||
|
||||
// Create site.
|
||||
$site_res = $api->add_site( [
|
||||
'webname' => $site_name,
|
||||
'path' => "/www/wwwroot/{$site_name}",
|
||||
'type_id' => 0,
|
||||
'version' => '',
|
||||
'port' => 80,
|
||||
'ps' => "WooCommerce order #{$order_id}",
|
||||
'ftp_username' => '',
|
||||
'ftp_password' => '',
|
||||
'sql' => 'MySQL',
|
||||
'codeing' => 'utf8mb4',
|
||||
'datauser' => $db_user,
|
||||
'datapassword' => $db_pass,
|
||||
] );
|
||||
|
||||
$site_ok = ! empty( $site_res['data']['siteStatus'] ) || ( $site_res['success'] ?? false );
|
||||
|
||||
if ( $site_ok ) {
|
||||
// Record site assignment.
|
||||
$wpdb->insert( "{$wpdb->prefix}wooaapanel_site_assignments", [
|
||||
'customer_id' => $customer_id,
|
||||
'server_id' => $server_id,
|
||||
'site_name' => $site_name,
|
||||
'domain' => '',
|
||||
] );
|
||||
|
||||
// Record DB assignment.
|
||||
$wpdb->insert( "{$wpdb->prefix}wooaapanel_db_assignments", [
|
||||
'customer_id' => $customer_id,
|
||||
'server_id' => $server_id,
|
||||
'db_name' => $db_name,
|
||||
] );
|
||||
|
||||
$order->update_meta_data( $provisioned_key, '1' );
|
||||
$order->add_order_note( sprintf(
|
||||
__( 'WooAApanel: provisioned site "%s" and database "%s" on server "%s".', 'wooaapanel' ),
|
||||
$site_name,
|
||||
$db_name,
|
||||
$server->name
|
||||
) );
|
||||
} else {
|
||||
$error = $site_res['error'] ?? wp_json_encode( $site_res['data'] ?? [] );
|
||||
$order->add_order_note( sprintf(
|
||||
__( 'WooAApanel: provisioning failed for product #%d on server "%s": %s', 'wooaapanel' ),
|
||||
$product_id,
|
||||
$server->name,
|
||||
$error
|
||||
) );
|
||||
}
|
||||
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
45
wooaapanel.php
Normal file
45
wooaapanel.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: WooAApanel
|
||||
* Description: aaPanel web hosting management for WooCommerce customers. Manage sites, databases and DNS records.
|
||||
* Version: 1.0.0
|
||||
* Author: WooAApanel
|
||||
* Text Domain: wooaapanel
|
||||
* Requires at least: 6.2
|
||||
* Requires PHP: 8.1
|
||||
* WC requires at least: 7.0
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
define( 'WOOAAPANEL_VERSION', '1.0.0' );
|
||||
define( 'WOOAAPANEL_PLUGIN_FILE', __FILE__ );
|
||||
define( 'WOOAAPANEL_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||
define( 'WOOAAPANEL_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
||||
|
||||
require_once WOOAAPANEL_PLUGIN_DIR . 'includes/class-wooaapanel-installer.php';
|
||||
require_once WOOAAPANEL_PLUGIN_DIR . 'includes/class-wooaapanel-api.php';
|
||||
require_once WOOAAPANEL_PLUGIN_DIR . 'includes/class-wooaapanel-admin.php';
|
||||
require_once WOOAAPANEL_PLUGIN_DIR . 'includes/class-wooaapanel-account.php';
|
||||
require_once WOOAAPANEL_PLUGIN_DIR . 'includes/class-wooaapanel-orders.php';
|
||||
|
||||
register_activation_hook( __FILE__, [ 'WooAApanel_Installer', 'install' ] );
|
||||
|
||||
add_action( 'init', function () {
|
||||
load_plugin_textdomain( 'wooaapanel', false, dirname( plugin_basename( WOOAAPANEL_PLUGIN_FILE ) ) . '/languages' );
|
||||
} );
|
||||
|
||||
add_action( 'plugins_loaded', function () {
|
||||
if ( ! class_exists( 'WooCommerce' ) ) {
|
||||
add_action( 'admin_notices', function () {
|
||||
printf(
|
||||
'<div class="notice notice-error"><p>%s</p></div>',
|
||||
esc_html__( 'WooAApanel requires WooCommerce to be installed and active.', 'wooaapanel' )
|
||||
);
|
||||
} );
|
||||
return;
|
||||
}
|
||||
new WooAApanel_Admin();
|
||||
new WooAApanel_Account();
|
||||
new WooAApanel_Orders();
|
||||
} );
|
||||
Reference in New Issue
Block a user