Skip to content

deepak-kumar20/CollabAi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

CollabAI

AI-powered real-time collaboration platform — think Notion + Slack + ChatGPT in one MERN-stack app.

Teams can edit documents together in real time with live cursors, chat in channels and DMs, share files, and ask an AI assistant for help — all under one workspace with role-based access.


Table of Contents

  1. What It Does
  2. Tech Stack
  3. Quick Start
  4. Environment Variables
  5. Project Structure
  6. Architecture Overview
  7. API Reference
  8. Socket Events
  9. Data Models
  10. How to Test Multi-User Features
  11. Roadmap

What It Does

Module Features
🔐 Auth Email/password registration & login · bcrypt hashing · JWT tokens · protected routes
🏢 Workspaces Create workspaces · invite by email · 4-tier roles (Owner / Admin / Member / Viewer) · member management
📝 Live Editor Tiptap rich-text · real-time collaborative editing (Yjs CRDT) · live cursors with user color & name · auto-save every 3s · version history snapshots
💬 Chat Public/private channels · 1:1 DMs · message history with infinite scroll · emoji reactions · typing indicators · online presence · edit & delete
📎 File Uploads Drag-and-drop in chat · images, videos, audio, PDFs, docs · Cloudinary integration with local-disk fallback for dev
AI Assistant Gemini-powered chat · conversation history · auto-generated titles · quick actions API (summarize / improve / fix grammar / generate / meeting notes / task list) · demo mode when no API key
🛡️ Security Helmet · CORS · rate limiting · Mongo sanitize · XSS clean · HPP · per-route auth/role middleware

Tech Stack

Frontend

  • React 18 (Vite)
  • Tailwind CSS + @tailwindcss/typography (for Tiptap prose content)
  • Zustand (state) with persist middleware for auth
  • React Router v6
  • Axios with auto-token interceptor
  • Tiptap (ProseMirror-based editor) + Yjs + y-protocols + custom Socket.io provider for collab
  • socket.io-client for chat/editor/presence
  • react-hot-toast for notifications

Backend

  • Node.js + Express 4
  • MongoDB + Mongoose 8
  • Socket.io 4 (JWT-authenticated)
  • Yjs (server-side CRDT for live editing)
  • jsonwebtoken + bcryptjs
  • Zod for input validation
  • multer for multipart uploads
  • cloudinary SDK (with local-disk fallback)
  • @google/generative-ai (Gemini)
  • Security: helmet, cors, express-rate-limit, express-mongo-sanitize, xss-clean, hpp

Storage / Services

  • MongoDB Atlas (or local MongoDB)
  • Cloudinary (optional — falls back to local server/uploads/)
  • Google Gemini API (optional — falls back to demo replies)

Quick Start

Prerequisites

  • Node.js 18+
  • MongoDB running locally OR a free MongoDB Atlas cluster
  • (Optional) Gemini API key from aistudio.google.com
  • (Optional) Cloudinary account from cloudinary.com

1. Backend

cd server
cp .env.example .env    # fill in MONGO_URI and JWT_SECRET (rest optional)
npm install
npm run dev

Server runs at http://localhost:5000. Visit /api/health to verify it's up.

2. Frontend

cd client
cp .env.example .env    # defaults work for local dev
npm install
npm run dev

App runs at http://localhost:5173.

Windows PowerShell users: use copy .env.example .env instead of cp.


Environment Variables

server/.env

# Required
NODE_ENV=development
PORT=5000
CLIENT_URL=http://localhost:5173
MONGO_URI=mongodb://127.0.0.1:27017/collabai
JWT_SECRET=replace-with-long-random-string-min-32-chars
JWT_EXPIRES_IN=7d

# Optional — without this, AI runs in demo mode (mock replies)
GEMINI_API_KEY=

# Optional — without these, files save to ./uploads/ and serve at /uploads
CLOUDINARY_CLOUD_NAME=
CLOUDINARY_API_KEY=
CLOUDINARY_API_SECRET=

client/.env

VITE_API_URL=http://localhost:5000/api
VITE_SOCKET_URL=http://localhost:5000

Project Structure

