Skip to content
11 changes: 11 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { AlertsModule } from "./alerts/alerts.module";
import { MetricsModule } from "./metrics/metrics.module";
import { AnalyticsModule } from "./analytics/analytics.module";
import { RateLimitModule } from "./quota/rate-limit.module";
import { NotificationsModule } from "./notifications/notifications.module";
import { MessagingModule } from "./messaging/messaging.module";

// Auth entities
Expand Down Expand Up @@ -65,6 +66,11 @@ import { AlertDeliveryLog } from "./alerts/entities/alert-delivery-log.entity";
import { AnalyticsEvent } from "./analytics/entities/analytics-event.entity";
import { DailyMetric } from "./analytics/entities/daily-metric.entity";

// Notifications entities
import { Notification } from "./notifications/entities/notification.entity";
import { NotificationDeliveryLog } from "./notifications/entities/notification-delivery-log.entity";
import { NotificationPreference } from "./notifications/entities/notification-preference.entity";

// Guards
import { ThrottlerUserIpGuard } from "./common/guard/throttler.guard";
import { RolesGuard } from "./common/guard/roles.guard";
Expand Down Expand Up @@ -122,6 +128,10 @@ import { QuotaGuard } from "./common/guard/quota.guard";
AlertDeliveryLog,
AnalyticsEvent,
DailyMetric,
// Notifications module entities
Notification,
NotificationDeliveryLog,
NotificationPreference,
Conversation,
Message,
UserPresence,
Expand Down Expand Up @@ -161,6 +171,7 @@ import { QuotaGuard } from "./common/guard/quota.guard";
MetricsModule,
AnalyticsModule,
RateLimitModule,
NotificationsModule,
MessagingModule,
],

Expand Down
200 changes: 200 additions & 0 deletions src/notifications/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
# Notifications Module

A comprehensive notifications system for StellAIverse backend supporting email, push, and in-app notifications with reliable delivery, retry logic, and comprehensive metrics.

## Features

### βœ… Core Capabilities
- **Unified API**: Create, list, and manage notifications through REST endpoints
- **Multiple Channels**: Email (SendGrid, Mailgun), Push (FCM, APNs), In-app
- **Delivery Guarantees**: BullMQ-backed queue with exponential backoff retry logic
- **Dead Letter Queue**: Automatic DLQ for permanently failed notifications
- **User Preferences**: Per-user channel and template preferences
- **Unread Count**: Real-time unread notification tracking
- **Queue Metrics**: Pending/processing/delivered/failed/DLQ counts
- **Test Mode**: Test provider integrations without sending real notifications
- **Rate Limiting**: Per-provider rate limits to avoid hitting API restrictions

## Architecture

```
src/notifications/
β”œβ”€β”€ controllers/ # API endpoints
β”‚ β”œβ”€β”€ notifications.controller.ts # Main notification CRUD
β”‚ └── notification-preferences.controller.ts # User preferences
β”œβ”€β”€ services/
β”‚ └── notifications.service.ts # Business logic
β”œβ”€β”€ providers/ # Channel providers
β”‚ β”œβ”€β”€ sendgrid.provider.ts # SendGrid email
β”‚ β”œβ”€β”€ mailgun.provider.ts # Mailgun email
β”‚ β”œβ”€β”€ fcm.provider.ts # Firebase Cloud Messaging
β”‚ β”œβ”€β”€ apns.provider.ts # Apple Push Notifications
β”‚ β”œβ”€β”€ in-app.provider.ts # In-app notifications
β”‚ └── provider-factory.service.ts # Provider resolution
β”œβ”€β”€ processors/ # Bull queue processors
β”‚ └── notification.processor.ts # Background processing
β”œβ”€β”€ entities/ # TypeORM entities
β”‚ β”œβ”€β”€ notification.entity.ts
β”‚ β”œβ”€β”€ notification-delivery-log.entity.ts
β”‚ β”œβ”€β”€ notification-preference.entity.ts
β”‚ └── notification.enums.ts
β”œβ”€β”€ dto/ # Validation DTOs
β”‚ β”œβ”€β”€ create-notification.dto.ts
β”‚ └── update-notification.dto.ts
β”œβ”€β”€ interfaces/ # TypeScript interfaces
β”‚ └── notification-provider.interface.ts
└── notifications.module.ts # Module definition
```

## API Endpoints

### Notifications
- `POST /notifications` - Create a new notification
- `GET /users/:id/notifications` - List user's notifications
- `GET /users/:id/notifications/unread-count` - Get unread count
- `GET /notifications/:id` - Get specific notification
- `PATCH /notifications/:id` - Update (mark read/archived)
- `POST /users/:id/notifications/mark-all-read` - Mark all as read
- `DELETE /notifications/:id` - Delete notification

### Queue Management
- `GET /notifications/queue/metrics` - Get queue metrics
- `POST /notifications/queue/retry` - Retry failed notifications

### Preferences
- `GET /users/:id/notification-preferences` - Get user preferences
- `PUT /users/:id/notification-preferences` - Update preferences

## Configuration

Add these variables to your `.env` file:

