@valentine-efagene/qshelter-common 2.0.33 → 2.0.35

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.
@@ -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,3 @@
1
+ export * from './notification-enums';
2
+ export * from './notification-event';
3
+ export * from './event-publisher';
@@ -0,0 +1,3 @@
1
+ export * from './notification-enums';
2
+ export * from './notification-event';
3
+ export * from './event-publisher';
@@ -0,0 +1,27 @@
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
+ PROPERTY_ALLOCATION = "propertyAllocation",
14
+ UPDATED_TERMS = "updatedTermsAndConditions",
15
+ ADMIN_CONTRIBUTION_RECEIVED = "adminContributionReceived",
16
+ ADMIN_PROPERTY_ALLOCATION = "adminPropertyAllocation",
17
+ ADMIN_INVITE = "adminInviteAdmin",
18
+ OTP = "otp"
19
+ }
20
+ /**
21
+ * Channels through which notifications can be delivered
22
+ */
23
+ export declare enum NotificationChannel {
24
+ EMAIL = "email",
25
+ SMS = "sms",
26
+ PUSH = "push"
27
+ }
@@ -0,0 +1,35 @@
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
+ // Property
17
+ NotificationType["PROPERTY_ALLOCATION"] = "propertyAllocation";
18
+ // Terms
19
+ NotificationType["UPDATED_TERMS"] = "updatedTermsAndConditions";
20
+ // Admin
21
+ NotificationType["ADMIN_CONTRIBUTION_RECEIVED"] = "adminContributionReceived";
22
+ NotificationType["ADMIN_PROPERTY_ALLOCATION"] = "adminPropertyAllocation";
23
+ NotificationType["ADMIN_INVITE"] = "adminInviteAdmin";
24
+ // OTP
25
+ NotificationType["OTP"] = "otp";
26
+ })(NotificationType || (NotificationType = {}));
27
+ /**
28
+ * Channels through which notifications can be delivered
29
+ */
30
+ export var NotificationChannel;
31
+ (function (NotificationChannel) {
32
+ NotificationChannel["EMAIL"] = "email";
33
+ NotificationChannel["SMS"] = "sms";
34
+ NotificationChannel["PUSH"] = "push";
35
+ })(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 {};
@@ -7,3 +7,4 @@ export * from '../generated/client/models';
7
7
  export * from './prisma/user';
8
8
  export * from './prisma/tenant';
9
9
  export * from './middleware';
10
+ export * from './events';
package/dist/src/index.js CHANGED
@@ -7,3 +7,4 @@ export * from '../generated/client/models';
7
7
  export * from './prisma/user';
8
8
  export * from './prisma/tenant';
9
9
  export * from './middleware';
10
+ export * from './events';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valentine-efagene/qshelter-common",
3
- "version": "2.0.33",
3
+ "version": "2.0.35",
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",