Skip to content

Introduction

Planner is a full-featured project management platform built with 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 docs (port 8081)
├── packages/
│   ├── shared-types/    # @planner/shared-types
│   ├── shared-utils/    # @planner/shared-utils
│   └── ui-kit/          # @planner/ui-kit
├── docker/              # Dockerfiles for each app
├── docker-compose.yml   # Development compose
├── docker-compose.prod.yml  # Production compose
├── turbo.json           # Turborepo pipeline config
├── pnpm-workspace.yaml
└── package.json

Frontend Architecture (Vue 3)

  • Routing: Vue Router with lazy-loaded views, nested layouts (AdminLayout, OrgLayout, TeamLayout)
  • State: Pinia setup stores with storeToRefs for reactive bindings
  • UI: PrimeVue 4 with Aura theme (emerald primary, slate surface)
  • Real-time: Socket.IO client connects on auth, joins user-specific rooms
  • i18n: vue-i18n@9 with Composition API, EN/RU locales persisted to localStorage

Backend Architecture (NestJS)

  • Modules: Feature-based modules (Auth, Users, Organizations, Teams, Projects, Tasks, Comments, Chat, Notifications, TaskHistory)
  • Auth: JWT access tokens (15min) + refresh tokens (7d) with passport-jwt strategy
  • Permissions: Custom @Permissions() decorator + PermissionsGuard — checks requiredPermissions against user's effective permissions
  • Real-time: NotificationsGateway (namespace /notifications) — emits to user:<id> rooms
  • Database: PostgreSQL with Prisma ORM, migrations via prisma db push

Key Design Decisions

Notifications Module

  • @Global() — all services inject NotificationsService directly
  • NotificationsService.create() is fire-and-forget (not awaited) to avoid slowing main operations
  • WebSocket gateway (/notifications namespace) authenticates via handshake.auth.token using JwtService

Tasks — Project-scoped

  • projectId on Task model is required with cascade delete from Project
  • CRUD via ProjectTasksController at /projects/:id/tasks (PROJECT scope permissions)
  • Task comments via ProjectTaskCommentsController at /projects/:id/tasks/:taskId/comments
  • Task history logged automatically on create/update/status change/assign/delete

Permissions System

  • 30+ granular permissions across 6 scopes: GLOBAL, ORGANIZATION, TEAM, PROJECT, TASK, CHAT
  • Custom roles can combine any permission set
  • User groups for bulk role assignment
  • Computed permissions aggregate from user's roles at global, org, team, and project levels