diff --git a/README.md b/README.md index f6c373e..b57db93 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

Modern, Responsive Web Interface for NaxOS Storage Appliance (NixOS 26.05 & OpenZFS)
- Built with React 18, TypeScript, Tailwind CSS, Lucide Icons, and Embedded CNCF Perses Analytics + Built with React 18, TypeScript, Tailwind CSS, Lucide Icons, and Seamless Integrated Telemetry

@@ -18,15 +18,15 @@ --- -The **NaxOS Web Dashboard** is a modern, responsive, non-technical web interface for managing the NaxOS storage appliance running on NixOS 26.05. Built with React, TypeScript, Tailwind CSS, Lucide Icons, and embedded CNCF Perses analytics. +The **NaxOS Web Dashboard** is a modern, responsive, non-technical web interface for managing the NaxOS storage appliance running on NixOS 26.05. Built with React, TypeScript, Tailwind CSS, Lucide Icons, and seamless integrated storage and system telemetry. ## Dashboard Showcase -| Overview & Telemetry | OpenZFS Storage Engine | +| Overview & Live Telemetry | OpenZFS Storage Engine | | :---: | :---: | | ![Dashboard Overview](public/screenshots/dashboard-overview.png) | ![Storage Management](public/screenshots/storage-management.png) | -| **CNCF Perses Analytics** | **App Store & Runtimes** | -| ![Perses Analytics](public/screenshots/perses-analytics.png) | ![App Store](public/screenshots/app-store.png) | +| **Integrated Performance Telemetry** | **App Store & Runtimes** | +| ![Integrated Telemetry](public/screenshots/system-telemetry.png) | ![App Store](public/screenshots/app-store.png) | | **Setup & Migration Wizard** | **File Shares Management** | | ![Migration Wizard](public/screenshots/migration-wizard.png) | ![Shares Management](public/screenshots/shares-management.png) | diff --git a/index.html b/index.html index 9376e96..cf5a684 100644 --- a/index.html +++ b/index.html @@ -9,7 +9,7 @@ - +

