189 lines
5.2 KiB
JavaScript
189 lines
5.2 KiB
JavaScript
const express = require('express');
|
|
const { Client } = require('node-osc');
|
|
const http = require('http');
|
|
const { Server } = require('socket.io');
|
|
|
|
const { exec } = require('child_process');
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const multer = require('multer');
|
|
|
|
const app = express();
|
|
app.use(express.json()); // Allow parsing JSON body
|
|
app.use(express.static(path.join(__dirname, '../frontend/dist')));
|
|
|
|
const si = require('systeminformation');
|
|
|
|
// System Monitoring API
|
|
app.get('/system/stats', async (req, res) => {
|
|
try {
|
|
const cpu = await si.currentLoad();
|
|
const mem = await si.mem();
|
|
const temp = await si.cpuTemperature();
|
|
res.json({
|
|
cpu: cpu.currentLoad.toFixed(1),
|
|
mem: {
|
|
total: (mem.total / 1024 / 1024).toFixed(0),
|
|
used: (mem.used / 1024 / 1024).toFixed(0),
|
|
free: (mem.free / 1024 / 1024).toFixed(0)
|
|
},
|
|
temp: temp.main
|
|
});
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'Failed to fetch system stats' });
|
|
}
|
|
});
|
|
|
|
// Media Directory Configuration
|
|
const MEDIA_DIR = process.env.MEDIA_DIR || '/home/pi/media';
|
|
if (!fs.existsSync(MEDIA_DIR)) {
|
|
fs.mkdirSync(MEDIA_DIR, { recursive: true });
|
|
}
|
|
|
|
// Multer Setup for File Uploads
|
|
const storage = multer.diskStorage({
|
|
destination: (req, file, cb) => {
|
|
cb(null, MEDIA_DIR);
|
|
},
|
|
filename: (req, file, cb) => {
|
|
cb(null, file.originalname);
|
|
}
|
|
});
|
|
const upload = multer({ storage: storage });
|
|
|
|
// Media API Endpoints
|
|
app.get('/media', (req, res) => {
|
|
fs.readdir(MEDIA_DIR, (err, files) => {
|
|
if (err) return res.status(500).json({ error: 'Failed to read media directory' });
|
|
res.json({ files });
|
|
});
|
|
});
|
|
|
|
app.post('/media/upload', upload.single('file'), (req, res) => {
|
|
if (!req.file) return res.status(400).json({ error: 'No file uploaded' });
|
|
res.json({ status: 'uploaded', file: req.file.filename });
|
|
});
|
|
|
|
app.delete('/media/:filename', (req, res) => {
|
|
const filePath = path.join(MEDIA_DIR, req.params.filename);
|
|
fs.unlink(filePath, (err) => {
|
|
if (err) return res.status(500).json({ error: 'Failed to delete file' });
|
|
res.json({ status: 'deleted' });
|
|
});
|
|
});
|
|
|
|
app.post('/media/assign', (req, res) => {
|
|
const { surfaceIndex, filename } = req.body;
|
|
oscClient.send('/ofxPiMapper/source/set', surfaceIndex, filename);
|
|
res.json({ status: 'assigned', surfaceIndex, filename });
|
|
});
|
|
|
|
// Networking API
|
|
app.post('/network/ap', (req, res) => {
|
|
exec('bash scripts/switch-to-ap.sh', (err, stdout) => {
|
|
if (err) return res.status(500).json({ error: err.message });
|
|
res.json({ status: 'switching to ap', output: stdout });
|
|
});
|
|
});
|
|
|
|
app.post('/network/client', (req, res) => {
|
|
exec('bash scripts/switch-to-client.sh', (err, stdout) => {
|
|
if (err) return res.status(500).json({ error: err.message });
|
|
res.json({ status: 'switching to client', output: stdout });
|
|
});
|
|
});
|
|
|
|
app.get('/network/scan', (req, res) => {
|
|
// Mock scan for WiFi networks
|
|
res.json({
|
|
networks: [
|
|
{ ssid: 'Venue-WiFi', strength: -60, encrypted: true },
|
|
{ ssid: 'Artist-Hotspot', strength: -45, encrypted: true },
|
|
{ ssid: 'Free-Internet-Coffee', strength: -80, encrypted: false }
|
|
]
|
|
});
|
|
});
|
|
|
|
// Process management for ofxPiMapper
|
|
app.post('/mapper/start', (req, res) => {
|
|
exec('ofxPiMapper -f', (error) => {
|
|
if (error) {
|
|
console.error(`Error starting ofxPiMapper: ${error.message}`);
|
|
return res.status(500).json({ error: 'Failed to start mapper' });
|
|
}
|
|
});
|
|
res.json({ status: 'starting' });
|
|
});
|
|
|
|
app.post('/mapper/stop', (req, res) => {
|
|
exec('pkill ofxPiMapper', (error) => {
|
|
if (error) {
|
|
console.error(`Error stopping ofxPiMapper: ${error.message}`);
|
|
return res.status(500).json({ error: 'Failed to stop mapper' });
|
|
}
|
|
res.json({ status: 'stopped' });
|
|
});
|
|
});
|
|
|
|
app.post('/mapper/restart', (req, res) => {
|
|
exec('pkill ofxPiMapper', () => {
|
|
setTimeout(() => {
|
|
exec('ofxPiMapper -f');
|
|
res.json({ status: 'restarting' });
|
|
}, 1000);
|
|
});
|
|
});
|
|
|
|
const server = http.createServer(app);
|
|
const io = new Server(server, {
|
|
cors: {
|
|
origin: "*",
|
|
}
|
|
});
|
|
|
|
// OSC Client for ofxPiMapper
|
|
const oscClient = new Client('127.0.0.1', 9999);
|
|
|
|
io.on('connection', (socket) => {
|
|
console.log('Client connected:', socket.id);
|
|
|
|
// Vertex Movement: Sends (surfaceIndex, vertexIndex, x, y)
|
|
socket.on('vertex:move', (data) => {
|
|
const { surfaceIndex, vertexIndex, x, y } = data;
|
|
oscClient.send('/ofxPiMapper/vertex/move', surfaceIndex, vertexIndex, x, y);
|
|
});
|
|
|
|
// Surface Selection
|
|
socket.on('surface:select', (data) => {
|
|
const { surfaceIndex } = data;
|
|
oscClient.send('/ofxPiMapper/surface/select', surfaceIndex);
|
|
});
|
|
|
|
// Add Surface (Quad or Triangle)
|
|
socket.on('surface:add', (data) => {
|
|
const { type } = data; // 'quad' or 'triangle'
|
|
oscClient.send('/ofxPiMapper/surface/add', type);
|
|
});
|
|
|
|
// Delete Surface
|
|
socket.on('surface:delete', (data) => {
|
|
const { surfaceIndex } = data;
|
|
oscClient.send('/ofxPiMapper/surface/delete', surfaceIndex);
|
|
});
|
|
|
|
socket.on('disconnect', () => {
|
|
console.log('Client disconnected:', socket.id);
|
|
});
|
|
});
|
|
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
app.get('/', (req, res) => {
|
|
res.send('MPVJ Backend is running.');
|
|
});
|
|
|
|
server.listen(PORT, () => {
|
|
console.log(`Backend listening on port ${PORT}`);
|
|
});
|