Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 89 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

> _Code together. Think faster._

A real-time collaborative code editor with AI-powered assistance. Multiple users can simultaneously write, edit, and debug code in the same room — with live cursor tracking, instant code synchronization, and an AI assistant powered by Google Gemini that streams responses in real-time.
- **Real-Time Collaboration:** A real-time collaborative code editor with AI-powered assistance. Multiple users can simultaneously write, edit, and debug code in the same room — with live cursor tracking, instant code synchronization, and an AI assistant powered by Google Gemini that streams responses in real-time.

- **Scalability:** Designed a stateless backend architecture by replacing in-memory state with Redis, enabling **horizontal scaling across multiple server** instances.

- **AI Integration:** Implemented an **AI assistant with streaming responses (SSE)**, delivering real-time incremental outputs similar to ChatGPT.

- **Distributed Architecture:** Developed a Redis-based data model **(room:{id}:users)** as a single source of truth for user presence, ensuring consistency in distributed environments.

- **Persistence & Reliability:** Built a snapshot persistence system with authenticated fetch keepalive, ensuring code is saved reliably even during page unload events.

[![CI Pipeline](https://github.com/rah7202/smart-code-lab/actions/workflows/ci.yml/badge.svg)](https://github.com/rah7202/smart-code-lab/actions)

Expand Down Expand Up @@ -37,38 +45,86 @@ A real-time collaborative code editor with AI-powered assistance. Multiple users

---

## Architecture
## System Architecture

```
┌─────────────────────────────────────┐
│ Frontend (React) │
│ ┌──────────┐ ┌──────┐ ┌────────┐ │
│ │ Monaco │ │ AI │ │Version │ │
│ │ Editor │ │Panel │ │History │ │
│ └────┬─────┘ └──┬───┘ └───┬────┘ │
│ │ │ │ │
│ ┌────┴──────────┴──────────┴────┐ │
│ │ Hooks Layer │ │
│ │ useCollaboration │ │
│ │ useAI (SSE streaming) │ │
│ │ useEditorPersistence │ │
│ └────┬──────────┬──────────┬────┘ │
└───────┼──────────┼──────────┼───────┘
│ WebSocket│ REST/SSE │ REST
│ │ │
┌───────┼──────────┼──────────┼───────┐
│ │ Backend (Express) │ │
│ ┌────┴────┐ ┌───┴───┐ ┌───┴────┐ │
│ │Socket.IO│ │ AI │ │ Room │ │
│ │ Server │ │Service│ │Service │ │
│ └────┬────┘ └───┬───┘ └───┬────┘ │
│ │ │ │ │
│ ┌────┴──────────┴──────────┴────┐ │
│ │ Data Layer │ │
│ │ PostgreSQL Redis │ │
│ │ (Prisma ORM) (state/adapter)│ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
```mermaid
%%{init: {'theme': 'base'}}%%
flowchart TB

subgraph Client["🖥️ Frontend · React + Vite"]
direction LR
Monaco["Monaco Editor"]
AIP["AI Panel"]
VH["Version History"]
end

subgraph HooksLayer["🪝 Hooks Layer"]
direction LR
HC["useCollaboration"]
HA["useAI"]
HP["useEditorPersistence"]
end

Client --> HooksLayer

HC <-->|"WebSocket"| SocketIO
HA <-->|"SSE Stream"| AIRoute
HP <-->|"REST + JWT"| RoomRoute

subgraph Server["⚙️ Backend · Express"]

MW["Middleware\nHelmet · CORS · Rate Limit · JWT · Zod"]

subgraph Core["Services"]
direction LR
SocketIO["Socket.IO Server\n+ Redis Adapter"]
AIRoute["AI Service\nGemini 2.5 Flash"]
RoomRoute["Room Service"]
CompileSvc["Compile Service"]
end

MW --> Core
end

CompileSvc -->|"HTTP"| Judge0["Judge0 API"]
AIRoute -->|"API"| Gemini["Google Gemini"]

subgraph DataStores["💾 Data Layer"]
direction LR
PG["PostgreSQL · Prisma ORM\nUser · Room · Snapshot · AIMessage"]
RD["Redis\nRoom State · Presence · Pub/Sub"]
end

Core --> DataStores
SocketIO -.->|"horizontal scaling"| RD

%% 🎨 COLOR DEFINITIONS
classDef client fill:#3b82f6,stroke:#1e40af,color:#fff
classDef core fill:#10b981,stroke:#065f46,color:#fff
classDef service fill:#f59e0b,stroke:#92400e,color:#fff
classDef realtime fill:#8b5cf6,stroke:#5b21b6,color:#fff
classDef data fill:#ef4444,stroke:#7f1d1d,color:#fff
classDef external fill:#f3f4f6,stroke:#9ca3af,color:#111

%% 🎯 APPLY COLORS

%% Frontend
class Monaco,AIP,VH client

%% Hooks + Middleware
class HC,HA,HP,MW core

%% Services
class AIRoute,RoomRoute,CompileSvc service

%% Realtime
class SocketIO realtime

%% Data
class PG,RD data

%% External APIs
class Judge0,Gemini external
```

---
Expand Down Expand Up @@ -134,7 +190,7 @@ smart-code-lab/

- **Node.js** ≥ 20
- **npm** ≥ 9
- **PostgreSQL** (local or hosted, e.g., Neon)
- **PostgreSQL** (local or hosted, e.g., Neon DB)
- **Redis** (local or hosted, e.g., Render Redis)
- **Gemini API Key** — [Get one here](https://aistudio.google.com/apikey)

Expand Down Expand Up @@ -277,7 +333,6 @@ frontend job (runs after backend passes):
| **Yjs / CRDT Integration** | 🔵 Next Major | Replace keystroke broadcasting with binary delta sync for better concurrency |
| **Docker Compose** | 🟡 Medium | Redis + PostgreSQL + App in one-command setup |
| **More Languages** | 🟡 Medium | Java, Go, Rust, etc. |
| **Room Sharing Link** | 🟢 Easy | Copy-to-clipboard shareable URL |
| **AI Context Awareness** | 🔵 Future | Pass execution output to Gemini for smarter debugging |
| **JWT Blacklist on Logout** | 🟢 Easy | `redis.set(blacklist:${token})` with TTL |

Expand Down
138 changes: 135 additions & 3 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,138 @@ backend/

## Architecture

### System Architecture

```mermaid

%%{init: {'theme': 'base'}}%%
flowchart LR

Client["🌐 Client\n(Browser)"]

subgraph MiddlewarePipeline["🛡️ Middleware Pipeline"]
direction TB
M1["Helmet\nSecurity Headers"]
M2["CORS\nOrigin Allowlist"]
M3["Body Parser\nJSON · 50KB limit"]
M4["Rate Limiter\n100 req/min"]
M1 --> M2 --> M3 --> M4
end

Client -->|"HTTP / REST"| MiddlewarePipeline

subgraph AuthGate["🔐 Auth Gate"]
direction TB
JWT["JWT Verify\nExtract userId"]
ZOD["Zod Validate\nSchema check"]
JWT --> ZOD
end

MiddlewarePipeline --> AuthGate

subgraph API["📋 API Routes"]
direction TB

subgraph R1["Auth"]
A1["POST /signup"]
A2["POST /signin"]
end

subgraph R2["Room"]
B1["GET /:roomId"]
B2["POST /create"]
B3["POST /:roomId/save"]
end

subgraph R3["Compile"]
C1["POST /compile"]
end

subgraph R4["AI"]
D1["POST /generate"]
D2["POST /stream ← SSE"]
D3["GET /history/:roomId"]
D4["DELETE /history/:roomId"]
end

subgraph R5["Snapshot"]
E1["POST /:roomId"]
E2["GET /:roomId"]
end
end

AuthGate --> API

subgraph Services["⚙️ Service Layer"]
direction TB
AuthSvc["Auth Service\nbcrypt + JWT sign"]
RoomSvc["Room Service\nFind or Create"]
CompileSvc["Judge0 Service\nCode Execution"]
AISvc["AI Service\nGemini 2.5 Flash\nBatch + Stream"]
SnapSvc["Snapshot Service\nDeduplicated Save"]
end

R1 --> AuthSvc
R2 --> RoomSvc
R3 --> CompileSvc
R4 --> AISvc
R5 --> SnapSvc

subgraph DataLayer["💾 Data Stores"]
direction TB
PG["PostgreSQL\n(Prisma ORM)\n───\nUser · Room\nCodeSnapshot\nAIMessage"]
RD["Redis\n───\nRoom state\nUser presence\nSocket mapping"]
end

AuthSvc & RoomSvc & SnapSvc --> PG
RoomSvc --> RD

subgraph External["🌍 External APIs"]
direction TB
Judge0["Judge0 CE\nCode Runner"]
Gemini["Google Gemini\nAI Generation"]
end

CompileSvc --> Judge0
AISvc --> Gemini

WSClient["🔌 Socket.IO\nClient"]

subgraph SocketServer["🔌 Socket.IO Server"]
direction TB
SAuth["JWT Handshake Auth"]
SEvents["Events\n───\njoin · content-edited\ncursor-move · disconnect"]
SAdapter["Redis Adapter\nMulti-server pub/sub"]
SAuth --> SEvents --> SAdapter
end

WSClient -->|"WebSocket"| SocketServer
SAdapter --> RD
SEvents --> PG

%% 🎨 COLOR DEFINITIONS
classDef client fill:#3b82f6,stroke:#1e40af,color:#fff
classDef core fill:#10b981,stroke:#065f46,color:#fff
classDef service fill:#f59e0b,stroke:#92400e,color:#fff
classDef socket fill:#8b5cf6,stroke:#5b21b6,color:#fff
classDef data fill:#ef4444,stroke:#7f1d1d,color:#fff
classDef external fill:#f3f4f6,stroke:#9ca3af,color:#111

%% 🎯 APPLY COLORS
class Client client

class M1,M2,M3,M4,JWT,ZOD,A1,A2,B1,B2,B3,C1,D1,D2,D3,D4,E1,E2 core

class AuthSvc,RoomSvc,CompileSvc,AISvc,SnapSvc service

class WSClient,SAuth,SEvents,SAdapter socket

class PG,RD data

class Judge0,Gemini external

```

### Request Flow

```
Expand All @@ -79,10 +211,10 @@ Client Request
CORS (origin allowlist)
Rate Limiter (global: 100/min, AI: 10/min)
Body Parser (50KB limit)
Body Parser (50KB limit)
Rate Limiter (global: 100/min, AI: 10/min)
authenticate middleware (JWT verify)
Expand Down Expand Up @@ -545,7 +677,7 @@ Room (1) ──→ (N) AIMessage
# Run all tests with coverage
npm test

# Coverage thresholds: 70% lines, 70% functions
# Coverage thresholds: 70% lines, 60% functions
```

Test environment variables are set in `src/__tests__/envSetup.ts` — `JWT_SECRET`, `NODE_ENV=test`, and `DATABASE_URL` are all provided so no real database is needed (Prisma is mocked).
Expand Down
Loading
Loading