Skip to content

Latest commit

Β 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Projectify

AI Interview Prep Banner

A multi-tenant project management platform β€” built from scratch to understand what actually goes into a system like Jira.

Node.js Express MongoDB Redis Socket.io React JWT License: MIT PRs Welcome


What is this?

Projectify is a full-stack project management platform with a Kanban board, real-time collaboration, workspace-based multi-tenancy, and role-based permissions β€” similar to Jira or Linear, built from scratch.

The goal wasn't to replace existing tools. It was to make every engineering decision that goes into building one: where to store tokens, how to design the database schema, how to scope real-time events, how to handle the token refresh race condition that nobody writes about in tutorials.



πŸ—οΈ System Architecture

Projectify System Architecture

Projectify follows a feature-based architecture where the React frontend communicates with the Express backend using REST APIs and Socket.io. Business logic is isolated inside services, Redis powers distributed rate limiting, and MongoDB stores all workspace, project, task, and notification data.


Features

Backend

  • JWT Authentication β€” HttpOnly cookies with dual-token system (15min access + 7day refresh). Token refresh race condition handled with a request queue
  • Google OAuth β€” Passport.js with account linking for existing email/password accounts
  • Role-Based Access Control β€” Per-workspace roles (owner / admin / member) enforced in middleware, not controllers
  • Multi-tenant Workspaces β€” Complete data isolation between workspaces
  • Project & Epic Management β€” Projects contain epics which group related tasks
  • Kanban Task Board β€” Tasks with fractional position indexing for O(1) drag-and-drop reordering
  • Real-time Updates β€” Socket.io with room-scoped events. Task updates reach only users viewing that board
  • Analytics Dashboard β€” 7 parallel aggregation queries via Promise.all (~50ms vs ~350ms sequential)
  • Search β€” Case-insensitive task search across workspace
  • Notifications β€” Per-user event notifications with TTL auto-expiry after 30 days
  • Tiered Rate Limiting β€” Three Redis-backed tiers: auth (10/15min), API (60/min), global (200/15min)
  • Security Stack β€” Helmet, CSRF protection, NoSQL injection sanitization, HPP
  • Structured Logging β€” Winston with JSON output in production, request ID tracing on every log line

Frontend

  • Kanban Board β€” Drag and drop with @dnd-kit, optimistic updates with rollback on failure
  • Real-time sync β€” Socket.io room-based live updates without polling
  • Command palette β€” Cmd+K search across projects and tasks
  • Analytics charts β€” Recharts: area chart (completion trend), pie (status), bar (priority)
  • Dark mode β€” System preference detection via next-themes
  • Form validation β€” React Hook Form + Zod (same schemas as backend)


πŸ” Authentication Flow

Authentication Flow

Projectify uses a dual-token authentication system with HttpOnly cookies, Google OAuth, automatic token refresh, and request queuing to eliminate refresh race conditions.


Tech Stack

Backend

Technology Purpose Why chosen
Node.js + Express HTTP server Async I/O fits this data-heavy workload
MongoDB + Mongoose Database Nested task data (checklists, arrays) maps naturally to documents
Redis (Upstash) Rate limiting Shared counter across server instances
Socket.io Real-time Room abstraction for scoped event delivery
JWT + Passport.js Auth Stateless sessions + Google OAuth
Zod Validation Schema-first, type coercion, same library on frontend
Winston Logging JSON structured output + custom http log level

Frontend

Technology Purpose
React + Vite UI framework + build tool
TanStack Query v5 Server state, caching, optimistic updates
Zustand Client state (auth, UI)
TailwindCSS + Shadcn UI Styling + accessible components
@dnd-kit Drag and drop
Recharts Analytics charts
Socket.io-client Real-time connection

Project Structure

