mirror of
https://github.com/Alvin-Zilverstand/narrow_casting_system.git
synced 2026-03-06 11:07:14 +01:00
298 lines
7.9 KiB
JavaScript
298 lines
7.9 KiB
JavaScript
const express = require('express');
|
|
const http = require('http');
|
|
const socketIo = require('socket.io');
|
|
const cors = require('cors');
|
|
const path = require('path');
|
|
const multer = require('multer');
|
|
const { v4: uuidv4 } = require('uuid');
|
|
const fs = require('fs-extra');
|
|
|
|
const DatabaseManager = require('./database/DatabaseManager');
|
|
const ContentManager = require('./services/ContentManager');
|
|
const ScheduleManager = require('./services/ScheduleManager');
|
|
|
|
const app = express();
|
|
const server = http.createServer(app);
|
|
const io = socketIo(server, {
|
|
cors: {
|
|
origin: "*",
|
|
methods: ["GET", "POST"]
|
|
}
|
|
});
|
|
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
// Middleware
|
|
app.use(cors());
|
|
app.use(express.json());
|
|
app.use(express.static(path.join(__dirname, '../public')));
|
|
|
|
// File upload configuration
|
|
const storage = multer.diskStorage({
|
|
destination: (req, file, cb) => {
|
|
let uploadPath;
|
|
if (file.mimetype.startsWith('image/')) {
|
|
uploadPath = path.join(__dirname, '../public/uploads/images');
|
|
} else if (file.mimetype.startsWith('video/')) {
|
|
uploadPath = path.join(__dirname, '../public/uploads/videos');
|
|
} else {
|
|
return cb(new Error('Unsupported file type'));
|
|
}
|
|
cb(null, uploadPath);
|
|
},
|
|
filename: (req, file, cb) => {
|
|
const uniqueName = `${uuidv4()}-${file.originalname}`;
|
|
cb(null, uniqueName);
|
|
}
|
|
});
|
|
|
|
const upload = multer({ storage: storage });
|
|
|
|
// Initialize managers
|
|
const dbManager = new DatabaseManager();
|
|
const contentManager = new ContentManager(dbManager);
|
|
const scheduleManager = new ScheduleManager(dbManager, io);
|
|
|
|
// Initialize database
|
|
dbManager.initialize();
|
|
|
|
// API Routes
|
|
|
|
// Content Management
|
|
app.post('/api/content/upload', upload.single('content'), async (req, res) => {
|
|
try {
|
|
if (!req.file) {
|
|
return res.status(400).json({ error: 'No file uploaded' });
|
|
}
|
|
|
|
const contentData = {
|
|
id: uuidv4(),
|
|
type: req.body.type,
|
|
title: req.body.title || req.file.originalname,
|
|
filename: req.file.filename,
|
|
originalName: req.file.originalname,
|
|
mimeType: req.file.mimetype,
|
|
size: req.file.size,
|
|
path: req.file.path,
|
|
url: `/uploads/${req.file.mimetype.startsWith('image/') ? 'images' : 'videos'}/${req.file.filename}`,
|
|
zone: req.body.zone || 'all',
|
|
duration: parseInt(req.body.duration) || 10,
|
|
createdAt: new Date().toISOString()
|
|
};
|
|
|
|
const content = await contentManager.addContent(contentData);
|
|
|
|
// Emit real-time update
|
|
io.emit('contentUpdated', {
|
|
type: 'content_added',
|
|
content: content
|
|
});
|
|
|
|
res.json({ success: true, content });
|
|
} catch (error) {
|
|
console.error('Upload error:', error);
|
|
res.status(500).json({ error: 'Upload failed' });
|
|
}
|
|
});
|
|
|
|
// Text Content Management
|
|
app.post('/api/content/text', async (req, res) => {
|
|
try {
|
|
const { title, textContent, zone, duration } = req.body;
|
|
|
|
if (!title || !textContent) {
|
|
return res.status(400).json({ error: 'Title and text content are required' });
|
|
}
|
|
|
|
const contentData = {
|
|
id: uuidv4(),
|
|
title: title,
|
|
textContent: textContent,
|
|
zone: zone || 'all',
|
|
duration: parseInt(duration) || 15,
|
|
createdAt: new Date().toISOString()
|
|
};
|
|
|
|
const content = await contentManager.addTextContent(contentData);
|
|
|
|
// Emit real-time update
|
|
io.emit('contentUpdated', {
|
|
type: 'content_added',
|
|
content: content
|
|
});
|
|
|
|
res.json({ success: true, content });
|
|
} catch (error) {
|
|
console.error('Text content creation error:', error);
|
|
res.status(500).json({ error: 'Failed to create text content' });
|
|
}
|
|
});
|
|
|
|
app.get('/api/content', async (req, res) => {
|
|
try {
|
|
const { zone, type } = req.query;
|
|
const content = await contentManager.getContent(zone, type);
|
|
res.json(content);
|
|
} catch (error) {
|
|
console.error('Get content error:', error);
|
|
res.status(500).json({ error: 'Failed to retrieve content' });
|
|
}
|
|
});
|
|
|
|
app.delete('/api/content/:id', async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const content = await contentManager.getContentById(id);
|
|
|
|
if (!content) {
|
|
return res.status(404).json({ error: 'Content not found' });
|
|
}
|
|
|
|
// Delete physical file
|
|
await fs.remove(content.path);
|
|
|
|
// Delete from database
|
|
await contentManager.deleteContent(id);
|
|
|
|
// Emit real-time update
|
|
io.emit('contentUpdated', {
|
|
type: 'content_deleted',
|
|
contentId: id
|
|
});
|
|
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
console.error('Delete content error:', error);
|
|
res.status(500).json({ error: 'Failed to delete content' });
|
|
}
|
|
});
|
|
|
|
// Schedule Management
|
|
app.post('/api/schedule', async (req, res) => {
|
|
try {
|
|
const scheduleData = {
|
|
id: uuidv4(),
|
|
contentId: req.body.contentId,
|
|
zone: req.body.zone,
|
|
startTime: req.body.startTime,
|
|
endTime: req.body.endTime,
|
|
priority: req.body.priority || 1,
|
|
createdAt: new Date().toISOString()
|
|
};
|
|
|
|
const schedule = await scheduleManager.addSchedule(scheduleData);
|
|
|
|
io.emit('scheduleUpdated', {
|
|
type: 'schedule_added',
|
|
schedule: schedule
|
|
});
|
|
|
|
res.json({ success: true, schedule });
|
|
} catch (error) {
|
|
console.error('Schedule creation error:', error);
|
|
res.status(500).json({ error: 'Failed to create schedule' });
|
|
}
|
|
});
|
|
|
|
app.get('/api/schedule/:zone', async (req, res) => {
|
|
try {
|
|
const { zone } = req.params;
|
|
const schedule = await scheduleManager.getActiveSchedule(zone);
|
|
res.json(schedule);
|
|
} catch (error) {
|
|
console.error('Get schedule error:', error);
|
|
res.status(500).json({ error: 'Failed to retrieve schedule' });
|
|
}
|
|
});
|
|
|
|
app.get('/api/zones', async (req, res) => {
|
|
try {
|
|
const zones = await dbManager.getZones();
|
|
res.json(zones);
|
|
} catch (error) {
|
|
console.error('Get zones error:', error);
|
|
res.status(500).json({ error: 'Failed to retrieve zones' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/zones', async (req, res) => {
|
|
try {
|
|
const { id, name, description, icon, displayOrder } = req.body;
|
|
|
|
if (!id || !name) {
|
|
return res.status(400).json({ error: 'Zone ID and name are required' });
|
|
}
|
|
|
|
const zoneData = {
|
|
id: id.toLowerCase().replace(/\s+/g, '-'),
|
|
name: name,
|
|
description: description || '',
|
|
icon: icon || 'fa-map-marker-alt',
|
|
displayOrder: parseInt(displayOrder) || 0
|
|
};
|
|
|
|
const zone = await dbManager.addZone(zoneData);
|
|
|
|
io.emit('zonesUpdated', {
|
|
type: 'zone_added',
|
|
zone: zone
|
|
});
|
|
|
|
res.json({ success: true, zone });
|
|
} catch (error) {
|
|
console.error('Create zone error:', error);
|
|
res.status(500).json({ error: 'Failed to create zone' });
|
|
}
|
|
});
|
|
|
|
// Weather widget data
|
|
app.get('/api/weather', (req, res) => {
|
|
// Mock weather data - in real implementation, integrate with weather API
|
|
const weatherData = {
|
|
temperature: -5,
|
|
snowCondition: 'Frisse sneeuw',
|
|
slopeCondition: 'Perfect',
|
|
humidity: 65,
|
|
windSpeed: 8,
|
|
lastUpdated: new Date().toISOString()
|
|
};
|
|
res.json(weatherData);
|
|
});
|
|
|
|
// Socket.io connection handling
|
|
io.on('connection', (socket) => {
|
|
console.log('Client connected:', socket.id);
|
|
|
|
socket.on('joinZone', (zone) => {
|
|
socket.join(zone);
|
|
console.log(`Client ${socket.id} joined zone: ${zone}`);
|
|
});
|
|
|
|
socket.on('leaveZone', (zone) => {
|
|
socket.leave(zone);
|
|
console.log(`Client ${socket.id} left zone: ${zone}`);
|
|
});
|
|
|
|
socket.on('disconnect', () => {
|
|
console.log('Client disconnected:', socket.id);
|
|
});
|
|
});
|
|
|
|
// Error handling middleware
|
|
app.use((error, req, res, next) => {
|
|
console.error('Server error:', error);
|
|
res.status(500).json({
|
|
error: 'Internal server error',
|
|
message: process.env.NODE_ENV === 'development' ? error.message : 'Something went wrong'
|
|
});
|
|
});
|
|
|
|
// 404 handler
|
|
app.use((req, res) => {
|
|
res.status(404).json({ error: 'Route not found' });
|
|
});
|
|
|
|
server.listen(PORT, () => {
|
|
console.log(`SnowWorld Narrowcasting Server running on port ${PORT}`);
|
|
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
|
|
}); |