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:
+2
-2
@@ -9,8 +9,8 @@ JWT_REFRESH_EXPIRES_IN="7d"
|
||||
|
||||
# Server
|
||||
NODE_ENV="production"
|
||||
WHM_PORT=2087
|
||||
CPANEL_PORT=2083
|
||||
NIXSERVER_PORT=2087
|
||||
NIXCLIENT_PORT=2083
|
||||
HOST="0.0.0.0"
|
||||
|
||||
# Admin
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Default — normalize to LF on commit
|
||||
* text=auto eol=lf
|
||||
|
||||
# Shell scripts must always be LF
|
||||
*.sh text eol=lf
|
||||
|
||||
# Windows-only files (if any)
|
||||
*.bat text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nixpanel/cpanel",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -34,7 +34,7 @@ export default function Layout() {
|
||||
</div>
|
||||
{sidebarOpen && (
|
||||
<div className="overflow-hidden">
|
||||
<p className="text-white text-sm font-semibold leading-tight">NixPanel</p>
|
||||
<p className="text-white text-sm font-semibold leading-tight">NixClient</p>
|
||||
<p className="text-[#64748b] text-xs truncate">{user?.domain}</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -40,7 +40,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
|
||||
const login = async (username: string, password: string) => {
|
||||
const { data } = await api.post('/auth/login', { username, password })
|
||||
if (data.data.role !== 'user') throw new Error('Use WHM to log in as admin or reseller')
|
||||
if (data.data.role !== 'user') throw new Error('Use NixServer to log in as admin or reseller')
|
||||
localStorage.setItem('access_token', data.data.accessToken)
|
||||
localStorage.setItem('refresh_token', data.data.refreshToken)
|
||||
setUser(data.data.user)
|
||||
@@ -22,7 +22,7 @@ export default function CronJobs() {
|
||||
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try { const r = await api.get('/cpanel/cron'); setJobs(r.data.data) }
|
||||
try { const r = await api.get('/nixclient/cron'); setJobs(r.data.data) }
|
||||
finally { setLoading(false) }
|
||||
}
|
||||
|
||||
@@ -30,18 +30,18 @@ export default function CronJobs() {
|
||||
|
||||
const save = async (e: React.FormEvent) => {
|
||||
e.preventDefault(); setSaving(true); setError('')
|
||||
try { await api.post('/cpanel/cron', form); setShowModal(false); setForm({ schedule: '0 * * * *', command: '', comment: '', enabled: true }); fetch() }
|
||||
try { await api.post('/nixclient/cron', form); setShowModal(false); setForm({ schedule: '0 * * * *', command: '', comment: '', enabled: true }); fetch() }
|
||||
catch (err: unknown) { setError((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
finally { setSaving(false) }
|
||||
}
|
||||
|
||||
const del = async (id: number) => {
|
||||
if (!confirm('Delete this cron job?')) return
|
||||
try { await api.delete(`/cpanel/cron/${id}`); fetch() } catch {}
|
||||
try { await api.delete(`/nixclient/cron/${id}`); fetch() } catch {}
|
||||
}
|
||||
|
||||
const toggle = async (job: CronJob) => {
|
||||
try { await api.put(`/cpanel/cron/${job.id}`, { enabled: !job.enabled }); fetch() } catch {}
|
||||
try { await api.put(`/nixclient/cron/${job.id}`, { enabled: !job.enabled }); fetch() } catch {}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -18,7 +18,7 @@ export default function Databases() {
|
||||
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try { const r = await api.get('/cpanel/databases'); setDbs(r.data.data) }
|
||||
try { const r = await api.get('/nixclient/databases'); setDbs(r.data.data) }
|
||||
finally { setLoading(false) }
|
||||
}
|
||||
|
||||
@@ -26,27 +26,27 @@ export default function Databases() {
|
||||
|
||||
const createDb = async (e: React.FormEvent) => {
|
||||
e.preventDefault(); setSaving(true); setError('')
|
||||
try { await api.post('/cpanel/databases', dbForm); setShowDbModal(false); setDbForm({ name: '' }); fetch() }
|
||||
try { await api.post('/nixclient/databases', dbForm); setShowDbModal(false); setDbForm({ name: '' }); fetch() }
|
||||
catch (err: unknown) { setError((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
finally { setSaving(false) }
|
||||
}
|
||||
|
||||
const deleteDb = async (id: number) => {
|
||||
if (!confirm('Delete this database and all its data?')) return
|
||||
try { await api.delete(`/cpanel/databases/${id}`); fetch() }
|
||||
try { await api.delete(`/nixclient/databases/${id}`); fetch() }
|
||||
catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
}
|
||||
|
||||
const createUser = async (e: React.FormEvent) => {
|
||||
e.preventDefault(); if (!showUserModal) return
|
||||
setSaving(true); setError('')
|
||||
try { await api.post(`/cpanel/databases/${showUserModal}/users`, userForm); setShowUserModal(null); setUserForm({ username: '', password: '', privileges: 'ALL' }); fetch() }
|
||||
try { await api.post(`/nixclient/databases/${showUserModal}/users`, userForm); setShowUserModal(null); setUserForm({ username: '', password: '', privileges: 'ALL' }); fetch() }
|
||||
catch (err: unknown) { setError((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
finally { setSaving(false) }
|
||||
}
|
||||
|
||||
const deleteUser = async (dbId: number, userId: number) => {
|
||||
try { await api.delete(`/cpanel/databases/${dbId}/users/${userId}`); fetch() }
|
||||
try { await api.delete(`/nixclient/databases/${dbId}/users/${userId}`); fetch() }
|
||||
catch {}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function Domains() {
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const r = await api.get(`/cpanel/domains/${tab}`)
|
||||
const r = await api.get(`/nixclient/domains/${tab}`)
|
||||
setItems(r.data.data)
|
||||
} finally { setLoading(false) }
|
||||
}
|
||||
@@ -28,9 +28,9 @@ export default function Domains() {
|
||||
const save = async (e: React.FormEvent) => {
|
||||
e.preventDefault(); setSaving(true); setError('')
|
||||
try {
|
||||
if (tab === 'subdomains') await api.post('/cpanel/domains/subdomains', { subdomain: form.subdomain })
|
||||
else if (tab === 'addon') await api.post('/cpanel/domains/addon', { domain: form.domain })
|
||||
else await api.post('/cpanel/domains/parked', { domain: form.domain })
|
||||
if (tab === 'subdomains') await api.post('/nixclient/domains/subdomains', { subdomain: form.subdomain })
|
||||
else if (tab === 'addon') await api.post('/nixclient/domains/addon', { domain: form.domain })
|
||||
else await api.post('/nixclient/domains/parked', { domain: form.domain })
|
||||
setShowModal(false); setForm({ subdomain: '', domain: '' }); fetch()
|
||||
} catch (err: unknown) { setError((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
finally { setSaving(false) }
|
||||
@@ -39,9 +39,9 @@ export default function Domains() {
|
||||
const del = async (id: number) => {
|
||||
if (!confirm('Remove this domain?')) return
|
||||
try {
|
||||
if (tab === 'subdomains') await api.delete(`/cpanel/domains/subdomains/${id}`)
|
||||
else if (tab === 'addon') await api.delete(`/cpanel/domains/addon/${id}`)
|
||||
else await api.delete(`/cpanel/domains/parked/${id}`)
|
||||
if (tab === 'subdomains') await api.delete(`/nixclient/domains/subdomains/${id}`)
|
||||
else if (tab === 'addon') await api.delete(`/nixclient/domains/addon/${id}`)
|
||||
else await api.delete(`/nixclient/domains/parked/${id}`)
|
||||
fetch()
|
||||
} catch {}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export default function Email() {
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const [em, fw] = await Promise.all([api.get('/cpanel/email'), api.get('/cpanel/email/forwarders')])
|
||||
const [em, fw] = await Promise.all([api.get('/nixclient/email'), api.get('/nixclient/email/forwarders')])
|
||||
setAccounts(em.data.data)
|
||||
setForwarders(fw.data.data)
|
||||
} finally { setLoading(false) }
|
||||
@@ -31,7 +31,7 @@ export default function Email() {
|
||||
const createAccount = async (e: React.FormEvent) => {
|
||||
e.preventDefault(); setSaving(true); setError('')
|
||||
try {
|
||||
await api.post('/cpanel/email', form)
|
||||
await api.post('/nixclient/email', form)
|
||||
setShowModal(false); fetch()
|
||||
} catch (err: unknown) { setError((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
finally { setSaving(false) }
|
||||
@@ -39,18 +39,18 @@ export default function Email() {
|
||||
|
||||
const del = async (id: number) => {
|
||||
if (!confirm('Delete this email account?')) return
|
||||
try { await api.delete(`/cpanel/email/${id}`); fetch() } catch {}
|
||||
try { await api.delete(`/nixclient/email/${id}`); fetch() } catch {}
|
||||
}
|
||||
|
||||
const addForwarder = async (e: React.FormEvent) => {
|
||||
e.preventDefault(); setSaving(true); setError('')
|
||||
try { await api.post('/cpanel/email/forwarders', fwdForm); setShowFwd(false); fetch() }
|
||||
try { await api.post('/nixclient/email/forwarders', fwdForm); setShowFwd(false); fetch() }
|
||||
catch (err: unknown) { setError((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
finally { setSaving(false) }
|
||||
}
|
||||
|
||||
const delFwd = async (id: number) => {
|
||||
try { await api.delete(`/cpanel/email/forwarders/${id}`); fetch() } catch {}
|
||||
try { await api.delete(`/nixclient/email/forwarders/${id}`); fetch() } catch {}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -14,7 +14,7 @@ export default function FTP() {
|
||||
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try { const r = await api.get('/cpanel/ftp'); setAccounts(r.data.data) }
|
||||
try { const r = await api.get('/nixclient/ftp'); setAccounts(r.data.data) }
|
||||
finally { setLoading(false) }
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ export default function FTP() {
|
||||
|
||||
const save = async (e: React.FormEvent) => {
|
||||
e.preventDefault(); setSaving(true); setError('')
|
||||
try { await api.post('/cpanel/ftp', form); setShowModal(false); fetch() }
|
||||
try { await api.post('/nixclient/ftp', form); setShowModal(false); fetch() }
|
||||
catch (err: unknown) { setError((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
finally { setSaving(false) }
|
||||
}
|
||||
|
||||
const del = async (id: number) => {
|
||||
if (!confirm('Delete this FTP account?')) return
|
||||
try { await api.delete(`/cpanel/ftp/${id}`); fetch() } catch {}
|
||||
try { await api.delete(`/nixclient/ftp/${id}`); fetch() } catch {}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -19,7 +19,7 @@ export default function FileManager() {
|
||||
const loadDir = async (p: string) => {
|
||||
setLoading(true); setSelected(new Set())
|
||||
try {
|
||||
const r = await api.get(`/cpanel/files?path=${encodeURIComponent(p)}`)
|
||||
const r = await api.get(`/nixclient/files?path=${encodeURIComponent(p)}`)
|
||||
setFiles(r.data.data.files); setPath(r.data.data.path)
|
||||
} catch (e: unknown) {
|
||||
alert((e as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed to load directory')
|
||||
@@ -35,7 +35,7 @@ export default function FileManager() {
|
||||
|
||||
const openFile = async (filePath: string) => {
|
||||
try {
|
||||
const r = await api.get(`/cpanel/files/content?path=${encodeURIComponent(filePath)}`)
|
||||
const r = await api.get(`/nixclient/files/content?path=${encodeURIComponent(filePath)}`)
|
||||
setEditing({ path: filePath, content: r.data.data.content })
|
||||
} catch (e: unknown) {
|
||||
alert((e as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Cannot open file')
|
||||
@@ -46,7 +46,7 @@ export default function FileManager() {
|
||||
if (!editing) return
|
||||
setSaving(true)
|
||||
try {
|
||||
await api.put('/cpanel/files/content', { path: editing.path, content: editing.content })
|
||||
await api.put('/nixclient/files/content', { path: editing.path, content: editing.content })
|
||||
setEditing(null)
|
||||
} catch (e: unknown) {
|
||||
alert((e as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Save failed')
|
||||
@@ -56,7 +56,7 @@ export default function FileManager() {
|
||||
const deleteSelected = async () => {
|
||||
if (!selected.size || !confirm(`Delete ${selected.size} item(s)?`)) return
|
||||
try {
|
||||
await api.delete('/cpanel/files', { data: { paths: Array.from(selected) } })
|
||||
await api.delete('/nixclient/files', { data: { paths: Array.from(selected) } })
|
||||
loadDir(path)
|
||||
} catch {}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ export default function FileManager() {
|
||||
const createDir = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
await api.post('/cpanel/files/mkdir', { path: `${path}/${newDir}` })
|
||||
await api.post('/nixclient/files/mkdir', { path: `${path}/${newDir}` })
|
||||
setNewDir(''); setShowNewDir(false); loadDir(path)
|
||||
} catch (e: unknown) {
|
||||
alert((e as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed')
|
||||
@@ -27,7 +27,7 @@ export default function Login() {
|
||||
<div className="w-12 h-12 rounded-2xl bg-sky-500 flex items-center justify-center mb-3">
|
||||
<span className="text-white text-xl font-bold">N</span>
|
||||
</div>
|
||||
<h1 className="text-white text-xl font-bold">NixPanel</h1>
|
||||
<h1 className="text-white text-xl font-bold">NixClient</h1>
|
||||
<p className="text-[#64748b] text-sm mt-1">Hosting Control Panel</p>
|
||||
</div>
|
||||
|
||||
@@ -49,7 +49,7 @@ export default function Login() {
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p className="text-center text-[#64748b] text-xs mt-4">NixPanel v0.1.0 · Port 2083</p>
|
||||
<p className="text-center text-[#64748b] text-xs mt-4">NixClient v0.2.0 · Port 2083</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -53,7 +53,7 @@ export default function Overview() {
|
||||
const [stats, setStats] = useState<Stats | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
api.get('/cpanel/stats').then(r => setStats(r.data.data)).catch(() => {})
|
||||
api.get('/nixclient/stats').then(r => setStats(r.data.data)).catch(() => {})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
@@ -10,14 +10,14 @@ export default function SSL() {
|
||||
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try { const r = await api.get('/whm/ssl'); setCerts(r.data.data) }
|
||||
try { const r = await api.get('/nixserver/ssl'); setCerts(r.data.data) }
|
||||
finally { setLoading(false) }
|
||||
}
|
||||
|
||||
useEffect(() => { fetch() }, [])
|
||||
|
||||
const renew = async (id: number) => {
|
||||
try { await api.post(`/whm/ssl/${id}/renew`); fetch() }
|
||||
try { await api.post(`/nixserver/ssl/${id}/renew`); fetch() }
|
||||
catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Renewal failed') }
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export default function Stats() {
|
||||
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try { const r = await api.get('/cpanel/stats'); setStats(r.data.data) }
|
||||
try { const r = await api.get('/nixclient/stats'); setStats(r.data.data) }
|
||||
finally { setLoading(false) }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nixpanel/whm",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -49,8 +49,8 @@ export default function Layout() {
|
||||
</div>
|
||||
{sidebarOpen && (
|
||||
<div className="overflow-hidden">
|
||||
<p className="text-white text-sm font-semibold leading-tight">NixPanel</p>
|
||||
<p className="text-[#64748b] text-xs">WHM</p>
|
||||
<p className="text-white text-sm font-semibold leading-tight">NixServer</p>
|
||||
<p className="text-[#64748b] text-xs">Admin Panel</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -44,7 +44,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
const login = async (username: string, password: string) => {
|
||||
const { data } = await api.post('/auth/login', { username, password })
|
||||
if (data.data.role !== 'admin' && data.data.role !== 'reseller') {
|
||||
throw new Error('Access denied — WHM is for administrators and resellers only')
|
||||
throw new Error('Access denied — NixServer is for administrators and resellers only')
|
||||
}
|
||||
localStorage.setItem('access_token', data.data.accessToken)
|
||||
localStorage.setItem('refresh_token', data.data.refreshToken)
|
||||
@@ -43,7 +43,7 @@ export default function Accounts() {
|
||||
const params = new URLSearchParams({ page: String(page), limit: '25' })
|
||||
if (search) params.set('search', search)
|
||||
if (status) params.set('status', status)
|
||||
const r = await api.get(`/whm/accounts?${params}`)
|
||||
const r = await api.get(`/nixserver/accounts?${params}`)
|
||||
setData(r.data.data)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
@@ -56,9 +56,9 @@ export default function Accounts() {
|
||||
setActionMenu(null)
|
||||
if (act === 'terminate' && !confirm('Terminate this account? This cannot be undone.')) return
|
||||
try {
|
||||
if (act === 'suspend') await api.post(`/whm/accounts/${id}/suspend`)
|
||||
else if (act === 'unsuspend') await api.post(`/whm/accounts/${id}/unsuspend`)
|
||||
else await api.delete(`/whm/accounts/${id}`)
|
||||
if (act === 'suspend') await api.post(`/nixserver/accounts/${id}/suspend`)
|
||||
else if (act === 'unsuspend') await api.post(`/nixserver/accounts/${id}/unsuspend`)
|
||||
else await api.delete(`/nixserver/accounts/${id}`)
|
||||
fetch()
|
||||
} catch (e: unknown) {
|
||||
alert((e as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Action failed')
|
||||
@@ -16,7 +16,7 @@ export default function Backup() {
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const [b, a] = await Promise.all([api.get('/whm/backup/list'), api.get('/whm/accounts?limit=100')])
|
||||
const [b, a] = await Promise.all([api.get('/nixserver/backup/list'), api.get('/nixserver/accounts?limit=100')])
|
||||
setBackups(b.data.data)
|
||||
setAccounts(a.data.data.items)
|
||||
} finally { setLoading(false) }
|
||||
@@ -27,7 +27,7 @@ export default function Backup() {
|
||||
const backupAccount = async () => {
|
||||
if (!selectedAccount) return alert('Select an account')
|
||||
setBacking(true)
|
||||
try { await api.post(`/whm/backup/account/${selectedAccount}`); fetch() }
|
||||
try { await api.post(`/nixserver/backup/account/${selectedAccount}`); fetch() }
|
||||
catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Backup failed') }
|
||||
finally { setBacking(false) }
|
||||
}
|
||||
@@ -35,14 +35,14 @@ export default function Backup() {
|
||||
const backupServer = async () => {
|
||||
if (!confirm('Start a full server backup? This may take a while.')) return
|
||||
setBacking(true)
|
||||
try { await api.post('/whm/backup/server'); fetch() }
|
||||
try { await api.post('/nixserver/backup/server'); fetch() }
|
||||
catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Backup failed') }
|
||||
finally { setBacking(false) }
|
||||
}
|
||||
|
||||
const del = async (path: string) => {
|
||||
if (!confirm('Delete this backup?')) return
|
||||
try { await api.delete('/whm/backup', { data: { path } }); fetch() }
|
||||
try { await api.delete('/nixserver/backup', { data: { path } }); fetch() }
|
||||
catch {}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function CreateAccount() {
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
api.get('/whm/packages').then(r => setPackages(r.data.data)).catch(() => {})
|
||||
api.get('/nixserver/packages').then(r => setPackages(r.data.data)).catch(() => {})
|
||||
}, [])
|
||||
|
||||
const set = (k: keyof typeof form) => (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) =>
|
||||
@@ -26,7 +26,7 @@ export default function CreateAccount() {
|
||||
setError('')
|
||||
setLoading(true)
|
||||
try {
|
||||
await api.post('/whm/accounts', {
|
||||
await api.post('/nixserver/accounts', {
|
||||
...form,
|
||||
packageId: form.packageId ? parseInt(form.packageId, 10) : undefined,
|
||||
})
|
||||
@@ -56,7 +56,7 @@ export default function CreateAccount() {
|
||||
{ key: 'username', label: 'Username', type: 'text', placeholder: 'john', hint: 'Lowercase letters, numbers, hyphens' },
|
||||
{ key: 'domain', label: 'Primary Domain', type: 'text', placeholder: 'example.com' },
|
||||
{ key: 'email', label: 'Contact Email', type: 'email', placeholder: '[email protected]' },
|
||||
{ key: 'password', label: 'cPanel Password', type: 'password', placeholder: '••••••••', hint: 'Min 8 characters' },
|
||||
{ key: 'password', label: 'NixClient Password', type: 'password', placeholder: '••••••••', hint: 'Min 8 characters' },
|
||||
].map(({ key, label, type, placeholder, hint }) => (
|
||||
<div key={key}>
|
||||
<label className="block text-[#94a3b8] text-xs font-medium mb-1.5">{label}</label>
|
||||
@@ -16,13 +16,13 @@ export default function DNS() {
|
||||
|
||||
const fetchZones = async () => {
|
||||
setLoading(true)
|
||||
try { const r = await api.get('/whm/dns'); setZones(r.data.data) }
|
||||
try { const r = await api.get('/nixserver/dns'); setZones(r.data.data) }
|
||||
finally { setLoading(false) }
|
||||
}
|
||||
|
||||
const loadZone = async (zone: Zone) => {
|
||||
setSelected(zone)
|
||||
const r = await api.get(`/whm/dns/${zone.id}`)
|
||||
const r = await api.get(`/nixserver/dns/${zone.id}`)
|
||||
setRecords(r.data.data.records ?? [])
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export default function DNS() {
|
||||
e.preventDefault(); if (!selected) return
|
||||
setSaving(true)
|
||||
try {
|
||||
await api.post(`/whm/dns/${selected.id}/records`, { ...form, priority: form.priority ? parseInt(form.priority, 10) : undefined })
|
||||
await api.post(`/nixserver/dns/${selected.id}/records`, { ...form, priority: form.priority ? parseInt(form.priority, 10) : undefined })
|
||||
setShowForm(false)
|
||||
loadZone(selected)
|
||||
} catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
@@ -41,7 +41,7 @@ export default function DNS() {
|
||||
|
||||
const deleteRecord = async (recordId: number) => {
|
||||
if (!selected || !confirm('Delete this DNS record?')) return
|
||||
try { await api.delete(`/whm/dns/${selected.id}/records/${recordId}`); loadZone(selected) }
|
||||
try { await api.delete(`/nixserver/dns/${selected.id}/records/${recordId}`); loadZone(selected) }
|
||||
catch {}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ export default function Dashboard() {
|
||||
const wsRef = useRef<WebSocket | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
api.get('/whm/accounts/stats').then(r => setAccountStats(r.data.data)).catch(() => {})
|
||||
api.get('/whm/system/stats').then(r => setStats(r.data.data)).catch(() => {})
|
||||
api.get('/nixserver/accounts/stats').then(r => setAccountStats(r.data.data)).catch(() => {})
|
||||
api.get('/nixserver/system/stats').then(r => setStats(r.data.data)).catch(() => {})
|
||||
|
||||
// WebSocket live stats
|
||||
const token = localStorage.getItem('access_token')
|
||||
@@ -16,7 +16,7 @@ export default function Email() {
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const [em, acc] = await Promise.all([api.get('/whm/email'), api.get('/whm/accounts?limit=100')])
|
||||
const [em, acc] = await Promise.all([api.get('/nixserver/email'), api.get('/nixserver/accounts?limit=100')])
|
||||
setAccounts(em.data.data)
|
||||
setHosting(acc.data.data.items)
|
||||
} finally { setLoading(false) }
|
||||
@@ -27,7 +27,7 @@ export default function Email() {
|
||||
const save = async (e: React.FormEvent) => {
|
||||
e.preventDefault(); setSaving(true); setError('')
|
||||
try {
|
||||
await api.post('/whm/email', { ...form, accountId: parseInt(form.accountId, 10), quotaMb: form.quotaMb })
|
||||
await api.post('/nixserver/email', { ...form, accountId: parseInt(form.accountId, 10), quotaMb: form.quotaMb })
|
||||
setShowModal(false); fetch()
|
||||
} catch (err: unknown) { setError((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
finally { setSaving(false) }
|
||||
@@ -35,7 +35,7 @@ export default function Email() {
|
||||
|
||||
const del = async (id: number, address: string) => {
|
||||
if (!confirm(`Delete ${address}?`)) return
|
||||
try { await api.delete(`/whm/email/${id}`); fetch() }
|
||||
try { await api.delete(`/nixserver/email/${id}`); fetch() }
|
||||
catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export default function Login() {
|
||||
<div className="w-12 h-12 rounded-2xl bg-indigo-600 flex items-center justify-center mb-3">
|
||||
<Shield size={24} className="text-white" />
|
||||
</div>
|
||||
<h1 className="text-white text-xl font-bold">NixPanel WHM</h1>
|
||||
<h1 className="text-white text-xl font-bold">NixServer</h1>
|
||||
<p className="text-[#64748b] text-sm mt-1">Web Host Manager</p>
|
||||
</div>
|
||||
|
||||
@@ -81,7 +81,7 @@ export default function Login() {
|
||||
</form>
|
||||
|
||||
<p className="text-center text-[#64748b] text-xs mt-4">
|
||||
NixPanel v0.1.0 · WHM Port 2087
|
||||
NixServer v0.2.0 · Port 2087
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -13,7 +13,7 @@ export default function PHP() {
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const [v, a] = await Promise.all([api.get('/whm/php/versions'), api.get('/whm/php/accounts')])
|
||||
const [v, a] = await Promise.all([api.get('/nixserver/php/versions'), api.get('/nixserver/php/accounts')])
|
||||
setVersions(v.data.data)
|
||||
setAccounts(a.data.data)
|
||||
} finally { setLoading(false) }
|
||||
@@ -23,7 +23,7 @@ export default function PHP() {
|
||||
|
||||
const changeVersion = async (accountId: number, phpVersion: string) => {
|
||||
try {
|
||||
await api.put(`/whm/php/accounts/${accountId}`, { phpVersion })
|
||||
await api.put(`/nixserver/php/accounts/${accountId}`, { phpVersion })
|
||||
fetch()
|
||||
} catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export default function Packages() {
|
||||
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try { const r = await api.get('/whm/packages'); setPackages(r.data.data) }
|
||||
try { const r = await api.get('/nixserver/packages'); setPackages(r.data.data) }
|
||||
finally { setLoading(false) }
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ export default function Packages() {
|
||||
const save = async (e: React.FormEvent) => {
|
||||
e.preventDefault(); setSaving(true); setError('')
|
||||
try {
|
||||
if (editing) await api.put(`/whm/packages/${editing.id}`, form)
|
||||
else await api.post('/whm/packages', form)
|
||||
if (editing) await api.put(`/nixserver/packages/${editing.id}`, form)
|
||||
else await api.post('/nixserver/packages', form)
|
||||
setShowModal(false); fetch()
|
||||
} catch (err: unknown) {
|
||||
setError((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Save failed')
|
||||
@@ -54,7 +54,7 @@ export default function Packages() {
|
||||
|
||||
const del = async (p: Package) => {
|
||||
if (!confirm(`Delete package "${p.name}"?`)) return
|
||||
try { await api.delete(`/whm/packages/${p.id}`); fetch() }
|
||||
try { await api.delete(`/nixserver/packages/${p.id}`); fetch() }
|
||||
catch (err: unknown) {
|
||||
alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Delete failed')
|
||||
}
|
||||
@@ -17,7 +17,7 @@ export default function Resellers() {
|
||||
setLoading(true)
|
||||
try {
|
||||
const params = search ? `?search=${encodeURIComponent(search)}` : ''
|
||||
const r = await api.get(`/whm/resellers${params}`)
|
||||
const r = await api.get(`/nixserver/resellers${params}`)
|
||||
setItems(r.data.data.items ?? r.data.data)
|
||||
} finally { setLoading(false) }
|
||||
}, [search])
|
||||
@@ -26,7 +26,7 @@ export default function Resellers() {
|
||||
|
||||
const save = async (e: React.FormEvent) => {
|
||||
e.preventDefault(); setSaving(true); setError('')
|
||||
try { await api.post('/whm/resellers', form); setShowModal(false); fetch() }
|
||||
try { await api.post('/nixserver/resellers', form); setShowModal(false); fetch() }
|
||||
catch (err: unknown) { setError((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
finally { setSaving(false) }
|
||||
}
|
||||
@@ -34,9 +34,9 @@ export default function Resellers() {
|
||||
const act = async (id: number, action: 'suspend' | 'unsuspend' | 'delete') => {
|
||||
if (action === 'delete' && !confirm('Delete this reseller?')) return
|
||||
try {
|
||||
if (action === 'suspend') await api.post(`/whm/resellers/${id}/suspend`)
|
||||
else if (action === 'unsuspend') await api.post(`/whm/resellers/${id}/unsuspend`)
|
||||
else await api.delete(`/whm/resellers/${id}`)
|
||||
if (action === 'suspend') await api.post(`/nixserver/resellers/${id}/suspend`)
|
||||
else if (action === 'unsuspend') await api.post(`/nixserver/resellers/${id}/unsuspend`)
|
||||
else await api.delete(`/nixserver/resellers/${id}`)
|
||||
fetch()
|
||||
} catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
}
|
||||
@@ -23,7 +23,7 @@ export default function SSL() {
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const [sl, acc] = await Promise.all([api.get('/whm/ssl'), api.get('/whm/accounts?limit=100')])
|
||||
const [sl, acc] = await Promise.all([api.get('/nixserver/ssl'), api.get('/nixserver/accounts?limit=100')])
|
||||
setCerts(sl.data.data)
|
||||
setHosting(acc.data.data.items)
|
||||
} finally { setLoading(false) }
|
||||
@@ -34,20 +34,20 @@ export default function SSL() {
|
||||
const issue = async (e: React.FormEvent) => {
|
||||
e.preventDefault(); setSaving(true); setError('')
|
||||
try {
|
||||
await api.post('/whm/ssl/issue', { ...form, accountId: parseInt(form.accountId, 10) })
|
||||
await api.post('/nixserver/ssl/issue', { ...form, accountId: parseInt(form.accountId, 10) })
|
||||
setShowModal(false); fetch()
|
||||
} catch (err: unknown) { setError((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
finally { setSaving(false) }
|
||||
}
|
||||
|
||||
const renew = async (id: number) => {
|
||||
try { await api.post(`/whm/ssl/${id}/renew`); fetch() }
|
||||
try { await api.post(`/nixserver/ssl/${id}/renew`); fetch() }
|
||||
catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Renewal failed') }
|
||||
}
|
||||
|
||||
const revoke = async (id: number) => {
|
||||
if (!confirm('Revoke this certificate?')) return
|
||||
try { await api.post(`/whm/ssl/${id}/revoke`); fetch() }
|
||||
try { await api.post(`/nixserver/ssl/${id}/revoke`); fetch() }
|
||||
catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Revoke failed') }
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ export default function Security() {
|
||||
setLoading(true)
|
||||
try {
|
||||
const [b, e] = await Promise.all([
|
||||
api.get('/whm/security/blocked-ips'),
|
||||
api.get('/whm/security/events'),
|
||||
api.get('/nixserver/security/blocked-ips'),
|
||||
api.get('/nixserver/security/events'),
|
||||
])
|
||||
setBlocked(b.data.data)
|
||||
setEvents(e.data.data.items ?? e.data.data)
|
||||
@@ -34,17 +34,17 @@ export default function Security() {
|
||||
|
||||
const block = async (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
try { await api.post('/whm/security/blocked-ips', { ip: newIp, reason }); setNewIp(''); setReason(''); fetch() }
|
||||
try { await api.post('/nixserver/security/blocked-ips', { ip: newIp, reason }); setNewIp(''); setReason(''); fetch() }
|
||||
catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
}
|
||||
|
||||
const unblock = async (id: number) => {
|
||||
try { await api.delete(`/whm/security/blocked-ips/${id}`); fetch() }
|
||||
try { await api.delete(`/nixserver/security/blocked-ips/${id}`); fetch() }
|
||||
catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
}
|
||||
|
||||
const resolve = async (id: number) => {
|
||||
try { await api.post(`/whm/security/events/${id}/resolve`); fetch() }
|
||||
try { await api.post(`/nixserver/security/events/${id}/resolve`); fetch() }
|
||||
catch {}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function Services() {
|
||||
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try { const r = await api.get('/whm/services'); setServices(r.data.data) }
|
||||
try { const r = await api.get('/nixserver/services'); setServices(r.data.data) }
|
||||
finally { setLoading(false) }
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function Services() {
|
||||
|
||||
const act = async (name: string, action: string) => {
|
||||
setActing(name + action)
|
||||
try { await api.post(`/whm/services/${name}`, { action }); fetch() }
|
||||
try { await api.post(`/nixserver/services/${name}`, { action }); fetch() }
|
||||
catch (e: unknown) { alert((e as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Failed') }
|
||||
finally { setActing(null) }
|
||||
}
|
||||
@@ -10,7 +10,7 @@ export default function Settings() {
|
||||
|
||||
const fetch = async () => {
|
||||
setLoading(true)
|
||||
try { const r = await api.get('/whm/system/settings'); setSettings(r.data.data) }
|
||||
try { const r = await api.get('/nixserver/system/settings'); setSettings(r.data.data) }
|
||||
finally { setLoading(false) }
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function Settings() {
|
||||
|
||||
const save = async (e: React.FormEvent) => {
|
||||
e.preventDefault(); setSaving(true)
|
||||
try { await api.put('/whm/system/settings', settings); setSaved(true); setTimeout(() => setSaved(false), 2000) }
|
||||
try { await api.put('/nixserver/system/settings', settings); setSaved(true); setTimeout(() => setSaved(false), 2000) }
|
||||
finally { setSaving(false) }
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ export default function System() {
|
||||
setLoading(true)
|
||||
try {
|
||||
const [infoR, procR] = await Promise.all([
|
||||
api.get('/whm/system/info'),
|
||||
api.get('/whm/system/processes'),
|
||||
api.get('/nixserver/system/info'),
|
||||
api.get('/nixserver/system/processes'),
|
||||
])
|
||||
setInfo(infoR.data.data)
|
||||
setProcesses(procR.data.data)
|
||||
@@ -14,9 +14,9 @@ export default function WebServer() {
|
||||
setLoading(true)
|
||||
try {
|
||||
const [s, v, l] = await Promise.all([
|
||||
api.get('/whm/webserver/status'),
|
||||
api.get('/whm/webserver/vhosts'),
|
||||
api.get(`/whm/webserver/logs/${logType}`),
|
||||
api.get('/nixserver/webserver/status'),
|
||||
api.get('/nixserver/webserver/vhosts'),
|
||||
api.get(`/nixserver/webserver/logs/${logType}`),
|
||||
])
|
||||
setStatus(s.data.data); setVhosts(v.data.data); setLogs(l.data.data)
|
||||
} finally { setLoading(false) }
|
||||
@@ -25,12 +25,12 @@ export default function WebServer() {
|
||||
useEffect(() => { fetch() }, [logType])
|
||||
|
||||
const testConfig = async () => {
|
||||
const r = await api.post('/whm/webserver/nginx/test')
|
||||
const r = await api.post('/nixserver/webserver/nginx/test')
|
||||
setTestResult(r.data.data.output)
|
||||
}
|
||||
|
||||
const reload = async () => {
|
||||
try { await api.post('/whm/webserver/nginx/reload'); fetch() }
|
||||
try { await api.post('/nixserver/webserver/nginx/reload'); fetch() }
|
||||
catch (err: unknown) { alert((err as { response?: { data?: { error?: string } } })?.response?.data?.error ?? 'Reload failed') }
|
||||
}
|
||||
|
||||
+7
-7
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "nixpanel-server",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"server",
|
||||
"whm",
|
||||
"cpanel"
|
||||
"nixserver",
|
||||
"nixclient"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "concurrently \"npm run dev --workspace=server\" \"npm run dev --workspace=whm\" \"npm run dev --workspace=cpanel\"",
|
||||
"dev": "concurrently \"npm run dev --workspace=server\" \"npm run dev --workspace=nixserver\" \"npm run dev --workspace=nixclient\"",
|
||||
"dev:server": "npm run dev --workspace=server",
|
||||
"dev:whm": "npm run dev --workspace=whm",
|
||||
"dev:cpanel": "npm run dev --workspace=cpanel",
|
||||
"build": "npm run build --workspace=server && npm run build --workspace=whm && npm run build --workspace=cpanel",
|
||||
"dev:nixserver": "npm run dev --workspace=nixserver",
|
||||
"dev:nixclient": "npm run dev --workspace=nixclient",
|
||||
"build": "npm run build --workspace=server && npm run build --workspace=nixserver && npm run build --workspace=nixclient",
|
||||
"db:migrate": "npm run db:migrate --workspace=server",
|
||||
"db:studio": "npm run db:studio --workspace=server"
|
||||
},
|
||||
|
||||
+18
-18
@@ -30,8 +30,8 @@ EOF
|
||||
echo -e "${RESET}"
|
||||
echo -e " Version : ${BOLD}0.1.0${RESET}"
|
||||
echo -e " GitHub : ${BOLD}https://github.com/NixReaper/nixpanel-server${RESET}"
|
||||
echo -e " WHM : ${BOLD}https://<your-ip>:2087${RESET}"
|
||||
echo -e " cPanel : ${BOLD}https://<your-ip>:2083${RESET}"
|
||||
echo -e " NixServer: ${BOLD}https://<your-ip>:2087${RESET}"
|
||||
echo -e " NixClient: ${BOLD}https://<your-ip>:2083${RESET}"
|
||||
echo ""
|
||||
|
||||
# ── Root check ────────────────────────────────────────────────────────────────
|
||||
@@ -341,8 +341,8 @@ JWT_REFRESH_EXPIRES_IN="7d"
|
||||
|
||||
# Server
|
||||
NODE_ENV="production"
|
||||
WHM_PORT=2087
|
||||
CPANEL_PORT=2083
|
||||
NIXSERVER_PORT=2087
|
||||
NIXCLIENT_PORT=2083
|
||||
HOST="0.0.0.0"
|
||||
|
||||
# Admin credentials (used during initial seed only)
|
||||
@@ -398,11 +398,11 @@ ADMIN_EMAIL="${ADMIN_EMAIL}" \
|
||||
success "Default admin, packages, and settings seeded"
|
||||
|
||||
# ── Build frontends ───────────────────────────────────────────────────────────
|
||||
step "Building WHM + cPanel frontends"
|
||||
step "Building NixServer + NixClient frontends"
|
||||
|
||||
cd "$INSTALL_DIR"
|
||||
npm run build --workspace=whm --silent
|
||||
npm run build --workspace=cpanel --silent
|
||||
npm run build --workspace=nixserver --silent
|
||||
npm run build --workspace=nixclient --silent
|
||||
success "Frontends built"
|
||||
|
||||
# ── systemd service ───────────────────────────────────────────────────────────
|
||||
@@ -453,13 +453,13 @@ fi
|
||||
|
||||
cat > /etc/nginx/sites-available/nixpanel.conf <<NGINX
|
||||
# NixPanel — generated by installer
|
||||
# WHM admin panel on port 2087
|
||||
# NixServer admin panel on port 2087
|
||||
server {
|
||||
listen 2087;
|
||||
server_name _;
|
||||
|
||||
# Serve pre-built WHM React app
|
||||
root ${INSTALL_DIR}/whm/dist;
|
||||
# Serve pre-built NixServer React app
|
||||
root ${INSTALL_DIR}/nixserver/dist;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
@@ -485,12 +485,12 @@ server {
|
||||
}
|
||||
}
|
||||
|
||||
# cPanel user panel on port 2083
|
||||
# NixClient user panel on port 2083
|
||||
server {
|
||||
listen 2083;
|
||||
server_name _;
|
||||
|
||||
root ${INSTALL_DIR}/cpanel/dist;
|
||||
root ${INSTALL_DIR}/nixclient/dist;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
@@ -538,8 +538,8 @@ if [[ "$PKG_MGR" == "apt" ]]; then
|
||||
ufw allow 22/tcp comment "SSH" >/dev/null
|
||||
ufw allow 80/tcp comment "HTTP" >/dev/null
|
||||
ufw allow 443/tcp comment "HTTPS" >/dev/null
|
||||
ufw allow 2087/tcp comment "WHM" >/dev/null
|
||||
ufw allow 2083/tcp comment "cPanel" >/dev/null
|
||||
ufw allow 2087/tcp comment "NixServer" >/dev/null
|
||||
ufw allow 2083/tcp comment "NixClient" >/dev/null
|
||||
ufw allow 21/tcp comment "FTP" >/dev/null
|
||||
ufw allow 25/tcp comment "SMTP" >/dev/null
|
||||
ufw allow 143/tcp comment "IMAP" >/dev/null
|
||||
@@ -637,12 +637,12 @@ echo -e "${BOLD}${GREEN}╔═════════════════
|
||||
echo -e "${BOLD}${GREEN}║ NixPanel Installation Complete! ║${RESET}"
|
||||
echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════════════════╝${RESET}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}WHM (Admin Panel)${RESET}"
|
||||
echo -e " ${BOLD}NixServer (Admin Panel)${RESET}"
|
||||
echo -e " URL : ${CYAN}http://${SERVER_IP}:2087${RESET}"
|
||||
echo -e " Username : ${BOLD}${ADMIN_USER}${RESET}"
|
||||
echo -e " Password : ${BOLD}${ADMIN_PASS}${RESET}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}cPanel (User Panel)${RESET}"
|
||||
echo -e " ${BOLD}NixClient (User Panel)${RESET}"
|
||||
echo -e " URL : ${CYAN}http://${SERVER_IP}:2083${RESET}"
|
||||
echo ""
|
||||
echo -e " ${BOLD}Database${RESET}"
|
||||
@@ -671,8 +671,8 @@ CRED_FILE="/root/.nixpanel_credentials"
|
||||
cat > "$CRED_FILE" <<CREDS
|
||||
NixPanel Credentials — $(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
||||
==========================================================
|
||||
WHM URL : http://${SERVER_IP}:2087
|
||||
cPanel URL : http://${SERVER_IP}:2083
|
||||
NixServer URL : http://${SERVER_IP}:2087
|
||||
NixClient URL : http://${SERVER_IP}:2083
|
||||
Admin User : ${ADMIN_USER}
|
||||
Admin Pass : ${ADMIN_PASS}
|
||||
Admin Email: ${ADMIN_EMAIL}
|
||||
|
||||
+2
-2
@@ -30,8 +30,8 @@ success "Migrations applied"
|
||||
|
||||
step "Rebuilding frontends"
|
||||
cd "$INSTALL_DIR"
|
||||
npm run build --workspace=whm --silent
|
||||
npm run build --workspace=cpanel --silent
|
||||
npm run build --workspace=nixserver --silent
|
||||
npm run build --workspace=nixclient --silent
|
||||
success "Frontends rebuilt"
|
||||
|
||||
step "Rebuilding server"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nixpanel/server",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
+8
-8
@@ -9,8 +9,8 @@ import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { config } from './config.js'
|
||||
import authRoutes from './routes/auth/index.js'
|
||||
import whmRoutes from './routes/whm/index.js'
|
||||
import cpanelRoutes from './routes/cpanel/index.js'
|
||||
import nixserverRoutes from './routes/nixserver/index.js'
|
||||
import nixclientRoutes from './routes/nixclient/index.js'
|
||||
import { getSystemStats } from './core/system.js'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
@@ -59,21 +59,21 @@ export async function buildApp() {
|
||||
|
||||
// ─── Static files ────────────────────────────────────────────────────────────
|
||||
|
||||
// WHM frontend served from /whm
|
||||
const whmDist = path.resolve(__dirname, '../../whm/dist')
|
||||
const cpanelDist = path.resolve(__dirname, '../../cpanel/dist')
|
||||
// NixServer frontend
|
||||
const whmDist = path.resolve(__dirname, '../../nixserver/dist')
|
||||
const cpanelDist = path.resolve(__dirname, '../../nixclient/dist')
|
||||
|
||||
// ─── API Routes ──────────────────────────────────────────────────────────────
|
||||
|
||||
fastify.register(authRoutes, { prefix: '/api/auth' })
|
||||
fastify.register(whmRoutes, { prefix: '/api/whm' })
|
||||
fastify.register(cpanelRoutes, { prefix: '/api/cpanel' })
|
||||
fastify.register(nixserverRoutes, { prefix: '/api/nixserver' })
|
||||
fastify.register(nixclientRoutes, { prefix: '/api/nixclient' })
|
||||
|
||||
// ─── Health check ─────────────────────────────────────────────────────────────
|
||||
|
||||
fastify.get('/api/health', async () => ({
|
||||
status: 'ok',
|
||||
version: '0.1.0',
|
||||
version: '0.2.0',
|
||||
timestamp: new Date().toISOString(),
|
||||
}))
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ export const config = {
|
||||
|
||||
server: {
|
||||
host: optional('HOST', '0.0.0.0'),
|
||||
whmPort: parseInt(optional('WHM_PORT', '2087'), 10),
|
||||
cpanelPort: parseInt(optional('CPANEL_PORT', '2083'), 10),
|
||||
nixserverPort: parseInt(optional('NIXSERVER_PORT', '2087'), 10),
|
||||
nixclientPort: parseInt(optional('NIXCLIENT_PORT', '2083'), 10),
|
||||
},
|
||||
|
||||
db: {
|
||||
|
||||
@@ -45,7 +45,7 @@ $TTL ${ttl}
|
||||
www IN A ${ip}
|
||||
mail IN A ${ip}
|
||||
ftp IN A ${ip}
|
||||
cpanel IN A ${ip}
|
||||
nixclient IN A ${ip}
|
||||
webmail IN A ${ip}
|
||||
|
||||
; MX record
|
||||
|
||||
+4
-4
@@ -13,10 +13,10 @@ async function start() {
|
||||
|
||||
app = await buildApp()
|
||||
|
||||
// WHM server on port 2087
|
||||
await app.listen({ port: config.server.whmPort, host: config.server.host })
|
||||
console.log(`NixPanel WHM running on port ${config.server.whmPort}`)
|
||||
console.log(`NixPanel cPanel API running on port ${config.server.whmPort} (/api/cpanel)`)
|
||||
// NixServer on port 2087
|
||||
await app.listen({ port: config.server.nixserverPort, host: config.server.host })
|
||||
console.log(`NixServer running on port ${config.server.nixserverPort}`)
|
||||
console.log(`NixClient API running on port ${config.server.nixserverPort} (/api/cpanel)`)
|
||||
|
||||
} catch (err) {
|
||||
console.error('Failed to start server:', err)
|
||||
|
||||
@@ -37,7 +37,7 @@ function signTokens(
|
||||
export default async function authRoutes(fastify: FastifyInstance) {
|
||||
/**
|
||||
* POST /api/auth/login
|
||||
* Login for admin, reseller, or cPanel user.
|
||||
* Login for admin, reseller, or NixClient user.
|
||||
* Body: { username, password }
|
||||
* Returns: { accessToken, refreshToken, role, user }
|
||||
*/
|
||||
@@ -111,7 +111,7 @@ export default async function authRoutes(fastify: FastifyInstance) {
|
||||
})
|
||||
}
|
||||
|
||||
// Try hosting account (cPanel user)
|
||||
// Try hosting account (NixClient user)
|
||||
const account = await prisma.account.findUnique({ where: { username } })
|
||||
if (account) {
|
||||
if (account.status !== 'active') {
|
||||
|
||||
@@ -12,7 +12,7 @@ function mysqlEscape(s: string): string {
|
||||
export default async function databaseRoutes(fastify: FastifyInstance) {
|
||||
const preHandler = [requireAuth]
|
||||
|
||||
// GET /api/cpanel/databases
|
||||
// GET /api/nixclient/databases
|
||||
fastify.get('/', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
const accountId = user.role === 'user' ? user.sub : parseInt((request.query as Record<string, string>).accountId ?? '0', 10)
|
||||
@@ -25,7 +25,7 @@ export default async function databaseRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: databases })
|
||||
})
|
||||
|
||||
// POST /api/cpanel/databases
|
||||
// POST /api/nixclient/databases
|
||||
fastify.post('/', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
const accountId = user.role === 'user' ? user.sub : parseInt((request.body as Record<string, string>).accountId ?? '0', 10)
|
||||
@@ -74,7 +74,7 @@ export default async function databaseRoutes(fastify: FastifyInstance) {
|
||||
return reply.code(201).send({ success: true, data: db })
|
||||
})
|
||||
|
||||
// DELETE /api/cpanel/databases/:id
|
||||
// DELETE /api/nixclient/databases/:id
|
||||
fastify.delete('/:id', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const user = request.user!
|
||||
@@ -93,7 +93,7 @@ export default async function databaseRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: `Database ${db.name} deleted` } })
|
||||
})
|
||||
|
||||
// POST /api/cpanel/databases/:id/users
|
||||
// POST /api/nixclient/databases/:id/users
|
||||
fastify.post('/:id/users', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const user = request.user!
|
||||
@@ -139,7 +139,7 @@ export default async function databaseRoutes(fastify: FastifyInstance) {
|
||||
return reply.code(201).send({ success: true, data: safe })
|
||||
})
|
||||
|
||||
// DELETE /api/cpanel/databases/:id/users/:userId
|
||||
// DELETE /api/nixclient/databases/:id/users/:userId
|
||||
fastify.delete('/:id/users/:userId', { preHandler }, async (request, reply) => {
|
||||
const { id, userId } = request.params as { id: string; userId: string }
|
||||
|
||||
@@ -8,7 +8,7 @@ import { exec } from '../../core/exec.js'
|
||||
export default async function cpanelEmailRoutes(fastify: FastifyInstance) {
|
||||
const preHandler = [requireAuth]
|
||||
|
||||
// GET /api/cpanel/email
|
||||
// GET /api/nixclient/email
|
||||
fastify.get('/', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
const accountId = user.role === 'user' ? user.sub : parseInt((request.query as Record<string, string>).accountId ?? '0', 10)
|
||||
@@ -21,7 +21,7 @@ export default async function cpanelEmailRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: accounts })
|
||||
})
|
||||
|
||||
// POST /api/cpanel/email
|
||||
// POST /api/nixclient/email
|
||||
fastify.post('/', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
const accountId = user.role === 'user' ? user.sub : (request.body as Record<string, unknown>).accountId as number
|
||||
@@ -62,7 +62,7 @@ export default async function cpanelEmailRoutes(fastify: FastifyInstance) {
|
||||
return reply.code(201).send({ success: true, data: safe })
|
||||
})
|
||||
|
||||
// DELETE /api/cpanel/email/:id
|
||||
// DELETE /api/nixclient/email/:id
|
||||
fastify.delete('/:id', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
const accountId = user.role === 'user' ? user.sub : 0
|
||||
@@ -80,7 +80,7 @@ export default async function cpanelEmailRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: 'Email account deleted' } })
|
||||
})
|
||||
|
||||
// GET /api/cpanel/email/forwarders
|
||||
// GET /api/nixclient/email/forwarders
|
||||
fastify.get('/forwarders', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
const accountId = user.role === 'user' ? user.sub : parseInt((request.query as Record<string, string>).accountId ?? '0', 10)
|
||||
@@ -89,7 +89,7 @@ export default async function cpanelEmailRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: forwarders })
|
||||
})
|
||||
|
||||
// POST /api/cpanel/email/forwarders
|
||||
// POST /api/nixclient/email/forwarders
|
||||
fastify.post('/forwarders', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
const accountId = user.role === 'user' ? user.sub : (request.body as Record<string, unknown>).accountId as number
|
||||
@@ -107,7 +107,7 @@ export default async function cpanelEmailRoutes(fastify: FastifyInstance) {
|
||||
return reply.code(201).send({ success: true, data: forwarder })
|
||||
})
|
||||
|
||||
// DELETE /api/cpanel/email/forwarders/:id
|
||||
// DELETE /api/nixclient/email/forwarders/:id
|
||||
fastify.delete('/forwarders/:id', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
const accountId = user.role === 'user' ? user.sub : 0
|
||||
@@ -26,10 +26,10 @@ async function getAccount(fastify: FastifyInstance, userId: number) {
|
||||
export default async function fileRoutes(fastify: FastifyInstance) {
|
||||
const preHandler = [requireAuth]
|
||||
|
||||
// GET /api/cpanel/files?path=
|
||||
// GET /api/nixclient/files?path=
|
||||
fastify.get('/', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'cPanel users only' })
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'NixClient users only' })
|
||||
|
||||
const account = await getAccount(fastify, user.sub)
|
||||
if (!account) return reply.code(404).send({ success: false, error: 'Account not found' })
|
||||
@@ -73,10 +73,10 @@ export default async function fileRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { path: userPath, files } })
|
||||
})
|
||||
|
||||
// GET /api/cpanel/files/content?path=
|
||||
// GET /api/nixclient/files/content?path=
|
||||
fastify.get('/content', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'cPanel users only' })
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'NixClient users only' })
|
||||
|
||||
const account = await getAccount(fastify, user.sub)
|
||||
if (!account) return reply.code(404).send({ success: false, error: 'Account not found' })
|
||||
@@ -102,10 +102,10 @@ export default async function fileRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { path: q.path, content } })
|
||||
})
|
||||
|
||||
// PUT /api/cpanel/files/content — save file content
|
||||
// PUT /api/nixclient/files/content — save file content
|
||||
fastify.put('/content', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'cPanel users only' })
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'NixClient users only' })
|
||||
|
||||
const account = await getAccount(fastify, user.sub)
|
||||
if (!account) return reply.code(404).send({ success: false, error: 'Account not found' })
|
||||
@@ -133,10 +133,10 @@ export default async function fileRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: 'File saved' } })
|
||||
})
|
||||
|
||||
// POST /api/cpanel/files/mkdir
|
||||
// POST /api/nixclient/files/mkdir
|
||||
fastify.post('/mkdir', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'cPanel users only' })
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'NixClient users only' })
|
||||
|
||||
const account = await getAccount(fastify, user.sub)
|
||||
if (!account) return reply.code(404).send({ success: false, error: 'Account not found' })
|
||||
@@ -157,10 +157,10 @@ export default async function fileRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: 'Directory created' } })
|
||||
})
|
||||
|
||||
// DELETE /api/cpanel/files
|
||||
// DELETE /api/nixclient/files
|
||||
fastify.delete('/', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'cPanel users only' })
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'NixClient users only' })
|
||||
|
||||
const account = await getAccount(fastify, user.sub)
|
||||
if (!account) return reply.code(404).send({ success: false, error: 'Account not found' })
|
||||
@@ -189,10 +189,10 @@ export default async function fileRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { deleted, errors } })
|
||||
})
|
||||
|
||||
// POST /api/cpanel/files/rename
|
||||
// POST /api/nixclient/files/rename
|
||||
fastify.post('/rename', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'cPanel users only' })
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'NixClient users only' })
|
||||
|
||||
const account = await getAccount(fastify, user.sub)
|
||||
if (!account) return reply.code(404).send({ success: false, error: 'Account not found' })
|
||||
@@ -207,10 +207,10 @@ export default async function fileRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: 'Renamed' } })
|
||||
})
|
||||
|
||||
// POST /api/cpanel/files/chmod
|
||||
// POST /api/nixclient/files/chmod
|
||||
fastify.post('/chmod', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'cPanel users only' })
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'NixClient users only' })
|
||||
|
||||
const account = await getAccount(fastify, user.sub)
|
||||
if (!account) return reply.code(404).send({ success: false, error: 'Account not found' })
|
||||
@@ -228,10 +228,10 @@ export default async function fileRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: 'Permissions updated' } })
|
||||
})
|
||||
|
||||
// POST /api/cpanel/files/compress — zip files
|
||||
// POST /api/nixclient/files/compress — zip files
|
||||
fastify.post('/compress', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'cPanel users only' })
|
||||
if (user.role !== 'user') return reply.code(403).send({ success: false, error: 'NixClient users only' })
|
||||
|
||||
const account = await getAccount(fastify, user.sub)
|
||||
if (!account) return reply.code(404).send({ success: false, error: 'Account not found' })
|
||||
@@ -7,7 +7,7 @@ import ftpRoutes from './ftp.js'
|
||||
import cronRoutes from './cron.js'
|
||||
import statsRoutes from './stats.js'
|
||||
|
||||
export default async function cpanelRoutes(fastify: FastifyInstance) {
|
||||
export default async function nixclientRoutes(fastify: FastifyInstance) {
|
||||
fastify.register(fileRoutes, { prefix: '/files' })
|
||||
fastify.register(databaseRoutes, { prefix: '/databases' })
|
||||
fastify.register(emailRoutes, { prefix: '/email' })
|
||||
@@ -6,7 +6,7 @@ import { exec } from '../../core/exec.js'
|
||||
export default async function statsRoutes(fastify: FastifyInstance) {
|
||||
const preHandler = [requireAuth]
|
||||
|
||||
// GET /api/cpanel/stats — account resource usage summary
|
||||
// GET /api/nixclient/stats — account resource usage summary
|
||||
fastify.get('/', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
const accountId = user.role === 'user' ? user.sub : parseInt((request.query as Record<string, string>).accountId ?? '0', 10)
|
||||
@@ -57,7 +57,7 @@ export default async function statsRoutes(fastify: FastifyInstance) {
|
||||
})
|
||||
})
|
||||
|
||||
// GET /api/cpanel/stats/bandwidth — bandwidth usage over time (placeholder for log parsing)
|
||||
// GET /api/nixclient/stats/bandwidth — bandwidth usage over time (placeholder for log parsing)
|
||||
fastify.get('/bandwidth', { preHandler }, async (request, reply) => {
|
||||
const user = request.user!
|
||||
const accountId = user.role === 'user' ? user.sub : parseInt((request.query as Record<string, string>).accountId ?? '0', 10)
|
||||
@@ -28,7 +28,7 @@ const UpdateAccountSchema = z.object({
|
||||
export default async function accountRoutes(fastify: FastifyInstance) {
|
||||
const preHandler = [requireAdminOrReseller]
|
||||
|
||||
// GET /api/whm/accounts
|
||||
// GET /api/nixserver/accounts
|
||||
fastify.get('/', { preHandler }, async (request, reply) => {
|
||||
const q = request.query as Record<string, string>
|
||||
const page = Math.max(1, parseInt(q.page ?? '1', 10))
|
||||
@@ -69,7 +69,7 @@ export default async function accountRoutes(fastify: FastifyInstance) {
|
||||
})
|
||||
})
|
||||
|
||||
// GET /api/whm/accounts/stats
|
||||
// GET /api/nixserver/accounts/stats
|
||||
fastify.get('/stats', { preHandler }, async (request, reply) => {
|
||||
const where = request.user!.role === 'reseller' ? { resellerId: request.user!.sub } : {}
|
||||
|
||||
@@ -83,7 +83,7 @@ export default async function accountRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { total, active, suspended, terminated } })
|
||||
})
|
||||
|
||||
// GET /api/whm/accounts/:id
|
||||
// GET /api/nixserver/accounts/:id
|
||||
fastify.get('/:id', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const where: Record<string, unknown> = { id: parseInt(id, 10) }
|
||||
@@ -111,7 +111,7 @@ export default async function accountRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: safe })
|
||||
})
|
||||
|
||||
// POST /api/whm/accounts
|
||||
// POST /api/nixserver/accounts
|
||||
fastify.post('/', { preHandler }, async (request, reply) => {
|
||||
const body = CreateAccountSchema.safeParse(request.body)
|
||||
if (!body.success) {
|
||||
@@ -228,7 +228,7 @@ export default async function accountRoutes(fastify: FastifyInstance) {
|
||||
return reply.code(201).send({ success: true, data: safe })
|
||||
})
|
||||
|
||||
// PUT /api/whm/accounts/:id
|
||||
// PUT /api/nixserver/accounts/:id
|
||||
fastify.put('/:id', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const body = UpdateAccountSchema.safeParse(request.body)
|
||||
@@ -261,7 +261,7 @@ export default async function accountRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: safe })
|
||||
})
|
||||
|
||||
// POST /api/whm/accounts/:id/suspend
|
||||
// POST /api/nixserver/accounts/:id/suspend
|
||||
fastify.post('/:id/suspend', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const { reason } = (request.body as Record<string, string>) ?? {}
|
||||
@@ -291,7 +291,7 @@ export default async function accountRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: `Account ${account.username} suspended` } })
|
||||
})
|
||||
|
||||
// POST /api/whm/accounts/:id/unsuspend
|
||||
// POST /api/nixserver/accounts/:id/unsuspend
|
||||
fastify.post('/:id/unsuspend', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
|
||||
@@ -319,7 +319,7 @@ export default async function accountRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: `Account ${account.username} unsuspended` } })
|
||||
})
|
||||
|
||||
// DELETE /api/whm/accounts/:id
|
||||
// DELETE /api/nixserver/accounts/:id
|
||||
fastify.delete('/:id', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const { removeFiles } = (request.body as Record<string, boolean>) ?? {}
|
||||
@@ -364,7 +364,7 @@ export default async function accountRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: `Account ${account.username} terminated` } })
|
||||
})
|
||||
|
||||
// POST /api/whm/accounts/bulk
|
||||
// POST /api/nixserver/accounts/bulk
|
||||
fastify.post('/bulk', { preHandler }, async (request, reply) => {
|
||||
const body = z.object({
|
||||
action: z.enum(['suspend', 'unsuspend', 'terminate']),
|
||||
@@ -9,7 +9,7 @@ import { config } from '../../config.js'
|
||||
const BACKUP_DIR = '/var/nixpanel/backups'
|
||||
|
||||
export default async function backupRoutes(fastify: FastifyInstance) {
|
||||
// POST /api/whm/backup/account/:id — backup a single account
|
||||
// POST /api/nixserver/backup/account/:id — backup a single account
|
||||
fastify.post('/account/:id', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const account = await prisma.account.findUnique({ where: { id: parseInt(id, 10) } })
|
||||
@@ -39,7 +39,7 @@ export default async function backupRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { filename, path: dest, size, createdAt: new Date() } })
|
||||
})
|
||||
|
||||
// POST /api/whm/backup/server — full server backup
|
||||
// POST /api/nixserver/backup/server — full server backup
|
||||
fastify.post('/server', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
|
||||
const filename = `server-backup-${timestamp}.tar.gz`
|
||||
@@ -61,7 +61,7 @@ export default async function backupRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { filename, path: dest, createdAt: new Date() } })
|
||||
})
|
||||
|
||||
// GET /api/whm/backup/list — list available backups
|
||||
// GET /api/nixserver/backup/list — list available backups
|
||||
fastify.get('/list', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const result = await exec('find', [BACKUP_DIR, '-name', '*.tar.gz', '-printf', '%p %s %T@\n'])
|
||||
if (result.exitCode !== 0 && !result.stdout) {
|
||||
@@ -81,7 +81,7 @@ export default async function backupRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: backups })
|
||||
})
|
||||
|
||||
// DELETE /api/whm/backup — delete a backup file
|
||||
// DELETE /api/nixserver/backup — delete a backup file
|
||||
fastify.delete('/', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const body = z.object({ path: z.string() }).safeParse(request.body)
|
||||
if (!body.success) {
|
||||
@@ -96,7 +96,7 @@ export default async function backupRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: 'Backup deleted' } })
|
||||
})
|
||||
|
||||
// POST /api/whm/backup/restore/:filename
|
||||
// POST /api/nixserver/backup/restore/:filename
|
||||
fastify.post('/restore/:filename', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { filename } = request.params as { filename: string }
|
||||
// Sanitize — no path traversal
|
||||
@@ -16,7 +16,7 @@ const DnsRecordSchema = z.object({
|
||||
export default async function dnsRoutes(fastify: FastifyInstance) {
|
||||
const preHandler = [requireAdminOrReseller]
|
||||
|
||||
// GET /api/whm/dns
|
||||
// GET /api/nixserver/dns
|
||||
fastify.get('/', { preHandler }, async (request, reply) => {
|
||||
const q = request.query as Record<string, string>
|
||||
const where: Record<string, unknown> = {}
|
||||
@@ -34,7 +34,7 @@ export default async function dnsRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: zones })
|
||||
})
|
||||
|
||||
// GET /api/whm/dns/:id
|
||||
// GET /api/nixserver/dns/:id
|
||||
fastify.get('/:id', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const zone = await prisma.dnsZone.findUnique({
|
||||
@@ -48,7 +48,7 @@ export default async function dnsRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: zone })
|
||||
})
|
||||
|
||||
// POST /api/whm/dns/:id/records
|
||||
// POST /api/nixserver/dns/:id/records
|
||||
fastify.post('/:id/records', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const body = DnsRecordSchema.safeParse(request.body)
|
||||
@@ -72,7 +72,7 @@ export default async function dnsRoutes(fastify: FastifyInstance) {
|
||||
return reply.code(201).send({ success: true, data: record })
|
||||
})
|
||||
|
||||
// PUT /api/whm/dns/:zoneId/records/:recordId
|
||||
// PUT /api/nixserver/dns/:zoneId/records/:recordId
|
||||
fastify.put('/:zoneId/records/:recordId', { preHandler }, async (request, reply) => {
|
||||
const { zoneId, recordId } = request.params as { zoneId: string; recordId: string }
|
||||
const body = DnsRecordSchema.partial().safeParse(request.body)
|
||||
@@ -95,7 +95,7 @@ export default async function dnsRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: updated })
|
||||
})
|
||||
|
||||
// DELETE /api/whm/dns/:zoneId/records/:recordId
|
||||
// DELETE /api/nixserver/dns/:zoneId/records/:recordId
|
||||
fastify.delete('/:zoneId/records/:recordId', { preHandler }, async (request, reply) => {
|
||||
const { zoneId, recordId } = request.params as { zoneId: string; recordId: string }
|
||||
|
||||
@@ -110,7 +110,7 @@ export default async function dnsRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: 'Record deleted' } })
|
||||
})
|
||||
|
||||
// POST /api/whm/dns/:id/reload
|
||||
// POST /api/nixserver/dns/:id/reload
|
||||
fastify.post('/:id/reload', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
await rebuildZone(parseInt(id, 10))
|
||||
@@ -8,7 +8,7 @@ import { exec } from '../../core/exec.js'
|
||||
export default async function emailRoutes(fastify: FastifyInstance) {
|
||||
const preHandler = [requireAdminOrReseller]
|
||||
|
||||
// GET /api/whm/email?accountId=
|
||||
// GET /api/nixserver/email?accountId=
|
||||
fastify.get('/', { preHandler }, async (request, reply) => {
|
||||
const q = request.query as Record<string, string>
|
||||
const where: Record<string, unknown> = {}
|
||||
@@ -27,7 +27,7 @@ export default async function emailRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: accounts })
|
||||
})
|
||||
|
||||
// POST /api/whm/email
|
||||
// POST /api/nixserver/email
|
||||
fastify.post('/', { preHandler }, async (request, reply) => {
|
||||
const body = z.object({
|
||||
accountId: z.number().int().positive(),
|
||||
@@ -81,7 +81,7 @@ export default async function emailRoutes(fastify: FastifyInstance) {
|
||||
return reply.code(201).send({ success: true, data: safe })
|
||||
})
|
||||
|
||||
// DELETE /api/whm/email/:id
|
||||
// DELETE /api/nixserver/email/:id
|
||||
fastify.delete('/:id', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const emailAccount = await prisma.emailAccount.findUnique({ where: { id: parseInt(id, 10) } })
|
||||
@@ -93,7 +93,7 @@ export default async function emailRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: 'Email account deleted' } })
|
||||
})
|
||||
|
||||
// POST /api/whm/email/:id/password
|
||||
// POST /api/nixserver/email/:id/password
|
||||
fastify.post('/:id/password', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const body = z.object({ password: z.string().min(8) }).safeParse(request.body)
|
||||
@@ -110,7 +110,7 @@ export default async function emailRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: 'Password updated' } })
|
||||
})
|
||||
|
||||
// GET /api/whm/email/forwarders?accountId=
|
||||
// GET /api/nixserver/email/forwarders?accountId=
|
||||
fastify.get('/forwarders', { preHandler }, async (request, reply) => {
|
||||
const q = request.query as Record<string, string>
|
||||
const where: Record<string, unknown> = {}
|
||||
@@ -120,7 +120,7 @@ export default async function emailRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: forwarders })
|
||||
})
|
||||
|
||||
// POST /api/whm/email/forwarders
|
||||
// POST /api/nixserver/email/forwarders
|
||||
fastify.post('/forwarders', { preHandler }, async (request, reply) => {
|
||||
const body = z.object({
|
||||
accountId: z.number().int().positive(),
|
||||
@@ -136,7 +136,7 @@ export default async function emailRoutes(fastify: FastifyInstance) {
|
||||
return reply.code(201).send({ success: true, data: forwarder })
|
||||
})
|
||||
|
||||
// DELETE /api/whm/email/forwarders/:id
|
||||
// DELETE /api/nixserver/email/forwarders/:id
|
||||
fastify.delete('/forwarders/:id', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
await prisma.emailForwarder.delete({ where: { id: parseInt(id, 10) } })
|
||||
@@ -12,7 +12,7 @@ import securityRoutes from './security.js'
|
||||
import backupRoutes from './backup.js'
|
||||
import webserverRoutes from './webserver.js'
|
||||
|
||||
export default async function whmRoutes(fastify: FastifyInstance) {
|
||||
export default async function nixserverRoutes(fastify: FastifyInstance) {
|
||||
fastify.register(accountRoutes, { prefix: '/accounts' })
|
||||
fastify.register(packageRoutes, { prefix: '/packages' })
|
||||
fastify.register(resellerRoutes, { prefix: '/resellers' })
|
||||
@@ -8,7 +8,7 @@ import { writeFile } from '../../core/exec.js'
|
||||
const SUPPORTED_PHP_VERSIONS = ['7.4', '8.0', '8.1', '8.2', '8.3']
|
||||
|
||||
export default async function phpRoutes(fastify: FastifyInstance) {
|
||||
// GET /api/whm/php/versions — list installed PHP versions
|
||||
// GET /api/nixserver/php/versions — list installed PHP versions
|
||||
fastify.get('/versions', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const versions: { version: string; installed: boolean; active: boolean }[] = []
|
||||
|
||||
@@ -21,7 +21,7 @@ export default async function phpRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: versions })
|
||||
})
|
||||
|
||||
// GET /api/whm/php/accounts — list which PHP version each account uses
|
||||
// GET /api/nixserver/php/accounts — list which PHP version each account uses
|
||||
fastify.get('/accounts', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const accounts = await prisma.account.findMany({
|
||||
where: { status: 'active' },
|
||||
@@ -33,7 +33,7 @@ export default async function phpRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: accounts })
|
||||
})
|
||||
|
||||
// PUT /api/whm/php/accounts/:id — change PHP version for account
|
||||
// PUT /api/nixserver/php/accounts/:id — change PHP version for account
|
||||
fastify.put('/accounts/:id', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const body = z.object({
|
||||
@@ -82,7 +82,7 @@ php_admin_value[error_log] = ${account.homedir}/logs/php_errors.log
|
||||
return reply.send({ success: true, data: { message: `PHP version set to ${body.data.phpVersion}` } })
|
||||
})
|
||||
|
||||
// GET /api/whm/php/ini/:version — get php.ini settings
|
||||
// GET /api/nixserver/php/ini/:version — get php.ini settings
|
||||
fastify.get('/ini/:version', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { version } = request.params as { version: string }
|
||||
if (!SUPPORTED_PHP_VERSIONS.includes(version)) {
|
||||
@@ -5,7 +5,7 @@ import { requireAdmin } from '../../middleware/auth.js'
|
||||
import { exec } from '../../core/exec.js'
|
||||
|
||||
export default async function securityRoutes(fastify: FastifyInstance) {
|
||||
// GET /api/whm/security/events
|
||||
// GET /api/nixserver/security/events
|
||||
fastify.get('/events', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const q = request.query as Record<string, string>
|
||||
const page = Math.max(1, parseInt(q.page ?? '1', 10))
|
||||
@@ -25,7 +25,7 @@ export default async function securityRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { items, total, page, limit, totalPages: Math.ceil(total / limit) } })
|
||||
})
|
||||
|
||||
// POST /api/whm/security/events/:id/resolve
|
||||
// POST /api/nixserver/security/events/:id/resolve
|
||||
fastify.post('/events/:id/resolve', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
await prisma.securityEvent.update({
|
||||
@@ -35,13 +35,13 @@ export default async function securityRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: 'Event resolved' } })
|
||||
})
|
||||
|
||||
// GET /api/whm/security/blocked-ips
|
||||
// GET /api/nixserver/security/blocked-ips
|
||||
fastify.get('/blocked-ips', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const blocked = await prisma.blockedIp.findMany({ orderBy: { createdAt: 'desc' } })
|
||||
return reply.send({ success: true, data: blocked })
|
||||
})
|
||||
|
||||
// POST /api/whm/security/blocked-ips
|
||||
// POST /api/nixserver/security/blocked-ips
|
||||
fastify.post('/blocked-ips', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const body = z.object({
|
||||
ip: z.string().ip(),
|
||||
@@ -69,7 +69,7 @@ export default async function securityRoutes(fastify: FastifyInstance) {
|
||||
return reply.code(201).send({ success: true, data: blocked })
|
||||
})
|
||||
|
||||
// DELETE /api/whm/security/blocked-ips/:id
|
||||
// DELETE /api/nixserver/security/blocked-ips/:id
|
||||
fastify.delete('/blocked-ips/:id', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const blocked = await prisma.blockedIp.findUnique({ where: { id: parseInt(id, 10) } })
|
||||
@@ -81,13 +81,13 @@ export default async function securityRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: `${blocked.ip} unblocked` } })
|
||||
})
|
||||
|
||||
// GET /api/whm/security/whitelisted-ips
|
||||
// GET /api/nixserver/security/whitelisted-ips
|
||||
fastify.get('/whitelisted-ips', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const list = await prisma.whitelistedIp.findMany({ orderBy: { createdAt: 'desc' } })
|
||||
return reply.send({ success: true, data: list })
|
||||
})
|
||||
|
||||
// POST /api/whm/security/whitelisted-ips
|
||||
// POST /api/nixserver/security/whitelisted-ips
|
||||
fastify.post('/whitelisted-ips', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const body = z.object({
|
||||
ip: z.string().ip(),
|
||||
@@ -102,20 +102,20 @@ export default async function securityRoutes(fastify: FastifyInstance) {
|
||||
return reply.code(201).send({ success: true, data: entry })
|
||||
})
|
||||
|
||||
// DELETE /api/whm/security/whitelisted-ips/:id
|
||||
// DELETE /api/nixserver/security/whitelisted-ips/:id
|
||||
fastify.delete('/whitelisted-ips/:id', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
await prisma.whitelistedIp.delete({ where: { id: parseInt(id, 10) } })
|
||||
return reply.send({ success: true, data: { message: 'Removed from whitelist' } })
|
||||
})
|
||||
|
||||
// GET /api/whm/security/fail2ban
|
||||
// GET /api/nixserver/security/fail2ban
|
||||
fastify.get('/fail2ban', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const result = await exec('fail2ban-client', ['status'])
|
||||
return reply.send({ success: true, data: { output: result.stdout } })
|
||||
})
|
||||
|
||||
// GET /api/whm/security/fail2ban/:jail
|
||||
// GET /api/nixserver/security/fail2ban/:jail
|
||||
fastify.get('/fail2ban/:jail', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { jail } = request.params as { jail: string }
|
||||
const result = await exec('fail2ban-client', ['status', jail])
|
||||
@@ -9,13 +9,13 @@ const ServiceActionSchema = z.object({
|
||||
})
|
||||
|
||||
export default async function serviceRoutes(fastify: FastifyInstance) {
|
||||
// GET /api/whm/services — list all managed services and their status
|
||||
// GET /api/nixserver/services — list all managed services and their status
|
||||
fastify.get('/', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const statuses = await Promise.all(MANAGED_SERVICES.map(getServiceStatus))
|
||||
return reply.send({ success: true, data: statuses })
|
||||
})
|
||||
|
||||
// GET /api/whm/services/:name
|
||||
// GET /api/nixserver/services/:name
|
||||
fastify.get('/:name', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { name } = request.params as { name: string }
|
||||
if (!MANAGED_SERVICES.includes(name)) {
|
||||
@@ -25,7 +25,7 @@ export default async function serviceRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: status })
|
||||
})
|
||||
|
||||
// POST /api/whm/services/:name — start/stop/restart/reload/enable/disable
|
||||
// POST /api/nixserver/services/:name — start/stop/restart/reload/enable/disable
|
||||
fastify.post('/:name', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { name } = request.params as { name: string }
|
||||
const body = ServiceActionSchema.safeParse(request.body)
|
||||
@@ -10,7 +10,7 @@ import { config } from '../../config.js'
|
||||
export default async function sslRoutes(fastify: FastifyInstance) {
|
||||
const preHandler = [requireAdminOrReseller]
|
||||
|
||||
// GET /api/whm/ssl
|
||||
// GET /api/nixserver/ssl
|
||||
fastify.get('/', { preHandler }, async (request, reply) => {
|
||||
const q = request.query as Record<string, string>
|
||||
const where: Record<string, unknown> = {}
|
||||
@@ -25,7 +25,7 @@ export default async function sslRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: certs })
|
||||
})
|
||||
|
||||
// POST /api/whm/ssl/issue — issue Let's Encrypt cert via certbot
|
||||
// POST /api/nixserver/ssl/issue — issue Let's Encrypt cert via certbot
|
||||
fastify.post('/issue', { preHandler }, async (request, reply) => {
|
||||
const body = z.object({
|
||||
accountId: z.number().int().positive(),
|
||||
@@ -100,7 +100,7 @@ export default async function sslRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: cert })
|
||||
})
|
||||
|
||||
// POST /api/whm/ssl/revoke
|
||||
// POST /api/nixserver/ssl/revoke
|
||||
fastify.post('/:id/revoke', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const cert = await prisma.sslCertificate.findUnique({ where: { id: parseInt(id, 10) } })
|
||||
@@ -121,7 +121,7 @@ export default async function sslRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: 'Certificate revoked' } })
|
||||
})
|
||||
|
||||
// POST /api/whm/ssl/:id/renew
|
||||
// POST /api/nixserver/ssl/:id/renew
|
||||
fastify.post('/:id/renew', { preHandler }, async (request, reply) => {
|
||||
const { id } = request.params as { id: string }
|
||||
const cert = await prisma.sslCertificate.findUnique({ where: { id: parseInt(id, 10) } })
|
||||
@@ -6,13 +6,13 @@ import { exec } from '../../core/exec.js'
|
||||
import { readFile } from 'fs/promises'
|
||||
|
||||
export default async function systemRoutes(fastify: FastifyInstance) {
|
||||
// GET /api/whm/system/stats
|
||||
// GET /api/nixserver/system/stats
|
||||
fastify.get('/stats', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const stats = await getSystemStats()
|
||||
return reply.send({ success: true, data: stats })
|
||||
})
|
||||
|
||||
// GET /api/whm/system/info
|
||||
// GET /api/nixserver/system/info
|
||||
fastify.get('/info', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const [hostname, osRelease, kernel, uptime] = await Promise.all([
|
||||
exec('hostname', ['-f']),
|
||||
@@ -39,7 +39,7 @@ export default async function systemRoutes(fastify: FastifyInstance) {
|
||||
})
|
||||
})
|
||||
|
||||
// GET /api/whm/system/processes
|
||||
// GET /api/nixserver/system/processes
|
||||
fastify.get('/processes', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const result = await exec('ps', ['aux', '--sort=-%cpu'])
|
||||
if (result.exitCode !== 0) {
|
||||
@@ -61,7 +61,7 @@ export default async function systemRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: processes })
|
||||
})
|
||||
|
||||
// POST /api/whm/system/processes/:pid/kill
|
||||
// POST /api/nixserver/system/processes/:pid/kill
|
||||
fastify.post('/processes/:pid/kill', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { pid } = request.params as { pid: string }
|
||||
if (!/^\d+$/.test(pid) || parseInt(pid, 10) <= 1) {
|
||||
@@ -74,7 +74,7 @@ export default async function systemRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: { message: `Process ${pid} killed` } })
|
||||
})
|
||||
|
||||
// GET /api/whm/system/audit-log
|
||||
// GET /api/nixserver/system/audit-log
|
||||
fastify.get('/audit-log', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const q = request.query as Record<string, string>
|
||||
const page = Math.max(1, parseInt(q.page ?? '1', 10))
|
||||
@@ -96,7 +96,7 @@ export default async function systemRoutes(fastify: FastifyInstance) {
|
||||
})
|
||||
})
|
||||
|
||||
// GET /api/whm/system/settings
|
||||
// GET /api/nixserver/system/settings
|
||||
fastify.get('/settings', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const settings = await prisma.setting.findMany({ orderBy: { key: 'asc' } })
|
||||
const map: Record<string, string> = {}
|
||||
@@ -104,7 +104,7 @@ export default async function systemRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: map })
|
||||
})
|
||||
|
||||
// PUT /api/whm/system/settings
|
||||
// PUT /api/nixserver/system/settings
|
||||
fastify.put('/settings', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const body = request.body as Record<string, string>
|
||||
if (typeof body !== 'object' || Array.isArray(body)) {
|
||||
@@ -5,7 +5,7 @@ import { exec } from '../../core/exec.js'
|
||||
import { readFile } from 'fs/promises'
|
||||
|
||||
export default async function webserverRoutes(fastify: FastifyInstance) {
|
||||
// GET /api/whm/webserver/status
|
||||
// GET /api/nixserver/webserver/status
|
||||
fastify.get('/status', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const [nginx, apache] = await Promise.all([
|
||||
exec('systemctl', ['is-active', 'nginx']),
|
||||
@@ -20,7 +20,7 @@ export default async function webserverRoutes(fastify: FastifyInstance) {
|
||||
})
|
||||
})
|
||||
|
||||
// GET /api/whm/webserver/vhosts
|
||||
// GET /api/nixserver/webserver/vhosts
|
||||
fastify.get('/vhosts', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const result = await exec('find', ['/etc/nginx/sites-enabled', '-name', '*.conf', '-type', 'f'])
|
||||
const files = result.stdout.split('\n').filter(Boolean)
|
||||
@@ -31,7 +31,7 @@ export default async function webserverRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: true, data: vhosts })
|
||||
})
|
||||
|
||||
// GET /api/whm/webserver/vhosts/:domain
|
||||
// GET /api/nixserver/webserver/vhosts/:domain
|
||||
fastify.get('/vhosts/:domain', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { domain } = request.params as { domain: string }
|
||||
if (domain.includes('/') || domain.includes('..')) {
|
||||
@@ -45,7 +45,7 @@ export default async function webserverRoutes(fastify: FastifyInstance) {
|
||||
}
|
||||
})
|
||||
|
||||
// POST /api/whm/webserver/nginx/test
|
||||
// POST /api/nixserver/webserver/nginx/test
|
||||
fastify.post('/nginx/test', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const result = await exec('nginx', ['-t'])
|
||||
return reply.send({
|
||||
@@ -54,7 +54,7 @@ export default async function webserverRoutes(fastify: FastifyInstance) {
|
||||
})
|
||||
})
|
||||
|
||||
// POST /api/whm/webserver/nginx/reload
|
||||
// POST /api/nixserver/webserver/nginx/reload
|
||||
fastify.post('/nginx/reload', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const test = await exec('nginx', ['-t'])
|
||||
if (test.exitCode !== 0) {
|
||||
@@ -64,7 +64,7 @@ export default async function webserverRoutes(fastify: FastifyInstance) {
|
||||
return reply.send({ success: result.exitCode === 0, data: { output: result.stdout } })
|
||||
})
|
||||
|
||||
// GET /api/whm/webserver/logs/:type — access or error log (last 200 lines)
|
||||
// GET /api/nixserver/webserver/logs/:type — access or error log (last 200 lines)
|
||||
fastify.get('/logs/:type', { preHandler: [requireAdmin] }, async (request, reply) => {
|
||||
const { type } = request.params as { type: string }
|
||||
const logFile = type === 'error' ? '/var/log/nginx/error.log' : '/var/log/nginx/access.log'
|
||||
Reference in New Issue
Block a user