#!/usr/bin/env node
const http = require('http');
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
const url = require('url');
const PORT = 3000;
// WEB URL CONFIGURATION
// Set these to match your web server setup
const WEB_ROOT = 'C:\\xampp\\htdocs'; // Windows: C:\xampp\htdocs Linux: /var/www/html
const BASE_URL = 'http://localhost'; // Your domain: http://localhost or http://yourdomain.com
// HTML + React Frontend (embedded)
const HTML_CONTENT = `
React File Manager - Standalone
`;
// Parse multipart form data manually (for file uploads)
function parseMultipart(buffer, boundary) {
const parts = [];
const boundaryBuffer = Buffer.from('--' + boundary);
let start = 0;
while (true) {
const boundaryIndex = buffer.indexOf(boundaryBuffer, start);
if (boundaryIndex === -1) break;
const headersEnd = buffer.indexOf('\r\n\r\n', boundaryIndex);
if (headersEnd === -1) break;
const headers = buffer.slice(boundaryIndex, headersEnd).toString();
const nameMatch = headers.match(/name="([^"]+)"/);
const filenameMatch = headers.match(/filename="([^"]+)"/);
start = headersEnd + 4;
const nextBoundary = buffer.indexOf(boundaryBuffer, start);
const content = nextBoundary === -1 ? buffer.slice(start) : buffer.slice(start, nextBoundary - 2);
if (nameMatch) {
parts.push({
name: nameMatch[1],
filename: filenameMatch ? filenameMatch[1] : null,
content: content
});
}
if (nextBoundary === -1) break;
start = nextBoundary;
}
return parts;
}
// Handle HTTP requests
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const pathname = parsedUrl.pathname;
// Set CORS headers
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
return;
}
// Serve HTML
if (pathname === '/' || pathname === '/index.html') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(HTML_CONTENT);
return;
}
// API endpoints
let body = [];
req.on('data', chunk => body.push(chunk));
req.on('end', async () => {
body = Buffer.concat(body);
try {
// List directory
if (pathname === '/api/list') {
const data = JSON.parse(body.toString());
const dirPath = data.path || process.cwd();
if (!fs.existsSync(dirPath)) {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Directory not found' }));
return;
}
const items = fs.readdirSync(dirPath, { withFileTypes: true });
const files = [];
items.forEach((item, index) => {
try {
const fullPath = path.join(dirPath, item.name);
const stats = fs.statSync(fullPath);
const isDirectory = item.isDirectory();
// Calculate web URL
let webUrl = '';
if (!isDirectory) {
const relativePath = fullPath.replace(WEB_ROOT, '').replace(/\\\\/g, '/');
webUrl = BASE_URL + relativePath;
}
files.push({
id: index + 1,
name: item.name,
type: isDirectory ? 'folder' : 'file',
parent: dirPath,
size: isDirectory ? null : stats.size,
modified: stats.mtime.toISOString().replace('T', ' ').substring(0, 19),
permissions: (stats.mode & parseInt('777', 8)).toString(8).padStart(4, '0'),
writable: true,
url: webUrl
});
} catch (err) {
// Skip files that can't be accessed
}
});
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ files, currentPath: dirPath }));
}
// Read file
else if (pathname === '/api/read') {
const data = JSON.parse(body.toString());
const filePath = path.join(data.parent, data.name);
if (!fs.existsSync(filePath)) {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'File not found' }));
return;
}
const content = fs.readFileSync(filePath, 'utf8');
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ content }));
}
// Write file
else if (pathname === '/api/write') {
const data = JSON.parse(body.toString());
const filePath = path.join(data.parent, data.name);
fs.writeFileSync(filePath, data.content, 'utf8');
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, message: 'File saved' }));
}
// Create file
else if (pathname === '/api/create-file') {
const data = JSON.parse(body.toString());
const filePath = path.join(data.parent, data.name);
if (fs.existsSync(filePath)) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'File already exists' }));
return;
}
fs.writeFileSync(filePath, '', 'utf8');
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, message: 'File created' }));
}
// Create folder
else if (pathname === '/api/create-folder') {
const data = JSON.parse(body.toString());
const folderPath = path.join(data.parent, data.name);
if (fs.existsSync(folderPath)) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Folder already exists' }));
return;
}
fs.mkdirSync(folderPath, { recursive: true });
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, message: 'Folder created' }));
}
// Rename
else if (pathname === '/api/rename') {
const data = JSON.parse(body.toString());
const oldPath = path.join(data.parent, data.oldName);
const newPath = path.join(data.parent, data.newName);
if (!fs.existsSync(oldPath)) {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Item not found' }));
return;
}
fs.renameSync(oldPath, newPath);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, message: 'Renamed successfully' }));
}
// Delete
else if (pathname === '/api/delete') {
const data = JSON.parse(body.toString());
const itemPath = path.join(data.parent, data.name);
if (!fs.existsSync(itemPath)) {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Item not found' }));
return;
}
const stats = fs.statSync(itemPath);
if (stats.isDirectory()) {
fs.rmSync(itemPath, { recursive: true, force: true });
} else {
fs.unlinkSync(itemPath);
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, message: 'Deleted' }));
}
// Bulk delete
else if (pathname === '/api/bulk-delete') {
const data = JSON.parse(body.toString());
let deleted = 0, failed = 0;
data.items.forEach(itemName => {
try {
const itemPath = path.join(data.parent, itemName);
if (fs.existsSync(itemPath)) {
const stats = fs.statSync(itemPath);
if (stats.isDirectory()) {
fs.rmSync(itemPath, { recursive: true, force: true });
} else {
fs.unlinkSync(itemPath);
}
deleted++;
} else {
failed++;
}
} catch (err) {
failed++;
}
});
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
success: true,
message: `Deleted ${deleted} item(s)${failed > 0 ? ` (Failed: ${failed})` : ''}`
}));
}
// Chmod
else if (pathname === '/api/chmod') {
const data = JSON.parse(body.toString());
const itemPath = path.join(data.parent, data.name);
if (!fs.existsSync(itemPath)) {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Item not found' }));
return;
}
const mode = parseInt(data.permissions, 8);
fs.chmodSync(itemPath, mode);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, message: 'Permissions changed' }));
}
// Download
else if (pathname === '/api/download') {
const data = JSON.parse(body.toString());
const itemPath = path.join(data.parent, data.name);
if (!fs.existsSync(itemPath)) {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Item not found' }));
return;
}
const stats = fs.statSync(itemPath);
if (stats.isFile()) {
res.writeHead(200, {
'Content-Type': 'application/octet-stream',
'Content-Disposition': `attachment; filename="${data.name}"`,
'Content-Length': stats.size
});
fs.createReadStream(itemPath).pipe(res);
} else {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Folder download not supported in standalone version' }));
}
}
// Upload
else if (pathname.startsWith('/api/upload')) {
const contentType = req.headers['content-type'];
const boundaryMatch = contentType.match(/boundary=(.+)$/);
if (!boundaryMatch) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid upload' }));
return;
}
const boundary = boundaryMatch[1];
const parts = parseMultipart(body, boundary);
const uploadPath = parsedUrl.query.path || process.cwd();
parts.forEach(part => {
if (part.filename) {
const filePath = path.join(uploadPath, part.filename);
fs.writeFileSync(filePath, part.content);
}
});
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, message: 'File uploaded' }));
}
// Execute command
else if (pathname === '/api/command') {
const data = JSON.parse(body.toString());
const command = data.command;
const cwd = data.cwd || process.cwd();
exec(command, { cwd, timeout: 10000 }, (error, stdout, stderr) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ output: stdout || stderr || error?.message || 'No output' }));
});
}
else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not found' }));
}
} catch (error) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: error.message }));
}
});
});
server.listen(PORT, () => {
console.log(`✅ File Manager Server running on http://localhost:${PORT}`);
console.log(`📁 Starting directory: ${process.cwd()}`);
console.log(`🛑 Press Ctrl+C to stop`);
});