remove unused packages
This commit is contained in:
Generated
+15698
-16888
File diff suppressed because it is too large
Load Diff
+1
-5
@@ -69,14 +69,10 @@
|
|||||||
"class-validator": "^0.14.2",
|
"class-validator": "^0.14.2",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"fastify": "^5.6.1",
|
"fastify": "^5.6.1",
|
||||||
"firebase-admin": "^13.6.0",
|
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"sanitize-html": "^2.17.0",
|
|
||||||
"slugify": "^1.6.6",
|
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"ulid": "^3.0.1",
|
"ulid": "^3.0.1"
|
||||||
"uuid": "^13.0.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.2.0",
|
"@eslint/eslintrc": "^3.2.0",
|
||||||
|
|||||||
Generated
-14002
File diff suppressed because it is too large
Load Diff
@@ -1,19 +0,0 @@
|
|||||||
import { ApiProperty, PartialType } from "@nestjs/swagger";
|
|
||||||
import { IsNotEmpty, IsUUID } from "class-validator";
|
|
||||||
|
|
||||||
import { CommonMessage } from "../enums/message.enum";
|
|
||||||
|
|
||||||
export class ParamDto {
|
|
||||||
@IsNotEmpty({ message: CommonMessage.ID_REQUIRED })
|
|
||||||
@IsUUID("4", { message: CommonMessage.ID_SHOULD_BE_UUID })
|
|
||||||
@ApiProperty({ description: "Id of the entity", example: "8b1e8b1e-8b1e-8b1e-8b1e-8b1e8b1e8b1e" })
|
|
||||||
id: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class OptionalParamDto extends PartialType(ParamDto) {}
|
|
||||||
|
|
||||||
export class ParamNumberIdDto {
|
|
||||||
@IsNotEmpty({ message: CommonMessage.ID_REQUIRED })
|
|
||||||
@ApiProperty({ description: "Id of the entity", example: "20" })
|
|
||||||
id: string;
|
|
||||||
}
|
|
||||||
@@ -5,9 +5,7 @@ import { JwtModule } from '@nestjs/jwt';
|
|||||||
import { Notification } from './entities/notification.entity';
|
import { Notification } from './entities/notification.entity';
|
||||||
import { NotificationService } from './services/notification.service';
|
import { NotificationService } from './services/notification.service';
|
||||||
import { NotificationQueueService } from './services/notification-queue.service';
|
import { NotificationQueueService } from './services/notification-queue.service';
|
||||||
import { PushNotificationService } from './services/push-notification.service';
|
|
||||||
import { SmsProcessor } from './processors/sms.processor';
|
import { SmsProcessor } from './processors/sms.processor';
|
||||||
import { PushProcessor } from './processors/push.processor';
|
|
||||||
import { NotificationsController } from './controllers/notifications.controller';
|
import { NotificationsController } from './controllers/notifications.controller';
|
||||||
import { User } from '../user/entities/user.entity';
|
import { User } from '../user/entities/user.entity';
|
||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
@@ -53,8 +51,6 @@ import { SmsListeners } from './listeners/sms.listeners';
|
|||||||
providers: [
|
providers: [
|
||||||
NotificationService,
|
NotificationService,
|
||||||
NotificationQueueService,
|
NotificationQueueService,
|
||||||
PushNotificationService,
|
|
||||||
PushProcessor,
|
|
||||||
SmsProcessor,
|
SmsProcessor,
|
||||||
InAppProcessor,
|
InAppProcessor,
|
||||||
NotificationsGateway,
|
NotificationsGateway,
|
||||||
@@ -65,6 +61,6 @@ import { SmsListeners } from './listeners/sms.listeners';
|
|||||||
SmsListeners,
|
SmsListeners,
|
||||||
],
|
],
|
||||||
exports: [NotificationService, NotificationQueueService,
|
exports: [NotificationService, NotificationQueueService,
|
||||||
PushNotificationService, SmsService, NotificationsGateway],
|
SmsService, NotificationsGateway],
|
||||||
})
|
})
|
||||||
export class NotificationsModule { }
|
export class NotificationsModule { }
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
import { Processor, WorkerHost, OnWorkerEvent } from '@nestjs/bullmq';
|
|
||||||
import { Logger } from '@nestjs/common';
|
|
||||||
import { Job } from 'bullmq';
|
|
||||||
import { InjectRepository } from '@mikro-orm/nestjs';
|
|
||||||
import { EntityRepository, EntityManager } from '@mikro-orm/postgresql';
|
|
||||||
import { Notification } from '../entities/notification.entity';
|
|
||||||
import { PushNotificationQueueJob } from '../interfaces/jobs-queue.interface';
|
|
||||||
import { NotificationQueueNameEnum } from '../constants/queue';
|
|
||||||
import { PushNotificationService } from '../services/push-notification.service';
|
|
||||||
|
|
||||||
@Processor(NotificationQueueNameEnum.PUSH)
|
|
||||||
export class PushProcessor extends WorkerHost {
|
|
||||||
private readonly logger = new Logger(PushProcessor.name);
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
@InjectRepository(Notification)
|
|
||||||
private readonly notificationRepository: EntityRepository<Notification>,
|
|
||||||
private readonly em: EntityManager,
|
|
||||||
private readonly pushNotificationService: PushNotificationService,
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
async process(job: Job<PushNotificationQueueJob>) {
|
|
||||||
const { title, content, action, pushToken, pushTokens } = job.data;
|
|
||||||
|
|
||||||
this.logger.log(`Processing push notification job: ${job.id} - Title: ${title},`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get push token from job data
|
|
||||||
const pushToken = job.data.pushToken;
|
|
||||||
const pushTokens = job.data.pushTokens;
|
|
||||||
|
|
||||||
if (!pushToken && (!pushTokens || pushTokens.length === 0)) {
|
|
||||||
this.logger.warn(`Push token(s) not provided for notification `);
|
|
||||||
throw new Error('Push token or pushTokens array is required for push notifications');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send push notification
|
|
||||||
// Convert NotificationTitle enum to a readable string for the notification title
|
|
||||||
const notificationTitle = String(title)
|
|
||||||
.replace(/\./g, ' ')
|
|
||||||
.replace(/\b\w/g, l => l.toUpperCase());
|
|
||||||
|
|
||||||
let pushResult;
|
|
||||||
if (pushTokens && pushTokens.length > 0) {
|
|
||||||
// Send to multiple tokens
|
|
||||||
pushResult = await this.pushNotificationService.sendToMultipleTokens({
|
|
||||||
title: notificationTitle,
|
|
||||||
body: content,
|
|
||||||
tokens: pushTokens,
|
|
||||||
data: {
|
|
||||||
action,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else if (pushToken) {
|
|
||||||
// Send to single token
|
|
||||||
pushResult = await this.pushNotificationService.sendToToken({
|
|
||||||
title: notificationTitle,
|
|
||||||
body: content,
|
|
||||||
token: pushToken,
|
|
||||||
data: {
|
|
||||||
action,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error('Push token or pushTokens array is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pushResult.success) {
|
|
||||||
throw new Error(pushResult.error || 'Failed to send push notification');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.log(`Push notification sent successfully`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
|
|
||||||
providerResponse: { messageId: pushResult.messageId },
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Error processing push notification job ${job.id}:`, error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnWorkerEvent('completed')
|
|
||||||
onCompleted(job: Job) {
|
|
||||||
this.logger.log(`Notification job ${job.id} completed`);
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnWorkerEvent('failed')
|
|
||||||
onFailed(job: Job, error: Error) {
|
|
||||||
this.logger.error(`Notification job ${job.id} failed:`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
import { Injectable, Logger, HttpException, HttpStatus } from '@nestjs/common';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
|
||||||
import * as admin from 'firebase-admin';
|
|
||||||
|
|
||||||
export interface PushNotificationPayload {
|
|
||||||
title: string;
|
|
||||||
body: string;
|
|
||||||
data?: Record<string, unknown>;
|
|
||||||
token?: string;
|
|
||||||
tokens?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PushNotificationResponse {
|
|
||||||
success: boolean;
|
|
||||||
messageId?: string;
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class PushNotificationService {
|
|
||||||
private readonly logger = new Logger(PushNotificationService.name);
|
|
||||||
private firebaseApp: admin.app.App | null = null;
|
|
||||||
|
|
||||||
constructor(private readonly configService: ConfigService) {
|
|
||||||
this.initializeFirebase();
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeFirebase() {
|
|
||||||
try {
|
|
||||||
const firebaseConfig = this.configService.get<string>('FIREBASE_SERVICE_ACCOUNT');
|
|
||||||
|
|
||||||
if (!firebaseConfig) {
|
|
||||||
this.logger.warn('Firebase service account not configured. Push notifications will be disabled.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const serviceAccount = JSON.parse(firebaseConfig);
|
|
||||||
|
|
||||||
if (!this.firebaseApp) {
|
|
||||||
this.firebaseApp = admin.initializeApp({
|
|
||||||
credential: admin.credential.cert(serviceAccount),
|
|
||||||
});
|
|
||||||
this.logger.log('Firebase initialized successfully');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error('Failed to initialize Firebase:', error);
|
|
||||||
this.logger.warn('Push notifications will be disabled');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async sendToToken(payload: PushNotificationPayload): Promise<PushNotificationResponse> {
|
|
||||||
if (!this.firebaseApp || !payload.token) {
|
|
||||||
throw new HttpException(
|
|
||||||
'Push notification service not configured or token missing',
|
|
||||||
HttpStatus.SERVICE_UNAVAILABLE,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const message: admin.messaging.Message = {
|
|
||||||
notification: {
|
|
||||||
title: payload.title,
|
|
||||||
body: payload.body,
|
|
||||||
},
|
|
||||||
data: this.convertDataToString(payload.data || {}),
|
|
||||||
token: payload.token,
|
|
||||||
android: {
|
|
||||||
priority: 'high',
|
|
||||||
},
|
|
||||||
apns: {
|
|
||||||
headers: {
|
|
||||||
'apns-priority': '10',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await admin.messaging().send(message);
|
|
||||||
this.logger.log(`Push notification sent successfully: ${response}`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
messageId: response,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Failed to send push notification: ${error}`);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : 'Unknown error',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async sendToMultipleTokens(payload: PushNotificationPayload): Promise<PushNotificationResponse> {
|
|
||||||
if (!this.firebaseApp || !payload.tokens || payload.tokens.length === 0) {
|
|
||||||
throw new HttpException(
|
|
||||||
'Push notification service not configured or tokens missing',
|
|
||||||
HttpStatus.SERVICE_UNAVAILABLE,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const message: admin.messaging.MulticastMessage = {
|
|
||||||
notification: {
|
|
||||||
title: payload.title,
|
|
||||||
body: payload.body,
|
|
||||||
},
|
|
||||||
data: this.convertDataToString(payload.data || {}),
|
|
||||||
tokens: payload.tokens,
|
|
||||||
android: {
|
|
||||||
priority: 'high',
|
|
||||||
},
|
|
||||||
apns: {
|
|
||||||
headers: {
|
|
||||||
'apns-priority': '10',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await admin.messaging().sendEachForMulticast(message);
|
|
||||||
this.logger.log(`Push notifications sent: ${response.successCount} successful, ${response.failureCount} failed`);
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: response.successCount > 0,
|
|
||||||
messageId: response.responses[0]?.messageId,
|
|
||||||
error: response.failureCount > 0 ? `${response.failureCount} notifications failed` : undefined,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Failed to send push notifications: ${error}`);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : 'Unknown error',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertDataToString(data: Record<string, unknown>): Record<string, string> {
|
|
||||||
const result: Record<string, string> = {};
|
|
||||||
for (const [key, value] of Object.entries(data)) {
|
|
||||||
result[key] = typeof value === 'string' ? value : JSON.stringify(value);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
isConfigured(): boolean {
|
|
||||||
return this.firebaseApp !== null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import sanitizeHtml from 'sanitize-html';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sanitizes user input to prevent XSS attacks and remove malicious content
|
|
||||||
* Allows only safe text content, removing all HTML tags and scripts
|
|
||||||
* @param input - The string to sanitize
|
|
||||||
* @returns Sanitized string with all HTML tags and scripts removed
|
|
||||||
*/
|
|
||||||
export function sanitizeText(input: string | undefined | null): string | undefined | null {
|
|
||||||
if (!input) {
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sanitizeHtml(input, {
|
|
||||||
allowedTags: [], // No HTML tags allowed
|
|
||||||
allowedAttributes: {}, // No attributes allowed
|
|
||||||
allowedSchemes: [], // No URL schemes allowed
|
|
||||||
}).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user