@crowdin/app-project-module 2.9.0 → 2.9.1
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.
|
@@ -27,45 +27,56 @@ const files_1 = require("./files");
|
|
|
27
27
|
const job_1 = require("./job");
|
|
28
28
|
const snapshot_1 = require("./snapshot");
|
|
29
29
|
const types_2 = require("./types");
|
|
30
|
+
// Maximum number of organizations processed concurrently during a cron run.
|
|
31
|
+
// Keeps DB connection and API usage bounded regardless of how many orgs are registered.
|
|
32
|
+
const CRON_ORG_CONCURRENCY = (0, util_1.getEnvInt)('CRON_ORG_CONCURRENCY', 5);
|
|
30
33
|
function runJob(_a) {
|
|
31
34
|
return __awaiter(this, arguments, void 0, function* ({ config, integration, job, }) {
|
|
32
35
|
(0, logger_1.log)(`Starting cron job with expression [${job.expression}]`);
|
|
33
36
|
const crowdinCredentialsList = yield (0, storage_1.getStorage)().getAllCrowdinCredentials();
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
config,
|
|
42
|
-
token,
|
|
43
|
-
organization: crowdinCredentials.id,
|
|
44
|
-
accountType: crowdinCredentials.type,
|
|
45
|
-
});
|
|
46
|
-
if (expired) {
|
|
47
|
-
(0, logger_1.log)(`Subscription expired. Skipping job [${job.expression}] for organization ${crowdinCredentials.id}`);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const integrationCredentialsList = yield (0, storage_1.getStorage)().getAllIntegrationCredentials(crowdinCredentials.id);
|
|
51
|
-
const allIntegrationConfigs = yield (0, storage_1.getStorage)().getAllIntegrationConfigs(crowdinCredentials.id);
|
|
52
|
-
for (const integrationCredentials of integrationCredentialsList) {
|
|
53
|
-
const integrationConfig = allIntegrationConfigs.find(({ integrationId }) => integrationId === integrationCredentials.id);
|
|
54
|
-
const projectId = (0, token_1.getProjectId)(integrationCredentials.id);
|
|
55
|
-
const credentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
56
|
-
const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
|
|
57
|
-
const intConfig = (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) ? JSON.parse(integrationConfig.config) : undefined;
|
|
58
|
-
(0, logger_1.log)(`Executing task for cron job with expression [${job.expression}] for project ${projectId}`);
|
|
59
|
-
yield job.task({
|
|
60
|
-
projectId,
|
|
61
|
-
client: crowdinClient,
|
|
62
|
-
credentials,
|
|
63
|
-
rootFolder,
|
|
64
|
-
settings: intConfig,
|
|
37
|
+
for (let i = 0; i < crowdinCredentialsList.length; i += CRON_ORG_CONCURRENCY) {
|
|
38
|
+
const batch = crowdinCredentialsList.slice(i, i + CRON_ORG_CONCURRENCY);
|
|
39
|
+
const results = yield Promise.allSettled(batch.map((crowdinCredentials) => __awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
const { token, client: crowdinClient } = yield (0, connection_1.prepareCrowdinClient)({
|
|
41
|
+
config,
|
|
42
|
+
credentials: crowdinCredentials,
|
|
43
|
+
autoRenew: true,
|
|
65
44
|
});
|
|
66
|
-
|
|
45
|
+
const { expired } = yield (0, subscription_1.checkSubscription)({
|
|
46
|
+
config,
|
|
47
|
+
token,
|
|
48
|
+
organization: crowdinCredentials.id,
|
|
49
|
+
accountType: crowdinCredentials.type,
|
|
50
|
+
});
|
|
51
|
+
if (expired) {
|
|
52
|
+
(0, logger_1.log)(`Subscription expired. Skipping job [${job.expression}] for organization ${crowdinCredentials.id}`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const integrationCredentialsList = yield (0, storage_1.getStorage)().getAllIntegrationCredentials(crowdinCredentials.id);
|
|
56
|
+
const allIntegrationConfigs = yield (0, storage_1.getStorage)().getAllIntegrationConfigs(crowdinCredentials.id);
|
|
57
|
+
for (const integrationCredentials of integrationCredentialsList) {
|
|
58
|
+
const integrationConfig = allIntegrationConfigs.find(({ integrationId }) => integrationId === integrationCredentials.id);
|
|
59
|
+
const projectId = (0, token_1.getProjectId)(integrationCredentials.id);
|
|
60
|
+
const credentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
61
|
+
const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
|
|
62
|
+
const intConfig = (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) ? JSON.parse(integrationConfig.config) : undefined;
|
|
63
|
+
(0, logger_1.log)(`Executing task for cron job with expression [${job.expression}] for project ${projectId}`);
|
|
64
|
+
yield job.task({
|
|
65
|
+
projectId,
|
|
66
|
+
client: crowdinClient,
|
|
67
|
+
credentials,
|
|
68
|
+
rootFolder,
|
|
69
|
+
settings: intConfig,
|
|
70
|
+
});
|
|
71
|
+
(0, logger_1.log)(`Task for cron job with expression [${job.expression}] for project ${projectId} completed`);
|
|
72
|
+
}
|
|
73
|
+
})));
|
|
74
|
+
for (const result of results) {
|
|
75
|
+
if (result.status === 'rejected') {
|
|
76
|
+
(0, logger_1.logError)(result.reason);
|
|
77
|
+
}
|
|
67
78
|
}
|
|
68
|
-
}
|
|
79
|
+
}
|
|
69
80
|
(0, logger_1.log)(`Cron job with expression [${job.expression}] completed`);
|
|
70
81
|
});
|
|
71
82
|
}
|
|
@@ -14,6 +14,7 @@ exports.reRunInProgressJobs = reRunInProgressJobs;
|
|
|
14
14
|
const storage_1 = require("../../../storage");
|
|
15
15
|
const token_1 = require("../../../util/app-functions/token");
|
|
16
16
|
const connection_1 = require("../../../util/connection");
|
|
17
|
+
const util_1 = require("../../../util");
|
|
17
18
|
const logger_1 = require("../../../util/logger");
|
|
18
19
|
const cron_1 = require("./cron");
|
|
19
20
|
const defaults_1 = require("./defaults");
|
|
@@ -27,6 +28,8 @@ const blockingJobs = {
|
|
|
27
28
|
[types_1.JobType.INTEGRATION_SETTINGS_SAVE]: [types_1.JobType.INTEGRATION_SETTINGS_SAVE],
|
|
28
29
|
};
|
|
29
30
|
const maxAttempts = 3;
|
|
31
|
+
// Maximum number of in-progress jobs retried concurrently on startup.
|
|
32
|
+
const RERUN_JOB_CONCURRENCY = (0, util_1.getEnvInt)('RERUN_JOB_CONCURRENCY', 5);
|
|
30
33
|
const store = {};
|
|
31
34
|
function runAsJob(_a) {
|
|
32
35
|
return __awaiter(this, arguments, void 0, function* ({ integrationId, crowdinId, type, title, payload, res, projectId, client, jobType, jobStoreType, responseData, jobCallback, onError, reRunJobId, forcePushTranslations, initiatedBy, }) {
|
|
@@ -219,74 +222,82 @@ function reRunInProgressJobs(config) {
|
|
|
219
222
|
if (!(inProgressJobs === null || inProgressJobs === void 0 ? void 0 : inProgressJobs.length)) {
|
|
220
223
|
return;
|
|
221
224
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
225
|
+
for (let i = 0; i < inProgressJobs.length; i += RERUN_JOB_CONCURRENCY) {
|
|
226
|
+
const batch = inProgressJobs.slice(i, i + RERUN_JOB_CONCURRENCY);
|
|
227
|
+
const results = yield Promise.allSettled(batch.map((activeJob) => __awaiter(this, void 0, void 0, function* () {
|
|
228
|
+
if (activeJob && ![types_1.JobStatus.IN_PROGRESS, types_1.JobStatus.CREATED].includes(activeJob.status)) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (activeJob.attempt && activeJob.attempt >= maxAttempts) {
|
|
232
|
+
yield storage.updateJob({ id: activeJob.id, status: types_1.JobStatus.FAILED });
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const { integrationId, crowdinId, type, title, payload } = activeJob;
|
|
236
|
+
const integration = config.projectIntegration;
|
|
237
|
+
let crowdinClient;
|
|
238
|
+
const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(integrationId);
|
|
239
|
+
const crowdinCredentials = yield (0, storage_1.getStorage)().getCrowdinCredentials(crowdinId);
|
|
240
|
+
const integrationConfig = yield (0, storage_1.getStorage)().getIntegrationConfig(integrationId);
|
|
241
|
+
if (!integrationCredentials || !crowdinCredentials) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const projectId = (0, token_1.getProjectId)(integrationCredentials.id);
|
|
245
|
+
const context = {
|
|
246
|
+
jwtPayload: {
|
|
247
|
+
context: {
|
|
248
|
+
project_id: projectId,
|
|
249
|
+
organization_id: crowdinCredentials.organizationId,
|
|
250
|
+
organization_domain: crowdinCredentials.domain,
|
|
251
|
+
user_id: crowdinCredentials.userId,
|
|
252
|
+
},
|
|
247
253
|
},
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
254
|
+
};
|
|
255
|
+
try {
|
|
256
|
+
const preparedCrowdinClient = yield (0, connection_1.prepareCrowdinClient)({
|
|
257
|
+
config,
|
|
258
|
+
credentials: crowdinCredentials,
|
|
259
|
+
autoRenew: true,
|
|
260
|
+
context,
|
|
261
|
+
});
|
|
262
|
+
crowdinClient = preparedCrowdinClient.client;
|
|
263
|
+
}
|
|
264
|
+
catch (e) {
|
|
265
|
+
(0, logger_1.logError)(e);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
|
|
269
|
+
const credentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
270
|
+
const intConfig = (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config)
|
|
271
|
+
? JSON.parse(integrationConfig.config)
|
|
272
|
+
: { schedule: '0', condition: '0' };
|
|
273
|
+
yield storage.updateJob({ attempt: +(activeJob.attempt || 0) + 1, id: activeJob.id });
|
|
274
|
+
if ([types_1.JobType.UPDATE_TO_CROWDIN, types_1.JobType.UPDATE_TO_INTEGRATION].includes(type)) {
|
|
275
|
+
yield (0, cron_1.runUpdateProviderJob)({
|
|
276
|
+
integrationId,
|
|
277
|
+
crowdinId,
|
|
278
|
+
type,
|
|
279
|
+
title,
|
|
280
|
+
payload: JSON.parse(payload),
|
|
281
|
+
jobType: types_1.JobClientType.RERUN,
|
|
282
|
+
projectId,
|
|
283
|
+
client: crowdinClient,
|
|
284
|
+
integration,
|
|
285
|
+
context,
|
|
286
|
+
credentials,
|
|
287
|
+
rootFolder,
|
|
288
|
+
settings: intConfig,
|
|
289
|
+
reRunJobId: activeJob.id,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
else if ([types_1.JobType.CROWDIN_SYNC_SETTINGS_SAVE, types_1.JobType.INTEGRATION_SYNC_SETTINGS_SAVE].includes(type)) {
|
|
293
|
+
yield storage.updateJob({ status: types_1.JobStatus.CANCELED, id: activeJob.id });
|
|
294
|
+
}
|
|
295
|
+
})));
|
|
296
|
+
for (const result of results) {
|
|
297
|
+
if (result.status === 'rejected') {
|
|
298
|
+
(0, logger_1.logError)(result.reason);
|
|
299
|
+
}
|
|
289
300
|
}
|
|
290
|
-
}
|
|
301
|
+
}
|
|
291
302
|
});
|
|
292
303
|
}
|
package/out/util/connection.js
CHANGED
|
@@ -302,13 +302,16 @@ function prepareIntegrationCredentials(config, integration, integrationCredentia
|
|
|
302
302
|
const performRefreshTokenRequest = (oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.performRefreshTokenRequest) || (integrationLogin === null || integrationLogin === void 0 ? void 0 : integrationLogin.performRefreshTokenRequest);
|
|
303
303
|
credentials.tokenProvider = {
|
|
304
304
|
getToken: (...args_1) => __awaiter(this, [...args_1], void 0, function* (force = false) {
|
|
305
|
-
var _a, _b, _c, _d;
|
|
306
305
|
const { expireIn } = credentials;
|
|
307
306
|
//2 min as an extra buffer
|
|
308
307
|
const isExpired = !expireIn || expireIn - 120 < Date.now() / 1000;
|
|
309
308
|
if (!force && !isExpired) {
|
|
310
309
|
return credentials.accessToken;
|
|
311
310
|
}
|
|
311
|
+
const existingRefresh = _integrationRefreshInFlight.get(integrationCredentials.id);
|
|
312
|
+
if (existingRefresh) {
|
|
313
|
+
return existingRefresh;
|
|
314
|
+
}
|
|
312
315
|
(0, logger_1.log)(force
|
|
313
316
|
? 'Force refreshing integration credentials'
|
|
314
317
|
: 'Integration credentials have expired during operation. Requesting a new credentials');
|
|
@@ -334,50 +337,62 @@ function prepareIntegrationCredentials(config, integration, integrationCredentia
|
|
|
334
337
|
}
|
|
335
338
|
return null;
|
|
336
339
|
});
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
newCredentials
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
if (
|
|
349
|
-
freshCredentials.
|
|
350
|
-
(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
340
|
+
const refreshPromise = (() => __awaiter(this, void 0, void 0, function* () {
|
|
341
|
+
var _a, _b, _c, _d;
|
|
342
|
+
let newCredentials;
|
|
343
|
+
try {
|
|
344
|
+
newCredentials = yield doRefresh(credentials);
|
|
345
|
+
}
|
|
346
|
+
catch (e) {
|
|
347
|
+
// In-memory refresh token may be stale (rotated by another process/worker,
|
|
348
|
+
// or the token itself expired). Try fetching fresh credentials from DB.
|
|
349
|
+
(0, logger_1.log)('Token refresh failed. Attempting to fetch fresh credentials from the database');
|
|
350
|
+
const freshStoredCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(integrationCredentials.id);
|
|
351
|
+
if (freshStoredCredentials) {
|
|
352
|
+
const freshCredentials = JSON.parse((0, _1.decryptData)(config, freshStoredCredentials.credentials));
|
|
353
|
+
if (freshCredentials.refreshToken &&
|
|
354
|
+
freshCredentials.refreshToken !== credentials.refreshToken) {
|
|
355
|
+
(0, logger_1.log)('Found a newer refresh token in the database, retrying token refresh');
|
|
356
|
+
try {
|
|
357
|
+
newCredentials = yield doRefresh(freshCredentials);
|
|
358
|
+
credentials.refreshToken = freshCredentials.refreshToken;
|
|
359
|
+
}
|
|
360
|
+
catch (retryError) {
|
|
361
|
+
(0, logger_1.logError)(retryError);
|
|
362
|
+
throw retryError;
|
|
363
|
+
}
|
|
354
364
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
throw retryError;
|
|
365
|
+
else {
|
|
366
|
+
throw e;
|
|
358
367
|
}
|
|
359
368
|
}
|
|
360
369
|
else {
|
|
361
370
|
throw e;
|
|
362
371
|
}
|
|
363
372
|
}
|
|
364
|
-
|
|
365
|
-
|
|
373
|
+
if (!newCredentials) {
|
|
374
|
+
return credentials.accessToken;
|
|
366
375
|
}
|
|
367
|
-
|
|
368
|
-
|
|
376
|
+
credentials.accessToken = newCredentials[((_a = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _a === void 0 ? void 0 : _a.accessToken) || 'access_token'];
|
|
377
|
+
credentials.expireIn =
|
|
378
|
+
Number(newCredentials[((_b = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _b === void 0 ? void 0 : _b.expiresIn) || 'expires_in']) +
|
|
379
|
+
Date.now() / 1000;
|
|
380
|
+
if (newCredentials[((_c = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _c === void 0 ? void 0 : _c.refreshToken) || 'refresh_token']) {
|
|
381
|
+
credentials.refreshToken =
|
|
382
|
+
newCredentials[((_d = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _d === void 0 ? void 0 : _d.refreshToken) || 'refresh_token'];
|
|
383
|
+
}
|
|
384
|
+
(0, logger_1.log)('Saving updated integration credentials in the database');
|
|
385
|
+
yield (0, storage_1.getStorage)().updateIntegrationCredentials(integrationCredentials.id, (0, _1.encryptData)(config, JSON.stringify(credentials)));
|
|
369
386
|
return credentials.accessToken;
|
|
387
|
+
}))();
|
|
388
|
+
if (_integrationRefreshInFlight.size < 1000) {
|
|
389
|
+
_integrationRefreshInFlight.set(integrationCredentials.id, refreshPromise);
|
|
390
|
+
refreshPromise.finally(() => _integrationRefreshInFlight.delete(integrationCredentials.id));
|
|
370
391
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
Number(newCredentials[((_b = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _b === void 0 ? void 0 : _b.expiresIn) || 'expires_in']) + Date.now() / 1000;
|
|
374
|
-
if (newCredentials[((_c = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _c === void 0 ? void 0 : _c.refreshToken) || 'refresh_token']) {
|
|
375
|
-
credentials.refreshToken =
|
|
376
|
-
newCredentials[((_d = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _d === void 0 ? void 0 : _d.refreshToken) || 'refresh_token'];
|
|
392
|
+
else {
|
|
393
|
+
(0, logger_1.log)('Warning: _integrationRefreshInFlight map reached 1000 entries; skipping deduplication for this refresh');
|
|
377
394
|
}
|
|
378
|
-
|
|
379
|
-
yield (0, storage_1.getStorage)().updateIntegrationCredentials(integrationCredentials.id, (0, _1.encryptData)(config, JSON.stringify(credentials)));
|
|
380
|
-
return credentials.accessToken;
|
|
395
|
+
return refreshPromise;
|
|
381
396
|
}),
|
|
382
397
|
refreshToken: () => {
|
|
383
398
|
return credentials.tokenProvider.getToken(true);
|
|
@@ -389,30 +404,48 @@ function prepareIntegrationCredentials(config, integration, integrationCredentia
|
|
|
389
404
|
return credentials;
|
|
390
405
|
});
|
|
391
406
|
}
|
|
407
|
+
// This map is used to deduplicate concurrent refresh requests for the same Crowdin credentials.
|
|
408
|
+
// Keyed by credentials.id. Assumes a given credentials.id is always used with the same config
|
|
409
|
+
// (i.e. clientId/clientSecret are 1:1 with the credential). Concurrent callers with the same id
|
|
410
|
+
// share one in-flight refresh and all receive the same result.
|
|
411
|
+
const _crowdinRefreshInFlight = new Map();
|
|
412
|
+
const _integrationRefreshInFlight = new Map();
|
|
392
413
|
function refreshCrowdinCreds(_a) {
|
|
393
414
|
return __awaiter(this, arguments, void 0, function* ({ config, credentials }) {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
415
|
+
const existing = _crowdinRefreshInFlight.get(credentials.id);
|
|
416
|
+
if (existing) {
|
|
417
|
+
return existing;
|
|
418
|
+
}
|
|
419
|
+
const connectionPromise = (() => __awaiter(this, void 0, void 0, function* () {
|
|
420
|
+
(0, logger_1.log)('Crowdin credentials have expired. Requesting new credentials');
|
|
421
|
+
const newCredentials = yield refreshToken(config, credentials);
|
|
422
|
+
(0, logger_1.log)('Saving updated crowdin credentials in the database');
|
|
423
|
+
const newCrowdinCredentials = {
|
|
424
|
+
id: credentials.id,
|
|
425
|
+
appSecret: credentials.appSecret,
|
|
426
|
+
domain: credentials.domain,
|
|
427
|
+
userId: credentials.userId,
|
|
428
|
+
agentId: credentials.agentId,
|
|
429
|
+
organizationId: credentials.organizationId,
|
|
430
|
+
baseUrl: credentials.baseUrl,
|
|
431
|
+
refreshToken: newCredentials.refreshToken,
|
|
432
|
+
accessToken: newCredentials.accessToken,
|
|
433
|
+
expire: (Date.now() / 1000 + newCredentials.expiresIn).toString(),
|
|
434
|
+
type: credentials.type,
|
|
435
|
+
};
|
|
436
|
+
yield (0, storage_1.getStorage)().updateCrowdinCredentials(newCrowdinCredentials);
|
|
437
|
+
const token = (0, _1.decryptData)(config, newCredentials.accessToken);
|
|
438
|
+
return { credentials: newCrowdinCredentials, token };
|
|
439
|
+
}))();
|
|
440
|
+
if (_crowdinRefreshInFlight.size < 1000) {
|
|
441
|
+
_crowdinRefreshInFlight.set(credentials.id, connectionPromise);
|
|
442
|
+
connectionPromise.finally(() => _crowdinRefreshInFlight.delete(credentials.id));
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
// should not really happen, but just in case, we don't want to block the refresh if the map is too big
|
|
446
|
+
(0, logger_1.log)('Warning: _crowdinRefreshInFlight map reached 1000 entries; skipping deduplication for this refresh');
|
|
447
|
+
}
|
|
448
|
+
return connectionPromise;
|
|
416
449
|
});
|
|
417
450
|
}
|
|
418
451
|
function refreshToken(config, credentials) {
|
package/out/util/index.d.ts
CHANGED
|
@@ -32,4 +32,5 @@ export declare function getFormattedDate({ date, userTimezone }: {
|
|
|
32
32
|
}): string;
|
|
33
33
|
export declare function kebabCase(str?: string): string;
|
|
34
34
|
export declare function snakeCase(str?: string): string;
|
|
35
|
+
export declare function getEnvInt(name: string, defaultValue: number): number;
|
|
35
36
|
export declare function uniqBy<T>(array: any, key: keyof T): T[];
|
package/out/util/index.js
CHANGED
|
@@ -61,6 +61,7 @@ exports.validateEmail = validateEmail;
|
|
|
61
61
|
exports.getFormattedDate = getFormattedDate;
|
|
62
62
|
exports.kebabCase = kebabCase;
|
|
63
63
|
exports.snakeCase = snakeCase;
|
|
64
|
+
exports.getEnvInt = getEnvInt;
|
|
64
65
|
exports.uniqBy = uniqBy;
|
|
65
66
|
const crypto = __importStar(require("crypto"));
|
|
66
67
|
const storage_1 = require("../storage");
|
|
@@ -345,6 +346,11 @@ function kebabCase(str) {
|
|
|
345
346
|
function snakeCase(str) {
|
|
346
347
|
return kebabCase(str).replace(/-/g, '_');
|
|
347
348
|
}
|
|
349
|
+
function getEnvInt(name, defaultValue) {
|
|
350
|
+
var _a;
|
|
351
|
+
const parsed = Number.parseInt((_a = process.env[name]) !== null && _a !== void 0 ? _a : '', 10);
|
|
352
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : defaultValue;
|
|
353
|
+
}
|
|
348
354
|
function uniqBy(array, key) {
|
|
349
355
|
if (!Array.isArray(array)) {
|
|
350
356
|
return [];
|
package/package.json
CHANGED