collabai/
├── README.md
├── .gitignore
│
├── client/                          # React + Vite frontend
│   ├── index.html
│   ├── vite.config.js
│   ├── tailwind.config.js
│   ├── postcss.config.js
│   ├── package.json
│   └── src/
│       ├── api/                     # Axios clients per resource
│       │   ├── axios.js             # base client + 401 interceptor
│       │   ├── auth.js
│       │   ├── workspaces.js
│       │   ├── documents.js
│       │   ├── chat.js
│       │   ├── ai.js
│       │   └── uploads.js
│       │
│       ├── components/
│       │   ├── ProtectedRoute.jsx
│       │   ├── ui/                  # Button, Input, Spinner
│       │   ├── editor/              # CollabEditor, EditorToolbar
│       │   └── chat/                # ChannelSidebar, MessageList,
│       │                            # MessageItem, MessageInput
│       │
│       ├── hooks/useAuth.js         # auth hydration + socket bootstrap
│       │
│       ├── layouts/
│       │   ├── AuthLayout.jsx       # split-screen login/register
│       │   └── DashboardLayout.jsx  # sidebar nav after login
│       │
│       ├── lib/
│       │   ├── socket.js            # socket.io-client singleton
│       │   └── yjsSocketProvider.js # Yjs <-> Socket.io provider
│       │
│       ├── pages/
│       │   ├── Landing.jsx
│       │   ├── auth/{Login,Register}.jsx
│       │   ├── Dashboard.jsx
│       │   ├── Workspaces.jsx
│       │   ├── WorkspaceDetail.jsx  # docs + members + invite
│       │   ├── Documents.jsx        # all docs across workspaces
│       │   ├── DocumentEditor.jsx   # full-screen editor page
│       │   ├── Chat.jsx
│       │   ├── AIAssistant.jsx
│       │   ├── ComingSoon.jsx
│       │   └── NotFound.jsx
│       │
│       ├── store/                   # Zustand stores
│       │   ├── authStore.js
│       │   ├── workspaceStore.js
│       │   ├── chatStore.js
│       │   └── aiStore.js
│       │
│       ├── App.jsx                  # all routes
│       ├── main.jsx                 # bootstrap + Toaster
│       └── index.css                # Tailwind + component classes
│
└── server/                          # Express backend
    ├── package.json
    └── src/
        ├── config/
        │   ├── env.js               # validated env loader
        │   ├── db.js
        │   ├── gemini.js
        │   └── cloudinary.js
        │
        ├── models/                  # Mongoose schemas
        │   ├── User.model.js
        │   ├── Workspace.model.js
        │   ├── Document.model.js
        │   ├── DocumentVersion.model.js
        │   ├── Channel.model.js
        │   ├── Message.model.js
        │   ├── AIConversation.model.js
        │   └── Upload.model.js
        │
        ├── controllers/             # Route handlers
        │   ├── auth.controller.js
        │   ├── user.controller.js
        │   ├── workspace.controller.js
        │   ├── document.controller.js
        │   ├── channel.controller.js
        │   ├── message.controller.js
        │   ├── ai.controller.js
        │   └── upload.controller.js
        │
        ├── routes/                  # Express routers
        │   ├── auth.routes.js
        │   ├── user.routes.js
        │   ├── workspace.routes.js
        │   ├── document.routes.js
        │   ├── chat.routes.js
        │   ├── ai.routes.js
        │   └── upload.routes.js
        │
        ├── middleware/
        │   ├── auth.middleware.js
        │   ├── workspace.middleware.js
        │   ├── validate.middleware.js
        │   ├── upload.middleware.js
        │   └── error.middleware.js
        │
        ├── validators/              # Zod schemas
        │   ├── auth.validator.js
        │   ├── workspace.validator.js
        │   ├── document.validator.js
        │   ├── chat.validator.js
        │   └── ai.validator.js
        │
        ├── services/                # Business logic
        │   ├── ai.service.js
        │   └── upload.service.js
        │
        ├── sockets/                 # Socket.io handlers
        │   ├── index.js             # auth + connect/disconnect dispatch
        │   ├── auth.js              # JWT middleware for sockets
        │   ├── presence.js          # online/offline tracking
        │   ├── document.socket.js   # Yjs collab rooms
        │   └── chat.socket.js       # send/typing/react/edit/delete
        │
        ├── utils/
        │   ├── ApiError.js
        │   ├── ApiResponse.js
        │   ├── asyncHandler.js
        │   └── slug.js
        │
        ├── app.js                   # Express app + middleware
        └── server.js                # HTTP + Socket.io bootstrap

Architecture Overview

Authentication

  • POST /api/auth/register and /login issue a single JWT (default 7-day lifetime)
  • Frontend persists token in localStorage via Zustand persist middleware
  • Axios interceptor attaches Authorization: Bearer <token> to every request
  • 401 responses trigger automatic logout
  • Socket.io connections authenticate via auth: { token } handshake field; the socket attaches the user to every event handler

Real-Time Editor

