Skip to content

escalated-dev/escalated-nestjs

Repository files navigation

العربيةDeutschEnglishEspañolFrançaisItaliano日本語한국어NederlandsPolskiPortuguês (BR)РусскийTürkçe简体中文

@escalated-dev/escalated-nestjs

Embedded helpdesk module for NestJS applications. Drop-in ticketing, SLA management, knowledge base, and more.

Features

  • Ticket Management -- Full CRUD with lifecycle tracking, priorities, departments, tags, and custom fields
  • SLA Policies -- Configurable response/resolution targets with business hours support
  • Automations -- Time-based processing via @nestjs/schedule (SLA checks, snooze wake-up, webhook retries)
  • Escalation Rules -- Automatic reassignment and notifications on SLA breach
  • Macros & Canned Responses -- One-click multi-action macros and templated replies
  • Custom Fields -- Dynamic fields with validation (text, number, select, checkbox, date)
  • Knowledge Base -- Articles with categories, search, view tracking, and helpfulness ratings
  • Webhooks -- HMAC-signed delivery with exponential backoff retry
  • API Tokens -- Bearer token authentication with scoped abilities
  • Roles & Permissions -- Granular permission system with NestJS guards
  • Audit Logging -- Interceptor-based activity tracking for all mutations
  • Import System -- Bulk import for tickets, tags, and departments
  • Side Conversations -- Threaded discussions within a ticket
  • Ticket Merging & Linking -- Merge duplicates, link related tickets
  • Ticket Splitting -- Break a ticket into separate issues
  • Ticket Snooze -- Snooze with automatic wake-up via cron
  • Saved Views -- Personal and shared filtered views
  • Widget API -- Public endpoints for embeddable support widget with rate limiting
  • Public Ticket System -- Unauthenticated submission via widget form or inbound email, Contact-based identity with email dedupe, configurable guest policy, signed Reply-To for threaded email conversations
  • Workflow Engine -- Admin-configured rules fire on ticket/reply events (conditions + actions: assign, tag, set priority/status/department, insert canned reply, etc.); evaluation logs per execution
  • Real-time Broadcasting -- Socket.IO gateway for live updates (opt-in)
  • Capacity Management -- Per-agent ticket limits with real-time tracking
  • Skill-based Routing -- Assign tickets based on agent skills and availability
  • CSAT Ratings -- Post-resolution satisfaction surveys with token-based submission
  • 2FA (TOTP) -- Two-factor authentication for agents via otplib
  • Guest Access -- Token-based ticket access without authentication
  • Internationalization -- Translations sourced from the central @escalated-dev/locale package via nestjs-i18n, with a chained overlay loader for plugin-local and host-app overrides (see src/i18n/overrides/README.md)

Requirements

  • Node.js 18+
  • NestJS 10+
  • TypeORM 0.3+
  • Any TypeORM-supported database (PostgreSQL, MySQL, SQLite, etc.)

Installation

npm install @escalated-dev/escalated-nestjs

Setup

1. Import the module

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { EscalatedModule } from '@escalated-dev/escalated-nestjs';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      database: 'myapp',
      username: 'user',
      password: 'pass',
      autoLoadEntities: true,
      synchronize: true, // disable in production
    }),
    EscalatedModule.forRoot({
      routePrefix: 'escalated',
      appName: 'My App',
      appUrl: 'https://myapp.com',
      enableWebsockets: false,
      enableKnowledgeBase: true,
      enableCsat: true,
      enable2fa: false,
    }),
  ],
})
export class AppModule {}

2. Configuration options

Option Type Default Description
routePrefix string 'escalated' URL prefix for all routes
enableWebsockets boolean false Enable Socket.IO real-time broadcasting
enableKnowledgeBase boolean true Enable KB articles and categories
enableCsat boolean true Enable satisfaction surveys
enable2fa boolean false Enable TOTP 2FA for agents
appName string 'Escalated' Branding name for emails
appUrl string -- Base URL for links
maxFileSize number 10485760 Max upload size in bytes
webhookMaxRetries number 3 Webhook retry attempts
widgetOrigins string[] ['*'] CORS origins for widget
adminGuard class -- Custom guard for admin routes
agentGuard class -- Custom guard for agent routes
customerGuard class -- Custom guard for customer routes
userResolver function -- Extract user from request
mail object -- Outbound email config (see below)
inbound object -- Inbound email config (see below)
guestPolicy object unassigned Guest identity policy (see below)

Outbound email (mail)

EscalatedModule.forRoot({
  mail: {
    from: 'support@example.com',
    transport: {
      host: 'smtp.example.com',
      port: 587,
      auth: { user: 'x', pass: 'y' },
    },
  },
});

When mail is absent, the MailerModule is not registered and EmailService silently no-ops — modules boot cleanly without a transport.

Inbound email (inbound)

EscalatedModule.forRoot({
  inbound: {
    replyDomain: 'reply.example.com',   // domain in our Message-ID + Reply-To
    replySecret: '32-byte-hex-secret',  // HMAC key for signed reply-to
    webhookSecret: 'shared-secret',     // X-Escalated-Inbound-Secret header value
    provider: 'postmark',               // parser adapter
  },
});

Webhook endpoint: POST /escalated/webhook/email/inbound. Guard requires X-Escalated-Inbound-Secret header to match webhookSecret (constant-time compare).

Guest policy (guestPolicy)

Controls the identity assigned to a ticket submitted via the public form or inbound email.

// Default: no host-app user, contact carries identity
{ mode: 'unassigned' }

// Assign all guest tickets to a shared "guest" user in the host app
{ mode: 'guest_user', guestUserId: 42 }

