Introduction
Planner is a full-featured project management platform built on a modern TypeScript stack. It supports multi-tenant organizations, teams, projects, tasks with a rich lifecycle, real-time chat, WebSocket notifications, and granular role-based access control.
Architecture
Monorepo Structure
The project uses pnpm workspaces with Turborepo for build orchestration:
planner/
├── apps/
│ ├── backend/ # NestJS API (port 4000)
│ ├── frontend/ # Vue 3 SPA (port 3000)
│ ├── website/ # Nuxt 4 marketing site (port 8080)
│ └── docs/ # VitePress documentation (port 8081)
├── mobile/ # Mobile apps (separate repo)
│ ├── ios/ # iOS (Swift)
│ └── android/ # Android (Kotlin)
├── packages/
│ ├── shared-types/ # @planner/shared-types
│ ├── shared-utils/ # @planner/shared-utils
│ ├── shared-i18n/ # @planner/shared-i18n
│ └── ui-kit/ # @planner/ui-kit
├── docker/ # Dockerfiles for each app
├── docker-compose.yml # Docker Compose for development
├── docker-compose.prod.yml # Docker Compose for production
├── turbo.json # Turborepo pipeline config
├── pnpm-workspace.yaml
└── package.jsonFrontend Architecture (Vue 3)
- Routing: Vue Router with lazy-loaded views, nested layouts (AdminLayout, OrgLayout, TeamLayout)
- State: Pinia setup stores with
storeToRefsfor reactive bindings - UI: PrimeVue 4 with Aura theme (emerald primary, gray surface)
- Real-time: Socket.IO client connects on auth, joins user rooms
- i18n: vue-i18n@9 with Composition API, EN/RU locales stored in localStorage
Backend Architecture (NestJS)
- Modules: Feature-based modules (Auth, Users, Organizations, Teams, Projects, Tasks, Comments, Chat, Notifications, TaskHistory, Invitations, Events, VideoChat, RBAC, Quotas, Plans, Storage, Email, Health, Admin)
- Auth: JWT access token (15 min) + refresh token (7 days) with passport-jwt strategy. Email verification required before first login. Refresh tokens stored as SHA-256 hash with rotation and reuse detection
- Security headers: CSP, HSTS (production), CORS whitelist via helmet
- Rate limiting: Per-endpoint limits (register 5/min, login 10/min, etc.) + global 60/min via Redis. Account lockout after 5 failed attempts
- Password policy: 8-128 chars, requires uppercase + lowercase + digit + special character, hashed with bcrypt (cost 10)
- Permissions: Custom
@Permissions()decorator +PermissionsGuard— checksrequiredPermissionsagainst user's effective permissions - Real-time:
NotificationsGateway(namespace/notifications) — sends touser:<id>rooms - Database: PostgreSQL with Prisma ORM,
prisma migratefor production,prisma db pushfor local dev - Swagger UI: Auto-generated API documentation available at
/api/docs(development only)
Key Design Decisions
Notifications Module
@Global()— all services injectNotificationsServicedirectlyNotificationsService.create()— fire-and-forget (not awaited) to avoid slowing main operations- WebSocket gateway (
/notifications) authenticates viahandshake.auth.tokenusingJwtService
Tasks — Project-scoped
projectIdonTaskmodel is required with cascade delete from Project- CRUD via
ProjectTasksControllerat/projects/:id/tasks(PROJECT scope permissions) - Task comments via
ProjectTaskCommentsControllerat/projects/:id/tasks/:taskId/comments - Task history is automatically logged on create/update/status change/assign/delete
Permission System
- 30+ granular permissions across 6 scopes: GLOBAL, ORGANIZATION, TEAM, PROJECT, TASK, CHAT
- Custom roles can combine any set of permissions
- User groups for bulk role assignment
- Effective permissions are aggregated from user's roles at global, org, team, and project levels