@crowdin/app-project-module 0.68.0 → 0.69.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/index.js +5 -0
- package/out/modules/api/api.js +4 -4
- package/out/modules/integration/handlers/crowdin-update.js +1 -0
- package/out/modules/integration/handlers/integration-update.js +1 -0
- package/out/modules/integration/handlers/sync-settings-save.js +1 -0
- package/out/modules/integration/types.d.ts +5 -1
- package/out/modules/integration/util/cron.js +9 -5
- package/out/modules/integration/util/defaults.js +1 -0
- package/out/modules/integration/util/job.d.ts +3 -2
- package/out/modules/integration/util/job.js +37 -8
- package/out/modules/integration/util/types.d.ts +1 -0
- package/out/modules/integration/util/webhooks.js +5 -4
- package/out/modules/manifest.js +12 -0
- package/out/modules/webhooks/handlers/webhook-handler.d.ts +5 -0
- package/out/modules/webhooks/handlers/webhook-handler.js +75 -0
- package/out/modules/webhooks/index.d.ts +6 -0
- package/out/modules/webhooks/index.js +18 -0
- package/out/modules/webhooks/types.d.ts +292 -0
- package/out/modules/webhooks/types.js +2 -0
- package/out/static/js/form.js +1 -1
- package/out/static/js/main.js +2 -2
- package/out/types.d.ts +5 -0
- package/out/views/main.handlebars +48 -24
- package/package.json +3 -1
package/out/index.js
CHANGED
|
@@ -79,6 +79,7 @@ const projectMenuApp = __importStar(require("./modules/project-menu"));
|
|
|
79
79
|
const projectMenuCrowdsourceApp = __importStar(require("./modules/project-menu-crowdsource"));
|
|
80
80
|
const projectReportsApp = __importStar(require("./modules/project-reports"));
|
|
81
81
|
const projectToolsApp = __importStar(require("./modules/project-tools"));
|
|
82
|
+
const webhooks = __importStar(require("./modules/webhooks"));
|
|
82
83
|
const subscription_1 = require("./util/subscription");
|
|
83
84
|
var types_2 = require("./types");
|
|
84
85
|
Object.defineProperty(exports, "ProjectPermissions", { enumerable: true, get: function () { return types_2.ProjectPermissions; } });
|
|
@@ -175,6 +176,7 @@ function addCrowdinEndpoints(app, clientConfig) {
|
|
|
175
176
|
aiTools.registerAiTools({ config, app });
|
|
176
177
|
aiTools.registerAiToolWidgets({ config, app });
|
|
177
178
|
externalQaCheck.register({ config, app });
|
|
179
|
+
webhooks.register({ config, app });
|
|
178
180
|
addFormSchema({ config, app });
|
|
179
181
|
return Object.assign(Object.assign({}, exports.metadataStore), { establishCrowdinConnection: (authRequest, moduleKey) => {
|
|
180
182
|
let jwtToken = '';
|
|
@@ -240,6 +242,9 @@ function convertClientConfig(clientConfig) {
|
|
|
240
242
|
throw new Error('Missing [clientId, clientSecret] parameters');
|
|
241
243
|
}
|
|
242
244
|
}
|
|
245
|
+
if (clientConfig.projectIntegration) {
|
|
246
|
+
clientConfig.api = Object.assign({ default: true }, clientConfig.api);
|
|
247
|
+
}
|
|
243
248
|
return Object.assign(Object.assign({}, clientConfig), { baseUrl: baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl, clientId,
|
|
244
249
|
clientSecret, awsConfig: {
|
|
245
250
|
tmpBucketName,
|
package/out/modules/api/api.js
CHANGED
|
@@ -462,7 +462,7 @@ function addDefaultApiEndpoints(app, config) {
|
|
|
462
462
|
}
|
|
463
463
|
exports.addDefaultApiEndpoints = addDefaultApiEndpoints;
|
|
464
464
|
function addSwagerApiDocumentation(app, config) {
|
|
465
|
-
var _a, _b;
|
|
465
|
+
var _a, _b, _c;
|
|
466
466
|
const options = {
|
|
467
467
|
swaggerDefinition: {
|
|
468
468
|
openapi: '3.0.0',
|
|
@@ -492,16 +492,16 @@ function addSwagerApiDocumentation(app, config) {
|
|
|
492
492
|
},
|
|
493
493
|
],
|
|
494
494
|
},
|
|
495
|
-
apis: config.projectIntegration
|
|
495
|
+
apis: config.projectIntegration && ((_a = config.api) === null || _a === void 0 ? void 0 : _a.default)
|
|
496
496
|
? [path_1.default.resolve(__dirname, './base.js'), path_1.default.resolve(__dirname, './components.js'), __filename]
|
|
497
497
|
: [],
|
|
498
498
|
};
|
|
499
|
-
if ((
|
|
499
|
+
if ((_b = config.api) === null || _b === void 0 ? void 0 : _b.docFile) {
|
|
500
500
|
options.apis.push(config.api.docFile);
|
|
501
501
|
}
|
|
502
502
|
const swaggerSpec = (0, swagger_jsdoc_1.default)(options);
|
|
503
503
|
// remove Login info from doc
|
|
504
|
-
if (config.projectIntegration && !((
|
|
504
|
+
if (config.projectIntegration && !((_c = config.projectIntegration) === null || _c === void 0 ? void 0 : _c.loginForm)) {
|
|
505
505
|
delete swaggerSpec.paths['/login'];
|
|
506
506
|
delete swaggerSpec.paths['/login-fields'];
|
|
507
507
|
delete swaggerSpec.components.schemas['Login'];
|
|
@@ -43,6 +43,7 @@ function handle(config, integration) {
|
|
|
43
43
|
projectId: projectId,
|
|
44
44
|
client: req.crowdinApiClient,
|
|
45
45
|
jobType: types_1.JobClientType.MANUAL,
|
|
46
|
+
jobStoreType: integration.jobStoreType,
|
|
46
47
|
jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
|
|
47
48
|
var _c;
|
|
48
49
|
if (req.body && ((_c = req.body) === null || _c === void 0 ? void 0 : _c.length)) {
|
|
@@ -37,6 +37,7 @@ function handle(config, integration) {
|
|
|
37
37
|
projectId: req.crowdinContext.jwtPayload.context.project_id,
|
|
38
38
|
client: req.crowdinApiClient,
|
|
39
39
|
jobType: types_1.JobClientType.MANUAL,
|
|
40
|
+
jobStoreType: integration.jobStoreType,
|
|
40
41
|
jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
|
|
41
42
|
const result = yield integration.updateIntegration({
|
|
42
43
|
projectId: req.crowdinContext.jwtPayload.context.project_id,
|
|
@@ -36,6 +36,7 @@ function handle(config, integration) {
|
|
|
36
36
|
projectId: req.crowdinContext.jwtPayload.context.project_id,
|
|
37
37
|
client: req.crowdinApiClient,
|
|
38
38
|
jobType: types_1.JobClientType.MANUAL,
|
|
39
|
+
jobStoreType: integration.jobStoreType,
|
|
39
40
|
jobCallback: () => __awaiter(this, void 0, void 0, function* () {
|
|
40
41
|
if (Array.isArray(expandIntegrationFolders) && expandIntegrationFolders.length) {
|
|
41
42
|
const allFiles = (yield (0, files_1.expandFilesTree)(expandIntegrationFolders, req, integration)).map((node) => ({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Crowdin, { SourceFilesModel, TranslationStatusModel } from '@crowdin/crowdin-api-client';
|
|
2
2
|
import { Request } from 'express';
|
|
3
3
|
import { CrowdinClientRequest, ModuleKey } from '../../types';
|
|
4
|
-
import { JobClient } from './util/types';
|
|
4
|
+
import { JobClient, JobStoreType } from './util/types';
|
|
5
5
|
export interface IntegrationLogic extends ModuleKey {
|
|
6
6
|
/**
|
|
7
7
|
* Customize your app login form
|
|
@@ -56,6 +56,10 @@ export interface IntegrationLogic extends ModuleKey {
|
|
|
56
56
|
appSettings?: any;
|
|
57
57
|
job: JobClient;
|
|
58
58
|
}) => Promise<void | ExtendedResult<void>>;
|
|
59
|
+
/**
|
|
60
|
+
* Store to use for memorizing job data
|
|
61
|
+
*/
|
|
62
|
+
jobStoreType?: JobStoreType;
|
|
59
63
|
/**
|
|
60
64
|
* function to define configuration(settings) modal for you app (by default app will not have any custom settings)
|
|
61
65
|
*/
|
|
@@ -93,6 +93,7 @@ function runUpdateProviderJob({ integrationId, crowdinId, type, title, payload,
|
|
|
93
93
|
projectId,
|
|
94
94
|
client,
|
|
95
95
|
reRunJobId,
|
|
96
|
+
jobStoreType: integration.jobStoreType,
|
|
96
97
|
jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
|
|
97
98
|
const updateParams = {
|
|
98
99
|
projectId,
|
|
@@ -174,6 +175,8 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
|
|
|
174
175
|
user_id: crowdinCredentials.userId,
|
|
175
176
|
},
|
|
176
177
|
},
|
|
178
|
+
crowdinId: crowdinCredentials.id,
|
|
179
|
+
clientId: integrationCredentials.id,
|
|
177
180
|
};
|
|
178
181
|
try {
|
|
179
182
|
const preparedCrowdinClient = yield (0, connection_1.prepareCrowdinClient)({
|
|
@@ -186,7 +189,7 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
|
|
|
186
189
|
crowdinClient = preparedCrowdinClient.client;
|
|
187
190
|
}
|
|
188
191
|
catch (e) {
|
|
189
|
-
(0, logger_1.logError)(e);
|
|
192
|
+
(0, logger_1.logError)(e, context);
|
|
190
193
|
return;
|
|
191
194
|
}
|
|
192
195
|
const { expired } = yield (0, subscription_1.checkSubscription)({
|
|
@@ -204,7 +207,7 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
|
|
|
204
207
|
.data;
|
|
205
208
|
}
|
|
206
209
|
catch (e) {
|
|
207
|
-
(0, logger_1.logError)(e);
|
|
210
|
+
(0, logger_1.logError)(e, context);
|
|
208
211
|
return;
|
|
209
212
|
}
|
|
210
213
|
// eslint-disable-next-line @typescript-eslint/camelcase
|
|
@@ -228,7 +231,7 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
|
|
|
228
231
|
});
|
|
229
232
|
}
|
|
230
233
|
catch (e) {
|
|
231
|
-
(0, logger_1.logError)(e);
|
|
234
|
+
(0, logger_1.logError)(e, context);
|
|
232
235
|
return;
|
|
233
236
|
}
|
|
234
237
|
}
|
|
@@ -267,6 +270,7 @@ function processSyncSettings({ config, integration, period, syncSettings, }) {
|
|
|
267
270
|
crowdinClient,
|
|
268
271
|
onlyApproved,
|
|
269
272
|
onlyTranslated,
|
|
273
|
+
context,
|
|
270
274
|
});
|
|
271
275
|
if (Object.keys(filesToProcess).length <= 0) {
|
|
272
276
|
return;
|
|
@@ -409,7 +413,7 @@ function getNewFoldersFile(folders, snapshotFiles) {
|
|
|
409
413
|
files = files.filter((file) => 'type' in file);
|
|
410
414
|
return files;
|
|
411
415
|
}
|
|
412
|
-
function getOnlyTranslatedOrApprovedFiles({ projectId, crowdinFiles, crowdinClient, onlyApproved, onlyTranslated, }) {
|
|
416
|
+
function getOnlyTranslatedOrApprovedFiles({ projectId, crowdinFiles, crowdinClient, onlyApproved, onlyTranslated, context, }) {
|
|
413
417
|
return __awaiter(this, void 0, void 0, function* () {
|
|
414
418
|
(0, logger_1.log)(`Filtering files to process only ${onlyApproved ? 'approved' : 'translated'} files`);
|
|
415
419
|
const filesInfo = yield Promise.all(Object.keys(crowdinFiles).map((fileId) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -424,7 +428,7 @@ function getOnlyTranslatedOrApprovedFiles({ projectId, crowdinFiles, crowdinClie
|
|
|
424
428
|
}
|
|
425
429
|
catch (e) {
|
|
426
430
|
delete crowdinFiles[fileId];
|
|
427
|
-
(0, logger_1.logError)(e);
|
|
431
|
+
(0, logger_1.logError)(e, context);
|
|
428
432
|
}
|
|
429
433
|
})));
|
|
430
434
|
const filteredFiles = {};
|
|
@@ -258,6 +258,7 @@ function applyIntegrationModuleDefaults(config, integration) {
|
|
|
258
258
|
if (!integration.userErrorLifetimeDays) {
|
|
259
259
|
integration.userErrorLifetimeDays = 30;
|
|
260
260
|
}
|
|
261
|
+
integration.jobStoreType = integration.jobStoreType || 'db';
|
|
261
262
|
}
|
|
262
263
|
exports.applyIntegrationModuleDefaults = applyIntegrationModuleDefaults;
|
|
263
264
|
function constructOauthUrl({ config, integration, clientId, loginForm, }) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { JobClient, JobClientType, JobType } from './types';
|
|
1
|
+
import { JobClient, JobClientType, JobStoreType, JobType } from './types';
|
|
2
2
|
import { Response } from 'express';
|
|
3
3
|
import Crowdin from '@crowdin/crowdin-api-client';
|
|
4
4
|
import { Config } from '../../../types';
|
|
5
|
-
export declare function runAsJob({ integrationId, crowdinId, type, title, payload, res, projectId, client, jobType, jobCallback, onError, reRunJobId, }: {
|
|
5
|
+
export declare function runAsJob({ integrationId, crowdinId, type, title, payload, res, projectId, client, jobType, jobStoreType, jobCallback, onError, reRunJobId, }: {
|
|
6
6
|
integrationId: string;
|
|
7
7
|
crowdinId: string;
|
|
8
8
|
type: JobType;
|
|
@@ -12,6 +12,7 @@ export declare function runAsJob({ integrationId, crowdinId, type, title, payloa
|
|
|
12
12
|
projectId: number;
|
|
13
13
|
client: Crowdin;
|
|
14
14
|
jobType: JobClientType;
|
|
15
|
+
jobStoreType: JobStoreType;
|
|
15
16
|
jobCallback: (arg1: JobClient) => Promise<any>;
|
|
16
17
|
onError?: (e: any) => Promise<void>;
|
|
17
18
|
reRunJobId?: string;
|
|
@@ -47,7 +47,8 @@ const blockingJobs = {
|
|
|
47
47
|
[types_1.JobType.INTEGRATION_SYNC_SETTINGS_SAVE]: [types_1.JobType.INTEGRATION_SYNC_SETTINGS_SAVE],
|
|
48
48
|
};
|
|
49
49
|
const maxAttempts = 3;
|
|
50
|
-
|
|
50
|
+
const store = {};
|
|
51
|
+
function runAsJob({ integrationId, crowdinId, type, title, payload, res, projectId, client, jobType, jobStoreType, jobCallback, onError, reRunJobId, }) {
|
|
51
52
|
return __awaiter(this, void 0, void 0, function* () {
|
|
52
53
|
let jobId;
|
|
53
54
|
const storage = (0, storage_1.getStorage)();
|
|
@@ -77,6 +78,7 @@ function runAsJob({ integrationId, crowdinId, type, title, payload, res, project
|
|
|
77
78
|
if (res) {
|
|
78
79
|
res.status(202).send({ jobId });
|
|
79
80
|
}
|
|
81
|
+
const isDbStore = jobStoreType === 'db';
|
|
80
82
|
const job = {
|
|
81
83
|
get: function getJob() {
|
|
82
84
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -102,14 +104,37 @@ function runAsJob({ integrationId, crowdinId, type, title, payload, res, project
|
|
|
102
104
|
},
|
|
103
105
|
type: jobType,
|
|
104
106
|
fetchTranslation: ({ fileId, languageId }) => __awaiter(this, void 0, void 0, function* () {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
var _a, _b, _c;
|
|
108
|
+
const translationCache = isDbStore
|
|
109
|
+
? yield storage.getFileTranslationCacheByLanguage({
|
|
110
|
+
integrationId,
|
|
111
|
+
crowdinId,
|
|
112
|
+
fileId,
|
|
113
|
+
languageId,
|
|
114
|
+
})
|
|
115
|
+
: (_c = (_b = (_a = store[integrationId]) === null || _a === void 0 ? void 0 : _a[crowdinId]) === null || _b === void 0 ? void 0 : _b[fileId]) === null || _c === void 0 ? void 0 : _c[languageId];
|
|
111
116
|
if (!translationCache) {
|
|
112
|
-
|
|
117
|
+
if (isDbStore) {
|
|
118
|
+
yield storage.saveTranslationCache({ integrationId, crowdinId, fileId, languageId });
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
if (!store[integrationId]) {
|
|
122
|
+
store[integrationId] = {};
|
|
123
|
+
}
|
|
124
|
+
if (!store[integrationId][crowdinId]) {
|
|
125
|
+
store[integrationId] = {
|
|
126
|
+
[crowdinId]: {},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
if (!store[integrationId][crowdinId][fileId]) {
|
|
130
|
+
store[integrationId] = {
|
|
131
|
+
[crowdinId]: {
|
|
132
|
+
[fileId]: {},
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
store[integrationId][crowdinId][fileId][languageId] = {};
|
|
137
|
+
}
|
|
113
138
|
}
|
|
114
139
|
let translation = null;
|
|
115
140
|
try {
|
|
@@ -126,6 +151,10 @@ function runAsJob({ integrationId, crowdinId, type, title, payload, res, project
|
|
|
126
151
|
}),
|
|
127
152
|
// translationUploaded: async ({ fileId, languageId, etag }) => {
|
|
128
153
|
translationUploaded: ({ fileId, translationParams }) => __awaiter(this, void 0, void 0, function* () {
|
|
154
|
+
if (!isDbStore) {
|
|
155
|
+
translationParams.forEach(({ languageId, etag }) => (store[integrationId][crowdinId][fileId][languageId] = { etag }));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
129
158
|
const translationCache = (yield storage.getFileTranslationCache({
|
|
130
159
|
integrationId,
|
|
131
160
|
crowdinId,
|
|
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
};
|
|
37
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
38
|
exports.listenQueueMessage = exports.updateCrowdinFromWebhookRequest = exports.prepareWebhookData = exports.unregisterAllCrowdinWebhooks = exports.unregisterWebhooks = exports.registerWebhooks = exports.HookEvents = void 0;
|
|
39
|
+
const logsFormatter = __importStar(require("@crowdin/logs-formatter"));
|
|
39
40
|
const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
|
|
40
41
|
const amqplib_1 = __importDefault(require("amqplib"));
|
|
41
42
|
const types_1 = require("../types");
|
|
@@ -265,8 +266,7 @@ function prepareWebhookData({ config, integration, provider, webhookUrlParam, })
|
|
|
265
266
|
if (!crowdinCredentials) {
|
|
266
267
|
return { projectId, crowdinClient, rootFolder, appSettings, syncSettings, preparedIntegrationCredentials };
|
|
267
268
|
}
|
|
268
|
-
const context = {
|
|
269
|
-
jwtPayload: {
|
|
269
|
+
const context = Object.assign({ jwtPayload: {
|
|
270
270
|
context: {
|
|
271
271
|
// eslint-disable-next-line @typescript-eslint/camelcase
|
|
272
272
|
project_id: projectId,
|
|
@@ -275,8 +275,9 @@ function prepareWebhookData({ config, integration, provider, webhookUrlParam, })
|
|
|
275
275
|
// eslint-disable-next-line @typescript-eslint/camelcase
|
|
276
276
|
user_id: crowdinCredentials === null || crowdinCredentials === void 0 ? void 0 : crowdinCredentials.userId,
|
|
277
277
|
},
|
|
278
|
-
},
|
|
279
|
-
|
|
278
|
+
}, crowdinId: crowdinCredentials.id }, ((integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.id) && { clientId: integrationCredentials.id }));
|
|
279
|
+
logsFormatter.resetContext();
|
|
280
|
+
logsFormatter.setContext(context);
|
|
280
281
|
crowdinClient = yield (0, connection_1.prepareCrowdinClient)({
|
|
281
282
|
config,
|
|
282
283
|
credentials: crowdinCredentials,
|
package/out/modules/manifest.js
CHANGED
|
@@ -225,6 +225,18 @@ function handle(config) {
|
|
|
225
225
|
})));
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
|
+
if (config.webhooks) {
|
|
229
|
+
const webhooks = Array.isArray(config.webhooks) ? config.webhooks : [config.webhooks];
|
|
230
|
+
modules['webhook'] = [];
|
|
231
|
+
for (let i = 0; i < webhooks.length; i++) {
|
|
232
|
+
webhooks[i].key = config.identifier + '-' + 'webhook-' + i;
|
|
233
|
+
modules['webhook'].push({
|
|
234
|
+
key: webhooks[i].key,
|
|
235
|
+
url: '/webhooks',
|
|
236
|
+
events: webhooks[i].events,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
228
240
|
if (config.externalQaCheck) {
|
|
229
241
|
config.externalQaCheck.key = config.identifier + '-qa-check';
|
|
230
242
|
const uiModule = config.externalQaCheck.settingsUiModule;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/// <reference types="qs" />
|
|
2
|
+
import { CrowdinClientRequest } from '../../../types';
|
|
3
|
+
import { Response } from 'express';
|
|
4
|
+
import { Webhook } from '../types';
|
|
5
|
+
export declare function webhookHandler(webhooks: Webhook[]): (req: 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;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.webhookHandler = void 0;
|
|
39
|
+
const util_1 = require("../../../util");
|
|
40
|
+
const lodash_isstring_1 = __importDefault(require("lodash.isstring"));
|
|
41
|
+
const crypto = __importStar(require("node:crypto"));
|
|
42
|
+
const storage = __importStar(require("../../../storage"));
|
|
43
|
+
function webhookHandler(webhooks) {
|
|
44
|
+
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
const domain = req.headers['x-crowdin-domain'];
|
|
46
|
+
const organizationId = req.headers['x-crowdin-organization-id'];
|
|
47
|
+
const signature = req.headers['x-crowdin-signature'];
|
|
48
|
+
const moduleKey = req.headers['x-application-webhook-key'];
|
|
49
|
+
if (!(0, lodash_isstring_1.default)(domain) || !(0, lodash_isstring_1.default)(organizationId) || !(0, lodash_isstring_1.default)(signature) || !(0, lodash_isstring_1.default)(moduleKey)) {
|
|
50
|
+
res.status(400).send({ error: 'Invalid request' });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const crowdinId = domain || organizationId;
|
|
54
|
+
const credentials = yield storage.getStorage().getCrowdinCredentials(crowdinId);
|
|
55
|
+
if (!credentials) {
|
|
56
|
+
throw new Error('Failed to find Crowdin credentials');
|
|
57
|
+
}
|
|
58
|
+
const hmac = crypto.createHmac('sha256', credentials.appSecret);
|
|
59
|
+
hmac.update(JSON.stringify(req.body).replace(/\//g, '\\/'));
|
|
60
|
+
const generatedSignature = hmac.digest('hex');
|
|
61
|
+
if (generatedSignature !== signature.replace('sha256=', '')) {
|
|
62
|
+
res.status(403).send({ error: 'Invalid signature' });
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
res.status(200).send();
|
|
66
|
+
for (const webhook of webhooks) {
|
|
67
|
+
if (webhook.key === moduleKey) {
|
|
68
|
+
for (const event of req.body.payload.events) {
|
|
69
|
+
yield webhook.callback({ credentials, event });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
exports.webhookHandler = webhookHandler;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.register = void 0;
|
|
7
|
+
const json_response_1 = __importDefault(require("../../middlewares/json-response"));
|
|
8
|
+
const webhook_handler_1 = require("./handlers/webhook-handler");
|
|
9
|
+
function register({ config, app }) {
|
|
10
|
+
if (!config.webhooks) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const webhooks = Array.isArray(config.webhooks) ? config.webhooks : [config.webhooks];
|
|
14
|
+
if (webhooks.length) {
|
|
15
|
+
app.post('/webhooks', json_response_1.default, (0, webhook_handler_1.webhookHandler)(webhooks));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.register = register;
|