Home   system-design  

System design for Google Meet system

Designing a system like Google Meet involves creating a real-time video conferencing platform that supports multi-party video calls, screen sharing, and collaborative features. Here's a detailed system design using Prisma for database modeling, Express.js for API routes, and a recommended tech stack:

Tech Stack

System Components

  1. Client Applications (Web and Mobile):

    • Interfaces for users to create meetings, join calls, share screens, chat, and manage settings.
  2. Express.js Backend

    • Prisma Models:

      // schema.prisma
      
      datasource db {
        provider = "postgresql"
        url      = env("DATABASE_URL")
      }
      
      generator client {
        provider = "prisma-client-js"
      }
      
      model User {
        id          Int      @id @default(autoincrement())
        username    String   @unique
        email       String   @unique
        password    String
        meetings    Meeting[]
        createdAt   DateTime @default(now())
      }
      
      model Meeting {
        id            Int       @id @default(autoincrement())
        title         String
        startTime     DateTime
        endTime       DateTime
        organizerId   Int
        organizer     User      @relation(fields: [organizerId], references: [id])
        participants  User[]    @relation("Participants", references: [id])
        messages      Message[]
        createdAt     DateTime  @default(now())
      }
      
      model Message {
        id          Int      @id @default(autoincrement())
        content     String
        senderId    Int
        sender      User     @relation(fields: [senderId], references: [id])
        meetingId   Int
        meeting     Meeting  @relation(fields: [meetingId], references: [id])
        createdAt   DateTime @default(now())
      }
      
    • Express API Routes:

      // server.js
      
      const express = require('express');
      const http = require('http');
      const { Server } = require('socket.io');
      const { PrismaClient } = require('@prisma/client');
      const prisma = new PrismaClient();
      const jwt = require('jsonwebtoken');
      const bcrypt = require('bcrypt');
      const { v4: uuidv4 } = require('uuid');
      
      const app = express();
      const server = http.createServer(app);
      const io = new Server(server);
      
      app.use(express.json());
      
      // Socket.io middleware for authentication
      io.use((socket, next) => {
        const token = socket.handshake.auth.token;
        try {
          const decoded = jwt.verify(token, 'secret');
          socket.userId = decoded.userId;
          next();
        } catch (error) {
          return next(new Error('Authentication error'));
        }
      });
      
      // Endpoint for user registration
      app.post('/register', async (req, res) => {
        const { username, email, password } = req.body;
        try {
          const hashedPassword = await bcrypt.hash(password, 10);
          const user = await prisma.user.create({
            data: {
              username,
              email,
              password: hashedPassword,
            },
          });
          res.json(user);
        } catch (error) {
          console.error(error);
          res.status(500).json({ error: 'Failed to register user' });
        }
      });
      
      // Endpoint for user login and JWT generation
      app.post('/login', async (req, res) => {
        const { username, password } = req.body;
        try {
          const user = await prisma.user.findUnique({ where: { username } });
          if (!user) {
            return res.status(404).json({ error: 'User not found' });
          }
          const passwordMatch = await bcrypt.compare(password, user.password);
          if (!passwordMatch) {
            return res.status(401).json({ error: 'Invalid password' });
          }
          const token = jwt.sign({ userId: user.id }, 'secret', { expiresIn: '1h' });
          res.json({ token });
        } catch (error) {
          console.error(error);
          res.status(500).json({ error: 'Login failed' });
        }
      });
      
      // Endpoint for creating a new meeting
      app.post('/meetings', async (req, res) => {
        const { title, startTime, endTime } = req.body;
        try {
          const user = await prisma.user.findUnique({ where: { id: req.userId } });
          if (!user) {
            return res.status(404).json({ error: 'User not found' });
          }
          const meeting = await prisma.meeting.create({
            data: {
              title,
              startTime,
              endTime,
              organizerId: user.id,
              participants: { connect: { id: user.id } },
              messages: {
                create: {
                  content: `${user.username} created the meeting`,
                  senderId: user.id,
                },
              },
            },
          });
          res.json(meeting);
        } catch (error) {
          console.error(error);
          res.status(500).json({ error: 'Failed to create meeting' });
        }
      });
      
      // WebSocket event handling for real-time communication
      io.on('connection', (socket) => {
        console.log(`User ${socket.userId} connected`);
        socket.on('send-message', async ({ meetingId, content }) => {
          try {
            const user = await prisma.user.findUnique({ where: { id: socket.userId } });
            if (!user) {
              throw new Error('User not found');
            }
            const message = await prisma.message.create({
              data: {
                content,
                senderId: socket.userId,
                meetingId,
              },
            });
            socket.broadcast.emit('receive-message', { meetingId, message });
          } catch (error) {
            console.error('Error sending message:', error);
          }
        });
      
        socket.on('disconnect', () => {
          console.log(`User ${socket.userId} disconnected`);
        });
      });
      
      const PORT = process.env.PORT || 3000;
      server.listen(PORT, () => {
        console.log(`Server is running on http://localhost:${PORT}`);
      });
      
  3. Database Layer

    • PostgreSQL: Stores user data, meeting details, chat messages, and transactional information.
    • Redis: Used for caching user sessions and improving performance of real-time communication.
  4. Real-time Communication

    • WebRTC: Enables real-time audio and video communication between participants in meetings, with peer-to-peer data channels.
  5. Authentication and Authorization

    • JWT: Token-based authentication for securing API endpoints and managing user sessions securely.
  6. Messaging

    • RabbitMQ: Used for handling asynchronous tasks such as sending notifications, managing meeting schedules, and updating participant lists.
  7. Storage

    • Amazon S3: Stores chat messages, media files (images, documents), and meeting recordings securely, with scalable storage and retrieval capabilities.
  8. Monitoring and Analytics

    • Prometheus and Grafana: Monitor system metrics, track performance, and troubleshoot issues proactively.

Why This Tech Stack?

Scalability and Fault Tolerance

Published on: Jul 10, 2024, 01:32 AM  
 

Comments

Add your comment