feat: rename WHM→NixServer, cPanel→NixClient, bump v0.2.0

- Rename frontend workspaces: whm/ → nixserver/, cpanel/ → nixclient/
- Rename server route dirs: routes/whm/ → routes/nixserver/, routes/cpanel/ → routes/nixclient/
- Update all API prefixes: /api/whm → /api/nixserver, /api/cpanel → /api/nixclient
- Rename env vars: WHM_PORT → NIXSERVER_PORT, CPANEL_PORT → NIXCLIENT_PORT
- Update config properties: whmPort → nixserverPort, cpanelPort → nixclientPort
- Update all UI labels, login pages, sidebar branding, and error messages
- Update installer and update scripts to match new names
- Add cpanel email route (missing from initial scaffold)
- Add .gitattributes to enforce LF line endings
- Bump version to 0.2.0 across all package.json files
This commit is contained in:
NixReaper
2026-04-05 10:54:15 -05:00
parent 16245b2d4b
commit 92d08d7b79
84 changed files with 243 additions and 234 deletions
+89
View File
@@ -0,0 +1,89 @@
import React, { useEffect, useState } from 'react'
import { Database, Mail, Globe, FolderOpen, Lock, Anchor, Clock, HardDrive } from 'lucide-react'
import { api } from '../api/client'
import { useAuth } from '../contexts/AuthContext'
interface Stats {
diskUsedMb: number
diskLimitMb: number
bandwidthUsedMb: number
bandwidthLimitMb: number
counts: {
emailAccounts: number; databases: number; ftpAccounts: number
subdomains: number; addonDomains: number; sslCertificates: number; cronJobs: number
}
limits: {
maxEmailAccounts: number; maxDatabases: number; maxFtpAccounts: number
maxSubdomains: number; maxAddonDomains: number; maxCronJobs: number
} | null
}
function UsageBar({ used, limit, label }: { used: number; limit: number; label: string }) {
const pct = limit === 0 ? 0 : Math.min(100, Math.round((used / limit) * 100))
const color = pct > 90 ? 'bg-red-500' : pct > 70 ? 'bg-amber-500' : 'bg-sky-500'
return (
<div>
<div className="flex justify-between text-xs mb-1">
<span className="text-[#94a3b8]">{label}</span>
<span className="text-[#64748b]">{used} / {limit === 0 ? '∞' : limit} MB {limit > 0 && `(${pct}%)`}</span>
</div>
<div className="h-1.5 bg-[#1e2d45] rounded-full overflow-hidden">
<div className={`h-full rounded-full transition-all ${color}`} style={{ width: `${pct}%` }} />
</div>
</div>
)
}
function CountCard({ icon: Icon, label, used, limit, to }: { icon: React.ComponentType<{ size?: number; className?: string }>; label: string; used: number; limit: number; to: string }) {
return (
<a href={to} className="bg-[#111827] border border-[#1e2d45] rounded-xl p-4 hover:border-sky-500/30 transition-colors block">
<div className="flex items-center justify-between mb-2">
<span className="text-[#64748b] text-xs">{label}</span>
<Icon size={14} className="text-sky-400" />
</div>
<p className="text-white text-2xl font-bold">{used}</p>
{limit > 0 && <p className="text-[#64748b] text-xs mt-0.5">of {limit}</p>}
{limit === 0 && <p className="text-[#64748b] text-xs mt-0.5">unlimited</p>}
</a>
)
}
export default function Overview() {
const { user } = useAuth()
const [stats, setStats] = useState<Stats | null>(null)
useEffect(() => {
api.get('/nixclient/stats').then(r => setStats(r.data.data)).catch(() => {})
}, [])
return (
<div className="space-y-6">
<div>
<h1 className="text-white text-xl font-semibold">Welcome, {user?.username}</h1>
<p className="text-[#64748b] text-sm">{user?.domain}</p>
</div>
{/* Resource usage */}
{stats && (
<div className="bg-[#111827] border border-[#1e2d45] rounded-xl p-4 space-y-4">
<h2 className="text-white text-sm font-medium">Resource Usage</h2>
<UsageBar used={stats.diskUsedMb} limit={stats.diskLimitMb} label="Disk Space" />
<UsageBar used={stats.bandwidthUsedMb} limit={stats.bandwidthLimitMb} label="Bandwidth" />
</div>
)}
{/* Feature grid */}
{stats && (
<div className="grid grid-cols-2 sm:grid-cols-3 xl:grid-cols-4 gap-3">
<CountCard icon={Mail} label="Email Accounts" used={stats.counts.emailAccounts} limit={stats.limits?.maxEmailAccounts ?? 0} to="/email" />
<CountCard icon={Database} label="Databases" used={stats.counts.databases} limit={stats.limits?.maxDatabases ?? 0} to="/databases" />
<CountCard icon={Anchor} label="FTP Accounts" used={stats.counts.ftpAccounts} limit={stats.limits?.maxFtpAccounts ?? 0} to="/ftp" />
<CountCard icon={Globe} label="Subdomains" used={stats.counts.subdomains} limit={stats.limits?.maxSubdomains ?? 0} to="/domains" />
<CountCard icon={Globe} label="Addon Domains" used={stats.counts.addonDomains} limit={stats.limits?.maxAddonDomains ?? 0} to="/domains" />
<CountCard icon={Lock} label="SSL Certs" used={stats.counts.sslCertificates} limit={0} to="/ssl" />
<CountCard icon={Clock} label="Cron Jobs" used={stats.counts.cronJobs} limit={stats.limits?.maxCronJobs ?? 0} to="/cron" />
</div>
)}
</div>
)
}