@lyxa.ai/core 1.4.333-alpha-2.0 → 1.4.333
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/libraries/event/events/mongo-stream-changes-batch-process-shops-event.d.ts +2 -2
- package/dist/libraries/event/events/punch-marketing/sync-user-shop-punch-marketing-history-event.d.ts +2 -2
- package/dist/libraries/event/events/sync-order-again-sections-for-shop-event.d.ts +2 -2
- package/dist/libraries/mongo/models/payment-intent.model.d.ts +2 -1
- package/dist/libraries/mongo/models/payment-intent.model.js +5 -0
- package/dist/libraries/mongo/models/payment-intent.model.js.map +1 -1
- package/dist/libraries/notification/firebase-message.builder.d.ts +1 -5
- package/dist/libraries/notification/firebase-message.builder.js +1 -34
- package/dist/libraries/notification/firebase-message.builder.js.map +1 -1
- package/dist/libraries/notification/index.d.ts +0 -1
- package/dist/libraries/notification/index.js.map +1 -1
- package/dist/libraries/notification/lyxa-fcm-sender.js +0 -2
- package/dist/libraries/notification/lyxa-fcm-sender.js.map +1 -1
- package/dist/libraries/notification/lyxa-fcm.service.d.ts +0 -1
- package/dist/libraries/notification/lyxa-fcm.service.js +0 -17
- package/dist/libraries/notification/lyxa-fcm.service.js.map +1 -1
- package/dist/libraries/redis/index.d.ts +5 -0
- package/dist/libraries/redis/index.js +16 -0
- package/dist/libraries/redis/index.js.map +1 -1
- package/dist/libraries/socket/events/chatroom-message-send.socket.event.d.ts +25 -25
- package/dist/libraries/socket/events/order-actions.socket.event.d.ts +24 -24
- package/dist/libraries/socket/events/rider-location-update.socket.event.d.ts +4 -4
- package/dist/libraries/socket/events/ticket-actions.socket.event.d.ts +12 -12
- package/dist/libraries/socket/events/ticket-assign.socket.event.d.ts +4 -4
- package/dist/libraries/trpc/middlewares/auth.d.ts +1 -1
- package/dist/libraries/trpc/middlewares/auth.js +2 -0
- package/dist/libraries/trpc/middlewares/auth.js.map +1 -1
- package/dist/libraries/trpc/middlewares/createRoleProtectedProcedure.d.ts +1 -1
- package/dist/libraries/trpc/middlewares/phone-verified.d.ts +1 -1
- package/dist/libraries/trpc/middlewares/publicUserDecoder.d.ts +1 -1
- package/dist/types/README.md +1 -1
- package/dist/types/package.json +1 -1
- package/dist/types/utilities/validation/common-validation.d.ts +66 -66
- package/dist/types/utilities/validation/validation-for-frontend.d.ts +2 -2
- package/dist/utilities/pagination.d.ts +3 -3
- package/dist/utilities/validation/common-validation.d.ts +70 -70
- package/dist/utilities/validation/validation-for-frontend.d.ts +2 -2
- package/package.json +1 -1
|
@@ -24,13 +24,13 @@ declare const MongoStreamsBatchProcessShopsSchema: z.ZodObject<{
|
|
|
24
24
|
}, "strict", z.ZodTypeAny, {
|
|
25
25
|
operation: ChangeStreamOperationTypes;
|
|
26
26
|
updateCollection: UpdateCollectionAtlasSearchEnum;
|
|
27
|
-
updatedFields?: any;
|
|
28
27
|
fullDocument?: any;
|
|
28
|
+
updatedFields?: any;
|
|
29
29
|
}, {
|
|
30
30
|
operation: ChangeStreamOperationTypes;
|
|
31
31
|
updateCollection: UpdateCollectionAtlasSearchEnum;
|
|
32
|
-
updatedFields?: any;
|
|
33
32
|
fullDocument?: any;
|
|
33
|
+
updatedFields?: any;
|
|
34
34
|
}>;
|
|
35
35
|
export type MongoStreamsBatchProcessShopsDTO = DTO<typeof MongoStreamsBatchProcessShopsSchema>;
|
|
36
36
|
export declare class MongoStreamsBatchProcessShopsEvent extends BaseEvent<Record<string, unknown>> {
|
|
@@ -6,12 +6,12 @@ declare const SyncUserShopPunchMarketingHistoryInCacheValidationSchema: import("
|
|
|
6
6
|
shopId: import("zod").ZodUnion<[import("zod").ZodEffects<import("zod").ZodEffects<import("zod").ZodString, string, string>, import("mongoose").Types.ObjectId, string>, import("zod").ZodType<import("mongoose").Types.ObjectId, import("zod").ZodTypeDef, import("mongoose").Types.ObjectId>]>;
|
|
7
7
|
operation: import("zod").ZodNativeEnum<typeof ChangeStreamOperationTypes>;
|
|
8
8
|
}, "strict", import("zod").ZodTypeAny, {
|
|
9
|
-
userId: import("mongoose").Types.ObjectId;
|
|
10
9
|
shopId: import("mongoose").Types.ObjectId;
|
|
10
|
+
userId: import("mongoose").Types.ObjectId;
|
|
11
11
|
operation: ChangeStreamOperationTypes;
|
|
12
12
|
}, {
|
|
13
|
-
userId: string | import("mongoose").Types.ObjectId;
|
|
14
13
|
shopId: string | import("mongoose").Types.ObjectId;
|
|
14
|
+
userId: string | import("mongoose").Types.ObjectId;
|
|
15
15
|
operation: ChangeStreamOperationTypes;
|
|
16
16
|
}>;
|
|
17
17
|
export type SyncUserShopPunchMarketingHistoryInCacheValidationDTO = DTO<typeof SyncUserShopPunchMarketingHistoryInCacheValidationSchema>;
|
|
@@ -4,11 +4,11 @@ declare const SyncSingleShopValidationSchema: import("zod").ZodObject<{
|
|
|
4
4
|
userId: import("zod").ZodUnion<[import("zod").ZodEffects<import("zod").ZodEffects<import("zod").ZodString, string, string>, import("mongoose").Types.ObjectId, string>, import("zod").ZodType<import("mongoose").Types.ObjectId, import("zod").ZodTypeDef, import("mongoose").Types.ObjectId>]>;
|
|
5
5
|
shopId: import("zod").ZodUnion<[import("zod").ZodEffects<import("zod").ZodEffects<import("zod").ZodString, string, string>, import("mongoose").Types.ObjectId, string>, import("zod").ZodType<import("mongoose").Types.ObjectId, import("zod").ZodTypeDef, import("mongoose").Types.ObjectId>]>;
|
|
6
6
|
}, "strict", import("zod").ZodTypeAny, {
|
|
7
|
-
userId: import("mongoose").Types.ObjectId;
|
|
8
7
|
shopId: import("mongoose").Types.ObjectId;
|
|
8
|
+
userId: import("mongoose").Types.ObjectId;
|
|
9
9
|
}, {
|
|
10
|
-
userId: string | import("mongoose").Types.ObjectId;
|
|
11
10
|
shopId: string | import("mongoose").Types.ObjectId;
|
|
11
|
+
userId: string | import("mongoose").Types.ObjectId;
|
|
12
12
|
}>;
|
|
13
13
|
export type SyncSingleShopOrderAgainCacheDTO = DTO<typeof SyncSingleShopValidationSchema>;
|
|
14
14
|
export declare class SyncSingleShopOrderAgainInCacheEvent extends BaseEvent<Record<string, unknown>> {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Ref } from '@typegoose/typegoose';
|
|
2
2
|
import { TimeStamps } from '@typegoose/typegoose/lib/defaultClasses';
|
|
3
3
|
import { CoreUser } from './core-user.model';
|
|
4
|
-
import { PaymentIntentStatus } from '../../../utilities/enum';
|
|
4
|
+
import { PaymentIntentStatus, PaymentProvider } from '../../../utilities/enum';
|
|
5
5
|
import { User } from './user.model';
|
|
6
6
|
export declare class PaymentIntent extends TimeStamps {
|
|
7
7
|
coreUser: Ref<CoreUser>;
|
|
@@ -14,4 +14,5 @@ export declare class PaymentIntent extends TimeStamps {
|
|
|
14
14
|
authorization?: string;
|
|
15
15
|
paymentContext?: any;
|
|
16
16
|
status?: PaymentIntentStatus;
|
|
17
|
+
provider?: PaymentProvider;
|
|
17
18
|
}
|
|
@@ -26,6 +26,7 @@ let PaymentIntent = class PaymentIntent extends defaultClasses_1.TimeStamps {
|
|
|
26
26
|
authorization;
|
|
27
27
|
paymentContext;
|
|
28
28
|
status;
|
|
29
|
+
provider;
|
|
29
30
|
};
|
|
30
31
|
exports.PaymentIntent = PaymentIntent;
|
|
31
32
|
__decorate([
|
|
@@ -68,6 +69,10 @@ __decorate([
|
|
|
68
69
|
(0, typegoose_1.prop)({ type: String, enum: enum_1.PaymentIntentStatus, default: enum_1.PaymentIntentStatus.PENDING }),
|
|
69
70
|
__metadata("design:type", String)
|
|
70
71
|
], PaymentIntent.prototype, "status", void 0);
|
|
72
|
+
__decorate([
|
|
73
|
+
(0, typegoose_1.prop)({ type: String, enum: enum_1.PaymentProvider }),
|
|
74
|
+
__metadata("design:type", String)
|
|
75
|
+
], PaymentIntent.prototype, "provider", void 0);
|
|
71
76
|
exports.PaymentIntent = PaymentIntent = __decorate([
|
|
72
77
|
(0, typegoose_1.modelOptions)({
|
|
73
78
|
schemaOptions: { collection: 'paymentIntents' },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payment-intent.model.js","sourceRoot":"/","sources":["libraries/mongo/models/payment-intent.model.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,oDAAmF;AACnF,4EAAqE;AACrE,uDAA6C;AAC7C,
|
|
1
|
+
{"version":3,"file":"payment-intent.model.js","sourceRoot":"/","sources":["libraries/mongo/models/payment-intent.model.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,oDAAmF;AACnF,4EAAqE;AACrE,uDAA6C;AAC7C,kDAA+E;AAC/E,6CAAoC;AAQ7B,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,2BAAU;IAErC,QAAQ,CAAiB;IAGzB,IAAI,CAAa;IAGjB,OAAO,CAAU;IAGjB,KAAK,CAAU;IAGf,UAAU,CAAU;IAGpB,MAAM,CAAU;IAGhB,eAAe,CAAU;IAGzB,aAAa,CAAU;IAGvB,cAAc,CAAO;IAGrB,MAAM,CAAuB;IAG7B,QAAQ,CAAmB;CAClC,CAAA;AAjCY,sCAAa;AAElB;IADN,IAAA,gBAAI,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,0BAAQ,EAAE,CAAC;;+CACd;AAGzB;IADN,IAAA,gBAAI,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,iBAAI,EAAE,CAAC;;2CAClB;AAGjB;IADN,IAAA,gBAAI,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;8CACf;AAGjB;IADN,IAAA,gBAAI,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;4CACD;AAGf;IADN,IAAA,gBAAI,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;iDACI;AAGpB;IADN,IAAA,gBAAI,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;6CAChB;AAGhB;IADN,IAAA,gBAAI,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;sDACS;AAGzB;IADN,IAAA,gBAAI,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;oDACO;AAGvB;IADN,IAAA,gBAAI,EAAC,EAAE,IAAI,EAAE,oBAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;;qDAChB;AAGrB;IADN,IAAA,gBAAI,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0BAAmB,EAAE,OAAO,EAAE,0BAAmB,CAAC,OAAO,EAAE,CAAC;;6CACpD;AAG7B;IADN,IAAA,gBAAI,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAe,EAAE,CAAC;;+CACZ;wBAhCtB,aAAa;IANzB,IAAA,wBAAY,EAAC;QACb,aAAa,EAAE,EAAE,UAAU,EAAE,gBAAgB,EAAE;QAC/C,OAAO,EAAE;YACR,UAAU,EAAE,oBAAQ,CAAC,KAAK;SAC1B;KACD,CAAC;GACW,aAAa,CAiCzB","sourcesContent":["import { modelOptions, mongoose, prop, Ref, Severity } from '@typegoose/typegoose';\nimport { TimeStamps } from '@typegoose/typegoose/lib/defaultClasses';\nimport { CoreUser } from './core-user.model';\nimport { PaymentIntentStatus, PaymentProvider } from '../../../utilities/enum';\nimport { User } from './user.model';\n\n@modelOptions({\n\tschemaOptions: { collection: 'paymentIntents' },\n\toptions: {\n\t\tallowMixed: Severity.ALLOW,\n\t},\n})\nexport class PaymentIntent extends TimeStamps {\n\t@prop({ required: true, ref: () => CoreUser })\n\tpublic coreUser!: Ref<CoreUser>;\n\n\t@prop({ required: true, ref: () => User })\n\tpublic user!: Ref<User>;\n\n\t@prop({ required: true, type: String })\n\tpublic orderId!: string;\n\n\t@prop({ type: String })\n\tpublic token?: string; // only for areeba\n\n\t@prop({ type: String })\n\tpublic externalId?: string; // only for whish\n\n\t@prop({ required: true, type: Number })\n\tpublic amount!: number;\n\n\t@prop({ type: Number })\n\tpublic secondaryAmount?: number;\n\n\t@prop({ type: String })\n\tpublic authorization?: string;\n\n\t@prop({ type: mongoose.Schema.Types.Mixed })\n\tpublic paymentContext?: any;\n\n\t@prop({ type: String, enum: PaymentIntentStatus, default: PaymentIntentStatus.PENDING })\n\tpublic status?: PaymentIntentStatus;\n\n\t@prop({ type: String, enum: PaymentProvider })\n\tpublic provider?: PaymentProvider;\n}\n"]}
|
|
@@ -3,8 +3,7 @@ import { FCMNotificationPayload } from './lyxa-fcm.service';
|
|
|
3
3
|
export declare enum MultiMessageType {
|
|
4
4
|
OLD = "old",
|
|
5
5
|
CONSOLE = "console",
|
|
6
|
-
DEFAULT = "default"
|
|
7
|
-
LIVE_ACTIVITY = "live_activity"
|
|
6
|
+
DEFAULT = "default"
|
|
8
7
|
}
|
|
9
8
|
export interface IFirebaseMessageBuilder {
|
|
10
9
|
buildMessage(payload: FCMNotificationPayload, tokens: string[]): messaging.MulticastMessage;
|
|
@@ -21,6 +20,3 @@ export declare class OldMessageBuilder implements IFirebaseMessageBuilder {
|
|
|
21
20
|
export declare class ConsoleMessageBuilder implements IFirebaseMessageBuilder {
|
|
22
21
|
buildMessage(payload: FCMNotificationPayload, tokens: string[]): messaging.MulticastMessage;
|
|
23
22
|
}
|
|
24
|
-
export declare class LiveActivityMessageBuilder implements IFirebaseMessageBuilder {
|
|
25
|
-
buildMessage(payload: FCMNotificationPayload, tokens: string[]): messaging.MulticastMessage;
|
|
26
|
-
}
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
7
|
-
const dayjs_1 = __importDefault(require("dayjs"));
|
|
3
|
+
exports.ConsoleMessageBuilder = exports.OldMessageBuilder = exports.DefaultMessageBuilder = exports.FirebaseMessageBuilderFactory = exports.MultiMessageType = void 0;
|
|
8
4
|
var MultiMessageType;
|
|
9
5
|
(function (MultiMessageType) {
|
|
10
6
|
MultiMessageType["OLD"] = "old";
|
|
11
7
|
MultiMessageType["CONSOLE"] = "console";
|
|
12
8
|
MultiMessageType["DEFAULT"] = "default";
|
|
13
|
-
MultiMessageType["LIVE_ACTIVITY"] = "live_activity";
|
|
14
9
|
})(MultiMessageType || (exports.MultiMessageType = MultiMessageType = {}));
|
|
15
10
|
class FirebaseMessageBuilderFactory {
|
|
16
11
|
buildMessage(type, payload, tokens) {
|
|
@@ -19,8 +14,6 @@ class FirebaseMessageBuilderFactory {
|
|
|
19
14
|
return new ConsoleMessageBuilder().buildMessage(payload, tokens);
|
|
20
15
|
case MultiMessageType.OLD:
|
|
21
16
|
return new OldMessageBuilder().buildMessage(payload, tokens);
|
|
22
|
-
case MultiMessageType.LIVE_ACTIVITY:
|
|
23
|
-
return new LiveActivityMessageBuilder().buildMessage(payload, tokens);
|
|
24
17
|
default:
|
|
25
18
|
return new DefaultMessageBuilder().buildMessage(payload, tokens);
|
|
26
19
|
}
|
|
@@ -111,30 +104,4 @@ class ConsoleMessageBuilder {
|
|
|
111
104
|
}
|
|
112
105
|
}
|
|
113
106
|
exports.ConsoleMessageBuilder = ConsoleMessageBuilder;
|
|
114
|
-
class LiveActivityMessageBuilder {
|
|
115
|
-
buildMessage(payload, tokens) {
|
|
116
|
-
return {
|
|
117
|
-
notification: {
|
|
118
|
-
title: payload.title,
|
|
119
|
-
body: payload.body,
|
|
120
|
-
},
|
|
121
|
-
tokens: tokens,
|
|
122
|
-
apns: {
|
|
123
|
-
payload: {
|
|
124
|
-
aps: {
|
|
125
|
-
timestamp: (0, dayjs_1.default)().unix(),
|
|
126
|
-
event: payload.data.event,
|
|
127
|
-
'content-state': { status: payload.data.status, etaMinutes: payload.data.deliveredInMinutes },
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
headers: {
|
|
131
|
-
'apns-push-type': 'liveactivity',
|
|
132
|
-
'apns-priority': '10',
|
|
133
|
-
'apns-topic': 'com.lyxa.app.push-type.liveactivity',
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
exports.LiveActivityMessageBuilder = LiveActivityMessageBuilder;
|
|
140
107
|
//# sourceMappingURL=firebase-message.builder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firebase-message.builder.js","sourceRoot":"/","sources":["libraries/notification/firebase-message.builder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"firebase-message.builder.js","sourceRoot":"/","sources":["libraries/notification/firebase-message.builder.ts"],"names":[],"mappings":"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","sourcesContent":["import { messaging } from 'firebase-admin';\nimport { FCMNotificationPayload } from './lyxa-fcm.service';\n\nexport enum MultiMessageType {\n\tOLD = 'old',\n\tCONSOLE = 'console',\n\tDEFAULT = 'default',\n}\n\nexport interface IFirebaseMessageBuilder {\n\tbuildMessage(payload: FCMNotificationPayload, tokens: string[]): messaging.MulticastMessage;\n}\n\nexport class FirebaseMessageBuilderFactory {\n\tpublic buildMessage(\n\t\ttype: MultiMessageType,\n\t\tpayload: FCMNotificationPayload,\n\t\ttokens: string[]\n\t): messaging.MulticastMessage {\n\t\tswitch (type) {\n\t\t\tcase MultiMessageType.CONSOLE:\n\t\t\t\treturn new ConsoleMessageBuilder().buildMessage(payload, tokens);\n\t\t\tcase MultiMessageType.OLD:\n\t\t\t\treturn new OldMessageBuilder().buildMessage(payload, tokens);\n\t\t\t// case MultiMessageType.DEFAULT:\n\t\t\t// \treturn new DefaultMessageBuilder().buildMessage(payload, tokens);\n\t\t\tdefault:\n\t\t\t\treturn new DefaultMessageBuilder().buildMessage(payload, tokens);\n\t\t}\n\t}\n}\n\nexport class DefaultMessageBuilder implements IFirebaseMessageBuilder {\n\tbuildMessage(payload: FCMNotificationPayload, tokens: string[]): messaging.MulticastMessage {\n\t\tconst isInvisible = payload.invisible === true;\n\t\tconst channelId = payload.sound ? 'order_alert' : 'default';\n\n\t\treturn {\n\t\t\tdata: { ...payload.data, channelId: isInvisible ? 'silent' : channelId },\n\t\t\tnotification: {\n\t\t\t\ttitle: payload.title,\n\t\t\t\tbody: payload.body,\n\t\t\t},\n\t\t\ttokens: tokens,\n\t\t\tandroid: {\n\t\t\t\tpriority: 'high',\n\t\t\t\tnotification: {\n\t\t\t\t\tchannelId: 'silent',\n\t\t\t\t},\n\t\t\t},\n\t\t\tapns: {\n\t\t\t\tpayload: {\n\t\t\t\t\taps: {\n\t\t\t\t\t\tsound: payload.sound || 'default',\n\t\t\t\t\t\tcontentAvailable: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\theaders: {\n\t\t\t\t\t'apns-push-type': 'background',\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n}\n\nexport class OldMessageBuilder implements IFirebaseMessageBuilder {\n\tbuildMessage(payload: FCMNotificationPayload, tokens: string[]): messaging.MulticastMessage {\n\t\tconst isInvisible = payload.invisible === true;\n\n\t\tconst message: messaging.MulticastMessage = {\n\t\t\tdata: payload.data || {},\n\t\t\tnotification: {\n\t\t\t\ttitle: payload.title,\n\t\t\t\tbody: payload.body,\n\t\t\t},\n\t\t\ttokens: tokens,\n\t\t\tandroid: { priority: 'high' },\n\t\t\tapns: {\n\t\t\t\tpayload: {\n\t\t\t\t\taps: {\n\t\t\t\t\t\tsound: payload.sound || 'default',\n\t\t\t\t\t\tcontentAvailable: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\n\t\tif (!isInvisible) {\n\t\t\tmessage.notification = {\n\t\t\t\ttitle: payload.title,\n\t\t\t\tbody: payload.body,\n\t\t\t};\n\n\t\t\tmessage.android = {\n\t\t\t\tpriority: 'high',\n\t\t\t\tnotification: {\n\t\t\t\t\tchannelId: payload.sound ? 'android_Push_Notification_Channel_1' : 'default',\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn message;\n\t}\n}\n\nexport class ConsoleMessageBuilder implements IFirebaseMessageBuilder {\n\tbuildMessage(payload: FCMNotificationPayload, tokens: string[]): messaging.MulticastMessage {\n\t\tconst isInvisible = payload.invisible === true;\n\t\tconst channelId = payload.sound ? 'order_alert' : 'default';\n\t\treturn {\n\t\t\tdata: {\n\t\t\t\t...payload.data,\n\t\t\t\tchannelId: isInvisible ? 'silent' : channelId,\n\t\t\t\tnotificationTitle: payload.title ?? '',\n\t\t\t\tnotificationBody: payload.body ?? '',\n\t\t\t},\n\t\t\ttokens: tokens,\n\t\t};\n\t}\n}\n"]}
|
|
@@ -10,7 +10,6 @@ export interface NotificationEventData {
|
|
|
10
10
|
clickableUrl?: string | null;
|
|
11
11
|
notificationType?: PushNotificationEventType;
|
|
12
12
|
saveNotification?: boolean;
|
|
13
|
-
isLiveActivity?: boolean;
|
|
14
13
|
[key: string]: any;
|
|
15
14
|
}
|
|
16
15
|
export type NotificationGlobalEventData = Omit<NotificationEventData, 'recipientId'>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/notification/index.ts"],"names":[],"mappings":";;;AAAA,+CAAqG;AACrG,kDAA+C;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/notification/index.ts"],"names":[],"mappings":";;;AAAA,+CAAqG;AACrG,kDAA+C;AAyB/C,MAAa,iBAAkB,SAAQ,qBAAgC;IACtE,YACC,OAEC;QAED,MAAM,YAAY,GAA0B;YAC3C,GAAG,OAAO;YACV,IAAI,EAAE;gBACL,WAAW,EAAE,KAAK;gBAClB,WAAW,EAAE,8BAAuB,CAAC,IAAI;gBACzC,GAAG,OAAO,CAAC,IAAI;aACf;SACD,CAAC;QAEF,KAAK,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IACrC,CAAC;CACD;AAjBD,8CAiBC;AAED,MAAa,uBAAwB,SAAQ,qBAAsC;IAClF,YACC,OAEC;QAED,MAAM,YAAY,GAAgC;YACjD,GAAG,OAAO;YACV,IAAI,EAAE;gBACL,WAAW,EAAE,KAAK;gBAClB,WAAW,EAAE,8BAAuB,CAAC,IAAI;gBACzC,GAAG,OAAO,CAAC,IAAI;aACf;SACD,CAAC;QAEF,KAAK,CAAC,qBAAqB,EAAE,YAAY,CAAC,CAAC;IAC5C,CAAC;CACD;AAjBD,0DAiBC","sourcesContent":["import { ClickType, NotificationAccountType, PushNotificationEventType } from '../../utilities/enum';\nimport { BaseEvent } from '../event/BaseEvent';\nimport mongoose from 'mongoose';\nimport { FCMNotificationPayload } from './lyxa-fcm.service';\n\nexport interface NotificationEventData {\n\taccountType: NotificationAccountType;\n\trecipientId: mongoose.Types.ObjectId;\n\tisClickable: boolean;\n\tclickType?: ClickType | null;\n\tclickableUrl?: string | null;\n\tnotificationType?: PushNotificationEventType;\n\tsaveNotification?: boolean;\n\t[key: string]: any;\n}\n\nexport type NotificationGlobalEventData = Omit<NotificationEventData, 'recipientId'>;\n\nexport interface NotificationEventType extends Omit<FCMNotificationPayload, 'data'> {\n\tdata: NotificationEventData;\n}\n\nexport interface NotificationGlobalEventType extends Omit<FCMNotificationPayload, 'data'> {\n\tdata: NotificationGlobalEventData;\n}\n\nexport class NotificationEvent extends BaseEvent<NotificationEventType> {\n\tconstructor(\n\t\tpayload: Partial<Omit<NotificationEventType, 'data'>> & {\n\t\t\tdata: Partial<NotificationEventData> & { recipientId: mongoose.Types.ObjectId };\n\t\t}\n\t) {\n\t\tconst finalPayload: NotificationEventType = {\n\t\t\t...payload,\n\t\t\tdata: {\n\t\t\t\tisClickable: false,\n\t\t\t\taccountType: NotificationAccountType.USER,\n\t\t\t\t...payload.data,\n\t\t\t},\n\t\t};\n\n\t\tsuper('notification', finalPayload);\n\t}\n}\n\nexport class NotificationGlobalEvent extends BaseEvent<NotificationGlobalEventType> {\n\tconstructor(\n\t\tpayload: Partial<Omit<NotificationGlobalEventType, 'data'>> & {\n\t\t\tdata?: Partial<NotificationGlobalEventData>;\n\t\t}\n\t) {\n\t\tconst finalPayload: NotificationGlobalEventType = {\n\t\t\t...payload,\n\t\t\tdata: {\n\t\t\t\tisClickable: false,\n\t\t\t\taccountType: NotificationAccountType.USER,\n\t\t\t\t...payload.data,\n\t\t\t},\n\t\t};\n\n\t\tsuper('notification-global', finalPayload);\n\t}\n}"]}
|
|
@@ -32,7 +32,6 @@ let LyxaFcmSender = class LyxaFcmSender {
|
|
|
32
32
|
if (!tokens.length)
|
|
33
33
|
return { failedTokens: [], tokensToDelete: [] };
|
|
34
34
|
const messages = this.buildMultiCastMessages(type, tokens, payload);
|
|
35
|
-
console.log({ project, type, payload, messages });
|
|
36
35
|
return await this.sendToAllTokens(project, type, payload, messages);
|
|
37
36
|
}
|
|
38
37
|
buildMultiCastMessages(type, tokens, payload) {
|
|
@@ -53,7 +52,6 @@ let LyxaFcmSender = class LyxaFcmSender {
|
|
|
53
52
|
await Promise.all(messages.map(async (message) => {
|
|
54
53
|
let logData;
|
|
55
54
|
try {
|
|
56
|
-
console.log("fcm service send message: ", { project, message });
|
|
57
55
|
const result = await this.fcmService.sendMessage(project, message);
|
|
58
56
|
const payloadKnownValid = result.successCount > 0;
|
|
59
57
|
result.responses.forEach((r, i) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lyxa-fcm-sender.js","sourceRoot":"/","sources":["libraries/notification/lyxa-fcm-sender.ts"],"names":[],"mappings":"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","sourcesContent":["import { Service } from 'typedi';\nimport { FirebaseFcmService } from './firebase-fcm.service';\nimport { FirebaseMessageBuilderFactory, MultiMessageType } from './firebase-message.builder';\nimport { messaging } from 'firebase-admin';\nimport { FCMNotificationPayload, FcmProject } from './lyxa-fcm.service';\nimport { getLibraries } from '../..';\n\nexport interface FcmSendResult {\n\t/** Every token that failed to receive the message, for any reason. */\n\tfailedTokens: string[];\n\t/** Subset of failedTokens that are confirmed invalid and must be deleted. */\n\ttokensToDelete: string[];\n}\n\n@Service()\nexport class LyxaFcmSender {\n\tpublic static readonly MAX_BATCH = 500;\n\n\t/**\n\t * FCM error codes that unambiguously mean the registration token itself is\n\t * malformed or no longer registered, so it will never be valid again and is\n\t * safe to delete from our records.\n\t *\n\t * Everything else (server/internal errors, rate limits, auth/credential\n\t * issues, transport failures) is transient and must NOT cause deletion.\n\t *\n\t * @see https://firebase.google.com/docs/cloud-messaging/manage-tokens#ensuring-registration-token-freshness\n\t * @see https://firebase.google.com/docs/cloud-messaging/manage-tokens\n\t */\n\tprivate static readonly REMOVABLE_TOKEN_ERROR_CODES: ReadonlySet<string> = new Set([\n\t\t// UNREGISTERED (404): token was unregistered (app uninstalled / token refreshed).\n\t\t'messaging/registration-token-not-registered',\n\t\t// Token is not a valid FCM registration token (wrong format).\n\t\t'messaging/invalid-registration-token',\n\t]);\n\n\tconstructor(\n\t\tprivate readonly fcmService: FirebaseFcmService,\n\t\tprivate readonly builder: FirebaseMessageBuilderFactory\n\t) {}\n\n\t/**\n\t * Sends notifications and reports the outcome. `failedTokens` contains every\n\t * failure; `tokensToDelete` is the subset that is confirmed invalid and must\n\t * be removed from our records. Transient failures stay out of\n\t * `tokensToDelete` so they never trigger token deletion.\n\t */\n\tpublic async sendMessages(\n\t\tproject: FcmProject,\n\t\ttype: MultiMessageType,\n\t\ttokens: string[],\n\t\tpayload: FCMNotificationPayload\n\t): Promise<FcmSendResult> {\n\t\tif (!tokens.length) return { failedTokens: [], tokensToDelete: [] };\n\t\tconst messages = this.buildMultiCastMessages(type, tokens, payload);\t\t\n\t\tconsole.log({ project, type, payload, messages })\n\t\treturn await this.sendToAllTokens(project, type, payload, messages);\n\t}\n\n\tprivate buildMultiCastMessages(\n\t\ttype: MultiMessageType,\n\t\ttokens: string[],\n\t\tpayload: FCMNotificationPayload\n\t): messaging.MulticastMessage[] {\n\t\tconst tokenChunks = this.chunkArray(tokens, LyxaFcmSender.MAX_BATCH);\n\t\treturn tokenChunks.map(chunk => this.builder.buildMessage(type, payload, chunk));\n\t}\n\n\tprivate chunkArray<T>(arr: T[], size: number): T[][] {\n\t\tconst chunks: T[][] = [];\n\t\tfor (let i = 0; i < arr.length; i += size) {\n\t\t\tchunks.push(arr.slice(i, i + size));\n\t\t}\n\t\treturn chunks;\n\t}\n\n\tprivate async sendToAllTokens(\n\t\tproject: FcmProject,\n\t\ttype: MultiMessageType,\n\t\tpayload: FCMNotificationPayload,\n\t\tmessages: messaging.MulticastMessage[]\n\t): Promise<FcmSendResult> {\n\t\tconst logger = getLibraries().getLoggerKit();\n\t\tconst failedTokens: string[] = [];\n\t\tconst tokensToDelete: string[] = [];\n\n\t\tawait Promise.all(\n\t\t\tmessages.map(async message => {\n\t\t\t\tlet logData: Record<string, unknown>;\n\n\t\t\t\ttry {\n\t\t\t\t\tconsole.log(\"fcm service send message: \",{ project, message })\n\t\t\t\t\tconst result = await this.fcmService.sendMessage(project, message);\n\n\t\t\t\t\t// A sibling token succeeding with the identical payload proves the\n\t\t\t\t\t// payload is valid, which is required before treating an\n\t\t\t\t\t// INVALID_ARGUMENT error as a bad token (see shouldRemoveToken).\n\t\t\t\t\tconst payloadKnownValid = result.successCount > 0;\n\n\t\t\t\t\tresult.responses.forEach((r, i) => {\n\t\t\t\t\t\tif (r.success) return;\n\t\t\t\t\t\tfailedTokens.push(message.tokens[i]);\n\t\t\t\t\t\tif (this.shouldRemoveToken(r.error?.code, payloadKnownValid)) {\n\t\t\t\t\t\t\ttokensToDelete.push(message.tokens[i]);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\tlogData = {\n\t\t\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\ttype,\n\t\t\t\t\t\tconsoleNotification: type === MultiMessageType.CONSOLE,\n\t\t\t\t\t\tgroupTokens: message.tokens,\n\t\t\t\t\t\tgroupPayload: payload,\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tsuccessCount: result.successCount,\n\t\t\t\t\t\t\tfailureCount: result.failureCount,\n\t\t\t\t\t\t\tresponses: result.responses.map((r, i) => ({\n\t\t\t\t\t\t\t\ttoken: message.tokens[i],\n\t\t\t\t\t\t\t\tsuccess: r.success,\n\t\t\t\t\t\t\t\t...(r.error && {\n\t\t\t\t\t\t\t\t\terrorCode: r.error.code,\n\t\t\t\t\t\t\t\t\terrorMessage: r.error.message,\n\t\t\t\t\t\t\t\t\twillDelete: this.shouldRemoveToken(r.error.code, payloadKnownValid),\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t\tlogger.info('Called Firebase function sendEachForMulticast', {\n\t\t\t\t\t\ttag: 'notification.attempted.new',\n\t\t\t\t\t\tdata: logData,\n\t\t\t\t\t});\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// The whole batch send threw (transport/auth/infra failure), not a\n\t\t\t\t\t// per-token rejection. They count as failed, but are not proven\n\t\t\t\t\t// invalid, so they must not be deleted.\n\t\t\t\t\tfailedTokens.push(...message.tokens);\n\n\t\t\t\t\tlogData = {\n\t\t\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\ttype,\n\t\t\t\t\t\tconsoleNotification: type === MultiMessageType.CONSOLE,\n\t\t\t\t\t\tgroupTokens: message.tokens,\n\t\t\t\t\t\tgroupPayload: payload,\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t};\n\t\t\t\t\tlogger.error('Called Firebase function sendEachForMulticast', err, {\n\t\t\t\t\t\ttag: 'notification.attempted.new',\n\t\t\t\t\t\tdata: logData,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t})\n\t\t);\n\n\t\treturn { failedTokens, tokensToDelete };\n\t}\n\n\t/**\n\t * Decides whether a per-token FCM failure means the token should be removed.\n\t *\n\t * Only the codes in {@link LyxaFcmSender.REMOVABLE_TOKEN_ERROR_CODES} are\n\t * unconditionally removable. `messaging/invalid-argument` (INVALID_ARGUMENT)\n\t * is special: Firebase notes it \"can also be returned in cases of issues in\n\t * the message payload, so it signals an invalid token only if the payload is\n\t * completely valid.\" We therefore only remove on it when we know the payload\n\t * was valid (a sibling token in the same batch succeeded).\n\t *\n\t * @see https://firebase.google.com/docs/cloud-messaging/manage-tokens\n\t */\n\tprivate shouldRemoveToken(errorCode: string | undefined, payloadKnownValid: boolean): boolean {\n\t\tif (!errorCode) return false;\n\t\tif (LyxaFcmSender.REMOVABLE_TOKEN_ERROR_CODES.has(errorCode)) return true;\n\t\tif (errorCode === 'messaging/invalid-argument') return payloadKnownValid;\n\t\treturn false;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lyxa-fcm-sender.js","sourceRoot":"/","sources":["libraries/notification/lyxa-fcm-sender.ts"],"names":[],"mappings":"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","sourcesContent":["import { Service } from 'typedi';\nimport { FirebaseFcmService } from './firebase-fcm.service';\nimport { FirebaseMessageBuilderFactory, MultiMessageType } from './firebase-message.builder';\nimport { messaging } from 'firebase-admin';\nimport { FCMNotificationPayload, FcmProject } from './lyxa-fcm.service';\nimport { getLibraries } from '../..';\n\nexport interface FcmSendResult {\n\t/** Every token that failed to receive the message, for any reason. */\n\tfailedTokens: string[];\n\t/** Subset of failedTokens that are confirmed invalid and must be deleted. */\n\ttokensToDelete: string[];\n}\n\n@Service()\nexport class LyxaFcmSender {\n\tpublic static readonly MAX_BATCH = 500;\n\n\t/**\n\t * FCM error codes that unambiguously mean the registration token itself is\n\t * malformed or no longer registered, so it will never be valid again and is\n\t * safe to delete from our records.\n\t *\n\t * Everything else (server/internal errors, rate limits, auth/credential\n\t * issues, transport failures) is transient and must NOT cause deletion.\n\t *\n\t * @see https://firebase.google.com/docs/cloud-messaging/manage-tokens#ensuring-registration-token-freshness\n\t * @see https://firebase.google.com/docs/cloud-messaging/manage-tokens\n\t */\n\tprivate static readonly REMOVABLE_TOKEN_ERROR_CODES: ReadonlySet<string> = new Set([\n\t\t// UNREGISTERED (404): token was unregistered (app uninstalled / token refreshed).\n\t\t'messaging/registration-token-not-registered',\n\t\t// Token is not a valid FCM registration token (wrong format).\n\t\t'messaging/invalid-registration-token',\n\t]);\n\n\tconstructor(\n\t\tprivate readonly fcmService: FirebaseFcmService,\n\t\tprivate readonly builder: FirebaseMessageBuilderFactory\n\t) {}\n\n\t/**\n\t * Sends notifications and reports the outcome. `failedTokens` contains every\n\t * failure; `tokensToDelete` is the subset that is confirmed invalid and must\n\t * be removed from our records. Transient failures stay out of\n\t * `tokensToDelete` so they never trigger token deletion.\n\t */\n\tpublic async sendMessages(\n\t\tproject: FcmProject,\n\t\ttype: MultiMessageType,\n\t\ttokens: string[],\n\t\tpayload: FCMNotificationPayload\n\t): Promise<FcmSendResult> {\n\t\tif (!tokens.length) return { failedTokens: [], tokensToDelete: [] };\n\t\tconst messages = this.buildMultiCastMessages(type, tokens, payload);\n\t\treturn await this.sendToAllTokens(project, type, payload, messages);\n\t}\n\n\tprivate buildMultiCastMessages(\n\t\ttype: MultiMessageType,\n\t\ttokens: string[],\n\t\tpayload: FCMNotificationPayload\n\t): messaging.MulticastMessage[] {\n\t\tconst tokenChunks = this.chunkArray(tokens, LyxaFcmSender.MAX_BATCH);\n\t\treturn tokenChunks.map(chunk => this.builder.buildMessage(type, payload, chunk));\n\t}\n\n\tprivate chunkArray<T>(arr: T[], size: number): T[][] {\n\t\tconst chunks: T[][] = [];\n\t\tfor (let i = 0; i < arr.length; i += size) {\n\t\t\tchunks.push(arr.slice(i, i + size));\n\t\t}\n\t\treturn chunks;\n\t}\n\n\tprivate async sendToAllTokens(\n\t\tproject: FcmProject,\n\t\ttype: MultiMessageType,\n\t\tpayload: FCMNotificationPayload,\n\t\tmessages: messaging.MulticastMessage[]\n\t): Promise<FcmSendResult> {\n\t\tconst logger = getLibraries().getLoggerKit();\n\t\tconst failedTokens: string[] = [];\n\t\tconst tokensToDelete: string[] = [];\n\n\t\tawait Promise.all(\n\t\t\tmessages.map(async message => {\n\t\t\t\tlet logData: Record<string, unknown>;\n\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await this.fcmService.sendMessage(project, message);\n\n\t\t\t\t\t// A sibling token succeeding with the identical payload proves the\n\t\t\t\t\t// payload is valid, which is required before treating an\n\t\t\t\t\t// INVALID_ARGUMENT error as a bad token (see shouldRemoveToken).\n\t\t\t\t\tconst payloadKnownValid = result.successCount > 0;\n\n\t\t\t\t\tresult.responses.forEach((r, i) => {\n\t\t\t\t\t\tif (r.success) return;\n\t\t\t\t\t\tfailedTokens.push(message.tokens[i]);\n\t\t\t\t\t\tif (this.shouldRemoveToken(r.error?.code, payloadKnownValid)) {\n\t\t\t\t\t\t\ttokensToDelete.push(message.tokens[i]);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\tlogData = {\n\t\t\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\ttype,\n\t\t\t\t\t\tconsoleNotification: type === MultiMessageType.CONSOLE,\n\t\t\t\t\t\tgroupTokens: message.tokens,\n\t\t\t\t\t\tgroupPayload: payload,\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t\tresponse: {\n\t\t\t\t\t\t\tsuccessCount: result.successCount,\n\t\t\t\t\t\t\tfailureCount: result.failureCount,\n\t\t\t\t\t\t\tresponses: result.responses.map((r, i) => ({\n\t\t\t\t\t\t\t\ttoken: message.tokens[i],\n\t\t\t\t\t\t\t\tsuccess: r.success,\n\t\t\t\t\t\t\t\t...(r.error && {\n\t\t\t\t\t\t\t\t\terrorCode: r.error.code,\n\t\t\t\t\t\t\t\t\terrorMessage: r.error.message,\n\t\t\t\t\t\t\t\t\twillDelete: this.shouldRemoveToken(r.error.code, payloadKnownValid),\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t\tlogger.info('Called Firebase function sendEachForMulticast', {\n\t\t\t\t\t\ttag: 'notification.attempted.new',\n\t\t\t\t\t\tdata: logData,\n\t\t\t\t\t});\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// The whole batch send threw (transport/auth/infra failure), not a\n\t\t\t\t\t// per-token rejection. They count as failed, but are not proven\n\t\t\t\t\t// invalid, so they must not be deleted.\n\t\t\t\t\tfailedTokens.push(...message.tokens);\n\n\t\t\t\t\tlogData = {\n\t\t\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\ttype,\n\t\t\t\t\t\tconsoleNotification: type === MultiMessageType.CONSOLE,\n\t\t\t\t\t\tgroupTokens: message.tokens,\n\t\t\t\t\t\tgroupPayload: payload,\n\t\t\t\t\t\tmessage,\n\t\t\t\t\t};\n\t\t\t\t\tlogger.error('Called Firebase function sendEachForMulticast', err, {\n\t\t\t\t\t\ttag: 'notification.attempted.new',\n\t\t\t\t\t\tdata: logData,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t})\n\t\t);\n\n\t\treturn { failedTokens, tokensToDelete };\n\t}\n\n\t/**\n\t * Decides whether a per-token FCM failure means the token should be removed.\n\t *\n\t * Only the codes in {@link LyxaFcmSender.REMOVABLE_TOKEN_ERROR_CODES} are\n\t * unconditionally removable. `messaging/invalid-argument` (INVALID_ARGUMENT)\n\t * is special: Firebase notes it \"can also be returned in cases of issues in\n\t * the message payload, so it signals an invalid token only if the payload is\n\t * completely valid.\" We therefore only remove on it when we know the payload\n\t * was valid (a sibling token in the same batch succeeded).\n\t *\n\t * @see https://firebase.google.com/docs/cloud-messaging/manage-tokens\n\t */\n\tprivate shouldRemoveToken(errorCode: string | undefined, payloadKnownValid: boolean): boolean {\n\t\tif (!errorCode) return false;\n\t\tif (LyxaFcmSender.REMOVABLE_TOKEN_ERROR_CODES.has(errorCode)) return true;\n\t\tif (errorCode === 'messaging/invalid-argument') return payloadKnownValid;\n\t\treturn false;\n\t}\n}\n"]}
|
|
@@ -33,7 +33,6 @@ export declare class LyxaFcmService {
|
|
|
33
33
|
private separateTokens;
|
|
34
34
|
static getProjectForAccountType(accountType: NotificationAccountType): FcmProject;
|
|
35
35
|
sendToTokens(project: FcmProject, payload: FCMNotificationPayload, targets: NotificationTokenTarget[]): Promise<FcmSendResult>;
|
|
36
|
-
sendToTokensToSpecificMultiMessageType(project: FcmProject, payload: FCMNotificationPayload, targets: NotificationTokenTarget[]): Promise<FcmSendResult>;
|
|
37
36
|
private isNotifiableEntityType;
|
|
38
37
|
private isConsoleEntityType;
|
|
39
38
|
private compareVersions;
|
|
@@ -136,23 +136,6 @@ let LyxaFcmService = class LyxaFcmService {
|
|
|
136
136
|
tokensToDelete: results.flatMap(r => r.tokensToDelete),
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
|
-
async sendToTokensToSpecificMultiMessageType(project, payload, targets) {
|
|
140
|
-
if (!targets.length)
|
|
141
|
-
return { failedTokens: [], tokensToDelete: [] };
|
|
142
|
-
console.log('targets', targets);
|
|
143
|
-
const cleanedTargets = this.deduplicateTargets(targets);
|
|
144
|
-
console.log('cleanedTargets', cleanedTargets);
|
|
145
|
-
const tokens = this.separateTokens(cleanedTargets);
|
|
146
|
-
console.log({ tokens });
|
|
147
|
-
const results = await Promise.all([
|
|
148
|
-
this.lyxaFcmSender.sendMessages(project, firebase_message_builder_1.MultiMessageType.LIVE_ACTIVITY, tokens.newTokens, payload),
|
|
149
|
-
]);
|
|
150
|
-
console.log({ results });
|
|
151
|
-
return {
|
|
152
|
-
failedTokens: results.flatMap(r => r.failedTokens),
|
|
153
|
-
tokensToDelete: results.flatMap(r => r.tokensToDelete),
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
139
|
isNotifiableEntityType(type) {
|
|
157
140
|
return (type === enum_1.NotificationAccountType.USER ||
|
|
158
141
|
type === enum_1.NotificationAccountType.RIDER ||
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lyxa-fcm.service.js","sourceRoot":"/","sources":["libraries/notification/lyxa-fcm.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Service } from 'typedi';\nimport { MultiMessageType } from './firebase-message.builder';\nimport { AppPlatform, NotificationAccountType } from '../../utilities/enum';\nimport { FcmSendResult, LyxaFcmSender } from './lyxa-fcm-sender';\nimport { getLibraries } from '../..';\n\nexport enum FcmProject {\n\tUSER = 'fcm.service.user',\n\tRIDER = 'fcm.service.rider',\n\tSHOP = 'fcm.service.shop',\n\tCPANEL = 'fcm.service.cpanel',\n}\n\nexport interface FCMNotificationPayload {\n\ttitle?: string;\n\tbody?: string;\n\tdata?: Record<string, string>;\n\tsound?: string;\n\tinvisible?: boolean;\n}\n\nexport interface NotificationTokenTarget {\n\ttoken: string;\n\tentityType: NotificationAccountType;\n\tdeviceId?: string;\n\tentityId?: string;\n\tplatform?: AppPlatform;\n\tappVersion?: string;\n}\n\ntype NotifiableEntityType =\n\t| NotificationAccountType.USER\n\t| NotificationAccountType.SHOP\n\t| NotificationAccountType.RIDER\n\t| NotificationAccountType.PROFESSIONAL;\n\nconst NotificationVersionRules: Record<NotifiableEntityType, Partial<Record<AppPlatform, string>>> = {\n\t[NotificationAccountType.RIDER]: {\n\t\t[AppPlatform.IOS]: '2.0.3',\n\t\t[AppPlatform.ANDROID]: '2.0.4',\n\t},\n\n\t[NotificationAccountType.USER]: {\n\t\t[AppPlatform.IOS]: '2.0.3',\n\t\t[AppPlatform.ANDROID]: '2.0.3',\n\t},\n\n\t[NotificationAccountType.SHOP]: {\n\t\t[AppPlatform.IOS]: '2.0.3',\n\t\t[AppPlatform.ANDROID]: '2.0.3',\n\t},\n\n\t[NotificationAccountType.PROFESSIONAL]: {\n\t\t[AppPlatform.IOS]: '2.0.3',\n\t\t[AppPlatform.ANDROID]: '2.0.3',\n\t},\n};\n\nexport type LyxaTokens = {\n\toldTokens: string[];\n\tnewTokens: string[];\n\tconsoleTokens: string[];\n};\n\n@Service()\nexport class LyxaFcmService {\n\tconstructor(private readonly lyxaFcmSender: LyxaFcmSender) {}\n\n\tprivate deduplicateTargets(targets: NotificationTokenTarget[]): NotificationTokenTarget[] {\n\t\tconst sorted = targets.slice().sort((a, b) => {\n\t\t\tif (a.token !== b.token) return a.token < b.token ? -1 : 1;\n\t\t\tif (!a.appVersion && !b.appVersion) return 0;\n\t\t\tif (!a.appVersion) return 1;\n\t\t\tif (!b.appVersion) return -1;\n\t\t\treturn this.compareVersions(b.appVersion, a.appVersion);\n\t\t});\n\t\treturn sorted.filter((t, i) => i === 0 || t.token !== sorted[i - 1].token);\n\t}\n\n\tprivate separateTokens(targets: NotificationTokenTarget[]): LyxaTokens {\n\t\tconst oldTokens: string[] = [];\n\t\tconst newTokens: string[] = [];\n\t\tconst consoleTokens: string[] = [];\n\n\t\tfor (const t of targets) {\n\t\t\tif (this.isConsoleEntityType(t.entityType)) {\n\t\t\t\tconsoleTokens.push(t.token);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst useNew = this.shouldUseNewStructure(t.entityType, t.platform, t.appVersion);\n\n\t\t\tif (useNew) newTokens.push(t.token);\n\t\t\telse oldTokens.push(t.token);\n\t\t}\n\n\t\tif (true) {\n\t\t\tgetLibraries()\n\t\t\t\t.getLoggerKit()\n\t\t\t\t.debug('FCM token separation', {\n\t\t\t\t\ttag: 'notification.token.separated',\n\t\t\t\t\tdata: {\n\t\t\t\t\t\ttotalTargets: targets.length,\n\t\t\t\t\t\tbuckets: {\n\t\t\t\t\t\t\told: oldTokens.length,\n\t\t\t\t\t\t\tnew: newTokens.length,\n\t\t\t\t\t\t\tconsole: consoleTokens.length,\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttargets: targets.map(t => ({\n\t\t\t\t\t\t\tentityId: t.entityId,\n\t\t\t\t\t\t\tentityType: t.entityType,\n\t\t\t\t\t\t\tplatform: t.platform,\n\t\t\t\t\t\t\tappVersion: t.appVersion,\n\t\t\t\t\t\t\tbucket: this.isConsoleEntityType(t.entityType)\n\t\t\t\t\t\t\t\t? 'console'\n\t\t\t\t\t\t\t\t: this.shouldUseNewStructure(t.entityType, t.platform, t.appVersion)\n\t\t\t\t\t\t\t\t\t? 'new'\n\t\t\t\t\t\t\t\t\t: 'old',\n\t\t\t\t\t\t})),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\toldTokens,\n\t\t\tnewTokens,\n\t\t\tconsoleTokens,\n\t\t};\n\t}\n\n\tpublic static getProjectForAccountType(accountType: NotificationAccountType): FcmProject {\n\t\tswitch (accountType) {\n\t\t\tcase NotificationAccountType.USER:\n\t\t\t\treturn FcmProject.USER;\n\t\t\tcase NotificationAccountType.SHOP:\n\t\t\tcase NotificationAccountType.PROFESSIONAL:\n\t\t\t\treturn FcmProject.SHOP;\n\t\t\tcase NotificationAccountType.RIDER:\n\t\t\t\treturn FcmProject.RIDER;\n\t\t\tdefault:\n\t\t\t\treturn FcmProject.USER;\n\t\t}\n\t}\n\n\tpublic async sendToTokens(\n\t\tproject: FcmProject,\n\t\tpayload: FCMNotificationPayload,\n\t\ttargets: NotificationTokenTarget[]\n\t): Promise<FcmSendResult> {\n\t\tif (!targets.length) return { failedTokens: [], tokensToDelete: [] };\n\t\tconsole.log('targets', targets);\n\t\tconst cleanedTargets = this.deduplicateTargets(targets);\n\t\tconsole.log('cleanedTargets', cleanedTargets);\n\t\tconst tokens = this.separateTokens(cleanedTargets);\n\n\t\tconst results = await Promise.all([\n\t\t\tthis.lyxaFcmSender.sendMessages(project, MultiMessageType.OLD, tokens.oldTokens, payload),\n\t\t\tthis.lyxaFcmSender.sendMessages(project, MultiMessageType.DEFAULT, tokens.newTokens, payload),\t\t\t\n\t\t\tthis.lyxaFcmSender.sendMessages(\n\t\t\t\tFcmProject.CPANEL,\n\t\t\t\tMultiMessageType.CONSOLE,\n\t\t\t\ttokens.consoleTokens,\n\t\t\t\tpayload\n\t\t\t),\n\t\t]);\n\n\t\treturn {\n\t\t\tfailedTokens: results.flatMap(r => r.failedTokens),\n\t\t\ttokensToDelete: results.flatMap(r => r.tokensToDelete),\n\t\t};\n\t}\t\n\n\tpublic async sendToTokensToSpecificMultiMessageType(\n\t\tproject: FcmProject,\n\t\tpayload: FCMNotificationPayload,\n\t\ttargets: NotificationTokenTarget[]\n\t): Promise<FcmSendResult> {\n\t\tif (!targets.length) return { failedTokens: [], tokensToDelete: [] };\n\t\tconsole.log('targets', targets);\n\t\tconst cleanedTargets = this.deduplicateTargets(targets);\n\t\tconsole.log('cleanedTargets', cleanedTargets);\n\t\tconst tokens = this.separateTokens(cleanedTargets);\n\t\tconsole.log({ tokens })\n\t\tconst results = await Promise.all([\n\t\t\tthis.lyxaFcmSender.sendMessages(project, MultiMessageType.LIVE_ACTIVITY, tokens.newTokens, payload),\n\t\t]);\n\t\tconsole.log({ results })\n\t\treturn {\n\t\t\tfailedTokens: results.flatMap(r => r.failedTokens),\n\t\t\ttokensToDelete: results.flatMap(r => r.tokensToDelete),\n\t\t};\n\t}\n\n\tprivate isNotifiableEntityType(type: NotificationAccountType): type is NotifiableEntityType {\n\t\treturn (\n\t\t\ttype === NotificationAccountType.USER ||\n\t\t\ttype === NotificationAccountType.RIDER ||\n\t\t\ttype === NotificationAccountType.SHOP ||\n\t\t\ttype === NotificationAccountType.PROFESSIONAL\n\t\t);\n\t}\n\n\tprivate isConsoleEntityType(type: NotificationAccountType): type is NotifiableEntityType {\n\t\treturn (\n\t\t\ttype === NotificationAccountType.SHOP_CONSOLE ||\n\t\t\ttype === NotificationAccountType.PARENT ||\n\t\t\ttype === NotificationAccountType.VENDOR ||\n\t\t\ttype === NotificationAccountType.VENDOR_PARENT\n\t\t);\n\t}\n\n\tprivate compareVersions(v1: string, v2: string): number {\n\t\tconst a = v1.split('.').map(Number);\n\t\tconst b = v2.split('.').map(Number);\n\n\t\tfor (let i = 0; i < Math.max(a.length, b.length); i++) {\n\t\t\tconst diff = (a[i] || 0) - (b[i] || 0);\n\t\t\tif (diff !== 0) return diff;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\tprivate shouldUseNewStructure(\n\t\tentityType: NotificationAccountType,\n\t\tplatform?: AppPlatform,\n\t\tversion?: string\n\t): boolean {\n\t\tif (!this.isNotifiableEntityType(entityType)) return false;\n\n\t\tif (!platform || !version) return false;\n\n\t\tconst minVersion = NotificationVersionRules?.[entityType]?.[platform];\n\n\t\tif (!minVersion) return false;\n\n\t\treturn this.compareVersions(version, minVersion) >= 0;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lyxa-fcm.service.js","sourceRoot":"/","sources":["libraries/notification/lyxa-fcm.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Service } from 'typedi';\nimport { MultiMessageType } from './firebase-message.builder';\nimport { AppPlatform, NotificationAccountType } from '../../utilities/enum';\nimport { FcmSendResult, LyxaFcmSender } from './lyxa-fcm-sender';\nimport { getLibraries } from '../..';\n\nexport enum FcmProject {\n\tUSER = 'fcm.service.user',\n\tRIDER = 'fcm.service.rider',\n\tSHOP = 'fcm.service.shop',\n\tCPANEL = 'fcm.service.cpanel',\n}\n\nexport interface FCMNotificationPayload {\n\ttitle?: string;\n\tbody?: string;\n\tdata?: Record<string, string>;\n\tsound?: string;\n\tinvisible?: boolean;\n}\n\nexport interface NotificationTokenTarget {\n\ttoken: string;\n\tentityType: NotificationAccountType;\n\tdeviceId?: string;\n\tentityId?: string;\n\tplatform?: AppPlatform;\n\tappVersion?: string;\n}\n\ntype NotifiableEntityType =\n\t| NotificationAccountType.USER\n\t| NotificationAccountType.SHOP\n\t| NotificationAccountType.RIDER\n\t| NotificationAccountType.PROFESSIONAL;\n\nconst NotificationVersionRules: Record<NotifiableEntityType, Partial<Record<AppPlatform, string>>> = {\n\t[NotificationAccountType.RIDER]: {\n\t\t[AppPlatform.IOS]: '2.0.3',\n\t\t[AppPlatform.ANDROID]: '2.0.4',\n\t},\n\n\t[NotificationAccountType.USER]: {\n\t\t[AppPlatform.IOS]: '2.0.3',\n\t\t[AppPlatform.ANDROID]: '2.0.3',\n\t},\n\n\t[NotificationAccountType.SHOP]: {\n\t\t[AppPlatform.IOS]: '2.0.3',\n\t\t[AppPlatform.ANDROID]: '2.0.3',\n\t},\n\n\t[NotificationAccountType.PROFESSIONAL]: {\n\t\t[AppPlatform.IOS]: '2.0.3',\n\t\t[AppPlatform.ANDROID]: '2.0.3',\n\t},\n};\n\nexport type LyxaTokens = {\n\toldTokens: string[];\n\tnewTokens: string[];\n\tconsoleTokens: string[];\n};\n\n@Service()\nexport class LyxaFcmService {\n\tconstructor(private readonly lyxaFcmSender: LyxaFcmSender) {}\n\n\tprivate deduplicateTargets(targets: NotificationTokenTarget[]): NotificationTokenTarget[] {\n\t\tconst sorted = targets.slice().sort((a, b) => {\n\t\t\tif (a.token !== b.token) return a.token < b.token ? -1 : 1;\n\t\t\tif (!a.appVersion && !b.appVersion) return 0;\n\t\t\tif (!a.appVersion) return 1;\n\t\t\tif (!b.appVersion) return -1;\n\t\t\treturn this.compareVersions(b.appVersion, a.appVersion);\n\t\t});\n\t\treturn sorted.filter((t, i) => i === 0 || t.token !== sorted[i - 1].token);\n\t}\n\n\tprivate separateTokens(targets: NotificationTokenTarget[]): LyxaTokens {\n\t\tconst oldTokens: string[] = [];\n\t\tconst newTokens: string[] = [];\n\t\tconst consoleTokens: string[] = [];\n\n\t\tfor (const t of targets) {\n\t\t\tif (this.isConsoleEntityType(t.entityType)) {\n\t\t\t\tconsoleTokens.push(t.token);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst useNew = this.shouldUseNewStructure(t.entityType, t.platform, t.appVersion);\n\n\t\t\tif (useNew) newTokens.push(t.token);\n\t\t\telse oldTokens.push(t.token);\n\t\t}\n\n\t\tif (true) {\n\t\t\tgetLibraries()\n\t\t\t\t.getLoggerKit()\n\t\t\t\t.debug('FCM token separation', {\n\t\t\t\t\ttag: 'notification.token.separated',\n\t\t\t\t\tdata: {\n\t\t\t\t\t\ttotalTargets: targets.length,\n\t\t\t\t\t\tbuckets: {\n\t\t\t\t\t\t\told: oldTokens.length,\n\t\t\t\t\t\t\tnew: newTokens.length,\n\t\t\t\t\t\t\tconsole: consoleTokens.length,\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttargets: targets.map(t => ({\n\t\t\t\t\t\t\tentityId: t.entityId,\n\t\t\t\t\t\t\tentityType: t.entityType,\n\t\t\t\t\t\t\tplatform: t.platform,\n\t\t\t\t\t\t\tappVersion: t.appVersion,\n\t\t\t\t\t\t\tbucket: this.isConsoleEntityType(t.entityType)\n\t\t\t\t\t\t\t\t? 'console'\n\t\t\t\t\t\t\t\t: this.shouldUseNewStructure(t.entityType, t.platform, t.appVersion)\n\t\t\t\t\t\t\t\t\t? 'new'\n\t\t\t\t\t\t\t\t\t: 'old',\n\t\t\t\t\t\t})),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\toldTokens,\n\t\t\tnewTokens,\n\t\t\tconsoleTokens,\n\t\t};\n\t}\n\n\tpublic static getProjectForAccountType(accountType: NotificationAccountType): FcmProject {\n\t\tswitch (accountType) {\n\t\t\tcase NotificationAccountType.USER:\n\t\t\t\treturn FcmProject.USER;\n\t\t\tcase NotificationAccountType.SHOP:\n\t\t\tcase NotificationAccountType.PROFESSIONAL:\n\t\t\t\treturn FcmProject.SHOP;\n\t\t\tcase NotificationAccountType.RIDER:\n\t\t\t\treturn FcmProject.RIDER;\n\t\t\tdefault:\n\t\t\t\treturn FcmProject.USER;\n\t\t}\n\t}\n\n\tpublic async sendToTokens(\n\t\tproject: FcmProject,\n\t\tpayload: FCMNotificationPayload,\n\t\ttargets: NotificationTokenTarget[]\n\t): Promise<FcmSendResult> {\n\t\tif (!targets.length) return { failedTokens: [], tokensToDelete: [] };\n\t\tconsole.log('targets', targets);\n\t\tconst cleanedTargets = this.deduplicateTargets(targets);\n\t\tconsole.log('cleanedTargets', cleanedTargets);\n\t\tconst tokens = this.separateTokens(cleanedTargets);\n\n\t\tconst results = await Promise.all([\n\t\t\tthis.lyxaFcmSender.sendMessages(project, MultiMessageType.OLD, tokens.oldTokens, payload),\n\t\t\tthis.lyxaFcmSender.sendMessages(project, MultiMessageType.DEFAULT, tokens.newTokens, payload),\n\t\t\tthis.lyxaFcmSender.sendMessages(\n\t\t\t\tFcmProject.CPANEL,\n\t\t\t\tMultiMessageType.CONSOLE,\n\t\t\t\ttokens.consoleTokens,\n\t\t\t\tpayload\n\t\t\t),\n\t\t]);\n\n\t\treturn {\n\t\t\tfailedTokens: results.flatMap(r => r.failedTokens),\n\t\t\ttokensToDelete: results.flatMap(r => r.tokensToDelete),\n\t\t};\n\t}\n\n\tprivate isNotifiableEntityType(type: NotificationAccountType): type is NotifiableEntityType {\n\t\treturn (\n\t\t\ttype === NotificationAccountType.USER ||\n\t\t\ttype === NotificationAccountType.RIDER ||\n\t\t\ttype === NotificationAccountType.SHOP ||\n\t\t\ttype === NotificationAccountType.PROFESSIONAL\n\t\t);\n\t}\n\n\tprivate isConsoleEntityType(type: NotificationAccountType): type is NotifiableEntityType {\n\t\treturn (\n\t\t\ttype === NotificationAccountType.SHOP_CONSOLE ||\n\t\t\ttype === NotificationAccountType.PARENT ||\n\t\t\ttype === NotificationAccountType.VENDOR ||\n\t\t\ttype === NotificationAccountType.VENDOR_PARENT\n\t\t);\n\t}\n\n\tprivate compareVersions(v1: string, v2: string): number {\n\t\tconst a = v1.split('.').map(Number);\n\t\tconst b = v2.split('.').map(Number);\n\n\t\tfor (let i = 0; i < Math.max(a.length, b.length); i++) {\n\t\t\tconst diff = (a[i] || 0) - (b[i] || 0);\n\t\t\tif (diff !== 0) return diff;\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\tprivate shouldUseNewStructure(\n\t\tentityType: NotificationAccountType,\n\t\tplatform?: AppPlatform,\n\t\tversion?: string\n\t): boolean {\n\t\tif (!this.isNotifiableEntityType(entityType)) return false;\n\n\t\tif (!platform || !version) return false;\n\n\t\tconst minVersion = NotificationVersionRules?.[entityType]?.[platform];\n\n\t\tif (!minVersion) return false;\n\n\t\treturn this.compareVersions(version, minVersion) >= 0;\n\t}\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Redis from 'ioredis';
|
|
2
|
+
import { AuthEntityType } from '../auth';
|
|
2
3
|
interface LockOptions {
|
|
3
4
|
maxRetries?: number;
|
|
4
5
|
retryDelayMs?: number;
|
|
@@ -21,5 +22,9 @@ export declare class RedisService {
|
|
|
21
22
|
acquireLock(key: string, ttl?: number): Promise<string | null>;
|
|
22
23
|
releaseLock(key: string, lockValue: string): Promise<boolean>;
|
|
23
24
|
executeWithLock<T>(key: string, ttl: number, callback: () => Promise<T>, errorMessage?: string, options?: LockOptions): Promise<T>;
|
|
25
|
+
private buildLastOnlineKey;
|
|
26
|
+
setLastOnline(entityType: AuthEntityType, entityId: string, timestamp?: number): Promise<void>;
|
|
27
|
+
clearLastOnline(entityType: AuthEntityType, entityId: string): Promise<void>;
|
|
28
|
+
getOnlineEntities(entityType: AuthEntityType, cutoffTime: Date): Promise<string[]>;
|
|
24
29
|
}
|
|
25
30
|
export {};
|
|
@@ -128,6 +128,22 @@ let RedisService = class RedisService {
|
|
|
128
128
|
cause: { retryable: true },
|
|
129
129
|
});
|
|
130
130
|
}
|
|
131
|
+
buildLastOnlineKey(entityType) {
|
|
132
|
+
return `lastOnline:${entityType}`;
|
|
133
|
+
}
|
|
134
|
+
async setLastOnline(entityType, entityId, timestamp = Date.now()) {
|
|
135
|
+
const key = this.buildLastOnlineKey(entityType);
|
|
136
|
+
await this._redisClient.zadd(key, timestamp, entityId);
|
|
137
|
+
}
|
|
138
|
+
async clearLastOnline(entityType, entityId) {
|
|
139
|
+
const key = this.buildLastOnlineKey(entityType);
|
|
140
|
+
await this._redisClient.zrem(key, entityId);
|
|
141
|
+
}
|
|
142
|
+
async getOnlineEntities(entityType, cutoffTime) {
|
|
143
|
+
const key = this.buildLastOnlineKey(entityType);
|
|
144
|
+
const cutoffMs = cutoffTime.getTime();
|
|
145
|
+
return await this._redisClient.zrangebyscore(key, cutoffMs, '+inf');
|
|
146
|
+
}
|
|
131
147
|
};
|
|
132
148
|
exports.RedisService = RedisService;
|
|
133
149
|
exports.RedisService = RedisService = __decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/redis/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,sDAA4B;AAC5B,mCAAiC;AACjC,mCAAoC;AACpC,kCAAoC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"/","sources":["libraries/redis/index.ts"],"names":[],"mappings":"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","sourcesContent":["import Redis from 'ioredis';\nimport { Service } from 'typedi';\nimport { randomUUID } from 'crypto';\nimport { TRPCError } from '../trpc';\nimport { AuthEntityType } from '../auth';\n\ninterface LockOptions {\n\tmaxRetries?: number;\n\tretryDelayMs?: number;\n}\n\n@Service()\nexport class RedisService {\n\tprivate _redisClient!: Redis;\n\n\tconstructor(redisURL: string) {\n\t\tthis._redisClient = new Redis(redisURL, {\n\t\t\tmaxRetriesPerRequest: null,\n\t\t});\n\t}\n\n\tget redisClient(): Redis {\n\t\treturn this._redisClient;\n\t}\n\n\tpublic async getCachedObject<T>(key: string): Promise<T | undefined> {\n\t\tconst usageResponse = await this._redisClient.get(key);\n\n\t\tif (usageResponse != undefined) {\n\t\t\treturn JSON.parse(usageResponse) as T;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tpublic async cacheObject<T>(key: string, object: T, expiry?: number): Promise<boolean> {\n\t\tconst stringifiedObject = JSON.stringify(object);\n\n\t\tif (expiry) {\n\t\t\tawait this._redisClient.setex(key, expiry, stringifiedObject);\n\t\t} else {\n\t\t\tawait this._redisClient.set(key, stringifiedObject);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tpublic async deleteCachedObject(key: string): Promise<boolean> {\n\t\tawait this._redisClient.del(key);\n\t\treturn true;\n\t}\n\n\tpublic async getCachedObjectByGroup<T>(group: string, key: string): Promise<T | undefined> {\n\t\tconst usageResponse = await this._redisClient.hget(group, key);\n\n\t\tif (usageResponse != undefined) {\n\t\t\treturn JSON.parse(usageResponse) as T;\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tpublic async setCachedObjectByGroup<T>(group: string, key: string, object: T): Promise<boolean> {\n\t\tconst stringifiedObject = JSON.stringify(object);\n\t\tawait this._redisClient.hset(group, key, stringifiedObject);\n\n\t\treturn true;\n\t}\n\n\tpublic async deleteCachedObjectByGroup<T>(group: string, key: string): Promise<boolean> {\n\t\tawait this._redisClient.hdel(group, key);\n\t\treturn true;\n\t}\n\n\tpublic sanitizeKey(key: string) {\n\t\treturn key\n\t\t\t.replace(/[^\\w\\s]/gi, '')\n\t\t\t.replace(/\\s+/g, '-')\n\t\t\t.toLowerCase();\n\t}\n\n\tpublic async addToSet(group: string, member: string): Promise<boolean> {\n\t\tawait this._redisClient.sadd(group, member);\n\t\treturn true;\n\t}\n\n\tpublic async removeFromSet(group: string, member: string): Promise<boolean> {\n\t\tawait this._redisClient.srem(group, member);\n\t\treturn true;\n\t}\n\n\tpublic async isMemberOfSet(group: string, member: string): Promise<boolean> {\n\t\treturn (await this._redisClient.sismember(group, member)) === 1;\n\t}\n\n\tpublic async getSetMembers(group: string): Promise<string[]> {\n\t\treturn await this._redisClient.smembers(group);\n\t}\n\n\tpublic async acquireLock(\n\t\tkey: string,\n\t\tttl: number = 10000 // default 10s\n\t): Promise<string | null> {\n\t\tconst lockKey = `lock:${key}`;\n\t\tconst lockValue = randomUUID(); // unique value per process\n\n\t\tconst result = await this._redisClient.set(lockKey, lockValue, 'PX', ttl, 'NX');\n\n\t\t// If result === 'OK' → lock acquired\n\t\tif (result === 'OK') {\n\t\t\treturn lockValue;\n\t\t}\n\n\t\treturn null; // lock not acquired\n\t}\n\n\tpublic async releaseLock(key: string, lockValue: string): Promise<boolean> {\n\t\tconst lockKey = `lock:${key}`;\n\n\t\tconst luaScript = `\n\t\tif redis.call(\"get\", KEYS[1]) == ARGV[1] then\n\t\t\treturn redis.call(\"del\", KEYS[1])\n\t\telse\n\t\t\treturn 0\n\t\tend\n\t`;\n\n\t\tconst result = await this._redisClient.eval(luaScript, 1, lockKey, lockValue);\n\n\t\treturn result === 1;\n\t}\n\n\tpublic async executeWithLock<T>(\n\t\tkey: string,\n\t\tttl: number,\n\t\tcallback: () => Promise<T>,\n\t\terrorMessage?: string,\n\t\toptions?: LockOptions\n\t): Promise<T> {\n\t\tconst maxRetries = options?.maxRetries ?? 10;\n\t\tconst retryDelayMs = options?.retryDelayMs ?? 500;\n\n\t\tlet attempt = 0;\n\n\t\twhile (attempt <= maxRetries) {\n\t\t\tconst lockValue = await this.acquireLock(key, ttl);\n\n\t\t\tif (lockValue) {\n\t\t\t\ttry {\n\t\t\t\t\treturn await callback();\n\t\t\t\t} finally {\n\t\t\t\t\tawait this.releaseLock(key, lockValue);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait new Promise(r => setTimeout(r, retryDelayMs + Math.random() * 100));\n\n\t\t\tattempt++;\n\t\t}\n\n\t\tthrow new TRPCError({\n\t\t\tcode: 'CONFLICT',\n\t\t\tmessage: errorMessage || 'Resource is locked',\n\t\t\tcause: { retryable: true },\n\t\t});\n\t}\n\n\tprivate buildLastOnlineKey(entityType: AuthEntityType) {\n\t\treturn `lastOnline:${entityType}`;\n\t}\n\n\tpublic async setLastOnline(entityType: AuthEntityType, entityId: string, timestamp: number = Date.now()) {\n\t\tconst key = this.buildLastOnlineKey(entityType);\n\t\tawait this._redisClient.zadd(key, timestamp, entityId);\n\t}\n\n\tpublic async clearLastOnline(entityType: AuthEntityType, entityId: string) {\n\t\tconst key = this.buildLastOnlineKey(entityType);\n\t\tawait this._redisClient.zrem(key, entityId);\n\t}\n\n\tpublic async getOnlineEntities(entityType: AuthEntityType, cutoffTime: Date) {\n\t\tconst key = this.buildLastOnlineKey(entityType);\n\t\tconst cutoffMs = cutoffTime.getTime();\n\n\t\treturn await this._redisClient.zrangebyscore(key, cutoffMs, '+inf');\n\t}\n}\n"]}
|