Skip to content

Latest commit

 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DevSync

Live Demo Frontend: Vercel Backend: Render Database: MongoDB Atlas

React TypeScript Node.js Express Socket.io Tailwind CSS Framer Motion

A real-time collaborative Kanban workspace for developers, startups, and agile teams — built to feel like a commercial SaaS product, not a tutorial clone.

Live Demo · Features · Tech Stack · Getting Started · Deployment · API Reference


Overview

Most Kanban apps feel outdated — delayed updates, page refreshes, cluttered UI, poor collaboration. DevSync is built around the opposite premise: every interaction should feel instant, fluid, and alive. Multiple people editing the same board should feel like being in the same room — live cursors, live task updates, live presence — with zero manual refreshing, and animation and depth used deliberately (3D card tilt, spring transitions, glassmorphism) rather than as decoration.

It's a full-stack project meant to demonstrate production-grade engineering end to end: real-time WebSocket architecture, optimistic UI, drag-and-drop, authentication, file uploads, rate limiting, input sanitization, code-split bundles, and a deploy pipeline — not just CRUD screens.

🔗 Live Demo

⚠️ Replace this once deployed: https://your-app.vercel.app

Frontend https://dev-sync-pied.vercel.app
Backend API https://devsync-czb3.onrender.com/api
Health check https://devsync-czb3.onrender.com/api/health

The backend is on Render's free tier, which spins down after inactivity — the first request after a while may take 30–60s to wake up.

✨ Features

Authentication

  • Email/password with JWT in httpOnly cookies
  • Forgot / reset password flow
  • Protected routes, auto-redirect on session expiry

Workspaces & Boards

  • Multi-workspace, multi-board per workspace
  • Member invites, roles, favorites
  • Custom labels per board

Columns & Tasks

  • Add / rename / delete / reorder columns
  • Full task CRUD — priority, due date, labels, assignees, checklists, file attachments (Cloudinary)
  • Drag-and-drop across columns with spring drop animation

Real-Time Collaboration

  • Socket.io — live task/column updates, no polling
  • Figma-style live cursors with name tags and glow
  • Typing indicators, online member count

Comments & Activity

  • Per-task comment threads
  • Full board activity timeline

Notifications

  • Real-time toast + notification panel
  • Mark-as-read, per-user socket rooms

Search

  • Global instant search across boards and tasks

Profile & Settings

  • Edit name, upload avatar, change password
  • Light / dark / system theme, persisted

Dashboard

  • Live stats (workspaces, boards, completed/pending tasks)
  • Boards-per-workspace chart (Recharts)

Polish

  • Route-level page transitions (fade + slide + scale)
  • 3D pointer-tilt on board & task cards
  • Code-split bundle, skeleton loading states
  • Reduced-motion support, keyboard-accessible

🛠 Tech Stack

Frontend

Framework React 19 + TypeScript + Vite
Styling Tailwind CSS v4
Animation Framer Motion
Routing React Router v7
Server state TanStack Query
Client state Zustand
Forms React Hook Form + Zod
Drag & drop DnD Kit
Real-time Socket.io Client
Charts Recharts
Testing Vitest + Testing Library

Backend

Runtime Node.js + Express 5
Database MongoDB + Mongoose
Real-time Socket.io
Auth JWT (httpOnly cookies) + bcrypt
File storage Cloudinary (Multer memory storage → stream upload)
Validation express-validator
Security Helmet, express-rate-limit, hand-rolled NoSQL-injection sanitizer
Testing Vitest + Supertest

Every library here was picked deliberately, not by default — Zustand over Redux for the smaller footprint this app's scope needs, TanStack Query so server state is never duplicated into Zustand, DnD Kit over react-beautiful-dnd because it's actively maintained, Cloudinary so uploads never touch the app server's disk.

📸 Screenshots

Add screenshots here after your first deploy — drop PNGs into docs/assets/screenshots/ and reference them like the row below. Suggested shots: Dashboard, Board view (mid-drag), Task modal, live cursors with two browser windows side by side, Login/Register.

🏗 Architecture

flowchart LR
    Browser["Browser<br/>React SPA"]
    API["Express API<br/>REST"]
    WS["Socket.io<br/>WebSocket server"]
    DB[("MongoDB Atlas")]
    CDN["Cloudinary<br/>media storage"]

    Browser -- "HTTPS / REST" --> API
    Browser <-- "WebSocket" --> WS
    API --> DB
    WS -. "relays events only<br/>REST is source of truth" .-> Browser
    API -- "upload stream" --> CDN