task_manager/
β”œβ”€β”€ server/                          # Backend
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ config/
β”‚   β”‚   β”‚   β”œβ”€β”€ db.js                # MongoDB connection
β”‚   β”‚   β”‚   β”œβ”€β”€ redis.js             # Redis/Upstash client
β”‚   β”‚   β”‚   β”œβ”€β”€ env.js               # Env validation
β”‚   β”‚   β”‚  
β”‚   β”‚   β”œβ”€β”€ core/
β”‚   β”‚   β”‚   β”œβ”€β”€ errors/              # Custom error classes
β”‚   β”‚   β”‚   β”œβ”€β”€ logger/              # Winston setup
β”‚   β”‚   β”‚   β”œβ”€β”€ middleware/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ authenticate.js  # JWT verification
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ validate.js      # Zod request validation
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ rateLimiter.js   # Tiered Redis rate limits
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ csrfProtection.js
β”‚   β”‚   β”‚   β”‚   └── validateObjectId.js
β”‚   β”‚   β”‚   └── utils/
β”‚   β”‚   β”‚       β”œβ”€β”€ response.js      # sendSuccess / sendCreated
β”‚   β”‚   β”‚       └── socketEmitter.js # Centralized socket emissions
β”‚   β”‚   β”œβ”€β”€ features/
β”‚   β”‚   β”‚   β”œβ”€β”€ auth/                # Login, signup, Google OAuth
β”‚   β”‚   β”‚   β”œβ”€β”€ workspace/           # Workspace CRUD + members
β”‚   β”‚   β”‚   β”œβ”€β”€ projects/            # Projects + epics
β”‚   β”‚   β”‚   β”œβ”€β”€ tasks/               # Tasks + Kanban + search
β”‚   β”‚   β”‚   β”œβ”€β”€ analytics/           # Aggregation-based stats
β”‚   β”‚   β”‚   └── notifications/       # User notifications
β”‚   β”‚   β”œβ”€β”€ seeders/
β”‚   β”‚   β”‚   └── seed.js              # Demo data seeder
β”‚   β”‚   β”œβ”€β”€ app.js                   # Express app + middleware chain
β”‚   β”‚   └── server.js                # HTTP server + Socket.io init
β”‚   └── .env
β”‚
└── client/                          # Frontend
    └── src/
        β”œβ”€β”€ core/                    # API client, stores, providers, router
        β”œβ”€β”€ features/                # auth, workspace, projects, tasks, analytics
        └── shared/                  # UI components, hooks, utils

Getting Started

Prerequisites

  • Node.js 18+
  • MongoDB Atlas account (free M0 tier works)
  • Upstash Redis account (free tier works)
  • Google Cloud Console project (for OAuth)

1. Clone the repository

git clone https://github.com/Siddhi561/Projectify
cd Projectify

2. Backend setup

cd server
npm install

Create server/.env:

# Server
PORT=5000
NODE_ENV=development

# Database
MONGODB_URI=mongodb+srv://user:password@cluster.mongodb.net/projectify

# Redis (use rediss:// for Upstash TLS)
REDIS_URL=rediss://default:password@your-instance.upstash.io:6379

# JWT β€” generate with: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
JWT_ACCESS_SECRET=your_64_char_hex_here
JWT_REFRESH_SECRET=different_64_char_hex_here

# Frontend URL (for CORS)
CLIENT_URL=http://localhost:5173

# Google OAuth β€” from console.cloud.google.com
GOOGLE_CLIENT_ID=your_client_id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-your_secret
GOOGLE_CALLBACK_URL=http://localhost:5000/api/auth/google/callback

Start the server:

npm run dev

3. Seed the database (optional)

Populate the database with realistic demo data for testing and development.

# Add demo data
npm run seed

# Clear existing data and reseed
npm run seed:clear

The seeder creates:

  • πŸ‘₯ 4 demo users
  • 🏒 2 workspaces
  • πŸ“ 4 projects
  • πŸ“Œ 8 epics
  • βœ… ~60 tasks
  • πŸ”” Sample notifications

Demo account

Email:    demo@projectify.dev
Password: Demo@1234

4. Frontend setup

cd ../client
npm install

# Install Shadcn UI components
npx shadcn-ui@latest init
npx shadcn-ui@latest add button input label textarea card dialog dropdown-menu \
  select avatar badge separator progress skeleton scroll-area popover \
  collapsible command tooltip

Create client/.env:

VITE_API_URL=http://localhost:5000

Start the frontend:

npm run dev

Open http://localhost:5173


Raw OpenAPI JSON:

http://localhost:5000/api/docs.json

Quick endpoint reference

