-
Notifications
You must be signed in to change notification settings - Fork 1
Technical
This page provides an in-depth technical overview of LinkUp's architecture, design decisions, and implementation details.
LinkUp follows a modern client-server architecture with real-time communication capabilities.
┌─────────────────────────────────────────────────────────┐
│ Client Browser │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ React UI │ │ WebRTC │ │ WebSocket │ │
│ │ Components │ │ P2P Video │ │ Signaling │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ FastAPI Backend │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ REST API │ │ WebSocket │ │ Database │ │
│ │ Endpoints │ │ Server │ │ Layer │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌──────────────────┐
│ PostgreSQL │
│ Database │
└──────────────────┘
- FastAPI - Modern, fast web framework for Python
- SQLAlchemy - Async ORM for database operations
- PostgreSQL - Relational database
- WebSocket - Real-time bidirectional communication
- Pydantic - Data validation using Python type hints
- Alembic - Database migration tool
The entry point that configures:
- CORS middleware for cross-origin requests
- WebSocket connections for real-time signaling
- REST API endpoints for meeting management
- In-memory storage for active connections and chat messages
Key Features:
# Active WebSocket connections
active_connections: Dict[str, Dict[str, WebSocket]] = {}
# In-memory chat storage
chat_messages: Dict[str, List[Dict]] = {}Three main models:
Meeting Model:
- id: Unique identifier
- code: 10-character meeting code
- title: Optional meeting title
- created_at: Timestamp
- is_active: Boolean flag
- max_participants: Integer (default 10)
- settings: JSON field for additional settingsParticipant Model:
- id: Unique identifier
- meeting_id: Foreign key to Meeting
- client_id: WebSocket client identifier
- display_name: User's display name
- audio_enabled: Boolean
- video_enabled: Boolean
- screen_sharing: Boolean
- is_host: First participant becomes host
- joined_at / left_at: TimestampsMeetingLog Model:
- id: Unique identifier
- meeting_id: Foreign key to Meeting
- participant_id: Foreign key to Participant
- event_type: join, leave, audio_toggle, video_toggle, etc.
- event_data: JSON field for event details
- timestamp: When the event occurredPydantic models for request/response validation:
-
MeetingCreate- Create meeting request -
MeetingResponse- Meeting details with participant count -
ParticipantJoin- Join meeting request -
ParticipantResponse- Participant details -
ChatMessage- Chat message structure -
WSMessageType- WebSocket message type constants
class WSMessageType:
# Signaling
JOIN = "join"
LEAVE = "leave"
OFFER = "offer"
ANSWER = "answer"
ICE_CANDIDATE = "ice-candidate"
# Meeting updates
USER_JOINED = "user-joined"
USER_LEFT = "user-left"
PARTICIPANTS_UPDATE = "participants-update"
# Media controls
AUDIO_TOGGLE = "audio-toggle"
VIDEO_TOGGLE = "video-toggle"
SCREEN_SHARE_START = "screen-share-start"
SCREEN_SHARE_STOP = "screen-share-stop"
# Chat
CHAT_MESSAGE = "chat-message"
CHAT_HISTORY = "chat-history"POST /api/meetings
- Creates a new meeting
- Generates unique 10-character code
- Returns meeting details
- Initializes chat storage
GET /api/meetings/{meeting_code}
- Retrieves meeting details
- Includes active participant count
- Returns 404 if meeting not found
GET /api/meetings/{meeting_code}/participants
- Lists all active participants in a meeting
- Includes their audio/video status
WS /ws/{meeting_code}/{client_id}
- Accepts WebSocket connection
- Handles real-time signaling
- Manages participant lifecycle
- Broadcasts events to other participants
-
Connection Establishment
- Client connects with meeting code and client ID
- Server validates meeting exists
- Determines if participant should be host (first to join)
- Stores connection in
active_connections
-
Participant Join
- Creates participant record in database
- Sends chat history to new participant
- Broadcasts
user-joinedto existing participants - Sends current participants list to new user
-
WebRTC Signaling
- Forwards offers, answers, and ICE candidates between peers
- Server acts as signaling server (doesn't handle media)
- All video/audio data flows peer-to-peer
-
Media Controls
- Updates participant status in database
- Broadcasts changes to all participants
- Logs events for audit trail
-
Disconnect Handling
- Marks participant as inactive
- Logs leave event
- Broadcasts
user-leftto remaining participants - Cleans up connection from memory
- React 19 - Latest version with improved performance
- TypeScript - Type-safe JavaScript
- Vite - Fast build tool and dev server
- Tailwind CSS - Utility-first CSS framework
- WebRTC - Peer-to-peer video/audio communication
- Axios - HTTP client for API calls
Home (Home.tsx)
- Landing page with create/join options
- Meeting code input and validation
- Name input for joining meetings
JoinMeeting (JoinMeeting.tsx)
- Pre-meeting screen
- Device permission requests
- Name entry before joining
MeetingRoom (MeetingRoom.tsx)
- Main meeting interface
- Manages WebRTC connections
- Handles WebSocket communication
- Coordinates all UI components
VideoGrid (VideoGrid.tsx)
- Displays all participant videos
- Handles local video mirroring
- Manages pinned participant view
- Responsive grid layout (1-10 participants)
MeetingControls (MeetingControls.tsx)
- Audio/video toggle buttons
- Screen share control
- Chat and participants toggles
- Leave meeting button
ChatPanel (ChatPanel.tsx)
- Sliding panel for messages
- Real-time message display
- Message input and sending
- Unread message indicators
ParticipantsList (ParticipantsList.tsx)
- List of all participants
- Shows audio/video status
- Pin/unpin functionality
- Participant count display
useWebRTC (useWebRTC.ts)
Manages all WebRTC peer connections:
interface UseWebRTCReturn {
localStream: MediaStream | null;
remoteStreams: Map<string, MediaStream>;
isScreenSharing: boolean;
initializeLocalStream(): Promise<boolean>;
createOffer(clientId: string): Promise<RTCSessionDescription>;
handleOffer(clientId: string, offer: RTCSessionDescription): Promise<RTCSessionDescription>;
handleAnswer(clientId: string, answer: RTCSessionDescription): Promise<void>;
handleIceCandidate(clientId: string, candidate: RTCIceCandidate): Promise<void>;
toggleAudio(enabled: boolean): void;
toggleVideo(enabled: boolean): void;
startScreenShare(): Promise<boolean>;
stopScreenShare(): Promise<void>;
removePeer(clientId: string): void;
cleanup(): void;
}Key Features:
- Maintains map of peer connections
- Handles ICE candidate gathering
- Manages local stream (camera/mic)
- Screen sharing with track replacement
- Automatic cleanup on disconnect
useWebSocket (useWebSocket.ts)
Manages WebSocket connection:
interface UseWebSocketReturn {
sendMessage: (message: any) => void;
isConnected: boolean;
lastMessage: WSMessage | null;
error: Error | null;
}Key Features:
- Auto-reconnection on disconnect
- Message queue for reliability
- Connection state management
- Error handling
Peer A (Offerer) Peer B (Answerer)
│ │
│ 1. Create Offer │
├────────────────────────────────────▶
│ │
│ 2. Send Offer via WebSocket │
│ (through server) │
│ │
│ │ 3. Receive Offer
│ │ 4. Create Answer
│ │
◀────────────────────────────────────┤
│ 5. Receive Answer via WebSocket │
│ │
│ 6. Exchange ICE Candidates │
├───────────────────────────────────▶
◀───────────────────────────────────┤
│ │
│ 7. P2P Connection Established │
│═══════════════════════════════════▶
│ (Video/Audio flows directly) │
const RTC_CONFIG: RTCConfiguration = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' },
],
};Uses public STUN servers for NAT traversal. For production, consider adding TURN servers.
LinkUp uses React hooks and local state:
-
useStatefor component state -
useReffor mutable values (WebSocket, peer connections) -
useCallbackfor memoized functions -
useEffectfor side effects and cleanup
No global state management library needed due to:
- Clear component hierarchy
- Props drilling is minimal
- WebSocket/WebRTC state isolated in hooks
See the Database Schema page for detailed information.
- CORS Protection - Configured allowed origins
- Meeting Code Generation - Random 10-character codes
- IP Logging - Tracks participant IPs for audit
- No Authentication - By design for simplicity
- HTTPS/WSS - Use secure protocols in production
- Rate Limiting - Prevent abuse of meeting creation
- TURN Servers - Better connectivity for restricted networks
- Meeting Passwords - Optional password protection
- Meeting Expiration - Auto-expire old meetings
- User Authentication - For enterprise deployments
- Async Operations - All I/O is non-blocking
- Connection Pooling - Database connection pooling
- In-Memory Storage - Active connections and chat in memory
- Efficient Broadcasting - Direct WebSocket writes
- Code Splitting - Lazy load routes
- Memoization - React.memo for components
- Debouncing - Input handlers debounced
- WebRTC Optimizations - Proper track management
- Single server instance
- In-memory connection storage
- No horizontal scaling
- Redis for Shared State - Move active connections to Redis
- Load Balancer - Sticky sessions for WebSocket
- Separate Signaling Server - Dedicated WebSocket servers
- Media Server (SFU) - For larger meetings (>10 participants)
pytest backend/tests/Test coverage:
- API endpoints
- WebSocket connections
- Database operations
- Message broadcasting
npm run testTest coverage:
- Component rendering
- User interactions
- WebRTC hooks
- WebSocket hooks
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)Logs include:
- Connection events
- Meeting creation/joins
- Errors and exceptions
- WebSocket messages (debug mode)
Console logs for:
- WebRTC connection states
- WebSocket events
- Stream changes
- Error conditions
Next: Check out the API Reference for detailed endpoint documentation.