#!/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`); });