managarten/services/mana-notify/CLAUDE.md
Till-JS b5fa0f42b6 feat(mana-notify): add central notification service
NestJS notification microservice for email, push, Matrix, and webhook
notifications across all ManaCore apps.

Features:
- Multi-channel delivery (email, push, Matrix, webhook)
- Handlebars template engine with defaults
- User notification preferences
- BullMQ async job processing
- Delivery tracking and logging
- Prometheus metrics

Includes @manacore/notify-client package for NestJS integration.
2026-01-29 22:07:38 +01:00

12 KiB

Mana Notify Service

Central notification microservice for email, push, Matrix, and webhook notifications across all ManaCore apps.

Overview

  • Port: 3040
  • Technology: NestJS + BullMQ + Drizzle ORM + PostgreSQL + Redis
  • Purpose: Unified notification API with template support and delivery tracking

Architecture

┌─────────────────────────────────────────────────────────────┐
│                 Consumer Apps                                │
│   Auth │ Calendar │ Chat │ Picture │ Zitare │ ...          │
└─────────────────────────┬───────────────────────────────────┘
                          ▼
┌─────────────────────────────────────────────────────────────┐
│              mana-notify (Port 3040)                         │
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │ Notification│  │ Template    │  │ Preferences │         │
│  │ API         │  │ Engine      │  │ Manager     │         │
│  └──────┬──────┘  └──────┬──────┘  └─────────────┘         │
│         │                │                                  │
│         ▼                ▼                                  │
│  ┌──────────────────────────────────────────────┐          │
│  │           BullMQ Job Queues                   │          │
│  │  Email │ Push │ Matrix │ Webhook              │          │
│  └──────────────────────────────────────────────┘          │
│         │         │         │         │                    │
│         ▼         ▼         ▼         ▼                    │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐          │
│  │ Brevo   │ │ Expo    │ │ Matrix  │ │ HTTP    │          │
│  │ SMTP    │ │ Push    │ │ API     │ │ Client  │          │
│  └─────────┘ └─────────┘ └─────────┘ └─────────┘          │
└─────────────────────────────────────────────────────────────┘

Quick Start

Development

# 1. Start PostgreSQL and Redis (from monorepo root)
pnpm docker:up

# 2. Install dependencies
pnpm install

# 3. Push database schema
pnpm db:push

# 4. Start in development mode
pnpm dev

Production

pnpm build
pnpm start

API Endpoints

Notifications (Service-Key Auth: X-Service-Key header)

Method Endpoint Description
POST /api/v1/notifications/send Send notification immediately
POST /api/v1/notifications/schedule Schedule notification for later
POST /api/v1/notifications/batch Send multiple notifications
GET /api/v1/notifications/:id Get notification status
DELETE /api/v1/notifications/:id Cancel pending notification

Templates (Service-Key Auth)

Method Endpoint Description
GET /api/v1/templates List all templates
GET /api/v1/templates/:slug Get template by slug
POST /api/v1/templates Create custom template
PUT /api/v1/templates/:slug Update template
DELETE /api/v1/templates/:slug Delete custom template
POST /api/v1/templates/:slug/preview Preview rendered template
POST /api/v1/templates/preview Preview custom template

Devices (JWT Auth: Bearer token)

Method Endpoint Description
POST /api/v1/devices/register Register push device
GET /api/v1/devices List user's devices
DELETE /api/v1/devices/:id Unregister device

Preferences (JWT Auth)

Method Endpoint Description
GET /api/v1/preferences Get user preferences
PUT /api/v1/preferences Update preferences

System

Method Endpoint Description
GET /health Health check
GET /metrics Prometheus metrics

Usage Examples

Send Email

curl -X POST http://localhost:3040/api/v1/notifications/send \
  -H "Content-Type: application/json" \
  -H "X-Service-Key: your-service-key" \
  -d '{
    "channel": "email",
    "appId": "auth",
    "template": "auth-password-reset",
    "recipient": "user@example.com",
    "data": {
      "resetUrl": "https://mana.how/reset?token=xxx",
      "userName": "Max"
    }
  }'

Send Push Notification

curl -X POST http://localhost:3040/api/v1/notifications/send \
  -H "Content-Type: application/json" \
  -H "X-Service-Key: your-service-key" \
  -d '{
    "channel": "push",
    "appId": "calendar",
    "userId": "user-uuid",
    "subject": "Erinnerung",
    "body": "Meeting in 15 Minuten",
    "data": { "eventId": "event-uuid" }
  }'

Register Device (User JWT)

curl -X POST http://localhost:3040/api/v1/devices/register \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $USER_JWT" \
  -d '{
    "pushToken": "ExponentPushToken[xxx]",
    "platform": "ios",
    "deviceName": "iPhone 15"
  }'

Schedule Notification

curl -X POST http://localhost:3040/api/v1/notifications/schedule \
  -H "Content-Type: application/json" \
  -H "X-Service-Key: your-service-key" \
  -d '{
    "channel": "email",
    "appId": "calendar",
    "template": "calendar-reminder",
    "recipient": "user@example.com",
    "data": {
      "eventTitle": "Team Meeting",
      "eventTime": "14:00 Uhr",
      "eventUrl": "https://calendar.mana.how/event/xxx"
    },
    "scheduledFor": "2024-12-20T13:45:00Z"
  }'

Environment Variables

Variable Default Description
PORT 3040 API port
DATABASE_URL - PostgreSQL connection URL
REDIS_HOST localhost Redis host for BullMQ
REDIS_PORT 6379 Redis port
SERVICE_KEY dev-service-key Internal service authentication key
MANA_CORE_AUTH_URL http://localhost:3001 Auth service URL for JWT validation
SMTP_HOST smtp-relay.brevo.com SMTP server host
SMTP_PORT 587 SMTP server port
SMTP_USER - SMTP username
SMTP_PASSWORD - SMTP password
SMTP_FROM ManaCore noreply@mana.how Default sender address
EXPO_ACCESS_TOKEN - Expo push notification access token
MATRIX_HOMESERVER_URL - Matrix homeserver URL
MATRIX_ACCESS_TOKEN - Matrix bot access token
RATE_LIMIT_EMAIL_PER_MINUTE 10 Email rate limit
RATE_LIMIT_PUSH_PER_MINUTE 100 Push notification rate limit

Development Commands

# Install dependencies
pnpm install

# Start development server
pnpm dev

# Build for production
pnpm build

# Start production server
pnpm start

# Type checking
pnpm type-check

# Linting
pnpm lint

# Database commands
pnpm db:push       # Push schema to database
pnpm db:generate   # Generate migrations
pnpm db:migrate    # Run migrations
pnpm db:studio     # Open Drizzle Studio

Database Schema

The service uses its own schema (notify) in the shared ManaCore database:

  • notify.notifications - Notification records with status tracking
  • notify.templates - Email/push templates with Handlebars support
  • notify.devices - Registered push notification devices
  • notify.preferences - User notification preferences
  • notify.delivery_logs - Delivery attempt logs

Default Templates

Slug Channel Purpose
auth-password-reset email Password reset email
auth-verification email Email verification
auth-welcome email Welcome email
calendar-reminder email Calendar event reminder

Notification Channels

Email (Brevo SMTP)

  • Uses Nodemailer with Brevo SMTP relay
  • Supports HTML and plain text
  • Template rendering with Handlebars

Push (Expo)

  • Uses Expo Server SDK
  • Supports iOS, Android, and web
  • Batch sending with automatic chunking

Matrix

  • Direct Matrix API integration
  • Supports formatted (HTML) messages
  • For bot notifications

Webhook

  • HTTP POST/PUT to external URLs
  • Configurable headers and timeout
  • Retry with exponential backoff

Integration with Other Services

Usage from NestJS Backend

// Direct HTTP call
const response = await fetch('http://mana-notify:3040/api/v1/notifications/send', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Service-Key': process.env.MANA_NOTIFY_SERVICE_KEY,
  },
  body: JSON.stringify({
    channel: 'email',
    appId: 'calendar',
    template: 'calendar-reminder',
    recipient: user.email,
    data: { eventTitle, eventTime },
  }),
});

Using the Client SDK

import { NotifyClient } from '@manacore/notify-client';

const notify = new NotifyClient({
  serviceUrl: 'http://localhost:3040',
  serviceKey: process.env.MANA_NOTIFY_SERVICE_KEY,
  appId: 'calendar',
});

// Send email
await notify.sendEmail({
  to: 'user@example.com',
  template: 'calendar-reminder',
  data: { eventTitle: 'Meeting', eventTime: '14:00' },
});

// Send push to user
await notify.sendPush({
  userId: 'user-uuid',
  title: 'Reminder',
  body: 'Meeting in 15 minutes',
  data: { eventId: 'xxx' },
});

Project Structure

services/mana-notify/
├── src/
│   ├── main.ts                    # Application entry point
│   ├── app.module.ts              # Root module
│   ├── config/
│   │   └── configuration.ts       # App configuration
│   ├── db/
│   │   ├── schema/                # Drizzle schemas
│   │   ├── database.module.ts     # Database provider
│   │   └── connection.ts          # DB connection
│   ├── common/
│   │   ├── filters/               # Exception filters
│   │   └── guards/                # Auth guards
│   ├── queue/
│   │   ├── queue.module.ts        # BullMQ setup
│   │   └── processors/            # Channel processors
│   ├── channels/
│   │   ├── email/                 # Nodemailer service
│   │   ├── push/                  # Expo push service
│   │   ├── matrix/                # Matrix API service
│   │   └── webhook/               # HTTP webhook service
│   ├── notifications/             # Core notification API
│   ├── templates/                 # Template engine
│   │   └── defaults/              # Default HBS templates
│   ├── devices/                   # Device registration
│   ├── preferences/               # User preferences
│   ├── health/                    # Health check
│   └── metrics/                   # Prometheus metrics
├── drizzle.config.ts
├── package.json
├── tsconfig.json
├── Dockerfile
└── CLAUDE.md

Troubleshooting

Email not sending

  1. Check SMTP credentials in environment
  2. Verify SMTP host/port settings
  3. Check logs for error messages

Push notifications failing

  1. Verify Expo push tokens are valid
  2. Check Expo access token is set
  3. Ensure devices are registered

Redis connection issues

# Check Redis
docker exec mana-notify-redis-dev redis-cli ping

# Check queue status
curl http://localhost:3040/health

Metrics

Available at /metrics in Prometheus format:

  • mana_notify_notifications_sent_total - Total notifications sent
  • mana_notify_notifications_failed_total - Total failed notifications
  • mana_notify_emails_sent_total - Emails sent by template
  • mana_notify_push_sent_total - Push notifications by platform
  • mana_notify_notification_latency_seconds - Processing latency