798 lines
22 KiB
JavaScript
798 lines
22 KiB
JavaScript
const express = require('express');
|
|
const app = express();
|
|
const http = require('http').createServer(app);
|
|
const io = require('socket.io')(http);
|
|
const { Sequelize, DataTypes } = require('sequelize');
|
|
const fs = require('fs');
|
|
const bcrypt = require('bcrypt');
|
|
const jwt = require('jsonwebtoken');
|
|
const { v4: uuidv4 } = require('uuid');
|
|
const multer = require('multer');
|
|
const path = require('path');
|
|
require('dotenv').config();
|
|
|
|
// Load configuration
|
|
const config = JSON.parse(fs.readFileSync('config.json'));
|
|
|
|
// Initialize Sequelize
|
|
let sequelize;
|
|
if (config.database.type === 'sqlite') {
|
|
sequelize = new Sequelize({
|
|
dialect: 'sqlite',
|
|
storage: config.database.storage,
|
|
});
|
|
} else if (config.database.type === 'mysql') {
|
|
sequelize = new Sequelize(
|
|
config.database.database,
|
|
config.database.username,
|
|
config.database.password,
|
|
{
|
|
host: config.database.host,
|
|
dialect: 'mysql',
|
|
}
|
|
);
|
|
} else if (config.database.type === 'postgres') {
|
|
sequelize = new Sequelize(
|
|
config.database.database,
|
|
config.database.username,
|
|
config.database.password,
|
|
{
|
|
host: config.database.host,
|
|
dialect: 'postgres',
|
|
}
|
|
);
|
|
}
|
|
|
|
// Define models
|
|
const User = sequelize.define('User', {
|
|
id: {
|
|
type: DataTypes.STRING,
|
|
primaryKey: true,
|
|
},
|
|
email: {
|
|
type: DataTypes.STRING,
|
|
unique: true,
|
|
allowNull: false,
|
|
},
|
|
nickname: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
},
|
|
password: {
|
|
type: DataTypes.STRING,
|
|
allowNull: true,
|
|
},
|
|
type: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
defaultValue: 'permanent',
|
|
},
|
|
expiresAt: {
|
|
type: DataTypes.DATE,
|
|
allowNull: true,
|
|
},
|
|
avatarUrl: {
|
|
type: DataTypes.STRING,
|
|
allowNull: true,
|
|
},
|
|
});
|
|
|
|
const Contact = sequelize.define('Contact', {
|
|
userId: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
},
|
|
contactId: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
},
|
|
contactEmail: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
},
|
|
contactNickname: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
},
|
|
});
|
|
|
|
const Room = sequelize.define('Room', {
|
|
id: {
|
|
type: DataTypes.STRING,
|
|
primaryKey: true,
|
|
},
|
|
name: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
},
|
|
creatorId: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
},
|
|
});
|
|
|
|
const Message = sequelize.define('Message', {
|
|
senderId: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
},
|
|
receiverId: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
},
|
|
roomId: {
|
|
type: DataTypes.STRING,
|
|
allowNull: true,
|
|
},
|
|
senderNickname: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
},
|
|
message: {
|
|
type: DataTypes.TEXT,
|
|
allowNull: true,
|
|
},
|
|
timestamp: {
|
|
type: DataTypes.DATE,
|
|
allowNull: false,
|
|
},
|
|
avatarUrl: {
|
|
type: DataTypes.STRING,
|
|
allowNull: true,
|
|
},
|
|
fileUrl: {
|
|
type: DataTypes.STRING,
|
|
allowNull: true,
|
|
},
|
|
fileName: {
|
|
type: DataTypes.STRING,
|
|
allowNull: true,
|
|
},
|
|
reactions: {
|
|
type: DataTypes.JSON,
|
|
allowNull: true,
|
|
defaultValue: {},
|
|
},
|
|
});
|
|
|
|
const Meeting = sequelize.define('Meeting', {
|
|
id: {
|
|
type: DataTypes.STRING,
|
|
primaryKey: true,
|
|
},
|
|
title: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
},
|
|
scheduledTime: {
|
|
type: DataTypes.DATE,
|
|
allowNull: false,
|
|
},
|
|
duration: {
|
|
type: DataTypes.INTEGER, // Duration in minutes
|
|
allowNull: false,
|
|
},
|
|
hostId: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
},
|
|
participants: {
|
|
type: DataTypes.JSON, // List of user IDs in the meeting
|
|
allowNull: true,
|
|
defaultValue: [],
|
|
},
|
|
waitingRoom: {
|
|
type: DataTypes.JSON, // List of user IDs in the waiting room
|
|
allowNull: true,
|
|
defaultValue: [],
|
|
},
|
|
breakoutRooms: {
|
|
type: DataTypes.JSON, // Map of breakout room IDs to user IDs
|
|
allowNull: true,
|
|
defaultValue: {},
|
|
},
|
|
status: {
|
|
type: DataTypes.STRING,
|
|
allowNull: false,
|
|
defaultValue: 'scheduled', // scheduled, active, ended
|
|
},
|
|
});
|
|
|
|
// Sync database
|
|
sequelize.sync({ alter: true }).then(() => {
|
|
console.log('Database synced');
|
|
});
|
|
|
|
// Set up file upload
|
|
const upload = multer({
|
|
dest: 'uploads/',
|
|
fileFilter: (req, file, cb) => {
|
|
console.log('Received file with MIME type:', file.mimetype);
|
|
|
|
const allowedMimeTypes = [
|
|
'image/jpeg',
|
|
'image/png',
|
|
'image/gif',
|
|
'image/bmp',
|
|
'image/webp',
|
|
'application/pdf',
|
|
'text/plain',
|
|
'application/msword',
|
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
];
|
|
|
|
if (allowedMimeTypes.includes(file.mimetype)) {
|
|
cb(null, true);
|
|
} else {
|
|
const extname = path.extname(file.originalname).toLowerCase();
|
|
const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.pdf', '.txt', '.doc', '.docx'];
|
|
if (allowedExtensions.includes(extname)) {
|
|
console.log('MIME type not recognized, but file extension is valid:', extname);
|
|
cb(null, true);
|
|
} else {
|
|
console.log('File rejected: Not an allowed file type');
|
|
cb(new Error('Only images, PDFs, text, and Word documents are allowed'), false);
|
|
}
|
|
}
|
|
},
|
|
});
|
|
|
|
// Serve uploaded files statically
|
|
app.use('/uploads', express.static('uploads'));
|
|
|
|
// JWT secret from environment variable
|
|
const JWT_SECRET = process.env.JWT_SECRET;
|
|
if (!JWT_SECRET) {
|
|
console.error('JWT_SECRET is not defined in .env file');
|
|
process.exit(1);
|
|
}
|
|
console.log('Using JWT_SECRET:', JWT_SECRET);
|
|
|
|
// Load BASE_URL from environment variable
|
|
const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
|
console.log('Using BASE_URL:', BASE_URL);
|
|
|
|
// JWT middleware
|
|
const authenticateJWT = async (req, res, next) => {
|
|
const token = req.headers.authorization?.split(' ')[1];
|
|
if (!token) {
|
|
console.log('No token provided');
|
|
return res.status(401).json({ error: 'Unauthorized: No token provided' });
|
|
}
|
|
try {
|
|
console.log('Verifying token with JWT_SECRET:', JWT_SECRET);
|
|
const decoded = jwt.verify(token, JWT_SECRET);
|
|
console.log('JWT decoded successfully:', decoded);
|
|
req.userId = decoded.userId;
|
|
next();
|
|
} catch (e) {
|
|
console.log('Invalid token:', e.message);
|
|
res.status(401).json({ error: `Invalid token: ${e.message}` });
|
|
}
|
|
};
|
|
|
|
// Middleware
|
|
app.use(express.json());
|
|
|
|
// API endpoints
|
|
app.post('/auth/login', async (req, res) => {
|
|
const { username, password } = req.body; // Username is the email
|
|
console.log('Login attempt:', { username });
|
|
const user = await User.findOne({ where: { email: username } });
|
|
if (!user || !user.password) {
|
|
console.log('User not found or no password');
|
|
return res.status(401).json({ error: 'Invalid email or password' });
|
|
}
|
|
const isValid = await bcrypt.compare(password, user.password);
|
|
if (!isValid) {
|
|
console.log('Invalid password');
|
|
return res.status(401).json({ error: 'Invalid email or password' });
|
|
}
|
|
console.log('Generating token with userId:', user.id);
|
|
console.log('Signing token with JWT_SECRET:', JWT_SECRET);
|
|
const token = jwt.sign({ userId: user.id }, JWT_SECRET, { expiresIn: '7d' });
|
|
console.log('Generated JWT for login:', token);
|
|
res.json({ token });
|
|
});
|
|
|
|
app.post('/auth/register', async (req, res) => {
|
|
const { username, nickname, password } = req.body; // Username is the email
|
|
console.log('Register attempt:', { username, nickname });
|
|
try {
|
|
const id = uuidv4();
|
|
const hashedPassword = await bcrypt.hash(password, 10);
|
|
const user = await User.create({
|
|
id,
|
|
email: username,
|
|
nickname,
|
|
password: hashedPassword,
|
|
type: 'permanent',
|
|
});
|
|
console.log('Generating token with userId:', id);
|
|
console.log('Signing token with JWT_SECRET:', JWT_SECRET);
|
|
const token = jwt.sign({ userId: id }, JWT_SECRET, { expiresIn: '7d' });
|
|
console.log('Generated JWT for register:', token);
|
|
res.status(201).json({ token });
|
|
} catch (e) {
|
|
console.log('Registration error:', e.message);
|
|
res.status(400).json({ error: e.message });
|
|
}
|
|
});
|
|
|
|
app.post('/auth/burner', async (req, res) => {
|
|
try {
|
|
const id = uuidv4();
|
|
const email = `guest_${id.substring(0, 8)}@epyks.com`;
|
|
const user = await User.create({
|
|
id,
|
|
email,
|
|
nickname: `Guest_${id.substring(0, 8)}`,
|
|
type: 'burner',
|
|
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
|
|
});
|
|
console.log('Generating token with userId:', id);
|
|
console.log('Signing token with JWT_SECRET:', JWT_SECRET);
|
|
const token = jwt.sign({ userId: id }, JWT_SECRET, { expiresIn: '24h' });
|
|
console.log('Generated JWT for burner:', token);
|
|
res.status(201).json({ token });
|
|
} catch (e) {
|
|
console.log('Burner account error:', e.message);
|
|
res.status(400).json({ error: e.message });
|
|
}
|
|
});
|
|
|
|
app.get('/users/me', authenticateJWT, async (req, res) => {
|
|
const user = await User.findByPk(req.userId);
|
|
if (user) {
|
|
res.json(user);
|
|
} else {
|
|
console.log('User not found for ID:', req.userId);
|
|
res.status(404).json({ error: 'User not found' });
|
|
}
|
|
});
|
|
|
|
app.get('/users/:id', authenticateJWT, async (req, res) => {
|
|
const user = await User.findByPk(req.params.id);
|
|
if (user) {
|
|
res.json(user);
|
|
} else {
|
|
res.status(404).json({ error: 'User not found' });
|
|
}
|
|
});
|
|
|
|
app.get('/users', authenticateJWT, async (req, res) => {
|
|
const { email } = req.query;
|
|
const user = await User.findOne({ where: { email } });
|
|
if (user) {
|
|
res.json(user);
|
|
} else {
|
|
res.status(404).json({ error: 'User not found' });
|
|
}
|
|
});
|
|
|
|
app.get('/contacts', authenticateJWT, async (req, res) => {
|
|
const { userId } = req.query;
|
|
const contacts = await Contact.findAll({ where: { userId } });
|
|
res.json(contacts);
|
|
});
|
|
|
|
app.post('/contacts', authenticateJWT, async (req, res) => {
|
|
try {
|
|
const contact = await Contact.create(req.body);
|
|
res.status(201).json(contact);
|
|
} catch (e) {
|
|
res.status(400).json({ error: e.message });
|
|
}
|
|
});
|
|
|
|
app.get('/rooms', authenticateJWT, async (req, res) => {
|
|
try {
|
|
const rooms = await Room.findAll();
|
|
res.status(200).json(rooms);
|
|
} catch (e) {
|
|
console.log('Error fetching rooms:', e.message);
|
|
res.status(500).json({ error: 'Server error fetching rooms' });
|
|
}
|
|
});
|
|
|
|
app.post('/rooms', authenticateJWT, async (req, res) => {
|
|
const { name } = req.body;
|
|
if (!name) {
|
|
return res.status(400).json({ error: 'Room name is required' });
|
|
}
|
|
try {
|
|
const id = uuidv4();
|
|
const room = await Room.create({
|
|
id,
|
|
name,
|
|
creatorId: req.userId,
|
|
});
|
|
res.status(201).json(room);
|
|
} catch (e) {
|
|
console.log('Error creating room:', e.message);
|
|
res.status(400).json({ error: 'Error creating room' });
|
|
}
|
|
});
|
|
|
|
app.post('/messages', authenticateJWT, async (req, res) => {
|
|
try {
|
|
const message = await Message.create(req.body);
|
|
res.status(201).json(message);
|
|
} catch (e) {
|
|
console.log('Error creating message:', e.message);
|
|
res.status(400).json({ error: 'Error creating message' });
|
|
}
|
|
});
|
|
|
|
app.get('/messages', authenticateJWT, async (req, res) => {
|
|
const { roomId } = req.query;
|
|
if (!roomId) {
|
|
return res.status(400).json({ error: 'roomId is required' });
|
|
}
|
|
try {
|
|
const messages = await Message.findAll({ where: { roomId } });
|
|
res.status(200).json(messages);
|
|
} catch (e) {
|
|
console.log('Error fetching messages:', e.message);
|
|
res.status(500).json({ error: 'Server error fetching messages' });
|
|
}
|
|
});
|
|
|
|
app.post('/users/update', authenticateJWT, upload.single('avatar'), async (req, res) => {
|
|
try {
|
|
const user = await User.findByPk(req.userId);
|
|
if (!user) {
|
|
return res.status(404).json({ error: 'User not found' });
|
|
}
|
|
if (req.file) {
|
|
const avatarPath = `/uploads/${req.file.filename}`;
|
|
const avatarUrl = `${BASE_URL}${avatarPath}`;
|
|
console.log('Avatar uploaded successfully:', avatarUrl);
|
|
await user.update({ avatarUrl });
|
|
res.json({ avatarUrl });
|
|
} else {
|
|
console.log('No file uploaded');
|
|
res.status(400).json({ error: 'No file uploaded' });
|
|
}
|
|
} catch (e) {
|
|
console.log('Error in /users/update:', e.message);
|
|
res.status(400).json({ error: e.message });
|
|
}
|
|
});
|
|
|
|
app.post('/messages/upload', authenticateJWT, upload.single('file'), async (req, res) => {
|
|
try {
|
|
const { roomId, senderId, senderNickname, timestamp } = req.body;
|
|
if (!roomId || !senderId || !senderNickname || !timestamp) {
|
|
return res.status(400).json({ error: 'Missing required fields' });
|
|
}
|
|
if (req.file) {
|
|
const filePath = `/uploads/${req.file.filename}`;
|
|
const fileUrl = `${BASE_URL}${filePath}`;
|
|
const fileName = req.file.originalname;
|
|
console.log('File uploaded successfully:', fileUrl);
|
|
|
|
const message = await Message.create({
|
|
senderId,
|
|
receiverId: senderId,
|
|
roomId,
|
|
senderNickname,
|
|
timestamp,
|
|
fileUrl,
|
|
fileName,
|
|
});
|
|
|
|
io.to(roomId).emit('chat-message', {
|
|
id: message.id,
|
|
senderId,
|
|
senderNickname,
|
|
timestamp,
|
|
roomId,
|
|
fileUrl,
|
|
fileName,
|
|
reactions: message.reactions || {},
|
|
});
|
|
|
|
res.json({ fileUrl, fileName });
|
|
} else {
|
|
console.log('No file uploaded');
|
|
res.status(400).json({ error: 'No file uploaded' });
|
|
}
|
|
} catch (e) {
|
|
console.log('Error in /messages/upload:', e.message);
|
|
res.status(400).json({ error: e.message });
|
|
}
|
|
});
|
|
|
|
app.post('/messages/:id/react', authenticateJWT, async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const { emoji } = req.body;
|
|
const userId = req.userId;
|
|
|
|
if (!emoji) {
|
|
return res.status(400).json({ error: 'Emoji is required' });
|
|
}
|
|
|
|
const message = await Message.findByPk(id);
|
|
if (!message) {
|
|
return res.status(404).json({ error: 'Message not found' });
|
|
}
|
|
|
|
let reactions = message.reactions || {};
|
|
|
|
if (reactions[emoji]) {
|
|
const userIndex = reactions[emoji].indexOf(userId);
|
|
if (userIndex !== -1) {
|
|
reactions[emoji].splice(userIndex, 1);
|
|
if (reactions[emoji].length === 0) {
|
|
delete reactions[emoji];
|
|
}
|
|
} else {
|
|
reactions[emoji].push(userId);
|
|
}
|
|
} else {
|
|
reactions[emoji] = [userId];
|
|
}
|
|
|
|
await message.update({ reactions });
|
|
|
|
io.to(message.roomId).emit('reaction-update', {
|
|
messageId: id,
|
|
reactions: message.reactions,
|
|
});
|
|
|
|
res.status(200).json({ reactions: message.reactions });
|
|
} catch (e) {
|
|
console.log('Error in /messages/:id/react:', e.message);
|
|
res.status(400).json({ error: e.message });
|
|
}
|
|
});
|
|
|
|
// Meeting Management Endpoints
|
|
app.post('/meetings/schedule', authenticateJWT, async (req, res) => {
|
|
try {
|
|
const { title, scheduledTime, duration } = req.body;
|
|
if (!title || !scheduledTime || !duration) {
|
|
return res.status(400).json({ error: 'Title, scheduledTime, and duration are required' });
|
|
}
|
|
|
|
const id = uuidv4();
|
|
const meeting = await Meeting.create({
|
|
id,
|
|
title,
|
|
scheduledTime,
|
|
duration,
|
|
hostId: req.userId,
|
|
status: 'scheduled',
|
|
});
|
|
|
|
res.status(201).json(meeting);
|
|
} catch (e) {
|
|
console.log('Error scheduling meeting:', e.message);
|
|
res.status(400).json({ error: e.message });
|
|
}
|
|
});
|
|
|
|
app.get('/meetings', authenticateJWT, async (req, res) => {
|
|
try {
|
|
const meetings = await Meeting.findAll({
|
|
where: {
|
|
[Sequelize.Op.or]: [
|
|
{ hostId: req.userId },
|
|
{ participants: { [Sequelize.Op.contains]: [req.userId] } },
|
|
],
|
|
},
|
|
});
|
|
res.status(200).json(meetings);
|
|
} catch (e) {
|
|
console.log('Error fetching meetings:', e.message);
|
|
res.status(500).json({ error: 'Server error fetching meetings' });
|
|
}
|
|
});
|
|
|
|
app.post('/meetings/:id/join', authenticateJWT, async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const userId = req.userId;
|
|
|
|
const meeting = await Meeting.findByPk(id);
|
|
if (!meeting) {
|
|
return res.status(404).json({ error: 'Meeting not found' });
|
|
}
|
|
|
|
if (meeting.hostId === userId) {
|
|
// Host joins directly
|
|
let participants = meeting.participants || [];
|
|
if (!participants.includes(userId)) {
|
|
participants.push(userId);
|
|
await meeting.update({ participants, status: 'active' });
|
|
}
|
|
io.to(id).emit('participant-admitted', { userId, meetingId: id });
|
|
res.status(200).json(meeting);
|
|
} else {
|
|
// Non-host joins the waiting room
|
|
let waitingRoom = meeting.waitingRoom || [];
|
|
if (!waitingRoom.includes(userId) && !meeting.participants.includes(userId)) {
|
|
waitingRoom.push(userId);
|
|
await meeting.update({ waitingRoom });
|
|
io.to(id).emit('waiting-room-update', { meetingId: id, waitingRoom });
|
|
}
|
|
res.status(200).json({ status: 'waiting', meeting });
|
|
}
|
|
} catch (e) {
|
|
console.log('Error joining meeting:', e.message);
|
|
res.status(400).json({ error: e.message });
|
|
}
|
|
});
|
|
|
|
app.post('/meetings/:id/admit', authenticateJWT, async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const { userId } = req.body;
|
|
const hostId = req.userId;
|
|
|
|
const meeting = await Meeting.findByPk(id);
|
|
if (!meeting) {
|
|
return res.status(404).json({ error: 'Meeting not found' });
|
|
}
|
|
|
|
if (meeting.hostId !== hostId) {
|
|
return res.status(403).json({ error: 'Only the host can admit participants' });
|
|
}
|
|
|
|
let waitingRoom = meeting.waitingRoom || [];
|
|
let participants = meeting.participants || [];
|
|
|
|
const userIndex = waitingRoom.indexOf(userId);
|
|
if (userIndex !== -1) {
|
|
waitingRoom.splice(userIndex, 1);
|
|
participants.push(userId);
|
|
await meeting.update({ waitingRoom, participants });
|
|
io.to(id).emit('waiting-room-update', { meetingId: id, waitingRoom });
|
|
io.to(id).emit('participant-admitted', { userId, meetingId: id });
|
|
}
|
|
|
|
res.status(200).json(meeting);
|
|
} catch (e) {
|
|
console.log('Error admitting participant:', e.message);
|
|
res.status(400).json({ error: e.message });
|
|
}
|
|
});
|
|
|
|
app.post('/meetings/:id/breakout', authenticateJWT, async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const { rooms } = req.body; // { room1: [userId1, userId2], room2: [userId3] }
|
|
const hostId = req.userId;
|
|
|
|
const meeting = await Meeting.findByPk(id);
|
|
if (!meeting) {
|
|
return res.status(404).json({ error: 'Meeting not found' });
|
|
}
|
|
|
|
if (meeting.hostId !== hostId) {
|
|
return res.status(403).json({ error: 'Only the host can create breakout rooms' });
|
|
}
|
|
|
|
await meeting.update({ breakoutRooms: rooms });
|
|
io.to(id).emit('breakout-rooms-update', { meetingId: id, breakoutRooms: rooms });
|
|
|
|
res.status(200).json(meeting);
|
|
} catch (e) {
|
|
console.log('Error creating breakout rooms:', e.message);
|
|
res.status(400).json({ error: e.message });
|
|
}
|
|
});
|
|
|
|
// Cleanup expired burner accounts
|
|
setInterval(async () => {
|
|
await User.destroy({
|
|
where: {
|
|
type: 'burner',
|
|
expiresAt: { [Sequelize.Op.lt]: new Date() },
|
|
},
|
|
});
|
|
}, 60 * 60 * 1000);
|
|
|
|
// Socket.io with JWT
|
|
io.use((socket, next) => {
|
|
const token = socket.handshake.auth.token;
|
|
if (!token) {
|
|
return next(new Error('Authentication error'));
|
|
}
|
|
try {
|
|
console.log('Verifying Socket.io token with JWT_SECRET:', JWT_SECRET);
|
|
const decoded = jwt.verify(token, JWT_SECRET);
|
|
socket.userId = decoded.userId;
|
|
next();
|
|
} catch (e) {
|
|
console.log('Socket.io auth error:', e.message);
|
|
next(new Error('Authentication error'));
|
|
}
|
|
});
|
|
|
|
io.on('connection', (socket) => {
|
|
console.log('User connected:', socket.userId);
|
|
|
|
socket.on('join', (data) => {
|
|
socket.join(data.userId);
|
|
socket.broadcast.emit('user-joined', { userId: data.userId });
|
|
});
|
|
|
|
socket.on('join-room', (data) => {
|
|
const { roomId } = data;
|
|
socket.join(roomId);
|
|
console.log(`User ${socket.userId} joined room ${roomId}`);
|
|
});
|
|
|
|
socket.on('join-meeting', (data) => {
|
|
const { meetingId } = data;
|
|
socket.join(meetingId);
|
|
console.log(`User ${socket.userId} joined meeting ${meetingId}`);
|
|
});
|
|
|
|
socket.on('leave-room', (data) => {
|
|
const { roomId } = data;
|
|
socket.leave(roomId);
|
|
console.log(`User ${socket.userId} left room ${roomId}`);
|
|
});
|
|
|
|
socket.on('leave-meeting', (data) => {
|
|
const { meetingId } = data;
|
|
socket.leave(meetingId);
|
|
console.log(`User ${socket.userId} left meeting ${meetingId}`);
|
|
});
|
|
|
|
socket.on('offer', (data) => {
|
|
socket.to(data.to).emit('offer', {
|
|
sdp: data.sdp,
|
|
type: data.type,
|
|
from: data.from,
|
|
});
|
|
});
|
|
|
|
socket.on('answer', (data) => {
|
|
socket.to(data.to).emit('answer', { sdp: data.sdp, type: data.type });
|
|
});
|
|
|
|
socket.on('ice-candidate', (data) => {
|
|
socket.to(data.to).emit('ice-candidate', { candidate: data.candidate });
|
|
});
|
|
|
|
socket.on('chat-message', async (data) => {
|
|
const { roomId } = data;
|
|
const message = await Message.create({
|
|
senderId: data.senderId,
|
|
receiverId: data.senderId,
|
|
roomId: data.roomId,
|
|
senderNickname: data.senderNickname,
|
|
message: data.message,
|
|
timestamp: data.timestamp,
|
|
avatarUrl: data.avatarUrl,
|
|
fileUrl: data.fileUrl,
|
|
fileName: data.fileName,
|
|
});
|
|
|
|
io.to(roomId).emit('chat-message', {
|
|
id: message.id,
|
|
senderId: data.senderId,
|
|
senderNickname: data.senderNickname,
|
|
message: data.message,
|
|
timestamp: data.timestamp,
|
|
roomId: data.roomId,
|
|
avatarUrl: data.avatarUrl,
|
|
fileUrl: data.fileUrl,
|
|
fileName: data.fileName,
|
|
reactions: message.reactions || {},
|
|
});
|
|
});
|
|
|
|
socket.on('disconnect', () => {
|
|
console.log('User disconnected:', socket.userId);
|
|
});
|
|
});
|
|
|
|
http.listen(3000, () => {
|
|
console.log('Server running on port 3000');
|
|
}); |