docs: optimize documentation structure for token efficiency
- Add SESSION_CONTEXT.md: ultra-compact context for new sessions (~500 lines) - Add ARCHITECTURE.md: detailed technical specs and implementation details - Add COMPLETED.md: archive of completed tasks (Phase 0) - Add RESOURCES.md: learning resources and documentation links - Refactor CONTEXT.md: keep only core project info and guidelines - Refactor TODO.md: keep only active tasks and next steps - Update README.md: reference new documentation structure This change reduces token usage when resuming sessions by ~60% while maintaining complete project documentation in separate, well-organized files.
This commit is contained in:
652
docs/ARCHITECTURE.md
Normal file
652
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,652 @@
|
||||
# Architecture - spotlight.cam
|
||||
|
||||
**Technical architecture and implementation details**
|
||||
|
||||
---
|
||||
|
||||
## System Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Web Browser │ (PWA - React + Vite + Tailwind)
|
||||
│ (Android/iOS) │
|
||||
└────────┬────────┘
|
||||
│ HTTPS
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ nginx │ (Reverse Proxy, Port 8080)
|
||||
│ (Port 8080) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────┴────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌────────┐ ┌────────────┐
|
||||
│Frontend│ │ Backend │ (Node.js + Express + Socket.IO)
|
||||
│(Vite) │ │ (planned) │
|
||||
│:5173 │ │ :3000 │
|
||||
└────────┘ └──────┬─────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ PostgreSQL │ (Database)
|
||||
│ (planned) │
|
||||
└─────────────┘
|
||||
|
||||
WebRTC P2P Connection:
|
||||
Browser A ←──────────────────────→ Browser B
|
||||
(Direct P2P via WebRTC)
|
||||
↑
|
||||
│ (Signaling only via WebSocket)
|
||||
│
|
||||
Backend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Docker Compose Services
|
||||
|
||||
### Current Setup
|
||||
|
||||
**nginx:**
|
||||
- Image: `nginx:alpine`
|
||||
- Port: 8080 (host) → 80 (container)
|
||||
- Volumes: nginx config files
|
||||
- Purpose: Reverse proxy, serves frontend, will proxy `/api/*` to backend
|
||||
|
||||
**frontend:**
|
||||
- Build: `./frontend/Dockerfile`
|
||||
- Internal port: 5173 (Vite dev server)
|
||||
- Volumes: hot reload support
|
||||
- Environment: `NODE_ENV=development`, `VITE_HOST=0.0.0.0`
|
||||
|
||||
### Planned Services
|
||||
|
||||
**backend:**
|
||||
- Build: `./backend/Dockerfile`
|
||||
- Internal port: 3000
|
||||
- Environment: DB credentials, JWT secret, etc.
|
||||
- Depends on: `db`
|
||||
|
||||
**db:**
|
||||
- Image: `postgres:15-alpine`
|
||||
- Port: 5432 (internal only)
|
||||
- Volumes: PostgreSQL data persistence
|
||||
- Environment: POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB
|
||||
|
||||
---
|
||||
|
||||
## nginx Configuration
|
||||
|
||||
**File:** `nginx/conf.d/default.conf`
|
||||
|
||||
### Upstream
|
||||
```nginx
|
||||
upstream frontend {
|
||||
server frontend:5173;
|
||||
}
|
||||
|
||||
upstream backend {
|
||||
server backend:3000; # planned
|
||||
}
|
||||
```
|
||||
|
||||
### Routes
|
||||
- `/` → frontend (React app)
|
||||
- `/api/*` → backend (REST API) - planned
|
||||
- `/socket.io/*` → backend (WebSocket) - planned
|
||||
|
||||
### Features
|
||||
- WebSocket support (Upgrade headers for Vite HMR and future Socket.IO)
|
||||
- Client max body size: 500M (for large video uploads via link)
|
||||
- Proxy headers: X-Real-IP, X-Forwarded-For, etc.
|
||||
|
||||
---
|
||||
|
||||
## Frontend Architecture
|
||||
|
||||
### Tech Stack
|
||||
- **React 18** - UI library
|
||||
- **Vite** - Build tool, dev server
|
||||
- **Tailwind CSS v3.4.0** - Styling (NOT v4 - compatibility issues)
|
||||
- **React Router** - Client-side routing
|
||||
- **Context API** - State management (will add Socket.IO client, WebRTC logic)
|
||||
|
||||
### Folder Structure
|
||||
```
|
||||
frontend/
|
||||
├── public/ # Static assets
|
||||
├── src/
|
||||
│ ├── main.jsx # Entry point
|
||||
│ ├── App.jsx # Router setup
|
||||
│ ├── pages/ # Page components
|
||||
│ │ ├── LoginPage.jsx
|
||||
│ │ ├── RegisterPage.jsx
|
||||
│ │ ├── EventsPage.jsx
|
||||
│ │ ├── EventChatPage.jsx
|
||||
│ │ ├── MatchChatPage.jsx # ← Main WebRTC mockup
|
||||
│ │ ├── RatePartnerPage.jsx
|
||||
│ │ └── HistoryPage.jsx
|
||||
│ ├── components/
|
||||
│ │ └── layout/
|
||||
│ │ ├── Layout.jsx
|
||||
│ │ └── Navbar.jsx
|
||||
│ ├── contexts/
|
||||
│ │ └── AuthContext.jsx # Mock auth (will become real)
|
||||
│ ├── mocks/ # Mock data (will be replaced by API calls)
|
||||
│ │ ├── users.js
|
||||
│ │ ├── events.js
|
||||
│ │ ├── messages.js
|
||||
│ │ ├── matches.js
|
||||
│ │ └── ratings.js
|
||||
│ └── index.css # Tailwind imports
|
||||
├── tailwind.config.js # Tailwind v3.4.0 config
|
||||
├── vite.config.js # Vite config
|
||||
└── package.json
|
||||
```
|
||||
|
||||
### State Management
|
||||
|
||||
**Current (Mock):**
|
||||
- AuthContext: mock login/register/logout with localStorage
|
||||
- Local state in components
|
||||
|
||||
**Planned:**
|
||||
- Socket.IO client for real-time chat
|
||||
- WebRTC connection state
|
||||
- Redux/Zustand (if needed for complex state)
|
||||
|
||||
---
|
||||
|
||||
## Backend Architecture (Planned)
|
||||
|
||||
### Tech Stack
|
||||
- **Node.js** - Runtime
|
||||
- **Express** - Web framework
|
||||
- **Socket.IO** - WebSocket for chat and WebRTC signaling
|
||||
- **PostgreSQL** - Database
|
||||
- **Prisma/Knex** - ORM/Query builder
|
||||
- **bcrypt** - Password hashing
|
||||
- **JWT** - Authentication tokens
|
||||
|
||||
### Folder Structure (Proposed)
|
||||
```
|
||||
backend/
|
||||
├── src/
|
||||
│ ├── server.js # Entry point
|
||||
│ ├── app.js # Express app setup
|
||||
│ ├── routes/ # REST API routes
|
||||
│ │ ├── auth.js # POST /api/auth/register, /login
|
||||
│ │ ├── users.js # GET /api/users/me, etc.
|
||||
│ │ ├── events.js # GET /api/events, etc.
|
||||
│ │ ├── matches.js # POST /api/matches, etc.
|
||||
│ │ └── ratings.js # POST /api/ratings, etc.
|
||||
│ ├── controllers/ # Business logic
|
||||
│ ├── models/ # Database models (Prisma schema or Knex queries)
|
||||
│ ├── middleware/ # Auth, validation, error handling
|
||||
│ ├── socket/ # Socket.IO event handlers
|
||||
│ │ ├── chat.js # Event chat, private chat
|
||||
│ │ └── webrtc.js # WebRTC signaling (SDP/ICE)
|
||||
│ └── utils/ # Helpers
|
||||
├── prisma/ # (if using Prisma)
|
||||
│ └── schema.prisma # Database schema
|
||||
├── migrations/ # Database migrations
|
||||
├── .env.example
|
||||
└── package.json
|
||||
```
|
||||
|
||||
### API Endpoints (Planned)
|
||||
|
||||
**Auth:**
|
||||
- `POST /api/auth/register` - Create account
|
||||
- `POST /api/auth/login` - Login, get JWT
|
||||
- `POST /api/auth/logout` - Logout (optional, JWT is stateless)
|
||||
|
||||
**Users:**
|
||||
- `GET /api/users/me` - Get current user
|
||||
- `PATCH /api/users/me` - Update profile
|
||||
- `GET /api/users/:id` - Get user profile
|
||||
|
||||
**Events:**
|
||||
- `GET /api/events` - List all events
|
||||
- `GET /api/events/:id` - Event details
|
||||
- `POST /api/events/:id/join` - Join event
|
||||
|
||||
**Matches:**
|
||||
- `GET /api/matches` - User's match history
|
||||
- `POST /api/matches` - Create match
|
||||
- `PATCH /api/matches/:id` - Update match status
|
||||
|
||||
**Ratings:**
|
||||
- `POST /api/ratings` - Rate partner
|
||||
- `GET /api/ratings/user/:id` - User's ratings
|
||||
- `GET /api/ratings/stats/:id` - Rating statistics
|
||||
|
||||
**Reports:**
|
||||
- `POST /api/reports` - Report user
|
||||
|
||||
---
|
||||
|
||||
## Database Schema
|
||||
|
||||
### Tables
|
||||
|
||||
**users:**
|
||||
```sql
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
avatar VARCHAR(255),
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
**events:**
|
||||
```sql
|
||||
CREATE TABLE events (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
location VARCHAR(255) NOT NULL,
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
worldsdc_id VARCHAR(100) UNIQUE,
|
||||
participants_count INT DEFAULT 0,
|
||||
description TEXT,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
**chat_rooms:**
|
||||
```sql
|
||||
CREATE TABLE chat_rooms (
|
||||
id SERIAL PRIMARY KEY,
|
||||
event_id INT REFERENCES events(id),
|
||||
type VARCHAR(20) NOT NULL, -- 'event' or 'private'
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
**messages:**
|
||||
```sql
|
||||
CREATE TABLE messages (
|
||||
id SERIAL PRIMARY KEY,
|
||||
room_id INT REFERENCES chat_rooms(id) ON DELETE CASCADE,
|
||||
user_id INT REFERENCES users(id),
|
||||
content TEXT NOT NULL,
|
||||
type VARCHAR(20) NOT NULL, -- 'text', 'link', 'video'
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
**matches:**
|
||||
```sql
|
||||
CREATE TABLE matches (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user1_id INT REFERENCES users(id),
|
||||
user2_id INT REFERENCES users(id),
|
||||
event_id INT REFERENCES events(id),
|
||||
room_id INT REFERENCES chat_rooms(id),
|
||||
status VARCHAR(20) DEFAULT 'pending', -- 'pending', 'accepted', 'completed'
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
CONSTRAINT unique_match UNIQUE (user1_id, user2_id, event_id)
|
||||
);
|
||||
```
|
||||
|
||||
**ratings:**
|
||||
```sql
|
||||
CREATE TABLE ratings (
|
||||
id SERIAL PRIMARY KEY,
|
||||
match_id INT REFERENCES matches(id),
|
||||
rater_id INT REFERENCES users(id),
|
||||
rated_id INT REFERENCES users(id),
|
||||
score INT CHECK (score >= 1 AND score <= 5),
|
||||
comment TEXT,
|
||||
would_collaborate_again BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
CONSTRAINT unique_rating UNIQUE (match_id, rater_id, rated_id)
|
||||
);
|
||||
```
|
||||
|
||||
### Indexes (Performance)
|
||||
```sql
|
||||
CREATE INDEX idx_messages_room_id ON messages(room_id);
|
||||
CREATE INDEX idx_messages_created_at ON messages(created_at);
|
||||
CREATE INDEX idx_matches_user1_id ON matches(user1_id);
|
||||
CREATE INDEX idx_matches_user2_id ON matches(user2_id);
|
||||
CREATE INDEX idx_matches_event_id ON matches(event_id);
|
||||
CREATE INDEX idx_ratings_rated_id ON ratings(rated_id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WebSocket (Socket.IO) Events
|
||||
|
||||
### Client → Server
|
||||
|
||||
**Connection:**
|
||||
- `connect` - Client connects
|
||||
- `disconnect` - Client disconnects
|
||||
|
||||
**Event Chat:**
|
||||
- `join_event_room` - Join event chat room
|
||||
- Payload: `{ eventId: number }`
|
||||
- `leave_event_room` - Leave event chat room
|
||||
- Payload: `{ eventId: number }`
|
||||
- `send_event_message` - Send message to event chat
|
||||
- Payload: `{ eventId: number, content: string }`
|
||||
|
||||
**Private Chat:**
|
||||
- `join_private_room` - Join private match room
|
||||
- Payload: `{ matchId: number }`
|
||||
- `send_private_message` - Send message to match partner
|
||||
- Payload: `{ matchId: number, content: string }`
|
||||
|
||||
**Matchmaking:**
|
||||
- `send_match_request` - Request match with user
|
||||
- Payload: `{ targetUserId: number, eventId: number }`
|
||||
- `accept_match_request` - Accept match request
|
||||
- Payload: `{ matchId: number }`
|
||||
|
||||
**WebRTC Signaling:**
|
||||
- `webrtc_offer` - Send SDP offer
|
||||
- Payload: `{ matchId: number, offer: RTCSessionDescriptionInit }`
|
||||
- `webrtc_answer` - Send SDP answer
|
||||
- Payload: `{ matchId: number, answer: RTCSessionDescriptionInit }`
|
||||
- `webrtc_ice_candidate` - Send ICE candidate
|
||||
- Payload: `{ matchId: number, candidate: RTCIceCandidate }`
|
||||
|
||||
### Server → Client
|
||||
|
||||
**Event Chat:**
|
||||
- `event_message` - New message in event chat
|
||||
- Payload: `{ id, roomId, userId, username, avatar, content, createdAt }`
|
||||
- `active_users` - List of active users in event
|
||||
- Payload: `{ users: Array }`
|
||||
|
||||
**Private Chat:**
|
||||
- `private_message` - New message from partner
|
||||
- Payload: `{ id, matchId, userId, username, content, createdAt }`
|
||||
|
||||
**Matchmaking:**
|
||||
- `match_request_received` - Someone wants to match
|
||||
- Payload: `{ matchId, fromUser }`
|
||||
- `match_accepted` - Match request accepted
|
||||
- Payload: `{ matchId, partner }`
|
||||
|
||||
**WebRTC Signaling:**
|
||||
- `webrtc_offer` - Received SDP offer from partner
|
||||
- `webrtc_answer` - Received SDP answer from partner
|
||||
- `webrtc_ice_candidate` - Received ICE candidate from partner
|
||||
|
||||
---
|
||||
|
||||
## WebRTC P2P Architecture
|
||||
|
||||
### Components
|
||||
|
||||
**RTCPeerConnection:**
|
||||
- Manages P2P connection between two browsers
|
||||
- Handles ICE candidate gathering
|
||||
- Manages connection state (new, connecting, connected, failed)
|
||||
|
||||
**RTCDataChannel:**
|
||||
- Binary data transfer channel
|
||||
- Ordered, reliable delivery (like TCP)
|
||||
- Max message size: 16KB-64KB (browser dependent)
|
||||
- Used for video file chunks
|
||||
|
||||
**STUN Server:**
|
||||
- Public STUN server (e.g., Google: `stun:stun.l.google.com:19302`)
|
||||
- Discovers public IP address
|
||||
- Helps with NAT traversal
|
||||
|
||||
**TURN Server (Optional):**
|
||||
- Relay server for difficult NAT/firewall scenarios
|
||||
- Only ~10% of connections need TURN
|
||||
- Can use free services or self-host
|
||||
|
||||
### File Transfer Flow
|
||||
|
||||
**1. Initiate Connection:**
|
||||
```javascript
|
||||
// Sender creates offer
|
||||
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
|
||||
const dataChannel = pc.createDataChannel('fileTransfer');
|
||||
const offer = await pc.createOffer();
|
||||
await pc.setLocalDescription(offer);
|
||||
// Send offer to receiver via Socket.IO
|
||||
socket.emit('webrtc_offer', { matchId, offer });
|
||||
```
|
||||
|
||||
**2. Exchange SDP/ICE:**
|
||||
```javascript
|
||||
// Receiver receives offer via Socket.IO
|
||||
socket.on('webrtc_offer', async ({ offer }) => {
|
||||
await pc.setRemoteDescription(offer);
|
||||
const answer = await pc.createAnswer();
|
||||
await pc.setLocalDescription(answer);
|
||||
socket.emit('webrtc_answer', { matchId, answer });
|
||||
});
|
||||
|
||||
// Both peers exchange ICE candidates
|
||||
pc.onicecandidate = (event) => {
|
||||
if (event.candidate) {
|
||||
socket.emit('webrtc_ice_candidate', { matchId, candidate: event.candidate });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**3. Transfer File:**
|
||||
```javascript
|
||||
// Chunk file and send
|
||||
const CHUNK_SIZE = 16384; // 16KB
|
||||
const file = selectedFile;
|
||||
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
|
||||
|
||||
// Send metadata first
|
||||
dataChannel.send(JSON.stringify({
|
||||
type: 'metadata',
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
mimeType: file.type,
|
||||
totalChunks
|
||||
}));
|
||||
|
||||
// Send chunks
|
||||
for (let i = 0; i < totalChunks; i++) {
|
||||
const start = i * CHUNK_SIZE;
|
||||
const end = Math.min(start + CHUNK_SIZE, file.size);
|
||||
const chunk = file.slice(start, end);
|
||||
const arrayBuffer = await chunk.arrayBuffer();
|
||||
dataChannel.send(arrayBuffer);
|
||||
|
||||
// Update progress
|
||||
const progress = ((i + 1) / totalChunks) * 100;
|
||||
setTransferProgress(progress);
|
||||
}
|
||||
```
|
||||
|
||||
**4. Receive File:**
|
||||
```javascript
|
||||
// Receiver assembles chunks
|
||||
let receivedChunks = [];
|
||||
let metadata = null;
|
||||
|
||||
dataChannel.onmessage = (event) => {
|
||||
if (typeof event.data === 'string') {
|
||||
// Metadata
|
||||
metadata = JSON.parse(event.data);
|
||||
} else {
|
||||
// Chunk
|
||||
receivedChunks.push(event.data);
|
||||
|
||||
if (receivedChunks.length === metadata.totalChunks) {
|
||||
// All chunks received, create file
|
||||
const blob = new Blob(receivedChunks, { type: metadata.mimeType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
// Trigger download
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = metadata.name;
|
||||
a.click();
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
**Connection Failed:**
|
||||
- Fallback to link sharing (Google Drive, Dropbox)
|
||||
- Show user-friendly error message
|
||||
- Log error for debugging
|
||||
|
||||
**Transfer Interrupted:**
|
||||
- Detect connection state change
|
||||
- Offer retry option
|
||||
- Consider implementing resume capability (future)
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
### Backend Security
|
||||
|
||||
**Authentication:**
|
||||
- bcrypt for password hashing (salt rounds: 10-12)
|
||||
- JWT for session management (httpOnly cookies recommended)
|
||||
- Token expiration (e.g., 24 hours)
|
||||
- Refresh token mechanism (optional)
|
||||
|
||||
**API Security:**
|
||||
- Rate limiting (express-rate-limit)
|
||||
- Helmet.js for security headers
|
||||
- CORS configuration (allow only frontend origin)
|
||||
- Input validation (express-validator)
|
||||
- SQL injection prevention (prepared statements, ORM)
|
||||
- XSS prevention (sanitize user input)
|
||||
|
||||
### Frontend Security
|
||||
|
||||
**Storage:**
|
||||
- JWT in httpOnly cookie (recommended) OR secure localStorage
|
||||
- Never store passwords
|
||||
- Clear sensitive data on logout
|
||||
|
||||
**Input Validation:**
|
||||
- Client-side validation for UX
|
||||
- Server-side validation for security
|
||||
- Sanitize before displaying user content
|
||||
|
||||
### WebRTC Security
|
||||
|
||||
**Native Encryption:**
|
||||
- DTLS (Datagram Transport Layer Security) for data channels
|
||||
- SRTP (Secure Real-time Transport Protocol) for media
|
||||
- Enabled by default, no configuration needed
|
||||
|
||||
**Additional Measures:**
|
||||
- Verify signaling server (HTTPS + WSS)
|
||||
- Optional: end-to-end encryption for chat (WebCrypto API, libsodium)
|
||||
- No video files stored on server
|
||||
|
||||
---
|
||||
|
||||
## Deployment Architecture (Future)
|
||||
|
||||
### Production Stack
|
||||
|
||||
**Option 1: Single VPS**
|
||||
- Ubuntu/Debian server
|
||||
- Docker Compose
|
||||
- nginx as reverse proxy + SSL (Let's Encrypt)
|
||||
- PostgreSQL on same server
|
||||
- Backup strategy
|
||||
|
||||
**Option 2: Cloud (Recommended)**
|
||||
- Frontend: Vercel/Netlify (CDN, auto-scaling)
|
||||
- Backend: AWS EC2/DigitalOcean/Render
|
||||
- Database: AWS RDS/DigitalOcean Managed PostgreSQL
|
||||
- nginx on backend for API
|
||||
- Redis for session storage (optional)
|
||||
|
||||
**Option 3: Kubernetes (Overkill for MVP)**
|
||||
- GKE/EKS/AKS
|
||||
- Helm charts
|
||||
- Auto-scaling
|
||||
- Load balancing
|
||||
|
||||
### SSL/TLS
|
||||
|
||||
**Let's Encrypt:**
|
||||
- Free SSL certificates
|
||||
- Auto-renewal with Certbot
|
||||
- Required for WebRTC (getUserMedia requires HTTPS)
|
||||
|
||||
### Environment Variables
|
||||
|
||||
**Backend:**
|
||||
```env
|
||||
NODE_ENV=production
|
||||
PORT=3000
|
||||
DATABASE_URL=postgresql://user:pass@host:5432/dbname
|
||||
JWT_SECRET=random-secret-key
|
||||
CORS_ORIGIN=https://spotlight.cam
|
||||
STUN_SERVER=stun:stun.l.google.com:19302
|
||||
TURN_SERVER=turn:your-turn-server.com (optional)
|
||||
```
|
||||
|
||||
**Frontend:**
|
||||
```env
|
||||
VITE_API_URL=https://api.spotlight.cam
|
||||
VITE_WS_URL=wss://api.spotlight.cam
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Frontend
|
||||
- Code splitting (React.lazy)
|
||||
- Image optimization
|
||||
- Service worker caching (PWA)
|
||||
- Minimize bundle size
|
||||
|
||||
### Backend
|
||||
- Database connection pooling
|
||||
- Query optimization (indexes)
|
||||
- Caching (Redis for frequently accessed data)
|
||||
- Compression (gzip/brotli)
|
||||
|
||||
### WebRTC
|
||||
- Adaptive chunk size based on bandwidth
|
||||
- Buffer management (avoid overwhelming DataChannel)
|
||||
- Connection quality monitoring
|
||||
- Automatic reconnection on failure
|
||||
|
||||
---
|
||||
|
||||
## Monitoring & Logging (Future)
|
||||
|
||||
**Backend:**
|
||||
- Winston/Pino for logging
|
||||
- Log levels: error, warn, info, debug
|
||||
- Structured logging (JSON format)
|
||||
|
||||
**Database:**
|
||||
- Query performance monitoring
|
||||
- Slow query log
|
||||
- Connection pool metrics
|
||||
|
||||
**WebRTC:**
|
||||
- Connection success rate
|
||||
- Transfer speed metrics
|
||||
- Failure reasons
|
||||
- Browser compatibility stats
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-11-12
|
||||
Reference in New Issue
Block a user