@crowdin/app-project-module 0.39.1 → 0.41.0
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/out/handlers/integration/crowdin-update.js +36 -17
- package/out/handlers/integration/integration-data.js +2 -0
- package/out/handlers/integration/integration-update.js +35 -17
- package/out/handlers/integration/integration-webhook.js +1 -1
- package/out/handlers/integration/job-cancel.d.ts +3 -0
- package/out/handlers/integration/job-cancel.js +28 -0
- package/out/handlers/integration/job-info.d.ts +3 -0
- package/out/handlers/integration/job-info.js +54 -0
- package/out/handlers/integration/main.js +6 -3
- package/out/handlers/integration/settings-save.js +8 -1
- package/out/handlers/integration/user-errors.d.ts +3 -0
- package/out/handlers/{user-errors.js → integration/user-errors.js} +2 -2
- package/out/handlers/uninstall.js +4 -2
- package/out/index.d.ts +2 -1
- package/out/index.js +35 -27
- package/out/middlewares/crowdin-client.js +1 -0
- package/out/middlewares/integration-credentials.js +3 -2
- package/out/middlewares/ui-module.js +1 -0
- package/out/models/index.d.ts +54 -18
- package/out/models/index.js +1 -0
- package/out/models/job.d.ts +44 -0
- package/out/models/job.js +16 -0
- package/out/static/js/form.js +13 -13
- package/out/static/js/main.js +1 -1
- package/out/storage/index.d.ts +11 -2
- package/out/storage/index.js +3 -0
- package/out/storage/mysql.d.ts +11 -2
- package/out/storage/mysql.js +155 -10
- package/out/storage/postgre.d.ts +11 -2
- package/out/storage/postgre.js +153 -9
- package/out/storage/sqlite.d.ts +14 -3
- package/out/storage/sqlite.js +160 -14
- package/out/util/cron.d.ts +1 -0
- package/out/util/cron.js +53 -6
- package/out/util/defaults.js +4 -11
- package/out/util/file-snapshot.js +2 -0
- package/out/util/files.d.ts +2 -1
- package/out/util/files.js +19 -1
- package/out/util/index.js +3 -1
- package/out/util/job.d.ts +12 -0
- package/out/util/job.js +88 -0
- package/out/util/logger.js +4 -0
- package/out/util/webhooks.js +53 -6
- package/out/views/main.handlebars +153 -5
- package/package.json +16 -15
- package/out/handlers/user-errors.d.ts +0 -3
package/out/util/webhooks.js
CHANGED
|
@@ -44,6 +44,8 @@ const connection_1 = require("./connection");
|
|
|
44
44
|
const defaults_1 = require("./defaults");
|
|
45
45
|
const index_1 = require("./index");
|
|
46
46
|
const logger_1 = require("./logger");
|
|
47
|
+
const prefetchCount = 20;
|
|
48
|
+
const forceProcessDelay = 5000;
|
|
47
49
|
exports.HookEvents = {
|
|
48
50
|
fileAdded: 'file.added',
|
|
49
51
|
fileDeleted: 'file.deleted',
|
|
@@ -227,6 +229,7 @@ function prepareWebhookData(config, integration, webhookUrlParam, provider) {
|
|
|
227
229
|
const { projectId, crowdinId, clientId } = decodedUrlParam(config, webhookUrlParam);
|
|
228
230
|
const crowdinCredentials = yield (0, storage_1.getStorage)().getCrowdinCredentials(crowdinId);
|
|
229
231
|
const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(clientId);
|
|
232
|
+
const integrationConfig = yield (0, storage_1.getStorage)().getIntegrationConfig(clientId);
|
|
230
233
|
const context = {
|
|
231
234
|
jwtPayload: {
|
|
232
235
|
context: {
|
|
@@ -245,8 +248,8 @@ function prepareWebhookData(config, integration, webhookUrlParam, provider) {
|
|
|
245
248
|
context,
|
|
246
249
|
});
|
|
247
250
|
const preparedIntegrationCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
248
|
-
if (
|
|
249
|
-
appSettings = JSON.parse(
|
|
251
|
+
if (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) {
|
|
252
|
+
appSettings = JSON.parse(integrationConfig.config);
|
|
250
253
|
const isWebhookSync = Number(appSettings.schedule) !== models_1.SyncSchedule.DISABLED;
|
|
251
254
|
if (isWebhookSync) {
|
|
252
255
|
syncSettings = (yield (0, storage_1.getStorage)().getSyncSettings(clientId, crowdinId, 'schedule', provider));
|
|
@@ -308,7 +311,7 @@ function listenQueueMessage(config, integration, queueUrl, queueName) {
|
|
|
308
311
|
const channel = yield connection.createChannel();
|
|
309
312
|
if (channel) {
|
|
310
313
|
yield channel.assertQueue(queueName, { durable: true });
|
|
311
|
-
yield channel.prefetch(
|
|
314
|
+
yield channel.prefetch(prefetchCount);
|
|
312
315
|
yield channel.consume(queueName, consumer(channel, config, integration), { noAck: false });
|
|
313
316
|
}
|
|
314
317
|
}
|
|
@@ -321,23 +324,67 @@ function listenQueueMessage(config, integration, queueUrl, queueName) {
|
|
|
321
324
|
}
|
|
322
325
|
exports.listenQueueMessage = listenQueueMessage;
|
|
323
326
|
function consumer(channel, config, integration) {
|
|
327
|
+
let messagesCounter = 0;
|
|
328
|
+
let webhooksInfo = {};
|
|
329
|
+
let webhooksData = [];
|
|
330
|
+
let timeoutId;
|
|
331
|
+
const resetStateVariables = () => {
|
|
332
|
+
messagesCounter = 0;
|
|
333
|
+
webhooksInfo = {};
|
|
334
|
+
webhooksData = [];
|
|
335
|
+
};
|
|
324
336
|
return function (msg) {
|
|
325
337
|
var _a;
|
|
326
338
|
return __awaiter(this, void 0, void 0, function* () {
|
|
339
|
+
messagesCounter++;
|
|
327
340
|
if (!msg) {
|
|
328
341
|
return;
|
|
329
342
|
}
|
|
343
|
+
clearTimeout(timeoutId);
|
|
330
344
|
try {
|
|
331
345
|
const data = JSON.parse(msg.content.toString());
|
|
332
346
|
const urlParam = (_a = integration.webhooks) === null || _a === void 0 ? void 0 : _a.urlParam;
|
|
333
347
|
const webhookUrlParam = data.query[urlParam];
|
|
334
|
-
const
|
|
335
|
-
|
|
348
|
+
const { clientId } = decodedUrlParam(config, webhookUrlParam);
|
|
349
|
+
if (!webhooksInfo[clientId]) {
|
|
350
|
+
webhooksInfo[clientId] = {};
|
|
351
|
+
webhooksInfo[clientId].data = [data];
|
|
352
|
+
webhooksInfo[clientId].integration = integration;
|
|
353
|
+
webhooksData.push(prepareWebhookData(config, integration, webhookUrlParam, models_1.Provider.INTEGRATION).then((res) => {
|
|
354
|
+
webhooksInfo[clientId].webhookData = res;
|
|
355
|
+
}));
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
webhooksInfo[clientId].data.push(data);
|
|
359
|
+
}
|
|
360
|
+
if (messagesCounter < prefetchCount) {
|
|
361
|
+
// if all messages are not received, wait 5 seconds to force process messages
|
|
362
|
+
timeoutId = setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
363
|
+
yield processMessages(webhooksData, webhooksInfo, channel, msg);
|
|
364
|
+
resetStateVariables();
|
|
365
|
+
}), forceProcessDelay);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
clearTimeout(timeoutId);
|
|
369
|
+
yield processMessages(webhooksData, webhooksInfo, channel, msg);
|
|
370
|
+
resetStateVariables();
|
|
336
371
|
}
|
|
337
372
|
catch (e) {
|
|
338
373
|
(0, logger_1.logError)(e);
|
|
339
374
|
}
|
|
340
|
-
channel.ack(msg);
|
|
341
375
|
});
|
|
342
376
|
};
|
|
343
377
|
}
|
|
378
|
+
function processMessages(webhooksData, webhooksInfo, channel, msg) {
|
|
379
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
380
|
+
yield Promise.all(webhooksData);
|
|
381
|
+
for (const { data, integration, webhookData } of Object.values(webhooksInfo)) {
|
|
382
|
+
yield updateCrowdinFromWebhookRequest({
|
|
383
|
+
integration: integration,
|
|
384
|
+
webhookData: webhookData,
|
|
385
|
+
req: data,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
channel.ack(msg, true);
|
|
389
|
+
});
|
|
390
|
+
}
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
{{/if}}
|
|
59
59
|
</div>
|
|
60
60
|
<crowdin-simple-integration
|
|
61
|
+
async-progress
|
|
61
62
|
{{#if syncNewElements.crowdin}}
|
|
62
63
|
skip-crowdin-auto-schedule
|
|
63
64
|
{{/if}}
|
|
@@ -101,6 +102,9 @@
|
|
|
101
102
|
</div>
|
|
102
103
|
</div>
|
|
103
104
|
<crowdin-toasts></crowdin-toasts>
|
|
105
|
+
<crowdin-async-progress
|
|
106
|
+
cancelAsyncAction=""
|
|
107
|
+
></crowdin-async-progress>
|
|
104
108
|
<crowdin-modal id="subscription-modal" modal-width="50" close-button="false">
|
|
105
109
|
<div>
|
|
106
110
|
<crowdin-alert type="warning">Subscribe to continue using the {{name}} app.</crowdin-alert>
|
|
@@ -296,6 +300,9 @@
|
|
|
296
300
|
});
|
|
297
301
|
document.body.addEventListener('uploadFilesToCrowdin', uploadFilesToCrowdin);
|
|
298
302
|
document.body.addEventListener('uploadFilesToIntegration', uploadFilesToIntegration);
|
|
303
|
+
document.body.addEventListener('cancelAsyncAction', (e) => {
|
|
304
|
+
cancelJob(e.detail);
|
|
305
|
+
});
|
|
299
306
|
{{#if integrationSearchListener}}
|
|
300
307
|
document.body.addEventListener("integrationFilterChange", (event) => {
|
|
301
308
|
getIntegrationData(false, 0, event.detail);
|
|
@@ -308,11 +315,27 @@
|
|
|
308
315
|
const fileType = '1';
|
|
309
316
|
const branchType = '2';
|
|
310
317
|
|
|
318
|
+
const JOB_TYPE = {
|
|
319
|
+
updateCrowdin: 'updateCrowdin',
|
|
320
|
+
updateIntegration: 'updateIntegration',
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const JOB_STATUS = {
|
|
324
|
+
created: 'created',
|
|
325
|
+
inProgress: 'inProgress',
|
|
326
|
+
failed: 'failed',
|
|
327
|
+
canceled: 'canceled',
|
|
328
|
+
finished: 'finished',
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const asyncJobs = {};
|
|
332
|
+
|
|
311
333
|
let project = {};
|
|
312
334
|
let crowdinData = [];
|
|
313
335
|
|
|
314
336
|
getCrowdinData();
|
|
315
337
|
getIntegrationData();
|
|
338
|
+
getActiveJobs();
|
|
316
339
|
|
|
317
340
|
function integrationLogout() {
|
|
318
341
|
checkOrigin()
|
|
@@ -481,10 +504,131 @@
|
|
|
481
504
|
body: JSON.stringify(req)
|
|
482
505
|
}))
|
|
483
506
|
.then(checkResponse)
|
|
484
|
-
.then(() =>
|
|
485
|
-
|
|
507
|
+
.then((res) => {
|
|
508
|
+
checkJob({
|
|
509
|
+
jobId: res?.jobId,
|
|
510
|
+
jobType: JOB_TYPE.updateCrowdin,
|
|
511
|
+
})
|
|
512
|
+
})
|
|
486
513
|
.catch(e => catchRejection(e, 'Can\'t upload templates to Crowdin'))
|
|
487
|
-
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function getActiveJobs() {
|
|
517
|
+
checkOrigin()
|
|
518
|
+
.then(restParams => fetch(`api/jobs${restParams}`))
|
|
519
|
+
.then(checkResponse)
|
|
520
|
+
.then((jobs) => {
|
|
521
|
+
if (!Array.isArray(jobs)) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
jobs.forEach((job) => {
|
|
526
|
+
switch (job.type) {
|
|
527
|
+
case JOB_TYPE.updateCrowdin:
|
|
528
|
+
appComponent.setAttribute('is-to-crowdin-process', true);
|
|
529
|
+
break;
|
|
530
|
+
case JOB_TYPE.updateIntegration:
|
|
531
|
+
appComponent.setAttribute('is-to-integration-process', true);
|
|
532
|
+
break;
|
|
533
|
+
default:
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
checkJob({
|
|
537
|
+
jobId: job.id,
|
|
538
|
+
jobType: job.type,
|
|
539
|
+
})
|
|
540
|
+
})
|
|
541
|
+
})
|
|
542
|
+
.catch(e => catchRejection(e, 'Sync status check failed'))
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function cancelJob(jobId) {
|
|
546
|
+
if (asyncJobs[jobId]?.isFailed) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
checkOrigin()
|
|
551
|
+
.then(restParams => fetch('api/jobs' + restParams + '&job_id=' + jobId, {
|
|
552
|
+
method: 'DELETE',
|
|
553
|
+
headers: { 'Content-Type': 'application/json' },
|
|
554
|
+
}))
|
|
555
|
+
.then(checkResponse)
|
|
556
|
+
.then(() => showToast('Cancellation…'))
|
|
557
|
+
.catch(e => catchRejection(e, 'Sync cancellation failed'));
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function checkJob({jobId, jobType, onSuccess, onError, onFinally}) {
|
|
561
|
+
switch (jobType) {
|
|
562
|
+
case JOB_TYPE.updateCrowdin:
|
|
563
|
+
if (!onSuccess) {
|
|
564
|
+
onSuccess = ((job) => {
|
|
565
|
+
getCrowdinData();
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (!onFinally) {
|
|
570
|
+
onFinally = (() => appComponent.setAttribute('is-to-crowdin-process', false));
|
|
571
|
+
}
|
|
572
|
+
break;
|
|
573
|
+
case JOB_TYPE.updateIntegration:
|
|
574
|
+
if (!onFinally) {
|
|
575
|
+
onFinally = (() => appComponent.setAttribute('is-to-integration-process', false));
|
|
576
|
+
}
|
|
577
|
+
break;
|
|
578
|
+
default:
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
checkOrigin()
|
|
582
|
+
.then(restParams => fetch('api/jobs' + restParams + '&job_id=' + jobId))
|
|
583
|
+
.then(checkResponse)
|
|
584
|
+
.then((job) => {
|
|
585
|
+
const isFailed = [JOB_STATUS.failed, JOB_STATUS.canceled].includes(job.status);
|
|
586
|
+
const progress = isFailed || JOB_STATUS.finished === job.status ? 100 : job.progress;
|
|
587
|
+
const info = JOB_STATUS.canceled === job.status ? `Cancelled\n${job.info || ''}` : job.info;
|
|
588
|
+
|
|
589
|
+
pushJobs([ {
|
|
590
|
+
id: job.id,
|
|
591
|
+
title: job.title,
|
|
592
|
+
progress,
|
|
593
|
+
info,
|
|
594
|
+
isFailed,
|
|
595
|
+
} ]);
|
|
596
|
+
|
|
597
|
+
if (isFailed) {
|
|
598
|
+
onError && onError();
|
|
599
|
+
} else if ([JOB_STATUS.created, JOB_STATUS.inProgress].includes(job.status) && job.progress < 100) {
|
|
600
|
+
setTimeout(() => {
|
|
601
|
+
checkJob({jobId, jobType, onSuccess, onError, onFinally});
|
|
602
|
+
}, {{asyncProgress.checkInterval}});
|
|
603
|
+
return;
|
|
604
|
+
} else {
|
|
605
|
+
onSuccess && onSuccess(job);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
onFinally && onFinally(job);
|
|
609
|
+
})
|
|
610
|
+
.catch((e) => {
|
|
611
|
+
onFinally && onFinally();
|
|
612
|
+
|
|
613
|
+
pushJobs([ {
|
|
614
|
+
id: jobId,
|
|
615
|
+
isFailed: true,
|
|
616
|
+
} ]);
|
|
617
|
+
|
|
618
|
+
showToast('Sync status check failed');
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function pushJobs(jobs) {
|
|
623
|
+
const el = document.querySelector('crowdin-async-progress');
|
|
624
|
+
if (el && el.pushJobs) {
|
|
625
|
+
el.pushJobs(jobs);
|
|
626
|
+
jobs.forEach((job) => (asyncJobs[job.id] = job));
|
|
627
|
+
} else {
|
|
628
|
+
setTimeout(() => {
|
|
629
|
+
pushJobs(jobs);
|
|
630
|
+
}, 300);
|
|
631
|
+
}
|
|
488
632
|
}
|
|
489
633
|
|
|
490
634
|
function uploadFilesToIntegration(e) {
|
|
@@ -504,9 +648,13 @@
|
|
|
504
648
|
body: JSON.stringify(req)
|
|
505
649
|
}))
|
|
506
650
|
.then(checkResponse)
|
|
507
|
-
.then(() =>
|
|
651
|
+
.then((res) => {
|
|
652
|
+
checkJob({
|
|
653
|
+
jobId: res?.jobId,
|
|
654
|
+
jobType: JOB_TYPE.updateIntegration,
|
|
655
|
+
})
|
|
656
|
+
})
|
|
508
657
|
.catch(e => catchRejection(e, 'Can\'t upload files to {{name}}'))
|
|
509
|
-
.finally(() => (appComponent.setAttribute('is-to-integration-process', false)));
|
|
510
658
|
}
|
|
511
659
|
|
|
512
660
|
{{#if configurationFields}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crowdin/app-project-module",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
|
|
5
5
|
"main": "out/index.js",
|
|
6
6
|
"types": "out/index.d.ts",
|
|
@@ -15,11 +15,10 @@
|
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@aws-sdk/client-s3": "^3.423.0",
|
|
18
|
-
"@aws-sdk/s3-request-presigner": "^3.
|
|
18
|
+
"@aws-sdk/s3-request-presigner": "^3.484.0",
|
|
19
19
|
"@crowdin/crowdin-apps-functions": "0.6.0",
|
|
20
|
-
"@crowdin/logs-formatter": "^2.
|
|
20
|
+
"@crowdin/logs-formatter": "^2.1.2",
|
|
21
21
|
"@godaddy/terminus": "^4.12.1",
|
|
22
|
-
"@types/pg": "^8.10.3",
|
|
23
22
|
"amqplib": "^0.10.3",
|
|
24
23
|
"crypto-js": "^4.2.0",
|
|
25
24
|
"express": "4.18.2",
|
|
@@ -33,29 +32,31 @@
|
|
|
33
32
|
"uuid": "^8.3.2"
|
|
34
33
|
},
|
|
35
34
|
"devDependencies": {
|
|
36
|
-
"@babel/preset-react": "^7.
|
|
35
|
+
"@babel/preset-react": "^7.23.3",
|
|
37
36
|
"@emotion/react": "^11.11.1",
|
|
38
37
|
"@emotion/styled": "^11.11.0",
|
|
39
|
-
"@mui/icons-material": "^5.
|
|
40
|
-
"@mui/material": "^5.
|
|
41
|
-
"@rjsf/core": "^5.
|
|
42
|
-
"@rjsf/mui": "^5.
|
|
43
|
-
"@rjsf/utils": "^5.15.
|
|
44
|
-
"@rjsf/validator-ajv8": "^5.
|
|
38
|
+
"@mui/icons-material": "^5.15.7",
|
|
39
|
+
"@mui/material": "^5.15.7",
|
|
40
|
+
"@rjsf/core": "^5.16.1",
|
|
41
|
+
"@rjsf/mui": "^5.16.1",
|
|
42
|
+
"@rjsf/utils": "^5.15.1",
|
|
43
|
+
"@rjsf/validator-ajv8": "^5.16.1",
|
|
45
44
|
"@rollup/plugin-babel": "^6.0.4",
|
|
46
45
|
"@rollup/plugin-commonjs": "^24.1.0",
|
|
47
46
|
"@rollup/plugin-json": "^6.0.0",
|
|
48
|
-
"@rollup/plugin-node-resolve": "^15.2.
|
|
47
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
49
48
|
"@rollup/plugin-replace": "^5.0.5",
|
|
50
49
|
"@rollup/plugin-terser": "^0.4.3",
|
|
51
50
|
"@types/amqplib": "^0.10.4",
|
|
52
51
|
"@types/crypto-js": "^4.1.3",
|
|
53
|
-
"@types/express": "4.17.
|
|
52
|
+
"@types/express": "4.17.21",
|
|
54
53
|
"@types/express-handlebars": "^5.3.1",
|
|
55
54
|
"@types/jest": "^29.5.5",
|
|
56
|
-
"@types/node": "^16.18.
|
|
55
|
+
"@types/node": "^16.18.69",
|
|
57
56
|
"@types/node-cron": "^3.0.9",
|
|
58
|
-
"@types/
|
|
57
|
+
"@types/pg": "^8.10.3",
|
|
58
|
+
"@types/swagger-jsdoc": "^6.0.4",
|
|
59
|
+
"@types/uuid": "^9.0.7",
|
|
59
60
|
"@typescript-eslint/eslint-plugin": "^2.3.1",
|
|
60
61
|
"@typescript-eslint/parser": "^2.3.1",
|
|
61
62
|
"eslint": "^6.4.0",
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
/// <reference types="qs" />
|
|
2
|
-
import { Response } from 'express';
|
|
3
|
-
export default function handle(): (req: import("../models").CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
|