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
+8 -8
View File
@@ -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(),
}))
+2 -2
View File
@@ -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: {
+1 -1
View File
@@ -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
View File
@@ -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)
+2 -2
View File
@@ -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'