Add presentation upload and management features to room functionality
Some checks failed
Build & Push Docker Image / build (push) Failing after 1m11s
Some checks failed
Build & Push Docker Image / build (push) Failing after 1m11s
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import { Router } from 'express';
|
||||
import crypto from 'crypto';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { getDb } from '../config/database.js';
|
||||
import { authenticateToken } from '../middleware/auth.js';
|
||||
import {
|
||||
@@ -10,6 +13,11 @@ import {
|
||||
isMeetingRunning,
|
||||
} from '../config/bbb.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const presentationsDir = path.join(__dirname, '..', '..', 'uploads', 'presentations');
|
||||
if (!fs.existsSync(presentationsDir)) fs.mkdirSync(presentationsDir, { recursive: true });
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Build avatar URL for a user (uploaded image or generated initials)
|
||||
@@ -19,7 +27,7 @@ function getUserAvatarURL(req, user) {
|
||||
return `${baseUrl}/api/auth/avatar/${user.avatar_image}`;
|
||||
}
|
||||
const color = user.avatar_color ? `?color=${encodeURIComponent(user.avatar_color)}` : '';
|
||||
return `${baseUrl}/api/auth/avatar/initials/${encodeURIComponent(user.name)}${color}`;
|
||||
return `${baseUrl}/api/auth/avatar/initials/${encodeURIComponent(user.display_name || user.name)}${color}`;
|
||||
}
|
||||
|
||||
// GET /api/rooms - List user's rooms (owned + shared)
|
||||
@@ -343,9 +351,13 @@ router.post('/:uid/start', authenticateToken, async (req, res) => {
|
||||
|
||||
const baseUrl = `${req.protocol}://${req.get('host')}`;
|
||||
const loginURL = `${baseUrl}/join/${room.uid}`;
|
||||
await createMeeting(room, baseUrl, loginURL);
|
||||
const presentationUrl = room.presentation_file
|
||||
? `${baseUrl}/uploads/presentations/${room.presentation_file}`
|
||||
: null;
|
||||
await createMeeting(room, baseUrl, loginURL, presentationUrl);
|
||||
const avatarURL = getUserAvatarURL(req, req.user);
|
||||
const joinUrl = await joinMeeting(room.uid, req.user.name, true, avatarURL);
|
||||
const displayName = req.user.display_name || req.user.name;
|
||||
const joinUrl = await joinMeeting(room.uid, displayName, true, avatarURL);
|
||||
res.json({ joinUrl });
|
||||
} catch (err) {
|
||||
console.error('Start meeting error:', err);
|
||||
@@ -379,7 +391,7 @@ router.post('/:uid/join', authenticateToken, async (req, res) => {
|
||||
const isShared = !isOwner && await db.get('SELECT id FROM room_shares WHERE room_id = ? AND user_id = ?', [room.id, req.user.id]);
|
||||
const isModerator = isOwner || !!isShared || room.all_join_moderator;
|
||||
const avatarURL = getUserAvatarURL(req, req.user);
|
||||
const joinUrl = await joinMeeting(room.uid, req.user.name, isModerator, avatarURL);
|
||||
const joinUrl = await joinMeeting(room.uid, req.user.display_name || req.user.name, isModerator, avatarURL);
|
||||
res.json({ joinUrl });
|
||||
} catch (err) {
|
||||
console.error('Join meeting error:', err);
|
||||
@@ -523,4 +535,73 @@ router.get('/:uid/status', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/rooms/:uid/presentation - Upload a presentation file for the room
|
||||
router.post('/:uid/presentation', 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: 'Room not found or no permission' });
|
||||
|
||||
const buffer = await new Promise((resolve, reject) => {
|
||||
const chunks = [];
|
||||
req.on('data', chunk => chunks.push(chunk));
|
||||
req.on('end', () => resolve(Buffer.concat(chunks)));
|
||||
req.on('error', reject);
|
||||
});
|
||||
|
||||
const contentType = req.headers['content-type'] || '';
|
||||
const extMap = {
|
||||
'application/pdf': 'pdf',
|
||||
'application/vnd.ms-powerpoint': 'ppt',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
|
||||
'application/vnd.oasis.opendocument.presentation': 'odp',
|
||||
'application/msword': 'doc',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
|
||||
};
|
||||
const ext = extMap[contentType];
|
||||
if (!ext) return res.status(400).json({ error: 'Unsupported file type. Allowed: PDF, PPT, PPTX, ODP, DOC, DOCX' });
|
||||
|
||||
// Max 50MB
|
||||
if (buffer.length > 50 * 1024 * 1024) return res.status(400).json({ error: 'File must not exceed 50MB' });
|
||||
|
||||
const filename = `${room.uid}_${Date.now()}.${ext}`;
|
||||
const filepath = path.join(presentationsDir, filename);
|
||||
|
||||
// Remove old presentation file if exists
|
||||
if (room.presentation_file) {
|
||||
const oldPath = path.join(presentationsDir, room.presentation_file);
|
||||
if (fs.existsSync(oldPath)) fs.unlinkSync(oldPath);
|
||||
}
|
||||
|
||||
fs.writeFileSync(filepath, buffer);
|
||||
await db.run('UPDATE rooms SET presentation_file = ?, updated_at = CURRENT_TIMESTAMP WHERE uid = ?', [filename, req.params.uid]);
|
||||
const updated = await db.get('SELECT * FROM rooms WHERE uid = ?', [req.params.uid]);
|
||||
res.json({ room: updated });
|
||||
} catch (err) {
|
||||
console.error('Presentation upload error:', err);
|
||||
res.status(500).json({ error: 'Presentation could not be uploaded' });
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /api/rooms/:uid/presentation - Remove presentation file
|
||||
router.delete('/:uid/presentation', 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: 'Room not found or no permission' });
|
||||
|
||||
if (room.presentation_file) {
|
||||
const filepath = path.join(presentationsDir, room.presentation_file);
|
||||
if (fs.existsSync(filepath)) fs.unlinkSync(filepath);
|
||||
}
|
||||
|
||||
await db.run('UPDATE rooms SET presentation_file = NULL, updated_at = CURRENT_TIMESTAMP WHERE uid = ?', [req.params.uid]);
|
||||
const updated = await db.get('SELECT * FROM rooms WHERE uid = ?', [req.params.uid]);
|
||||
res.json({ room: updated });
|
||||
} catch (err) {
|
||||
console.error('Presentation delete error:', err);
|
||||
res.status(500).json({ error: 'Presentation could not be removed' });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user