diff --git a/public/screenshots/app-store.png b/public/screenshots/app-store.png index b5dccc4..8f9ac61 100644 Binary files a/public/screenshots/app-store.png and b/public/screenshots/app-store.png differ diff --git a/public/screenshots/dashboard-overview.png b/public/screenshots/dashboard-overview.png index 3df57f0..cb0c6fa 100644 Binary files a/public/screenshots/dashboard-overview.png and b/public/screenshots/dashboard-overview.png differ diff --git a/public/screenshots/gitops-sync.png b/public/screenshots/gitops-sync.png index b71dc4e..13ab350 100644 Binary files a/public/screenshots/gitops-sync.png and b/public/screenshots/gitops-sync.png differ diff --git a/public/screenshots/migration-wizard.png b/public/screenshots/migration-wizard.png index fc96472..da43874 100644 Binary files a/public/screenshots/migration-wizard.png and b/public/screenshots/migration-wizard.png differ diff --git a/public/screenshots/perses-analytics.png b/public/screenshots/perses-analytics.png index 7c8963d..5fe3edd 100644 Binary files a/public/screenshots/perses-analytics.png and b/public/screenshots/perses-analytics.png differ diff --git a/public/screenshots/shares-management.png b/public/screenshots/shares-management.png index 1bebfb1..496e0cd 100644 Binary files a/public/screenshots/shares-management.png and b/public/screenshots/shares-management.png differ diff --git a/public/screenshots/storage-management.png b/public/screenshots/storage-management.png index 6fbc729..d5b5f97 100644 Binary files a/public/screenshots/storage-management.png and b/public/screenshots/storage-management.png differ diff --git a/public/screenshots/system-logs.png b/public/screenshots/system-logs.png index 850e05d..9a8f568 100644 Binary files a/public/screenshots/system-logs.png and b/public/screenshots/system-logs.png differ diff --git a/public/screenshots/system-telemetry.png b/public/screenshots/system-telemetry.png new file mode 100644 index 0000000..5eadf70 Binary files /dev/null and b/public/screenshots/system-telemetry.png differ diff --git a/src/App.tsx b/src/App.tsx index 1900e89..56686cd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,7 +7,6 @@ import { OverviewView } from './views/OverviewView.js'; import { StorageView } from './views/StorageView.js'; import { SharesView } from './views/SharesView.js'; import { AppsView } from './views/AppsView.js'; -import { AnalyticsView } from './views/AnalyticsView.js'; import { GitOpsView } from './views/GitOpsView.js'; import { LogsView } from './views/LogsView.js'; import { WizardView } from './views/WizardView.js'; @@ -187,8 +186,8 @@ export const App: React.FC = () => { }; return ( -
- +
+
{ />
- {currentTab === 'overview' && ( + {(currentTab === 'overview' || currentTab === 'analytics') && ( { /> )} - {currentTab === 'analytics' && } - {currentTab === 'gitops' && ( = ({ isOpen, onClose }) =
{/* Header */}
-
-
- +
+
+ NaxOS
-

Declarative NixOS Switch

+

+ Declarative NixOS 26.05 Switch + GitOps +

Safe atomic rebuild with auto-rollback

@@ -68,8 +71,8 @@ export const RebuildModal: React.FC = ({ isOpen, onClose }) =
{logs.map((log, idx) => (
- > - + > + {log}
@@ -81,7 +84,7 @@ export const RebuildModal: React.FC = ({ isOpen, onClose }) =
{isDone && !isError && ( - + Switch completed successfully diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index b0710ab..b90a60f 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -4,11 +4,11 @@ import { HardDrive, FolderSync, Layers, - LineChart, GitBranch, FileTerminal, - Server, Settings, + Sparkles, + ShieldCheck, } from 'lucide-react'; interface SidebarProps { @@ -21,30 +21,36 @@ export const Sidebar: React.FC = ({ currentTab, onSelectTab }) => { id: 'overview', label: 'Dashboard', icon: LayoutDashboard }, { id: 'storage', label: 'Storage & ZFS', icon: HardDrive, badge: 'ZFS' }, { id: 'shares', label: 'File Shares', icon: FolderSync, badge: 'SMB/NFS' }, - { id: 'apps', label: 'App Store & Runtime', icon: Layers }, - { id: 'analytics', label: 'Perses Analytics', icon: LineChart, badge: 'Native' }, + { id: 'apps', label: 'App Store & Services', icon: Layers }, { id: 'gitops', label: 'GitOps & Updates', icon: GitBranch }, { id: 'logs', label: 'System Logs', icon: FileTerminal }, - { id: 'wizard', label: 'Setup Wizard', icon: Settings }, + { id: 'wizard', label: 'Setup & Migration', icon: Settings }, ]; return ( -
-
- - {/* Grid of Perses Dashboard Panels */} -
- {/* Panel 1: ZFS ARC Hit Ratio */} -
-
-
- - ZFS ARC Hit Ratio (%) -
- 98.6% -
-
- {[97.8, 98.1, 98.4, 98.2, 98.6, 98.8, 98.5, 98.7, 98.4, 98.9, 98.6, 98.7, 98.5, 98.8, 98.6].map((val, i) => ( -
- ))} -
-
- Query: zfs_arc_hits / (zfs_arc_hits + zfs_arc_misses) * 100 - Live -
-
- - {/* Panel 2: Storage Pool Throughput (MB/s) */} -
-
-
- - Storage Pool Read / Write Bandwidth -
- Peak: 142 MB/s -
-
- {[24, 45, 18, 62, 88, 142, 95, 30, 48, 70, 85, 40, 55, 90, 60].map((val, i) => ( -
- ))} -
-
- Query: rate(node_disk_read_bytes_total[1m]) + write - Target: tank -
-
- - {/* Panel 3: CPU Core Utilization */} -
-
-
- - CPU Core Utilization -
- 12.4% Avg -
-
- {[8, 12, 14, 25, 42, 19, 11, 9, 15, 18, 30, 16, 12, 14, 10].map((val, i) => ( -
- ))} -
-
- Query: rate(node_cpu_seconds_total) - 4 Cores -
-
- - {/* Panel 4: Memory & ARC Distribution */} -
-
-
- - Memory Footprint Breakdown -
- 13.2 / 32.0 GB -
-
-
-
-
- ZFS ARC Cache - 4.0 GB (100% Target) -
-
-
-
-
-
-
- Applications & Immich ML - 9.2 GB -
-
-
-
-
-
-
-
- Query: node_memory_MemTotal_bytes - Free: 18.8 GB -
-
-
-
- ); -}; diff --git a/src/views/AppsView.tsx b/src/views/AppsView.tsx index 4ce8f06..cbe2c39 100644 --- a/src/views/AppsView.tsx +++ b/src/views/AppsView.tsx @@ -69,7 +69,7 @@ export const AppsView: React.FC = ({ @@ -160,7 +160,7 @@ export const AppsView: React.FC = ({

- + Active Workloads

{installedApps.length} services @@ -178,7 +178,7 @@ export const AppsView: React.FC = ({

{app.name}

{app.status.toUpperCase()} @@ -217,7 +217,7 @@ export const AppsView: React.FC = ({ ) : ( - + {/* Top Banner / Hero Header with Portara Styling */} +
+
+ +
+
+
+ NaxOS +
+
+
+

Appliance Overview

+ + NixOS 26.05 + + + OpenZFS 2.2 + +
+

+ Active Node: {status?.hostname || 'naxos-storage'} · Generation {status?.nixosGeneration || 42} · GitOps Engine Synchronized +

+
+
+ +
+ + +
{/* KPI Cards Grid */}
{/* Storage Pool Card */} -
+
Pool: {primaryPool?.name || 'tank'} - + {primaryPool?.health || 'ONLINE'}
@@ -72,7 +116,7 @@ export const OverviewView: React.FC = ({ {/* Progress Bar */}
@@ -84,27 +128,28 @@ export const OverviewView: React.FC = ({
{/* ZFS ARC Hit Ratio */} -
+
ZFS ARC Cache - +
-
- {status?.arcHitRatioPercent || 98.6}% +
+ {status?.arcHitRatioPercent || 98.6}% + Efficiency

- ARC Size: 4.0 GB (Max: 4.0 GB) + ARC Size: 4.0 GB (Target: 4.0 GB)

-
+
Optimal cache hit performance
{/* Workloads Card */} -
+
Running Workloads @@ -120,7 +165,7 @@ export const OverviewView: React.FC = ({
-
+
@@ -134,10 +179,10 @@ export const OverviewView: React.FC = ({
{/* Network Shares */} -
+
Active File Shares - +
@@ -148,7 +193,173 @@ export const OverviewView: React.FC = ({

- Bonjour / mDNS discovery enabled + Bonjour / mDNS discovery active +
+
+
+ + {/* SEAMLESS LIVE TELEMETRY & SYSTEM PERFORMANCE */} +
+
+
+
+ +
+
+
+

Live System & Storage Telemetry

+ + + + +
+

+ Real-time OpenZFS ARC caching, storage pool I/O bandwidth, and host telemetry +

+
+
+ +
+
+ {(['15m', '1h', '6h', '24h'] as const).map((range) => ( + + ))} +
+
+ Live Feed +
+
+
+ + {/* 4-Panel Telemetry Grid */} +
+ {/* Telemetry 1: ZFS ARC Cache Hit Ratio */} +
+
+
+ + ZFS ARC Hit Ratio +
+ 98.6% +
+ +
+ {arcRatioData.map((val, i) => ( +
+ ))} +
+ +
+ Data: 99.1% + Metadata: 97.8% +
+
+ + {/* Telemetry 2: Storage Pool Read/Write Bandwidth */} +
+
+
+ + Pool I/O Throughput +
+ 142 MB/s Peak +
+ +
+ {throughputData.map((val, i) => ( +
+ ))} +
+ +
+ Read: 94 MB/s + Write: 48 MB/s +
+
+ + {/* Telemetry 3: CPU Core Utilization */} +
+
+
+ + CPU Load & Cores +
+ 12.4% Avg +
+ +
+ {cpuData.map((val, i) => ( +
+ ))} +
+ +
+ 4 Cores active + Load: 0.42, 0.38 +
+
+ + {/* Telemetry 4: Memory & ARC Footprint */} +
+
+
+ + RAM & ARC Footprint +
+ 13.2 / 32.0 GB +
+ +
+
+
+ ZFS ARC Cache + 4.0 GB (100%) +
+
+
+
+
+ +
+
+ Apps & Workloads + 9.2 GB +
+
+
+
+
+
+ +
+ Free: 18.8 GB + ZFS ARC Limit: 4 GB +
@@ -157,32 +368,32 @@ export const OverviewView: React.FC = ({
- +

Appliance Services & Workloads

- - Homarr Dashboard + + Native Workload Engine
{/* Workload 1: Immich Photo Hub */} -
+
- - ONLINE + + ONLINE

Immich Photo Hub

@@ -198,7 +409,7 @@ export const OverviewView: React.FC = ({ href="http://naxos.local:2283" target="_blank" rel="noreferrer" - className="flex items-center gap-1 text-xs text-emerald-400 hover:text-emerald-300 font-medium" + className="flex items-center gap-1 text-xs text-cyan-400 hover:text-cyan-300 font-medium" > Launch @@ -207,15 +418,15 @@ export const OverviewView: React.FC = ({
{/* Workload 2: Jellyfin Media Server */} -
+
- - ONLINE + + ONLINE

Jellyfin Media Server

@@ -231,7 +442,7 @@ export const OverviewView: React.FC = ({ href="http://naxos.local:8096" target="_blank" rel="noreferrer" - className="flex items-center gap-1 text-xs text-emerald-400 hover:text-emerald-300 font-medium" + className="flex items-center gap-1 text-xs text-cyan-400 hover:text-cyan-300 font-medium" > Launch @@ -239,39 +450,8 @@ export const OverviewView: React.FC = ({
- {/* Workload 3: CNCF Perses Analytics */} -
-
-
-
- -
-
- - ONLINE -
-
-

CNCF Perses Telemetry

-

- Declarative visualization for ZFS I/O, ARC caching, and host metrics -

-
-
- - :8080 - - -
-
- - {/* Workload 4: Nextcloud Hub */} -
+ {/* Workload 3: Nextcloud Hub */} +
@@ -279,7 +459,7 @@ export const OverviewView: React.FC = ({
- READY + CATALOG

Nextcloud Hub

@@ -289,11 +469,42 @@ export const OverviewView: React.FC = ({
- Catalog + Ready +
+
+ + {/* Workload 4: Paperless-ngx */} +
+
+
+
+ +
+
+ + CATALOG +
+
+

Paperless-ngx

+

+ Automated document archiving with OCR and full-text search +

+
+
+ + Ready + +
- - {/* Embedded Native Perses Analytics Panel */} -
-
-
- -

Native Perses Telemetry

-
- -
- -
- {/* Simulated Perses Panel 1: Throughput */} -
-
Pool Read/Write Throughput
-
- {[42, 65, 30, 85, 95, 120, 80, 45, 60, 90, 110, 75, 40, 95, 130, 85, 70, 90, 105, 140, 95, 60, 80, 115].map((val, i) => ( -
- ))} -
-
- -30m - 115 MB/s Peak - Now -
-
- - {/* Simulated Perses Panel 2: Memory & ARC */} -
-
System Memory & ZFS ARC Distribution
-
-
-
- ZFS ARC Cache - 4.0 GB -
-
-
-
-
- Services & Containers - 9.2 GB -
-
-
-
-
- Free Available - 18.8 GB -
-
-
-
-
-
-
-
-
); }; diff --git a/src/views/SharesView.tsx b/src/views/SharesView.tsx index 8c7917c..eba2bc0 100644 --- a/src/views/SharesView.tsx +++ b/src/views/SharesView.tsx @@ -66,7 +66,7 @@ export const SharesView: React.FC = ({ diff --git a/src/views/StorageView.tsx b/src/views/StorageView.tsx index 2e9750d..792100b 100644 --- a/src/views/StorageView.tsx +++ b/src/views/StorageView.tsx @@ -69,7 +69,7 @@ export const StorageView: React.FC = ({ onClick={() => setActiveSubTab('pools')} className={`px-3 py-1.5 rounded-lg text-xs font-medium transition ${ activeSubTab === 'pools' - ? 'bg-emerald-600 text-white shadow-sm' + ? 'bg-cyan-600 text-white shadow-sm' : 'text-slate-400 hover:text-white' }`} > @@ -79,7 +79,7 @@ export const StorageView: React.FC = ({ onClick={() => setActiveSubTab('datasets')} className={`px-3 py-1.5 rounded-lg text-xs font-medium transition ${ activeSubTab === 'datasets' - ? 'bg-emerald-600 text-white shadow-sm' + ? 'bg-cyan-600 text-white shadow-sm' : 'text-slate-400 hover:text-white' }`} > @@ -89,7 +89,7 @@ export const StorageView: React.FC = ({ onClick={() => setActiveSubTab('snapshots')} className={`px-3 py-1.5 rounded-lg text-xs font-medium transition ${ activeSubTab === 'snapshots' - ? 'bg-emerald-600 text-white shadow-sm' + ? 'bg-cyan-600 text-white shadow-sm' : 'text-slate-400 hover:text-white' }`} > @@ -112,8 +112,8 @@ export const StorageView: React.FC = ({
{importSuccess && ( -
- +
+ {importSuccess}
)} @@ -125,13 +125,13 @@ export const StorageView: React.FC = ({
-
+

{pool.name} - + {pool.health}

@@ -160,12 +160,33 @@ export const StorageView: React.FC = ({
+ {/* Live Pool I/O Telemetry */} +
+
+ + + Live Pool Bandwidth & IOPS + + 3,420 Read IOPS · 1,180 Write IOPS +
+
+ {[24, 45, 18, 62, 88, 142, 95, 30, 48, 70, 85, 40, 55, 90, 60, 75, 110, 85, 95, 120].map((val, i) => ( +
+ ))} +
+
+ {/* Status summary */}
Last Scrub: {pool.scanStatus || 'Completed with 0 errors'} @@ -181,7 +202,7 @@ export const StorageView: React.FC = ({

- + ZFS Datasets & Mountpoints

Total {datasets.length} datasets @@ -208,7 +229,7 @@ export const StorageView: React.FC = ({ {ds.name} {ds.mountpoint} - {ds.used} + {ds.used} {ds.available} @@ -232,7 +253,7 @@ export const StorageView: React.FC = ({

- + ZFS Point-in-Time Snapshots

Total {snapshots.length} snapshots @@ -255,7 +276,7 @@ export const StorageView: React.FC = ({ {snap.name} {snap.dataset} {snap.creationTime} - {snap.usedBytes} + {snap.usedBytes} diff --git a/src/views/WizardView.tsx b/src/views/WizardView.tsx index 284998a..bb307d7 100644 --- a/src/views/WizardView.tsx +++ b/src/views/WizardView.tsx @@ -27,10 +27,13 @@ export const WizardView: React.FC = () => { return (
-
+
+
+ NaxOS +

NaxOS Appliance Setup Wizard

- Step-by-step guided configuration for storage layout, network, and security + Step-by-step guided configuration for storage layout, network, and security (NixOS 26.05)

@@ -46,9 +49,9 @@ export const WizardView: React.FC = () => {
s.num - ? 'bg-emerald-500/20 text-emerald-400' + ? 'bg-cyan-500/20 text-cyan-400' : 'bg-slate-800 text-slate-500' }`} > @@ -66,7 +69,7 @@ export const WizardView: React.FC = () => { {step === 1 && (

- + Storage Architecture & ZFS Pool

@@ -76,7 +79,7 @@ export const WizardView: React.FC = () => { onClick={() => setStorageMode('import')} className={`p-4 rounded-xl border text-left transition ${ storageMode === 'import' - ? 'bg-emerald-500/10 border-emerald-500 text-white' + ? 'bg-cyan-500/10 border-cyan-500 text-white' : 'bg-slate-950 border-slate-800 text-slate-400 hover:text-white' }`} > @@ -91,7 +94,7 @@ export const WizardView: React.FC = () => { onClick={() => setStorageMode('create')} className={`p-4 rounded-xl border text-left transition ${ storageMode === 'create' - ? 'bg-emerald-500/10 border-emerald-500 text-white' + ? 'bg-cyan-500/10 border-cyan-500 text-white' : 'bg-slate-950 border-slate-800 text-slate-400 hover:text-white' }`} > @@ -108,7 +111,7 @@ export const WizardView: React.FC = () => { type="text" value={poolName} onChange={(e) => setPoolName(e.target.value)} - className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white font-mono text-xs focus:outline-none focus:border-emerald-500" + className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white font-mono text-xs focus:outline-none focus:border-cyan-500" />
@@ -117,7 +120,7 @@ export const WizardView: React.FC = () => { {step === 2 && (

- + Network Configuration

@@ -127,7 +130,7 @@ export const WizardView: React.FC = () => { type="text" value={hostname} onChange={(e) => setHostname(e.target.value)} - className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white font-mono text-xs focus:outline-none focus:border-emerald-500" + className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white font-mono text-xs focus:outline-none focus:border-cyan-500" />
@@ -140,7 +143,7 @@ export const WizardView: React.FC = () => { {step === 3 && (

- + Administrator Credentials

@@ -150,7 +153,7 @@ export const WizardView: React.FC = () => { type="text" value={adminUser} onChange={(e) => setAdminUser(e.target.value)} - className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white font-mono text-xs focus:outline-none focus:border-emerald-500" + className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white font-mono text-xs focus:outline-none focus:border-cyan-500" />
@@ -161,7 +164,7 @@ export const WizardView: React.FC = () => { value={adminPassword} onChange={(e) => setAdminPassword(e.target.value)} placeholder="••••••••••••" - className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white font-mono text-xs focus:outline-none focus:border-emerald-500" + className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white font-mono text-xs focus:outline-none focus:border-cyan-500" />
@@ -170,7 +173,7 @@ export const WizardView: React.FC = () => { {step === 4 && (

- + GitOps Remote & Final Validation

@@ -180,7 +183,7 @@ export const WizardView: React.FC = () => { type="text" value={gitopsUrl} onChange={(e) => setGitopsUrl(e.target.value)} - className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white font-mono text-xs focus:outline-none focus:border-emerald-500" + className="w-full px-3 py-2 bg-slate-950 border border-slate-800 rounded-xl text-white font-mono text-xs focus:outline-none focus:border-cyan-500" />

All future web dashboard configuration adjustments will be committed here. @@ -188,8 +191,8 @@ export const WizardView: React.FC = () => {

{isDone && ( -
- +
+ NaxOS Appliance successfully initialized and ready for production!
)} @@ -212,7 +215,7 @@ export const WizardView: React.FC = () => {