// Ticket stays unassigned until the guest accepts a signup invite
{ mode: 'prompt_signup', signupUrlTemplate: 'https://app.example.com/signup?token={token}' }

Admins can override at runtime via PUT /escalated/admin/settings:

{ "key": "guest_policy", "type": "json", "value": { "mode": "guest_user", "guestUserId": 99 } }

3. Database migration

With synchronize: true, TypeORM auto-creates tables. For production, generate migrations:

npx typeorm migration:generate -n EscalatedSetup
npx typeorm migration:run

All tables are prefixed with escalated_ to avoid conflicts.

API Endpoints

Agent Routes (/escalated/agent/)

Method Path Description
GET /tickets List tickets with filters
POST /tickets Create ticket
GET /tickets/:id Show ticket with replies
PUT /tickets/:id Update ticket
DELETE /tickets/:id Delete ticket
POST /tickets/:id/replies Add reply
POST /tickets/:id/merge/:targetId Merge tickets
POST /tickets/:id/split Split ticket
POST /tickets/:id/snooze Snooze ticket
GET /tickets/:ticketId/links List ticket links
POST /tickets/:ticketId/links Link tickets
GET /tickets/:ticketId/side-conversations List side conversations
POST /tickets/:ticketId/side-conversations Create side conversation
GET /macros List macros
POST /macros/:macroId/execute/:ticketId Execute macro
GET /canned-responses List canned responses
GET /saved-views List saved views
POST /saved-views Create saved view

Admin Routes (/escalated/admin/)

Method Path Description
GET/PUT /settings Manage settings
CRUD /departments Manage departments
CRUD /tags Manage tags
CRUD /custom-fields Manage custom fields
CRUD /roles Manage roles
CRUD /sla/policies Manage SLA policies
CRUD /sla/escalation-rules Manage escalation rules
CRUD /sla/schedules Manage business schedules
CRUD /webhooks Manage webhooks
CRUD /api-tokens Manage API tokens
CRUD /agents Manage agent profiles
CRUD /macros Manage macros
CRUD /canned-responses Manage canned responses
CRUD /kb/categories Manage KB categories
CRUD /kb/articles Manage KB articles
POST /import/tickets Bulk import tickets
POST /2fa/generate Generate 2FA secret
POST /2fa/enable Enable 2FA
GET /audit-logs View audit logs

Customer Routes (/escalated/customer/)

Method Path Description
GET /tickets List own tickets
POST /tickets Create ticket
GET /tickets/:id View own ticket
POST /tickets/:id/replies Reply to own ticket
POST /tickets/:id/rate Submit CSAT rating
GET /kb/categories Browse KB categories
GET /kb/articles Browse KB articles
GET /kb/search Search KB

Widget Routes (/escalated/widget/)

Method Path Description
POST /tickets Create ticket (public)
GET /tickets/:id View ticket (guest token)
POST /tickets/:id/replies Reply (guest token)
GET /kb/search Search KB
POST /rate/:token Submit CSAT

Using Services Directly

All services are exported and can be injected into your own code:

import { Injectable } from '@nestjs/common';
import { TicketService, AgentService } from '@escalated-dev/escalated-nestjs';

@Injectable()
export class MyService {
  constructor(
    private readonly ticketService: TicketService,
    private readonly agentService: AgentService,
  ) {}

  async assignToAvailableAgent(ticketId: number) {
    const agent = await this.agentService.findAvailableAgent();
    if (agent) {
      await this.ticketService.update(ticketId, { assigneeId: agent.userId }, 0);
    }
  }
}

Events

The module emits events via @nestjs/event-emitter:

import { OnEvent } from '@nestjs/event-emitter';
import { ESCALATED_EVENTS, TicketCreatedEvent } from '@escalated-dev/escalated-nestjs';

@Injectable()
export class NotificationService {
  @OnEvent(ESCALATED_EVENTS.TICKET_CREATED)
  handleTicketCreated(event: TicketCreatedEvent) {
    // Send notification, update external system, etc.
  }
}

Events: TICKET_CREATED, TICKET_UPDATED, TICKET_ASSIGNED, TICKET_STATUS_CHANGED, TICKET_REPLY_CREATED, TICKET_MERGED, TICKET_SPLIT, SLA_BREACHED.

Real-time Updates

Enable WebSocket broadcasting for live ticket updates:

EscalatedModule.forRoot({
  enableWebsockets: true,
});

Client-side (Socket.IO):

const socket = io('/escalated');
socket.emit('join:ticket', { ticketId: 1 });
socket.on('ticket:updated', (data) => console.log('Updated:', data));
socket.on('ticket:reply', (data) => console.log('New reply:', data));

Development

git clone https://github.com/escalated-dev/escalated-nestjs.git
cd escalated-nestjs
npm install --legacy-peer-deps
npm test
npm run build

TypeORM Entities

All 32 entities are exported and prefixed with escalated_:

Core: Ticket, TicketStatus, Reply, Attachment, TicketActivity, Tag, Department, TicketLink, SatisfactionRating

SLA: SlaPolicy, EscalationRule, BusinessSchedule, Holiday

Agents: AgentProfile, AgentCapacity, Skill

Messaging: CannedResponse, Macro, SideConversation, SideConversationReply

Admin: Role, Permission, ApiToken, Webhook, WebhookDelivery, AuditLog

Custom: CustomField, CustomFieldValue

Config: EscalatedSettings, SavedView

Knowledge Base: KbCategory, KbArticle

License

MIT

About

Escalated support ticket system for NestJS

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages