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
| 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
- Check SMTP credentials in environment
- Verify SMTP host/port settings
- Check logs for error messages
Push notifications failing
- Verify Expo push tokens are valid
- Check Expo access token is set
- 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