feat: role-aware hub — service filtering, onboarding, upgrade path
Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/af4088da-8845-4f7f-914f-259fd33884ed Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
c28de5def9
commit
58966646c2
@@ -259,6 +259,20 @@ ROLE_LABELS = {
|
|||||||
"node": "Bitcoin Node",
|
"node": "Bitcoin Node",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Categories shown per role (None = show all)
|
||||||
|
ROLE_CATEGORIES: dict[str, set[str] | None] = {
|
||||||
|
"server_plus_desktop": None,
|
||||||
|
"desktop": {"infrastructure", "support", "feature-manager"},
|
||||||
|
"node": {"infrastructure", "bitcoin-base", "bitcoin-apps", "support", "feature-manager"},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Features shown per role (None = show all)
|
||||||
|
ROLE_FEATURES: dict[str, set[str] | None] = {
|
||||||
|
"server_plus_desktop": None,
|
||||||
|
"desktop": {"rdp"},
|
||||||
|
"node": {"bip110", "bitcoin-core", "mempool"},
|
||||||
|
}
|
||||||
|
|
||||||
SERVICE_DESCRIPTIONS: dict[str, str] = {
|
SERVICE_DESCRIPTIONS: dict[str, str] = {
|
||||||
"bitcoind.service": (
|
"bitcoind.service": (
|
||||||
"The foundation of your financial sovereignty. Your node independently verifies "
|
"The foundation of your financial sovereignty. Your node independently verifies "
|
||||||
@@ -1322,14 +1336,69 @@ async def api_onboarding_complete():
|
|||||||
async def api_config():
|
async def api_config():
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
role = cfg.get("role", "server_plus_desktop")
|
role = cfg.get("role", "server_plus_desktop")
|
||||||
|
allowed_cats = ROLE_CATEGORIES.get(role)
|
||||||
|
cats = CATEGORY_ORDER if allowed_cats is None else [
|
||||||
|
c for c in CATEGORY_ORDER if c[0] in allowed_cats
|
||||||
|
]
|
||||||
return {
|
return {
|
||||||
"role": role,
|
"role": role,
|
||||||
"role_label": ROLE_LABELS.get(role, role),
|
"role_label": ROLE_LABELS.get(role, role),
|
||||||
"category_order": CATEGORY_ORDER,
|
"category_order": cats,
|
||||||
"feature_manager": True,
|
"feature_manager": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ROLE_STATE_NIX = """\
|
||||||
|
# THIS FILE IS AUTO-GENERATED. DO NOT EDIT.
|
||||||
|
{ config, lib, ... }:
|
||||||
|
{
|
||||||
|
sovran_systemsOS.roles.server_plus_desktop = lib.mkDefault true;
|
||||||
|
sovran_systemsOS.roles.desktop = lib.mkDefault false;
|
||||||
|
sovran_systemsOS.roles.node = lib.mkDefault false;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/api/role/upgrade-to-server")
|
||||||
|
async def api_upgrade_to_server():
|
||||||
|
"""Upgrade from Node role to Server+Desktop role by writing role-state.nix and rebuilding."""
|
||||||
|
cfg = load_config()
|
||||||
|
if cfg.get("role", "server_plus_desktop") != "node":
|
||||||
|
raise HTTPException(status_code=400, detail="Upgrade is only available for the Node role.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open("/etc/nixos/role-state.nix", "w") as f:
|
||||||
|
f.write(ROLE_STATE_NIX)
|
||||||
|
except OSError as exc:
|
||||||
|
raise HTTPException(status_code=500, detail=f"Failed to write role-state.nix: {exc}")
|
||||||
|
|
||||||
|
# Reset onboarding so the wizard runs for the newly unlocked services
|
||||||
|
try:
|
||||||
|
os.remove(ONBOARDING_FLAG)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Clear stale rebuild log
|
||||||
|
try:
|
||||||
|
open(REBUILD_LOG, "w").close()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
await asyncio.create_subprocess_exec(
|
||||||
|
"systemctl", "reset-failed", REBUILD_UNIT,
|
||||||
|
stdout=asyncio.subprocess.DEVNULL,
|
||||||
|
stderr=asyncio.subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
proc = await asyncio.create_subprocess_exec(
|
||||||
|
"systemctl", "start", "--no-block", REBUILD_UNIT,
|
||||||
|
stdout=asyncio.subprocess.DEVNULL,
|
||||||
|
stderr=asyncio.subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
await proc.wait()
|
||||||
|
|
||||||
|
return {"ok": True, "status": "rebuilding"}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/services")
|
@app.get("/api/services")
|
||||||
async def api_services():
|
async def api_services():
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
@@ -2082,8 +2151,14 @@ async def api_features():
|
|||||||
ssl_email_path = os.path.join(DOMAINS_DIR, "sslemail")
|
ssl_email_path = os.path.join(DOMAINS_DIR, "sslemail")
|
||||||
ssl_email_configured = os.path.exists(ssl_email_path)
|
ssl_email_configured = os.path.exists(ssl_email_path)
|
||||||
|
|
||||||
|
role = load_config().get("role", "server_plus_desktop")
|
||||||
|
allowed_features = ROLE_FEATURES.get(role)
|
||||||
|
registry = FEATURE_REGISTRY if allowed_features is None else [
|
||||||
|
f for f in FEATURE_REGISTRY if f["id"] in allowed_features
|
||||||
|
]
|
||||||
|
|
||||||
features = []
|
features = []
|
||||||
for feat in FEATURE_REGISTRY:
|
for feat in registry:
|
||||||
feat_id = feat["id"]
|
feat_id = feat["id"]
|
||||||
|
|
||||||
# Determine enabled state:
|
# Determine enabled state:
|
||||||
|
|||||||
@@ -82,6 +82,62 @@
|
|||||||
margin: 16px 0;
|
margin: 16px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Sidebar: Upgrade button (Node role) ────────────────────────── */
|
||||||
|
|
||||||
|
.sidebar-upgrade-btn {
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
background-color: rgba(137, 180, 250, 0.06);
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-upgrade-btn:hover {
|
||||||
|
background-color: rgba(137, 180, 250, 0.14);
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-upgrade-btn .sidebar-support-hint {
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Upgrade modal ──────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.upgrade-dialog {
|
||||||
|
max-width: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upgrade-info-box {
|
||||||
|
background-color: var(--card-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 14px 18px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upgrade-info-title {
|
||||||
|
font-size: 0.88rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upgrade-info-list {
|
||||||
|
padding-left: 20px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
line-height: 1.7;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upgrade-info-list a {
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.upgrade-rebuild-note {
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--text-dim);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Tiles area ─────────────────────────────────────────────────── */
|
/* ── Tiles area ─────────────────────────────────────────────────── */
|
||||||
|
|
||||||
#tiles-area {
|
#tiles-area {
|
||||||
|
|||||||
@@ -33,6 +33,43 @@ if ($modal) $modal.addEventListener("click", function(e) { if (e.target === $mod
|
|||||||
if ($credsModal) $credsModal.addEventListener("click", function(e) { if (e.target === $credsModal) closeCredsModal(); });
|
if ($credsModal) $credsModal.addEventListener("click", function(e) { if (e.target === $credsModal) closeCredsModal(); });
|
||||||
if ($supportModal) $supportModal.addEventListener("click", function(e) { if (e.target === $supportModal) closeSupportModal(); });
|
if ($supportModal) $supportModal.addEventListener("click", function(e) { if (e.target === $supportModal) closeSupportModal(); });
|
||||||
|
|
||||||
|
// Upgrade modal
|
||||||
|
if ($upgradeCloseBtn) $upgradeCloseBtn.addEventListener("click", closeUpgradeModal);
|
||||||
|
if ($upgradeCancelBtn) $upgradeCancelBtn.addEventListener("click", closeUpgradeModal);
|
||||||
|
if ($upgradeModal) $upgradeModal.addEventListener("click", function(e) { if (e.target === $upgradeModal) closeUpgradeModal(); });
|
||||||
|
|
||||||
|
// ── Upgrade modal functions ───────────────────────────────────────
|
||||||
|
|
||||||
|
function openUpgradeModal() {
|
||||||
|
if ($upgradeModal) $upgradeModal.classList.add("open");
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeUpgradeModal() {
|
||||||
|
if ($upgradeModal) $upgradeModal.classList.remove("open");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doUpgradeToServer() {
|
||||||
|
var confirmBtn = $upgradeConfirmBtn;
|
||||||
|
if (confirmBtn) { confirmBtn.disabled = true; confirmBtn.textContent = "Upgrading…"; }
|
||||||
|
closeUpgradeModal();
|
||||||
|
|
||||||
|
// Reuse the rebuild modal to show progress
|
||||||
|
_rebuildFeatureName = "Server + Desktop";
|
||||||
|
_rebuildIsEnabling = true;
|
||||||
|
openRebuildModal();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await apiFetch("/api/role/upgrade-to-server", { method: "POST" });
|
||||||
|
} catch (err) {
|
||||||
|
if ($rebuildStatus) $rebuildStatus.textContent = "✗ Upgrade failed: " + err.message;
|
||||||
|
if ($rebuildSpinner) $rebuildSpinner.classList.remove("spinning");
|
||||||
|
if ($rebuildClose) $rebuildClose.disabled = false;
|
||||||
|
if (confirmBtn) { confirmBtn.disabled = false; confirmBtn.textContent = "Yes, Upgrade"; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($upgradeConfirmBtn) $upgradeConfirmBtn.addEventListener("click", doUpgradeToServer);
|
||||||
|
|
||||||
// ── Init ──────────────────────────────────────────────────────────
|
// ── Init ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
@@ -49,6 +86,7 @@ async function init() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
var cfg = await apiFetch("/api/config");
|
var cfg = await apiFetch("/api/config");
|
||||||
|
_currentRole = cfg.role || "server_plus_desktop";
|
||||||
if (cfg.category_order) {
|
if (cfg.category_order) {
|
||||||
for (var i = 0; i < cfg.category_order.length; i++) {
|
for (var i = 0; i < cfg.category_order.length; i++) {
|
||||||
_categoryLabels[cfg.category_order[i][0]] = cfg.category_order[i][1];
|
_categoryLabels[cfg.category_order[i][0]] = cfg.category_order[i][1];
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ let _supportStatus = null; // last fetched /api/support/status payload
|
|||||||
let _walletUnlockTimerInt = null;
|
let _walletUnlockTimerInt = null;
|
||||||
let _cachedExternalIp = null;
|
let _cachedExternalIp = null;
|
||||||
|
|
||||||
|
// Current role (set during init from /api/config)
|
||||||
|
let _currentRole = "server_plus_desktop";
|
||||||
|
|
||||||
// Feature Manager state
|
// Feature Manager state
|
||||||
let _featuresData = null;
|
let _featuresData = null;
|
||||||
let _rebuildLog = "";
|
let _rebuildLog = "";
|
||||||
@@ -89,5 +92,11 @@ const $portReqModal = document.getElementById("port-requirements-modal");
|
|||||||
const $portReqBody = document.getElementById("port-req-body");
|
const $portReqBody = document.getElementById("port-req-body");
|
||||||
const $portReqClose = document.getElementById("port-req-close-btn");
|
const $portReqClose = document.getElementById("port-req-close-btn");
|
||||||
|
|
||||||
|
// Upgrade modal (Node → Server+Desktop)
|
||||||
|
const $upgradeModal = document.getElementById("upgrade-modal");
|
||||||
|
const $upgradeConfirmBtn = document.getElementById("upgrade-confirm-btn");
|
||||||
|
const $upgradeCancelBtn = document.getElementById("upgrade-cancel-btn");
|
||||||
|
const $upgradeCloseBtn = document.getElementById("upgrade-close-btn");
|
||||||
|
|
||||||
// System status banner
|
// System status banner
|
||||||
// (removed — health is now shown per-tile via the composite health field)
|
// (removed — health is now shown per-tile via the composite health field)
|
||||||
@@ -71,6 +71,20 @@ function renderSidebarSupport(supportServices) {
|
|||||||
backupBtn.addEventListener("click", function() { openBackupModal(); });
|
backupBtn.addEventListener("click", function() { openBackupModal(); });
|
||||||
$sidebarSupport.appendChild(backupBtn);
|
$sidebarSupport.appendChild(backupBtn);
|
||||||
|
|
||||||
|
// ── Upgrade button (Node role only)
|
||||||
|
if (_currentRole === "node") {
|
||||||
|
var upgradeBtn = document.createElement("button");
|
||||||
|
upgradeBtn.className = "sidebar-support-btn sidebar-upgrade-btn";
|
||||||
|
upgradeBtn.innerHTML =
|
||||||
|
'<span class="sidebar-support-icon">🚀</span>' +
|
||||||
|
'<span class="sidebar-support-text">' +
|
||||||
|
'<span class="sidebar-support-title">Upgrade to Full Server</span>' +
|
||||||
|
'<span class="sidebar-support-hint">Unlock all services</span>' +
|
||||||
|
'</span>';
|
||||||
|
upgradeBtn.addEventListener("click", function() { openUpgradeModal(); });
|
||||||
|
$sidebarSupport.appendChild(upgradeBtn);
|
||||||
|
}
|
||||||
|
|
||||||
var hr = document.createElement("hr");
|
var hr = document.createElement("hr");
|
||||||
hr.className = "sidebar-divider";
|
hr.className = "sidebar-divider";
|
||||||
$sidebarSupport.appendChild(hr);
|
$sidebarSupport.appendChild(hr);
|
||||||
|
|||||||
@@ -6,6 +6,16 @@
|
|||||||
|
|
||||||
const TOTAL_STEPS = 4;
|
const TOTAL_STEPS = 4;
|
||||||
|
|
||||||
|
// Steps to skip per role (steps 2 and 3 involve domain/port setup)
|
||||||
|
const ROLE_SKIP_STEPS = {
|
||||||
|
"desktop": [2, 3],
|
||||||
|
"node": [2, 3],
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Role state (loaded at init) ───────────────────────────────────
|
||||||
|
|
||||||
|
var _onboardingRole = "server_plus_desktop";
|
||||||
|
|
||||||
// Domains that may need configuration, with service unit mapping for enabled check
|
// Domains that may need configuration, with service unit mapping for enabled check
|
||||||
const DOMAIN_DEFS = [
|
const DOMAIN_DEFS = [
|
||||||
{ name: "matrix", label: "Matrix (Synapse)", unit: "matrix-synapse.service", needsDdns: true },
|
{ name: "matrix", label: "Matrix (Synapse)", unit: "matrix-synapse.service", needsDdns: true },
|
||||||
@@ -83,6 +93,22 @@ function showStep(step) {
|
|||||||
if (step === 3) loadStep3();
|
if (step === 3) loadStep3();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the next step number, skipping over role-excluded steps
|
||||||
|
function nextStep(current) {
|
||||||
|
var skip = ROLE_SKIP_STEPS[_onboardingRole] || [];
|
||||||
|
var next = current + 1;
|
||||||
|
while (next < TOTAL_STEPS && skip.indexOf(next) !== -1) next++;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the previous step number, skipping over role-excluded steps
|
||||||
|
function prevStep(current) {
|
||||||
|
var skip = ROLE_SKIP_STEPS[_onboardingRole] || [];
|
||||||
|
var prev = current - 1;
|
||||||
|
while (prev > 1 && skip.indexOf(prev) !== -1) prev--;
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Step 1: Welcome ───────────────────────────────────────────────
|
// ── Step 1: Welcome ───────────────────────────────────────────────
|
||||||
|
|
||||||
async function loadStep1() {
|
async function loadStep1() {
|
||||||
@@ -319,9 +345,9 @@ async function completeOnboarding() {
|
|||||||
// ── Event wiring ──────────────────────────────────────────────────
|
// ── Event wiring ──────────────────────────────────────────────────
|
||||||
|
|
||||||
function wireNavButtons() {
|
function wireNavButtons() {
|
||||||
// Step 1 → 2
|
// Step 1 → next (may skip 2+3 for desktop/node)
|
||||||
var s1next = document.getElementById("step-1-next");
|
var s1next = document.getElementById("step-1-next");
|
||||||
if (s1next) s1next.addEventListener("click", function() { showStep(2); });
|
if (s1next) s1next.addEventListener("click", function() { showStep(nextStep(1)); });
|
||||||
|
|
||||||
// Step 2 → 3 (save first)
|
// Step 2 → 3 (save first)
|
||||||
var s2next = document.getElementById("step-2-next");
|
var s2next = document.getElementById("step-2-next");
|
||||||
@@ -331,12 +357,12 @@ function wireNavButtons() {
|
|||||||
await saveStep2();
|
await saveStep2();
|
||||||
s2next.disabled = false;
|
s2next.disabled = false;
|
||||||
s2next.textContent = "Save & Continue →";
|
s2next.textContent = "Save & Continue →";
|
||||||
showStep(3);
|
showStep(nextStep(2));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Step 3 → 4 (Complete)
|
// Step 3 → 4 (Complete)
|
||||||
var s3next = document.getElementById("step-3-next");
|
var s3next = document.getElementById("step-3-next");
|
||||||
if (s3next) s3next.addEventListener("click", function() { showStep(4); });
|
if (s3next) s3next.addEventListener("click", function() { showStep(nextStep(3)); });
|
||||||
|
|
||||||
// Step 4: finish
|
// Step 4: finish
|
||||||
var s4finish = document.getElementById("step-4-finish");
|
var s4finish = document.getElementById("step-4-finish");
|
||||||
@@ -345,7 +371,7 @@ function wireNavButtons() {
|
|||||||
// Back buttons
|
// Back buttons
|
||||||
document.querySelectorAll(".onboarding-btn-back").forEach(function(btn) {
|
document.querySelectorAll(".onboarding-btn-back").forEach(function(btn) {
|
||||||
var prev = parseInt(btn.dataset.prev, 10);
|
var prev = parseInt(btn.dataset.prev, 10);
|
||||||
btn.addEventListener("click", function() { showStep(prev); });
|
btn.addEventListener("click", function() { showStep(prevStep(prev + 1)); });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,6 +387,12 @@ document.addEventListener("DOMContentLoaded", async function() {
|
|||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
|
// Load role so step-skipping is applied before wiring nav buttons
|
||||||
|
try {
|
||||||
|
var cfg = await apiFetch("/api/config");
|
||||||
|
if (cfg.role) _onboardingRole = cfg.role;
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
wireNavButtons();
|
wireNavButtons();
|
||||||
updateProgress(1);
|
updateProgress(1);
|
||||||
loadStep1();
|
loadStep1();
|
||||||
|
|||||||
@@ -172,6 +172,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Upgrade Modal (Node → Server+Desktop) -->
|
||||||
|
<div class="modal-overlay" id="upgrade-modal" role="dialog" aria-modal="true" aria-labelledby="upgrade-modal-title">
|
||||||
|
<div class="creds-dialog upgrade-dialog">
|
||||||
|
<div class="creds-header">
|
||||||
|
<span class="creds-title" id="upgrade-modal-title">🚀 Upgrade to Server + Desktop</span>
|
||||||
|
<button class="creds-close-btn" id="upgrade-close-btn" title="Close">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="creds-body">
|
||||||
|
<p class="support-desc">
|
||||||
|
Upgrading to the full <strong>Server + Desktop</strong> experience will unlock all services —
|
||||||
|
encrypted messaging, password management, cloud storage, website hosting, and more.
|
||||||
|
</p>
|
||||||
|
<div class="upgrade-info-box">
|
||||||
|
<p class="upgrade-info-title">⚠ What you should know:</p>
|
||||||
|
<ul class="upgrade-info-list">
|
||||||
|
<li>You will need to purchase domains for your services (we recommend <a href="https://njal.la" target="_blank" rel="noopener noreferrer">njal.la</a>)</li>
|
||||||
|
<li>Some services require ports to be opened on your router</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<p class="support-desc">
|
||||||
|
<strong>Don't worry</strong> — the Hub will walk you through every step after the upgrade.
|
||||||
|
Domain setup, port forwarding, and configuration are all guided.
|
||||||
|
</p>
|
||||||
|
<p class="support-desc upgrade-rebuild-note">
|
||||||
|
The system will rebuild after upgrading. This may take several minutes.
|
||||||
|
</p>
|
||||||
|
<div class="domain-field-actions">
|
||||||
|
<button class="btn btn-close-modal" id="upgrade-cancel-btn">Cancel</button>
|
||||||
|
<button class="btn btn-primary" id="upgrade-confirm-btn">Yes, Upgrade</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Reboot overlay -->
|
<!-- Reboot overlay -->
|
||||||
<div class="reboot-overlay" id="reboot-overlay">
|
<div class="reboot-overlay" id="reboot-overlay">
|
||||||
<div class="reboot-card">
|
<div class="reboot-card">
|
||||||
|
|||||||
@@ -4,16 +4,22 @@ let
|
|||||||
cfg = config.sovran_systemsOS;
|
cfg = config.sovran_systemsOS;
|
||||||
|
|
||||||
monitoredServices =
|
monitoredServices =
|
||||||
# ── Infrastructure (always present) ────────────────────────
|
# ── Infrastructure — System Passwords (always present) ─────
|
||||||
[
|
[
|
||||||
{ name = "Caddy"; unit = "caddy.service"; type = "system"; icon = "caddy"; enabled = true; category = "infrastructure"; credentials = []; }
|
|
||||||
{ name = "Tor"; unit = "tor.service"; type = "system"; icon = "tor"; enabled = true; category = "infrastructure"; credentials = []; }
|
|
||||||
{ name = "System Passwords"; unit = "root-password-setup.service"; type = "system"; icon = "passwords"; enabled = true; category = "infrastructure"; credentials = [
|
{ name = "System Passwords"; unit = "root-password-setup.service"; type = "system"; icon = "passwords"; enabled = true; category = "infrastructure"; credentials = [
|
||||||
{ label = "Free Account — Username"; value = "free"; }
|
{ label = "Free Account — Username"; value = "free"; }
|
||||||
{ label = "Free Account — Password"; file = "/var/lib/secrets/free-password"; }
|
{ label = "Free Account — Password"; file = "/var/lib/secrets/free-password"; }
|
||||||
{ label = "Root Password"; file = "/var/lib/secrets/root-password"; }
|
{ label = "Root Password"; file = "/var/lib/secrets/root-password"; }
|
||||||
{ label = "SSH Local Access"; value = "ssh root@localhost / Passphrase: gosovransystems"; }
|
{ label = "SSH Local Access"; value = "ssh root@localhost / Passphrase: gosovransystems"; }
|
||||||
]; }
|
]; }
|
||||||
|
]
|
||||||
|
# ── Infrastructure — Caddy + Tor (NOT desktop-only) ────────
|
||||||
|
++ lib.optionals (!cfg.roles.desktop) [
|
||||||
|
{ name = "Caddy"; unit = "caddy.service"; type = "system"; icon = "caddy"; enabled = true; category = "infrastructure"; credentials = []; }
|
||||||
|
{ name = "Tor"; unit = "tor.service"; type = "system"; icon = "tor"; enabled = true; category = "infrastructure"; credentials = []; }
|
||||||
|
]
|
||||||
|
# ── Infrastructure — Remote Desktop (roles with a desktop) ─
|
||||||
|
++ lib.optionals (!cfg.roles.node) [
|
||||||
{ name = "Remote Desktop"; unit = "gnome-remote-desktop.service"; type = "system"; icon = "rdp"; enabled = cfg.features.rdp; category = "infrastructure"; credentials = [
|
{ name = "Remote Desktop"; unit = "gnome-remote-desktop.service"; type = "system"; icon = "rdp"; enabled = cfg.features.rdp; category = "infrastructure"; credentials = [
|
||||||
{ label = "Username"; file = "/var/lib/gnome-remote-desktop/rdp-username"; }
|
{ label = "Username"; file = "/var/lib/gnome-remote-desktop/rdp-username"; }
|
||||||
{ label = "Password"; file = "/var/lib/gnome-remote-desktop/rdp-password"; }
|
{ label = "Password"; file = "/var/lib/gnome-remote-desktop/rdp-password"; }
|
||||||
@@ -22,7 +28,7 @@ let
|
|||||||
]; }
|
]; }
|
||||||
]
|
]
|
||||||
# ── Bitcoin Base (node implementations) ────────────────────
|
# ── Bitcoin Base (node implementations) ────────────────────
|
||||||
++ [
|
++ lib.optionals cfg.services.bitcoin [
|
||||||
{ name = "Bitcoin Knots + BIP110"; unit = "bitcoind.service"; type = "system"; icon = "bip110"; enabled = cfg.features.bip110; category = "bitcoin-base"; credentials = [
|
{ name = "Bitcoin Knots + BIP110"; unit = "bitcoind.service"; type = "system"; icon = "bip110"; enabled = cfg.features.bip110; category = "bitcoin-base"; credentials = [
|
||||||
{ label = "Tor Address"; file = "/var/lib/tor/onion/bitcoind/hostname"; prefix = "http://"; }
|
{ label = "Tor Address"; file = "/var/lib/tor/onion/bitcoind/hostname"; prefix = "http://"; }
|
||||||
]; }
|
]; }
|
||||||
@@ -34,7 +40,7 @@ let
|
|||||||
]; }
|
]; }
|
||||||
]
|
]
|
||||||
# ── Bitcoin Apps (services on top of the node) ─────────────
|
# ── Bitcoin Apps (services on top of the node) ─────────────
|
||||||
++ [
|
++ lib.optionals cfg.services.bitcoin [
|
||||||
{ name = "Electrs"; unit = "electrs.service"; type = "system"; icon = "electrs"; enabled = cfg.services.bitcoin; category = "bitcoin-apps"; credentials = [
|
{ name = "Electrs"; unit = "electrs.service"; type = "system"; icon = "electrs"; enabled = cfg.services.bitcoin; category = "bitcoin-apps"; credentials = [
|
||||||
{ label = "Tor Address"; file = "/var/lib/tor/onion/electrs/hostname"; prefix = "http://"; }
|
{ label = "Tor Address"; file = "/var/lib/tor/onion/electrs/hostname"; prefix = "http://"; }
|
||||||
{ label = "Port"; value = "50001"; }
|
{ label = "Port"; value = "50001"; }
|
||||||
@@ -58,8 +64,8 @@ let
|
|||||||
{ label = "Local Network"; file = "/var/lib/secrets/internal-ip"; prefix = "http://"; suffix = ":60847"; }
|
{ label = "Local Network"; file = "/var/lib/secrets/internal-ip"; prefix = "http://"; suffix = ":60847"; }
|
||||||
]; }
|
]; }
|
||||||
]
|
]
|
||||||
# ── Communication ──────────────────────────────────────────
|
# ── Communication (server+desktop only) ────────────────────
|
||||||
++ [
|
++ lib.optionals cfg.roles.server_plus_desktop [
|
||||||
{ name = "Matrix-Synapse"; unit = "matrix-synapse.service"; type = "system"; icon = "synapse"; enabled = cfg.services.synapse; category = "communication"; credentials = [
|
{ name = "Matrix-Synapse"; unit = "matrix-synapse.service"; type = "system"; icon = "synapse"; enabled = cfg.services.synapse; category = "communication"; credentials = [
|
||||||
{ label = "Homeserver URL"; file = "/var/lib/secrets/matrix-homeserver-url"; }
|
{ label = "Homeserver URL"; file = "/var/lib/secrets/matrix-homeserver-url"; }
|
||||||
{ label = "Admin Username"; file = "/var/lib/secrets/matrix-admin-username"; }
|
{ label = "Admin Username"; file = "/var/lib/secrets/matrix-admin-username"; }
|
||||||
@@ -69,8 +75,8 @@ let
|
|||||||
]; }
|
]; }
|
||||||
{ name = "Element-Call"; unit = "livekit.service"; type = "system"; icon = "element-calling"; enabled = cfg.features.element-calling; category = "communication"; credentials = []; }
|
{ name = "Element-Call"; unit = "livekit.service"; type = "system"; icon = "element-calling"; enabled = cfg.features.element-calling; category = "communication"; credentials = []; }
|
||||||
]
|
]
|
||||||
# ── Self-Hosted Apps ───────────────────────────────────────
|
# ── Self-Hosted Apps (server+desktop only) ─────────────────
|
||||||
++ [
|
++ lib.optionals cfg.roles.server_plus_desktop [
|
||||||
{ name = "VaultWarden"; unit = "vaultwarden.service"; type = "system"; icon = "vaultwarden"; enabled = cfg.services.vaultwarden; category = "apps"; credentials = [
|
{ name = "VaultWarden"; unit = "vaultwarden.service"; type = "system"; icon = "vaultwarden"; enabled = cfg.services.vaultwarden; category = "apps"; credentials = [
|
||||||
{ label = "URL"; file = "/var/lib/domains/vaultwarden"; prefix = "https://"; }
|
{ label = "URL"; file = "/var/lib/domains/vaultwarden"; prefix = "https://"; }
|
||||||
{ label = "Admin Panel"; file = "/var/lib/domains/vaultwarden"; prefix = "https://"; suffix = "/admin"; }
|
{ label = "Admin Panel"; file = "/var/lib/domains/vaultwarden"; prefix = "https://"; suffix = "/admin"; }
|
||||||
@@ -83,11 +89,11 @@ let
|
|||||||
{ label = "Credentials"; file = "/var/lib/secrets/wordpress-admin"; multiline = true; }
|
{ label = "Credentials"; file = "/var/lib/secrets/wordpress-admin"; multiline = true; }
|
||||||
]; }
|
]; }
|
||||||
]
|
]
|
||||||
# ── Nostr / Relay ──────────────────────────────────────────
|
# ── Nostr / Relay (server+desktop only) ────────────────────
|
||||||
++ [
|
++ lib.optionals cfg.roles.server_plus_desktop [
|
||||||
{ name = "Haven Relay"; unit = "haven-relay.service"; type = "system"; icon = "haven"; enabled = cfg.features.haven; category = "nostr"; credentials = []; }
|
{ name = "Haven Relay"; unit = "haven-relay.service"; type = "system"; icon = "haven"; enabled = cfg.features.haven; category = "nostr"; credentials = []; }
|
||||||
]
|
]
|
||||||
# ── Support ────────────────────────────────────────────────
|
# ── Support (always present) ────────────────────────────────
|
||||||
++ [
|
++ [
|
||||||
{ name = "Tech Support"; unit = "sovran-tech-support"; type = "support"; icon = "support"; enabled = true; category = "support"; credentials = []; }
|
{ name = "Tech Support"; unit = "sovran-tech-support"; type = "support"; icon = "support"; enabled = true; category = "support"; credentials = []; }
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user