Method Endpoint Auth Min Role
POST /api/auth/signup ❌ β€”
POST /api/auth/login ❌ β€”
GET /api/auth/me βœ… any
POST /api/auth/logout βœ… any
POST /api/auth/refresh ❌ β€”
GET /api/workspaces βœ… any
POST /api/workspaces βœ… any
GET /api/workspaces/:id βœ… member
PATCH /api/workspaces/:id βœ… admin
DELETE /api/workspaces/:id βœ… owner
POST /api/workspaces/:id/members/invite βœ… admin
PATCH /api/workspaces/:id/members/:mId/role βœ… admin
DELETE /api/workspaces/:id/members/:mId βœ… admin
GET /api/workspaces/:id/projects βœ… member
POST /api/workspaces/:id/projects βœ… member
GET /api/workspaces/:id/projects/:pid βœ… member
PATCH /api/workspaces/:id/projects/:pid βœ… member
DELETE /api/workspaces/:id/projects/:pid βœ… admin
GET /api/workspaces/:id/projects/:pid/epics βœ… member
POST /api/workspaces/:id/projects/:pid/epics βœ… member
PATCH /api/workspaces/:id/projects/:pid/epics/:eid βœ… member
DELETE /api/workspaces/:id/projects/:pid/epics/:eid βœ… admin
GET /api/workspaces/:id/projects/:pid/tasks βœ… member
GET /api/workspaces/:id/projects/:pid/tasks/grouped βœ… member
POST /api/workspaces/:id/projects/:pid/tasks βœ… member
GET /api/workspaces/:id/tasks/:tid βœ… member
PATCH /api/workspaces/:id/tasks/:tid βœ… member
DELETE /api/workspaces/:id/tasks/:tid βœ… creator/admin
POST /api/workspaces/:id/tasks/reorder βœ… member
GET /api/workspaces/:id/tasks/search βœ… member
GET /api/workspaces/:id/stats βœ… member
GET /api/workspaces/:id/projects/:pid/stats βœ… member
GET /api/notifications βœ… any
PATCH /api/notifications/:nid/read βœ… any
PATCH /api/notifications/read-all βœ… any

Key Engineering Decisions

1. HttpOnly Cookies instead of localStorage

localStorage can be read by any JavaScript on the page. HttpOnly cookies cannot be read by JavaScript at all β€” not your code, not an injected script. Token theft via XSS is eliminated at the storage layer.

2. Dual tokens with path-scoped refresh

The refresh token cookie has path: '/api/auth/refresh' β€” the browser only sends it on that one endpoint. Every other API call never carries the most sensitive token.

3. Token refresh race condition

When multiple API calls get 401 simultaneously, a request queue ensures only one refresh fires. Others wait and retry after. Without this, concurrent 401s cause logout when refresh tokens rotate.

4. Fractional indexing for Kanban position

Task positions are floating point numbers. Inserting between positions 1000 and 2000 yields 1500 β€” only one document updated per drag. Integer positions would require updating every subsequent card.

5. Promise.all for analytics

Seven independent database queries run simultaneously instead of sequentially. Result: ~50ms instead of ~350ms for the analytics dashboard.

6. Room-scoped Socket.io events

io.emit() sends to every connected user. Rooms send only to users viewing the relevant board. 500 users online, one task update β€” 10 receive it, 490 are unaffected.

7. Feature-based folder structure

Each feature owns its routes, controller, service, model, and schemas together. Services never touch req or res β€” they receive plain arguments and return plain data, making them independently testable and reusable.


Security Measures

Layer Protects Against
Helmet XSS via CSP, clickjacking, MIME sniffing
express-mongo-sanitize NoSQL injection ($gt, $where operators)
HPP HTTP parameter pollution
Custom CSRF header Cross-site request forgery
SameSite=Lax cookies CSRF on cross-site requests
HttpOnly cookies XSS-based token theft
Redis rate limiting (3 tiers) Brute force, credential stuffing
Zod validation Malformed and malicious input
validateObjectId middleware MongoDB CastError crashes


πŸ›‘οΈ Security Middleware Pipeline

Security Middleware Pipeline