```env
# SendGrid (Email)
SENDGRID_API_KEY=your_api_key
SENDGRID_FROM_EMAIL=notifications@stellaiverse.com
SENDGRID_FROM_NAME=StellAIverse
SENDGRID_RATE_LIMIT=100

# Mailgun (Alternative Email)
MAILGUN_API_KEY=your_api_key
MAILGUN_DOMAIN=mg.stellaiverse.com
MAILGUN_FROM_EMAIL=notifications@mg.stellaiverse.com
MAILGUN_FROM_NAME=StellAIverse
MAILGUN_RATE_LIMIT=100

# FCM (Android Push)
FCM_SERVER_KEY=your_fcm_key
FCM_RATE_LIMIT=500

# APNs (iOS Push)
APNS_AUTH_KEY=your_apns_key
APNS_BUNDLE_ID=com.stellaiverse.app
APNS_RATE_LIMIT=500

# Redis (for Bull queue)
REDIS_HOST=localhost
REDIS_PORT=6379
```

## Usage Examples

### Create a Notification
```typescript
// Email notification
await notificationsService.create({
userId: 'user-uuid',
type: NotificationType.EMAIL,
channel: NotificationChannel.SENDGRID,
subject: 'Welcome to StellAIverse!',
content: '<h1>Welcome!</h1>...',
template: 'user_welcome',
templateData: { name: 'John' },
priority: NotificationPriority.HIGH,
recipient: 'user@example.com',
});

// Push notification
await notificationsService.create({
userId: 'user-uuid',
type: NotificationType.PUSH,
channel: NotificationChannel.FCM,
subject: 'Your portfolio is growing!',
content: '+12.5% ROI this week',
template: 'portfolio_update',
metadata: { pushTokens: ['fcm_token1', 'fcm_token2'] },
});

// In-app notification
await notificationsService.create({
userId: 'user-uuid',
type: NotificationType.IN_APP,
channel: NotificationChannel.INTERNAL,
subject: 'New transaction completed',
content: 'Your ETH transfer was successful',
template: 'transaction_complete',
});
```

## Retry Logic

Failed notifications are retried with exponential backoff:
- Max retries: 5 attempts
- Base delay: 1 second
- Max delay: 5 minutes
- Backoff formula: `1s * 2^retry_count`

## Delivery Tracking

Every delivery attempt is logged in `notification_delivery_logs` with:
- Success/failure status
- Error messages
- Provider response data
- Timestamps

## User Preferences

Users can opt in/out of channels:
```typescript
{
emailEnabled: boolean,
pushEnabled: boolean,
inAppEnabled: boolean,
channelPreferences: { email: { email: string } },
templatePreferences: { template_id: { enabled: boolean } }
}
```

## Metrics

Get real-time queue metrics:
```typescript
{
pending: 5, // Waiting to be processed
processing: 2, // Currently processing
delivered: 1234, // Successfully delivered
failed: 12, // Failed, will be retried
deadLetter: 3 // Permanently failed
}
```

## Testing

Run the notification module tests:
```bash
npm test -- notifications
```

## Integration Status

βœ… All core features implemented:
- Provider abstraction layer
- Queue processing with retries
- REST API with Swagger docs
- User preference system
- Metrics endpoints
- Test mode for all providers
- Rate limiting
- Delivery logging
- Dead letter queue
- Unread count tracking
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import {
Controller,
Get,
Put,
Body,
Param,
UseGuards,
ParseUUIDPipe,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
import { CurrentUser } from '../../auth/decorators/current-user.decorator';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { NotificationPreference } from '../entities/notification-preference.entity';

class UpdatePreferencesDto {
emailEnabled?: boolean;
pushEnabled?: boolean;
inAppEnabled?: boolean;
channelPreferences?: any;
templatePreferences?: any;
}

@ApiTags('notification-preferences')
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Controller('users/:id/notification-preferences')
export class NotificationPreferencesController {
constructor(
@InjectRepository(NotificationPreference)
private preferenceRepository: Repository<NotificationPreference>,
) {}

@Get()
@ApiOperation({ summary: 'Get notification preferences for a user' })
@ApiResponse({ status: 200, description: 'Preferences retrieved successfully' })
async getPreferences(
@Param('id', ParseUUIDPipe) userId: string,
@CurrentUser() currentUser: any,
) {
if (currentUser.id !== userId) {
throw new Error('Unauthorized to access these preferences');
}

let preferences = await this.preferenceRepository.findOne({
where: { userId },
});

if (!preferences) {
preferences = this.preferenceRepository.create({
userId,
emailEnabled: true,
pushEnabled: true,
inAppEnabled: true,
});
await this.preferenceRepository.save(preferences);
}

return preferences;
}

@Put()
@ApiOperation({ summary: 'Update notification preferences' })
@ApiResponse({ status: 200, description: 'Preferences updated successfully' })
async updatePreferences(
@Param('id', ParseUUIDPipe) userId: string,
@Body() updateDto: UpdatePreferencesDto,
@CurrentUser() currentUser: any,
) {
if (currentUser.id !== userId) {
throw new Error('Unauthorized to update these preferences');
}

let preferences = await this.preferenceRepository.findOne({
where: { userId },
});

if (!preferences) {
preferences = this.preferenceRepository.create({
userId,
...updateDto,
});
} else {
Object.assign(preferences, updateDto);
}

return this.preferenceRepository.save(preferences);
}
}
Loading
Loading