Files
WooAApanel/assets/js/wooaapanel-account.js
Malin 8bb96b9048 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>
2026-03-06 12:48:06 +01:00

390 lines
19 KiB
JavaScript

/**
* 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,'&amp;').replace(/</g,'&lt;')
.replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
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);