diff --git a/CHANGELOG.md b/CHANGELOG.md index 27a59a8..f246ba1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,35 @@ Pre-1.0 versions treat MINOR as feature releases and PATCH as bug fixes. --- -## [0.5.2] — 2026-04-11 +## [0.6.2] — 2026-04-12 ### Bug Fixes - **`curl | bash` aborts immediately at confirmation prompt** — `set -euo pipefail` caused the script to exit when `read` received EOF from the pipe instead of a terminal. Fixed by reading from `/dev/tty` directly so the prompt works correctly whether the script is piped or run from a file. --- +## [0.6.1] — 2026-04-11 + +### Changes +- **`CLAUDE.md` added** — development standards file auto-read by Claude Code before every session. Documents version numbering rules, changelog format, commit message conventions, file size limits, section/page architecture patterns, API conventions, and project structure. + +--- + +## [0.6.0] — 2026-04-11 + +### Features +- **Basic NixServer Setup page** — full WHM-equivalent settings page at `/server-configuration/basic-setup` with two tabs (Contact Information, Basic Config) and a Nameservers section. +- **Server Configuration section page** — new section index at `/server-configuration` showing all 13 tools as a card grid. +- **`GET /api/nixserver/system/network-interfaces`** — new endpoint returning all non-loopback interfaces, used to populate the Ethernet Device dropdown. + +### Refactors +- **File size limit enforced** — `Layout.tsx`, `Settings.tsx`, `accounts.ts` each split into focused sub-files under 300 lines. +- **`NavCategory` gains optional `to` field** — links sidebar category headers to section index pages. +- **Shared components added** — `SectionCard.tsx` and `ComingSoon.tsx`. +- **`/settings` redirected** to `/server-configuration/basic-setup` for backwards compatibility. + +--- + ## [0.5.1] — 2026-04-11 ### Bug Fixes diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..22546cb --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,165 @@ +# NixPanel Development Standards + +This file is read automatically by Claude Code before every session. +All rules below are **mandatory** — follow them without being asked. + +--- + +## 1. Version Numbering + +Versioning follows [Semantic Versioning](https://semver.org/). Pre-1.0 rules: + +| Change type | Bump | Example | +|---|---|---| +| New feature, new page, new section | **MINOR** | 0.5.1 → 0.6.0 | +| Bug fix, refactor, chore, docs | **PATCH** | 0.5.1 → 0.5.2 | +| Breaking API/schema change | **MINOR** | 0.5.x → 0.6.0 | + +**Always** bump the version in `package.json` before committing any user-visible change. + +--- + +## 2. Changelog + +File: `CHANGELOG.md` at the repo root. + +### Format + +```markdown +## [X.Y.Z] — YYYY-MM-DD + +### Features ← new functionality +### Changes ← non-breaking behaviour changes / refactors +### Bug Fixes ← defect corrections +### Refactors ← internal restructuring with no behaviour change +``` + +### Rules + +- Add a new entry **at the top** of the file (below the header block) for every commit. +- One entry per version — if multiple changes share a version, group them under the same header. +- Bullet points must be descriptive enough that a developer can understand what changed and why without reading the diff. +- Use bold for the affected component/file: `- **BasicSetup.tsx** — description`. +- Today's date format: `YYYY-MM-DD`. + +--- + +## 3. Commit Messages + +Format: `type(scope): short imperative summary` + +| Type | When to use | +|---|---| +| `feat` | New feature or page | +| `fix` | Bug fix | +| `refactor` | Code restructuring, no behaviour change | +| `chore` | Version bump, changelog, config, tooling | +| `docs` | Documentation only | + +Rules: +- Summary line ≤ 72 characters. +- Always include `Co-Authored-By: Claude Sonnet 4.6 ` trailer. +- Use a multiline HEREDOC — never single-line for complex messages. + +### Workflow for every change + +1. Make code changes. +2. Bump version in `package.json`. +3. Add entry to `CHANGELOG.md`. +4. Stage all changed files including `package.json` and `CHANGELOG.md`. +5. Commit everything together in **one commit**. +6. Push to `origin main`. + +> Do **not** make a separate `chore: bump version` commit — version + changelog belong in the same commit as the code change. + +--- + +## 4. File Size Limit + +**No source file should exceed 300 lines.** + +When a file approaches this limit, split it by responsibility: +- React pages → extract sub-components into a `pages/
/` subfolder. +- Fastify route files → split into sub-plugins under `routes/
/` with an `index.ts` registering them. +- Shared UI primitives → `components/` folder. + +--- + +## 5. Page / Section Architecture + +### Frontend + +Each nav category in the sidebar maps to a **section folder**: +``` +pages// + index.tsx ← card-grid landing page (always create this first) + ToolName.tsx ← one file per tool/feature +``` + +Unimplemented tools use `` from `components/ComingSoon.tsx`. + +Section landing pages use `` from `components/SectionCard.tsx`. + +### Backend + +Route files live under `routes/nixserver/
/`: +``` +routes/nixserver/
/ + index.ts ← registers sub-plugins, no route logic here + list.ts ← read-only GET routes + create.ts ← POST / creation route + modify.ts ← PUT / PATCH routes + status.ts ← suspend / unsuspend / delete / bulk +``` + +--- + +## 6. Project Structure (key paths) + +``` +nixpanel-server/ + package.json ← root version number lives here + CHANGELOG.md ← always update before committing + CLAUDE.md ← this file + nixserver/src/ + components/ + navData.ts ← NAV array + NavCategory/NavItem types + Sidebar.tsx ← sidebar UI + Layout.tsx ← thin shell (topbar + Outlet) + SectionCard.tsx ← reusable section card + ComingSoon.tsx ← placeholder for unimplemented tools + pages/ + server-configuration/ ← example of section folder pattern + index.tsx + BasicSetup.tsx + ChangeRoot.tsx + ... + server/src/ + routes/nixserver/ + accounts/ ← example of split route folder + index.ts + list.ts + create.ts + modify.ts + status.ts +``` + +--- + +## 7. API Conventions + +- All responses: `{ success: true, data: ... }` or `{ success: false, error: "..." }`. +- All admin routes use `preHandler: [requireAdmin]` or `preHandler: [requireAdminOrReseller]`. +- Settings stored as key-value pairs in the `Setting` model (`key String @id`, `value String`). +- New settings keys use UPPERCASE (matching WHM convention): `CONTACTEMAIL`, `TTL`, etc. + +--- + +## 8. Git Remote + +``` +origin https://github.com/NixReaper/nixpanel-server.git (fetch/push) +branch main +``` + +Always push to `origin main` after committing. diff --git a/nixserver/src/App.tsx b/nixserver/src/App.tsx index 8c3f20c..a89a130 100644 --- a/nixserver/src/App.tsx +++ b/nixserver/src/App.tsx @@ -23,6 +23,22 @@ const System = React.lazy(() => import('./pages/System')) const WebServer = React.lazy(() => import('./pages/WebServer')) const Settings = React.lazy(() => import('./pages/Settings')) +// Server Configuration section +const ServerConfiguration = React.lazy(() => import('./pages/server-configuration/index')) +const SCBasicSetup = React.lazy(() => import('./pages/server-configuration/BasicSetup')) +const SCChangeRoot = React.lazy(() => import('./pages/server-configuration/ChangeRoot')) +const SCAnalytics = React.lazy(() => import('./pages/server-configuration/Analytics')) +const SCCronJobs = React.lazy(() => import('./pages/server-configuration/CronJobs')) +const SCQuotaSetup = React.lazy(() => import('./pages/server-configuration/QuotaSetup')) +const SCLinkNodes = React.lazy(() => import('./pages/server-configuration/LinkNodes')) +const SCServerProfile = React.lazy(() => import('./pages/server-configuration/ServerProfile')) +const SCServerTime = React.lazy(() => import('./pages/server-configuration/ServerTime')) +const SCStatisticsSoftware = React.lazy(() => import('./pages/server-configuration/StatisticsSoftware')) +const SCTerminal = React.lazy(() => import('./pages/server-configuration/Terminal')) +const SCTweakSettings = React.lazy(() => import('./pages/server-configuration/TweakSettings')) +const SCUpdatePreferences = React.lazy(() => import('./pages/server-configuration/UpdatePreferences')) +const SCMarketplace = React.lazy(() => import('./pages/server-configuration/Marketplace')) + // Accounts sub-pages const AccountList = React.lazy(() => import('./pages/accounts/List')) const AccountParkedDomains = React.lazy(() => import('./pages/accounts/ParkedDomains')) @@ -181,6 +197,52 @@ function AppRoutes() { }> } /> + {/* Server Configuration section */} + }> + } /> + }> + } /> + }> + } /> + }> + } /> + }> + } /> + }> + } /> + }> + } /> + }> + } /> + }> + } /> + }> + } /> + }> + } /> + }> + } /> + }> + } /> + }> + } /> + {/* Redirect old /settings to new canonical route */} + } /> + {/* Other sections */} } /> +
+

