@valentine-efagene/qshelter-common 2.0.34 → 2.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/events/event-publisher.d.ts +41 -0
- package/dist/src/events/event-publisher.js +111 -0
- package/dist/src/events/index.d.ts +3 -0
- package/dist/src/events/index.js +3 -0
- package/dist/src/events/notification-enums.d.ts +38 -0
- package/dist/src/events/notification-enums.js +48 -0
- package/dist/src/events/notification-event.d.ts +76 -0
- package/dist/src/events/notification-event.js +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NotificationMeta } from './notification-event';
|
|
2
|
+
import { NotificationType, NotificationChannel } from './notification-enums';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for the event publisher
|
|
5
|
+
*/
|
|
6
|
+
interface EventPublisherConfig {
|
|
7
|
+
region?: string;
|
|
8
|
+
endpoint?: string;
|
|
9
|
+
topicArn?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Event Publisher for sending notification events to SNS
|
|
13
|
+
* Used by all services to publish events to the notifications topic
|
|
14
|
+
*/
|
|
15
|
+
export declare class EventPublisher {
|
|
16
|
+
private readonly snsClient;
|
|
17
|
+
private readonly topicArn;
|
|
18
|
+
private readonly serviceName;
|
|
19
|
+
constructor(serviceName: string, config?: EventPublisherConfig);
|
|
20
|
+
/**
|
|
21
|
+
* Publish a notification event to SNS
|
|
22
|
+
*/
|
|
23
|
+
publish<T>(type: NotificationType, channel: NotificationChannel, payload: T, meta?: Partial<NotificationMeta>): Promise<string>;
|
|
24
|
+
/**
|
|
25
|
+
* Convenience method for publishing email notifications
|
|
26
|
+
*/
|
|
27
|
+
publishEmail<T>(type: NotificationType, payload: T, meta?: Partial<NotificationMeta>): Promise<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Convenience method for publishing SMS notifications
|
|
30
|
+
*/
|
|
31
|
+
publishSMS<T>(type: NotificationType, payload: T, meta?: Partial<NotificationMeta>): Promise<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Convenience method for publishing push notifications
|
|
34
|
+
*/
|
|
35
|
+
publishPush<T>(type: NotificationType, payload: T, meta?: Partial<NotificationMeta>): Promise<string>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get or create an EventPublisher for a service
|
|
39
|
+
*/
|
|
40
|
+
export declare function getEventPublisher(serviceName: string, config?: EventPublisherConfig): EventPublisher;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';
|
|
2
|
+
import { NotificationChannel } from './notification-enums';
|
|
3
|
+
/**
|
|
4
|
+
* Get SNS client configured for LocalStack or AWS
|
|
5
|
+
*/
|
|
6
|
+
function createSNSClient(config) {
|
|
7
|
+
const isLocalStack = process.env.LOCALSTACK_ENDPOINT || process.env.NODE_ENV === 'test';
|
|
8
|
+
const clientConfig = {
|
|
9
|
+
region: config.region || process.env.AWS_REGION || 'us-east-1',
|
|
10
|
+
};
|
|
11
|
+
if (isLocalStack) {
|
|
12
|
+
clientConfig.endpoint = config.endpoint || process.env.LOCALSTACK_ENDPOINT || 'http://localhost:4566';
|
|
13
|
+
clientConfig.credentials = {
|
|
14
|
+
accessKeyId: 'test',
|
|
15
|
+
secretAccessKey: 'test',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return new SNSClient(clientConfig);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Event Publisher for sending notification events to SNS
|
|
22
|
+
* Used by all services to publish events to the notifications topic
|
|
23
|
+
*/
|
|
24
|
+
export class EventPublisher {
|
|
25
|
+
snsClient;
|
|
26
|
+
topicArn;
|
|
27
|
+
serviceName;
|
|
28
|
+
constructor(serviceName, config) {
|
|
29
|
+
this.serviceName = serviceName;
|
|
30
|
+
this.snsClient = createSNSClient(config || {});
|
|
31
|
+
// Topic ARN can be passed directly or constructed from env vars
|
|
32
|
+
const stage = process.env.STAGE || process.env.NODE_ENV || 'test';
|
|
33
|
+
const region = config?.region || process.env.AWS_REGION || 'us-east-1';
|
|
34
|
+
const accountId = process.env.AWS_ACCOUNT_ID || '000000000000';
|
|
35
|
+
this.topicArn = config?.topicArn ||
|
|
36
|
+
process.env.NOTIFICATIONS_TOPIC_ARN ||
|
|
37
|
+
`arn:aws:sns:${region}:${accountId}:qshelter-${stage}-notifications`;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Publish a notification event to SNS
|
|
41
|
+
*/
|
|
42
|
+
async publish(type, channel, payload, meta) {
|
|
43
|
+
const event = {
|
|
44
|
+
type,
|
|
45
|
+
channel,
|
|
46
|
+
payload,
|
|
47
|
+
meta: {
|
|
48
|
+
source: this.serviceName,
|
|
49
|
+
timestamp: new Date().toISOString(),
|
|
50
|
+
correlationId: meta?.correlationId || crypto.randomUUID(),
|
|
51
|
+
userId: meta?.userId,
|
|
52
|
+
tenantId: meta?.tenantId,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
const command = new PublishCommand({
|
|
56
|
+
TopicArn: this.topicArn,
|
|
57
|
+
Message: JSON.stringify(event),
|
|
58
|
+
MessageAttributes: {
|
|
59
|
+
notificationType: {
|
|
60
|
+
DataType: 'String',
|
|
61
|
+
StringValue: type,
|
|
62
|
+
},
|
|
63
|
+
channel: {
|
|
64
|
+
DataType: 'String',
|
|
65
|
+
StringValue: channel,
|
|
66
|
+
},
|
|
67
|
+
source: {
|
|
68
|
+
DataType: 'String',
|
|
69
|
+
StringValue: this.serviceName,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
const result = await this.snsClient.send(command);
|
|
74
|
+
console.log(`[EventPublisher] Published ${type} event to SNS`, {
|
|
75
|
+
messageId: result.MessageId,
|
|
76
|
+
type,
|
|
77
|
+
channel,
|
|
78
|
+
source: this.serviceName,
|
|
79
|
+
});
|
|
80
|
+
return result.MessageId || '';
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Convenience method for publishing email notifications
|
|
84
|
+
*/
|
|
85
|
+
async publishEmail(type, payload, meta) {
|
|
86
|
+
return this.publish(type, NotificationChannel.EMAIL, payload, meta);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Convenience method for publishing SMS notifications
|
|
90
|
+
*/
|
|
91
|
+
async publishSMS(type, payload, meta) {
|
|
92
|
+
return this.publish(type, NotificationChannel.SMS, payload, meta);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Convenience method for publishing push notifications
|
|
96
|
+
*/
|
|
97
|
+
async publishPush(type, payload, meta) {
|
|
98
|
+
return this.publish(type, NotificationChannel.PUSH, payload, meta);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Singleton instances per service
|
|
102
|
+
const publisherInstances = new Map();
|
|
103
|
+
/**
|
|
104
|
+
* Get or create an EventPublisher for a service
|
|
105
|
+
*/
|
|
106
|
+
export function getEventPublisher(serviceName, config) {
|
|
107
|
+
if (!publisherInstances.has(serviceName)) {
|
|
108
|
+
publisherInstances.set(serviceName, new EventPublisher(serviceName, config));
|
|
109
|
+
}
|
|
110
|
+
return publisherInstances.get(serviceName);
|
|
111
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types of notifications that can be sent
|
|
3
|
+
* Used by all services to ensure consistent event naming
|
|
4
|
+
*/
|
|
5
|
+
export declare enum NotificationType {
|
|
6
|
+
WELCOME = "welcome",
|
|
7
|
+
VERIFY_EMAIL = "verifyEmail",
|
|
8
|
+
ACCOUNT_VERIFIED = "accountVerified",
|
|
9
|
+
ACCOUNT_SUSPENDED = "accountSuspended",
|
|
10
|
+
PASSWORD_RESET = "resetPassword",
|
|
11
|
+
MISSED_PAYMENT = "missedPayments",
|
|
12
|
+
WALLET_TOP_UP = "walletTopUp",
|
|
13
|
+
PAYMENT_RECEIVED = "paymentReceived",
|
|
14
|
+
PAYMENT_FAILED = "paymentFailed",
|
|
15
|
+
PAYMENT_REMINDER = "paymentReminder",
|
|
16
|
+
PROPERTY_ALLOCATION = "propertyAllocation",
|
|
17
|
+
UPDATED_TERMS = "updatedTermsAndConditions",
|
|
18
|
+
PREQUALIFICATION_SUBMITTED = "prequalificationSubmitted",
|
|
19
|
+
PREQUALIFICATION_APPROVED = "prequalificationApproved",
|
|
20
|
+
PREQUALIFICATION_REJECTED = "prequalificationRejected",
|
|
21
|
+
CONTRACT_CREATED = "contractCreated",
|
|
22
|
+
CONTRACT_ACTIVATED = "contractActivated",
|
|
23
|
+
CONTRACT_TERMINATION_REQUESTED = "contractTerminationRequested",
|
|
24
|
+
CONTRACT_TERMINATION_APPROVED = "contractTerminationApproved",
|
|
25
|
+
CONTRACT_TERMINATED = "contractTerminated",
|
|
26
|
+
ADMIN_CONTRIBUTION_RECEIVED = "adminContributionReceived",
|
|
27
|
+
ADMIN_PROPERTY_ALLOCATION = "adminPropertyAllocation",
|
|
28
|
+
ADMIN_INVITE = "adminInviteAdmin",
|
|
29
|
+
OTP = "otp"
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Channels through which notifications can be delivered
|
|
33
|
+
*/
|
|
34
|
+
export declare enum NotificationChannel {
|
|
35
|
+
EMAIL = "email",
|
|
36
|
+
SMS = "sms",
|
|
37
|
+
PUSH = "push"
|
|
38
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types of notifications that can be sent
|
|
3
|
+
* Used by all services to ensure consistent event naming
|
|
4
|
+
*/
|
|
5
|
+
export var NotificationType;
|
|
6
|
+
(function (NotificationType) {
|
|
7
|
+
// User lifecycle
|
|
8
|
+
NotificationType["WELCOME"] = "welcome";
|
|
9
|
+
NotificationType["VERIFY_EMAIL"] = "verifyEmail";
|
|
10
|
+
NotificationType["ACCOUNT_VERIFIED"] = "accountVerified";
|
|
11
|
+
NotificationType["ACCOUNT_SUSPENDED"] = "accountSuspended";
|
|
12
|
+
NotificationType["PASSWORD_RESET"] = "resetPassword";
|
|
13
|
+
// Payments
|
|
14
|
+
NotificationType["MISSED_PAYMENT"] = "missedPayments";
|
|
15
|
+
NotificationType["WALLET_TOP_UP"] = "walletTopUp";
|
|
16
|
+
NotificationType["PAYMENT_RECEIVED"] = "paymentReceived";
|
|
17
|
+
NotificationType["PAYMENT_FAILED"] = "paymentFailed";
|
|
18
|
+
NotificationType["PAYMENT_REMINDER"] = "paymentReminder";
|
|
19
|
+
// Property
|
|
20
|
+
NotificationType["PROPERTY_ALLOCATION"] = "propertyAllocation";
|
|
21
|
+
// Terms
|
|
22
|
+
NotificationType["UPDATED_TERMS"] = "updatedTermsAndConditions";
|
|
23
|
+
// Prequalification
|
|
24
|
+
NotificationType["PREQUALIFICATION_SUBMITTED"] = "prequalificationSubmitted";
|
|
25
|
+
NotificationType["PREQUALIFICATION_APPROVED"] = "prequalificationApproved";
|
|
26
|
+
NotificationType["PREQUALIFICATION_REJECTED"] = "prequalificationRejected";
|
|
27
|
+
// Contract
|
|
28
|
+
NotificationType["CONTRACT_CREATED"] = "contractCreated";
|
|
29
|
+
NotificationType["CONTRACT_ACTIVATED"] = "contractActivated";
|
|
30
|
+
NotificationType["CONTRACT_TERMINATION_REQUESTED"] = "contractTerminationRequested";
|
|
31
|
+
NotificationType["CONTRACT_TERMINATION_APPROVED"] = "contractTerminationApproved";
|
|
32
|
+
NotificationType["CONTRACT_TERMINATED"] = "contractTerminated";
|
|
33
|
+
// Admin
|
|
34
|
+
NotificationType["ADMIN_CONTRIBUTION_RECEIVED"] = "adminContributionReceived";
|
|
35
|
+
NotificationType["ADMIN_PROPERTY_ALLOCATION"] = "adminPropertyAllocation";
|
|
36
|
+
NotificationType["ADMIN_INVITE"] = "adminInviteAdmin";
|
|
37
|
+
// OTP
|
|
38
|
+
NotificationType["OTP"] = "otp";
|
|
39
|
+
})(NotificationType || (NotificationType = {}));
|
|
40
|
+
/**
|
|
41
|
+
* Channels through which notifications can be delivered
|
|
42
|
+
*/
|
|
43
|
+
export var NotificationChannel;
|
|
44
|
+
(function (NotificationChannel) {
|
|
45
|
+
NotificationChannel["EMAIL"] = "email";
|
|
46
|
+
NotificationChannel["SMS"] = "sms";
|
|
47
|
+
NotificationChannel["PUSH"] = "push";
|
|
48
|
+
})(NotificationChannel || (NotificationChannel = {}));
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { NotificationType, NotificationChannel } from './notification-enums';
|
|
2
|
+
/**
|
|
3
|
+
* Metadata attached to every notification event for tracing and debugging
|
|
4
|
+
*/
|
|
5
|
+
export interface NotificationMeta {
|
|
6
|
+
/** Service that published the event */
|
|
7
|
+
source: string;
|
|
8
|
+
/** ISO timestamp of when event was created */
|
|
9
|
+
timestamp: string;
|
|
10
|
+
/** Correlation ID for distributed tracing */
|
|
11
|
+
correlationId?: string;
|
|
12
|
+
/** User ID if applicable */
|
|
13
|
+
userId?: string;
|
|
14
|
+
/** Tenant ID if applicable */
|
|
15
|
+
tenantId?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Standard notification event payload sent via SNS->SQS
|
|
19
|
+
* All services must use this interface when publishing notification events
|
|
20
|
+
*/
|
|
21
|
+
export interface NotificationEvent<T = Record<string, unknown>> {
|
|
22
|
+
/** Type of notification - determines which template/handler to use */
|
|
23
|
+
type: NotificationType;
|
|
24
|
+
/** Delivery channel - email, sms, or push */
|
|
25
|
+
channel: NotificationChannel;
|
|
26
|
+
/** The actual notification data (varies by type) */
|
|
27
|
+
payload: T;
|
|
28
|
+
/** Event metadata for tracing */
|
|
29
|
+
meta: NotificationMeta;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Email-specific payload fields that most email templates need
|
|
33
|
+
*/
|
|
34
|
+
export interface EmailPayload {
|
|
35
|
+
/** Recipient email address */
|
|
36
|
+
to_email: string;
|
|
37
|
+
/** Optional subject override (defaults to template title) */
|
|
38
|
+
subject?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Verify email payload
|
|
42
|
+
*/
|
|
43
|
+
export interface VerifyEmailPayload extends EmailPayload {
|
|
44
|
+
homeBuyerName: string;
|
|
45
|
+
verificationLink: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Password reset payload
|
|
49
|
+
*/
|
|
50
|
+
export interface PasswordResetPayload extends EmailPayload {
|
|
51
|
+
homeBuyerName: string;
|
|
52
|
+
otp: string;
|
|
53
|
+
ttl: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Welcome email payload
|
|
57
|
+
*/
|
|
58
|
+
export interface WelcomePayload extends EmailPayload {
|
|
59
|
+
homeBuyerName: string;
|
|
60
|
+
loginLink: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Missed payments payload
|
|
64
|
+
*/
|
|
65
|
+
export interface MissedPaymentsPayload extends EmailPayload {
|
|
66
|
+
homeBuyerName: string;
|
|
67
|
+
amount: number;
|
|
68
|
+
loginLink: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Account verified payload
|
|
72
|
+
*/
|
|
73
|
+
export interface AccountVerifiedPayload extends EmailPayload {
|
|
74
|
+
homeBuyerName: string;
|
|
75
|
+
loginLink: string;
|
|
76
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@valentine-efagene/qshelter-common",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.36",
|
|
4
4
|
"description": "Shared database schemas and utilities for QShelter services",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@aws-sdk/client-secrets-manager": "^3.500.0",
|
|
32
|
+
"@aws-sdk/client-sns": "^3.500.0",
|
|
32
33
|
"@aws-sdk/client-ssm": "^3.500.0",
|
|
33
34
|
"@prisma/client": "^7.0.0",
|
|
34
35
|
"dotenv": "^17.2.3",
|