Dashboard
Monitor & control your XAMPP services
Activity Log
Quick Queries
Query History
Apache HTTP Server
Local web server — port 80 & 443
MariaDB Database
SQL database engine — port 3306
Console Output
Project Runner
Run Laravel & PHP projects from your browser. Data is auto-saved.
No projects running yet
Link Notifier
Save important links & paths — synced to Database
No links saved yet
API Key Management
Generate & manage API keys to secure your web.js
web.js so only your Meta Panel can access the API. Keys are
auto-saved and validated per request.Implementation in web.js
// Add this at the top of web.js after const http = require('http') const VALID_API_KEYS = new Set([ /* paste key here */ ]); // Inside createServer, add validation: const reqKey = req.headers['x-api-key']; if (!VALID_API_KEYS.has(reqKey)) { res.writeHead(401, {'Content-Type': 'application/json'}); res.end(JSON.stringify({error:'Invalid API key'})); return; }
node web.js. Meta Panel will automatically send the X-API-Key
header on every request.No API keys yet
Click "Generate New Key" to create one
Public Chat
Global chat room — visible to all users
Installation & Setup
Steps to prepare your environment before using Meta Panel API.
Install XAMPP
Download XAMPP from Apache Friends. Make sure Apache and MariaDB are installed.
C:\xampp. Adjust XAMPP_PATH if different.Create web.js File
Create a working folder, create a
web.js file, and copy the code from the Source Code page.
Verify
After running node web.js, open
http://localhost:8080/api/start-apache. If you see JSON output, installation is successful.
web.js Source Code
Copy this entire code into your web.js
file.
XAMPP_PATH to match
your XAMPP installation location.const { exec } = require('child_process'); const http = require('http'); const XAMPP_PATH = 'C:\\xampp'; const runBg = (cmd, msg) => { exec(`cmd /c "${cmd}"`, (err) => { if (err) console.error(`[ERROR] ${err.message}`); else console.log(`[OK] ${msg}`); }); }; const execStart = (cmd, msg, res) => { exec(`cmd /c "${cmd}"`, (err) => { if (err) res.end(JSON.stringify({ success: false, message: err.message })); else res.end(JSON.stringify({ success: true, message: msg })); }); }; const execStop = (cmd, msg, res) => { exec(cmd, () => res.end(JSON.stringify({ success: true, message: msg }))); }; const execStream = (cmd, res) => { const child = exec(cmd, { maxBuffer: 50*1024*1024 }); res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.setHeader('Transfer-Encoding', 'chunked'); child.stdout.on('data', (d) => res.write(d)); child.stderr.on('data', (d) => res.write(d)); child.on('close', () => res.end()); child.on('error', (e) => { res.write('Error: '+e.message); res.end(); }); return child; }; const server = http.createServer((req, res) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Headers', '*'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } const url = req.url; if (url === '/api/start-apache') execStart(`${XAMPP_PATH}\\apache_start.bat`, 'Apache Started', res); else if (url === '/api/stop-apache') execStop('taskkill /F /IM httpd.exe /T', 'Apache Force Stopped', res); else if (url === '/api/start-mysql') execStart(`${XAMPP_PATH}\\mysql_start.bat`, 'MySQL Started', res); else if (url === '/api/stop-mysql') execStop('taskkill /F /IM mysqld.exe /T', 'MySQL Force Stopped', res); else if (url.startsWith('/api/mysql-query?')) { let q = decodeURIComponent(url.split('query=')[1]); const blocked = 'GRANT,REVOKE,SHUTDOWN,LOAD_FILE,INTO OUTFILE'.split(','); if (blocked.some(b => q.toUpperCase().includes(b.trim()))) { res.end(JSON.stringify({success:false,message:'Command blocked.'})); return; } exec(`"${XAMPP_PATH}\\mysql\\bin\\mysql.exe" -u root -e "${q.replace(/"/g, '\\"')}"`, {maxBuffer:10*1024*1024}, (err,stdout,stderr) => { if (err && !stdout) res.end(JSON.stringify({success:false,message:stderr||err.message})); else res.end(JSON.stringify({success:true,result:(stdout||'').trim()})); }); } else if (url.startsWith('/api/run-stream?')) { let params = new URL(`http://localhost${url}`).searchParams; let cmd = params.get('cmd'); if (cmd) execStream(cmd, res); else res.end(JSON.stringify({error:'Missing cmd'})); } else if (url.startsWith('/api/run?')) { let params = new URL(`http://localhost${url}`).searchParams; let cmd = params.get('cmd'); if (cmd) exec(cmd,{maxBuffer:50*1024*1024,timeout:30000},(err,stdout,stderr)=>{ res.end(JSON.stringify({success:!err,result:(stdout||'').trim(),error:stderr||''})); }); else res.end(JSON.stringify({error:'Missing cmd'})); } else if (url.startsWith('/api/kill-stream?')) { let params = new URL(`http://localhost${url}`).searchParams; let pid = params.get('pid'); if (pid) exec(`taskkill /F /PID ${pid} /T`,()=>res.end(JSON.stringify({success:true}))); else res.end(JSON.stringify({error:'Missing pid'})); } else res.end(JSON.stringify({error:'Endpoint not found'})); }); server.listen(8080, () => { console.log(`Meta Panel API on http://localhost:8080`); runBg(`${XAMPP_PATH}\\apache_start.bat`, 'Apache started'); runBg(`${XAMPP_PATH}\\mysql_start.bat`, 'MariaDB started'); }); process.on('SIGINT', () => { exec('taskkill /F /IM httpd.exe /T', () => exec('taskkill /F /IM mysqld.exe /T', () => process.exit(0))); });
How to Run the Server
Operational guide for Meta Panel API.
Starting the Server
Open CMD/PowerShell, navigate to your
web.js folder:
cd C:\web node web.js
Stopping the Server
Press Ctrl+C in the terminal. Do not close using the X button.
API Endpoints Reference
All available endpoints.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/start-apache | Start Apache |
| GET | /api/stop-apache | Stop Apache |
| GET | /api/start-mysql | Start MariaDB |
| GET | /api/stop-mysql | Stop MariaDB |
| GET | /api/mysql-query?query=... | Execute SQL query |
| GET | /api/run-stream?cmd=... | Streaming command execution |
| GET | /api/run?cmd=... | Execute command (JSON) |
| GET | /api/kill-stream?pid=... | Kill streaming process |
Fetch Implementation Examples
Examples of API integration into your web UI.
// Service control async function control(endpoint) { const r = await fetch(`http://localhost:8080${endpoint}`); const d = await r.json(); console.log(d.success ? d.message : 'Error: '+d.message); } // MySQL query (auto-prefixed with USE db if database selected) async function query(sql, dbName) { let fullSql = dbName ? `USE \`${dbName}\`; ${sql}` : sql; const r = await fetch(`http://localhost:8080/api/mysql-query?query=${encodeURIComponent(fullSql)}`); const d = await r.json(); if (d.success) console.log(d.result); else console.error(d.message); } // Streaming (Laravel/PHP) async function runStream(cmd) { const r = await fetch(`http://localhost:8080/api/run-stream?cmd=${encodeURIComponent(cmd)}`); const reader = r.body.getReader(); const decoder = new TextDecoder(); while (true) { const {done, value} = await reader.read(); if (done) break; process.stdout.write(decoder.decode(value)); } }
Troubleshooting
Common issues and their solutions.
ERR_CONNECTION_REFUSED
Solution: Make sure
node web.js is running in a terminal.
"No database selected"
Solution: Type
use database_name in the MySQL terminal. The latest version auto-prefixes USE.
Port 8080 Already in Use
Solution: Change
const PORT = 8080 to another port.
Database Login Failed
Solution: Make sure popups are not blocked by your browser. Try incognito mode.
API Key 401 Unauthorized
Solution: Make sure the API key in web.js matches the one on the API Keys page. Keys are case-sensitive.