{title}

+ {description &&

{description}

} +
+
+
+ +
+
+

Coming Soon

+

This feature is under development.

+
+
+ + ) +} diff --git a/nixserver/src/components/Layout.tsx b/nixserver/src/components/Layout.tsx index 1ff01bd..966bf6c 100644 --- a/nixserver/src/components/Layout.tsx +++ b/nixserver/src/components/Layout.tsx @@ -1,781 +1,15 @@ -import { useState, useMemo, useEffect, useCallback } from 'react' -import { Outlet, Link, useNavigate, useLocation } from 'react-router-dom' -import { - LayoutDashboard, Settings, HelpCircle, Network, BookUser, UserCog, - Wrench, Languages, HardDrive, GitFork, RefreshCw, Activity, - Users, UserCheck, ArrowRightLeft, Paintbrush, Package, - Globe, Database, Wifi, Box, Mail, HeartPulse, Lock, - ShoppingBag, Code2, Puzzle, LogOut, Menu, X, - ChevronDown, ChevronRight, Search, RotateCcw, RefreshCcw, - ArrowUpCircle, CheckCircle2, AlertCircle, Loader2 -} from 'lucide-react' -import { useAuth } from '../contexts/AuthContext' -import { api } from '../api/client' - -// ─── Nav data ──────────────────────────────────────────────────────────────── - -type NavItem = { label: string; to: string } -type NavCategory = { - id: string - label: string - icon: React.ComponentType<{ size?: number | string; className?: string }> - items: NavItem[] -} - -const NAV: NavCategory[] = [ - { - id: 'server-config', - label: 'Server Configuration', - icon: Settings, - items: [ - { label: 'Basic NixServer Setup', to: '/settings' }, - { label: 'Change Root Password', to: '/security' }, - { label: 'Configure Analytics', to: '/settings' }, - { label: 'Configure Cron Jobs', to: '/settings' }, - { label: 'Initial Quota Setup', to: '/settings' }, - { label: 'Link Server Nodes', to: '/settings' }, - { label: 'Server Profile', to: '/settings' }, - { label: 'Server Time', to: '/system' }, - { label: 'Statistics Software', to: '/settings' }, - { label: 'Terminal', to: '/system' }, - { label: 'Tweak Settings', to: '/settings' }, - { label: 'Update Preferences', to: '/settings' }, - { label: 'NixServer Marketplace', to: '/settings' }, - ], - }, - { - id: 'support', - label: 'Support', - icon: HelpCircle, - items: [ - { label: 'Grant NixPanel Support Access', to: '/security' }, - { label: 'Support Center', to: '/settings' }, - ], - }, - { - id: 'networking', - label: 'Networking Setup', - icon: Network, - items: [ - { label: 'Change Hostname', to: '/security' }, - { label: 'Resolver Configuration', to: '/security' }, - { label: 'Security Center', to: '/security' }, - { label: 'Apache mod_userdir Tweak', to: '/webserver' }, - { label: 'SMTP Restrictions', to: '/email' }, - { label: 'Compiler Access', to: '/security' }, - { label: 'Configure Security Policies', to: '/security' }, - { label: 'mXSTOP Brute Force Protection', to: '/security' }, - { label: 'Host Access Control', to: '/security' }, - { label: 'Manage External Authentications', to: '/security' }, - { label: "Manage root's SSH Keys", to: '/security' }, - { label: 'Manage Wheel Group Users', to: '/security' }, - { label: 'ModSecurity™ Configuration', to: '/security' }, - { label: 'ModSecurity™ Tools', to: '/security' }, - { label: 'ModSecurity™ Vendors', to: '/security' }, - { label: 'Password Strength Configuration', to: '/security' }, - { label: 'Security Advisor', to: '/security' }, - { label: 'Security Questions', to: '/security' }, - { label: 'Shell Fork Bomb Protection', to: '/security' }, - { label: 'SSH Password Authorization Tweak', to: '/security' }, - { label: 'Traceroute Enable/Disable', to: '/security' }, - { label: 'Two-Factor Authentication', to: '/security' }, - ], - }, - { - id: 'contacts', - label: 'Server Contacts', - icon: BookUser, - items: [ - { label: 'Contact Manager', to: '/settings' }, - { label: 'Edit System Mail Preferences', to: '/email' }, - ], - }, - { - id: 'resellers', - label: 'Resellers', - icon: UserCog, - items: [ - { label: 'Change Ownership of an Account', to: '/resellers' }, - { label: 'Change Ownership of Multiple Accounts', to: '/resellers' }, - { label: 'Edit Reseller Nameservers and Privileges', to: '/resellers' }, - { label: 'Email All Resellers', to: '/resellers' }, - { label: "Manage Reseller's IP Delegation", to: '/resellers' }, - { label: "Manage Reseller's Shared IP", to: '/resellers' }, - { label: 'Reseller Center', to: '/resellers' }, - { label: 'Reset Resellers', to: '/resellers' }, - { label: 'Show Reseller Accounts', to: '/resellers' }, - { label: 'View Reseller Usage', to: '/resellers' }, - ], - }, - { - id: 'service-config', - label: 'Service Configuration', - icon: Wrench, - items: [ - { label: 'Apache Configuration', to: '/webserver' }, - { label: 'Log Rotation Configuration', to: '/settings' }, - { label: 'Web Disk Configuration', to: '/settings' }, - { label: 'Web Services Configuration', to: '/settings' }, - { label: 'Exim Configuration Manager', to: '/email' }, - { label: 'FTP Server Selection', to: '/settings' }, - { label: 'Mailserver Configuration', to: '/email' }, - { label: 'Manage Service SSL Certificates', to: '/ssl' }, - { label: 'Nameserver Selection', to: '/dns' }, - { label: 'Service Manager', to: '/services' }, - ], - }, - { - id: 'locales', - label: 'Locales', - icon: Languages, - items: [ - { label: 'Configure Application Locales', to: '/settings' }, - { label: 'Copy a Locale', to: '/settings' }, - { label: 'Delete a Locale', to: '/settings' }, - { label: 'Edit a Locale', to: '/settings' }, - { label: 'Locale XML Download', to: '/settings' }, - { label: 'Locale XML Upload', to: '/settings' }, - { label: 'View Available Locales', to: '/settings' }, - ], - }, - { - id: 'backup', - label: 'Backup', - icon: HardDrive, - items: [ - { label: 'Backup Configuration', to: '/backup' }, - { label: 'Backup Restoration', to: '/backup' }, - { label: 'Backup User Selection', to: '/backup' }, - { label: 'File and Directory Restoration', to: '/backup' }, - ], - }, - { - id: 'clusters', - label: 'Clusters', - icon: GitFork, - items: [ - { label: 'Configuration Cluster', to: '/settings' }, - { label: 'DNS Cluster', to: '/dns' }, - { label: 'Remote Access Key', to: '/security' }, - ], - }, - { - id: 'reboot', - label: 'System Reboot', - icon: RotateCcw, - items: [ - { label: 'Forceful Server Reboot', to: '/system' }, - { label: 'Graceful Server Reboot', to: '/system' }, - ], - }, - { - id: 'server-status', - label: 'Server Status', - icon: Activity, - items: [ - { label: 'Apache Status', to: '/webserver' }, - { label: 'Daily Process Log', to: '/system' }, - { label: 'Server Information',to: '/system' }, - { label: 'Service Status', to: '/services' }, - { label: 'Task Queue Monitor',to: '/system' }, - ], - }, - { - id: 'account-info', - label: 'Account Information', - icon: Users, - items: [ - { label: 'List Accounts', to: '/accounts/list' }, - { label: 'List Parked Domains', to: '/accounts/parked-domains'}, - { label: 'List Subdomains', to: '/accounts/subdomains' }, - { label: 'List Suspended Accounts', to: '/accounts/suspended' }, - { label: 'Show Accounts Over Quota', to: '/accounts/over-quota' }, - { label: 'View Bandwidth Usage', to: '/accounts/bandwidth' }, - ], - }, - { - id: 'account-functions', - label: 'Account Functions', - icon: UserCheck, - items: [ - { label: "Change Site's IP Address", to: '/accounts/change-ip' }, - { label: 'Create a New Account', to: '/accounts/create' }, - { label: 'Email All Users', to: '/accounts/email-users' }, - { label: 'Force Password Change', to: '/accounts/force-password' }, - { label: 'Limit Bandwidth Usage', to: '/accounts/limit-bandwidth' }, - { label: 'Manage Account Suspension', to: '/accounts/suspension' }, - { label: 'Manage Demo Mode', to: '/accounts/demo-mode' }, - { label: 'Manage Shell Access', to: '/accounts/shell-access' }, - { label: 'Modify an Account', to: '/accounts/modify' }, - { label: 'Password Modification', to: '/accounts/password' }, - { label: 'Quota Modification', to: '/accounts/quota' }, - { label: 'Raw Apache Log Download', to: '/accounts/apache-logs' }, - { label: 'Raw NGINX® Log Download', to: '/accounts/nginx-logs' }, - { label: 'Rearrange an Account', to: '/accounts/rearrange' }, - { label: 'Reset Account Bandwidth', to: '/accounts/reset-bandwidth' }, - { label: 'Terminate Accounts', to: '/accounts/terminate' }, - { label: 'Unsuspend Bandwidth Exceeders', to: '/accounts/unsuspend-bandwidth' }, - { label: 'Upgrade/Downgrade an Account', to: '/accounts/upgrade' }, - { label: 'Web Template Editor', to: '/accounts/web-template' }, - ], - }, - { - id: 'multi-account', - label: 'Multi Account Functions', - icon: Users, - items: [ - { label: "Change Multiple Sites' IP Addresses", to: '/accounts/bulk-ip' }, - { label: 'Modify/Upgrade Multiple Accounts', to: '/accounts/bulk-modify' }, - ], - }, - { - id: 'transfers', - label: 'Transfers', - icon: ArrowRightLeft, - items: [ - { label: 'Convert Addon Domain to Account', to: '/accounts' }, - { label: 'Review Transfers and Restores', to: '/accounts' }, - { label: 'Transfer or Restore a cPanel Account', to: '/accounts' }, - { label: 'Transfer Tool', to: '/accounts' }, - ], - }, - { - id: 'themes', - label: 'Themes', - icon: Paintbrush, - items: [ - { label: 'Theme Manager', to: '/settings' }, - ], - }, - { - id: 'packages', - label: 'Packages', - icon: Package, - items: [ - { label: 'Add a Package', to: '/packages' }, - { label: 'Delete a Package', to: '/packages' }, - { label: 'Edit a Package', to: '/packages' }, - { label: 'Feature Manager', to: '/packages' }, - ], - }, - { - id: 'dns', - label: 'DNS Functions', - icon: Globe, - items: [ - { label: 'Add a DNS Zone', to: '/dns' }, - { label: 'Add an A Entry for Hostname', to: '/dns' }, - { label: 'Delete a DNS Zone', to: '/dns' }, - { label: 'DNS Zone Manager', to: '/dns' }, - { label: 'Edit Zone Templates', to: '/dns' }, - { label: 'Email DNS Record Manager', to: '/dns' }, - { label: 'Email Routing Configuration', to: '/email' }, - { label: 'Enable DKIM/SPF Globally', to: '/email' }, - { label: 'Nameserver Record Report', to: '/dns' }, - { label: 'Park a Domain', to: '/dns' }, - { label: 'Perform a DNS Cleanup', to: '/dns' }, - { label: 'Set Zone TTL', to: '/dns' }, - { label: 'Setup/Edit Domain Forwarding',to: '/dns' }, - { label: 'Synchronize DNS Records', to: '/dns' }, - ], - }, - { - id: 'databases', - label: 'Database Services', - icon: Database, - items: [ - { label: 'Change Database Root Password', to: '/settings' }, - { label: 'Change Database User Password', to: '/settings' }, - { label: 'Database Map Tool', to: '/settings' }, - { label: 'Edit Database Configuration', to: '/settings' }, - { label: 'Manage Database Access Hosts', to: '/settings' }, - { label: 'Manage Database Profiles', to: '/settings' }, - { label: 'Manage Database Users', to: '/settings' }, - { label: 'Manage Databases', to: '/settings' }, - { label: 'phpMyAdmin', to: '/settings' }, - { label: 'Repair Databases', to: '/settings' }, - { label: 'Show Database Processes', to: '/settings' }, - { label: 'Upgrade Database Version', to: '/settings' }, - ], - }, - { - id: 'ip', - label: 'IP Functions', - icon: Wifi, - items: [ - { label: 'IPv6 Ranges', to: '/security' }, - { label: 'Add a New IP Address', to: '/security' }, - { label: 'Assign IPv6 Address', to: '/security' }, - { label: 'Configure Remote Service IPs', to: '/security' }, - { label: 'IP Migration Wizard', to: '/security' }, - { label: 'Rebuild the IP Address Pool', to: '/security' }, - { label: 'Show IP Address Usage', to: '/security' }, - { label: 'Show or Delete Current IPs', to: '/security' }, - { label: 'Show/Edit Reserved IPs', to: '/security' }, - ], - }, - { - id: 'software', - label: 'Software', - icon: Box, - items: [ - { label: 'Install a Perl Module', to: '/system' }, - { label: 'Install Distro Packages', to: '/system' }, - { label: 'Module Installers', to: '/system' }, - { label: 'MultiPHP INI Editor', to: '/php' }, - { label: 'MultiPHP Manager', to: '/php' }, - { label: 'NGINX® Manager', to: '/webserver' }, - { label: 'Rebuild RPM Database', to: '/system' }, - { label: 'System Update', to: '/system' }, - { label: 'Update Server Software', to: '/system' }, - { label: 'EasyApache 4', to: '/webserver' }, - ], - }, - { - id: 'email', - label: 'Email', - icon: Mail, - items: [ - { label: 'Email Deliverability', to: '/email' }, - { label: 'Filter Incoming Emails by Country', to: '/email' }, - { label: 'Filter Incoming Emails by Domain', to: '/email' }, - { label: 'Greylisting', to: '/email' }, - { label: 'Mail Delivery Reports', to: '/email' }, - { label: 'Mail Queue Manager', to: '/email' }, - { label: 'Mail Troubleshooter', to: '/email' }, - { label: 'Mailbox Conversion', to: '/email' }, - { label: 'Repair Mailbox Permissions', to: '/email' }, - { label: 'Spamd Startup Configuration', to: '/email' }, - { label: 'View Mail Statistics Summary', to: '/email' }, - { label: 'View Relayers', to: '/email' }, - { label: 'View Sent Summary', to: '/email' }, - ], - }, - { - id: 'system-health', - label: 'System Health', - icon: HeartPulse, - items: [ - { label: 'Background Process Killer', to: '/system' }, - { label: 'Process Manager', to: '/system' }, - { label: 'Show Current Disk Usage', to: '/system' }, - { label: 'Show Current Running Processes', to: '/system' }, - ], - }, - { - id: 'nixpanel', - label: 'NixPanel', - icon: LayoutDashboard, - items: [ - { label: 'Change Log', to: '/settings' }, - { label: 'Customization', to: '/settings' }, - { label: 'Manage Plugins', to: '/settings' }, - { label: 'Modify NixPanel News', to: '/settings' }, - { label: 'Reset a Mailman Password', to: '/email' }, - { label: 'Upgrade to Latest Version', to: '/settings' }, - ], - }, - { - id: 'ssl', - label: 'SSL/TLS', - icon: Lock, - items: [ - { label: 'Generate an SSL Certificate and Signing Request', to: '/ssl' }, - { label: 'Install an SSL Certificate on a Domain', to: '/ssl' }, - { label: 'Manage AutoSSL', to: '/ssl' }, - { label: 'Manage SSL Hosts', to: '/ssl' }, - { label: 'Purchase and Install an SSL Certificate', to: '/ssl' }, - { label: 'SSL Storage Manager', to: '/ssl' }, - { label: 'SSL/TLS Configuration', to: '/ssl' }, - ], - }, - { - id: 'market', - label: 'Market', - icon: ShoppingBag, - items: [ - { label: 'Market Provider Manager', to: '/settings' }, - ], - }, - { - id: 'restart-services', - label: 'Restart Services', - icon: RefreshCw, - items: [ - { label: 'Database Server', to: '/services' }, - { label: 'DNS Server', to: '/services' }, - { label: 'HTTP Server (Apache)', to: '/services' }, - { label: 'IMAP Server', to: '/services' }, - { label: 'Mail Server (Exim)', to: '/services' }, - { label: 'Mailing List Manager (Mailman)', to: '/services' }, - { label: 'PHP-FPM service for Apache', to: '/services' }, - { label: 'SSH Server (OpenSSH)', to: '/services' }, - ], - }, - { - id: 'development', - label: 'Development', - icon: Code2, - items: [ - { label: 'Apps Managed by AppConfig', to: '/settings' }, - { label: 'NixPanel Development Forum', to: '/settings' }, - { label: 'NixPanel Plugin File Generator', to: '/settings' }, - { label: 'Developer Documentation', to: '/settings' }, - { label: 'Manage API Tokens', to: '/settings' }, - { label: 'Manage Hooks', to: '/settings' }, - { label: 'OpenAPI Documentation for NixPanel', to: '/settings' }, - { label: 'OpenAPI Documentation for NixServer', to: '/settings' }, - ], - }, - { - id: 'plugins', - label: 'Plugins', - icon: Puzzle, - items: [ - { label: 'Backuply by Softaculous', to: '/backup' }, - { label: 'ImunifyAV', to: '/security' }, - { label: 'Server Monitoring', to: '/system' }, - { label: 'SitePad Website Builder', to: '/settings' }, - { label: 'Softaculous - Instant Installs', to: '/settings' }, - { label: 'WP Toolkit', to: '/settings' }, - ], - }, -] - -// ─── Helpers ───────────────────────────────────────────────────────────────── - -function isItemActive(to: string, pathname: string) { - if (to === '/') return pathname === '/' - return pathname === to || pathname.startsWith(to + '/') -} - -// ─── Component ─────────────────────────────────────────────────────────────── +import { useState } from 'react' +import { Outlet } from 'react-router-dom' +import { Menu, X } from 'lucide-react' +import Sidebar from './Sidebar' export default function Layout() { - const { user, logout } = useAuth() - const navigate = useNavigate() - const location = useLocation() - const [sidebarOpen, setSidebarOpen] = useState(true) - const [search, setSearch] = useState('') - - // ── Version / upgrade state ────────────────────────────────────────────── - type VersionInfo = { currentVersion: string; latestVersion: string | null; updateAvailable: boolean } - const [versionInfo, setVersionInfo] = useState(null) - const [versionError, setVersionError] = useState(false) - const [upgradeState, setUpgradeState] = useState<'idle' | 'confirm' | 'running' | 'done' | 'error'>('idle') - - const fetchVersion = useCallback(async (manual = false) => { - setVersionError(false) - // On a manual refresh, clear the stale value so the spinner shows - if (manual) setVersionInfo(null) - try { - const { data } = await api.get('/nixserver/system/version', { - timeout: 10_000, - // Cache-bust so the browser never serves a stale version response - params: { _t: Date.now() }, - }) - setVersionInfo(data.data) - } catch { - setVersionError(true) - } - }, []) - - // Initial fetch - useEffect(() => { fetchVersion() }, [fetchVersion]) - - // Auto-retry every 5 s while in error state (handles the service-restart window after an upgrade) - useEffect(() => { - if (!versionError) return - const t = setTimeout(fetchVersion, 5_000) - return () => clearTimeout(t) - }, [versionError, fetchVersion]) - - const handleUpgrade = async () => { - if (upgradeState === 'confirm') { - setUpgradeState('running') - try { - await api.post('/nixserver/system/upgrade') - setUpgradeState('done') - } catch { - setUpgradeState('error') - } - } else { - setUpgradeState('confirm') - } - } - - // ───────────────────────────────────────────────────────────────────────── - - const [openCats, setOpenCats] = useState>(() => { - // Auto-open the category that contains the active route on first render - const active = NAV.find(cat => - cat.items.some(item => isItemActive(item.to, location.pathname)) - ) - return active ? new Set([active.id]) : new Set() - }) - - // When the route changes (e.g. user navigates), auto-open the matching category - useEffect(() => { - const active = NAV.find(cat => - cat.items.some(item => isItemActive(item.to, location.pathname)) - ) - if (active) { - setOpenCats(prev => prev.has(active.id) ? prev : new Set([...prev, active.id])) - } - }, [location.pathname]) - - const toggleCat = (id: string) => { - setOpenCats(prev => { - const next = new Set(prev) - next.has(id) ? next.delete(id) : next.add(id) - return next - }) - } - - // Filter nav based on search query - const filteredNav = useMemo(() => { - const q = search.trim().toLowerCase() - if (!q) return NAV - return NAV.map(cat => ({ - ...cat, - items: cat.items.filter(item => item.label.toLowerCase().includes(q)), - })).filter(cat => - cat.items.length > 0 || cat.label.toLowerCase().includes(q) - ) - }, [search]) - - // When searching, all matching categories are forced open - const effectiveOpen = (id: string) => - search.trim() ? filteredNav.some(c => c.id === id) : openCats.has(id) - - const handleLogout = async () => { - await logout() - navigate('/login') - } return (
+ setSidebarOpen(v => !v)} /> - {/* ── Sidebar ─────────────────────────────────────────────────────── */} - - - {/* ── Main ────────────────────────────────────────────────────────── */}
{/* Topbar */}
@@ -791,7 +25,7 @@ export default function Layout() {
- {/* Content */} + {/* Page content */}
diff --git a/nixserver/src/components/SectionCard.tsx b/nixserver/src/components/SectionCard.tsx new file mode 100644 index 0000000..bf53f24 --- /dev/null +++ b/nixserver/src/components/SectionCard.tsx @@ -0,0 +1,46 @@ +import { Link } from 'react-router-dom' +import type { ComponentType } from 'react' + +interface SectionCardProps { + label: string + description?: string + to: string + icon?: ComponentType<{ size?: number; className?: string }> + comingSoon?: boolean +} + +export default function SectionCard({ label, description, to, icon: Icon, comingSoon }: SectionCardProps) { + if (comingSoon) { + return ( +
+ {Icon && ( +
+ +
+ )} +
+

{label}

+ {description &&

{description}

} + Coming soon +
+
+ ) + } + + return ( + + {Icon && ( +
+ +
+ )} +
+

{label}

+ {description &&

{description}

} +
+ + ) +} diff --git a/nixserver/src/components/Sidebar.tsx b/nixserver/src/components/Sidebar.tsx new file mode 100644 index 0000000..531c67d --- /dev/null +++ b/nixserver/src/components/Sidebar.tsx @@ -0,0 +1,340 @@ +import { useState, useMemo, useEffect, useCallback } from 'react' +import { Link, useLocation, useNavigate } from 'react-router-dom' +import { + LayoutDashboard, LogOut, Menu, X, + ChevronDown, ChevronRight, Search, RefreshCcw, + ArrowUpCircle, CheckCircle2, AlertCircle, Loader2, ExternalLink, +} from 'lucide-react' +import { useAuth } from '../contexts/AuthContext' +import { api } from '../api/client' +import { NAV, isItemActive } from './navData' + +type VersionInfo = { + currentVersion: string + latestVersion: string | null + updateAvailable: boolean +} + +type UpgradeState = 'idle' | 'confirm' | 'running' | 'done' | 'error' + +interface SidebarProps { + open: boolean + onToggle: () => void +} + +export default function Sidebar({ open, onToggle }: SidebarProps) { + const { user, logout } = useAuth() + const location = useLocation() + const navigate = useNavigate() + + const [search, setSearch] = useState('') + const [versionInfo, setVersionInfo] = useState(null) + const [versionError, setVersionError] = useState(false) + const [upgradeState, setUpgradeState] = useState('idle') + + // ── Version checking ─────────────────────────────────────────────────────── + + const fetchVersion = useCallback(async (manual = false) => { + setVersionError(false) + if (manual) setVersionInfo(null) + try { + const { data } = await api.get('/nixserver/system/version', { + timeout: 10_000, + params: { _t: Date.now() }, + }) + setVersionInfo(data.data) + } catch { + setVersionError(true) + } + }, []) + + useEffect(() => { fetchVersion() }, [fetchVersion]) + + useEffect(() => { + if (!versionError) return + const t = setTimeout(fetchVersion, 5_000) + return () => clearTimeout(t) + }, [versionError, fetchVersion]) + + const handleUpgrade = async () => { + if (upgradeState === 'confirm') { + setUpgradeState('running') + try { + await api.post('/nixserver/system/upgrade') + setUpgradeState('done') + } catch { + setUpgradeState('error') + } + } else { + setUpgradeState('confirm') + } + } + + // ── Category open/close ──────────────────────────────────────────────────── + + const [openCats, setOpenCats] = useState>(() => { + const active = NAV.find(cat => + cat.items.some(item => isItemActive(item.to, location.pathname)) + ) + return active ? new Set([active.id]) : new Set() + }) + + useEffect(() => { + const active = NAV.find(cat => + cat.items.some(item => isItemActive(item.to, location.pathname)) + ) + if (active) { + setOpenCats(prev => prev.has(active.id) ? prev : new Set([...prev, active.id])) + } + }, [location.pathname]) + + const toggleCat = (id: string) => { + setOpenCats(prev => { + const next = new Set(prev) + next.has(id) ? next.delete(id) : next.add(id) + return next + }) + } + + // ── Search filter ────────────────────────────────────────────────────────── + + const filteredNav = useMemo(() => { + const q = search.trim().toLowerCase() + if (!q) return NAV + return NAV.map(cat => ({ + ...cat, + items: cat.items.filter(item => item.label.toLowerCase().includes(q)), + })).filter(cat => cat.items.length > 0 || cat.label.toLowerCase().includes(q)) + }, [search]) + + const effectiveOpen = (id: string) => + search.trim() ? filteredNav.some(c => c.id === id) : openCats.has(id) + + const handleLogout = async () => { + await logout() + navigate('/login') + } + + // ── Render ───────────────────────────────────────────────────────────────── + + return ( + + ) +} diff --git a/nixserver/src/components/navData.ts b/nixserver/src/components/navData.ts new file mode 100644 index 0000000..637b702 --- /dev/null +++ b/nixserver/src/components/navData.ts @@ -0,0 +1,443 @@ +import type { ComponentType } from 'react' +import { + LayoutDashboard, Settings, HelpCircle, Network, BookUser, UserCog, + Wrench, Languages, HardDrive, GitFork, RefreshCw, Activity, + Users, UserCheck, ArrowRightLeft, Paintbrush, Package, + Globe, Database, Wifi, Box, Mail, HeartPulse, Lock, + ShoppingBag, Code2, Puzzle, RotateCcw, +} from 'lucide-react' + +export type NavItem = { label: string; to: string } +export type NavCategory = { + id: string + label: string + icon: ComponentType<{ size?: number | string; className?: string }> + to?: string // optional link to a section index page + items: NavItem[] +} + +export function isItemActive(to: string, pathname: string): boolean { + if (to === '/') return pathname === '/' + return pathname === to || pathname.startsWith(to + '/') +} + +export const NAV: NavCategory[] = [ + { + id: 'server-config', + label: 'Server Configuration', + icon: Settings, + to: '/server-configuration', + items: [ + { label: 'Basic NixServer Setup', to: '/server-configuration/basic-setup' }, + { label: 'Change Root Password', to: '/server-configuration/change-root' }, + { label: 'Configure Analytics', to: '/server-configuration/analytics' }, + { label: 'Configure Cron Jobs', to: '/server-configuration/cron-jobs' }, + { label: 'Initial Quota Setup', to: '/server-configuration/quota-setup' }, + { label: 'Link Server Nodes', to: '/server-configuration/link-nodes' }, + { label: 'Server Profile', to: '/server-configuration/server-profile' }, + { label: 'Server Time', to: '/server-configuration/server-time' }, + { label: 'Statistics Software', to: '/server-configuration/statistics-software' }, + { label: 'Terminal', to: '/server-configuration/terminal' }, + { label: 'Tweak Settings', to: '/server-configuration/tweak-settings' }, + { label: 'Update Preferences', to: '/server-configuration/update-preferences' }, + { label: 'NixServer Marketplace', to: '/server-configuration/marketplace' }, + ], + }, + { + id: 'support', + label: 'Support', + icon: HelpCircle, + items: [ + { label: 'Grant NixPanel Support Access', to: '/security' }, + { label: 'Support Center', to: '/settings' }, + ], + }, + { + id: 'networking', + label: 'Networking Setup', + icon: Network, + items: [ + { label: 'Change Hostname', to: '/security' }, + { label: 'Resolver Configuration', to: '/security' }, + { label: 'Security Center', to: '/security' }, + { label: 'Apache mod_userdir Tweak', to: '/webserver' }, + { label: 'SMTP Restrictions', to: '/email' }, + { label: 'Compiler Access', to: '/security' }, + { label: 'Configure Security Policies', to: '/security' }, + { label: 'mXSTOP Brute Force Protection', to: '/security' }, + { label: 'Host Access Control', to: '/security' }, + { label: 'Manage External Authentications', to: '/security' }, + { label: "Manage root's SSH Keys", to: '/security' }, + { label: 'Manage Wheel Group Users', to: '/security' }, + { label: 'ModSecurity™ Configuration', to: '/security' }, + { label: 'ModSecurity™ Tools', to: '/security' }, + { label: 'ModSecurity™ Vendors', to: '/security' }, + { label: 'Password Strength Configuration', to: '/security' }, + { label: 'Security Advisor', to: '/security' }, + { label: 'Security Questions', to: '/security' }, + { label: 'Shell Fork Bomb Protection', to: '/security' }, + { label: 'SSH Password Authorization Tweak', to: '/security' }, + { label: 'Traceroute Enable/Disable', to: '/security' }, + { label: 'Two-Factor Authentication', to: '/security' }, + ], + }, + { + id: 'contacts', + label: 'Server Contacts', + icon: BookUser, + items: [ + { label: 'Contact Manager', to: '/settings' }, + { label: 'Edit System Mail Preferences', to: '/email' }, + ], + }, + { + id: 'resellers', + label: 'Resellers', + icon: UserCog, + items: [ + { label: 'Change Ownership of an Account', to: '/resellers' }, + { label: 'Change Ownership of Multiple Accounts', to: '/resellers' }, + { label: 'Edit Reseller Nameservers and Privileges', to: '/resellers' }, + { label: 'Email All Resellers', to: '/resellers' }, + { label: "Manage Reseller's IP Delegation", to: '/resellers' }, + { label: "Manage Reseller's Shared IP", to: '/resellers' }, + { label: 'Reseller Center', to: '/resellers' }, + { label: 'Reset Resellers', to: '/resellers' }, + { label: 'Show Reseller Accounts', to: '/resellers' }, + { label: 'View Reseller Usage', to: '/resellers' }, + ], + }, + { + id: 'service-config', + label: 'Service Configuration', + icon: Wrench, + items: [ + { label: 'Apache Configuration', to: '/webserver' }, + { label: 'Log Rotation Configuration', to: '/settings' }, + { label: 'Web Disk Configuration', to: '/settings' }, + { label: 'Web Services Configuration', to: '/settings' }, + { label: 'Exim Configuration Manager', to: '/email' }, + { label: 'FTP Server Selection', to: '/settings' }, + { label: 'Mailserver Configuration', to: '/email' }, + { label: 'Manage Service SSL Certificates', to: '/ssl' }, + { label: 'Nameserver Selection', to: '/dns' }, + { label: 'Service Manager', to: '/services' }, + ], + }, + { + id: 'locales', + label: 'Locales', + icon: Languages, + items: [ + { label: 'Configure Application Locales', to: '/settings' }, + { label: 'Copy a Locale', to: '/settings' }, + { label: 'Delete a Locale', to: '/settings' }, + { label: 'Edit a Locale', to: '/settings' }, + { label: 'Locale XML Download', to: '/settings' }, + { label: 'Locale XML Upload', to: '/settings' }, + { label: 'View Available Locales', to: '/settings' }, + ], + }, + { + id: 'backup', + label: 'Backup', + icon: HardDrive, + items: [ + { label: 'Backup Configuration', to: '/backup' }, + { label: 'Backup Restoration', to: '/backup' }, + { label: 'Backup User Selection', to: '/backup' }, + { label: 'File and Directory Restoration', to: '/backup' }, + ], + }, + { + id: 'clusters', + label: 'Clusters', + icon: GitFork, + items: [ + { label: 'Configuration Cluster', to: '/settings' }, + { label: 'DNS Cluster', to: '/dns' }, + { label: 'Remote Access Key', to: '/security' }, + ], + }, + { + id: 'reboot', + label: 'System Reboot', + icon: RotateCcw, + items: [ + { label: 'Forceful Server Reboot', to: '/system' }, + { label: 'Graceful Server Reboot', to: '/system' }, + ], + }, + { + id: 'server-status', + label: 'Server Status', + icon: Activity, + items: [ + { label: 'Apache Status', to: '/webserver' }, + { label: 'Daily Process Log', to: '/system' }, + { label: 'Server Information', to: '/system' }, + { label: 'Service Status', to: '/services' }, + { label: 'Task Queue Monitor', to: '/system' }, + ], + }, + { + id: 'account-info', + label: 'Account Information', + icon: Users, + items: [ + { label: 'List Accounts', to: '/accounts/list' }, + { label: 'List Parked Domains', to: '/accounts/parked-domains' }, + { label: 'List Subdomains', to: '/accounts/subdomains' }, + { label: 'List Suspended Accounts', to: '/accounts/suspended' }, + { label: 'Show Accounts Over Quota', to: '/accounts/over-quota' }, + { label: 'View Bandwidth Usage', to: '/accounts/bandwidth' }, + ], + }, + { + id: 'account-functions', + label: 'Account Functions', + icon: UserCheck, + items: [ + { label: "Change Site's IP Address", to: '/accounts/change-ip' }, + { label: 'Create a New Account', to: '/accounts/create' }, + { label: 'Email All Users', to: '/accounts/email-users' }, + { label: 'Force Password Change', to: '/accounts/force-password' }, + { label: 'Limit Bandwidth Usage', to: '/accounts/limit-bandwidth' }, + { label: 'Manage Account Suspension', to: '/accounts/suspension' }, + { label: 'Manage Demo Mode', to: '/accounts/demo-mode' }, + { label: 'Manage Shell Access', to: '/accounts/shell-access' }, + { label: 'Modify an Account', to: '/accounts/modify' }, + { label: 'Password Modification', to: '/accounts/password' }, + { label: 'Quota Modification', to: '/accounts/quota' }, + { label: 'Raw Apache Log Download', to: '/accounts/apache-logs' }, + { label: 'Raw NGINX® Log Download', to: '/accounts/nginx-logs' }, + { label: 'Rearrange an Account', to: '/accounts/rearrange' }, + { label: 'Reset Account Bandwidth', to: '/accounts/reset-bandwidth' }, + { label: 'Terminate Accounts', to: '/accounts/terminate' }, + { label: 'Unsuspend Bandwidth Exceeders',to: '/accounts/unsuspend-bandwidth'}, + { label: 'Upgrade/Downgrade an Account', to: '/accounts/upgrade' }, + { label: 'Web Template Editor', to: '/accounts/web-template' }, + ], + }, + { + id: 'multi-account', + label: 'Multi Account Functions', + icon: Users, + items: [ + { label: "Change Multiple Sites' IP Addresses", to: '/accounts/bulk-ip' }, + { label: 'Modify/Upgrade Multiple Accounts', to: '/accounts/bulk-modify' }, + ], + }, + { + id: 'transfers', + label: 'Transfers', + icon: ArrowRightLeft, + items: [ + { label: 'Convert Addon Domain to Account', to: '/accounts' }, + { label: 'Review Transfers and Restores', to: '/accounts' }, + { label: 'Transfer or Restore a cPanel Account', to: '/accounts' }, + { label: 'Transfer Tool', to: '/accounts' }, + ], + }, + { + id: 'themes', + label: 'Themes', + icon: Paintbrush, + items: [ + { label: 'Theme Manager', to: '/settings' }, + ], + }, + { + id: 'packages', + label: 'Packages', + icon: Package, + items: [ + { label: 'Add a Package', to: '/packages' }, + { label: 'Delete a Package', to: '/packages' }, + { label: 'Edit a Package', to: '/packages' }, + { label: 'Feature Manager', to: '/packages' }, + ], + }, + { + id: 'dns', + label: 'DNS Functions', + icon: Globe, + items: [ + { label: 'Add a DNS Zone', to: '/dns' }, + { label: 'Add an A Entry for Hostname', to: '/dns' }, + { label: 'Delete a DNS Zone', to: '/dns' }, + { label: 'DNS Zone Manager', to: '/dns' }, + { label: 'Edit Zone Templates', to: '/dns' }, + { label: 'Email DNS Record Manager', to: '/dns' }, + { label: 'Email Routing Configuration', to: '/email' }, + { label: 'Enable DKIM/SPF Globally', to: '/email' }, + { label: 'Nameserver Record Report', to: '/dns' }, + { label: 'Park a Domain', to: '/dns' }, + { label: 'Perform a DNS Cleanup', to: '/dns' }, + { label: 'Set Zone TTL', to: '/dns' }, + { label: 'Setup/Edit Domain Forwarding', to: '/dns' }, + { label: 'Synchronize DNS Records', to: '/dns' }, + ], + }, + { + id: 'databases', + label: 'Database Services', + icon: Database, + items: [ + { label: 'Change Database Root Password', to: '/settings' }, + { label: 'Change Database User Password', to: '/settings' }, + { label: 'Database Map Tool', to: '/settings' }, + { label: 'Edit Database Configuration', to: '/settings' }, + { label: 'Manage Database Access Hosts', to: '/settings' }, + { label: 'Manage Database Profiles', to: '/settings' }, + { label: 'Manage Database Users', to: '/settings' }, + { label: 'Manage Databases', to: '/settings' }, + { label: 'phpMyAdmin', to: '/settings' }, + { label: 'Repair Databases', to: '/settings' }, + { label: 'Show Database Processes', to: '/settings' }, + { label: 'Upgrade Database Version', to: '/settings' }, + ], + }, + { + id: 'ip', + label: 'IP Functions', + icon: Wifi, + items: [ + { label: 'IPv6 Ranges', to: '/security' }, + { label: 'Add a New IP Address', to: '/security' }, + { label: 'Assign IPv6 Address', to: '/security' }, + { label: 'Configure Remote Service IPs', to: '/security' }, + { label: 'IP Migration Wizard', to: '/security' }, + { label: 'Rebuild the IP Address Pool', to: '/security' }, + { label: 'Show IP Address Usage', to: '/security' }, + { label: 'Show or Delete Current IPs', to: '/security' }, + { label: 'Show/Edit Reserved IPs', to: '/security' }, + ], + }, + { + id: 'software', + label: 'Software', + icon: Box, + items: [ + { label: 'Install a Perl Module', to: '/system' }, + { label: 'Install Distro Packages', to: '/system' }, + { label: 'Module Installers', to: '/system' }, + { label: 'MultiPHP INI Editor', to: '/php' }, + { label: 'MultiPHP Manager', to: '/php' }, + { label: 'NGINX® Manager', to: '/webserver' }, + { label: 'Rebuild RPM Database', to: '/system' }, + { label: 'System Update', to: '/system' }, + { label: 'Update Server Software', to: '/system' }, + { label: 'EasyApache 4', to: '/webserver' }, + ], + }, + { + id: 'email', + label: 'Email', + icon: Mail, + items: [ + { label: 'Email Deliverability', to: '/email' }, + { label: 'Filter Incoming Emails by Country', to: '/email' }, + { label: 'Filter Incoming Emails by Domain', to: '/email' }, + { label: 'Greylisting', to: '/email' }, + { label: 'Mail Delivery Reports', to: '/email' }, + { label: 'Mail Queue Manager', to: '/email' }, + { label: 'Mail Troubleshooter', to: '/email' }, + { label: 'Mailbox Conversion', to: '/email' }, + { label: 'Repair Mailbox Permissions', to: '/email' }, + { label: 'Spamd Startup Configuration', to: '/email' }, + { label: 'View Mail Statistics Summary', to: '/email' }, + { label: 'View Relayers', to: '/email' }, + { label: 'View Sent Summary', to: '/email' }, + ], + }, + { + id: 'system-health', + label: 'System Health', + icon: HeartPulse, + items: [ + { label: 'Background Process Killer', to: '/system' }, + { label: 'Process Manager', to: '/system' }, + { label: 'Show Current Disk Usage', to: '/system' }, + { label: 'Show Current Running Processes', to: '/system' }, + ], + }, + { + id: 'nixpanel', + label: 'NixPanel', + icon: LayoutDashboard, + items: [ + { label: 'Change Log', to: '/settings' }, + { label: 'Customization', to: '/settings' }, + { label: 'Manage Plugins', to: '/settings' }, + { label: 'Modify NixPanel News', to: '/settings' }, + { label: 'Reset a Mailman Password', to: '/email' }, + { label: 'Upgrade to Latest Version',to: '/settings' }, + ], + }, + { + id: 'ssl', + label: 'SSL/TLS', + icon: Lock, + items: [ + { label: 'Generate an SSL Certificate and Signing Request', to: '/ssl' }, + { label: 'Install an SSL Certificate on a Domain', to: '/ssl' }, + { label: 'Manage AutoSSL', to: '/ssl' }, + { label: 'Manage SSL Hosts', to: '/ssl' }, + { label: 'Purchase and Install an SSL Certificate', to: '/ssl' }, + { label: 'SSL Storage Manager', to: '/ssl' }, + { label: 'SSL/TLS Configuration', to: '/ssl' }, + ], + }, + { + id: 'market', + label: 'Market', + icon: ShoppingBag, + items: [ + { label: 'Market Provider Manager', to: '/settings' }, + ], + }, + { + id: 'restart-services', + label: 'Restart Services', + icon: RefreshCw, + items: [ + { label: 'Database Server', to: '/services' }, + { label: 'DNS Server', to: '/services' }, + { label: 'HTTP Server (Apache)', to: '/services' }, + { label: 'IMAP Server', to: '/services' }, + { label: 'Mail Server (Exim)', to: '/services' }, + { label: 'Mailing List Manager (Mailman)', to: '/services' }, + { label: 'PHP-FPM service for Apache', to: '/services' }, + { label: 'SSH Server (OpenSSH)', to: '/services' }, + ], + }, + { + id: 'development', + label: 'Development', + icon: Code2, + items: [ + { label: 'Apps Managed by AppConfig', to: '/settings' }, + { label: 'NixPanel Development Forum', to: '/settings' }, + { label: 'NixPanel Plugin File Generator', to: '/settings' }, + { label: 'Developer Documentation', to: '/settings' }, + { label: 'Manage API Tokens', to: '/settings' }, + { label: 'Manage Hooks', to: '/settings' }, + { label: 'OpenAPI Documentation for NixPanel', to: '/settings' }, + { label: 'OpenAPI Documentation for NixServer', to: '/settings' }, + ], + }, + { + id: 'plugins', + label: 'Plugins', + icon: Puzzle, + items: [ + { label: 'Backuply by Softaculous', to: '/backup' }, + { label: 'ImunifyAV', to: '/security' }, + { label: 'Server Monitoring', to: '/system' }, + { label: 'SitePad Website Builder', to: '/settings' }, + { label: 'Softaculous - Instant Installs', to: '/settings' }, + { label: 'WP Toolkit', to: '/settings' }, + ], + }, +] diff --git a/nixserver/src/pages/Settings.tsx b/nixserver/src/pages/Settings.tsx index 931f78a..74749f1 100644 --- a/nixserver/src/pages/Settings.tsx +++ b/nixserver/src/pages/Settings.tsx @@ -1,49 +1,125 @@ import React, { useEffect, useState } from 'react' -import { Save, RefreshCw } from 'lucide-react' +import { Save, RefreshCw, AlertTriangle } from 'lucide-react' import { api } from '../api/client' +import ContactTab from './settings/ContactTab' +import BasicTab from './settings/BasicTab' +import NameserversSection from './settings/NameserversSection' + +type Tab = 'contact' | 'basic' + +const TABS: { id: Tab; label: string }[] = [ + { id: 'contact', label: 'Contact Information' }, + { id: 'basic', label: 'Basic Config' }, +] export default function Settings() { const [settings, setSettings] = useState>({}) + const [interfaces, setInterfaces] = useState([]) + const [tab, setTab] = useState('contact') const [loading, setLoading] = useState(false) const [saving, setSaving] = useState(false) const [saved, setSaved] = useState(false) + const [error, setError] = useState('') - const fetch = async () => { + const load = async () => { setLoading(true) - try { const r = await api.get('/nixserver/system/settings'); setSettings(r.data.data) } - finally { setLoading(false) } + setError('') + try { + const [settingsRes, ifaceRes] = await Promise.allSettled([ + api.get('/nixserver/system/settings'), + api.get('/nixserver/system/network-interfaces'), + ]) + if (settingsRes.status === 'fulfilled') setSettings(settingsRes.value.data.data) + if (ifaceRes.status === 'fulfilled') setInterfaces(ifaceRes.value.data.data ?? []) + } catch { + setError('Failed to load settings') + } finally { + setLoading(false) + } } - useEffect(() => { fetch() }, []) + useEffect(() => { load() }, []) + + const handleChange = (key: string, value: string) => + setSettings(s => ({ ...s, [key]: value })) const save = async (e: React.FormEvent) => { - e.preventDefault(); setSaving(true) - try { await api.put('/nixserver/system/settings', settings); setSaved(true); setTimeout(() => setSaved(false), 2000) } - finally { setSaving(false) } + e.preventDefault() + setSaving(true) + setError('') + try { + await api.put('/nixserver/system/settings', settings) + setSaved(true) + setTimeout(() => setSaved(false), 2500) + } catch { + setError('Failed to save settings') + } finally { + setSaving(false) + } } return ( -
+
-

Settings

System-wide configuration

- +
+

Basic NixServer Setup

+

Configure server-wide defaults

+
+
-
- {Object.entries(settings).map(([key, value]) => ( -
- - setSettings(s => ({ ...s, [key]: e.target.value }))} - className="flex-1 bg-[#0f1117] border border-[#2a2d3e] text-white text-sm rounded px-3 py-1.5 focus:outline-none focus:border-indigo-500" - /> -
- ))} -
- + ))} +
+ +
+ {tab === 'contact' + ? + : + } +
+ + + +
+ + {saved && Settings saved successfully.}
diff --git a/nixserver/src/pages/server-configuration/Analytics.tsx b/nixserver/src/pages/server-configuration/Analytics.tsx new file mode 100644 index 0000000..b2e05bc --- /dev/null +++ b/nixserver/src/pages/server-configuration/Analytics.tsx @@ -0,0 +1,10 @@ +import ComingSoon from '../../components/ComingSoon' + +export default function Analytics() { + return ( + + ) +} diff --git a/nixserver/src/pages/server-configuration/BasicSetup.tsx b/nixserver/src/pages/server-configuration/BasicSetup.tsx new file mode 100644 index 0000000..e39a675 --- /dev/null +++ b/nixserver/src/pages/server-configuration/BasicSetup.tsx @@ -0,0 +1,3 @@ +// Basic NixServer Setup — full implementation lives in the settings/ sub-components. +// This file is the route entry point at /server-configuration/basic-setup. +export { default } from '../Settings' diff --git a/nixserver/src/pages/server-configuration/ChangeRoot.tsx b/nixserver/src/pages/server-configuration/ChangeRoot.tsx new file mode 100644 index 0000000..d9aba81 --- /dev/null +++ b/nixserver/src/pages/server-configuration/ChangeRoot.tsx @@ -0,0 +1,10 @@ +import ComingSoon from '../../components/ComingSoon' + +export default function ChangeRoot() { + return ( + + ) +} diff --git a/nixserver/src/pages/server-configuration/CronJobs.tsx b/nixserver/src/pages/server-configuration/CronJobs.tsx new file mode 100644 index 0000000..34fb217 --- /dev/null +++ b/nixserver/src/pages/server-configuration/CronJobs.tsx @@ -0,0 +1,10 @@ +import ComingSoon from '../../components/ComingSoon' + +export default function CronJobs() { + return ( + + ) +} diff --git a/nixserver/src/pages/server-configuration/LinkNodes.tsx b/nixserver/src/pages/server-configuration/LinkNodes.tsx new file mode 100644 index 0000000..b29a425 --- /dev/null +++ b/nixserver/src/pages/server-configuration/LinkNodes.tsx @@ -0,0 +1,10 @@ +import ComingSoon from '../../components/ComingSoon' + +export default function LinkNodes() { + return ( + + ) +} diff --git a/nixserver/src/pages/server-configuration/Marketplace.tsx b/nixserver/src/pages/server-configuration/Marketplace.tsx new file mode 100644 index 0000000..679244c --- /dev/null +++ b/nixserver/src/pages/server-configuration/Marketplace.tsx @@ -0,0 +1,10 @@ +import ComingSoon from '../../components/ComingSoon' + +export default function Marketplace() { + return ( + + ) +} diff --git a/nixserver/src/pages/server-configuration/QuotaSetup.tsx b/nixserver/src/pages/server-configuration/QuotaSetup.tsx new file mode 100644 index 0000000..1563a98 --- /dev/null +++ b/nixserver/src/pages/server-configuration/QuotaSetup.tsx @@ -0,0 +1,10 @@ +import ComingSoon from '../../components/ComingSoon' + +export default function QuotaSetup() { + return ( + + ) +} diff --git a/nixserver/src/pages/server-configuration/ServerProfile.tsx b/nixserver/src/pages/server-configuration/ServerProfile.tsx new file mode 100644 index 0000000..153cee8 --- /dev/null +++ b/nixserver/src/pages/server-configuration/ServerProfile.tsx @@ -0,0 +1,10 @@ +import ComingSoon from '../../components/ComingSoon' + +export default function ServerProfile() { + return ( + + ) +} diff --git a/nixserver/src/pages/server-configuration/ServerTime.tsx b/nixserver/src/pages/server-configuration/ServerTime.tsx new file mode 100644 index 0000000..1e11ac4 --- /dev/null +++ b/nixserver/src/pages/server-configuration/ServerTime.tsx @@ -0,0 +1,10 @@ +import ComingSoon from '../../components/ComingSoon' + +export default function ServerTime() { + return ( + + ) +} diff --git a/nixserver/src/pages/server-configuration/StatisticsSoftware.tsx b/nixserver/src/pages/server-configuration/StatisticsSoftware.tsx new file mode 100644 index 0000000..27b01b3 --- /dev/null +++ b/nixserver/src/pages/server-configuration/StatisticsSoftware.tsx @@ -0,0 +1,10 @@ +import ComingSoon from '../../components/ComingSoon' + +export default function StatisticsSoftware() { + return ( + + ) +} diff --git a/nixserver/src/pages/server-configuration/Terminal.tsx b/nixserver/src/pages/server-configuration/Terminal.tsx new file mode 100644 index 0000000..95b7b3a --- /dev/null +++ b/nixserver/src/pages/server-configuration/Terminal.tsx @@ -0,0 +1,10 @@ +import ComingSoon from '../../components/ComingSoon' + +export default function Terminal() { + return ( + + ) +} diff --git a/nixserver/src/pages/server-configuration/TweakSettings.tsx b/nixserver/src/pages/server-configuration/TweakSettings.tsx new file mode 100644 index 0000000..670063d --- /dev/null +++ b/nixserver/src/pages/server-configuration/TweakSettings.tsx @@ -0,0 +1,10 @@ +import ComingSoon from '../../components/ComingSoon' + +export default function TweakSettings() { + return ( + + ) +} diff --git a/nixserver/src/pages/server-configuration/UpdatePreferences.tsx b/nixserver/src/pages/server-configuration/UpdatePreferences.tsx new file mode 100644 index 0000000..9590805 --- /dev/null +++ b/nixserver/src/pages/server-configuration/UpdatePreferences.tsx @@ -0,0 +1,10 @@ +import ComingSoon from '../../components/ComingSoon' + +export default function UpdatePreferences() { + return ( + + ) +} diff --git a/nixserver/src/pages/server-configuration/index.tsx b/nixserver/src/pages/server-configuration/index.tsx new file mode 100644 index 0000000..f62294e --- /dev/null +++ b/nixserver/src/pages/server-configuration/index.tsx @@ -0,0 +1,116 @@ +import { + Settings, Lock, BarChart2, Clock, HardDrive, Link2, + User, Timer, PieChart, Terminal, Sliders, RefreshCw, ShoppingBag, +} from 'lucide-react' +import SectionCard from '../../components/SectionCard' + +const TOOLS = [ + { + label: 'Basic NixServer Setup', + description: 'Configure contact info, shared IP, nameservers, DNS TTL, home directory, and Apache log style.', + to: '/server-configuration/basic-setup', + icon: Settings, + comingSoon: false, + }, + { + label: 'Change Root Password', + description: 'Update the root account password for this server.', + to: '/server-configuration/change-root', + icon: Lock, + comingSoon: false, + }, + { + label: 'Configure Analytics', + description: 'Enable and configure server-side analytics and statistics collection.', + to: '/server-configuration/analytics', + icon: BarChart2, + comingSoon: true, + }, + { + label: 'Configure Cron Jobs', + description: 'Schedule recurring system-level tasks and maintenance scripts.', + to: '/server-configuration/cron-jobs', + icon: Clock, + comingSoon: true, + }, + { + label: 'Initial Quota Setup', + description: 'Configure disk quota enforcement and default account limits.', + to: '/server-configuration/quota-setup', + icon: HardDrive, + comingSoon: true, + }, + { + label: 'Link Server Nodes', + description: 'Connect additional server nodes for clustering or load distribution.', + to: '/server-configuration/link-nodes', + icon: Link2, + comingSoon: true, + }, + { + label: 'Server Profile', + description: 'Set server-wide identity, locale, and branding preferences.', + to: '/server-configuration/server-profile', + icon: User, + comingSoon: true, + }, + { + label: 'Server Time', + description: 'Configure the system clock, timezone, and NTP synchronization.', + to: '/server-configuration/server-time', + icon: Timer, + comingSoon: true, + }, + { + label: 'Statistics Software', + description: 'Install and configure AWStats, Webalizer, or other log analysis tools.', + to: '/server-configuration/statistics-software', + icon: PieChart, + comingSoon: true, + }, + { + label: 'Terminal', + description: 'Open a browser-based terminal session with server access.', + to: '/server-configuration/terminal', + icon: Terminal, + comingSoon: true, + }, + { + label: 'Tweak Settings', + description: 'Fine-tune advanced server behaviours and feature toggles.', + to: '/server-configuration/tweak-settings', + icon: Sliders, + comingSoon: true, + }, + { + label: 'Update Preferences', + description: 'Control automatic update behaviour, channels, and scheduling.', + to: '/server-configuration/update-preferences', + icon: RefreshCw, + comingSoon: true, + }, + { + label: 'NixServer Marketplace', + description: 'Browse and install official extensions, plugins, and integrations.', + to: '/server-configuration/marketplace', + icon: ShoppingBag, + comingSoon: true, + }, +] + +export default function ServerConfiguration() { + return ( +
+
+

