Flask-based VPS management panel with SSH remote command execution. Includes E2E encrypted SSH tunnel (AES-256-GCM + Go agent), setup wizard, security hardening tools, DNS management, firewall configs, monitoring, backup, and .sec patch update system. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
517 lines
30 KiB
HTML
517 lines
30 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Setup Wizard{% endblock %}
|
|
{% block content %}
|
|
<h1>[+] Setup Wizard</h1>
|
|
|
|
<div id="wiz-steps" style="margin-bottom:15px;font-size:11px;color:#555">
|
|
<span id="ws-1" class="status-ok">[1] Terms</span> →
|
|
<span id="ws-2">[2] SSH Keys</span> →
|
|
<span id="ws-3">[3] VPS</span> →
|
|
<span id="ws-4">[4] API</span> →
|
|
<span id="ws-5">[5] Paths</span> →
|
|
<span id="ws-6">[6] Test</span>
|
|
</div>
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════ -->
|
|
<!-- Step 1: Terms / License -->
|
|
<!-- ═══════════════════════════════════════════════════════════════ -->
|
|
<div id="step-1" class="card">
|
|
<div class="card-title">License Agreement & Terms of Use</div>
|
|
<div style="font-size:12px;line-height:1.6;max-height:400px;overflow-y:auto;padding:10px;background:#000;border:1px solid #333;margin-bottom:15px">
|
|
<p style="color:#ff4444;margin-bottom:10px"><strong>IMPORTANT - READ BEFORE CONTINUING</strong></p>
|
|
|
|
<p style="margin-bottom:10px"><strong style="color:#ff4444">1. RESTRICTED USE LICENSE</strong><br>
|
|
This software is licensed for use by <strong>private individuals, independent security
|
|
researchers, and non-governmental organizations ONLY</strong>. By using this software you
|
|
affirm that you are not acting on behalf of, employed by, contracted by, or otherwise
|
|
affiliated with any:<br>
|
|
• <strong style="color:#ff4444">Law enforcement agency</strong> (local, state, federal, or international)<br>
|
|
• <strong style="color:#ff4444">Government agency or department</strong> (civilian or military)<br>
|
|
• <strong style="color:#ff4444">Intelligence service</strong> (domestic or foreign)<br>
|
|
• <strong style="color:#ff4444">Government contractor</strong> performing work for any of the above<br>
|
|
<br>
|
|
Use of this software by any of the above entities or their agents is <strong>strictly
|
|
prohibited</strong> and constitutes a violation of this license. This restriction applies
|
|
regardless of the purpose, including but not limited to: investigations, surveillance,
|
|
offensive operations, defensive operations, or "research." No exceptions.</p>
|
|
|
|
<p style="margin-bottom:10px"><strong style="color:#88ff88">2. NO WARRANTY</strong><br>
|
|
This software is provided "AS IS" without warranty of any kind, express or implied.
|
|
SETEC LABS makes no guarantees regarding reliability, availability, or fitness for
|
|
any particular purpose. Use at your own risk.</p>
|
|
|
|
<p style="margin-bottom:10px"><strong style="color:#88ff88">3. NO GUARANTEE OF SUPPORT</strong><br>
|
|
While the community may assist, there is no obligation to provide support, updates,
|
|
or bug fixes. This is free, open-source software maintained by volunteers.</p>
|
|
|
|
<p style="margin-bottom:10px"><strong style="color:#88ff88">4. LIMITATION OF LIABILITY</strong><br>
|
|
Under no circumstances shall SETEC LABS, its contributors, or the darkHal group be
|
|
liable for any direct, indirect, incidental, or consequential damages arising from
|
|
the use of this software. This includes but is not limited to: data loss, system
|
|
downtime, security breaches, or misconfiguration of your server.</p>
|
|
|
|
<p style="margin-bottom:10px"><strong style="color:#ff4444">5. IF YOU PAID FOR THIS SOFTWARE, YOU WERE SCAMMED</strong><br>
|
|
SETEC LABS Manager is <strong>100% free and open source</strong>. If someone charged you
|
|
money for this application, you were ripped off and likely received a version bundled
|
|
with malware. Delete it immediately.</p>
|
|
|
|
<p style="margin-bottom:10px"><strong style="color:#88ff88">6. OFFICIAL DOWNLOAD SOURCES</strong><br>
|
|
Only download SETEC applications from these trusted sources:<br>
|
|
• <span style="color:#00ff41">repo.seteclabs.io</span> (Official Gitea repository)<br>
|
|
• <span style="color:#00ff41">github.com/DigiJEth</span> (Official GitHub mirror)<br>
|
|
Any other source is unauthorized and potentially dangerous.</p>
|
|
|
|
<p style="margin-bottom:10px"><strong style="color:#88ff88">7. ROOT ACCESS WARNING</strong><br>
|
|
This software executes commands on remote servers via SSH with the privileges of the
|
|
configured user. Misconfiguration can result in data loss or security vulnerabilities.
|
|
You are solely responsible for the actions performed through this tool.</p>
|
|
|
|
<p style="color:#888;font-size:10px;margin-top:15px;border-top:1px solid #333;padding-top:10px">
|
|
SETEC LABS Manager • Free Software • darkHal Group • For the people, not the state.</p>
|
|
</div>
|
|
<div style="margin-bottom:10px">
|
|
<label style="display:inline;cursor:pointer">
|
|
<input type="checkbox" id="tos-accept" onchange="tosChanged()" style="margin-right:8px">
|
|
<span style="color:#ffaa00">I have read and accept these terms and I am not affiliated with any government or law enforcement entity</span>
|
|
</label>
|
|
</div>
|
|
<button class="btn" id="btn-tos-next" onclick="acceptTOS()" disabled style="opacity:0.3">Next →</button>
|
|
</div>
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════ -->
|
|
<!-- Step 2: SSH Key Selection/Generation -->
|
|
<!-- ═══════════════════════════════════════════════════════════════ -->
|
|
<div id="step-2" class="card" style="display:none">
|
|
<div class="card-title">SSH Key Setup</div>
|
|
|
|
<p style="font-size:12px;color:#888;margin-bottom:15px">
|
|
SETEC Manager connects to your VPS via SSH using key-based authentication.
|
|
Do you already have SSH keys generated?
|
|
</p>
|
|
|
|
<div style="margin-bottom:15px">
|
|
<button class="btn" id="btn-has-keys" onclick="sshKeyChoice('yes')" style="padding:10px 20px">
|
|
Yes, I have SSH keys
|
|
</button>
|
|
<button class="btn" id="btn-no-keys" onclick="sshKeyChoice('no')" style="padding:10px 20px">
|
|
No, I need to create them
|
|
</button>
|
|
</div>
|
|
|
|
<!-- YES: User has keys -->
|
|
<div id="ssh-has-keys" style="display:none">
|
|
<label>SSH Private Key Path</label>
|
|
<input type="text" id="w-key" style="width:100%" placeholder="C:/keys/setec">
|
|
<div style="font-size:10px;color:#555;margin:2px 3px 5px">
|
|
Enter the full path to your <strong style="color:#888">private</strong> key file (not the .pub file).
|
|
</div>
|
|
<div style="font-size:10px;color:#555;margin:2px 3px 10px">
|
|
Common locations:<br>
|
|
<span style="color:#00ff41;line-height:1.8">
|
|
• C:/Users/YourName/.ssh/id_ed25519<br>
|
|
• C:/Users/YourName/.ssh/id_rsa<br>
|
|
• C:/keys/setec<br>
|
|
• ~/.ssh/id_ed25519 (Linux/Mac)
|
|
</span>
|
|
</div>
|
|
<div style="margin-top:10px">
|
|
<button class="btn" onclick="goStep(1)">← Back</button>
|
|
<button class="btn" onclick="saveKeyAndContinue()">Save & Continue →</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- NO: User needs keys -->
|
|
<div id="ssh-no-keys" style="display:none">
|
|
<p style="font-size:12px;color:#ffaa00;margin-bottom:10px">
|
|
No problem! Select your VPS hosting provider below for a step-by-step guide.
|
|
</p>
|
|
|
|
<label>Select your VPS host</label>
|
|
<select id="w-ssh-host" style="width:100%" onchange="sshHostChanged()">
|
|
<option value="">-- Select Host --</option>
|
|
<option value="hostinger">Hostinger</option>
|
|
<option value="digitalocean">DigitalOcean</option>
|
|
<option value="vultr">Vultr</option>
|
|
<option value="linode">Linode (Akamai)</option>
|
|
<option value="hetzner">Hetzner</option>
|
|
<option value="ovh">OVH / OVHcloud</option>
|
|
<option value="aws">AWS (EC2)</option>
|
|
<option value="contabo">Contabo</option>
|
|
<option value="other">Other / Self-Hosted</option>
|
|
</select>
|
|
|
|
<div id="ssh-host-guide" style="display:none;margin-top:15px;padding:12px;background:#000;border:1px solid #333;font-size:11px;line-height:1.8"></div>
|
|
|
|
<div id="ssh-generic-guide" style="display:none;margin-top:15px;padding:12px;background:#000;border:1px solid #333">
|
|
<p style="color:#88ff88;font-size:12px;margin-bottom:8px"><strong>Generate SSH Keys (any platform)</strong></p>
|
|
<p style="font-size:11px;color:#888;margin-bottom:8px">Open a terminal and run:</p>
|
|
<div style="font-size:10px;margin-bottom:10px">
|
|
<p style="color:#888;margin-bottom:3px">1. Generate key pair:</p>
|
|
<code style="color:#00ff41">ssh-keygen -t ed25519 -f C:/keys/setec -N ""</code>
|
|
</div>
|
|
<div style="font-size:10px;margin-bottom:10px">
|
|
<p style="color:#888;margin-bottom:3px">2. Copy the public key to your server:</p>
|
|
<code style="color:#00ff41">ssh-copy-id -i C:/keys/setec.pub root@YOUR_SERVER_IP</code>
|
|
</div>
|
|
<div style="font-size:10px;margin-bottom:10px">
|
|
<p style="color:#888;margin-bottom:3px">3. Test the connection:</p>
|
|
<code style="color:#00ff41">ssh -i C:/keys/setec root@YOUR_SERVER_IP</code>
|
|
</div>
|
|
<p style="font-size:10px;color:#555;margin-top:8px">Your private key will be at <span style="color:#00ff41">C:/keys/setec</span></p>
|
|
</div>
|
|
|
|
<div style="margin-top:15px">
|
|
<label>Once your keys are ready, enter the private key path:</label>
|
|
<input type="text" id="w-key-new" style="width:100%" placeholder="C:/keys/setec" value="C:/keys/setec">
|
|
</div>
|
|
|
|
<div style="margin-top:10px">
|
|
<button class="btn" onclick="goStep(1)">← Back</button>
|
|
<button class="btn" onclick="saveNewKeyAndContinue()">Save & Continue →</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════ -->
|
|
<!-- Step 3: VPS Connection -->
|
|
<!-- ═══════════════════════════════════════════════════════════════ -->
|
|
<div id="step-3" class="card" style="display:none">
|
|
<div class="card-title">VPS Connection Setup</div>
|
|
|
|
<label>Server IP Address</label>
|
|
<input type="text" id="w-host" style="width:100%" placeholder="e.g. 192.168.1.100">
|
|
<div style="font-size:10px;color:#555;margin:2px 3px 10px">
|
|
Find your VPS IP in your hosting provider's control panel.
|
|
</div>
|
|
|
|
<label>SSH Username</label>
|
|
<input type="text" id="w-user" style="width:100%" placeholder="root" value="root">
|
|
<div style="font-size:10px;color:#555;margin:2px 3px 10px">
|
|
<strong style="color:#888">Recommended:</strong> Use <span style="color:#00ff41">root</span> or create a sudo user:<br>
|
|
<code style="color:#00ff41;font-size:10px">adduser setecadmin && usermod -aG sudo setecadmin</code><br>
|
|
<span style="color:#ffaa00">Important:</span> Disable password login after setting up SSH keys:<br>
|
|
<code style="color:#00ff41;font-size:10px">sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config && systemctl restart sshd</code>
|
|
</div>
|
|
|
|
<label>SSH Port</label>
|
|
<input type="number" id="w-port" style="width:100%" placeholder="2222" value="2222">
|
|
<div style="font-size:10px;color:#555;margin:2px 3px 10px">
|
|
<strong style="color:#ffaa00">We strongly recommend port 2222</strong> instead of default 22 to reduce brute-force attacks.<br>
|
|
<code style="color:#00ff41;font-size:10px">sed -i 's/^#*Port .*/Port 2222/' /etc/ssh/sshd_config && ufw allow 2222/tcp && systemctl restart sshd</code><br>
|
|
<span style="color:#ff4444">Do NOT close your current session until you verify the new port works!</span>
|
|
</div>
|
|
|
|
<div style="margin-top:10px">
|
|
<button class="btn" onclick="goStep(2)">← Back</button>
|
|
<button class="btn" onclick="saveVPS()">Save & Continue →</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════ -->
|
|
<!-- Step 4: API Setup -->
|
|
<!-- ═══════════════════════════════════════════════════════════════ -->
|
|
<div id="step-4" class="card" style="display:none">
|
|
<div class="card-title">DNS API Setup</div>
|
|
|
|
<label>Domain</label>
|
|
<input type="text" id="w-domain" style="width:100%" placeholder="example.com">
|
|
<div style="font-size:10px;color:#555;margin:2px 3px 5px">Your primary domain name managed by this panel.</div>
|
|
|
|
<label>DNS Provider</label>
|
|
<select id="w-provider" style="width:100%" onchange="wizProviderChanged()">
|
|
<option value="">-- Select Provider --</option>
|
|
</select>
|
|
<div id="w-provider-notes" style="font-size:11px;color:#ffaa00;margin:5px 3px;min-height:20px"></div>
|
|
|
|
<label id="w-lbl-apikey">API Key</label>
|
|
<input type="text" id="w-apikey" style="width:100%" placeholder="Enter API key">
|
|
<div id="w-provider-docs" style="font-size:11px;margin:5px 3px"></div>
|
|
<div id="w-provider-help" style="font-size:10px;color:#555;margin:2px 3px 10px;display:none">
|
|
<div id="w-help-content"></div>
|
|
</div>
|
|
|
|
<div style="margin-top:10px">
|
|
<button class="btn" onclick="goStep(3)">← Back</button>
|
|
<button class="btn" onclick="saveAPI()">Save & Continue →</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════ -->
|
|
<!-- Step 5: Paths -->
|
|
<!-- ═══════════════════════════════════════════════════════════════ -->
|
|
<div id="step-5" class="card" style="display:none">
|
|
<div class="card-title">Web Root & Compose Path</div>
|
|
|
|
<label>Web Root Directory</label>
|
|
<input type="text" id="w-webroot" style="width:100%" placeholder="/var/www" value="/var/www">
|
|
<div style="font-size:10px;color:#555;margin:2px 3px 10px">Default: <span style="color:#00ff41">/var/www</span></div>
|
|
|
|
<label>Docker Compose Path</label>
|
|
<input type="text" id="w-compose" style="width:100%" placeholder="/opt/seteclabs/docker-compose.yml">
|
|
<div style="font-size:10px;color:#00ff41;margin:0 3px 5px;line-height:1.8">
|
|
• /opt/seteclabs/docker-compose.yml<br>
|
|
• /root/docker-compose.yml<br>
|
|
• /srv/docker-compose.yml
|
|
</div>
|
|
|
|
<div style="margin-top:10px">
|
|
<button class="btn" onclick="goStep(4)">← Back</button>
|
|
<button class="btn" onclick="savePaths()">Save & Finish Setup →</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 6: Test (modal trigger) -->
|
|
<div id="step-6" style="display:none"></div>
|
|
|
|
<!-- Modal overlay -->
|
|
<div id="wiz-modal" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.85);z-index:999;display:none;align-items:center;justify-content:center">
|
|
<div style="background:#111;border:1px solid #00ff41;padding:25px;max-width:500px;width:90%;max-height:80vh;overflow-y:auto">
|
|
<div id="modal-title" style="font-size:14px;color:#88ff88;margin-bottom:15px;border-bottom:1px solid #333;padding-bottom:8px">Setup Complete</div>
|
|
<div id="modal-body" style="font-size:12px;line-height:1.6"></div>
|
|
<div id="modal-buttons" style="margin-top:15px"></div>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
let wizProviders = [];
|
|
let currentStep = 1;
|
|
|
|
// ── Step navigation ─────────────────────────────────────────────
|
|
function goStep(n) {
|
|
for (let i = 1; i <= 6; i++) {
|
|
const el = document.getElementById('step-' + i);
|
|
if (el) el.style.display = (i === n) ? 'block' : 'none';
|
|
const ws = document.getElementById('ws-' + i);
|
|
if (ws) ws.className = (i <= n) ? 'status-ok' : '';
|
|
}
|
|
currentStep = n;
|
|
}
|
|
|
|
function tosChanged() {
|
|
const btn = document.getElementById('btn-tos-next');
|
|
if (document.getElementById('tos-accept').checked) {
|
|
btn.disabled = false; btn.style.opacity = '1';
|
|
} else {
|
|
btn.disabled = true; btn.style.opacity = '0.3';
|
|
}
|
|
}
|
|
|
|
// ── TOS acceptance ──────────────────────────────────────────────
|
|
async function acceptTOS() {
|
|
const res = await fetch('/api/wizard/accept-tos', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({})
|
|
}).then(r => r.json());
|
|
if (res.ok) {
|
|
goStep(2);
|
|
} else {
|
|
alert('Error saving TOS acceptance: ' + (res.error || 'Unknown'));
|
|
}
|
|
}
|
|
|
|
// ── Provider help ────────────────────────────────────────────────
|
|
const providerHelp = {
|
|
hostinger: 'Log in to <strong>hPanel</strong> → click your profile icon → <strong>API Keys</strong> → Create new key with DNS permissions.',
|
|
cloudflare: 'Go to <strong>dash.cloudflare.com</strong> → My Profile → <strong>API Tokens</strong> → Create Token → use "Edit zone DNS" template.',
|
|
digitalocean: 'Go to <strong>cloud.digitalocean.com</strong> → API → <strong>Tokens</strong> → Generate New Token with read+write scope.',
|
|
vultr: 'Go to <strong>my.vultr.com</strong> → Account → <strong>API</strong> → Enable API and copy the key.',
|
|
linode: 'Go to <strong>cloud.linode.com</strong> → My Profile → <strong>API Tokens</strong> → Create Personal Access Token.',
|
|
godaddy: 'Go to <strong>developer.godaddy.com</strong> → API Keys → Create New API Key. Format: <span style="color:#00ff41">key:secret</span>.',
|
|
namecheap: 'Go to <strong>namecheap.com</strong> → Profile → Tools → <strong>API Access</strong>. Whitelist your IP.',
|
|
hetzner: 'Go to <strong>dns.hetzner.com</strong> → API Tokens → Create new token.',
|
|
ovh: 'Go to <strong>api.ovh.com/createApp</strong>. You need Application Key, Application Secret, and Consumer Key.',
|
|
aws_route53: 'In <strong>AWS IAM Console</strong>, create access key with Route53 permissions. Format: <span style="color:#00ff41">ACCESS_KEY:SECRET</span>.'
|
|
};
|
|
|
|
async function loadWizProviders() {
|
|
const res = await apiGet('/api/hosting/providers');
|
|
if (!res.ok) return;
|
|
wizProviders = res.data;
|
|
const sel = document.getElementById('w-provider');
|
|
wizProviders.forEach(p => {
|
|
const opt = document.createElement('option');
|
|
opt.value = p.id;
|
|
opt.textContent = p.name;
|
|
sel.appendChild(opt);
|
|
});
|
|
}
|
|
|
|
function wizProviderChanged() {
|
|
const id = document.getElementById('w-provider').value;
|
|
const p = wizProviders.find(x => x.id === id);
|
|
if (p) {
|
|
document.getElementById('w-lbl-apikey').textContent = p.api_key_label || 'API Key';
|
|
document.getElementById('w-provider-notes').textContent = p.notes || '';
|
|
document.getElementById('w-provider-docs').innerHTML = p.docs ?
|
|
'Docs: <a href="' + escHtml(p.docs) + '" target="_blank" style="color:#00ff41">' + escHtml(p.docs) + '</a>' : '';
|
|
const helpDiv = document.getElementById('w-provider-help');
|
|
const helpContent = document.getElementById('w-help-content');
|
|
if (providerHelp[id]) {
|
|
helpContent.innerHTML = '<strong style="color:#88ff88">How to get your key:</strong><br>' + providerHelp[id];
|
|
helpDiv.style.display = 'block';
|
|
} else { helpDiv.style.display = 'none'; }
|
|
} else {
|
|
document.getElementById('w-lbl-apikey').textContent = 'API Key';
|
|
document.getElementById('w-provider-notes').textContent = '';
|
|
document.getElementById('w-provider-docs').innerHTML = '';
|
|
document.getElementById('w-provider-help').style.display = 'none';
|
|
}
|
|
}
|
|
|
|
// ── SSH host guides ─────────────────────────────────────────────
|
|
const sshHostGuides = {
|
|
hostinger: { name: 'Hostinger', url: 'https://support.hostinger.com/en/articles/1583522-how-to-generate-ssh-keys', steps: 'Log in to <strong>hPanel</strong> → VPS → Settings → <strong>SSH Keys</strong> → Add SSH Key.' },
|
|
digitalocean: { name: 'DigitalOcean', url: 'https://docs.digitalocean.com/products/droplets/how-to/add-ssh-keys/', steps: 'Go to <strong>Settings</strong> → <strong>Security</strong> → SSH Keys → Add SSH Key.' },
|
|
vultr: { name: 'Vultr', url: 'https://docs.vultr.com/how-do-i-generate-ssh-keys', steps: 'Go to <strong>Account</strong> → <strong>SSH Keys</strong> → Add SSH Key.' },
|
|
linode: { name: 'Linode (Akamai)', url: 'https://www.linode.com/docs/guides/use-public-key-authentication-with-ssh/', steps: 'Go to <strong>Profile</strong> → <strong>SSH Keys</strong> → Add SSH Key.' },
|
|
hetzner: { name: 'Hetzner', url: 'https://docs.hetzner.com/cloud/servers/getting-started/connecting-to-the-server/', steps: 'Go to <strong>Security</strong> → <strong>SSH Keys</strong> in Hetzner Cloud Console.' },
|
|
ovh: { name: 'OVH', url: 'https://help.ovhcloud.com/csm/en-dedicated-servers-creating-ssh-keys?id=kb_article_view&sysparm_article=KB0047697', steps: 'In OVHcloud Control Panel → <strong>Public Cloud</strong> → <strong>SSH Keys</strong>.' },
|
|
aws: { name: 'AWS (EC2)', url: 'https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html', steps: 'AWS Console → EC2 → <strong>Key Pairs</strong> → Create or Import.' },
|
|
contabo: { name: 'Contabo', url: 'https://contabo.com/blog/establishing-connection-server-ssh/', steps: 'Generate keys locally, copy manually with <code style="color:#00ff41">ssh-copy-id</code>.' },
|
|
other: { name: 'Other', url: '', steps: '' }
|
|
};
|
|
|
|
function sshKeyChoice(choice) {
|
|
document.getElementById('ssh-has-keys').style.display = (choice === 'yes') ? 'block' : 'none';
|
|
document.getElementById('ssh-no-keys').style.display = (choice === 'no') ? 'block' : 'none';
|
|
document.getElementById('btn-has-keys').style.background = (choice === 'yes') ? '#00ff41' : '';
|
|
document.getElementById('btn-has-keys').style.color = (choice === 'yes') ? '#000' : '';
|
|
document.getElementById('btn-no-keys').style.background = (choice === 'no') ? '#00ff41' : '';
|
|
document.getElementById('btn-no-keys').style.color = (choice === 'no') ? '#000' : '';
|
|
}
|
|
|
|
function sshHostChanged() {
|
|
const id = document.getElementById('w-ssh-host').value;
|
|
const guideDiv = document.getElementById('ssh-host-guide');
|
|
const genericDiv = document.getElementById('ssh-generic-guide');
|
|
if (!id) { guideDiv.style.display = 'none'; genericDiv.style.display = 'none'; return; }
|
|
const host = sshHostGuides[id];
|
|
genericDiv.style.display = 'block';
|
|
if (host && (host.url || host.steps)) {
|
|
let html = '<p style="color:#88ff88;font-size:12px;margin-bottom:8px"><strong>' + escHtml(host.name) + ' SSH Key Guide</strong></p>';
|
|
if (host.steps) html += '<p style="font-size:11px;color:#888;margin-bottom:8px">' + host.steps + '</p>';
|
|
if (host.url) html += '<p style="margin-top:8px"><a href="' + escHtml(host.url) + '" target="_blank" style="color:#00ff41">' + escHtml(host.url) + '</a></p>';
|
|
guideDiv.innerHTML = html;
|
|
guideDiv.style.display = 'block';
|
|
} else { guideDiv.style.display = 'none'; }
|
|
}
|
|
|
|
function saveKeyAndContinue() {
|
|
if (!document.getElementById('w-key').value) { alert('Enter SSH private key path.'); return; }
|
|
goStep(3);
|
|
}
|
|
|
|
function saveNewKeyAndContinue() {
|
|
const k = document.getElementById('w-key-new').value;
|
|
if (!k) { alert('Enter SSH private key path.'); return; }
|
|
document.getElementById('w-key').value = k;
|
|
goStep(3);
|
|
}
|
|
|
|
function getKeyPath() {
|
|
return document.getElementById('w-key').value || document.getElementById('w-key-new').value || '';
|
|
}
|
|
|
|
async function saveVPS() {
|
|
const body = { vps_host: document.getElementById('w-host').value, vps_user: document.getElementById('w-user').value,
|
|
vps_port: parseInt(document.getElementById('w-port').value) || 22, ssh_key_path: getKeyPath() };
|
|
if (!body.vps_host) { alert('Enter server IP.'); return; }
|
|
if (!body.ssh_key_path) { alert('Go back and enter SSH key path.'); return; }
|
|
const res = await apiPost('/api/settings', body);
|
|
if (res.ok) goStep(4); else alert('Error: ' + (res.error || 'Unknown'));
|
|
}
|
|
|
|
async function saveAPI() {
|
|
const body = { domain: document.getElementById('w-domain').value, hosting_provider: document.getElementById('w-provider').value,
|
|
hostinger_api_key: document.getElementById('w-apikey').value };
|
|
if (!body.domain) { alert('Enter your domain.'); return; }
|
|
const res = await apiPost('/api/settings', body);
|
|
if (res.ok) goStep(5); else alert('Error: ' + (res.error || 'Unknown'));
|
|
}
|
|
|
|
async function savePaths() {
|
|
const body = { web_root: document.getElementById('w-webroot').value || '/var/www',
|
|
compose_path: document.getElementById('w-compose').value, setup_complete: true };
|
|
const res = await apiPost('/api/settings', body);
|
|
if (res.ok) showTestModal(); else alert('Error: ' + (res.error || 'Unknown'));
|
|
}
|
|
|
|
// ── Test modal (same as before) ─────────────────────────────────
|
|
function showTestModal() {
|
|
document.getElementById('wiz-modal').style.display = 'flex';
|
|
document.getElementById('modal-title').textContent = 'Setup Complete!';
|
|
document.getElementById('modal-body').innerHTML =
|
|
'<p style="margin-bottom:10px">Your settings have been saved.</p>' +
|
|
'<p>Would you like to test the connection?</p>';
|
|
document.getElementById('modal-buttons').innerHTML =
|
|
'<button class="btn" onclick="runTest()">Yes, Test Connection</button> ' +
|
|
'<button class="btn" onclick="closeModal()">Skip</button>';
|
|
}
|
|
|
|
async function runTest() {
|
|
document.getElementById('modal-title').textContent = 'Testing Connection...';
|
|
document.getElementById('modal-body').innerHTML = '<span style="color:#00ff41">Connecting via SSH...</span>';
|
|
document.getElementById('modal-buttons').innerHTML = '';
|
|
const sshRes = await apiGet('/api/wizard/test');
|
|
if (sshRes.ok && sshRes.data) {
|
|
const d = sshRes.data;
|
|
if (d.ssh_ok) {
|
|
let html = '<p style="color:#00ff41;margin-bottom:10px"><strong>SSH: SUCCESS</strong></p>';
|
|
html += '<div style="background:#000;padding:8px;border:1px solid #333;font-size:11px;margin-bottom:10px">' + escHtml(d.ssh_output || '') + '</div>';
|
|
if (d.api_ok) html += '<p style="color:#00ff41"><strong>DNS API: SUCCESS</strong></p>';
|
|
else if (d.api_error) html += '<p style="color:#ffaa00"><strong>DNS API: ' + escHtml(d.api_error) + '</strong></p>';
|
|
document.getElementById('modal-title').textContent = 'Connection Successful!';
|
|
document.getElementById('modal-body').innerHTML = html;
|
|
document.getElementById('modal-buttons').innerHTML =
|
|
'<button class="btn" onclick="window.location.href=\'/\'">Go to Dashboard</button>';
|
|
} else { showTestFailed(d.ssh_error || d.error || 'Connection failed'); }
|
|
} else { showTestFailed(sshRes.error || 'Connection failed'); }
|
|
}
|
|
|
|
function showTestFailed(error) {
|
|
document.getElementById('modal-title').textContent = 'Connection Failed';
|
|
document.getElementById('modal-body').innerHTML =
|
|
'<p style="color:#ff4444;margin-bottom:10px"><strong>Connection Failed</strong></p>' +
|
|
'<div style="background:#000;padding:8px;border:1px solid #ff4444;font-size:11px;margin-bottom:10px;color:#ff4444;white-space:pre-wrap">' + escHtml(error) + '</div>' +
|
|
'<p style="color:#ffaa00;margin-bottom:8px">Check:</p>' +
|
|
'<ul style="font-size:11px;color:#888;margin-left:15px;line-height:1.8">' +
|
|
'<li>Server IP is correct and VPS is running</li>' +
|
|
'<li>SSH port is open and correct</li>' +
|
|
'<li>SSH key exists at the specified path</li>' +
|
|
'<li>Public key is in ~/.ssh/authorized_keys on server</li></ul>';
|
|
document.getElementById('modal-buttons').innerHTML =
|
|
'<button class="btn" onclick="closeModal()">Close & Fix Settings</button>';
|
|
}
|
|
|
|
function closeModal() { document.getElementById('wiz-modal').style.display = 'none'; }
|
|
|
|
// ── Prefill ─────────────────────────────────────────────────────
|
|
async function prefillWizard() {
|
|
const res = await apiGet('/api/settings');
|
|
if (!res.ok) return;
|
|
const d = res.data;
|
|
if (d.vps_host) document.getElementById('w-host').value = d.vps_host;
|
|
if (d.vps_user) document.getElementById('w-user').value = d.vps_user;
|
|
if (d.vps_port) document.getElementById('w-port').value = d.vps_port;
|
|
if (d.ssh_key_path) {
|
|
document.getElementById('w-key').value = d.ssh_key_path;
|
|
document.getElementById('w-key-new').value = d.ssh_key_path;
|
|
}
|
|
if (d.domain) document.getElementById('w-domain').value = d.domain;
|
|
if (d.hostinger_api_key) document.getElementById('w-apikey').value = d.hostinger_api_key;
|
|
if (d.web_root) document.getElementById('w-webroot').value = d.web_root;
|
|
if (d.compose_path) document.getElementById('w-compose').value = d.compose_path;
|
|
if (d.hosting_provider) {
|
|
document.getElementById('w-provider').value = d.hosting_provider;
|
|
wizProviderChanged();
|
|
}
|
|
}
|
|
|
|
loadWizProviders().then(() => prefillWizard());
|
|
</script>
|
|
{% endblock %}
|