@flusys/nestjs-task-manager
v5.4.0
Published
Task management module with dynamic boards and real-time collaboration
Maintainers
Readme
@flusys/nestjs-task-manager
Task management module for NestJS — dynamic boards, fully user-defined lists (no hardcoded statuses), real-time collaboration via Socket.IO WebSocket, board-scoped membership and permission model, and full activity audit log.
Design Decisions
- Board-first architecture — A
TaskBoardholds company context.TaskListandTaskcarry only aboardId; nocompanyIdis needed on them. - Fully dynamic lists — Users create their own lists (columns) with any name, color, and position. There are no hardcoded status enums.
- Board-member assignee restriction — Assignees are validated against
TaskBoardMemberat write time (both service layer and frontend). - Real-time —
TaskBoardGatewaybroadcasts all mutations to every client in aboard:{boardId}Socket.IO room. - Append-only activity log — Every mutation (create, move, assign, comment) writes to
TaskActivity— never updated or deleted.
Installation
npm install @flusys/nestjs-task-manager @flusys/nestjs-shared @flusys/nestjs-core
npm install @nestjs/platform-socket.io socket.io jsonwebtoken1. Module Registration
forRoot (sync)
import { TaskManagerModule } from '@flusys/nestjs-task-manager';
@Module({
imports: [
TaskManagerModule.forRoot({
global: true,
includeController: true,
bootstrapAppConfig: {
databaseMode: 'single',
enableCompanyFeature: true,
},
config: {
jwtSecret: process.env.JWT_SECRET,
defaultDatabaseConfig: {
type: 'postgres',
host: process.env.DB_HOST,
port: Number(process.env.DB_PORT ?? 5432),
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
},
},
}),
],
})
export class AppModule {}forRootAsync (factory)
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TaskManagerModule } from '@flusys/nestjs-task-manager';
TaskManagerModule.forRootAsync({
global: true,
includeController: true,
bootstrapAppConfig: {
databaseMode: 'single',
enableCompanyFeature: true,
},
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
jwtSecret: config.get('JWT_SECRET'),
defaultDatabaseConfig: {
type: 'postgres',
host: config.get('DB_HOST'),
port: config.get<number>('DB_PORT'),
username: config.get('DB_USER'),
password: config.get('DB_PASSWORD'),
database: config.get('DB_NAME'),
},
}),
inject: [ConfigService],
});2. Register Entities
import { getTaskManagerEntitiesByConfig } from '@flusys/nestjs-task-manager/entities';
TypeOrmModule.forRoot({
entities: [
...getTaskManagerEntitiesByConfig(true), // pass enableCompanyFeature
],
});| enableCompanyFeature | Entities registered |
|---|---|
| false | TaskBoard, TaskBoardMember, TaskList, Task, TaskLabel, TaskTaskLabel, TaskComment, TaskActivity |
| true | Same set + TaskBoardWithCompany (adds companyId column) |
3. Entities
| Entity | Table | Key Columns |
|---|---|---|
| TaskBoard | task_board | name, description, color, isActive, settings (JSON) |
| TaskBoardWithCompany | task_board | extends TaskBoard + companyId |
| TaskBoardMember | task_board_member | boardId, userId, role (OWNER/ADMIN/MEMBER/VIEWER) |
| TaskList | task_list | boardId, name, color, position, isDoneList |
| Task | task | boardId, listId, title, priority, dueDate, position, assigneeId, reporterId, parentTaskId |
| TaskLabel | task_label | boardId, name, color |
| TaskTaskLabel | task_task_label | taskId, labelId (junction) |
| TaskComment | task_comment | taskId, userId, content, parentCommentId |
| TaskActivity | task_activity | taskId, boardId, userId, action, oldValue, newValue — append-only |
4. API Endpoints
All endpoints are POST-only RPC. The base path is set by includeController: true.
Boards (/board)
| Endpoint | Description |
|---|---|
| POST /board/insert | Create a board; creator is auto-assigned as OWNER |
| POST /board/update | Update board metadata |
| POST /board/delete | Soft delete |
| POST /board/get/:id | Get board by ID |
| POST /board/get-all | Paginated list (filtered by companyId when enabled) |
| POST /board/add-member | Add a user to the board with a role |
| POST /board/remove-member | Remove a board member |
| POST /board/get-members | List all members of a board |
Lists (/task-list)
| Endpoint | Description |
|---|---|
| POST /task-list/insert | Create a list in a board |
| POST /task-list/update | Rename, recolor, or toggle isDoneList |
| POST /task-list/delete | Soft delete |
| POST /task-list/get-board-lists | All lists for a board, ordered by position |
| POST /task-list/reorder | Bulk-reorder lists (gap strategy: position = i * 100) |
Tasks (/task)
| Endpoint | Description |
|---|---|
| POST /task/insert | Create a task; assigneeId must be a board member |
| POST /task/update | Update task fields |
| POST /task/delete | Soft delete |
| POST /task/get-board-tasks | All tasks for a board with labels |
| POST /task/move | Move task to another list + set position |
| POST /task/assign | Assign task to a board member |
Labels (/task-label)
| Endpoint | Description |
|---|---|
| POST /task-label/insert | Create a board-scoped label |
| POST /task-label/update | Update label |
| POST /task-label/delete | Delete label |
| POST /task-label/get-board-labels | All labels for a board |
Comments (/task-comment)
| Endpoint | Description |
|---|---|
| POST /task-comment/insert | Add a comment (supports threading via parentCommentId) |
| POST /task-comment/update | Edit own comment (throws ForbiddenException otherwise) |
| POST /task-comment/delete | Soft delete |
| POST /task-comment/get-task-comments | All comments for a task, ordered by createdAt ASC |
5. WebSocket Gateway
Namespace: /task-boards
Auth: JWT verified on connection via Authorization header (uses jwtSecret from module config).
Client events (emit)
| Event | Payload | Description |
|---|---|---|
| board:join | { boardId: string } | Subscribe to real-time events for this board |
| board:leave | { boardId: string } | Unsubscribe |
Server events (listen)
| Event | Payload type | Emitted when |
|---|---|---|
| board:event | ITaskBoardSocketEvent | Any mutation on the board |
ITaskBoardSocketEvent
interface ITaskBoardSocketEvent {
type:
| 'task:created' | 'task:updated' | 'task:moved' | 'task:deleted'
| 'list:created' | 'list:updated' | 'list:deleted' | 'list:reordered';
boardId: string;
payload: unknown; // the updated entity or reorder array
}6. Board Member Roles
| Role | Capabilities |
|---|---|
| OWNER | Full control including deleting the board and managing members |
| ADMIN | Create/edit/delete tasks and lists, manage members |
| MEMBER | Create and edit tasks |
| VIEWER | Read-only access |
7. Services Reference
| Service | Scope | Key Methods |
|---|---|---|
| TaskBoardService | REQUEST | addMember(), removeMember(), getBoardMembers(), isBoardMember() |
| TaskListService | REQUEST | getListsForBoard(), reorderLists() |
| TaskService | REQUEST | getTasksForBoard(), moveTask(), assignTask() |
| TaskLabelService | REQUEST | getLabelsForBoard() |
| TaskCommentService | REQUEST | getCommentsForTask() |
| TaskActivityService | REQUEST | log(), getForTask() |
| TaskBoardGateway | singleton | emitToBoardRoom(boardId, event) |
License
MIT © FLUSYS