Every incoming request passes through multiple middleware layers before reaching business logic. Each layer removes a specific attack vector such as XSS, CSRF, NoSQL injection, brute-force attacks, malformed input, or unauthorized access.


Known Limitations

These are deliberate tradeoffs, not oversights:

Limitation Impact Fix
No MongoDB transactions on cascade delete Project delete is not atomic across 3 collections Wrap in session.startTransaction()
Refresh tokens not stored Can't revoke individual sessions immediately Store in Redis, delete to revoke
Regex search (no text index) Full collection scan on every search Add $text index or Atlas Search
No Socket.io Redis adapter Rooms break across multiple server instances Add @socket.io/redis-adapter
No optimistic locking on task reorder Concurrent drags = last write wins Add version field, reject stale writes

Environment Variables Reference

Variable Where to get it
PORT Choose any available port (default: 5000)
NODE_ENV Set to development locally, production on server
MONGODB_URI MongoDB Atlas β†’ Connect β†’ Drivers β†’ copy connection string
REDIS_URL Upstash β†’ Database β†’ Details tab β†’ Redis Connect URL (rediss://)
JWT_ACCESS_SECRET Generate: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
JWT_REFRESH_SECRET Same command, different value
CLIENT_URL Your frontend URL (http://localhost:5173 locally)
GOOGLE_CLIENT_ID Google Cloud Console β†’ APIs & Services β†’ Credentials β†’ OAuth 2.0 Client
GOOGLE_CLIENT_SECRET Same as above
GOOGLE_CALLBACK_URL Must match exactly what you set in Google Console

Real-time Events (Socket.io)

The server emits to scoped rooms β€” not broadcast.

Room Joined when Events received
project:{id} User opens a Kanban board task:created, task:updated, task:deleted, task:reordered
workspace:{id} User enters any workspace page member:joined, member:removed
user:{id} User connects (automatic) notification:new


⚑ Real-time Architecture

Socket.io Architecture

Every task update is scoped to Socket.io rooms. Only users currently viewing the relevant project receive updates, eliminating unnecessary broadcasts.


Scripts

Backend

npm run dev       # Start with nodemon (hot reload)
npm start         # Production start
npm run lint      # ESLint check

# Seeder
node src/seeders/seed.js           # Add demo data
node src/seeders/seed.js --clear   # Clear all + reseed

Frontend

npm run dev       # Vite dev server with HMR
npm run build     # Production build
npm run preview   # Preview production build
npm run lint      # ESLint check

Deployment

Backend (Railway / Render / Fly.io)

  1. Set all environment variables in the dashboard
  2. Set NODE_ENV=production
  3. Set CLIENT_URL to your deployed frontend URL
  4. Update GOOGLE_CALLBACK_URL to your production backend URL
  5. In Google Cloud Console, add the production callback URL to authorized redirect URIs

Frontend (Vercel / Netlify)

  1. Set VITE_API_URL to your deployed backend URL
  2. Build command: npm run build
  3. Output directory: dist

What I'd Add Next

Immediate (know exactly how to build):

  • MongoDB transactions for cascade deletes
  • Refresh token rotation with Redis storage
  • MongoDB text index for proper full-text search

Medium term:

  • Email notifications via BullMQ queue + nodemailer
  • File attachments on tasks (S3 presigned URLs)
  • Task comments system with real-time updates
  • Activity feed / audit log

Longer term:

  • Socket.io Redis adapter for multi-instance support
  • Elasticsearch or Atlas Search for ranked full-text search
  • API versioning (/api/v1/, /api/v2/)
  • Integration test suite (Jest + Supertest)

License

MIT License β€” see LICENSE for details.


πŸ‘¨β€πŸ’» Author

Built as a production-inspired full-stack portfolio project to explore the engineering challenges behind modern project management platforms like Jira.

This project showcases secure authentication, workspace-based multi-tenancy, role-based access control (RBAC), real-time collaboration with Socket.io, scalable REST APIs, MongoDB data modeling, Redis-backed rate limiting, and modern React development.

If you found this project helpful or learned something from it, consider giving it a ⭐ to support the project.

About

A production-inspired project management platform demonstrating scalable backend architecture, secure authentication, real-time collaboration, RBAC, and modern MERN development practices.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages