import { Router } from 'express'; import crypto from 'crypto'; import { getDb } from '../config/database.js'; import { authenticateToken } from '../middleware/auth.js'; import { createMeeting, joinMeeting, endMeeting, getMeetingInfo, isMeetingRunning, } from '../config/bbb.js'; const router = Router(); // GET /api/rooms - List user's rooms router.get('/', authenticateToken, async (req, res) => { try { const db = getDb(); const rooms = await db.all(` SELECT r.*, u.name as owner_name FROM rooms r JOIN users u ON r.user_id = u.id WHERE r.user_id = ? ORDER BY r.created_at DESC `, [req.user.id]); res.json({ rooms }); } catch (err) { console.error('List rooms error:', err); res.status(500).json({ error: 'Räume konnten nicht geladen werden' }); } }); // GET /api/rooms/:uid - Get room details router.get('/:uid', authenticateToken, async (req, res) => { try { const db = getDb(); const room = await db.get(` SELECT r.*, u.name as owner_name FROM rooms r JOIN users u ON r.user_id = u.id WHERE r.uid = ? `, [req.params.uid]); if (!room) { return res.status(404).json({ error: 'Raum nicht gefunden' }); } res.json({ room }); } catch (err) { console.error('Get room error:', err); res.status(500).json({ error: 'Raum konnte nicht geladen werden' }); } }); // POST /api/rooms - Create room router.post('/', authenticateToken, async (req, res) => { try { const { name, welcome_message, max_participants, access_code, mute_on_join, require_approval, anyone_can_start, all_join_moderator, record_meeting, guest_access, moderator_code, } = req.body; if (!name || name.trim().length === 0) { return res.status(400).json({ error: 'Raumname ist erforderlich' }); } const uid = crypto.randomBytes(8).toString('hex'); const db = getDb(); const result = await db.run(` INSERT INTO rooms (uid, name, user_id, welcome_message, max_participants, access_code, mute_on_join, require_approval, anyone_can_start, all_join_moderator, record_meeting, guest_access, moderator_code) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `, [ uid, name.trim(), req.user.id, welcome_message || 'Willkommen im Meeting!', max_participants || 0, access_code || null, mute_on_join !== false ? 1 : 0, require_approval ? 1 : 0, anyone_can_start ? 1 : 0, all_join_moderator ? 1 : 0, record_meeting !== false ? 1 : 0, guest_access ? 1 : 0, moderator_code || null, ]); const room = await db.get('SELECT * FROM rooms WHERE id = ?', [result.lastInsertRowid]); res.status(201).json({ room }); } catch (err) { console.error('Create room error:', err); res.status(500).json({ error: 'Raum konnte nicht erstellt werden' }); } }); // PUT /api/rooms/:uid - Update room router.put('/:uid', authenticateToken, async (req, res) => { try { const db = getDb(); const room = await db.get('SELECT * FROM rooms WHERE uid = ? AND user_id = ?', [req.params.uid, req.user.id]); if (!room) { return res.status(404).json({ error: 'Raum nicht gefunden oder keine Berechtigung' }); } const { name, welcome_message, max_participants, access_code, mute_on_join, require_approval, anyone_can_start, all_join_moderator, record_meeting, guest_access, moderator_code, } = req.body; await db.run(` UPDATE rooms SET name = COALESCE(?, name), welcome_message = COALESCE(?, welcome_message), max_participants = COALESCE(?, max_participants), access_code = ?, mute_on_join = COALESCE(?, mute_on_join), require_approval = COALESCE(?, require_approval), anyone_can_start = COALESCE(?, anyone_can_start), all_join_moderator = COALESCE(?, all_join_moderator), record_meeting = COALESCE(?, record_meeting), guest_access = COALESCE(?, guest_access), moderator_code = ?, updated_at = CURRENT_TIMESTAMP WHERE uid = ? `, [ name, welcome_message, max_participants, access_code ?? room.access_code, mute_on_join !== undefined ? (mute_on_join ? 1 : 0) : null, require_approval !== undefined ? (require_approval ? 1 : 0) : null, anyone_can_start !== undefined ? (anyone_can_start ? 1 : 0) : null, all_join_moderator !== undefined ? (all_join_moderator ? 1 : 0) : null, record_meeting !== undefined ? (record_meeting ? 1 : 0) : null, guest_access !== undefined ? (guest_access ? 1 : 0) : null, moderator_code !== undefined ? (moderator_code || null) : room.moderator_code, req.params.uid, ]); const updated = await db.get('SELECT * FROM rooms WHERE uid = ?', [req.params.uid]); res.json({ room: updated }); } catch (err) { console.error('Update room error:', err); res.status(500).json({ error: 'Raum konnte nicht aktualisiert werden' }); } }); // DELETE /api/rooms/:uid - Delete room router.delete('/:uid', authenticateToken, async (req, res) => { try { const db = getDb(); const room = await db.get('SELECT * FROM rooms WHERE uid = ?', [req.params.uid]); if (!room) { return res.status(404).json({ error: 'Raum nicht gefunden' }); } if (room.user_id !== req.user.id && req.user.role !== 'admin') { return res.status(403).json({ error: 'Keine Berechtigung' }); } await db.run('DELETE FROM rooms WHERE uid = ?', [req.params.uid]); res.json({ message: 'Raum erfolgreich gelöscht' }); } catch (err) { console.error('Delete room error:', err); res.status(500).json({ error: 'Raum konnte nicht gelöscht werden' }); } }); // POST /api/rooms/:uid/start - Start meeting router.post('/:uid/start', authenticateToken, async (req, res) => { try { const db = getDb(); const room = await db.get('SELECT * FROM rooms WHERE uid = ? AND user_id = ?', [req.params.uid, req.user.id]); if (!room) { return res.status(404).json({ error: 'Raum nicht gefunden oder keine Berechtigung' }); } await createMeeting(room); const joinUrl = await joinMeeting(room.uid, req.user.name, true); res.json({ joinUrl }); } catch (err) { console.error('Start meeting error:', err); res.status(500).json({ error: 'Meeting konnte nicht gestartet werden' }); } }); // POST /api/rooms/:uid/join - Join meeting router.post('/:uid/join', authenticateToken, async (req, res) => { try { const db = getDb(); const room = await db.get('SELECT * FROM rooms WHERE uid = ?', [req.params.uid]); if (!room) { return res.status(404).json({ error: 'Raum nicht gefunden' }); } // Check access code if set if (room.access_code && req.body.access_code !== room.access_code) { return res.status(403).json({ error: 'Falscher Zugangscode' }); } // Check if meeting is running const running = await isMeetingRunning(room.uid); if (!running) { return res.status(400).json({ error: 'Meeting läuft nicht. Bitte warten Sie, bis der Moderator das Meeting gestartet hat.' }); } const isModerator = room.user_id === req.user.id || room.all_join_moderator; const joinUrl = await joinMeeting(room.uid, req.user.name, isModerator); res.json({ joinUrl }); } catch (err) { console.error('Join meeting error:', err); res.status(500).json({ error: 'Meeting konnte nicht beigetreten werden' }); } }); // POST /api/rooms/:uid/end - End meeting router.post('/:uid/end', authenticateToken, async (req, res) => { try { const db = getDb(); const room = await db.get('SELECT * FROM rooms WHERE uid = ? AND user_id = ?', [req.params.uid, req.user.id]); if (!room) { return res.status(404).json({ error: 'Raum nicht gefunden oder keine Berechtigung' }); } await endMeeting(room.uid); res.json({ message: 'Meeting beendet' }); } catch (err) { console.error('End meeting error:', err); res.status(500).json({ error: 'Meeting konnte nicht beendet werden' }); } }); // GET /api/rooms/:uid/public - Get public room info (no auth needed) router.get('/:uid/public', async (req, res) => { try { const db = getDb(); const room = await db.get(` SELECT r.uid, r.name, r.guest_access, r.welcome_message, r.access_code, u.name as owner_name FROM rooms r JOIN users u ON r.user_id = u.id WHERE r.uid = ? `, [req.params.uid]); if (!room) { return res.status(404).json({ error: 'Raum nicht gefunden' }); } if (!room.guest_access) { return res.status(403).json({ error: 'Gastzugang ist für diesen Raum nicht aktiviert' }); } const running = await isMeetingRunning(room.uid); res.json({ room: { uid: room.uid, name: room.name, owner_name: room.owner_name, welcome_message: room.welcome_message, has_access_code: !!room.access_code, }, running, }); } catch (err) { console.error('Public room info error:', err); res.status(500).json({ error: 'Rauminfos konnten nicht geladen werden' }); } }); // POST /api/rooms/:uid/guest-join - Join meeting as guest (no auth needed) router.post('/:uid/guest-join', async (req, res) => { try { const { name, access_code, moderator_code } = req.body; if (!name || name.trim().length === 0) { return res.status(400).json({ error: 'Name ist erforderlich' }); } const db = getDb(); const room = await db.get('SELECT * FROM rooms WHERE uid = ?', [req.params.uid]); if (!room) { return res.status(404).json({ error: 'Raum nicht gefunden' }); } if (!room.guest_access) { return res.status(403).json({ error: 'Gastzugang ist für diesen Raum nicht aktiviert' }); } // Check access code if set if (room.access_code && access_code !== room.access_code) { return res.status(403).json({ error: 'Falscher Zugangscode' }); } // Check if meeting is running (or if anyone_can_start is enabled) const running = await isMeetingRunning(room.uid); if (!running && !room.anyone_can_start) { return res.status(400).json({ error: 'Meeting läuft nicht. Bitte warten Sie, bis der Moderator das Meeting gestartet hat.' }); } // If meeting not running but anyone_can_start, create it if (!running && room.anyone_can_start) { await createMeeting(room); } // Check moderator code let isModerator = !!room.all_join_moderator; if (!isModerator && moderator_code && room.moderator_code && moderator_code === room.moderator_code) { isModerator = true; } const joinUrl = await joinMeeting(room.uid, name.trim(), isModerator); res.json({ joinUrl }); } catch (err) { console.error('Guest join error:', err); res.status(500).json({ error: 'Beitritt als Gast fehlgeschlagen' }); } }); // GET /api/rooms/:uid/status - Check if meeting is running (public, no guard needed) router.get('/:uid/status', async (req, res) => { try { const running = await isMeetingRunning(req.params.uid); let info = null; if (running) { try { info = await getMeetingInfo(req.params.uid); } catch (e) { // Meeting info might fail } } res.json({ running, participantCount: info?.participantCount || 0, moderatorCount: info?.moderatorCount || 0, }); } catch (err) { res.json({ running: false, participantCount: 0, moderatorCount: 0 }); } }); export default router;