@checkfirst/nestjs-outlook 9.0.1 → 9.0.2
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/interfaces/config/outlook-config.interface.d.ts +4 -0
- package/dist/repositories/outlook-webhook-subscription.repository.d.ts +1 -0
- package/dist/repositories/outlook-webhook-subscription.repository.js +8 -0
- package/dist/repositories/outlook-webhook-subscription.repository.js.map +1 -1
- package/dist/services/calendar/lifecycle-event-handler.service.d.ts +1 -3
- package/dist/services/calendar/lifecycle-event-handler.service.js +6 -15
- package/dist/services/calendar/lifecycle-event-handler.service.js.map +1 -1
- package/dist/services/subscription/microsoft-subscription.service.d.ts +4 -9
- package/dist/services/subscription/microsoft-subscription.service.js +70 -146
- package/dist/services/subscription/microsoft-subscription.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -6,6 +6,9 @@ export interface MicrosoftOutlookStateConfig {
|
|
|
6
6
|
required?: boolean;
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
|
+
export interface SubscriptionHealthConfig {
|
|
10
|
+
staleNotificationThresholdHours?: number;
|
|
11
|
+
}
|
|
9
12
|
export interface MicrosoftOutlookConfig {
|
|
10
13
|
clientId: string;
|
|
11
14
|
clientSecret: string;
|
|
@@ -15,4 +18,5 @@ export interface MicrosoftOutlookConfig {
|
|
|
15
18
|
calendarWebhookPath?: string;
|
|
16
19
|
state?: MicrosoftOutlookStateConfig;
|
|
17
20
|
revocationEmitFlagTtlMs?: number;
|
|
21
|
+
subscription?: SubscriptionHealthConfig;
|
|
18
22
|
}
|
|
@@ -18,4 +18,5 @@ export declare class OutlookWebhookSubscriptionRepository {
|
|
|
18
18
|
updateLastNotificationAt(subscriptionId: string): Promise<void>;
|
|
19
19
|
findStaleSubscriptions(staleThresholdHours: number): Promise<OutlookWebhookSubscription[]>;
|
|
20
20
|
count(options: Parameters<Repository<OutlookWebhookSubscription>['count']>[0]): Promise<number>;
|
|
21
|
+
findExpiringSoon(threshold: Date): Promise<OutlookWebhookSubscription[]>;
|
|
21
22
|
}
|
|
@@ -142,6 +142,14 @@ let OutlookWebhookSubscriptionRepository = class OutlookWebhookSubscriptionRepos
|
|
|
142
142
|
async count(options) {
|
|
143
143
|
return await this.repository.count(options);
|
|
144
144
|
}
|
|
145
|
+
async findExpiringSoon(threshold) {
|
|
146
|
+
return this.repository.find({
|
|
147
|
+
where: {
|
|
148
|
+
isActive: true,
|
|
149
|
+
expirationDateTime: (0, typeorm_2.LessThanOrEqual)(threshold),
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
}
|
|
145
153
|
};
|
|
146
154
|
exports.OutlookWebhookSubscriptionRepository = OutlookWebhookSubscriptionRepository;
|
|
147
155
|
exports.OutlookWebhookSubscriptionRepository = OutlookWebhookSubscriptionRepository = __decorate([
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outlook-webhook-subscription.repository.js","sourceRoot":"","sources":["../../src/repositories/outlook-webhook-subscription.repository.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6CAAmD;AACnD,
|
|
1
|
+
{"version":3,"file":"outlook-webhook-subscription.repository.js","sourceRoot":"","sources":["../../src/repositories/outlook-webhook-subscription.repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
2
2
|
import { OutlookWebhookSubscriptionRepository } from '../../repositories/outlook-webhook-subscription.repository';
|
|
3
|
-
import { CalendarService } from './calendar.service';
|
|
4
3
|
import { MicrosoftSubscriptionService } from '../subscription/microsoft-subscription.service';
|
|
5
4
|
import { OutlookWebhookNotificationItemDto } from '../../dto/outlook-webhook-notification.dto';
|
|
6
5
|
import { UserIdConverterService } from '../shared/user-id-converter.service';
|
|
7
6
|
export declare class LifecycleEventHandlerService {
|
|
8
|
-
private readonly calendarService;
|
|
9
7
|
private readonly subscriptionService;
|
|
10
8
|
private readonly subscriptionRepository;
|
|
11
9
|
private readonly eventEmitter;
|
|
12
10
|
private readonly userIdConverter;
|
|
13
11
|
private readonly logger;
|
|
14
|
-
constructor(
|
|
12
|
+
constructor(subscriptionService: MicrosoftSubscriptionService, subscriptionRepository: OutlookWebhookSubscriptionRepository, eventEmitter: EventEmitter2, userIdConverter: UserIdConverterService);
|
|
15
13
|
handleLifecycleEvent(notificationItem: OutlookWebhookNotificationItemDto): Promise<{
|
|
16
14
|
success: boolean;
|
|
17
15
|
message: string;
|
|
@@ -8,9 +8,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
|
-
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
-
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
-
};
|
|
14
11
|
var LifecycleEventHandlerService_1;
|
|
15
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
13
|
exports.LifecycleEventHandlerService = void 0;
|
|
@@ -19,12 +16,10 @@ const event_emitter_1 = require("@nestjs/event-emitter");
|
|
|
19
16
|
const lifecycle_event_types_enum_1 = require("../../enums/lifecycle-event-types.enum");
|
|
20
17
|
const event_types_enum_1 = require("../../enums/event-types.enum");
|
|
21
18
|
const outlook_webhook_subscription_repository_1 = require("../../repositories/outlook-webhook-subscription.repository");
|
|
22
|
-
const calendar_service_1 = require("./calendar.service");
|
|
23
19
|
const microsoft_subscription_service_1 = require("../subscription/microsoft-subscription.service");
|
|
24
20
|
const user_id_converter_service_1 = require("../shared/user-id-converter.service");
|
|
25
21
|
let LifecycleEventHandlerService = LifecycleEventHandlerService_1 = class LifecycleEventHandlerService {
|
|
26
|
-
constructor(
|
|
27
|
-
this.calendarService = calendarService;
|
|
22
|
+
constructor(subscriptionService, subscriptionRepository, eventEmitter, userIdConverter) {
|
|
28
23
|
this.subscriptionService = subscriptionService;
|
|
29
24
|
this.subscriptionRepository = subscriptionRepository;
|
|
30
25
|
this.eventEmitter = eventEmitter;
|
|
@@ -135,6 +130,7 @@ let LifecycleEventHandlerService = LifecycleEventHandlerService_1 = class Lifecy
|
|
|
135
130
|
await this.subscriptionService.createWebhookSubscription(externalUserId);
|
|
136
131
|
recreated = true;
|
|
137
132
|
this.logger.log(`[SUBSCRIPTION_REMOVED] Successfully re-created subscription for user ${subscription.userId}`);
|
|
133
|
+
this.subscriptionService.triggerCatchUpReconcile(subscription.userId, 'subscription_removed');
|
|
138
134
|
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.SUBSCRIPTION_RECREATED, {
|
|
139
135
|
subscriptionId,
|
|
140
136
|
tenantId,
|
|
@@ -187,19 +183,16 @@ let LifecycleEventHandlerService = LifecycleEventHandlerService_1 = class Lifecy
|
|
|
187
183
|
message: 'Subscription not found',
|
|
188
184
|
};
|
|
189
185
|
}
|
|
190
|
-
this.logger.log(`[MISSED]
|
|
191
|
-
|
|
192
|
-
const changesCount = await this.calendarService.syncDeltaChanges(externalUserId, subscriptionId);
|
|
193
|
-
this.logger.log(`[MISSED] Delta sync completed. Found ${changesCount} changes for subscription ${subscriptionId}`);
|
|
186
|
+
this.logger.log(`[MISSED] Triggering catch-up reconcile for user ${subscription.userId} to recover missed changes`);
|
|
187
|
+
this.subscriptionService.triggerCatchUpReconcile(subscription.userId, 'lifecycle-missed');
|
|
194
188
|
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.LIFECYCLE_MISSED, {
|
|
195
189
|
subscriptionId,
|
|
196
190
|
tenantId,
|
|
197
191
|
userId: subscription.userId,
|
|
198
|
-
changesFound: changesCount,
|
|
199
192
|
});
|
|
200
193
|
return {
|
|
201
194
|
success: true,
|
|
202
|
-
message:
|
|
195
|
+
message: 'Catch-up reconcile triggered to recover missed changes.',
|
|
203
196
|
};
|
|
204
197
|
}
|
|
205
198
|
catch (error) {
|
|
@@ -221,9 +214,7 @@ let LifecycleEventHandlerService = LifecycleEventHandlerService_1 = class Lifecy
|
|
|
221
214
|
exports.LifecycleEventHandlerService = LifecycleEventHandlerService;
|
|
222
215
|
exports.LifecycleEventHandlerService = LifecycleEventHandlerService = LifecycleEventHandlerService_1 = __decorate([
|
|
223
216
|
(0, common_1.Injectable)(),
|
|
224
|
-
|
|
225
|
-
__metadata("design:paramtypes", [calendar_service_1.CalendarService,
|
|
226
|
-
microsoft_subscription_service_1.MicrosoftSubscriptionService,
|
|
217
|
+
__metadata("design:paramtypes", [microsoft_subscription_service_1.MicrosoftSubscriptionService,
|
|
227
218
|
outlook_webhook_subscription_repository_1.OutlookWebhookSubscriptionRepository,
|
|
228
219
|
event_emitter_1.EventEmitter2,
|
|
229
220
|
user_id_converter_service_1.UserIdConverterService])
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle-event-handler.service.js","sourceRoot":"","sources":["../../../src/services/calendar/lifecycle-event-handler.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lifecycle-event-handler.service.js","sourceRoot":"","sources":["../../../src/services/calendar/lifecycle-event-handler.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,6 @@ import { OutlookWebhookSubscription } from '../../entities/outlook-webhook-subsc
|
|
|
7
7
|
import { MicrosoftUser } from '../../entities/microsoft-user.entity';
|
|
8
8
|
import { MicrosoftOutlookConfig } from '../../interfaces/config/outlook-config.interface';
|
|
9
9
|
import { UserIdConverterService } from '../shared/user-id-converter.service';
|
|
10
|
-
import { GraphRateLimiterService } from '../shared/graph-rate-limiter.service';
|
|
11
10
|
export interface MicrosoftSubscription {
|
|
12
11
|
id: string;
|
|
13
12
|
resource: string;
|
|
@@ -50,11 +49,12 @@ export declare class MicrosoftSubscriptionService {
|
|
|
50
49
|
private readonly microsoftConfig;
|
|
51
50
|
private readonly microsoftUserRepository;
|
|
52
51
|
private readonly userIdConverter;
|
|
53
|
-
private readonly rateLimiter;
|
|
54
52
|
private readonly logger;
|
|
55
53
|
private readonly graphApiBaseUrl;
|
|
56
54
|
private readonly msAuthUrl;
|
|
57
|
-
|
|
55
|
+
private readonly staleNotificationThresholdHours;
|
|
56
|
+
constructor(microsoftAuthService: MicrosoftAuthService, webhookSubscriptionRepository: OutlookWebhookSubscriptionRepository, eventEmitter: EventEmitter2, microsoftConfig: MicrosoftOutlookConfig, microsoftUserRepository: Repository<MicrosoftUser>, userIdConverter: UserIdConverterService);
|
|
57
|
+
triggerCatchUpReconcile(internalUserId: number, reason: string): void;
|
|
58
58
|
getActiveSubscriptions(accessToken: string): Promise<MicrosoftSubscription[]>;
|
|
59
59
|
getActiveSubscriptionsForClient(clientId: number, accessToken: string): Promise<MicrosoftSubscription[]>;
|
|
60
60
|
getActiveSubscriptionsForUser(userId: number, accessToken: string): Promise<MicrosoftSubscription[]>;
|
|
@@ -72,14 +72,9 @@ export declare class MicrosoftSubscriptionService {
|
|
|
72
72
|
cleanupSubscriptionsForUserAndResource(externalUserId: string, resource: string): Promise<SubscriptionCleanupResult>;
|
|
73
73
|
revokeTokens(refreshToken: string): Promise<void>;
|
|
74
74
|
fullCleanup(refreshToken: string, accessToken: string, filter?: SubscriptionFilter): Promise<SubscriptionCleanupResult>;
|
|
75
|
+
runProactiveRenewal(): Promise<void>;
|
|
75
76
|
verifySubscriptionHealth(): Promise<void>;
|
|
76
77
|
retryFailedSubscriptions(): Promise<void>;
|
|
77
|
-
private filterExpiringSoon;
|
|
78
|
-
private groupByUserId;
|
|
79
78
|
private detectStaleSubscriptions;
|
|
80
|
-
private verifySubscriptionsForUser;
|
|
81
|
-
private verifySubscriptionsBatch;
|
|
82
|
-
private handleBatchResponseItem;
|
|
83
|
-
private maybeRenewIfExpiringSoon;
|
|
84
79
|
private handleSubscriptionNotFoundAtMicrosoft;
|
|
85
80
|
}
|
|
@@ -28,24 +28,37 @@ const constants_1 = require("../../constants");
|
|
|
28
28
|
const event_types_enum_1 = require("../../enums/event-types.enum");
|
|
29
29
|
const microsoft_user_status_enum_1 = require("../../enums/microsoft-user-status.enum");
|
|
30
30
|
const user_id_converter_service_1 = require("../shared/user-id-converter.service");
|
|
31
|
-
const graph_rate_limiter_service_1 = require("../shared/graph-rate-limiter.service");
|
|
32
31
|
const outlook_api_executor_util_1 = require("../../utils/outlook-api-executor.util");
|
|
33
|
-
const
|
|
34
|
-
const HEALTH_CHECK_WINDOW_HOURS = 24;
|
|
35
|
-
const FORCE_RENEWAL_WINDOW_HOURS = 12;
|
|
36
|
-
const STALE_NOTIFICATION_THRESHOLD_HOURS = 24;
|
|
32
|
+
const DEFAULT_STALE_NOTIFICATION_THRESHOLD_HOURS = 24;
|
|
37
33
|
let MicrosoftSubscriptionService = MicrosoftSubscriptionService_1 = class MicrosoftSubscriptionService {
|
|
38
|
-
constructor(microsoftAuthService, webhookSubscriptionRepository, eventEmitter, microsoftConfig, microsoftUserRepository, userIdConverter
|
|
34
|
+
constructor(microsoftAuthService, webhookSubscriptionRepository, eventEmitter, microsoftConfig, microsoftUserRepository, userIdConverter) {
|
|
35
|
+
var _a, _b;
|
|
39
36
|
this.microsoftAuthService = microsoftAuthService;
|
|
40
37
|
this.webhookSubscriptionRepository = webhookSubscriptionRepository;
|
|
41
38
|
this.eventEmitter = eventEmitter;
|
|
42
39
|
this.microsoftConfig = microsoftConfig;
|
|
43
40
|
this.microsoftUserRepository = microsoftUserRepository;
|
|
44
41
|
this.userIdConverter = userIdConverter;
|
|
45
|
-
this.rateLimiter = rateLimiter;
|
|
46
42
|
this.logger = new common_1.Logger(MicrosoftSubscriptionService_1.name);
|
|
47
43
|
this.graphApiBaseUrl = 'https://graph.microsoft.com/v1.0';
|
|
48
44
|
this.msAuthUrl = 'https://login.microsoftonline.com/common/oauth2/v2.0';
|
|
45
|
+
const sub = (_a = this.microsoftConfig.subscription) !== null && _a !== void 0 ? _a : {};
|
|
46
|
+
this.staleNotificationThresholdHours =
|
|
47
|
+
(_b = sub.staleNotificationThresholdHours) !== null && _b !== void 0 ? _b : DEFAULT_STALE_NOTIFICATION_THRESHOLD_HOURS;
|
|
48
|
+
}
|
|
49
|
+
triggerCatchUpReconcile(internalUserId, reason) {
|
|
50
|
+
try {
|
|
51
|
+
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.EVENT_NOTIFICATION, {
|
|
52
|
+
userId: internalUserId,
|
|
53
|
+
resource: { id: `catchup:${reason}` },
|
|
54
|
+
changeType: 'updated',
|
|
55
|
+
});
|
|
56
|
+
this.logger.log(`[catchUp] Triggered catch-up reconcile for user ${internalUserId} (reason=${reason})`);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
this.logger.warn(`[catchUp] Failed to trigger catch-up reconcile for user ${internalUserId} (reason=${reason}): ` +
|
|
60
|
+
(err instanceof Error ? err.message : String(err)));
|
|
61
|
+
}
|
|
49
62
|
}
|
|
50
63
|
async getActiveSubscriptions(accessToken) {
|
|
51
64
|
try {
|
|
@@ -220,6 +233,7 @@ let MicrosoftSubscriptionService = MicrosoftSubscriptionService_1 = class Micros
|
|
|
220
233
|
}
|
|
221
234
|
this.logger.log(`[${correlationId}] Successfully renewed subscription ${subscriptionId}. ` +
|
|
222
235
|
`New expiration: ${response.data.expirationDateTime}`);
|
|
236
|
+
this.triggerCatchUpReconcile(internalUserId, 'renew');
|
|
223
237
|
return response.data;
|
|
224
238
|
}
|
|
225
239
|
catch (error) {
|
|
@@ -529,6 +543,35 @@ let MicrosoftSubscriptionService = MicrosoftSubscriptionService_1 = class Micros
|
|
|
529
543
|
this.logger.log('✅ Full cleanup completed');
|
|
530
544
|
return result;
|
|
531
545
|
}
|
|
546
|
+
async runProactiveRenewal() {
|
|
547
|
+
const correlationId = `proactive-renewal-${Date.now()}`;
|
|
548
|
+
try {
|
|
549
|
+
const threshold = new Date(Date.now() + 60 * 60 * 1000);
|
|
550
|
+
const expiringSoon = await this.webhookSubscriptionRepository.findExpiringSoon(threshold);
|
|
551
|
+
if (expiringSoon.length === 0) {
|
|
552
|
+
this.logger.debug(`[${correlationId}] No subscriptions expiring within 1 hour`);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
this.logger.log(`[${correlationId}] Proactive renewal: ${expiringSoon.length} subscription(s) expiring within 1 hour`);
|
|
556
|
+
let renewed = 0;
|
|
557
|
+
let failed = 0;
|
|
558
|
+
for (const sub of expiringSoon) {
|
|
559
|
+
try {
|
|
560
|
+
await this.renewWebhookSubscription(sub.subscriptionId, sub.userId);
|
|
561
|
+
renewed++;
|
|
562
|
+
}
|
|
563
|
+
catch (err) {
|
|
564
|
+
failed++;
|
|
565
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
566
|
+
this.logger.error(`[${correlationId}] Failed to renew subscription ${sub.subscriptionId}: ${msg}`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
this.logger.log(`[${correlationId}] Proactive renewal complete: ${renewed} renewed, ${failed} failed`);
|
|
570
|
+
}
|
|
571
|
+
catch (error) {
|
|
572
|
+
this.logger.error(`[${correlationId}] Proactive renewal job failed:`, error);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
532
575
|
async verifySubscriptionHealth() {
|
|
533
576
|
const correlationId = `health-check-${Date.now()}`;
|
|
534
577
|
try {
|
|
@@ -545,22 +588,15 @@ let MicrosoftSubscriptionService = MicrosoftSubscriptionService_1 = class Micros
|
|
|
545
588
|
this.logger.debug(`[${correlationId}] No active subscriptions to verify`);
|
|
546
589
|
return;
|
|
547
590
|
}
|
|
548
|
-
|
|
549
|
-
this.
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
counters.recreated += perUser.recreated;
|
|
558
|
-
counters.renewed += perUser.renewed;
|
|
559
|
-
counters.failed += perUser.failed;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
this.logger.log(`[${correlationId}] Health check complete: ${counters.verified} verified, ${counters.recreated} recreated, ${counters.renewed} renewed, ${counters.staleDetected} stale-detected, ${counters.failed} failed`);
|
|
563
|
-
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.HEALTH_CHECK_COMPLETED, Object.assign({ correlationId, totalActive: activeSubs.length, corruptedUsersExcluded: corruptedUserIds.length }, counters));
|
|
591
|
+
this.logger.log(`[${correlationId}] Health check: ${activeSubs.length} active subscriptions`);
|
|
592
|
+
const staleDetected = this.detectStaleSubscriptions(activeSubs, correlationId);
|
|
593
|
+
this.logger.log(`[${correlationId}] Health check complete: ${staleDetected} stale subscription(s) detected`);
|
|
594
|
+
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.HEALTH_CHECK_COMPLETED, {
|
|
595
|
+
correlationId,
|
|
596
|
+
totalActive: activeSubs.length,
|
|
597
|
+
corruptedUsersExcluded: corruptedUserIds.length,
|
|
598
|
+
staleDetected,
|
|
599
|
+
});
|
|
564
600
|
}
|
|
565
601
|
catch (error) {
|
|
566
602
|
this.logger.error(`[${correlationId}] Subscription health check job failed:`, error);
|
|
@@ -614,141 +650,23 @@ let MicrosoftSubscriptionService = MicrosoftSubscriptionService_1 = class Micros
|
|
|
614
650
|
this.logger.error(`[${correlationId}] Retry failed subscriptions job failed:`, error);
|
|
615
651
|
}
|
|
616
652
|
}
|
|
617
|
-
filterExpiringSoon(subs, hoursAhead) {
|
|
618
|
-
const threshold = new Date(Date.now() + hoursAhead * 3600 * 1000);
|
|
619
|
-
return subs.filter((sub) => sub.expirationDateTime < threshold);
|
|
620
|
-
}
|
|
621
|
-
groupByUserId(subs) {
|
|
622
|
-
const byUser = new Map();
|
|
623
|
-
for (const sub of subs) {
|
|
624
|
-
const bucket = byUser.get(sub.userId);
|
|
625
|
-
if (bucket)
|
|
626
|
-
bucket.push(sub);
|
|
627
|
-
else
|
|
628
|
-
byUser.set(sub.userId, [sub]);
|
|
629
|
-
}
|
|
630
|
-
return byUser;
|
|
631
|
-
}
|
|
632
653
|
detectStaleSubscriptions(subs, correlationId) {
|
|
633
|
-
const staleThreshold = new Date(Date.now() -
|
|
654
|
+
const staleThreshold = new Date(Date.now() - this.staleNotificationThresholdHours * 3600 * 1000);
|
|
634
655
|
let staleDetected = 0;
|
|
635
656
|
for (const sub of subs) {
|
|
636
657
|
if (!sub.lastNotificationAt || sub.lastNotificationAt >= staleThreshold)
|
|
637
658
|
continue;
|
|
638
|
-
this.logger.warn(`[${correlationId}] Subscription ${sub.subscriptionId} has not received notifications since ${sub.lastNotificationAt.toISOString()}.
|
|
659
|
+
this.logger.warn(`[${correlationId}] Subscription ${sub.subscriptionId} has not received notifications since ${sub.lastNotificationAt.toISOString()}. Triggering catch-up reconcile.`);
|
|
639
660
|
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.LIFECYCLE_MISSED, {
|
|
640
661
|
subscriptionId: sub.subscriptionId,
|
|
641
662
|
userId: sub.userId,
|
|
642
663
|
reason: 'health_check_stale',
|
|
643
664
|
});
|
|
665
|
+
this.triggerCatchUpReconcile(sub.userId, 'health_check_stale');
|
|
644
666
|
staleDetected++;
|
|
645
667
|
}
|
|
646
668
|
return staleDetected;
|
|
647
669
|
}
|
|
648
|
-
async verifySubscriptionsForUser(userId, userSubs, correlationId) {
|
|
649
|
-
const totals = { verified: 0, recreated: 0, renewed: 0, staleDetected: 0, failed: 0 };
|
|
650
|
-
let externalUserId;
|
|
651
|
-
let accessToken;
|
|
652
|
-
try {
|
|
653
|
-
externalUserId = await this.userIdConverter.internalToExternal(userId);
|
|
654
|
-
accessToken = await this.microsoftAuthService.getUserAccessToken({ internalUserId: userId });
|
|
655
|
-
}
|
|
656
|
-
catch (error) {
|
|
657
|
-
totals.failed += userSubs.length;
|
|
658
|
-
this.logger.error(`[${correlationId}] Token fetch failed for user ${userId}; skipping ${userSubs.length} sub(s):`, error);
|
|
659
|
-
return totals;
|
|
660
|
-
}
|
|
661
|
-
for (let offset = 0; offset < userSubs.length; offset += GRAPH_BATCH_SIZE) {
|
|
662
|
-
const chunk = userSubs.slice(offset, offset + GRAPH_BATCH_SIZE);
|
|
663
|
-
const chunkTotals = await this.verifySubscriptionsBatch(chunk, userId, externalUserId, accessToken, correlationId);
|
|
664
|
-
totals.verified += chunkTotals.verified;
|
|
665
|
-
totals.recreated += chunkTotals.recreated;
|
|
666
|
-
totals.renewed += chunkTotals.renewed;
|
|
667
|
-
totals.failed += chunkTotals.failed;
|
|
668
|
-
}
|
|
669
|
-
return totals;
|
|
670
|
-
}
|
|
671
|
-
async verifySubscriptionsBatch(chunk, userId, externalUserId, accessToken, correlationId) {
|
|
672
|
-
const totals = { verified: 0, recreated: 0, renewed: 0, staleDetected: 0, failed: 0 };
|
|
673
|
-
await this.rateLimiter.acquirePermit(externalUserId);
|
|
674
|
-
const batchPayload = {
|
|
675
|
-
requests: chunk.map((sub, index) => ({
|
|
676
|
-
id: String(index),
|
|
677
|
-
method: 'GET',
|
|
678
|
-
url: `/subscriptions/${sub.subscriptionId}`,
|
|
679
|
-
headers: { 'Prefer': 'IdType="ImmutableId"' },
|
|
680
|
-
})),
|
|
681
|
-
};
|
|
682
|
-
try {
|
|
683
|
-
const response = await (0, outlook_api_executor_util_1.executeGraphApiCall)(() => axios_1.default.post(`${this.graphApiBaseUrl}/$batch`, batchPayload, {
|
|
684
|
-
headers: {
|
|
685
|
-
Authorization: `Bearer ${accessToken}`,
|
|
686
|
-
'Content-Type': 'application/json',
|
|
687
|
-
},
|
|
688
|
-
}), {
|
|
689
|
-
logger: this.logger,
|
|
690
|
-
resourceName: `verify batch (user ${userId}, ${chunk.length} subs)`,
|
|
691
|
-
maxRetries: 2,
|
|
692
|
-
});
|
|
693
|
-
if (!(response === null || response === void 0 ? void 0 : response.data)) {
|
|
694
|
-
totals.failed += chunk.length;
|
|
695
|
-
this.logger.error(`[${correlationId}] Batch verify returned null for user ${userId}`);
|
|
696
|
-
return totals;
|
|
697
|
-
}
|
|
698
|
-
for (const item of response.data.responses) {
|
|
699
|
-
const sub = chunk[parseInt(item.id, 10)];
|
|
700
|
-
if (!sub)
|
|
701
|
-
continue;
|
|
702
|
-
const itemTotals = await this.handleBatchResponseItem(sub, item, correlationId);
|
|
703
|
-
totals.verified += itemTotals.verified;
|
|
704
|
-
totals.recreated += itemTotals.recreated;
|
|
705
|
-
totals.renewed += itemTotals.renewed;
|
|
706
|
-
totals.failed += itemTotals.failed;
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
catch (error) {
|
|
710
|
-
totals.failed += chunk.length;
|
|
711
|
-
this.logger.error(`[${correlationId}] Batch verify failed for user ${userId} (${chunk.length} subs):`, error);
|
|
712
|
-
}
|
|
713
|
-
return totals;
|
|
714
|
-
}
|
|
715
|
-
async handleBatchResponseItem(sub, item, correlationId) {
|
|
716
|
-
const totals = { verified: 0, recreated: 0, renewed: 0, staleDetected: 0, failed: 0 };
|
|
717
|
-
if (item.status === 200) {
|
|
718
|
-
totals.verified = 1;
|
|
719
|
-
if (await this.maybeRenewIfExpiringSoon(sub, item.body, correlationId)) {
|
|
720
|
-
totals.renewed = 1;
|
|
721
|
-
}
|
|
722
|
-
return totals;
|
|
723
|
-
}
|
|
724
|
-
if (item.status === 404) {
|
|
725
|
-
const result = await this.handleSubscriptionNotFoundAtMicrosoft(sub.subscriptionId, sub.userId, 'health_check_404', correlationId);
|
|
726
|
-
if (result === 'recreated')
|
|
727
|
-
totals.recreated = 1;
|
|
728
|
-
else
|
|
729
|
-
totals.failed = 1;
|
|
730
|
-
return totals;
|
|
731
|
-
}
|
|
732
|
-
totals.failed = 1;
|
|
733
|
-
this.logger.error(`[${correlationId}] Unexpected status ${item.status} for sub ${sub.subscriptionId}: ${JSON.stringify(item.body)}`);
|
|
734
|
-
return totals;
|
|
735
|
-
}
|
|
736
|
-
async maybeRenewIfExpiringSoon(sub, graphSub, correlationId) {
|
|
737
|
-
if (!graphSub.expirationDateTime)
|
|
738
|
-
return false;
|
|
739
|
-
const expiration = new Date(graphSub.expirationDateTime);
|
|
740
|
-
const renewalThreshold = new Date(Date.now() + FORCE_RENEWAL_WINDOW_HOURS * 3600 * 1000);
|
|
741
|
-
if (expiration >= renewalThreshold)
|
|
742
|
-
return false;
|
|
743
|
-
this.logger.log(`[${correlationId}] Subscription ${sub.subscriptionId} expires within ${FORCE_RENEWAL_WINDOW_HOURS}h. Forcing renewal.`);
|
|
744
|
-
try {
|
|
745
|
-
await this.renewWebhookSubscription(sub.subscriptionId, sub.userId);
|
|
746
|
-
}
|
|
747
|
-
catch (error) {
|
|
748
|
-
this.logger.warn(`[${correlationId}] Forced renewal of ${sub.subscriptionId} failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
749
|
-
}
|
|
750
|
-
return true;
|
|
751
|
-
}
|
|
752
670
|
async handleSubscriptionNotFoundAtMicrosoft(subscriptionId, internalUserId, reason, correlationId) {
|
|
753
671
|
this.logger.warn(`[${correlationId}] Subscription ${subscriptionId} not found at Microsoft. Deactivating and attempting re-creation.`);
|
|
754
672
|
await this.webhookSubscriptionRepository.deactivateSubscription(subscriptionId);
|
|
@@ -761,6 +679,7 @@ let MicrosoftSubscriptionService = MicrosoftSubscriptionService_1 = class Micros
|
|
|
761
679
|
userId: internalUserId,
|
|
762
680
|
reason,
|
|
763
681
|
});
|
|
682
|
+
this.triggerCatchUpReconcile(internalUserId, `recreate_${reason}`);
|
|
764
683
|
return 'recreated';
|
|
765
684
|
}
|
|
766
685
|
catch (recreateError) {
|
|
@@ -777,6 +696,12 @@ let MicrosoftSubscriptionService = MicrosoftSubscriptionService_1 = class Micros
|
|
|
777
696
|
}
|
|
778
697
|
};
|
|
779
698
|
exports.MicrosoftSubscriptionService = MicrosoftSubscriptionService;
|
|
699
|
+
__decorate([
|
|
700
|
+
(0, schedule_1.Cron)('0 * * * *'),
|
|
701
|
+
__metadata("design:type", Function),
|
|
702
|
+
__metadata("design:paramtypes", []),
|
|
703
|
+
__metadata("design:returntype", Promise)
|
|
704
|
+
], MicrosoftSubscriptionService.prototype, "runProactiveRenewal", null);
|
|
780
705
|
__decorate([
|
|
781
706
|
(0, schedule_1.Cron)('0 */6 * * *'),
|
|
782
707
|
__metadata("design:type", Function),
|
|
@@ -797,7 +722,6 @@ exports.MicrosoftSubscriptionService = MicrosoftSubscriptionService = MicrosoftS
|
|
|
797
722
|
__metadata("design:paramtypes", [microsoft_auth_service_1.MicrosoftAuthService,
|
|
798
723
|
outlook_webhook_subscription_repository_1.OutlookWebhookSubscriptionRepository,
|
|
799
724
|
event_emitter_1.EventEmitter2, Object, typeorm_2.Repository,
|
|
800
|
-
user_id_converter_service_1.UserIdConverterService
|
|
801
|
-
graph_rate_limiter_service_1.GraphRateLimiterService])
|
|
725
|
+
user_id_converter_service_1.UserIdConverterService])
|
|
802
726
|
], MicrosoftSubscriptionService);
|
|
803
727
|
//# sourceMappingURL=microsoft-subscription.service.js.map
|