Spaces:
Paused
Paused
| 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<string, string> = { | |
| '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<string, number>, 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<string>((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; | |