Loading

Every mutation goes Component → Service → REST API → MongoDB first; the socket layer is a pure relay that broadcasts the confirmed change to everyone else in the room, so there's a single source of truth and no risk of the UI diverging from the database.

📁 Project Structure

devsync/
├── backend/
│   └── src/
│       ├── config/         # db + cloudinary setup
│       ├── controllers/    # request handlers
│       ├── middleware/     # auth, upload, sanitize
│       ├── models/         # Mongoose schemas
│       ├── routes/         # Express routers
│       ├── services/       # business logic
│       ├── socket/         # Socket.io relay layer
│       ├── validators/     # express-validator chains
│       └── server.js
├── frontend/
│   └── src/
│       ├── components/     # shared UI (Button, Modal, layout shells...)
│       ├── hooks/          # useBoardData, useBoardSocket, useAuth...
│       ├── pages/          # route-level feature folders
│       ├── services/       # API + socket client layer
│       ├── store/          # Zustand (auth, theme, ui — UI state only)
│       ├── types/          # shared TS types
│       └── utils/
├── docs/assets/             # README images (logo, screenshots)
├── backend/render.yaml      # Render deploy config
└── frontend/vercel.json     # Vercel SPA rewrite config

Server state (boards, tasks, comments...) lives in TanStack Query; only UI/session state (auth, theme, sidebar) lives in Zustand. Components never call the API directly — everything routes through a services/ layer, and no socket event is ever emitted directly from a component.

🚀 Getting Started

Prerequisites

  • Node.js 20+
  • A MongoDB instance (local, or a free Atlas cluster)
  • A free Cloudinary account (for avatar/attachment uploads)

1. Clone

git clone https://github.com/<your-username>/devsync.git
cd devsync

2. Backend

cd backend
cp .env.example .env    # fill in the values — see table below
npm install
npm run dev              # http://localhost:5000

3. Frontend

cd frontend
cp .env.example .env     # defaults already point at localhost:5000
npm install
npm run dev               # http://localhost:5173

Open http://localhost:5173, register an account, and you're in.

🔑 Environment Variables

backend/.env

Variable Description
NODE_ENV development locally, production when deployed
PORT Port the API listens on (Render sets this itself)
CLIENT_URL Frontend origin, for CORS + cookie config
MONGO_URI MongoDB connection string
JWT_SECRET Long random string for signing auth tokens
JWT_EXPIRES_IN Token lifetime, e.g. 7d
CLOUDINARY_CLOUD_NAME From your Cloudinary dashboard
CLOUDINARY_API_KEY From your Cloudinary dashboard
CLOUDINARY_API_SECRET From your Cloudinary dashboard

frontend/.env

Variable Description
VITE_API_BASE_URL Backend REST base, e.g. http://localhost:5000/api
VITE_SOCKET_URL Backend socket origin, e.g. http://localhost:5000

📜 Available Scripts

Backend

Command What it does
npm run dev Start with nodemon (auto-restart)
npm start Start for production
npm test Run the Vitest + Supertest suite

Frontend

Command What it does
npm run dev Start the Vite dev server
npm run build Type-check (tsc -b) + production build
npm run preview Serve the production build locally
npm test Run the Vitest + Testing Library suite
npm run lint Lint with oxlint

🧪 Testing

cd backend && npm test    # health check, auth validation, auth-guard paths
cd frontend && npm test   # utils, shared UI components

Current coverage is smoke-level (validated end to end by hand during development, including a full browser pass with real drag-and-drop, sockets, and modal interactions) — not exhaustive integration or E2E coverage. See "Known Limitations" below.

☁️ Deployment

DevSync deploys as two independent services plus two managed cloud dependencies. Both deploy configs already live in the repo — connect them and fill in secrets.

