@crowdin/app-project-module 0.34.2 → 0.35.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.
- package/out/handlers/file-processing/custom-file-format.js +8 -9
- package/out/handlers/file-processing/pre-post-process.js +13 -10
- package/out/handlers/integration/crowdin-webhook.js +1 -1
- package/out/handlers/integration/main.js +1 -0
- package/out/handlers/integration/sync-settings-save.js +2 -1
- package/out/index.js +5 -0
- package/out/models/index.d.ts +39 -11
- package/out/static/js/form.js +36 -15
- package/out/util/cron.js +3 -2
- package/out/util/defaults.d.ts +2 -1
- package/out/util/defaults.js +24 -2
- package/out/util/files.d.ts +5 -2
- package/out/util/files.js +13 -9
- package/out/util/webhooks.js +2 -2
- package/out/views/main.handlebars +39 -11
- package/package.json +21 -19
|
@@ -37,8 +37,7 @@ function handle(baseUrl, folder, config) {
|
|
|
37
37
|
file = Buffer.from(body.file.content, 'base64');
|
|
38
38
|
}
|
|
39
39
|
else if (body.file.contentUrl) {
|
|
40
|
-
|
|
41
|
-
file = Buffer.from(rawContent).toString('base64');
|
|
40
|
+
file = yield (0, files_1.getFileContent)(body.file.contentUrl);
|
|
42
41
|
}
|
|
43
42
|
let response = {};
|
|
44
43
|
let error;
|
|
@@ -71,7 +70,7 @@ function handleBuildFile(baseUrl, dataFolder, config, req, client, context, proj
|
|
|
71
70
|
strings = req.strings;
|
|
72
71
|
}
|
|
73
72
|
else {
|
|
74
|
-
strings = yield (0, files_1.
|
|
73
|
+
strings = yield (0, files_1.getFileStrings)(req.stringsUrl);
|
|
75
74
|
}
|
|
76
75
|
let res;
|
|
77
76
|
if ((req.file.id || !file) && config.exportStrings) {
|
|
@@ -113,9 +112,8 @@ function handleParseFile(baseUrl, dataFolder, config, req, client, context, proj
|
|
|
113
112
|
maxSize = maxSize / 2;
|
|
114
113
|
}
|
|
115
114
|
if (res.previewFile) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
response.preview = previewFileEncoded;
|
|
115
|
+
if (Buffer.byteLength(res.previewFile) < maxSize) {
|
|
116
|
+
response.preview = res.previewFile.toString('base64');
|
|
119
117
|
}
|
|
120
118
|
else {
|
|
121
119
|
let url;
|
|
@@ -141,16 +139,17 @@ function handleParseFile(baseUrl, dataFolder, config, req, client, context, proj
|
|
|
141
139
|
});
|
|
142
140
|
}
|
|
143
141
|
const stringsNDJson = strings.map((s) => JSON.stringify(s)).join('\n\r');
|
|
144
|
-
|
|
142
|
+
const bufferData = Buffer.from(stringsNDJson, 'utf8');
|
|
143
|
+
if (Buffer.byteLength(bufferData) < maxSize) {
|
|
145
144
|
response.strings = strings;
|
|
146
145
|
}
|
|
147
146
|
else {
|
|
148
147
|
let url;
|
|
149
148
|
if (config.storeFile) {
|
|
150
|
-
url = yield config.storeFile(
|
|
149
|
+
url = yield config.storeFile(bufferData);
|
|
151
150
|
}
|
|
152
151
|
else {
|
|
153
|
-
const storedFile = yield (0, files_1.storeFile)(
|
|
152
|
+
const storedFile = yield (0, files_1.storeFile)(bufferData, path_1.default.join(dataFolder, 'custom-file-format'));
|
|
154
153
|
url = `${baseUrl}?file=${storedFile}`;
|
|
155
154
|
}
|
|
156
155
|
response.stringsUrl = url;
|
|
@@ -30,14 +30,14 @@ function handle(baseConfig, config, folderName) {
|
|
|
30
30
|
let fileContent;
|
|
31
31
|
let rawContent;
|
|
32
32
|
if (body.stringsUrl) {
|
|
33
|
-
fileContent = yield (0, files_1.
|
|
33
|
+
fileContent = yield (0, files_1.getFileStrings)(body.stringsUrl);
|
|
34
34
|
}
|
|
35
35
|
else if (body.strings) {
|
|
36
36
|
fileContent = body.strings;
|
|
37
37
|
}
|
|
38
38
|
else if (body.file.contentUrl) {
|
|
39
39
|
rawContent = yield (0, files_1.getFileContent)(body.file.contentUrl);
|
|
40
|
-
fileContent =
|
|
40
|
+
fileContent = rawContent;
|
|
41
41
|
}
|
|
42
42
|
else if (body.file.content) {
|
|
43
43
|
rawContent = body.file.content;
|
|
@@ -45,18 +45,20 @@ function handle(baseConfig, config, folderName) {
|
|
|
45
45
|
}
|
|
46
46
|
if (body.jobType === models_1.ProcessFileJobType.PRE_IMPORT || body.jobType === models_1.ProcessFileJobType.POST_EXPORT) {
|
|
47
47
|
body.getRawContent = (encoding) => __awaiter(this, void 0, void 0, function* () {
|
|
48
|
-
|
|
48
|
+
if (typeof rawContent === 'string') {
|
|
49
|
+
return Buffer.from(rawContent, 'base64').toString(encoding);
|
|
50
|
+
}
|
|
51
|
+
return rawContent;
|
|
49
52
|
});
|
|
50
53
|
}
|
|
51
54
|
const fileProcessResult = yield config.fileProcess(body, fileContent, req.crowdinApiClient, req.crowdinContext, req.crowdinContext.jwtPayload.context.project_id);
|
|
52
55
|
switch (body.jobType) {
|
|
53
56
|
case models_1.ProcessFileJobType.PRE_IMPORT:
|
|
54
57
|
case models_1.ProcessFileJobType.POST_EXPORT:
|
|
55
|
-
const { contentFile,
|
|
58
|
+
const { contentFile, fileName, fileType, error: contentFileError, } = fileProcessResult;
|
|
56
59
|
if (contentFile) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
response.content = contentFileEncoded;
|
|
60
|
+
if (Buffer.byteLength(contentFile) < files_1.MAX_BODY_SIZE) {
|
|
61
|
+
response.content = contentFile.toString('base64');
|
|
60
62
|
}
|
|
61
63
|
else {
|
|
62
64
|
let url;
|
|
@@ -83,16 +85,17 @@ function handle(baseConfig, config, folderName) {
|
|
|
83
85
|
const { strings, error: stringsFileError } = fileProcessResult;
|
|
84
86
|
if (strings) {
|
|
85
87
|
const stringsNDJson = strings.map((s) => JSON.stringify(s)).join('\n\r');
|
|
86
|
-
|
|
88
|
+
const bufferData = Buffer.from(stringsNDJson, 'utf-8');
|
|
89
|
+
if (Buffer.byteLength(bufferData) < files_1.MAX_BODY_SIZE) {
|
|
87
90
|
response.strings = strings;
|
|
88
91
|
}
|
|
89
92
|
else {
|
|
90
93
|
let url;
|
|
91
94
|
if (config.storeFile) {
|
|
92
|
-
url = yield config.storeFile(
|
|
95
|
+
url = yield config.storeFile(bufferData);
|
|
93
96
|
}
|
|
94
97
|
else {
|
|
95
|
-
const storedFile = yield (0, files_1.storeFile)(
|
|
98
|
+
const storedFile = yield (0, files_1.storeFile)(bufferData, path_1.default.join(folderPath, folderName));
|
|
96
99
|
url = `${baseFilesUrl}?file=${storedFile}`;
|
|
97
100
|
}
|
|
98
101
|
response.stringsUrl = url;
|
|
@@ -37,7 +37,7 @@ function handle(config, integration) {
|
|
|
37
37
|
}
|
|
38
38
|
const crowdinFiles = yield (0, cron_1.skipFoldersFromIntegrationRequest)(config, integration, projectId, Object.assign(Object.assign({}, filesToSync), newFiles), crowdinClient.client);
|
|
39
39
|
const result = yield integration.updateIntegration(projectId, crowdinClient.client, preparedIntegrationCredentials, crowdinFiles, rootFolder, appSettings);
|
|
40
|
-
if (newFiles) {
|
|
40
|
+
if (Object.keys(newFiles).length) {
|
|
41
41
|
yield (0, storage_1.getStorage)().updateSyncSettings(JSON.stringify(Object.assign(Object.assign({}, filesToSync), newFiles)), syncSettings.integrationId, syncSettings.crowdinId, 'schedule', syncSettings.provider);
|
|
42
42
|
const currentFileSnapshot = yield (0, file_snapshot_1.getCrowdinSnapshot)(config, integration, crowdinClient.client, projectId, appSettings);
|
|
43
43
|
yield (0, storage_1.getStorage)().updateFilesSnapshot(JSON.stringify(currentFileSnapshot), syncSettings.integrationId, syncSettings.crowdinId, syncSettings.provider);
|
|
@@ -17,7 +17,8 @@ function handle(config, integration) {
|
|
|
17
17
|
var _a;
|
|
18
18
|
const { files, provider } = req.body;
|
|
19
19
|
yield (0, cron_1.createOrUpdateSyncSettings)(config, req, files, provider);
|
|
20
|
-
|
|
20
|
+
const appSettings = req.integrationSettings || {};
|
|
21
|
+
if (((_a = integration.syncNewElements) === null || _a === void 0 ? void 0 : _a[provider]) && appSettings[`new-${provider}-files`]) {
|
|
21
22
|
yield (0, file_snapshot_1.createOrUpdateFileSnapshot)(config, integration, req, provider);
|
|
22
23
|
}
|
|
23
24
|
res.status(204).end();
|
package/out/index.js
CHANGED
|
@@ -186,22 +186,27 @@ function addCrowdinEndpoints(app, clientConfig) {
|
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
if (config.customFileFormat) {
|
|
189
|
+
(0, defaults_1.applyFileProcessorsModuleDefaults)(config, config.customFileFormat);
|
|
189
190
|
app.post('/process', (0, crowdin_client_1.default)(config), (0, custom_file_format_1.default)(config.baseUrl, config.customFileFormat.filesFolder || config.dbFolder, config.customFileFormat));
|
|
190
191
|
app.get('/file/download', (0, file_download_1.default)(config, config.customFileFormat, 'custom-file-format'));
|
|
191
192
|
}
|
|
192
193
|
if (config.filePreImport) {
|
|
194
|
+
(0, defaults_1.applyFileProcessorsModuleDefaults)(config, config.filePreImport);
|
|
193
195
|
app.post('/pre-import', (0, crowdin_client_1.default)(config), (0, pre_post_process_1.default)(config, config.filePreImport, models_1.ProcessFileJobType.PRE_IMPORT));
|
|
194
196
|
app.get(`/file/download/${models_1.ProcessFileJobType.PRE_IMPORT}`, (0, file_download_1.default)(config, config.filePreImport, models_1.ProcessFileJobType.PRE_IMPORT));
|
|
195
197
|
}
|
|
196
198
|
if (config.filePostImport) {
|
|
199
|
+
(0, defaults_1.applyFileProcessorsModuleDefaults)(config, config.filePostImport);
|
|
197
200
|
app.post('/post-import', (0, crowdin_client_1.default)(config), (0, pre_post_process_1.default)(config, config.filePostImport, models_1.ProcessFileJobType.POST_IMPORT));
|
|
198
201
|
app.get(`/file/download/${models_1.ProcessFileJobType.POST_IMPORT}`, (0, file_download_1.default)(config, config.filePostImport, models_1.ProcessFileJobType.POST_IMPORT));
|
|
199
202
|
}
|
|
200
203
|
if (config.filePreExport) {
|
|
204
|
+
(0, defaults_1.applyFileProcessorsModuleDefaults)(config, config.filePreExport);
|
|
201
205
|
app.post('/pre-export', (0, crowdin_client_1.default)(config), (0, pre_post_process_1.default)(config, config.filePreExport, models_1.ProcessFileJobType.PRE_EXPORT));
|
|
202
206
|
app.get(`/file/download/${models_1.ProcessFileJobType.PRE_EXPORT}`, (0, file_download_1.default)(config, config.filePreExport, models_1.ProcessFileJobType.PRE_EXPORT));
|
|
203
207
|
}
|
|
204
208
|
if (config.filePostExport) {
|
|
209
|
+
(0, defaults_1.applyFileProcessorsModuleDefaults)(config, config.filePostExport);
|
|
205
210
|
app.post('/post-export', (0, crowdin_client_1.default)(config), (0, pre_post_process_1.default)(config, config.filePostExport, models_1.ProcessFileJobType.POST_EXPORT));
|
|
206
211
|
app.get(`/file/download/${models_1.ProcessFileJobType.POST_EXPORT}`, (0, file_download_1.default)(config, config.filePostExport, models_1.ProcessFileJobType.POST_EXPORT));
|
|
207
212
|
}
|
package/out/models/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import Crowdin, { LanguagesModel, SourceFilesModel, SourceStringsModel, TranslationStatusModel } from '@crowdin/crowdin-api-client';
|
|
2
3
|
import { JwtPayload, VerifyOptions } from '@crowdin/crowdin-apps-functions';
|
|
3
4
|
import { Request } from 'express';
|
|
@@ -156,6 +157,12 @@ export interface ClientConfig extends ImagePath {
|
|
|
156
157
|
* Disable formatting logs
|
|
157
158
|
*/
|
|
158
159
|
disableLogsFormatter?: boolean;
|
|
160
|
+
/**
|
|
161
|
+
* AWS configuration for uploading big files to temporary bucket. Used with customFileFormat and file processors modules.
|
|
162
|
+
*
|
|
163
|
+
* Not necessary to configure if environment variables AWS_REGION and AWS_TMP_BUCKET_NAME are properly set.
|
|
164
|
+
*/
|
|
165
|
+
awsConfig?: AWSConfig;
|
|
159
166
|
}
|
|
160
167
|
export type Config = ClientConfig & {
|
|
161
168
|
baseUrl: string;
|
|
@@ -296,6 +303,16 @@ export interface IntegrationLogic {
|
|
|
296
303
|
* Register Crowdin webhook to get notified when translations are ready
|
|
297
304
|
*/
|
|
298
305
|
webhooks?: Webhooks;
|
|
306
|
+
/**
|
|
307
|
+
* define a notification for your application at the top of the screen
|
|
308
|
+
*/
|
|
309
|
+
notice?: {
|
|
310
|
+
title: string;
|
|
311
|
+
content: string;
|
|
312
|
+
type: NoticeType;
|
|
313
|
+
icon: boolean;
|
|
314
|
+
close: boolean;
|
|
315
|
+
};
|
|
299
316
|
}
|
|
300
317
|
export type FormEntity = FormField | FormDelimiter;
|
|
301
318
|
export interface FormDelimiter {
|
|
@@ -554,7 +571,7 @@ export interface FileProcessLogic {
|
|
|
554
571
|
/**
|
|
555
572
|
* Override to store huge responses (by default they will be stored in fs)
|
|
556
573
|
*/
|
|
557
|
-
storeFile?: (content:
|
|
574
|
+
storeFile?: (content: Buffer) => Promise<string>;
|
|
558
575
|
}
|
|
559
576
|
export interface CustomFileFormatLogic extends FileProcessLogic {
|
|
560
577
|
/**
|
|
@@ -588,28 +605,29 @@ export interface CustomFileFormatLogic extends FileProcessLogic {
|
|
|
588
605
|
/**
|
|
589
606
|
* Used for initial source file upload, source file update, and translation upload
|
|
590
607
|
*/
|
|
591
|
-
parseFile?: (fileContent:
|
|
608
|
+
parseFile?: (fileContent: Buffer, req: Omit<ProcessFileRequest, 'jobType' | 'file'>, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<ParseFileResponse>;
|
|
592
609
|
/**
|
|
593
610
|
* Used for translation download
|
|
594
611
|
*/
|
|
595
|
-
buildFile?: (fileContent:
|
|
612
|
+
buildFile?: (fileContent: Buffer, req: Omit<ProcessFileRequest, 'jobType' | 'file'>, strings: ProcessFileString[], client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<BuildFileResponse>;
|
|
596
613
|
/**
|
|
597
614
|
* Used for strings export
|
|
598
615
|
*/
|
|
599
616
|
exportStrings?: (req: Omit<ProcessFileRequest, 'jobType'>, strings: ProcessFileString[], client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<BuildFileResponse>;
|
|
600
617
|
}
|
|
601
618
|
export type FileImportExportLogic = FilePreImportLogic | FilePostImportLogic | FilePreExportLogic | FilePostExportLogic;
|
|
619
|
+
export type FileImportExportContent = ProcessFileString[] | Buffer | undefined;
|
|
602
620
|
export interface FilePreImportLogic extends FileProcessLogic {
|
|
603
|
-
fileProcess: (req: ProcessFileRequest, content:
|
|
621
|
+
fileProcess: (req: ProcessFileRequest, content: FileImportExportContent, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<BuildFileResponse>;
|
|
604
622
|
}
|
|
605
623
|
export interface FilePostImportLogic extends FileProcessLogic {
|
|
606
|
-
fileProcess: (req: ProcessFileRequest, content:
|
|
624
|
+
fileProcess: (req: ProcessFileRequest, content: FileImportExportContent, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<ParseFileResponse>;
|
|
607
625
|
}
|
|
608
626
|
export interface FilePreExportLogic extends FileProcessLogic {
|
|
609
|
-
fileProcess: (req: ProcessFileRequest, content:
|
|
627
|
+
fileProcess: (req: ProcessFileRequest, content: FileImportExportContent, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<ParseFileResponse>;
|
|
610
628
|
}
|
|
611
629
|
export interface FilePostExportLogic extends FileProcessLogic {
|
|
612
|
-
fileProcess: (req: ProcessFileRequest, content:
|
|
630
|
+
fileProcess: (req: ProcessFileRequest, content: FileImportExportContent, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<BuildFileResponse>;
|
|
613
631
|
}
|
|
614
632
|
export interface SignaturePatterns {
|
|
615
633
|
fileName?: string;
|
|
@@ -622,7 +640,7 @@ export interface ProcessFileRequest {
|
|
|
622
640
|
targetLanguages: LanguagesModel.Language[];
|
|
623
641
|
strings: ProcessFileString[];
|
|
624
642
|
stringsUrl: string;
|
|
625
|
-
getRawContent?: (encoding:
|
|
643
|
+
getRawContent?: (encoding: BufferEncoding) => Promise<string | Buffer>;
|
|
626
644
|
}
|
|
627
645
|
export interface ProcessFileRecord {
|
|
628
646
|
content?: string;
|
|
@@ -640,13 +658,12 @@ export declare enum ProcessFileJobType {
|
|
|
640
658
|
POST_EXPORT = "post-export-file"
|
|
641
659
|
}
|
|
642
660
|
export interface ParseFileResponse {
|
|
643
|
-
previewFile?:
|
|
661
|
+
previewFile?: Buffer;
|
|
644
662
|
strings?: ProcessFileString[];
|
|
645
663
|
error?: string;
|
|
646
664
|
}
|
|
647
665
|
export interface BuildFileResponse {
|
|
648
|
-
contentFile:
|
|
649
|
-
base64EncodedContent?: string;
|
|
666
|
+
contentFile: Buffer;
|
|
650
667
|
error?: string;
|
|
651
668
|
fileName?: string;
|
|
652
669
|
fileType?: string;
|
|
@@ -909,4 +926,15 @@ export interface DefaultPermissions {
|
|
|
909
926
|
user?: UserPermissions;
|
|
910
927
|
project?: ProjectPermissions;
|
|
911
928
|
}
|
|
929
|
+
type NoticeType = 'info' | 'warning' | 'danger' | 'success' | 'error' | 'dataLostWarning';
|
|
930
|
+
export interface AWSConfig {
|
|
931
|
+
/**
|
|
932
|
+
* AWS bucket name for temporary files
|
|
933
|
+
*/
|
|
934
|
+
tmpBucketName?: string;
|
|
935
|
+
/**
|
|
936
|
+
* AWS region
|
|
937
|
+
*/
|
|
938
|
+
region?: string;
|
|
939
|
+
}
|
|
912
940
|
export {};
|