From e58769a4bdbf65cee4eb782bfbbf9ab315bc462f Mon Sep 17 00:00:00 2001 From: NixReaper Date: Sat, 25 Apr 2026 09:22:12 -0500 Subject: [PATCH] feat: add NixServer and NixClient React UIs, wire into installer - nixserver/: React 18 + TypeScript + Vite + Tailwind - Login page + placeholder admin dashboard with card grid - Blue accent color (#3b82f6) - Full Tailwind + PostCSS + TypeScript setup - nixclient/: identical structure, user-facing panel - Green accent color (#10b981) - Same build tooling as NixServer - install.sh: new 'Building NixServer & NixClient UIs' step - npm install + npm run build for each - Outputs to /opt/nixpanel/nixserver/dist and /opt/nixpanel/nixclient/dist - Both panels immediately accessible via Apache on 2087 and 2083 - Skips gracefully if source not present (early dev) --- install.sh | 35 ++++++++++- nixclient/index.html | 12 ++++ nixclient/package.json | 24 ++++++++ nixclient/postcss.config.js | 6 ++ nixclient/src/App.tsx | 108 ++++++++++++++++++++++++++++++++++ nixclient/src/index.css | 22 +++++++ nixclient/src/main.tsx | 10 ++++ nixclient/tailwind.config.js | 11 ++++ nixclient/tsconfig.json | 18 ++++++ nixclient/tsconfig.node.json | 10 ++++ nixclient/vite.config.ts | 15 +++++ nixserver/index.html | 12 ++++ nixserver/package.json | 24 ++++++++ nixserver/postcss.config.js | 6 ++ nixserver/src/App.tsx | 110 +++++++++++++++++++++++++++++++++++ nixserver/src/index.css | 22 +++++++ nixserver/src/main.tsx | 10 ++++ nixserver/tailwind.config.js | 11 ++++ nixserver/tsconfig.json | 18 ++++++ nixserver/tsconfig.node.json | 10 ++++ nixserver/vite.config.ts | 15 +++++ 21 files changed, 506 insertions(+), 3 deletions(-) create mode 100644 nixclient/index.html create mode 100644 nixclient/package.json create mode 100644 nixclient/postcss.config.js create mode 100644 nixclient/src/App.tsx create mode 100644 nixclient/src/index.css create mode 100644 nixclient/src/main.tsx create mode 100644 nixclient/tailwind.config.js create mode 100644 nixclient/tsconfig.json create mode 100644 nixclient/tsconfig.node.json create mode 100644 nixclient/vite.config.ts create mode 100644 nixserver/index.html create mode 100644 nixserver/package.json create mode 100644 nixserver/postcss.config.js create mode 100644 nixserver/src/App.tsx create mode 100644 nixserver/src/index.css create mode 100644 nixserver/src/main.tsx create mode 100644 nixserver/tailwind.config.js create mode 100644 nixserver/tsconfig.json create mode 100644 nixserver/tsconfig.node.json create mode 100644 nixserver/vite.config.ts diff --git a/install.sh b/install.sh index 5ce1c83..8eceee9 100644 --- a/install.sh +++ b/install.sh @@ -190,19 +190,48 @@ if ! step_done "source"; then fi success "Source ready" -# ── 8. Build panel binary ───────────────────────────────────────────────────── +# ── 8. Build panel UIs (React apps) ─────────────────────────────────────────── +step "Building NixServer & NixClient UIs" +if ! step_done "build_uis"; then + cd "$SRC_DIR" + + # Build NixServer (WHM admin panel) + if [[ -f "nixserver/package.json" ]]; then + info "Building NixServer…" + cd "$SRC_DIR/nixserver" + npm install --legacy-peer-deps --quiet 2>/dev/null || true + npm run build --silent 2>/dev/null + mkdir -p "$INSTALL_DIR/nixserver/dist" + cp -r dist/* "$INSTALL_DIR/nixserver/dist/" 2>/dev/null || true + success "NixServer built" + fi + + # Build NixClient (cPanel user panel) + if [[ -f "nixclient/package.json" ]]; then + info "Building NixClient…" + cd "$SRC_DIR/nixclient" + npm install --legacy-peer-deps --quiet 2>/dev/null || true + npm run build --silent 2>/dev/null + mkdir -p "$INSTALL_DIR/nixclient/dist" + cp -r dist/* "$INSTALL_DIR/nixclient/dist/" 2>/dev/null || true + success "NixClient built" + fi + + mark_done "build_uis" +fi + +# ── 9. Build panel binary ───────────────────────────────────────────────────── step "Building nixpanel (Rust)" if ! step_done "build_panel"; then if [[ -f "$SRC_DIR/Cargo.toml" ]]; then cd "$SRC_DIR" $CARGO build --release --quiet - # Copy any produced binaries [[ -f "target/release/nixpanel" ]] && \ cp target/release/nixpanel "$BIN_DIR/nixpanel" && chmod 755 "$BIN_DIR/nixpanel" mark_done "build_panel" success "Panel binary built" else - warn "Panel Rust source not yet present in repo — skipping build" + warn "Panel Rust backend not yet present in repo — skipping build" warn "This is expected during early development. Re-run installer when source is ready." fi fi diff --git a/nixclient/index.html b/nixclient/index.html new file mode 100644 index 0000000..cdc5fa5 --- /dev/null +++ b/nixclient/index.html @@ -0,0 +1,12 @@ + + + + + + NixClient — Your Hosting Account + + +
+ + + diff --git a/nixclient/package.json b/nixclient/package.json new file mode 100644 index 0000000..1722da3 --- /dev/null +++ b/nixclient/package.json @@ -0,0 +1,24 @@ +{ + "name": "nixpanel-nixclient", + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@vitejs/plugin-react": "^4.2.0", + "tailwindcss": "^3.4.0", + "postcss": "^8.4.0", + "autoprefixer": "^10.4.0", + "typescript": "^5.3.0", + "vite": "^5.0.0" + } +} diff --git a/nixclient/postcss.config.js b/nixclient/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/nixclient/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/nixclient/src/App.tsx b/nixclient/src/App.tsx new file mode 100644 index 0000000..49ca5cc --- /dev/null +++ b/nixclient/src/App.tsx @@ -0,0 +1,108 @@ +import { useState } from 'react' + +export default function App() { + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + const [isLoggedIn, setIsLoggedIn] = useState(false) + + const handleLogin = (e: React.FormEvent) => { + e.preventDefault() + if (email && password) { + setIsLoggedIn(true) + } + } + + if (isLoggedIn) { + return ( +
+ + +
+
+ {[ + { title: 'My Domains', icon: '🌐', count: '0' }, + { title: 'Email Accounts', icon: '📧', count: '0' }, + { title: 'Databases', icon: '💾', count: '0' }, + { title: 'Backups', icon: '💾', count: '0' }, + ].map((item) => ( +
+
{item.icon}
+

{item.title}

+

{item.count}

+
+ ))} +
+ +
+

Welcome to Your Account

+

+ Manage your domains, emails, databases, and more — all from one dashboard. +

+
+
+
+ ) + } + + return ( +
+
+
+

+ NixClient +

+

Your Hosting Control Panel

+
+ +
+
+
+ + setEmail(e.target.value)} + required + className="w-full bg-gray-900 border border-gray-700 text-white rounded-lg px-4 py-2.5 text-sm placeholder-gray-500 focus:outline-none focus:border-emerald-500 transition-colors" + placeholder="you@example.com" + /> +
+ +
+ + setPassword(e.target.value)} + required + className="w-full bg-gray-900 border border-gray-700 text-white rounded-lg px-4 py-2.5 text-sm placeholder-gray-500 focus:outline-none focus:border-emerald-500 transition-colors" + placeholder="••••••••" + /> +
+ + +
+
+ +

+ NixPanel Client — Manage your hosting account +

+
+
+ ) +} diff --git a/nixclient/src/index.css b/nixclient/src/index.css new file mode 100644 index 0000000..05f13e9 --- /dev/null +++ b/nixclient/src/index.css @@ -0,0 +1,22 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + background-color: rgb(3, 7, 30); + color: rgb(229, 231, 235); +} diff --git a/nixclient/src/main.tsx b/nixclient/src/main.tsx new file mode 100644 index 0000000..964aeb4 --- /dev/null +++ b/nixclient/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/nixclient/tailwind.config.js b/nixclient/tailwind.config.js new file mode 100644 index 0000000..dca8ba0 --- /dev/null +++ b/nixclient/tailwind.config.js @@ -0,0 +1,11 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/nixclient/tsconfig.json b/nixclient/tsconfig.json new file mode 100644 index 0000000..3b0c417 --- /dev/null +++ b/nixclient/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/nixclient/tsconfig.node.json b/nixclient/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/nixclient/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/nixclient/vite.config.ts b/nixclient/vite.config.ts new file mode 100644 index 0000000..b1aaf32 --- /dev/null +++ b/nixclient/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + build: { + outDir: 'dist', + sourcemap: false, + minify: 'terser', + }, + server: { + port: 3001, + strictPort: true, + }, +}) diff --git a/nixserver/index.html b/nixserver/index.html new file mode 100644 index 0000000..ef30f00 --- /dev/null +++ b/nixserver/index.html @@ -0,0 +1,12 @@ + + + + + + NixServer — NixPanel Admin + + +
+ + + diff --git a/nixserver/package.json b/nixserver/package.json new file mode 100644 index 0000000..59d166a --- /dev/null +++ b/nixserver/package.json @@ -0,0 +1,24 @@ +{ + "name": "nixpanel-nixserver", + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@vitejs/plugin-react": "^4.2.0", + "tailwindcss": "^3.4.0", + "postcss": "^8.4.0", + "autoprefixer": "^10.4.0", + "typescript": "^5.3.0", + "vite": "^5.0.0" + } +} diff --git a/nixserver/postcss.config.js b/nixserver/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/nixserver/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/nixserver/src/App.tsx b/nixserver/src/App.tsx new file mode 100644 index 0000000..62ffd93 --- /dev/null +++ b/nixserver/src/App.tsx @@ -0,0 +1,110 @@ +import { useState } from 'react' + +export default function App() { + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + const [isLoggedIn, setIsLoggedIn] = useState(false) + + const handleLogin = (e: React.FormEvent) => { + e.preventDefault() + if (email && password) { + setIsLoggedIn(true) + } + } + + if (isLoggedIn) { + return ( +
+ + +
+
+ {[ + { title: 'Accounts', icon: '👥', count: '0' }, + { title: 'Domains', icon: '🌐', count: '0' }, + { title: 'Databases', icon: '💾', count: '0' }, + { title: 'Email Accounts', icon: '📧', count: '0' }, + { title: 'SSL Certificates', icon: '🔒', count: '0' }, + { title: 'Backups', icon: '💾', count: '0' }, + ].map((item) => ( +
+
{item.icon}
+

{item.title}

+

{item.count}

+
+ ))} +
+ +
+

Welcome to NixServer

+

+ This is a placeholder dashboard. Full server management features are coming soon. +

+
+
+
+ ) + } + + return ( +
+
+
+

+ NixServer +

+

Admin Control Panel

+
+ +
+
+
+ + setEmail(e.target.value)} + required + className="w-full bg-gray-900 border border-gray-700 text-white rounded-lg px-4 py-2.5 text-sm placeholder-gray-500 focus:outline-none focus:border-blue-500 transition-colors" + placeholder="admin@example.com" + /> +
+ +
+ + setPassword(e.target.value)} + required + className="w-full bg-gray-900 border border-gray-700 text-white rounded-lg px-4 py-2.5 text-sm placeholder-gray-500 focus:outline-none focus:border-blue-500 transition-colors" + placeholder="••••••••" + /> +
+ + +
+
+ +

+ NixPanel Server Admin — Login with your credentials +

+
+
+ ) +} diff --git a/nixserver/src/index.css b/nixserver/src/index.css new file mode 100644 index 0000000..05f13e9 --- /dev/null +++ b/nixserver/src/index.css @@ -0,0 +1,22 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + background-color: rgb(3, 7, 30); + color: rgb(229, 231, 235); +} diff --git a/nixserver/src/main.tsx b/nixserver/src/main.tsx new file mode 100644 index 0000000..964aeb4 --- /dev/null +++ b/nixserver/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/nixserver/tailwind.config.js b/nixserver/tailwind.config.js new file mode 100644 index 0000000..dca8ba0 --- /dev/null +++ b/nixserver/tailwind.config.js @@ -0,0 +1,11 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/nixserver/tsconfig.json b/nixserver/tsconfig.json new file mode 100644 index 0000000..3b0c417 --- /dev/null +++ b/nixserver/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/nixserver/tsconfig.node.json b/nixserver/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/nixserver/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/nixserver/vite.config.ts b/nixserver/vite.config.ts new file mode 100644 index 0000000..badef3f --- /dev/null +++ b/nixserver/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], + build: { + outDir: 'dist', + sourcemap: false, + minify: 'terser', + }, + server: { + port: 3000, + strictPort: true, + }, +})