1. MongoDB Atlas (database)

  1. Create a free cluster at mongodb.com/atlas
  2. Create a database user + password
  3. Network Access → allow 0.0.0.0/0 (or Render's static outbound IPs for tighter security)
  4. Copy the mongodb+srv://... connection string → this becomes MONGO_URI

2. Cloudinary (media storage)

  1. Create a free account at cloudinary.com
  2. Copy Cloud name, API Key, API Secret from the dashboard

3. Backend → Render

  1. New Web Service → connect this repo → Render auto-detects backend/render.yaml
  2. Fill in the env vars it leaves blank: CLIENT_URL, MONGO_URI, CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET (JWT_SECRET auto-generates)
  3. Deploy — health check is /api/health

4. Frontend → Vercel

  1. New Project → connect this repo → set root directory to frontend
  2. Vercel auto-detects frontend/vercel.json (handles the SPA rewrite so client-side routes survive a refresh)
  3. Set env vars: VITE_API_BASE_URL=https://<your-render-service>.onrender.com/api and VITE_SOCKET_URL=https://<your-render-service>.onrender.com
  4. Deploy

5. Close the loop

Go back to Render and set CLIENT_URL to your live Vercel URL, then redeploy the backend once. Cross-domain auth cookies are already handled — generateToken.js switches to sameSite: 'none', secure: true automatically once NODE_ENV=production.

📡 API Reference

All routes are prefixed with /api. Full request/response contracts are in each *.controller.js.

Auth/auth
Method Path Description
POST /register Create account
POST /login Log in
POST /logout Clear session
GET /profile Current user
PUT /profile Update name / avatar
PUT /password Change password
POST /forgot-password Request reset link
POST /reset-password Complete reset
Workspaces/workspaces
Method Path Description
POST / Create workspace
GET / List my workspaces
GET /:id Get workspace
PUT /:id Update workspace
DELETE /:id Delete workspace
POST /:id/invite Invite member
DELETE /:id/members/:memberId Remove member
POST /:id/leave Leave workspace
Boards/boards
Method Path Description
POST / Create board
GET /workspace/:workspaceId List boards in workspace
GET /:id Get board
PUT /:id Update board
DELETE /:id Delete board (cascades columns/tasks)
POST /:id/favorite Toggle favorite
POST /:id/labels Add label
DELETE /:id/labels/:labelId Remove label
Columns/columns
Method Path Description
POST / Create column
POST /reorder Reorder columns
GET /board/:boardId List columns
PUT /:id Rename column
DELETE /:id Delete column
Tasks/tasks
Method Path Description
POST / Create task
GET /board/:boardId List tasks on board
GET /:id Get task
PUT /:id Update task
PATCH /:id/move Move between columns / reorder
DELETE /:id Delete task
POST /:id/checklist Add checklist item
PATCH /:id/checklist/:itemId Toggle checklist item
DELETE /:id/checklist/:itemId Remove checklist item
POST /:id/attachments Upload attachment
DELETE /:id/attachments/:attachmentId Remove attachment
Comments, Notifications, Activity, Search
Method Path Description
POST /comments/task/:taskId Add comment
GET /comments/task/:taskId List comments
PUT /comments/:id Edit comment
DELETE /comments/:id Delete comment
GET /notifications List notifications
PUT /notifications/read-all Mark all read
PUT /notifications/:id/read Mark one read
GET /activity/board/:boardId Board activity log
GET /search?q= Global search

Socket Events

Client emits Server broadcasts
join-board, leave-board user-online, user-offline
task-created, task-updated, task-deleted, task-moved task-updated
column-created, column-updated column-updated
cursor-move, typing presence-update
join-user-room new-notification

Sockets are a relay layer only — every mutation persists via REST first, then broadcasts. See backend/src/socket/index.js.

🗺 Known Limitations / Roadmap

Deliberately out of scope for this pass:

  • No transactional email provider — password reset link is logged server-side / returned in dev
  • No Google OAuth
  • No per-type notification preferences (all types fire; no backend schema for opt-out yet)
  • Smoke-level automated tests only — no live-DB integration tests or E2E suite yet
  • No virtualized lists — untested at very large (100+ tasks/column) scale

🤝 Contributing

This started as a solo/portfolio build, but issues and PRs are welcome. A few conventions worth knowing before opening a PR: components stay under ~200 lines, server data never goes in Zustand, API calls always go through services/, and Framer Motion animations stay under 400ms.

📄 License

No license file yet — all rights reserved by default until one is added. If you want this open for reuse, MIT is the usual choice for a project like this.


Built with ❤️ to feel like a product, not a project.

About

Real-time collaborative Kanban workspace — React, Express, MongoDB, and Socket.io, built to feel like a commercial SaaS product (Linear/Notion/Figma inspired) rather than a tutorial clone.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages