294 lines
12 KiB
Perl
294 lines
12 KiB
Perl
#!/usr/local/cpanel/3rdparty/bin/perl
|
|
# gniza WHM Plugin — Main Config Editor
|
|
use strict;
|
|
use warnings;
|
|
|
|
use lib '/usr/local/cpanel/whostmgr/docroot/cgi/gniza-whm/lib';
|
|
|
|
use Whostmgr::HTMLInterface ();
|
|
use Cpanel::Form ();
|
|
use GnizaWHM::Config;
|
|
use GnizaWHM::Validator;
|
|
use GnizaWHM::UI;
|
|
|
|
my $CONFIG_FILE = '/etc/gniza/gniza.conf';
|
|
my $form = Cpanel::Form::parseform();
|
|
my $method = $ENV{'REQUEST_METHOD'} // 'GET';
|
|
my $action = $form->{'action'} // '';
|
|
|
|
# ── Handle SMTP Test (JSON) ──────────────────────────────────
|
|
|
|
if ($action eq 'test_smtp') {
|
|
print "Content-Type: application/json\r\n\r\n";
|
|
|
|
my $host = $form->{'SMTP_HOST'} // '';
|
|
my $port = $form->{'SMTP_PORT'} || '587';
|
|
my $user = $form->{'SMTP_USER'} // '';
|
|
my $password = $form->{'SMTP_PASSWORD'} // '';
|
|
my $from = $form->{'SMTP_FROM'} // '';
|
|
my $security = $form->{'SMTP_SECURITY'} || 'tls';
|
|
my $to = $form->{'NOTIFY_EMAIL'} // '';
|
|
|
|
if ($host eq '') {
|
|
print qq({"success":false,"message":"SMTP Host is required."});
|
|
exit;
|
|
}
|
|
if ($to eq '') {
|
|
print qq({"success":false,"message":"Notification email is required for test."});
|
|
exit;
|
|
}
|
|
|
|
my ($ok, $err) = GnizaWHM::UI::test_smtp_connection(
|
|
host => $host,
|
|
port => $port,
|
|
user => $user,
|
|
password => $password,
|
|
from => $from,
|
|
security => $security,
|
|
to => $to,
|
|
);
|
|
if ($ok) {
|
|
print qq({"success":true,"message":"Test email sent successfully. Check your inbox."});
|
|
} else {
|
|
$err //= 'Unknown error';
|
|
$err =~ s/\\/\\\\/g;
|
|
$err =~ s/"/\\"/g;
|
|
$err =~ s/\n/\\n/g;
|
|
$err =~ s/\r/\\r/g;
|
|
$err =~ s/\t/\\t/g;
|
|
$err =~ s/[\x00-\x1f]//g;
|
|
print qq({"success":false,"message":"SMTP test failed: $err"});
|
|
}
|
|
exit;
|
|
}
|
|
|
|
# ── Handle POST ──────────────────────────────────────────────
|
|
|
|
my @errors;
|
|
my $saved = 0;
|
|
|
|
if ($method eq 'POST') {
|
|
unless (GnizaWHM::UI::verify_csrf_token($form->{'gniza_csrf'})) {
|
|
push @errors, 'Invalid or expired form token. Please try again.';
|
|
}
|
|
|
|
if (!@errors) {
|
|
my %data;
|
|
for my $key (@GnizaWHM::Config::MAIN_KEYS) {
|
|
$data{$key} = $form->{$key} // '';
|
|
}
|
|
|
|
my $validation_errors = GnizaWHM::Validator::validate_main_config(\%data);
|
|
|
|
if (@$validation_errors) {
|
|
@errors = @$validation_errors;
|
|
} else {
|
|
my ($ok, $err) = GnizaWHM::Config::write($CONFIG_FILE, \%data, \@GnizaWHM::Config::MAIN_KEYS);
|
|
if ($ok) {
|
|
GnizaWHM::UI::set_flash('success', 'Configuration saved successfully.');
|
|
print "Status: 302 Found\r\n";
|
|
print "Location: settings.cgi\r\n\r\n";
|
|
exit;
|
|
} else {
|
|
push @errors, "Failed to save config: $err";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# ── Render Page ──────────────────────────────────────────────
|
|
|
|
print "Content-Type: text/html\r\n\r\n";
|
|
|
|
Whostmgr::HTMLInterface::defheader('GNIZA Backup Manager — Settings', '', '/cgi/gniza-whm/settings.cgi');
|
|
|
|
print GnizaWHM::UI::page_header('Settings');
|
|
print GnizaWHM::UI::render_nav('settings.cgi');
|
|
print GnizaWHM::UI::render_flash();
|
|
|
|
if (@errors) {
|
|
print GnizaWHM::UI::render_errors(\@errors);
|
|
}
|
|
|
|
# Load current config (or use POST data if validation failed)
|
|
my $conf;
|
|
if (@errors && $method eq 'POST') {
|
|
$conf = {};
|
|
for my $key (@GnizaWHM::Config::MAIN_KEYS) {
|
|
$conf->{$key} = $form->{$key} // '';
|
|
}
|
|
} else {
|
|
$conf = GnizaWHM::Config::parse($CONFIG_FILE, 'main');
|
|
}
|
|
|
|
# Helper to output a text field row
|
|
sub field_text {
|
|
my ($key, $label, $hint, $extra) = @_;
|
|
$extra //= '';
|
|
my $val = GnizaWHM::UI::esc($conf->{$key} // '');
|
|
my $hint_html = $hint ? qq{ <span class="text-xs text-base-content/60 ml-2">$hint</span>} : '';
|
|
print qq{<div class="flex items-center gap-3 mb-2.5">\n};
|
|
print qq{ <label class="w-44 font-medium text-sm" for="$key">$label</label>\n};
|
|
print qq{ <input type="text" class="input input-bordered input-sm w-full max-w-xs" id="$key" name="$key" value="$val" $extra>\n};
|
|
print qq{ $hint_html\n} if $hint;
|
|
print qq{</div>\n};
|
|
}
|
|
|
|
# Helper to output a select field row
|
|
sub field_select {
|
|
my ($key, $label, $options_ref) = @_;
|
|
my $current = $conf->{$key} // '';
|
|
print qq{<div class="flex items-center gap-3 mb-2.5">\n};
|
|
print qq{ <label class="w-44 font-medium text-sm" for="$key">$label</label>\n};
|
|
print qq{ <select class="select select-bordered select-sm w-full max-w-xs" id="$key" name="$key">\n};
|
|
for my $opt (@$options_ref) {
|
|
my $sel = ($current eq $opt) ? ' selected' : '';
|
|
my $esc_opt = GnizaWHM::UI::esc($opt);
|
|
print qq{ <option value="$esc_opt"$sel>$esc_opt</option>\n};
|
|
}
|
|
print qq{ </select>\n};
|
|
print qq{</div>\n};
|
|
}
|
|
|
|
# ── Form ─────────────────────────────────────────────────────
|
|
|
|
print qq{<form method="POST" action="settings.cgi">\n};
|
|
print GnizaWHM::UI::csrf_hidden_field();
|
|
|
|
# Section: Local Settings
|
|
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
|
|
print qq{<h2 class="card-title text-sm">Local Settings</h2>\n};
|
|
field_text('TEMP_DIR', 'Working Directory', 'Default: /usr/local/gniza/workdir');
|
|
print qq{</div>\n</div>\n};
|
|
|
|
# Section: Account Filtering
|
|
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
|
|
print qq{<h2 class="card-title text-sm">Account Filtering</h2>\n};
|
|
my $inc_val = GnizaWHM::UI::esc($conf->{INCLUDE_ACCOUNTS} // '');
|
|
my $exc_val = GnizaWHM::UI::esc($conf->{EXCLUDE_ACCOUNTS} // '');
|
|
print qq{<div class="flex items-center gap-3 mb-2.5">\n};
|
|
print qq{ <label class="w-44 font-medium text-sm" for="INCLUDE_ACCOUNTS">Include Accounts</label>\n};
|
|
print qq{ <textarea class="textarea textarea-bordered textarea-sm w-full max-w-xs" id="INCLUDE_ACCOUNTS" name="INCLUDE_ACCOUNTS" placeholder="Comma-separated, empty = all">$inc_val</textarea>\n};
|
|
print qq{</div>\n};
|
|
print qq{<div class="flex items-center gap-3 mb-2.5">\n};
|
|
print qq{ <label class="w-44 font-medium text-sm" for="EXCLUDE_ACCOUNTS">Exclude Accounts</label>\n};
|
|
print qq{ <textarea class="textarea textarea-bordered textarea-sm w-full max-w-xs" id="EXCLUDE_ACCOUNTS" name="EXCLUDE_ACCOUNTS" placeholder="Comma-separated">$exc_val</textarea>\n};
|
|
print qq{</div>\n};
|
|
|
|
my @accounts = GnizaWHM::UI::get_cpanel_accounts();
|
|
if (@accounts) {
|
|
print qq{<div class="text-xs text-base-content/60 mt-2">};
|
|
print qq{Available accounts: } . GnizaWHM::UI::esc(join(', ', @accounts));
|
|
print qq{</div>\n};
|
|
}
|
|
print qq{</div>\n</div>\n};
|
|
|
|
# Section: Logging
|
|
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
|
|
print qq{<h2 class="card-title text-sm">Logging</h2>\n};
|
|
field_text('LOG_DIR', 'Log Directory', 'Default: /var/log/gniza');
|
|
field_select('LOG_LEVEL', 'Log Level', ['debug', 'info', 'warn', 'error']);
|
|
field_text('LOG_RETAIN', 'Log Retention (days)', 'Default: 90');
|
|
print qq{</div>\n</div>\n};
|
|
|
|
# Section: Notifications
|
|
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
|
|
print qq{<h2 class="card-title text-sm">Notifications</h2>\n};
|
|
field_text('NOTIFY_EMAIL', 'Email Address(es)', 'Comma-separated, empty = disabled');
|
|
field_select('NOTIFY_ON', 'Notify On', ['always', 'failure', 'never']);
|
|
print qq{</div>\n</div>\n};
|
|
|
|
# Section: SMTP Settings
|
|
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
|
|
print qq{<h2 class="card-title text-sm">SMTP Settings</h2>\n};
|
|
print qq{<p class="text-xs text-base-content/60 mb-3">Optional. When SMTP Host is empty, system mail/sendmail is used.</p>\n};
|
|
field_text('SMTP_HOST', 'SMTP Host', 'e.g. smtp.gmail.com');
|
|
field_text('SMTP_PORT', 'SMTP Port', 'Default: 587');
|
|
field_text('SMTP_USER', 'SMTP Username', 'e.g. user@gmail.com');
|
|
|
|
# SMTP Password (type=password)
|
|
my $smtp_pw_val = GnizaWHM::UI::esc($conf->{SMTP_PASSWORD} // '');
|
|
print qq{<div class="flex items-center gap-3 mb-2.5">\n};
|
|
print qq{ <label class="w-44 font-medium text-sm" for="SMTP_PASSWORD">SMTP Password</label>\n};
|
|
print qq{ <input type="password" class="input input-bordered input-sm w-full max-w-xs" id="SMTP_PASSWORD" name="SMTP_PASSWORD" value="$smtp_pw_val">\n};
|
|
print qq{</div>\n};
|
|
|
|
field_text('SMTP_FROM', 'From Address', 'Falls back to SMTP Username');
|
|
field_select('SMTP_SECURITY', 'Security', ['tls', 'ssl', 'none']);
|
|
|
|
print qq{<div class="flex gap-2 mt-3">\n};
|
|
print qq{ <button type="button" class="btn btn-secondary btn-sm" id="test-smtp-btn" onclick="gnizaTestSmtp()">Send Test Email</button>\n};
|
|
print qq{</div>\n};
|
|
print qq{<div id="gniza-smtp-alert" class="mt-3"></div>\n};
|
|
print qq{</div>\n</div>\n};
|
|
|
|
# Section: Advanced
|
|
print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
|
|
print qq{<h2 class="card-title text-sm">Advanced</h2>\n};
|
|
field_text('LOCK_FILE', 'Lock File', 'Default: /var/run/gniza.lock');
|
|
field_text('SSH_TIMEOUT', 'SSH Timeout (seconds)', 'Default: 30');
|
|
field_text('SSH_RETRIES', 'SSH Retries', 'Default: 3');
|
|
field_text('RSYNC_EXTRA_OPTS', 'Extra rsync Options', 'Additional flags for rsync');
|
|
print qq{</div>\n</div>\n};
|
|
|
|
# Submit
|
|
print qq{<div class="flex items-center gap-2 mt-4">\n};
|
|
print qq{ <button type="submit" class="btn btn-primary btn-sm">Save Settings</button>\n};
|
|
print qq{</div>\n};
|
|
|
|
print qq{</form>\n};
|
|
|
|
print <<'JS';
|
|
<script>
|
|
function gnizaTestSmtp() {
|
|
var btn = document.getElementById('test-smtp-btn');
|
|
var fd = new FormData();
|
|
fd.append('action', 'test_smtp');
|
|
fd.append('SMTP_HOST', document.getElementById('SMTP_HOST').value);
|
|
fd.append('SMTP_PORT', document.getElementById('SMTP_PORT').value);
|
|
fd.append('SMTP_USER', document.getElementById('SMTP_USER').value);
|
|
fd.append('SMTP_PASSWORD', document.getElementById('SMTP_PASSWORD').value);
|
|
fd.append('SMTP_FROM', document.getElementById('SMTP_FROM').value);
|
|
fd.append('SMTP_SECURITY', document.getElementById('SMTP_SECURITY').value);
|
|
fd.append('NOTIFY_EMAIL', document.getElementById('NOTIFY_EMAIL').value);
|
|
|
|
var email = document.getElementById('NOTIFY_EMAIL').value;
|
|
var host = document.getElementById('SMTP_HOST').value;
|
|
if (!host) { gnizaSmtpToast('error', 'SMTP Host is required.'); return; }
|
|
if (!email) { gnizaSmtpToast('error', 'Notification email is required for test.'); return; }
|
|
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<span class="loading loading-spinner loading-xs"></span> Sending\u2026';
|
|
|
|
fetch('settings.cgi', { method: 'POST', body: fd })
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
gnizaSmtpToast(data.success ? 'success' : 'error', data.message);
|
|
})
|
|
.catch(function(err) {
|
|
gnizaSmtpToast('error', 'Request failed: ' + err.toString());
|
|
})
|
|
.finally(function() {
|
|
btn.disabled = false;
|
|
btn.innerHTML = 'Send Test Email';
|
|
});
|
|
}
|
|
|
|
function gnizaSmtpToast(type, msg) {
|
|
var area = document.getElementById('gniza-smtp-alert');
|
|
if (!area) return;
|
|
area.innerHTML = '';
|
|
var el = document.createElement('div');
|
|
el.className = 'alert alert-' + type;
|
|
el.className += ' px-5 py-3 rounded-lg text-sm';
|
|
el.textContent = msg;
|
|
area.appendChild(el);
|
|
setTimeout(function() { el.style.opacity = '0'; }, type === 'error' ? 6000 : 3000);
|
|
setTimeout(function() { area.innerHTML = ''; }, type === 'error' ? 6500 : 3500);
|
|
}
|
|
</script>
|
|
JS
|
|
|
|
print GnizaWHM::UI::page_footer();
|
|
Whostmgr::HTMLInterface::footer();
|