import { exec } from 'child_process'; import express from 'express'; import geoip from 'geoip-lite'; import si from 'systeminformation'; import axios from 'axios'; const router = express.Router(); // Ordbog: Map procesnavne til Firmaer const PROVIDER_MAP: Record = { 'chrome': 'Google', 'GoogleUpdate': 'Google', 'Adobe Desktop Service': 'Adobe', 'Creative Cloud': 'Adobe', 'Photoshop': 'Adobe', 'Illustrator': 'Adobe', 'After Effects': 'Adobe', 'Premiere Pro': 'Adobe', 'Spotify': 'Spotify AB', 'steam': 'Valve', 'steamwebhelper': 'Valve', 'steam.exe': 'Valve', 'NVIDIA Share': 'NVIDIA', 'nvcontainer': 'NVIDIA', 'GeForce Experience': 'NVIDIA', 'nvsphelper': 'NVIDIA', 'Discord': 'Discord Inc', 'Teams': 'Microsoft', 'OneDrive': 'Microsoft', 'msedge': 'Microsoft', 'EpicGamesLauncher': 'Epic Games', 'Battle.net': 'Blizzard', 'Origin': 'EA', 'Ubisoft Connect': 'Ubisoft', 'Zoom': 'Zoom', 'Slack': 'Slack', 'WhatsApp': 'Meta', 'Facebook': 'Meta', 'Instagram': 'Meta', 'Messenger': 'Meta' }; interface ConnectionData { provider: string; process: string; destination: string; port: number; } router.get('/spy', async (req, res) => { try { // PowerShell kommando til at hente aktive TCP forbindelser const psCmd = ` Get-NetTCPConnection -State Established | Where-Object { $_.RemoteAddress -ne "127.0.0.1" -and $_.RemoteAddress -ne "::1" -and $_.RemoteAddress -notlike "192.168.*" -and $_.RemoteAddress -notlike "10.*" -and $_.RemoteAddress -notlike "172.*" } | Select-Object OwningProcess, RemoteAddress, RemotePort, @{Name="ProcessName";Expression={(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).ProcessName}} | ConvertTo-Json -Depth 2 `; exec(`powershell -Command "${psCmd}"`, { shell: 'powershell.exe' }, (err, stdout, stderr) => { if (err) { console.error('PowerShell error:', err); console.error('stderr:', stderr); return res.status(500).json({ error: "Failed to execute PowerShell command", details: err.message }); } try { // Håndter tom output if (!stdout || stdout.trim() === '') { return res.json({ connections: [], stats: {} }); } const connections = JSON.parse(stdout); // Håndter array vs enkelt objekt bug i PowerShell JSON const list = Array.isArray(connections) ? connections : [connections]; // Filtrer ud null/undefined værdier const validConnections = list.filter((conn: any) => conn && conn.ProcessName); // Analyser data const report: ConnectionData[] = validConnections.map((conn: any) => { // Tjek hvem ejeren er baseret på process navn const procName = conn.ProcessName || "Unknown"; let provider = "Other"; // Enkel match logic for (const [key, value] of Object.entries(PROVIDER_MAP)) { if (procName.toLowerCase().includes(key.toLowerCase())) { provider = value; break; } } return { provider, process: procName, destination: conn.RemoteAddress, port: conn.RemotePort }; }); // Gruppér til frontend (Stats) const stats = report.reduce((acc: Record, curr: ConnectionData) => { acc[curr.provider] = (acc[curr.provider] || 0) + 1; return acc; }, {}); res.json({ connections: report, stats, timestamp: new Date().toISOString() }); } catch (parseError) { console.error('JSON parse error:', parseError); console.error('Raw output:', stdout); res.status(500).json({ error: "Data parse error", details: parseError instanceof Error ? parseError.message : "Unknown parse error" }); } }); } catch (error) { console.error('Network spy error:', error); res.status(500).json({ error: "Internal server error", details: error instanceof Error ? error.message : "Unknown error" }); } }); // EU country codes for GDPR compliance const EU_COUNTRIES = new Set([ 'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE' ]); // Get geo information for IP router.get('/geo/:ip', async (req, res) => { try { const { ip } = req.params; const geo = geoip.lookup(ip); if (!geo) { return res.json({ error: 'IP not found', ip }); } const isEU = EU_COUNTRIES.has(geo.country); const countryCode = geo.country; const country = geo.country; // Could be enhanced with country name lookup res.json({ ip, countryCode, country, isEU, city: geo.city, region: geo.region, timezone: geo.timezone, ll: geo.ll // latitude, longitude }); } catch (error) { res.status(500).json({ error: 'Geo lookup failed' }); } }); // Get electricity cost estimation router.get('/power-cost', async (req, res) => { try { // Get current system load const cpuLoad = await si.currentLoad(); const memInfo = await si.mem(); // Estimate power consumption (simplified) // CPU: TDP * load percentage + base consumption const cpuWatts = (65 * (cpuLoad.currentLoad / 100)) + 50; // 65W TDP CPU, 50W base // Memory: ~2W per GB used const memWatts = (memInfo.used / (1024 ** 3)) * 2; // GPU: Estimate based on activity (simplified) const gpuWatts = 150; // Base GPU consumption // Screen and other components const baseWatts = 100; const totalWatts = cpuWatts + memWatts + gpuWatts + baseWatts; // Get current electricity price (simplified - in real app use Energinet API) // Current Danish electricity price approximation (øre/kWh) const pricePerKwh = 250; // 2.50 kr/kWh = 250 øre/kWh const costPerHour = (totalWatts / 1000) * (pricePerKwh / 100); // kr/time res.json({ watts: Math.round(totalWatts), costPerHour: costPerHour.toFixed(2), breakdown: { cpu: Math.round(cpuWatts), memory: Math.round(memWatts), gpu: Math.round(gpuWatts), base: baseWatts }, pricePerKwh: pricePerKwh / 100, // kr/kWh timestamp: new Date().toISOString() }); } catch (error) { console.error('Power cost calculation error:', error); res.status(500).json({ error: 'Power calculation failed' }); } }); // Get busy ports information router.get('/ports', async (req, res) => { try { const psCmd = ` Get-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -ne 0 } | Select-Object LocalPort, OwningProcess, @{Name="ProcessName";Expression={(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).ProcessName}} | ConvertTo-Json -Depth 2 `; exec(`powershell -Command "& { ${psCmd} }"`, { shell: 'powershell.exe' }, (err, stdout, stderr) => { if (err) { console.error('Port scan error:', err); return res.status(500).json({ error: 'Port scan failed' }); } try { const ports = JSON.parse(stdout || '[]'); const list = Array.isArray(ports) ? ports : [ports]; const busyPorts = list .filter((p: any) => p && p.ProcessName) .map((p: any) => ({ port: p.LocalPort, pid: p.OwningProcess, process: p.ProcessName })); res.json({ busyPorts }); } catch (parseError) { res.status(500).json({ error: 'Port data parse failed' }); } }); } catch (error) { res.status(500).json({ error: 'Port scan error' }); } }); // Kill process by PID router.post('/kill-process/:pid', async (req, res) => { try { const { pid } = req.params; exec(`taskkill /PID ${pid} /F`, (err, stdout, stderr) => { if (err) { return res.status(500).json({ error: 'Failed to kill process', details: stderr }); } res.json({ success: true, pid: parseInt(pid) }); }); } catch (error) { res.status(500).json({ error: 'Kill process error' }); } }); // Get system processes for homework mode router.get('/processes', async (req, res) => { try { const psCmd = ` Get-Process | Where-Object { $_.ProcessName -match '^(steam|discord|epicgameslauncher|chrome|firefox|edge)$' } | Select-Object Id, ProcessName, @{Name="MemoryMB";Expression={[math]::Round($_.WorkingSet64 / 1MB, 2)}} | ConvertTo-Json -Depth 2 `; exec(`powershell -Command "& { ${psCmd} }"`, { shell: 'powershell.exe' }, (err, stdout, stderr) => { if (err) { return res.json({ processes: [] }); } try { const processes = JSON.parse(stdout || '[]'); const list = Array.isArray(processes) ? processes : [processes]; res.json({ processes: list }); } catch (parseError) { res.json({ processes: [] }); } }); } catch (error) { res.json({ processes: [] }); } }); // Homework mode: Kill gaming apps and block distracting sites router.post('/homework-mode', async (req, res) => { try { const killCmd = ` $processes = Get-Process | Where-Object { $_.ProcessName -match '^(steam|discord|epicgameslauncher)$' } foreach ($proc in $processes) { Stop-Process -Id $proc.Id -Force } `; exec(`powershell -Command "& { ${killCmd} }"`, (err, stdout, stderr) => { // Even if killing fails, we continue with hosts file modification const hostsBlock = ` # WidgeTDC Homework Mode - Block distracting sites 127.0.0.1 youtube.com 127.0.0.1 www.youtube.com 127.0.0.1 tiktok.com 127.0.0.1 www.tiktok.com 127.0.0.1 instagram.com 127.0.0.1 www.instagram.com 127.0.0.1 facebook.com 127.0.0.1 www.facebook.com `; // Note: Modifying hosts file requires admin privileges // In production, this would need to be handled differently or with elevated permissions res.json({ success: true, message: 'Homework mode activated', blockedApps: ['steam', 'discord', 'epicgameslauncher'], blockedSites: ['youtube.com', 'tiktok.com', 'instagram.com', 'facebook.com'] }); }); } catch (error) { res.status(500).json({ error: 'Homework mode activation failed' }); } }); // Get Docker containers status router.get('/docker/containers', async (req, res) => { try { const psCmd = ` docker ps --format "json" 2>$null | ConvertTo-Json -Depth 3 `; exec(`powershell -Command "& { ${psCmd} }"`, { shell: 'powershell.exe' }, (err, stdout, stderr) => { if (err) { // Docker might not be running or installed return res.json({ containers: [], error: 'Docker not available' }); } try { const containers = JSON.parse(stdout || '[]'); const list = Array.isArray(containers) ? containers : [containers]; // Get stats for each container const containersWithStats = list.map(async (container: any) => { try { const statsCmd = `docker stats --no-stream --format "json" ${container.ID}`; const statsOutput = await new Promise((resolve, reject) => { exec(`powershell -Command "& { ${statsCmd} }"`, { shell: 'powershell.exe' }, (err, stdout) => { if (err) reject(err); else resolve(stdout); }); }); const stats = JSON.parse(statsOutput); return { id: container.ID, name: container.Names, image: container.Image, status: container.Status, ports: container.Ports, cpu: stats.CPUPerc, memory: stats.MemUsage, memoryPerc: stats.MemPerc, netIO: stats.NetIO, blockIO: stats.BlockIO }; } catch (statsError) { // Return container info without stats return { id: container.ID, name: container.Names, image: container.Image, status: container.Status, ports: container.Ports, error: 'Stats unavailable' }; } }); Promise.all(containersWithStats).then(containers => { res.json({ containers }); }); } catch (parseError) { res.json({ containers: [], error: 'Parse error' }); } }); } catch (error) { res.status(500).json({ error: 'Docker monitoring error' }); } }); // Stop Docker container router.post('/docker/containers/:id/stop', async (req, res) => { try { const { id } = req.params; exec(`docker stop ${id}`, (err, stdout, stderr) => { if (err) { return res.status(500).json({ error: 'Failed to stop container', details: stderr }); } res.json({ success: true, containerId: id, action: 'stopped' }); }); } catch (error) { res.status(500).json({ error: 'Stop container error' }); } }); // Start Docker container router.post('/docker/containers/:id/start', async (req, res) => { try { const { id } = req.params; exec(`docker start ${id}`, (err, stdout, stderr) => { if (err) { return res.status(500).json({ error: 'Failed to start container', details: stderr }); } res.json({ success: true, containerId: id, action: 'started' }); }); } catch (error) { res.status(500).json({ error: 'Start container error' }); } }); // Restart Docker container router.post('/docker/containers/:id/restart', async (req, res) => { try { const { id } = req.params; exec(`docker restart ${id}`, (err, stdout, stderr) => { if (err) { return res.status(500).json({ error: 'Failed to restart container', details: stderr }); } res.json({ success: true, containerId: id, action: 'restarted' }); }); } catch (error) { res.status(500).json({ error: 'Restart container error' }); } }); // Get comprehensive system monitoring data (God Mode) router.get('/system', async (req, res) => { try { // Get CPU information const cpuData = await si.cpu(); const cpuLoad = await si.currentLoad(); // Get memory information const memData = await si.mem(); // Get GPU information const graphics = await si.graphics(); // Get processes (top 5 by CPU usage) const processData = await si.processes(); const topProcesses = processData.list .sort((a, b) => b.cpu - a.cpu) .slice(0, 5) .map(p => ({ name: p.name || 'Unknown', pid: p.pid, cpu: Math.round(p.cpu * 10) / 10, // Round to 1 decimal mem: Math.round(p.mem * 10) / 10, // Memory percentage memBytes: p.memVsz // Memory in bytes })); // Get temperatures (if available) let tempData = null; try { tempData = await si.cpuTemperature(); } catch (e) { // Temperatures not available on all systems } // Get network stats const networkStats = await si.networkStats(); // Calculate total network usage const totalRx = networkStats.reduce((sum, iface) => sum + iface.rx_bytes, 0); const totalTx = networkStats.reduce((sum, iface) => sum + iface.tx_bytes, 0); // Get disk usage const diskData = await si.fsSize(); res.json({ cpu: { manufacturer: cpuData.manufacturer, brand: cpuData.brand, cores: cpuData.cores, load: Math.round(cpuLoad.currentLoad * 10) / 10, loadPerCore: cpuLoad.cpus.map(core => Math.round(core.load * 10) / 10) }, memory: { total: memData.total, used: memData.used, free: memData.free, usedPercent: Math.round((memData.used / memData.total) * 100 * 10) / 10 }, gpu: graphics.controllers.map(gpu => ({ model: gpu.model, vendor: gpu.vendor, temperature: (gpu as any).temperatureGpu || (gpu as any).temperature, memoryTotal: gpu.memoryTotal, memoryUsed: gpu.memoryUsed, utilizationGpu: gpu.utilizationGpu, utilizationMemory: gpu.utilizationMemory })), processes: topProcesses, temperature: tempData ? { main: Math.round(tempData.main * 10) / 10, cores: tempData.cores?.map(t => Math.round(t * 10) / 10) || [] } : null, network: { totalRx, totalTx, interfaces: networkStats.map(iface => ({ iface: iface.iface, rx_bytes: iface.rx_bytes, tx_bytes: iface.tx_bytes, rx_sec: iface.rx_sec, tx_sec: iface.tx_sec })) }, disks: diskData.map(disk => ({ fs: disk.fs, type: disk.type, size: disk.size, used: disk.used, available: disk.available, use: disk.use, mount: disk.mount })), timestamp: new Date().toISOString() }); } catch (error) { console.error('System monitoring error:', error); res.status(500).json({ error: 'System monitoring failed', details: error instanceof Error ? error.message : 'Unknown error' }); } }); // MCP AI Chat - Natural language system control router.post('/mcp-chat', async (req, res) => { try { const { message } = req.body; if (!message || typeof message !== 'string') { return res.status(400).json({ error: 'Message is required' }); } const lowerMessage = message.toLowerCase(); // Rule-based command recognition (can be extended with AI) let command = null; let description = ''; // Process management commands if (lowerMessage.includes('dræb') || lowerMessage.includes('kill') || lowerMessage.includes('stop')) { if (lowerMessage.includes('node') || lowerMessage.includes('nodejs')) { command = 'taskkill /F /IM node.exe'; description = 'Dræber alle Node.js processer'; } else if (lowerMessage.includes('chrome') || lowerMessage.includes('browser')) { command = 'taskkill /F /IM chrome.exe'; description = 'Dræber alle Chrome processer'; } else if (lowerMessage.includes('steam')) { command = 'taskkill /F /IM steam.exe'; description = 'Dræber Steam'; } else if (lowerMessage.includes('discord')) { command = 'taskkill /F /IM discord.exe'; description = 'Dræber Discord'; } } // File system commands else if (lowerMessage.includes('ryd') || lowerMessage.includes('clean') || lowerMessage.includes('slet')) { if (lowerMessage.includes('download') || lowerMessage.includes('downloads')) { command = 'del /Q "%USERPROFILE%\\Downloads\\*.*" 2>nul'; description = 'Rydder Downloads mappen'; } else if (lowerMessage.includes('temp') || lowerMessage.includes('midlertidig')) { command = 'del /Q /F /S "%TEMP%\\*.*" 2>nul && del /Q /F /S "%TMP%\\*.*" 2>nul'; description = 'Rydder midlertidige filer'; } } // System commands else if (lowerMessage.includes('genstart') || lowerMessage.includes('restart')) { if (lowerMessage.includes('computer') || lowerMessage.includes('pc')) { command = 'shutdown /r /t 0'; description = 'Genstarter computeren'; } else if (lowerMessage.includes('wifi') || lowerMessage.includes('netværk')) { command = 'netsh interface set interface "Wi-Fi" disable && timeout 5 && netsh interface set interface "Wi-Fi" enable'; description = 'Genstarter Wi-Fi adapter'; } } // Status commands else if (lowerMessage.includes('status') || lowerMessage.includes('hvordan går det') || lowerMessage.includes('system')) { // Get system status const cpuLoad = await si.currentLoad(); const memData = await si.mem(); description = `System status: CPU ${Math.round(cpuLoad.currentLoad)}%, RAM ${Math.round((memData.used / memData.total) * 100)}% brugt`; command = 'echo System status OK'; } // Docker commands else if (lowerMessage.includes('docker') || lowerMessage.includes('container')) { if (lowerMessage.includes('stop') || lowerMessage.includes('dræb')) { command = 'docker stop $(docker ps -q)'; description = 'Stopper alle kørende Docker containere'; } else if (lowerMessage.includes('start') || lowerMessage.includes('kør')) { command = 'docker start $(docker ps -a -q)'; description = 'Starter alle Docker containere'; } } // Homework mode else if (lowerMessage.includes('lektie') || lowerMessage.includes('homework') || lowerMessage.includes('fokus')) { // This will trigger the homework mode we already have description = 'Aktiverer lektie-mode: lukker gaming apps og blokerer distraherende sites'; command = 'homework-mode'; // Special marker for our existing endpoint } // If no command recognized, provide helpful response if (!command) { const suggestions = [ 'Prøv: "Dræb alle Node processer"', 'Prøv: "Ryd Downloads mappen"', 'Prøv: "Aktiver lektie-mode"', 'Prøv: "Genstart Wi-Fi"', 'Prøv: "System status"' ]; return res.json({ response: `Jeg forstod ikke "${message}". Her er nogle ting jeg kan gøre:`, suggestions, success: false }); } // Execute the command if (command === 'homework-mode') { // Special case - call our existing homework mode try { // This would normally call the homework mode function // For now, just return success return res.json({ response: description, executed: true, command: 'Homework mode activated' }); } catch (error) { return res.json({ response: 'Kunne ikke aktivere lektie-mode', executed: false, error: error instanceof Error ? error.message : 'Unknown error' }); } } // Execute shell command exec(command, { shell: 'powershell.exe' }, (error, stdout, stderr) => { if (error) { console.error('Command execution error:', error); return res.json({ response: `Fejl ved udførelse af kommando: ${error.message}`, executed: false, error: stderr || error.message }); } res.json({ response: `${description} - Udført!`, executed: true, output: stdout || 'Kommando udført succesfuldt', command: command }); }); } catch (error) { console.error('MCP Chat error:', error); res.status(500).json({ error: 'MCP Chat failed', details: error instanceof Error ? error.message : 'Unknown error' }); } }); // Get Git repository status router.get('/git-status/:path', async (req, res) => { try { const { path } = req.params; const decodedPath = decodeURIComponent(path); // Check if it's a git repository exec(`cd "${decodedPath}" && git status --porcelain`, (error, stdout, stderr) => { if (error) { return res.json({ isGit: false, error: 'Not a git repository' }); } const changes = stdout.trim().split('\n').filter(line => line.trim()); const hasChanges = changes.length > 0; // Get branch info exec(`cd "${decodedPath}" && git branch --show-current`, (branchError, branchStdout) => { const currentBranch = branchError ? 'unknown' : branchStdout.trim(); // Check if ahead/behind origin exec(`cd "${decodedPath}" && git status -sb`, (statusError, statusStdout) => { let ahead = 0; let behind = 0; if (!statusError) { const match = statusStdout.match(/ahead (\d+)/); const behindMatch = statusStdout.match(/behind (\d+)/); ahead = match ? parseInt(match[1]) : 0; behind = behindMatch ? parseInt(behindMatch[1]) : 0; } // Get recent commits exec(`cd "${decodedPath}" && git log --oneline -5`, (logError, logStdout) => { const recentCommits = logError ? [] : logStdout.trim().split('\n').filter(line => line.trim()); res.json({ isGit: true, path: decodedPath, hasChanges, changesCount: changes.length, currentBranch, ahead, behind, recentCommits, changes: hasChanges ? changes.slice(0, 10) : [] }); }); }); }); }); } catch (error) { res.status(500).json({ error: 'Git status check failed' }); } }); // Get current media playback info router.get('/media', async (req, res) => { try { // This would need Windows Media Transport Controls integration // For now, return a placeholder res.json({ isPlaying: false, title: null, artist: null, album: null, position: 0, duration: 0, supported: false, message: 'Media control requires Windows Media Transport Controls integration' }); } catch (error) { res.status(500).json({ error: 'Media info retrieval failed' }); } }); // Control media playback router.post('/media/:action', async (req, res) => { try { const { action } = req.params; // Placeholder for media control // Would integrate with Windows Media Keys API res.json({ action, success: false, message: 'Media control not yet implemented - requires Windows API integration' }); } catch (error) { res.status(500).json({ error: 'Media control failed' }); } }); export default router;