@crowdin/app-project-module 0.62.0 → 0.63.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 +10 -3
- package/out/modules/integration/util/cron.js +224 -216
- package/out/types.d.ts +4 -0
- package/out/util/credentials-masker.d.ts +6 -0
- package/out/util/credentials-masker.js +102 -0
- package/out/util/form-schema.d.ts +3 -0
- package/out/util/form-schema.js +21 -0
- package/out/util/index.d.ts +0 -1
- package/out/util/index.js +1 -9
- package/package.json +1 -1
package/out/index.js
CHANGED
|
@@ -49,12 +49,14 @@ const uninstall_1 = __importDefault(require("./modules/uninstall"));
|
|
|
49
49
|
const storage = __importStar(require("./storage"));
|
|
50
50
|
const types_1 = require("./types");
|
|
51
51
|
const util_1 = require("./util");
|
|
52
|
+
const form_schema_1 = require("./util/form-schema");
|
|
52
53
|
const connection_1 = require("./util/connection");
|
|
53
54
|
const handlebars_1 = require("./util/handlebars");
|
|
54
55
|
const logger = __importStar(require("./util/logger"));
|
|
55
56
|
const logger_1 = require("./util/logger");
|
|
56
57
|
const terminus_express_1 = __importDefault(require("./util/terminus-express"));
|
|
57
58
|
exports.express = terminus_express_1.default;
|
|
59
|
+
const credentials_masker_1 = require("./util/credentials-masker");
|
|
58
60
|
//apps
|
|
59
61
|
const apiApp = __importStar(require("./modules/api"));
|
|
60
62
|
const contextMenuApp = __importStar(require("./modules/context-menu"));
|
|
@@ -192,13 +194,18 @@ function addCrowdinEndpoints(app, clientConfig) {
|
|
|
192
194
|
}
|
|
193
195
|
exports.addCrowdinEndpoints = addCrowdinEndpoints;
|
|
194
196
|
function addFormSchema({ app, config }) {
|
|
197
|
+
let moduleConfigWithForm;
|
|
195
198
|
const shouldAddRoutes = Object.keys(config).some((moduleKey) => {
|
|
196
199
|
const moduleConfig = config[moduleKey];
|
|
197
|
-
|
|
200
|
+
if ((0, form_schema_1.hasFormSchema)(moduleConfig)) {
|
|
201
|
+
moduleConfigWithForm = (0, form_schema_1.getLowCodeUiConfigFromModuleConfig)(moduleConfig);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
198
205
|
});
|
|
199
206
|
if (shouldAddRoutes) {
|
|
200
|
-
app.get('/api/form-data', json_response_1.default, (0, crowdin_client_1.default)(config), (0, form_data_display_1.default)());
|
|
201
|
-
app.post('/api/form-data', (0, crowdin_client_1.default)(config), (0, form_data_save_1.default)());
|
|
207
|
+
app.get('/api/form-data', json_response_1.default, (0, crowdin_client_1.default)(config), (0, credentials_masker_1.getRequestCredentialsMasker)(moduleConfigWithForm), (0, form_data_display_1.default)());
|
|
208
|
+
app.post('/api/form-data', (0, crowdin_client_1.default)(config), (0, credentials_masker_1.postRequestCredentialsMasker)(moduleConfigWithForm), (0, form_data_save_1.default)());
|
|
202
209
|
}
|
|
203
210
|
}
|
|
204
211
|
function convertClientConfig(clientConfig) {
|
|
@@ -81,248 +81,256 @@ function runJob({ config, integration, job, }) {
|
|
|
81
81
|
}
|
|
82
82
|
exports.runJob = runJob;
|
|
83
83
|
function filesCron({ config, integration, period, }) {
|
|
84
|
-
var _a;
|
|
85
84
|
return __awaiter(this, void 0, void 0, function* () {
|
|
86
85
|
(0, logger_1.log)(`Starting files cron job with period [${period}]`);
|
|
87
86
|
const syncSettingsList = yield (0, storage_1.getStorage)().getAllSyncSettingsByType('schedule');
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
87
|
+
const crowdinSyncSettings = syncSettingsList.filter((syncSettings) => syncSettings.provider === types_1.Provider.CROWDIN);
|
|
88
|
+
const integrationSyncSettings = syncSettingsList.filter((syncSettings) => syncSettings.provider === types_1.Provider.INTEGRATION);
|
|
89
|
+
yield Promise.all(crowdinSyncSettings.map((syncSettings) => processSyncSettings({ config, integration, period, syncSettings })));
|
|
90
|
+
yield Promise.all([
|
|
91
|
+
integrationSyncSettings.map((syncSettings) => processSyncSettings({ config, integration, period, syncSettings })),
|
|
92
|
+
]);
|
|
93
|
+
(0, logger_1.log)(`Files cron job with period [${period}] completed`);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
exports.filesCron = filesCron;
|
|
97
|
+
function processSyncSettings({ config, integration, period, syncSettings, }) {
|
|
98
|
+
var _a;
|
|
99
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
100
|
+
let projectData;
|
|
101
|
+
let crowdinClient;
|
|
102
|
+
let token;
|
|
103
|
+
let files = JSON.parse(syncSettings.files);
|
|
104
|
+
let newFiles = [];
|
|
105
|
+
const crowdinCredentials = yield (0, storage_1.getStorage)().getCrowdinCredentials(syncSettings.crowdinId);
|
|
106
|
+
const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(syncSettings.integrationId);
|
|
107
|
+
const integrationConfig = yield (0, storage_1.getStorage)().getIntegrationConfig(syncSettings.integrationId);
|
|
108
|
+
if (!crowdinCredentials || !integrationCredentials) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const intConfig = (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config)
|
|
112
|
+
? JSON.parse(integrationConfig.config)
|
|
113
|
+
: { schedule: '0', condition: '0' };
|
|
114
|
+
if (period !== intConfig.schedule) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const projectId = crowdinAppFunctions.getProjectId(integrationCredentials.id);
|
|
118
|
+
const context = {
|
|
119
|
+
jwtPayload: {
|
|
120
|
+
context: {
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/camelcase
|
|
122
|
+
project_id: projectId,
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/camelcase
|
|
124
|
+
organization_id: crowdinCredentials.organizationId,
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/camelcase
|
|
126
|
+
organization_domain: crowdinCredentials.domain,
|
|
127
|
+
// eslint-disable-next-line @typescript-eslint/camelcase
|
|
128
|
+
user_id: crowdinCredentials.userId,
|
|
119
129
|
},
|
|
120
|
-
}
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
try {
|
|
133
|
+
const preparedCrowdinClient = yield (0, connection_1.prepareCrowdinClient)({
|
|
134
|
+
config,
|
|
135
|
+
credentials: crowdinCredentials,
|
|
136
|
+
autoRenew: true,
|
|
137
|
+
context,
|
|
138
|
+
});
|
|
139
|
+
token = preparedCrowdinClient.token;
|
|
140
|
+
crowdinClient = preparedCrowdinClient.client;
|
|
141
|
+
}
|
|
142
|
+
catch (e) {
|
|
143
|
+
(0, logger_1.logError)(e);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const { expired } = yield (0, subscription_1.checkSubscription)({
|
|
147
|
+
config,
|
|
148
|
+
token,
|
|
149
|
+
organization: crowdinCredentials.id,
|
|
150
|
+
accountType: crowdinCredentials.type,
|
|
151
|
+
});
|
|
152
|
+
if (expired) {
|
|
153
|
+
(0, logger_1.log)(`Subscription expired. Skipping job [${period}] for organization ${crowdinCredentials.id}`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
projectData = (yield crowdinClient.projectsGroupsApi.getProject(projectId))
|
|
158
|
+
.data;
|
|
159
|
+
}
|
|
160
|
+
catch (e) {
|
|
161
|
+
(0, logger_1.logError)(e);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/camelcase
|
|
165
|
+
context.jwtPayload.context.project_identifier = projectData.identifier;
|
|
166
|
+
const rootFolder = yield (0, defaults_1.getRootFolder)(config, integration, crowdinClient, projectId);
|
|
167
|
+
const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
168
|
+
if (!integration.webhooks &&
|
|
169
|
+
((_a = integration.syncNewElements) === null || _a === void 0 ? void 0 : _a[syncSettings.provider]) &&
|
|
170
|
+
intConfig[`new-${syncSettings.provider}-files`]) {
|
|
121
171
|
try {
|
|
122
|
-
|
|
172
|
+
newFiles = yield getAllNewFiles({
|
|
123
173
|
config,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
174
|
+
integration,
|
|
175
|
+
projectData,
|
|
176
|
+
syncSettings,
|
|
177
|
+
crowdinApiClient: crowdinClient,
|
|
178
|
+
crowdinId: crowdinCredentials.id,
|
|
179
|
+
integrationCredentials: apiCredentials,
|
|
180
|
+
integrationId: integrationCredentials.id,
|
|
181
|
+
integrationSettings: intConfig,
|
|
127
182
|
});
|
|
128
|
-
token = preparedCrowdinClient.token;
|
|
129
|
-
crowdinClient = preparedCrowdinClient.client;
|
|
130
183
|
}
|
|
131
184
|
catch (e) {
|
|
132
185
|
(0, logger_1.logError)(e);
|
|
133
|
-
|
|
186
|
+
return;
|
|
134
187
|
}
|
|
135
|
-
|
|
188
|
+
}
|
|
189
|
+
if (integration.webhooks) {
|
|
190
|
+
const webhooks = yield (0, storage_1.getStorage)().getAllWebhooks(syncSettings.integrationId, syncSettings.crowdinId, syncSettings.provider);
|
|
191
|
+
const webhooksFileIds = (webhooks || []).map((webhook) => webhook.fileId);
|
|
192
|
+
if (syncSettings.provider === types_1.Provider.CROWDIN) {
|
|
193
|
+
files = webhooksFileIds.reduce((acc, fileId) => {
|
|
194
|
+
if (files[fileId]) {
|
|
195
|
+
acc[fileId] = files[fileId];
|
|
196
|
+
}
|
|
197
|
+
return acc;
|
|
198
|
+
}, {});
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
files = files.filter((file) => webhooksFileIds.includes(file.id));
|
|
202
|
+
}
|
|
203
|
+
yield (0, storage_1.getStorage)().deleteWebhooks(webhooksFileIds, syncSettings.integrationId, syncSettings.crowdinId, syncSettings.provider);
|
|
204
|
+
}
|
|
205
|
+
if (syncSettings.provider === types_1.Provider.CROWDIN) {
|
|
206
|
+
const crowdinFiles = yield skipFoldersFromIntegrationRequest({
|
|
136
207
|
config,
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
208
|
+
integration,
|
|
209
|
+
projectId,
|
|
210
|
+
crowdinFiles: Object.assign(Object.assign({}, files), newFiles),
|
|
211
|
+
crowdinClient,
|
|
140
212
|
});
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
213
|
+
const onlyTranslated = +intConfig.condition === types_1.SyncCondition.TRANSLATED;
|
|
214
|
+
const onlyApproved = +intConfig.condition === types_1.SyncCondition.APPROVED;
|
|
215
|
+
const all = +intConfig.condition === types_1.SyncCondition.ALL || intConfig.condition === undefined;
|
|
216
|
+
const filesToProcess = all
|
|
217
|
+
? crowdinFiles
|
|
218
|
+
: yield getOnlyTranslatedOrApprovedFiles({
|
|
219
|
+
projectId,
|
|
220
|
+
crowdinFiles,
|
|
221
|
+
crowdinClient,
|
|
222
|
+
onlyApproved,
|
|
223
|
+
onlyTranslated,
|
|
224
|
+
});
|
|
225
|
+
if (Object.keys(filesToProcess).length <= 0) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
(0, logger_1.log)(`Executing updateIntegration task for files cron job with period [${period}] for project ${projectId}.Files ${Object.keys(filesToProcess).length}`);
|
|
229
|
+
if (!all) {
|
|
230
|
+
if (Object.keys(filesToProcess).length === 0) {
|
|
231
|
+
(0, logger_1.log)(`There is no ${onlyApproved ? 'approved' : 'translated'} file`);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
236
|
+
if (!(intConfig === null || intConfig === void 0 ? void 0 : intConfig.inContext)) {
|
|
237
|
+
removeInContextLanguage(filesToProcess, projectData);
|
|
144
238
|
}
|
|
145
239
|
try {
|
|
146
|
-
|
|
147
|
-
.
|
|
240
|
+
yield (0, job_1.runAsJob)({
|
|
241
|
+
integrationId: syncSettings.integrationId,
|
|
242
|
+
crowdinId: syncSettings.crowdinId,
|
|
243
|
+
type: types_2.JobType.UPDATE_TO_INTEGRATION,
|
|
244
|
+
title: `Sync files to ${config.name} [scheduled]`,
|
|
245
|
+
payload: filesToProcess,
|
|
246
|
+
jobType: types_2.JobClientType.CRON,
|
|
247
|
+
projectId: projectId,
|
|
248
|
+
client: crowdinClient,
|
|
249
|
+
jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
|
|
250
|
+
yield integration.updateIntegration({
|
|
251
|
+
projectId,
|
|
252
|
+
client: crowdinClient,
|
|
253
|
+
credentials: apiCredentials,
|
|
254
|
+
request: filesToProcess,
|
|
255
|
+
rootFolder,
|
|
256
|
+
appSettings: intConfig,
|
|
257
|
+
job,
|
|
258
|
+
});
|
|
259
|
+
}),
|
|
260
|
+
});
|
|
148
261
|
}
|
|
149
262
|
catch (e) {
|
|
150
|
-
(0, logger_1.
|
|
151
|
-
|
|
263
|
+
yield (0, logger_1.handleUserError)({
|
|
264
|
+
action: 'Auto sync files to External Service',
|
|
265
|
+
error: e,
|
|
266
|
+
crowdinId: syncSettings.crowdinId,
|
|
267
|
+
clientId: syncSettings.integrationId,
|
|
268
|
+
});
|
|
269
|
+
(0, logger_1.logError)(e, context);
|
|
270
|
+
return;
|
|
152
271
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (!integration.webhooks &&
|
|
158
|
-
((_a = integration.syncNewElements) === null || _a === void 0 ? void 0 : _a[syncSettings.provider]) &&
|
|
159
|
-
intConfig[`new-${syncSettings.provider}-files`]) {
|
|
160
|
-
try {
|
|
161
|
-
newFiles = yield getAllNewFiles({
|
|
162
|
-
config,
|
|
163
|
-
integration,
|
|
164
|
-
projectData,
|
|
165
|
-
syncSettings,
|
|
166
|
-
crowdinApiClient: crowdinClient,
|
|
167
|
-
crowdinId: crowdinCredentials.id,
|
|
168
|
-
integrationCredentials: apiCredentials,
|
|
169
|
-
integrationId: integrationCredentials.id,
|
|
170
|
-
integrationSettings: intConfig,
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
catch (e) {
|
|
174
|
-
(0, logger_1.logError)(e);
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
272
|
+
if (Object.keys(newFiles).length) {
|
|
273
|
+
yield (0, storage_1.getStorage)().updateSyncSettings(JSON.stringify(Object.assign(Object.assign({}, files), newFiles)), syncSettings.integrationId, syncSettings.crowdinId, 'schedule', syncSettings.provider);
|
|
274
|
+
const currentFileSnapshot = yield (0, snapshot_1.getCrowdinSnapshot)(config, integration, crowdinClient, projectId, intConfig);
|
|
275
|
+
yield (0, storage_1.getStorage)().updateFilesSnapshot(JSON.stringify(currentFileSnapshot), syncSettings.integrationId, syncSettings.crowdinId, syncSettings.provider);
|
|
177
276
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
else {
|
|
190
|
-
files = files.filter((file) => webhooksFileIds.includes(file.id));
|
|
191
|
-
}
|
|
192
|
-
yield (0, storage_1.getStorage)().deleteWebhooks(webhooksFileIds, syncSettings.integrationId, syncSettings.crowdinId, syncSettings.provider);
|
|
277
|
+
(0, logger_1.log)(`updateIntegration task for files cron job with period [${period}] for project ${projectId} completed`);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
const allIntFiles = [...files, ...newFiles].map((file) => (Object.assign({ id: file.id, name: file.name, parentId: file.parent_id || file.parentId,
|
|
281
|
+
// eslint-disable-next-line @typescript-eslint/camelcase
|
|
282
|
+
parent_id: file.parent_id || file.parentId,
|
|
283
|
+
// eslint-disable-next-line @typescript-eslint/camelcase
|
|
284
|
+
node_type: file.nodeType || file.node_type }, (file.type ? { type: file.type } : {}))));
|
|
285
|
+
const intFiles = allIntFiles.filter((file) => 'type' in file);
|
|
286
|
+
if (intFiles.length <= 0) {
|
|
287
|
+
return;
|
|
193
288
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
289
|
+
(0, logger_1.log)(`Executing updateCrowdin task for files cron job with period [${period}] for project ${projectId}. Files ${intFiles.length}`);
|
|
290
|
+
const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
291
|
+
try {
|
|
292
|
+
yield (0, job_1.runAsJob)({
|
|
293
|
+
integrationId: syncSettings.integrationId,
|
|
294
|
+
crowdinId: syncSettings.crowdinId,
|
|
295
|
+
type: types_2.JobType.UPDATE_TO_CROWDIN,
|
|
296
|
+
title: 'Sync files to Crowdin [scheduled]',
|
|
297
|
+
payload: intFiles,
|
|
298
|
+
jobType: types_2.JobClientType.CRON,
|
|
299
|
+
projectId: projectId,
|
|
300
|
+
client: crowdinClient,
|
|
301
|
+
jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
|
|
302
|
+
yield integration.updateCrowdin({
|
|
303
|
+
projectId,
|
|
304
|
+
client: crowdinClient,
|
|
305
|
+
credentials: apiCredentials,
|
|
306
|
+
request: intFiles,
|
|
307
|
+
rootFolder,
|
|
308
|
+
appSettings: intConfig,
|
|
309
|
+
job,
|
|
310
|
+
});
|
|
311
|
+
}),
|
|
201
312
|
});
|
|
202
|
-
const onlyTranslated = +intConfig.condition === types_1.SyncCondition.TRANSLATED;
|
|
203
|
-
const onlyApproved = +intConfig.condition === types_1.SyncCondition.APPROVED;
|
|
204
|
-
const all = +intConfig.condition === types_1.SyncCondition.ALL || intConfig.condition === undefined;
|
|
205
|
-
const filesToProcess = all
|
|
206
|
-
? crowdinFiles
|
|
207
|
-
: yield getOnlyTranslatedOrApprovedFiles({
|
|
208
|
-
projectId,
|
|
209
|
-
crowdinFiles,
|
|
210
|
-
crowdinClient,
|
|
211
|
-
onlyApproved,
|
|
212
|
-
onlyTranslated,
|
|
213
|
-
});
|
|
214
|
-
if (Object.keys(filesToProcess).length <= 0) {
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
(0, logger_1.log)(`Executing updateIntegration task for files cron job with period [${period}] for project ${projectId}.Files ${Object.keys(filesToProcess).length}`);
|
|
218
|
-
if (!all) {
|
|
219
|
-
if (Object.keys(filesToProcess).length === 0) {
|
|
220
|
-
(0, logger_1.log)(`There is no ${onlyApproved ? 'approved' : 'translated'} file`);
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
const apiCredentials = yield (0, connection_1.prepareIntegrationCredentials)(config, integration, integrationCredentials);
|
|
225
|
-
if (!(intConfig === null || intConfig === void 0 ? void 0 : intConfig.inContext)) {
|
|
226
|
-
removeInContextLanguage(filesToProcess, projectData);
|
|
227
|
-
}
|
|
228
|
-
try {
|
|
229
|
-
yield (0, job_1.runAsJob)({
|
|
230
|
-
integrationId: syncSettings.integrationId,
|
|
231
|
-
crowdinId: syncSettings.crowdinId,
|
|
232
|
-
type: types_2.JobType.UPDATE_TO_INTEGRATION,
|
|
233
|
-
title: `Sync files to ${config.name} [scheduled]`,
|
|
234
|
-
payload: filesToProcess,
|
|
235
|
-
jobType: types_2.JobClientType.CRON,
|
|
236
|
-
projectId: projectId,
|
|
237
|
-
client: crowdinClient,
|
|
238
|
-
jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
|
|
239
|
-
yield integration.updateIntegration({
|
|
240
|
-
projectId,
|
|
241
|
-
client: crowdinClient,
|
|
242
|
-
credentials: apiCredentials,
|
|
243
|
-
request: filesToProcess,
|
|
244
|
-
rootFolder,
|
|
245
|
-
appSettings: intConfig,
|
|
246
|
-
job,
|
|
247
|
-
});
|
|
248
|
-
}),
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
catch (e) {
|
|
252
|
-
yield (0, logger_1.handleUserError)({
|
|
253
|
-
action: 'Auto sync files to External Service',
|
|
254
|
-
error: e,
|
|
255
|
-
crowdinId: syncSettings.crowdinId,
|
|
256
|
-
clientId: syncSettings.integrationId,
|
|
257
|
-
});
|
|
258
|
-
(0, logger_1.logError)(e, context);
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
if (Object.keys(newFiles).length) {
|
|
262
|
-
yield (0, storage_1.getStorage)().updateSyncSettings(JSON.stringify(Object.assign(Object.assign({}, files), newFiles)), syncSettings.integrationId, syncSettings.crowdinId, 'schedule', syncSettings.provider);
|
|
263
|
-
const currentFileSnapshot = yield (0, snapshot_1.getCrowdinSnapshot)(config, integration, crowdinClient, projectId, intConfig);
|
|
264
|
-
yield (0, storage_1.getStorage)().updateFilesSnapshot(JSON.stringify(currentFileSnapshot), syncSettings.integrationId, syncSettings.crowdinId, syncSettings.provider);
|
|
265
|
-
}
|
|
266
|
-
(0, logger_1.log)(`updateIntegration task for files cron job with period [${period}] for project ${projectId} completed`);
|
|
267
313
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
crowdinId: syncSettings.crowdinId,
|
|
284
|
-
type: types_2.JobType.UPDATE_TO_CROWDIN,
|
|
285
|
-
title: 'Sync files to Crowdin [scheduled]',
|
|
286
|
-
payload: intFiles,
|
|
287
|
-
jobType: types_2.JobClientType.CRON,
|
|
288
|
-
projectId: projectId,
|
|
289
|
-
client: crowdinClient,
|
|
290
|
-
jobCallback: (job) => __awaiter(this, void 0, void 0, function* () {
|
|
291
|
-
yield integration.updateCrowdin({
|
|
292
|
-
projectId,
|
|
293
|
-
client: crowdinClient,
|
|
294
|
-
credentials: apiCredentials,
|
|
295
|
-
request: intFiles,
|
|
296
|
-
rootFolder,
|
|
297
|
-
appSettings: intConfig,
|
|
298
|
-
job,
|
|
299
|
-
});
|
|
300
|
-
}),
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
catch (e) {
|
|
304
|
-
yield (0, logger_1.handleUserError)({
|
|
305
|
-
action: 'Auto sync files to Crowdin',
|
|
306
|
-
error: e,
|
|
307
|
-
crowdinId: syncSettings.crowdinId,
|
|
308
|
-
clientId: syncSettings.integrationId,
|
|
309
|
-
});
|
|
310
|
-
(0, logger_1.logError)(e, context);
|
|
311
|
-
continue;
|
|
312
|
-
}
|
|
313
|
-
if (Object.keys(newFiles).length) {
|
|
314
|
-
const newSyncSettingsFields = allIntFiles.map((file) => (Object.assign(Object.assign({}, file), { schedule: true, sync: false })));
|
|
315
|
-
yield (0, storage_1.getStorage)().updateSyncSettings(JSON.stringify(newSyncSettingsFields), syncSettings.integrationId, syncSettings.crowdinId, 'schedule', syncSettings.provider);
|
|
316
|
-
const currentFileSnapshot = yield (0, snapshot_1.getIntegrationSnapshot)(integration, apiCredentials, intConfig);
|
|
317
|
-
yield (0, storage_1.getStorage)().updateFilesSnapshot(JSON.stringify(currentFileSnapshot), syncSettings.integrationId, syncSettings.crowdinId, syncSettings.provider);
|
|
318
|
-
}
|
|
319
|
-
(0, logger_1.log)(`updateCrowdin task for files cron job with period [${period}] for project ${projectId} completed`);
|
|
314
|
+
catch (e) {
|
|
315
|
+
yield (0, logger_1.handleUserError)({
|
|
316
|
+
action: 'Auto sync files to Crowdin',
|
|
317
|
+
error: e,
|
|
318
|
+
crowdinId: syncSettings.crowdinId,
|
|
319
|
+
clientId: syncSettings.integrationId,
|
|
320
|
+
});
|
|
321
|
+
(0, logger_1.logError)(e, context);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (Object.keys(newFiles).length) {
|
|
325
|
+
const newSyncSettingsFields = allIntFiles.map((file) => (Object.assign(Object.assign({}, file), { schedule: true, sync: false })));
|
|
326
|
+
yield (0, storage_1.getStorage)().updateSyncSettings(JSON.stringify(newSyncSettingsFields), syncSettings.integrationId, syncSettings.crowdinId, 'schedule', syncSettings.provider);
|
|
327
|
+
const currentFileSnapshot = yield (0, snapshot_1.getIntegrationSnapshot)(integration, apiCredentials, intConfig);
|
|
328
|
+
yield (0, storage_1.getStorage)().updateFilesSnapshot(JSON.stringify(currentFileSnapshot), syncSettings.integrationId, syncSettings.crowdinId, syncSettings.provider);
|
|
320
329
|
}
|
|
330
|
+
(0, logger_1.log)(`updateCrowdin task for files cron job with period [${period}] for project ${projectId} completed`);
|
|
321
331
|
}
|
|
322
|
-
(0, logger_1.log)(`Files cron job with period [${period}] completed`);
|
|
323
332
|
});
|
|
324
333
|
}
|
|
325
|
-
exports.filesCron = filesCron;
|
|
326
334
|
function getFileDiff(currentFiles, savedFiles) {
|
|
327
335
|
return currentFiles.filter((x) => !savedFiles.some((x2) => x2.id === x.id));
|
|
328
336
|
}
|
package/out/types.d.ts
CHANGED
|
@@ -364,6 +364,10 @@ export interface UiModule {
|
|
|
364
364
|
* Module name
|
|
365
365
|
*/
|
|
366
366
|
name?: string;
|
|
367
|
+
/**
|
|
368
|
+
* Temporary property. Indicates if passwords should be masked. Will be dropped when all existing apps will migrate.
|
|
369
|
+
*/
|
|
370
|
+
maskPasswords?: boolean;
|
|
367
371
|
}
|
|
368
372
|
export interface ImagePath {
|
|
369
373
|
/**
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="qs" />
|
|
2
|
+
import { CrowdinClientRequest, UiModule } from '../types';
|
|
3
|
+
import { Request, Response } from 'express';
|
|
4
|
+
declare function postRequestCredentialsMasker(moduleConfig?: UiModule, credentialsExtractor?: Function): (req: CrowdinClientRequest | Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
|
|
5
|
+
declare function getRequestCredentialsMasker(moduleConfig?: UiModule): (req: Request | CrowdinClientRequest, res: Response, next: Function) => any;
|
|
6
|
+
export { getRequestCredentialsMasker, postRequestCredentialsMasker };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.postRequestCredentialsMasker = exports.getRequestCredentialsMasker = void 0;
|
|
16
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
17
|
+
const index_1 = require("./index");
|
|
18
|
+
const index_2 = require("../index");
|
|
19
|
+
const crowdin_client_1 = require("../middlewares/crowdin-client");
|
|
20
|
+
const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
|
|
21
|
+
function maskKey(key) {
|
|
22
|
+
const maskWith = '*';
|
|
23
|
+
const unmaskedCharsAtEnd = 3;
|
|
24
|
+
const repeatCount = key.length > unmaskedCharsAtEnd ? key.length - unmaskedCharsAtEnd : 0;
|
|
25
|
+
return maskWith.repeat(repeatCount) + key.substring(key.length - 3);
|
|
26
|
+
}
|
|
27
|
+
function getMaskableFieldsKeys(moduleConfig) {
|
|
28
|
+
if (!moduleConfig.formUiSchema) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
return Object.keys(moduleConfig.formUiSchema).filter((fieldKey) => {
|
|
32
|
+
return lodash_1.default.get(moduleConfig, `formUiSchema[${fieldKey}]['ui:widget']`) === 'password';
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function postRequestCredentialsMasker(moduleConfig, credentialsExtractor) {
|
|
36
|
+
return (0, index_1.runAsyncWrapper)((req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
37
|
+
if (!moduleConfig || !moduleConfig.formSchema || !moduleConfig.formUiSchema) {
|
|
38
|
+
return next();
|
|
39
|
+
}
|
|
40
|
+
// temporary
|
|
41
|
+
if (!moduleConfig.maskPasswords) {
|
|
42
|
+
return next();
|
|
43
|
+
}
|
|
44
|
+
const fieldsKeysInRequest = Object.keys(req.body.data);
|
|
45
|
+
let unmaskedFields = {};
|
|
46
|
+
if (credentialsExtractor) {
|
|
47
|
+
unmaskedFields = yield credentialsExtractor(req, res);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// most common way of storing data
|
|
51
|
+
const jwtToken = (0, crowdin_client_1.getToken)(req) || '';
|
|
52
|
+
const jwtPayload = yield (0, crowdin_apps_functions_1.validateJwtToken)(jwtToken, process.env.CROWDIN_CLIENT_SECRET || '');
|
|
53
|
+
const crowdinId = `${jwtPayload.context.organization_id}`;
|
|
54
|
+
unmaskedFields = yield index_2.metadataStore.getMetadata(`form-data-${crowdinId}`);
|
|
55
|
+
}
|
|
56
|
+
unmaskedFields = unmaskedFields || {};
|
|
57
|
+
const maskableFieldsKeys = getMaskableFieldsKeys(moduleConfig);
|
|
58
|
+
Object.keys(unmaskedFields).forEach((fieldKey) => {
|
|
59
|
+
if (!maskableFieldsKeys.includes(fieldKey) || !fieldsKeysInRequest.includes(fieldKey)) {
|
|
60
|
+
delete unmaskedFields[fieldKey];
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
req.body.data = Object.assign(Object.assign({}, req.body.data), Object.keys(unmaskedFields).reduce((acc, key) => {
|
|
64
|
+
if (maskKey(unmaskedFields[key]) === req.body.data[key]) {
|
|
65
|
+
acc[key] = unmaskedFields[key];
|
|
66
|
+
}
|
|
67
|
+
return acc;
|
|
68
|
+
}, {}));
|
|
69
|
+
next();
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
exports.postRequestCredentialsMasker = postRequestCredentialsMasker;
|
|
73
|
+
function getRequestCredentialsMasker(moduleConfig) {
|
|
74
|
+
return function (req, res, next) {
|
|
75
|
+
// we can't find "password" fields without ui schema
|
|
76
|
+
if (!moduleConfig || !moduleConfig.formSchema || !moduleConfig.formUiSchema) {
|
|
77
|
+
return next();
|
|
78
|
+
}
|
|
79
|
+
// temporary
|
|
80
|
+
if (!moduleConfig.maskPasswords) {
|
|
81
|
+
return next();
|
|
82
|
+
}
|
|
83
|
+
const maskableFieldsKeys = getMaskableFieldsKeys(moduleConfig);
|
|
84
|
+
if (!maskableFieldsKeys.length) {
|
|
85
|
+
return next();
|
|
86
|
+
}
|
|
87
|
+
const originalSend = res.send;
|
|
88
|
+
res.send = function (body) {
|
|
89
|
+
if (body.formData) {
|
|
90
|
+
maskableFieldsKeys.forEach((fieldKey) => {
|
|
91
|
+
if (!body.formData[fieldKey]) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
body.formData[fieldKey] = maskKey(body.formData[fieldKey]);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return originalSend.apply(res, [body]);
|
|
98
|
+
};
|
|
99
|
+
return next();
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
exports.getRequestCredentialsMasker = getRequestCredentialsMasker;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hasFormSchema = exports.getLowCodeUiConfigFromModuleConfig = void 0;
|
|
4
|
+
function getLowCodeUiConfigFromModuleConfig(moduleConfig) {
|
|
5
|
+
var _a;
|
|
6
|
+
if (typeof moduleConfig !== 'object' || moduleConfig === null) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
if ((_a = moduleConfig.settingsUiModule) === null || _a === void 0 ? void 0 : _a.formSchema) {
|
|
10
|
+
return moduleConfig.settingsUiModule;
|
|
11
|
+
}
|
|
12
|
+
if (moduleConfig.formSchema) {
|
|
13
|
+
return moduleConfig;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
exports.getLowCodeUiConfigFromModuleConfig = getLowCodeUiConfigFromModuleConfig;
|
|
18
|
+
function hasFormSchema(moduleConfig) {
|
|
19
|
+
return !!getLowCodeUiConfigFromModuleConfig(moduleConfig);
|
|
20
|
+
}
|
|
21
|
+
exports.hasFormSchema = hasFormSchema;
|
package/out/util/index.d.ts
CHANGED
|
@@ -10,4 +10,3 @@ export declare function decryptData(config: Config, data: string): string;
|
|
|
10
10
|
export declare function executeWithRetry<T>(func: () => Promise<T>, numOfRetries?: number): Promise<T>;
|
|
11
11
|
export declare function getLogoUrl(moduleConfig?: ImagePath, modulePath?: string): string;
|
|
12
12
|
export declare function isAuthorizedConfig(config: Config | UnauthorizedConfig): config is Config;
|
|
13
|
-
export declare function hasFormSchema(moduleConfig: any): boolean;
|
package/out/util/index.js
CHANGED
|
@@ -32,7 +32,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
32
32
|
});
|
|
33
33
|
};
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.
|
|
35
|
+
exports.isAuthorizedConfig = exports.getLogoUrl = exports.executeWithRetry = exports.decryptData = exports.encryptData = exports.runAsyncWrapper = exports.CodeError = void 0;
|
|
36
36
|
const crypto = __importStar(require("crypto-js"));
|
|
37
37
|
const storage_1 = require("../storage");
|
|
38
38
|
const types_1 = require("../types");
|
|
@@ -118,11 +118,3 @@ function isAuthorizedConfig(config) {
|
|
|
118
118
|
return !!config.clientId && !!config.clientSecret && config.authenticationType !== types_1.AuthenticationType.NONE;
|
|
119
119
|
}
|
|
120
120
|
exports.isAuthorizedConfig = isAuthorizedConfig;
|
|
121
|
-
function hasFormSchema(moduleConfig) {
|
|
122
|
-
var _a;
|
|
123
|
-
if (typeof moduleConfig === 'object' && moduleConfig !== null) {
|
|
124
|
-
return moduleConfig.formSchema || ((_a = moduleConfig.settingsUiModule) === null || _a === void 0 ? void 0 : _a.formSchema);
|
|
125
|
-
}
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
exports.hasFormSchema = hasFormSchema;
|
package/package.json
CHANGED