@checkfirst/nestjs-outlook 7.1.1 → 7.1.3
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/constants.d.ts +3 -0
- package/dist/constants.js +4 -1
- package/dist/constants.js.map +1 -1
- package/dist/controllers/microsoft-auth.controller.js +20 -0
- package/dist/controllers/microsoft-auth.controller.js.map +1 -1
- package/dist/entities/outlook-webhook-subscription.entity.d.ts +1 -0
- package/dist/entities/outlook-webhook-subscription.entity.js +5 -0
- package/dist/entities/outlook-webhook-subscription.entity.js.map +1 -1
- package/dist/enums/event-types.enum.d.ts +3 -0
- package/dist/enums/event-types.enum.js +3 -0
- package/dist/enums/event-types.enum.js.map +1 -1
- package/dist/errors/mailbox-inactive.error.d.ts +4 -0
- package/dist/errors/mailbox-inactive.error.js +12 -0
- package/dist/errors/mailbox-inactive.error.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/migrations/1776600000000-AddLastNotificationAtToSubscriptions.d.ts +5 -0
- package/dist/migrations/1776600000000-AddLastNotificationAtToSubscriptions.js +18 -0
- package/dist/migrations/1776600000000-AddLastNotificationAtToSubscriptions.js.map +1 -0
- package/dist/repositories/outlook-webhook-subscription.repository.d.ts +5 -1
- package/dist/repositories/outlook-webhook-subscription.repository.js +23 -1
- package/dist/repositories/outlook-webhook-subscription.repository.js.map +1 -1
- package/dist/services/auth/microsoft-auth.service.d.ts +6 -3
- package/dist/services/auth/microsoft-auth.service.js +49 -8
- package/dist/services/auth/microsoft-auth.service.js.map +1 -1
- package/dist/services/calendar/calendar.service.d.ts +3 -11
- package/dist/services/calendar/calendar.service.js +22 -247
- package/dist/services/calendar/calendar.service.js.map +1 -1
- package/dist/services/calendar/lifecycle-event-handler.service.d.ts +3 -1
- package/dist/services/calendar/lifecycle-event-handler.service.js +35 -5
- package/dist/services/calendar/lifecycle-event-handler.service.js.map +1 -1
- package/dist/services/subscription/microsoft-subscription.service.d.ts +24 -0
- package/dist/services/subscription/microsoft-subscription.service.js +395 -2
- package/dist/services/subscription/microsoft-subscription.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -20,10 +20,12 @@ const lifecycle_event_types_enum_1 = require("../../enums/lifecycle-event-types.
|
|
|
20
20
|
const event_types_enum_1 = require("../../enums/event-types.enum");
|
|
21
21
|
const outlook_webhook_subscription_repository_1 = require("../../repositories/outlook-webhook-subscription.repository");
|
|
22
22
|
const calendar_service_1 = require("./calendar.service");
|
|
23
|
+
const microsoft_subscription_service_1 = require("../subscription/microsoft-subscription.service");
|
|
23
24
|
const user_id_converter_service_1 = require("../shared/user-id-converter.service");
|
|
24
25
|
let LifecycleEventHandlerService = LifecycleEventHandlerService_1 = class LifecycleEventHandlerService {
|
|
25
|
-
constructor(calendarService, subscriptionRepository, eventEmitter, userIdConverter) {
|
|
26
|
+
constructor(calendarService, subscriptionService, subscriptionRepository, eventEmitter, userIdConverter) {
|
|
26
27
|
this.calendarService = calendarService;
|
|
28
|
+
this.subscriptionService = subscriptionService;
|
|
27
29
|
this.subscriptionRepository = subscriptionRepository;
|
|
28
30
|
this.eventEmitter = eventEmitter;
|
|
29
31
|
this.userIdConverter = userIdConverter;
|
|
@@ -82,7 +84,7 @@ let LifecycleEventHandlerService = LifecycleEventHandlerService_1 = class Lifecy
|
|
|
82
84
|
};
|
|
83
85
|
}
|
|
84
86
|
this.logger.log(`[REAUTHORIZATION_REQUIRED] Attempting to renew subscription ${subscriptionId}`);
|
|
85
|
-
const renewedSubscription = await this.
|
|
87
|
+
const renewedSubscription = await this.subscriptionService.renewWebhookSubscription(subscriptionId, subscription.userId);
|
|
86
88
|
const expirationDate = renewedSubscription.expirationDateTime
|
|
87
89
|
? new Date(renewedSubscription.expirationDateTime).toISOString()
|
|
88
90
|
: 'unknown';
|
|
@@ -126,15 +128,43 @@ let LifecycleEventHandlerService = LifecycleEventHandlerService_1 = class Lifecy
|
|
|
126
128
|
}
|
|
127
129
|
await this.subscriptionRepository.deactivateSubscription(subscriptionId);
|
|
128
130
|
this.logger.log(`[SUBSCRIPTION_REMOVED] Deactivated subscription ${subscriptionId} in database`);
|
|
131
|
+
const externalUserId = await this.userIdConverter.internalToExternal(subscription.userId);
|
|
132
|
+
let recreated = false;
|
|
133
|
+
try {
|
|
134
|
+
this.logger.log(`[SUBSCRIPTION_REMOVED] Attempting to re-create subscription for user ${subscription.userId}`);
|
|
135
|
+
await this.subscriptionService.createWebhookSubscription(externalUserId);
|
|
136
|
+
recreated = true;
|
|
137
|
+
this.logger.log(`[SUBSCRIPTION_REMOVED] Successfully re-created subscription for user ${subscription.userId}`);
|
|
138
|
+
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.SUBSCRIPTION_RECREATED, {
|
|
139
|
+
subscriptionId,
|
|
140
|
+
tenantId,
|
|
141
|
+
userId: subscription.userId,
|
|
142
|
+
reason: 'subscription_removed',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (recreateError) {
|
|
146
|
+
const recreateMsg = recreateError instanceof Error ? recreateError.message : 'Unknown error';
|
|
147
|
+
this.logger.error(`[SUBSCRIPTION_REMOVED] Failed to re-create subscription for user ${subscription.userId}: ${recreateMsg}`);
|
|
148
|
+
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.SUBSCRIPTION_RECREATION_FAILED, {
|
|
149
|
+
subscriptionId,
|
|
150
|
+
tenantId,
|
|
151
|
+
userId: subscription.userId,
|
|
152
|
+
reason: 'subscription_removed',
|
|
153
|
+
error: recreateMsg,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
129
156
|
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.LIFECYCLE_SUBSCRIPTION_REMOVED, {
|
|
130
157
|
subscriptionId,
|
|
131
158
|
tenantId,
|
|
132
159
|
userId: subscription.userId,
|
|
133
160
|
resource: subscription.resource,
|
|
161
|
+
recreated,
|
|
134
162
|
});
|
|
135
163
|
return {
|
|
136
164
|
success: true,
|
|
137
|
-
message:
|
|
165
|
+
message: recreated
|
|
166
|
+
? 'Subscription removed and re-created successfully'
|
|
167
|
+
: 'Subscription marked as inactive (re-creation failed)',
|
|
138
168
|
};
|
|
139
169
|
}
|
|
140
170
|
catch (error) {
|
|
@@ -159,8 +189,7 @@ let LifecycleEventHandlerService = LifecycleEventHandlerService_1 = class Lifecy
|
|
|
159
189
|
}
|
|
160
190
|
this.logger.log(`[MISSED] Starting delta sync for user ${subscription.userId} to recover missed changes`);
|
|
161
191
|
const externalUserId = await this.userIdConverter.internalToExternal(subscription.userId);
|
|
162
|
-
const
|
|
163
|
-
const changesCount = changes.length;
|
|
192
|
+
const changesCount = await this.calendarService.syncDeltaChanges(externalUserId, subscriptionId);
|
|
164
193
|
this.logger.log(`[MISSED] Delta sync completed. Found ${changesCount} changes for subscription ${subscriptionId}`);
|
|
165
194
|
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.LIFECYCLE_MISSED, {
|
|
166
195
|
subscriptionId,
|
|
@@ -194,6 +223,7 @@ exports.LifecycleEventHandlerService = LifecycleEventHandlerService = LifecycleE
|
|
|
194
223
|
(0, common_1.Injectable)(),
|
|
195
224
|
__param(0, (0, common_1.Inject)((0, common_1.forwardRef)(() => calendar_service_1.CalendarService))),
|
|
196
225
|
__metadata("design:paramtypes", [calendar_service_1.CalendarService,
|
|
226
|
+
microsoft_subscription_service_1.MicrosoftSubscriptionService,
|
|
197
227
|
outlook_webhook_subscription_repository_1.OutlookWebhookSubscriptionRepository,
|
|
198
228
|
event_emitter_1.EventEmitter2,
|
|
199
229
|
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":";;;;;;;;;;;;;;;;AAAA,2CAAwE;AACxE,yDAAsD;AACtD,uFAA4E;AAC5E,mEAAiE;AACjE,wHAAkH;AAClH,yDAAqD;
|
|
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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"}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
2
|
+
import { Repository } from 'typeorm';
|
|
3
|
+
import { Subscription } from '../../types';
|
|
4
|
+
import { MicrosoftAuthService } from '../auth/microsoft-auth.service';
|
|
5
|
+
import { OutlookWebhookSubscriptionRepository } from '../../repositories/outlook-webhook-subscription.repository';
|
|
6
|
+
import { OutlookWebhookSubscription } from '../../entities/outlook-webhook-subscription.entity';
|
|
7
|
+
import { MicrosoftUser } from '../../entities/microsoft-user.entity';
|
|
8
|
+
import { MicrosoftOutlookConfig } from '../../interfaces/config/outlook-config.interface';
|
|
9
|
+
import { UserIdConverterService } from '../shared/user-id-converter.service';
|
|
1
10
|
export interface MicrosoftSubscription {
|
|
2
11
|
id: string;
|
|
3
12
|
resource: string;
|
|
@@ -23,16 +32,31 @@ export interface SubscriptionCleanupResult {
|
|
|
23
32
|
}>;
|
|
24
33
|
}
|
|
25
34
|
export declare class MicrosoftSubscriptionService {
|
|
35
|
+
private readonly microsoftAuthService;
|
|
36
|
+
private readonly webhookSubscriptionRepository;
|
|
37
|
+
private readonly eventEmitter;
|
|
38
|
+
private readonly microsoftConfig;
|
|
39
|
+
private readonly microsoftUserRepository;
|
|
40
|
+
private readonly userIdConverter;
|
|
26
41
|
private readonly logger;
|
|
27
42
|
private readonly graphApiBaseUrl;
|
|
28
43
|
private readonly msAuthUrl;
|
|
44
|
+
constructor(microsoftAuthService: MicrosoftAuthService, webhookSubscriptionRepository: OutlookWebhookSubscriptionRepository, eventEmitter: EventEmitter2, microsoftConfig: MicrosoftOutlookConfig, microsoftUserRepository: Repository<MicrosoftUser>, userIdConverter: UserIdConverterService);
|
|
29
45
|
getActiveSubscriptions(accessToken: string): Promise<MicrosoftSubscription[]>;
|
|
30
46
|
getActiveSubscriptionsForClient(clientId: number, accessToken: string): Promise<MicrosoftSubscription[]>;
|
|
31
47
|
getActiveSubscriptionsForUser(userId: number, accessToken: string): Promise<MicrosoftSubscription[]>;
|
|
32
48
|
deleteSubscription(subscriptionId: string, accessToken: string): Promise<void>;
|
|
49
|
+
createWebhookSubscription(externalUserId: string): Promise<Subscription>;
|
|
50
|
+
renewWebhookSubscription(subscriptionId: string, internalUserId: number): Promise<Subscription>;
|
|
51
|
+
deleteWebhookSubscription(subscriptionId: string, userId: string | number): Promise<boolean>;
|
|
52
|
+
getSubscription(subscriptionId: string): Promise<OutlookWebhookSubscription | null>;
|
|
53
|
+
getActiveSubscriptionForUser(externalUserId: string): Promise<string | null>;
|
|
54
|
+
trackNotificationReceived(subscriptionId: string): void;
|
|
33
55
|
cleanupSubscriptions(options: SubscriptionCleanupOptions): Promise<SubscriptionCleanupResult>;
|
|
34
56
|
cleanupSubscriptionsForClient(clientId: number, accessToken: string): Promise<SubscriptionCleanupResult>;
|
|
35
57
|
cleanupSubscriptionsForUser(userId: number, accessToken: string): Promise<SubscriptionCleanupResult>;
|
|
36
58
|
revokeTokens(refreshToken: string): Promise<void>;
|
|
37
59
|
fullCleanup(refreshToken: string, accessToken: string, filter?: SubscriptionFilter): Promise<SubscriptionCleanupResult>;
|
|
60
|
+
renewSubscriptions(): Promise<void>;
|
|
61
|
+
verifySubscriptionHealth(): Promise<void>;
|
|
38
62
|
}
|
|
@@ -5,14 +5,37 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
7
|
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
8
14
|
var MicrosoftSubscriptionService_1;
|
|
9
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
16
|
exports.MicrosoftSubscriptionService = void 0;
|
|
17
|
+
const crypto_1 = require("crypto");
|
|
11
18
|
const common_1 = require("@nestjs/common");
|
|
19
|
+
const event_emitter_1 = require("@nestjs/event-emitter");
|
|
20
|
+
const schedule_1 = require("@nestjs/schedule");
|
|
21
|
+
const typeorm_1 = require("@nestjs/typeorm");
|
|
22
|
+
const typeorm_2 = require("typeorm");
|
|
12
23
|
const axios_1 = require("axios");
|
|
24
|
+
const microsoft_auth_service_1 = require("../auth/microsoft-auth.service");
|
|
25
|
+
const outlook_webhook_subscription_repository_1 = require("../../repositories/outlook-webhook-subscription.repository");
|
|
26
|
+
const microsoft_user_entity_1 = require("../../entities/microsoft-user.entity");
|
|
27
|
+
const constants_1 = require("../../constants");
|
|
28
|
+
const event_types_enum_1 = require("../../enums/event-types.enum");
|
|
29
|
+
const user_id_converter_service_1 = require("../shared/user-id-converter.service");
|
|
13
30
|
const outlook_api_executor_util_1 = require("../../utils/outlook-api-executor.util");
|
|
14
31
|
let MicrosoftSubscriptionService = MicrosoftSubscriptionService_1 = class MicrosoftSubscriptionService {
|
|
15
|
-
constructor() {
|
|
32
|
+
constructor(microsoftAuthService, webhookSubscriptionRepository, eventEmitter, microsoftConfig, microsoftUserRepository, userIdConverter) {
|
|
33
|
+
this.microsoftAuthService = microsoftAuthService;
|
|
34
|
+
this.webhookSubscriptionRepository = webhookSubscriptionRepository;
|
|
35
|
+
this.eventEmitter = eventEmitter;
|
|
36
|
+
this.microsoftConfig = microsoftConfig;
|
|
37
|
+
this.microsoftUserRepository = microsoftUserRepository;
|
|
38
|
+
this.userIdConverter = userIdConverter;
|
|
16
39
|
this.logger = new common_1.Logger(MicrosoftSubscriptionService_1.name);
|
|
17
40
|
this.graphApiBaseUrl = 'https://graph.microsoft.com/v1.0';
|
|
18
41
|
this.msAuthUrl = 'https://login.microsoftonline.com/common/oauth2/v2.0';
|
|
@@ -68,6 +91,247 @@ let MicrosoftSubscriptionService = MicrosoftSubscriptionService_1 = class Micros
|
|
|
68
91
|
throw error;
|
|
69
92
|
}
|
|
70
93
|
}
|
|
94
|
+
async createWebhookSubscription(externalUserId) {
|
|
95
|
+
var _a, _b;
|
|
96
|
+
const internalUserId = await this.userIdConverter.externalToInternal(externalUserId, { cache: false });
|
|
97
|
+
const correlationId = `webhook-${internalUserId}-${Date.now()}`;
|
|
98
|
+
this.logger.log(`[${correlationId}] Starting webhook subscription creation for user ${internalUserId}`);
|
|
99
|
+
try {
|
|
100
|
+
this.logger.log(`[${correlationId}] Fetching access token for user ${internalUserId}`);
|
|
101
|
+
const accessToken = await this.microsoftAuthService.getUserAccessToken({ internalUserId, cache: false });
|
|
102
|
+
this.logger.log(`[${correlationId}] Successfully obtained access token`);
|
|
103
|
+
const expirationDateTime = new Date();
|
|
104
|
+
expirationDateTime.setHours(expirationDateTime.getHours() + 72);
|
|
105
|
+
const appUrl = this.microsoftConfig.backendBaseUrl || "http://localhost:3000";
|
|
106
|
+
const basePath = this.microsoftConfig.basePath;
|
|
107
|
+
const basePathUrl = basePath ? `${appUrl}/${basePath}` : appUrl;
|
|
108
|
+
const webhookPath = this.microsoftConfig.calendarWebhookPath || '/calendar/webhook';
|
|
109
|
+
const notificationUrl = `${basePathUrl}${webhookPath}`;
|
|
110
|
+
const subscriptionData = {
|
|
111
|
+
changeType: "created,updated,deleted",
|
|
112
|
+
notificationUrl,
|
|
113
|
+
lifecycleNotificationUrl: notificationUrl,
|
|
114
|
+
resource: "/me/events",
|
|
115
|
+
expirationDateTime: expirationDateTime.toISOString(),
|
|
116
|
+
clientState: `user_${internalUserId}_${(0, crypto_1.randomUUID)()}`,
|
|
117
|
+
};
|
|
118
|
+
this.logger.log(`[${correlationId}] Creating webhook subscription with notificationUrl: ${notificationUrl}`);
|
|
119
|
+
this.logger.debug(`[${correlationId}] Subscription data: ${JSON.stringify(subscriptionData)}`);
|
|
120
|
+
this.logger.log(`[${correlationId}] Sending POST request to Microsoft Graph API`);
|
|
121
|
+
const response = await (0, outlook_api_executor_util_1.executeGraphApiCall)(() => axios_1.default.post(`${this.graphApiBaseUrl}/subscriptions`, subscriptionData, {
|
|
122
|
+
headers: {
|
|
123
|
+
Authorization: `Bearer ${accessToken}`,
|
|
124
|
+
"Content-Type": "application/json",
|
|
125
|
+
"Prefer": 'IdType="ImmutableId"',
|
|
126
|
+
},
|
|
127
|
+
}), {
|
|
128
|
+
logger: this.logger,
|
|
129
|
+
resourceName: `create webhook subscription for user ${internalUserId}`,
|
|
130
|
+
maxRetries: 7,
|
|
131
|
+
});
|
|
132
|
+
if (!(response === null || response === void 0 ? void 0 : response.data)) {
|
|
133
|
+
throw new Error('Subscription creation returned null response');
|
|
134
|
+
}
|
|
135
|
+
this.logger.log(`[${correlationId}] Created webhook subscription ${response.data.id || "unknown"} for user ${internalUserId}`);
|
|
136
|
+
this.logger.log(`[${correlationId}] Saving subscription to database (internalUserId: ${internalUserId}, externalUserId: ${externalUserId})`);
|
|
137
|
+
await this.webhookSubscriptionRepository.saveSubscription({
|
|
138
|
+
subscriptionId: response.data.id,
|
|
139
|
+
userId: internalUserId,
|
|
140
|
+
resource: response.data.resource,
|
|
141
|
+
changeType: response.data.changeType,
|
|
142
|
+
clientState: response.data.clientState || "",
|
|
143
|
+
notificationUrl: response.data.notificationUrl,
|
|
144
|
+
expirationDateTime: response.data.expirationDateTime
|
|
145
|
+
? new Date(response.data.expirationDateTime)
|
|
146
|
+
: new Date(),
|
|
147
|
+
});
|
|
148
|
+
this.logger.log(`[${correlationId}] Successfully stored subscription in database`);
|
|
149
|
+
this.logger.log(`[${correlationId}] Webhook subscription creation completed successfully`);
|
|
150
|
+
return response.data;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
154
|
+
this.logger.error(`[${correlationId}] Microsoft Graph API error: Status ${(_a = error.response) === null || _a === void 0 ? void 0 : _a.status}, ` +
|
|
155
|
+
`Message: ${JSON.stringify((_b = error.response) === null || _b === void 0 ? void 0 : _b.data)}`);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
this.logger.error(`[${correlationId}] Failed to create webhook subscription:`, error);
|
|
159
|
+
}
|
|
160
|
+
throw new Error(`Failed to create webhook subscription: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async renewWebhookSubscription(subscriptionId, internalUserId) {
|
|
164
|
+
var _a, _b;
|
|
165
|
+
const correlationId = `renew-${subscriptionId}-${Date.now()}`;
|
|
166
|
+
try {
|
|
167
|
+
this.logger.log(`[${correlationId}] Attempting to renew subscription ${subscriptionId} for user ${internalUserId}`);
|
|
168
|
+
const user = await this.microsoftUserRepository.findOne({
|
|
169
|
+
where: { id: internalUserId, isActive: true }
|
|
170
|
+
});
|
|
171
|
+
if (!user) {
|
|
172
|
+
this.logger.warn(`[${correlationId}] User ${internalUserId} not found or inactive. ` +
|
|
173
|
+
`Deactivating subscription ${subscriptionId}`);
|
|
174
|
+
await this.webhookSubscriptionRepository.deactivateSubscription(subscriptionId);
|
|
175
|
+
throw new Error(`Cannot renew subscription ${subscriptionId}: ` +
|
|
176
|
+
`User ${internalUserId} not found or inactive. ` +
|
|
177
|
+
`Subscription has been automatically deactivated.`);
|
|
178
|
+
}
|
|
179
|
+
this.logger.debug(`[${correlationId}] User ${internalUserId} validated successfully`);
|
|
180
|
+
const accessToken = await this.microsoftAuthService.getUserAccessToken({
|
|
181
|
+
internalUserId
|
|
182
|
+
});
|
|
183
|
+
this.logger.debug(`[${correlationId}] Access token obtained`);
|
|
184
|
+
const expirationDateTime = new Date();
|
|
185
|
+
expirationDateTime.setHours(expirationDateTime.getHours() + 72);
|
|
186
|
+
const renewalData = {
|
|
187
|
+
expirationDateTime: expirationDateTime.toISOString(),
|
|
188
|
+
};
|
|
189
|
+
this.logger.debug(`[${correlationId}] Calling Microsoft Graph API to renew subscription`);
|
|
190
|
+
const response = await (0, outlook_api_executor_util_1.executeGraphApiCall)(() => axios_1.default.patch(`${this.graphApiBaseUrl}/subscriptions/${subscriptionId}`, renewalData, {
|
|
191
|
+
headers: {
|
|
192
|
+
Authorization: `Bearer ${accessToken}`,
|
|
193
|
+
"Content-Type": "application/json",
|
|
194
|
+
"Prefer": 'IdType="ImmutableId"',
|
|
195
|
+
},
|
|
196
|
+
}), {
|
|
197
|
+
logger: this.logger,
|
|
198
|
+
resourceName: `renew webhook subscription ${subscriptionId} for user ${internalUserId}`,
|
|
199
|
+
maxRetries: 7,
|
|
200
|
+
});
|
|
201
|
+
if (!(response === null || response === void 0 ? void 0 : response.data)) {
|
|
202
|
+
throw new Error('Subscription renewal returned null response');
|
|
203
|
+
}
|
|
204
|
+
this.logger.debug(`[${correlationId}] Microsoft Graph API returned status: ${response.status}`);
|
|
205
|
+
if (response.data.expirationDateTime) {
|
|
206
|
+
await this.webhookSubscriptionRepository.updateSubscriptionExpiration(subscriptionId, new Date(response.data.expirationDateTime));
|
|
207
|
+
this.logger.debug(`[${correlationId}] Updated local database with new expiration: ${response.data.expirationDateTime}`);
|
|
208
|
+
}
|
|
209
|
+
this.logger.log(`[${correlationId}] Successfully renewed subscription ${subscriptionId}. ` +
|
|
210
|
+
`New expiration: ${response.data.expirationDateTime}`);
|
|
211
|
+
return response.data;
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
215
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
216
|
+
const statusCode = (_a = error.response) === null || _a === void 0 ? void 0 : _a.status;
|
|
217
|
+
if (statusCode === 404) {
|
|
218
|
+
this.logger.warn(`[${correlationId}] Subscription ${subscriptionId} not found at Microsoft. ` +
|
|
219
|
+
`Deactivating and attempting re-creation.`);
|
|
220
|
+
await this.webhookSubscriptionRepository.deactivateSubscription(subscriptionId);
|
|
221
|
+
try {
|
|
222
|
+
const externalUserId = await this.userIdConverter.internalToExternal(internalUserId);
|
|
223
|
+
await this.createWebhookSubscription(externalUserId);
|
|
224
|
+
this.logger.log(`[${correlationId}] Successfully re-created subscription for user ${internalUserId} after 404`);
|
|
225
|
+
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.SUBSCRIPTION_RECREATED, {
|
|
226
|
+
subscriptionId,
|
|
227
|
+
userId: internalUserId,
|
|
228
|
+
reason: 'renewal_404',
|
|
229
|
+
});
|
|
230
|
+
return {};
|
|
231
|
+
}
|
|
232
|
+
catch (recreateError) {
|
|
233
|
+
const recreateMsg = recreateError instanceof Error ? recreateError.message : 'Unknown error';
|
|
234
|
+
this.logger.error(`[${correlationId}] Failed to re-create subscription for user ${internalUserId}: ${recreateMsg}`);
|
|
235
|
+
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.SUBSCRIPTION_RECREATION_FAILED, {
|
|
236
|
+
subscriptionId,
|
|
237
|
+
userId: internalUserId,
|
|
238
|
+
reason: 'renewal_404',
|
|
239
|
+
error: recreateMsg,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
throw new Error(`Subscription ${subscriptionId} not found at Microsoft. ` +
|
|
243
|
+
`Subscription has been deactivated and re-creation failed.`);
|
|
244
|
+
}
|
|
245
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
246
|
+
this.logger.error(`[${correlationId}] Authentication failed for subscription ${subscriptionId}. ` +
|
|
247
|
+
`Status: ${statusCode}, Response: ${JSON.stringify((_b = error.response) === null || _b === void 0 ? void 0 : _b.data)}. ` +
|
|
248
|
+
`Deactivating subscription to prevent repeated failures.`);
|
|
249
|
+
await this.webhookSubscriptionRepository.deactivateSubscription(subscriptionId);
|
|
250
|
+
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.SUBSCRIPTION_AUTH_FAILED, {
|
|
251
|
+
subscriptionId,
|
|
252
|
+
userId: internalUserId,
|
|
253
|
+
statusCode,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
if (statusCode === 429) {
|
|
257
|
+
this.logger.warn(`[${correlationId}] Rate limited by Microsoft Graph API for subscription ${subscriptionId}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
this.logger.error(`[${correlationId}] Failed to renew subscription ${subscriptionId}: ${errorMessage}`);
|
|
261
|
+
throw new Error(`Failed to renew webhook subscription: ${errorMessage}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
async deleteWebhookSubscription(subscriptionId, userId) {
|
|
265
|
+
var _a;
|
|
266
|
+
const correlationId = `delete-sub-${subscriptionId}-${Date.now()}`;
|
|
267
|
+
try {
|
|
268
|
+
this.logger.log(`[${correlationId}] Deleting calendar subscription ${subscriptionId} for user ${userId}`);
|
|
269
|
+
const internalUserId = await this.userIdConverter.toInternalUserId(userId);
|
|
270
|
+
const accessToken = await this.microsoftAuthService.getUserAccessToken({
|
|
271
|
+
internalUserId,
|
|
272
|
+
includeInactive: true
|
|
273
|
+
});
|
|
274
|
+
this.logger.debug(`[${correlationId}] Access token obtained`);
|
|
275
|
+
this.logger.debug(`[${correlationId}] Calling Microsoft Graph API to delete subscription`);
|
|
276
|
+
await this.deleteSubscription(subscriptionId, accessToken);
|
|
277
|
+
this.logger.log(`[${correlationId}] Successfully deleted subscription ${subscriptionId} at Microsoft`);
|
|
278
|
+
await this.webhookSubscriptionRepository.deactivateSubscription(subscriptionId);
|
|
279
|
+
this.logger.debug(`[${correlationId}] Deactivated subscription in local database`);
|
|
280
|
+
const otherActiveSubscriptions = await this.webhookSubscriptionRepository.count({
|
|
281
|
+
where: {
|
|
282
|
+
userId: internalUserId,
|
|
283
|
+
isActive: true,
|
|
284
|
+
subscriptionId: (0, typeorm_2.Not)(subscriptionId)
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
if (otherActiveSubscriptions === 0) {
|
|
288
|
+
const updateCriteria = typeof userId === 'string' ? { externalUserId: userId } : { id: userId };
|
|
289
|
+
await this.microsoftUserRepository.update(updateCriteria, { isActive: false });
|
|
290
|
+
this.logger.debug(`[${correlationId}] Marked Microsoft user as inactive - no other subscriptions remain`);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
this.logger.debug(`[${correlationId}] User still has ${otherActiveSubscriptions} other active subscription(s), keeping user active`);
|
|
294
|
+
}
|
|
295
|
+
this.logger.log(`[${correlationId}] Successfully deleted calendar subscription ${subscriptionId}`);
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
300
|
+
if (axios_1.default.isAxiosError(error) && ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404) {
|
|
301
|
+
this.logger.log(`[${correlationId}] Subscription ${subscriptionId} not found at Microsoft, ` +
|
|
302
|
+
`cleaning up local database`);
|
|
303
|
+
await this.webhookSubscriptionRepository.deactivateSubscription(subscriptionId);
|
|
304
|
+
this.logger.log(`[${correlationId}] Successfully cleaned up orphaned subscription ${subscriptionId}`);
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
this.logger.error(`[${correlationId}] Failed to delete subscription ${subscriptionId}: ${errorMessage}`);
|
|
308
|
+
throw new Error(`Failed to delete webhook subscription: ${errorMessage}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
async getSubscription(subscriptionId) {
|
|
312
|
+
return this.webhookSubscriptionRepository.findBySubscriptionId(subscriptionId);
|
|
313
|
+
}
|
|
314
|
+
async getActiveSubscriptionForUser(externalUserId) {
|
|
315
|
+
var _a;
|
|
316
|
+
try {
|
|
317
|
+
const internalUserId = await this.userIdConverter.externalToInternal(externalUserId, { cache: false });
|
|
318
|
+
this.logger.log(`[getActiveSubscriptionForUser] Getting active subscription for user ${externalUserId} (internalUserId: ${internalUserId})`);
|
|
319
|
+
const subscription = await this.webhookSubscriptionRepository.findActiveByUserId(internalUserId);
|
|
320
|
+
this.logger.log(`[getActiveSubscriptionForUser] Found subscription: ${subscription === null || subscription === void 0 ? void 0 : subscription.subscriptionId}`);
|
|
321
|
+
return (_a = subscription === null || subscription === void 0 ? void 0 : subscription.subscriptionId) !== null && _a !== void 0 ? _a : null;
|
|
322
|
+
}
|
|
323
|
+
catch (_b) {
|
|
324
|
+
this.logger.debug(`No active subscription for user ${externalUserId}`);
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
trackNotificationReceived(subscriptionId) {
|
|
329
|
+
this.webhookSubscriptionRepository
|
|
330
|
+
.updateLastNotificationAt(subscriptionId)
|
|
331
|
+
.catch((err) => {
|
|
332
|
+
this.logger.warn(`Failed to update lastNotificationAt for subscription ${subscriptionId}: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
333
|
+
});
|
|
334
|
+
}
|
|
71
335
|
async cleanupSubscriptions(options) {
|
|
72
336
|
const { accessToken, filter } = options;
|
|
73
337
|
const result = {
|
|
@@ -158,9 +422,138 @@ let MicrosoftSubscriptionService = MicrosoftSubscriptionService_1 = class Micros
|
|
|
158
422
|
this.logger.log('✅ Full cleanup completed');
|
|
159
423
|
return result;
|
|
160
424
|
}
|
|
425
|
+
async renewSubscriptions() {
|
|
426
|
+
try {
|
|
427
|
+
const expiringSubscriptions = await this.webhookSubscriptionRepository.findSubscriptionsNeedingRenewal(24, { skipLocked: true });
|
|
428
|
+
if (expiringSubscriptions.length === 0) {
|
|
429
|
+
this.logger.debug("No subscriptions need renewal");
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
this.logger.log(`Found ${String(expiringSubscriptions.length)} subscriptions that need renewal`);
|
|
433
|
+
for (const subscription of expiringSubscriptions) {
|
|
434
|
+
try {
|
|
435
|
+
await this.renewWebhookSubscription(subscription.subscriptionId, subscription.userId);
|
|
436
|
+
}
|
|
437
|
+
catch (error) {
|
|
438
|
+
this.logger.error(`Failed to renew subscription ${subscription.subscriptionId}:`, error);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
this.logger.error("Error in subscription renewal job:", error);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
async verifySubscriptionHealth() {
|
|
447
|
+
const correlationId = `health-check-${Date.now()}`;
|
|
448
|
+
try {
|
|
449
|
+
const activeSubscriptions = await this.webhookSubscriptionRepository.findActiveSubscriptions();
|
|
450
|
+
if (activeSubscriptions.length === 0) {
|
|
451
|
+
this.logger.debug(`[${correlationId}] No active subscriptions to verify`);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
this.logger.log(`[${correlationId}] Verifying health of ${String(activeSubscriptions.length)} active subscriptions`);
|
|
455
|
+
let verified = 0;
|
|
456
|
+
let recreated = 0;
|
|
457
|
+
let staleDetected = 0;
|
|
458
|
+
let failed = 0;
|
|
459
|
+
for (const subscription of activeSubscriptions) {
|
|
460
|
+
try {
|
|
461
|
+
const accessToken = await this.microsoftAuthService.getUserAccessToken({
|
|
462
|
+
internalUserId: subscription.userId,
|
|
463
|
+
});
|
|
464
|
+
const response = await (0, outlook_api_executor_util_1.executeGraphApiCall)(() => axios_1.default.get(`${this.graphApiBaseUrl}/subscriptions/${subscription.subscriptionId}`, {
|
|
465
|
+
headers: {
|
|
466
|
+
Authorization: `Bearer ${accessToken}`,
|
|
467
|
+
"Prefer": 'IdType="ImmutableId"',
|
|
468
|
+
},
|
|
469
|
+
}), {
|
|
470
|
+
logger: this.logger,
|
|
471
|
+
resourceName: `verify subscription ${subscription.subscriptionId}`,
|
|
472
|
+
maxRetries: 2,
|
|
473
|
+
return404AsNull: true,
|
|
474
|
+
});
|
|
475
|
+
if (!response) {
|
|
476
|
+
this.logger.warn(`[${correlationId}] Subscription ${subscription.subscriptionId} not found at Microsoft. Attempting re-creation.`);
|
|
477
|
+
await this.webhookSubscriptionRepository.deactivateSubscription(subscription.subscriptionId);
|
|
478
|
+
try {
|
|
479
|
+
const externalUserId = await this.userIdConverter.internalToExternal(subscription.userId);
|
|
480
|
+
await this.createWebhookSubscription(externalUserId);
|
|
481
|
+
recreated++;
|
|
482
|
+
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.SUBSCRIPTION_RECREATED, {
|
|
483
|
+
subscriptionId: subscription.subscriptionId,
|
|
484
|
+
userId: subscription.userId,
|
|
485
|
+
reason: 'health_check_404',
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
catch (recreateError) {
|
|
489
|
+
failed++;
|
|
490
|
+
this.logger.error(`[${correlationId}] Failed to re-create subscription for user ${String(subscription.userId)}: ${recreateError instanceof Error ? recreateError.message : 'Unknown error'}`);
|
|
491
|
+
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.SUBSCRIPTION_RECREATION_FAILED, {
|
|
492
|
+
subscriptionId: subscription.subscriptionId,
|
|
493
|
+
userId: subscription.userId,
|
|
494
|
+
reason: 'health_check_404',
|
|
495
|
+
error: recreateError instanceof Error ? recreateError.message : 'Unknown error',
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
verified++;
|
|
501
|
+
if (response.data.expirationDateTime) {
|
|
502
|
+
const expiration = new Date(response.data.expirationDateTime);
|
|
503
|
+
const twelveHoursFromNow = new Date();
|
|
504
|
+
twelveHoursFromNow.setHours(twelveHoursFromNow.getHours() + 12);
|
|
505
|
+
if (expiration < twelveHoursFromNow) {
|
|
506
|
+
this.logger.log(`[${correlationId}] Subscription ${subscription.subscriptionId} expires within 12h. Forcing renewal.`);
|
|
507
|
+
await this.renewWebhookSubscription(subscription.subscriptionId, subscription.userId);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (subscription.lastNotificationAt) {
|
|
511
|
+
const twentyFourHoursAgo = new Date();
|
|
512
|
+
twentyFourHoursAgo.setHours(twentyFourHoursAgo.getHours() - 24);
|
|
513
|
+
if (subscription.lastNotificationAt < twentyFourHoursAgo) {
|
|
514
|
+
this.logger.warn(`[${correlationId}] Subscription ${subscription.subscriptionId} has not received notifications since ${subscription.lastNotificationAt.toISOString()}. Emitting LIFECYCLE_MISSED for delta sync.`);
|
|
515
|
+
staleDetected++;
|
|
516
|
+
this.eventEmitter.emit(event_types_enum_1.OutlookEventTypes.LIFECYCLE_MISSED, {
|
|
517
|
+
subscriptionId: subscription.subscriptionId,
|
|
518
|
+
userId: subscription.userId,
|
|
519
|
+
reason: 'health_check_stale',
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
catch (error) {
|
|
525
|
+
failed++;
|
|
526
|
+
this.logger.error(`[${correlationId}] Health check failed for subscription ${subscription.subscriptionId}:`, error);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
this.logger.log(`[${correlationId}] Health check complete: ${String(verified)} verified, ${String(recreated)} recreated, ${String(staleDetected)} stale-detected, ${String(failed)} failed`);
|
|
530
|
+
}
|
|
531
|
+
catch (error) {
|
|
532
|
+
this.logger.error(`[${correlationId}] Subscription health check job failed:`, error);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
161
535
|
};
|
|
162
536
|
exports.MicrosoftSubscriptionService = MicrosoftSubscriptionService;
|
|
537
|
+
__decorate([
|
|
538
|
+
(0, schedule_1.Cron)(schedule_1.CronExpression.EVERY_HOUR),
|
|
539
|
+
__metadata("design:type", Function),
|
|
540
|
+
__metadata("design:paramtypes", []),
|
|
541
|
+
__metadata("design:returntype", Promise)
|
|
542
|
+
], MicrosoftSubscriptionService.prototype, "renewSubscriptions", null);
|
|
543
|
+
__decorate([
|
|
544
|
+
(0, schedule_1.Cron)('0 */6 * * *'),
|
|
545
|
+
__metadata("design:type", Function),
|
|
546
|
+
__metadata("design:paramtypes", []),
|
|
547
|
+
__metadata("design:returntype", Promise)
|
|
548
|
+
], MicrosoftSubscriptionService.prototype, "verifySubscriptionHealth", null);
|
|
163
549
|
exports.MicrosoftSubscriptionService = MicrosoftSubscriptionService = MicrosoftSubscriptionService_1 = __decorate([
|
|
164
|
-
(0, common_1.Injectable)()
|
|
550
|
+
(0, common_1.Injectable)(),
|
|
551
|
+
__param(0, (0, common_1.Inject)((0, common_1.forwardRef)(() => microsoft_auth_service_1.MicrosoftAuthService))),
|
|
552
|
+
__param(3, (0, common_1.Inject)(constants_1.MICROSOFT_CONFIG)),
|
|
553
|
+
__param(4, (0, typeorm_1.InjectRepository)(microsoft_user_entity_1.MicrosoftUser)),
|
|
554
|
+
__metadata("design:paramtypes", [microsoft_auth_service_1.MicrosoftAuthService,
|
|
555
|
+
outlook_webhook_subscription_repository_1.OutlookWebhookSubscriptionRepository,
|
|
556
|
+
event_emitter_1.EventEmitter2, Object, typeorm_2.Repository,
|
|
557
|
+
user_id_converter_service_1.UserIdConverterService])
|
|
165
558
|
], MicrosoftSubscriptionService);
|
|
166
559
|
//# sourceMappingURL=microsoft-subscription.service.js.map
|