Server Configuration

+

Server-wide settings and system configuration tools

+
+ +
+ {TOOLS.map(tool => ( + + ))} +
+
+ ) +} diff --git a/nixserver/src/pages/settings/BasicTab.tsx b/nixserver/src/pages/settings/BasicTab.tsx new file mode 100644 index 0000000..fef44f5 --- /dev/null +++ b/nixserver/src/pages/settings/BasicTab.tsx @@ -0,0 +1,130 @@ +import { AlertTriangle } from 'lucide-react' +import { Field, TextInput, RadioGroup, get } from './FormControls' + +interface Props { + settings: Record + onChange: (key: string, value: string) => void + interfaces: string[] +} + +export default function BasicTab({ settings, onChange, interfaces }: Props) { + const ethdev = get(settings, 'ETHDEV') + const isOther = ethdev !== '' && !interfaces.includes(ethdev) + + return ( +
+ + onChange('ADDR', v)} + placeholder="192.168.1.100" + /> + + + + onChange('ADDR6', v)} + placeholder="2001:db8::1" + /> + + + +
+ + {isOther && ( + onChange('ETHDEV', v)} + placeholder="eth0" + /> + )} +
+
+ + + onChange('HOMEDIR', v)} + placeholder="/home" + /> + + + + onChange('HOMEMATCH', v)} + placeholder="home" + /> + + + + onChange('SCRIPTALIAS', v)} + options={[ + { value: 'yes', label: 'Yes' }, + { value: 'no', label: 'No' }, + ]} + /> + + + + onChange('TTL', v)} + placeholder="14400" + /> + + + + onChange('NSTTL', v)} + placeholder="86400" + /> + + + +
+ onChange('LOGSTYLE', v)} + options={[ + { value: 'combined', label: 'Combined' }, + { value: 'common', label: 'Common' }, + ]} + /> + {get(settings, 'LOGSTYLE') === 'common' && ( +
+ + + Warning: "Common" log format omits the referrer and user-agent + fields. "Combined" is recommended for most setups. + +
+ )} +
+
+
+ ) +} diff --git a/nixserver/src/pages/settings/ContactTab.tsx b/nixserver/src/pages/settings/ContactTab.tsx new file mode 100644 index 0000000..88145bd --- /dev/null +++ b/nixserver/src/pages/settings/ContactTab.tsx @@ -0,0 +1,107 @@ +import { Field, TextInput, get } from './FormControls' + +interface Props { + settings: Record + onChange: (key: string, value: string) => void +} + +export default function ContactTab({ settings, onChange }: Props) { + return ( +
+ + onChange('CONTACTEMAIL', v)} + placeholder="admin@example.com" + type="email" + /> +

+ Enter the email address(es) to which you would like server notification messages to be + sent. Separate multiple addresses with spaces. +

+
+ + + onChange('EMAILFROMNAME', v)} + placeholder="NixPanel" + /> + + + + onChange('EMAILREPLYTO', v)} + placeholder="noreply@example.com" + type="email" + /> + + + + onChange('CONTACTPAGER', v)} + placeholder="5551234567@txt.att.net" + /> + + + + onChange('CONTACTPUSHBULLET', v)} + placeholder="o.xxxxxxxxxxxxxxxxxxxxxx" + type="password" + /> + + + +