The hardest part of the platform. Pipeline:

  1. User opens /documents/:id
  2. Frontend instantiates a Y.Doc and a custom SocketIOProvider
  3. Provider emits doc:join → server validates workspace membership and returns the document's binary state (Y.encodeStateAsUpdate, base64-encoded)
  4. Tiptap binds to the same Y.Doc via @tiptap/extension-collaboration
  5. Every local edit emits a Yjs update over Socket.io → server applies to its in-memory Y.Doc and broadcasts to the room
  6. Other clients merge the update — CRDT guarantees zero conflicts even with concurrent edits
  7. Awareness protocol (y-protocols/awareness) syncs cursor positions and user metadata for live cursors
  8. Server debounces saves: 3 seconds after the last edit, the binary state + plaintext + a version snapshot get persisted to MongoDB
  9. When the last user leaves a room, the in-memory doc is evicted (after a final flush)

Chat

  • Same Socket.io connection used for the editor
  • Each channel has a room chan:<channelId>; joining requires workspace membership and (for private/DM) explicit channel membership
  • Messages persist to MongoDB synchronously, then broadcast to the room
  • Presence: in-memory Map<userId, Set<socketId>> — broadcasts presence:online / presence:offline to workspace rooms when a user's last/first socket connects
  • Typing: client emits debounced (2 s) chat:typing, broadcast to the channel room
  • Reactions: stored inline on Message as [{ emoji, users[] }]; toggle pattern
  • Pagination: cursor-based on _id (ObjectIds are time-sortable), 50 per page, infinite scroll up

AI Assistant

  • Each user has their own conversations (AIConversation model holds a thread of {role, content} messages)
  • POST /api/ai/conversations/:id/messages appends the user turn, sends the last 20 turns as context to Gemini 1.5 Flash, persists the reply
  • First-exchange titles are auto-generated by a second Gemini call
  • No API key? ai.service.js returns a mock reply explaining how to enable Gemini — every UI flow remains testable
  • Rate limit: 30 requests/min/user

File Uploads

  • POST /api/uploads accepts multipart/form-data with field file
  • Multer parses into memory (10 MB cap, MIME whitelist)
  • If Cloudinary creds are present → upload_stream → returns secure_url
  • Otherwise → write to server/uploads/<random>.<ext> → returns /<host>/uploads/<filename> (served by express.static)
  • Chat messages attach the upload metadata; MessageItem renders image/video/audio inline and other types as download cards

API Reference

All routes are prefixed /api. All non-auth routes require Authorization: Bearer <token>.

Auth (/api/auth)

Method Path Body Returns
POST /register {name, email, password} {user, token}
POST /login {email, password} {user, token}
GET /me {user}

Workspaces (/api/workspaces)

Method Path Notes
GET / List workspaces the current user belongs to
POST / Create a workspace; creator becomes Owner
GET /:id Get one (must be member)
PATCH /:id Update (admin+)
DELETE /:id Delete (owner only)
POST /:id/members Invite by email (admin+)
PATCH /:id/members/:userId Change role (admin+)
DELETE /:id/members/:userId Remove member (admin+)

Documents (/api/documents and /api/workspaces/:workspaceId/documents)

Method Path Notes
GET /workspaces/:workspaceId/documents List docs in workspace
POST /workspaces/:workspaceId/documents Create (member+)
GET /documents/:id Get metadata + role
PATCH /documents/:id Rename (member+)
DELETE /documents/:id Delete (admin+)
GET /documents/:id/versions Version history (50 most recent)

Chat (/api/channels, /api/messages, /api/dm)

Method Path Notes
GET /workspaces/:workspaceId/channels {channels, dms}
POST /workspaces/:workspaceId/channels Create channel
GET /channels/:id Get with members populated
PATCH /channels/:id Rename
DELETE /channels/:id Delete (creator or workspace admin)
POST /channels/:id/join Self-join a public channel
POST /dm {workspaceId, userId} → get or create 1:1 DM
GET /channels/:channelId/messages?before=&limit= Paginated history
POST /channels/:channelId/messages REST send (also via socket)
PATCH /messages/:id Edit own message
DELETE /messages/:id Soft-delete (own message or workspace admin)
POST /messages/:id/reactions Toggle reaction {emoji}

AI (/api/ai)

Method Path Notes
GET /status {enabled, provider, model}
GET /conversations List with previews
POST /conversations Create new conversation
GET /conversations/:id Get with full message history
PATCH /conversations/:id Rename
DELETE /conversations/:id Delete
POST /conversations/:id/messages Send turn, get AI reply
POST /quick {action, text} — quick action without conversation

Quick action keys: summarize, improve, fix-grammar, generate, meeting-notes, task-list.

Uploads (/api/uploads)

Method Path Body Notes
POST / multipart/form-data with field file Single file, max 10 MB
POST /multiple field files[] Up to 5 files

Socket Events

All sockets must authenticate via the handshake: io(URL, { auth: { token } }).

Editor

Direction Event Payload
C → S doc:join {documentId} → ack {ok, state (base64), role}
C → S doc:update {documentId, update (base64)}
C → S awareness:update {documentId, update (base64)}
C → S doc:leave {documentId}
S → C doc:update {update, from}
S → C awareness:update {update, from}
S → C presence:join / presence:leave {socketId, user?}

Chat

Direction Event Payload
C → S chat:join / chat:leave {channelId}
C → S chat:send {channelId, content, replyTo?, attachments?}
C → S chat:typing {channelId, isTyping}
C → S chat:react {messageId, emoji}
C → S chat:edit {messageId, content}
C → S chat:delete {messageId}
S → C chat:message {message}
S → C chat:edit {message}
S → C chat:delete {messageId, channelId}
S → C chat:reaction {messageId, reactions}
S → C chat:typing {channelId, userId, user, isTyping}

Presence

Direction Event Payload
C → S presence:list {workspaceId} → ack {ok, online: [userId]}
S → C presence:online {userId}
S → C presence:offline {userId}

Data Models

User

name, email (unique, lowercased), password (bcrypt, select: false), avatar, isEmailVerified, workspaceIds[], lastLoginAt, timestamps.

Workspace

name, slug (auto-generated, unique), description, icon, owner, members: [{user, role, joinedAt}] where role ∈ {owner, admin, member, viewer}, timestamps.

Document

title, workspace, createdBy, lastEditedBy, yState (Buffer — Yjs binary state), plainText (extracted for search/preview), timestamps. Companion DocumentVersion for snapshots.

Channel

workspace, type ∈ {channel, dm}, name, description, isPrivate, members[], createdBy, lastMessageAt, timestamps. DMs are type: 'dm' with exactly 2 members.

Message

channel, sender, content, attachments: [{url, name, mimeType, size, kind, publicId, storage}], replyTo, reactions: [{emoji, users[]}], editedAt, deletedAt (soft delete), timestamps.

AIConversation

user, title, messages: [{role, content, createdAt}] where role ∈ {user, assistant}, timestamps.

Upload

uploadedBy, url, publicId, name, mimeType, size, kind ∈ {image, video, audio, pdf, document, other}, storage ∈ {cloudinary, local}, timestamps.


How to Test Multi-User Features

The real value of this app is collaboration. To test it end-to-end:

  1. Open two browsers (e.g. Chrome + Chrome Incognito, or Chrome + Edge)
  2. Register two accounts: alice@test.com and bob@test.com
  3. As Alice → create a workspace → open it → Members section → invite bob@test.com as Member
  4. As Alice → create a document → copy the URL
  5. Paste the URL into Bob's browser
  6. You should now see:
    • Both avatars in the editor header
    • Each user's cursor in the doc with their name and a unique color
    • Live updates as either user types
    • Auto-save indicator after pauses
  7. In Chat: both join #general → type → see typing dots, reactions, edit, delete, file attachments live
  8. Start a DM from either side → online dot turns green/grey as the other user connects/disconnects

Roadmap

Status Phase Scope
1 Auth + Workspaces + Roles
2 Real-time editor (Yjs + Tiptap + custom Socket.io provider)
3 Real-time chat (channels, DMs, presence, typing, reactions, edit/delete)
4 AI assistant (Gemini) + file uploads (Cloudinary/local)
5 Notifications (mentions, invites) · Analytics dashboard · Redis caching · Docker · CI/CD

Known Gaps (Easy to Add)

  • Email verification & forgot-password flows (no SMTP wired)
  • Google OAuth (placeholder env vars present)
  • Version history UI (snapshots are stored, restore UI not built)
  • Reply-to UI (model + render supports it, no "Reply" button yet)
  • AI quick actions inside the editor (backend ready, toolbar buttons not added)
  • Search across docs and chat (no full-text index yet)
  • Streaming AI responses (currently non-streaming with a "thinking" spinner)

Scripts Reference

Backend (/server)

Script What it does
npm run dev Run with nodemon (auto-restart on file change)
npm start Run with plain node (for production)

Frontend (/client)

Script What it does
npm run dev Vite dev server (HMR)
npm run build Production build → dist/
npm run preview Serve the built output locally

Deployment Notes

  • Frontend → Vercel (or Netlify): set VITE_API_URL and VITE_SOCKET_URL to your production backend URL
  • Backend → Render (or Railway/Fly.io): set all server/.env vars, point to MongoDB Atlas, ensure WebSocket support is enabled
  • Database → MongoDB Atlas free tier is enough to start
  • Files → Cloudinary is recommended in production (local disk doesn't persist on Render's ephemeral filesystem)

License

MIT — use it, learn from it, ship it.


Built with the MERN stack — React + Express + MongoDB + Node.js.

About

Developed a real-time document collaboration platform with live cursors, user presence, autosave, and conflict-free synchronization using Yjs and Socket.io.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors