@crowdin/app-project-module 0.28.0-9 → 0.28.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.
@@ -12,12 +12,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- const axios_1 = __importDefault(require("axios"));
16
15
  const fs_1 = __importDefault(require("fs"));
17
16
  const path_1 = __importDefault(require("path"));
18
17
  const models_1 = require("../../models");
19
18
  const util_1 = require("../../util");
20
- const MAX_BODY_SIZE = 4.9 * 1024 * 1024; //4.9mb
19
+ const files_1 = require("../../util/files");
21
20
  function handle(baseConfig, baseUrl, folder, config) {
22
21
  if (!fs_1.default.existsSync(path_1.default.join(folder, 'custom-file-format'))) {
23
22
  fs_1.default.mkdirSync(path_1.default.join(folder, 'custom-file-format'), { recursive: true });
@@ -38,7 +37,7 @@ function handle(baseConfig, baseUrl, folder, config) {
38
37
  file = Buffer.from(body.file.content, 'base64').toString();
39
38
  }
40
39
  else if (body.file.contentUrl) {
41
- file = (yield axios_1.default.get(body.file.contentUrl)).data;
40
+ file = yield (0, files_1.getFileContent)(body.file.contentUrl);
42
41
  }
43
42
  let response = {};
44
43
  let error;
@@ -71,10 +70,7 @@ function handleBuildFile(baseUrl, dataFolder, config, req, client, context, proj
71
70
  strings = req.strings;
72
71
  }
73
72
  else {
74
- // the response is presented in the ndjson format
75
- const response = (yield axios_1.default.get(req.stringsUrl)).data;
76
- const jsonRows = response.split(/\n|\n\r/).filter(Boolean);
77
- strings = jsonRows.map((jsonStringRow) => JSON.parse(jsonStringRow));
73
+ strings = yield (0, files_1.getFileContent)(req.stringsUrl, true);
78
74
  }
79
75
  let res;
80
76
  if ((req.file.id || !file) && config.exportStrings) {
@@ -87,7 +83,7 @@ function handleBuildFile(baseUrl, dataFolder, config, req, client, context, proj
87
83
  return { response };
88
84
  }
89
85
  const contentFileEncoded = Buffer.from(res.contentFile).toString('base64');
90
- if (Buffer.byteLength(contentFileEncoded, 'utf8') < MAX_BODY_SIZE) {
86
+ if (Buffer.byteLength(contentFileEncoded, 'utf8') < files_1.MAX_BODY_SIZE) {
91
87
  response.content = contentFileEncoded;
92
88
  }
93
89
  else {
@@ -96,7 +92,7 @@ function handleBuildFile(baseUrl, dataFolder, config, req, client, context, proj
96
92
  url = yield config.storeFile(res.contentFile);
97
93
  }
98
94
  else {
99
- const storedFile = yield storeFile(res.contentFile, dataFolder);
95
+ const storedFile = yield (0, files_1.storeFile)(res.contentFile, path_1.default.join(dataFolder, 'custom-file-format'));
100
96
  url = `${baseUrl}?file=${storedFile}`;
101
97
  }
102
98
  response.contentUrl = url;
@@ -111,7 +107,7 @@ function handleParseFile(baseUrl, dataFolder, config, req, client, context, proj
111
107
  return { response };
112
108
  }
113
109
  const res = yield config.parseFile(file, req, client, context, projectId);
114
- let maxSize = MAX_BODY_SIZE;
110
+ let maxSize = files_1.MAX_BODY_SIZE;
115
111
  if (res.strings && res.previewFile) {
116
112
  maxSize = maxSize / 2;
117
113
  }
@@ -126,7 +122,7 @@ function handleParseFile(baseUrl, dataFolder, config, req, client, context, proj
126
122
  url = yield config.storeFile(res.previewFile);
127
123
  }
128
124
  else {
129
- const storedFile = yield storeFile(res.previewFile, dataFolder);
125
+ const storedFile = yield (0, files_1.storeFile)(res.previewFile, path_1.default.join(dataFolder, 'custom-file-format'));
130
126
  url = `${baseUrl}?file=${storedFile}`;
131
127
  }
132
128
  response.previewUrl = url;
@@ -153,7 +149,7 @@ function handleParseFile(baseUrl, dataFolder, config, req, client, context, proj
153
149
  url = yield config.storeFile(stringsNDJson);
154
150
  }
155
151
  else {
156
- const storedFile = yield storeFile(stringsNDJson, dataFolder);
152
+ const storedFile = yield (0, files_1.storeFile)(stringsNDJson, path_1.default.join(dataFolder, 'custom-file-format'));
157
153
  url = `${baseUrl}?file=${storedFile}`;
158
154
  }
159
155
  response.stringsUrl = url;
@@ -162,14 +158,3 @@ function handleParseFile(baseUrl, dataFolder, config, req, client, context, proj
162
158
  return { response, error: res.error };
163
159
  });
164
160
  }
165
- function storeFile(fileContent, folder) {
166
- const fileName = `file${Date.now()}`;
167
- return new Promise((res, rej) => fs_1.default.writeFile(path_1.default.join(folder, 'custom-file-format', fileName), fileContent, (err) => {
168
- if (err) {
169
- rej(err);
170
- }
171
- else {
172
- res(fileName);
173
- }
174
- }));
175
- }
@@ -0,0 +1,4 @@
1
+ /// <reference types="qs" />
2
+ import { Request, Response } from 'express';
3
+ import { Config, FileProcessLogic } from '../../models';
4
+ export default function handle(config: Config, processingConfig: FileProcessLogic, folderName: string): (req: Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
@@ -15,9 +15,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const fs_1 = __importDefault(require("fs"));
16
16
  const path_1 = __importDefault(require("path"));
17
17
  const util_1 = require("../../util");
18
- function handle(config, folder) {
18
+ function handle(config, processingConfig, folderName) {
19
+ const folder = processingConfig.filesFolder || config.dbFolder;
19
20
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
20
- const filePath = path_1.default.join(folder, 'custom-file-format', req.query.file);
21
+ const filePath = path_1.default.join(folder, folderName, req.query.file);
21
22
  (0, util_1.log)(`Downloading file ${filePath}`, config.logger);
22
23
  res.download(filePath, function (err) {
23
24
  if (err) {
@@ -0,0 +1,4 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ import { Config, FileImportExportLogic } from '../../models';
4
+ export default function handle(baseConfig: Config, config: FileImportExportLogic, folderName: string): (req: 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,99 @@
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
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
17
+ const models_1 = require("../../models");
18
+ const util_1 = require("../../util");
19
+ const files_1 = require("../../util/files");
20
+ function handle(baseConfig, config, folderName) {
21
+ const folderPath = config.filesFolder || baseConfig.dbFolder;
22
+ if (!fs_1.default.existsSync(path_1.default.join(folderPath, folderName))) {
23
+ fs_1.default.mkdirSync(path_1.default.join(folderPath, folderName), { recursive: true });
24
+ }
25
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
26
+ const response = {};
27
+ const baseFilesUrl = `${baseConfig.baseUrl}/file/download/${folderName}`;
28
+ const body = req.body;
29
+ let processingError;
30
+ let fileContent;
31
+ if (body.stringsUrl) {
32
+ fileContent = yield (0, files_1.getFileContent)(body.stringsUrl, true);
33
+ }
34
+ else if (body.strings) {
35
+ fileContent = body.strings;
36
+ }
37
+ else if (body.file.contentUrl) {
38
+ fileContent = yield (0, files_1.getFileContent)(body.file.contentUrl);
39
+ }
40
+ else if (body.file.content) {
41
+ fileContent = Buffer.from(body.file.content, 'base64').toString();
42
+ }
43
+ const fileProcessResult = yield config.fileProcess(body, fileContent, req.crowdinApiClient, req.crowdinContext, req.crowdinContext.jwtPayload.context.project_id);
44
+ switch (body.jobType) {
45
+ case models_1.ProcessFileJobType.PRE_IMPORT:
46
+ case models_1.ProcessFileJobType.POST_EXPORT:
47
+ const { contentFile, base64EncodedContent, fileName, fileType, error: contentFileError, } = fileProcessResult;
48
+ if (contentFile) {
49
+ const contentFileEncoded = base64EncodedContent || Buffer.from(contentFile).toString('base64');
50
+ if (Buffer.byteLength(contentFileEncoded, 'utf8') < files_1.MAX_BODY_SIZE) {
51
+ response.content = contentFileEncoded;
52
+ }
53
+ else {
54
+ let url;
55
+ if (config.storeFile) {
56
+ url = yield config.storeFile(contentFile);
57
+ }
58
+ else {
59
+ const storedFile = yield (0, files_1.storeFile)(contentFile, path_1.default.join(folderPath, folderName));
60
+ url = `${baseFilesUrl}?file=${storedFile}`;
61
+ }
62
+ response.contentUrl = url;
63
+ }
64
+ }
65
+ if (fileName) {
66
+ response.fileName = fileName;
67
+ }
68
+ if (fileType) {
69
+ response.fileType = fileType;
70
+ }
71
+ processingError = contentFileError;
72
+ break;
73
+ case models_1.ProcessFileJobType.POST_IMPORT:
74
+ case models_1.ProcessFileJobType.PRE_EXPORT:
75
+ const { strings, error: stringsFileError } = fileProcessResult;
76
+ if (strings) {
77
+ const stringsNDJson = strings.map((s) => JSON.stringify(s)).join('\n\r');
78
+ if (Buffer.byteLength(stringsNDJson, 'utf8') < files_1.MAX_BODY_SIZE) {
79
+ response.strings = strings;
80
+ }
81
+ else {
82
+ let url;
83
+ if (config.storeFile) {
84
+ url = yield config.storeFile(stringsNDJson);
85
+ }
86
+ else {
87
+ const storedFile = yield (0, files_1.storeFile)(stringsNDJson, path_1.default.join(folderPath, folderName));
88
+ url = `${baseFilesUrl}?file=${storedFile}`;
89
+ }
90
+ response.stringsUrl = url;
91
+ }
92
+ }
93
+ processingError = stringsFileError;
94
+ break;
95
+ }
96
+ res.send({ data: response, error: processingError ? { message: processingError } : undefined });
97
+ }), baseConfig.onError);
98
+ }
99
+ exports.default = handle;
@@ -31,6 +31,42 @@ function handle(config) {
31
31
  },
32
32
  ];
33
33
  }
34
+ if (config.filePreImport) {
35
+ modules['file-pre-import'] = [
36
+ {
37
+ key: config.identifier + '-pri',
38
+ signaturePatterns: config.filePreImport.signaturePatterns,
39
+ url: '/pre-import',
40
+ },
41
+ ];
42
+ }
43
+ if (config.filePostImport) {
44
+ modules['file-post-import'] = [
45
+ {
46
+ key: config.identifier + '-poi',
47
+ signaturePatterns: config.filePostImport.signaturePatterns,
48
+ url: '/post-import',
49
+ },
50
+ ];
51
+ }
52
+ if (config.filePreExport) {
53
+ modules['file-pre-export'] = [
54
+ {
55
+ key: config.identifier + '-pre',
56
+ signaturePatterns: config.filePreExport.signaturePatterns,
57
+ url: '/pre-export',
58
+ },
59
+ ];
60
+ }
61
+ if (config.filePostExport) {
62
+ modules['file-post-export'] = [
63
+ {
64
+ key: config.identifier + '-poe',
65
+ signaturePatterns: config.filePostExport.signaturePatterns,
66
+ url: '/post-export',
67
+ },
68
+ ];
69
+ }
34
70
  if (config.customMT) {
35
71
  modules['custom-mt'] = [
36
72
  {
@@ -116,6 +152,33 @@ function handle(config) {
116
152
  },
117
153
  ];
118
154
  }
155
+ if (config.modal) {
156
+ modules['modal'] = [
157
+ {
158
+ key: config.identifier + '-modal',
159
+ name: config.name,
160
+ url: config.modal.url,
161
+ environments: config.modal.environments,
162
+ },
163
+ ];
164
+ }
165
+ if (config.contextMenu) {
166
+ modules['context-menu'] = [
167
+ {
168
+ key: config.identifier + '-context-menu',
169
+ name: config.name,
170
+ description: config.description,
171
+ options: Object.assign(Object.assign({ location: config.contextMenu.location, type: config.contextMenu.type }, (config.contextMenu.module
172
+ ? {
173
+ module: {
174
+ [config.contextMenu.module]: modules[config.contextMenu.module][0].key,
175
+ },
176
+ }
177
+ : {})), { url: config.contextMenu.url }),
178
+ environments: config.contextMenu.environments,
179
+ },
180
+ ];
181
+ }
119
182
  const events = {
120
183
  installed: '/installed',
121
184
  uninstall: '/uninstall',
package/out/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Express } from 'express';
2
- import { Config, CrowdinAppUtilities } from './models';
2
+ import { ClientConfig, Config, CrowdinAppUtilities } from './models';
3
3
  export { Scope } from './models';
4
- export declare function addCrowdinEndpoints(app: Express, plainConfig: Config): CrowdinAppUtilities;
5
- export declare function createApp(config: Config): void;
4
+ export declare function addCrowdinEndpoints(app: Express, clientConfig: Config | ClientConfig): CrowdinAppUtilities;
5
+ export declare function createApp(clientConfig: ClientConfig): void;
package/out/index.js CHANGED
@@ -45,22 +45,23 @@ const crowdin_files_1 = __importDefault(require("./handlers/crowdin-files"));
45
45
  const crowdin_project_1 = __importDefault(require("./handlers/crowdin-project"));
46
46
  const crowdin_update_1 = __importDefault(require("./handlers/crowdin-update"));
47
47
  const crowdin_webhook_1 = __importDefault(require("./handlers/crowdin-webhook"));
48
- const integration_webhook_1 = __importDefault(require("./handlers/integration-webhook"));
49
- const download_1 = __importDefault(require("./handlers/custom-file-format/download"));
50
- const process_1 = __importDefault(require("./handlers/custom-file-format/process"));
51
48
  const translate_1 = __importDefault(require("./handlers/custom-mt/translate"));
49
+ const custom_file_format_1 = __importDefault(require("./handlers/file-processing/custom-file-format"));
50
+ const file_download_1 = __importDefault(require("./handlers/file-processing/file-download"));
51
+ const pre_post_process_1 = __importDefault(require("./handlers/file-processing/pre-post-process"));
52
+ const form_data_display_1 = __importDefault(require("./handlers/form-data-display"));
53
+ const form_data_save_1 = __importDefault(require("./handlers/form-data-save"));
52
54
  const install_1 = __importDefault(require("./handlers/install"));
53
55
  const integration_data_1 = __importDefault(require("./handlers/integration-data"));
54
56
  const integration_login_1 = __importDefault(require("./handlers/integration-login"));
55
57
  const integration_logout_1 = __importDefault(require("./handlers/integration-logout"));
56
58
  const integration_update_1 = __importDefault(require("./handlers/integration-update"));
59
+ const integration_webhook_1 = __importDefault(require("./handlers/integration-webhook"));
57
60
  const main_1 = __importDefault(require("./handlers/main"));
58
61
  const manifest_1 = __importDefault(require("./handlers/manifest"));
59
62
  const oauth_login_1 = __importDefault(require("./handlers/oauth-login"));
60
63
  const oauth_url_1 = __importDefault(require("./handlers/oauth-url"));
61
64
  const settings_save_1 = __importDefault(require("./handlers/settings-save"));
62
- const form_data_display_1 = __importDefault(require("./handlers/form-data-display"));
63
- const form_data_save_1 = __importDefault(require("./handlers/form-data-save"));
64
65
  const subscription_info_1 = __importDefault(require("./handlers/subscription-info"));
65
66
  const subscription_paid_1 = __importDefault(require("./handlers/subscription-paid"));
66
67
  const sync_settings_1 = __importDefault(require("./handlers/sync-settings"));
@@ -69,22 +70,23 @@ const uninstall_1 = __importDefault(require("./handlers/uninstall"));
69
70
  const crowdin_client_1 = __importStar(require("./middlewares/crowdin-client"));
70
71
  const integration_credentials_1 = __importDefault(require("./middlewares/integration-credentials"));
71
72
  const json_response_1 = __importDefault(require("./middlewares/json-response"));
72
- const ui_module_1 = __importDefault(require("./middlewares/ui-module"));
73
73
  const render_ui_module_1 = __importDefault(require("./middlewares/render-ui-module"));
74
+ const ui_module_1 = __importDefault(require("./middlewares/ui-module"));
75
+ const models_1 = require("./models");
74
76
  const storage = __importStar(require("./storage"));
75
77
  const util_1 = require("./util");
76
78
  const connection_1 = require("./util/connection");
77
79
  const cron_1 = require("./util/cron");
78
80
  const defaults_1 = require("./util/defaults");
79
81
  const webhooks_1 = require("./util/webhooks");
80
- var models_1 = require("./models");
81
- Object.defineProperty(exports, "Scope", { enumerable: true, get: function () { return models_1.Scope; } });
82
- function addCrowdinEndpoints(app, plainConfig) {
82
+ var models_2 = require("./models");
83
+ Object.defineProperty(exports, "Scope", { enumerable: true, get: function () { return models_2.Scope; } });
84
+ function addCrowdinEndpoints(app, clientConfig) {
83
85
  var _a, _b, _c;
84
- if (!plainConfig.disableGlobalErrorHandling) {
85
- handleUncaughtErrors(plainConfig);
86
+ const config = (0, defaults_1.convertClientConfig)(clientConfig);
87
+ if (!config.disableGlobalErrorHandling) {
88
+ handleUncaughtErrors(config);
86
89
  }
87
- const config = Object.assign(Object.assign({}, plainConfig), { baseUrl: plainConfig.baseUrl.endsWith('/') ? plainConfig.baseUrl.slice(0, -1) : plainConfig.baseUrl });
88
90
  storage.initialize(config);
89
91
  app.use(express_1.default.json({ limit: '50mb' }));
90
92
  app.use('/assets', express_1.default.static((0, path_1.join)(__dirname, 'static')));
@@ -117,7 +119,7 @@ function addCrowdinEndpoints(app, plainConfig) {
117
119
  },
118
120
  }));
119
121
  app.set('view engine', 'handlebars');
120
- app.get('/logo.png', (req, res) => res.sendFile(config.imagePath || (0, path_1.join)(__dirname, 'logo.png')));
122
+ app.get('/logo.png', (req, res) => res.sendFile(config.imagePath));
121
123
  app.post('/installed', (0, install_1.default)(config));
122
124
  app.post('/uninstall', (0, uninstall_1.default)(config));
123
125
  app.get('/manifest.json', json_response_1.default, (0, manifest_1.default)(config));
@@ -126,8 +128,8 @@ function addCrowdinEndpoints(app, plainConfig) {
126
128
  }
127
129
  const integrationLogic = config.projectIntegration;
128
130
  if (integrationLogic) {
129
- (0, defaults_1.applyDefaults)(config, integrationLogic);
130
- app.get('/logo/integration/logo.png', (req, res) => res.sendFile(integrationLogic.imagePath || config.imagePath || (0, path_1.join)(__dirname, 'logo.png')));
131
+ (0, defaults_1.applyIntegrationModuleDefaults)(config, integrationLogic);
132
+ app.get('/logo/integration/logo.png', (req, res) => res.sendFile(integrationLogic.imagePath || config.imagePath));
131
133
  app.get('/', (0, crowdin_client_1.default)(config, true, false), (0, integration_credentials_1.default)(config, integrationLogic, true), (0, main_1.default)(config, integrationLogic));
132
134
  app.get('/api/subscription-info', json_response_1.default, (0, crowdin_client_1.default)(config), (0, subscription_info_1.default)(config));
133
135
  app.post('/api/settings', (0, crowdin_client_1.default)(config), (0, integration_credentials_1.default)(config, integrationLogic), (0, settings_save_1.default)(config, integrationLogic));
@@ -172,19 +174,35 @@ function addCrowdinEndpoints(app, plainConfig) {
172
174
  }
173
175
  }
174
176
  if (config.customFileFormat) {
175
- app.post('/process', (0, crowdin_client_1.default)(config), (0, process_1.default)(config, config.baseUrl, config.customFileFormat.filesFolder || config.dbFolder || '/', config.customFileFormat));
176
- app.get('/file/download', (0, download_1.default)(config, config.customFileFormat.filesFolder || config.dbFolder || '/'));
177
+ app.post('/process', (0, crowdin_client_1.default)(config), (0, custom_file_format_1.default)(config, config.baseUrl, config.customFileFormat.filesFolder || config.dbFolder, config.customFileFormat));
178
+ app.get('/file/download', (0, file_download_1.default)(config, config.customFileFormat, 'custom-file-format'));
179
+ }
180
+ if (config.filePreImport) {
181
+ app.post('/pre-import', (0, crowdin_client_1.default)(config), (0, pre_post_process_1.default)(config, config.filePreImport, models_1.ProcessFileJobType.PRE_IMPORT));
182
+ app.get(`/file/download/${models_1.ProcessFileJobType.PRE_IMPORT}`, (0, file_download_1.default)(config, config.filePreImport, models_1.ProcessFileJobType.PRE_IMPORT));
183
+ }
184
+ if (config.filePostImport) {
185
+ app.post('/post-import', (0, crowdin_client_1.default)(config), (0, pre_post_process_1.default)(config, config.filePostImport, models_1.ProcessFileJobType.POST_IMPORT));
186
+ app.get(`/file/download/${models_1.ProcessFileJobType.POST_IMPORT}`, (0, file_download_1.default)(config, config.filePostImport, models_1.ProcessFileJobType.POST_IMPORT));
187
+ }
188
+ if (config.filePreExport) {
189
+ app.post('/pre-export', (0, crowdin_client_1.default)(config), (0, pre_post_process_1.default)(config, config.filePreExport, models_1.ProcessFileJobType.PRE_EXPORT));
190
+ app.get(`/file/download/${models_1.ProcessFileJobType.PRE_EXPORT}`, (0, file_download_1.default)(config, config.filePreExport, models_1.ProcessFileJobType.PRE_EXPORT));
191
+ }
192
+ if (config.filePostExport) {
193
+ app.post('/post-export', (0, crowdin_client_1.default)(config), (0, pre_post_process_1.default)(config, config.filePostExport, models_1.ProcessFileJobType.POST_EXPORT));
194
+ app.get(`/file/download/${models_1.ProcessFileJobType.POST_EXPORT}`, (0, file_download_1.default)(config, config.filePostExport, models_1.ProcessFileJobType.POST_EXPORT));
177
195
  }
178
196
  if (config.customMT) {
179
- app.get('/logo/mt/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.customMT) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath || (0, path_1.join)(__dirname, 'logo.png')); });
197
+ app.get('/logo/mt/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.customMT) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath); });
180
198
  app.post('/translate', (0, crowdin_client_1.default)(config), (0, translate_1.default)(config, config.customMT));
181
199
  }
182
200
  if (config.profileResourcesMenu) {
183
- app.get('/logo/resources/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.profileResourcesMenu) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath || (0, path_1.join)(__dirname, 'logo.png')); });
201
+ app.get('/logo/resources/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.profileResourcesMenu) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath); });
184
202
  app.use('/resources', (0, ui_module_1.default)(config, config.profileResourcesMenu.allowUnauthorized), (0, render_ui_module_1.default)(config, config.profileResourcesMenu));
185
203
  }
186
204
  if (config.organizationMenu) {
187
- app.get('/logo/resources/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.organizationMenu) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath || (0, path_1.join)(__dirname, 'logo.png')); });
205
+ app.get('/logo/resources/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.organizationMenu) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath); });
188
206
  app.use('/resources', (0, ui_module_1.default)(config, config.organizationMenu.allowUnauthorized), (0, render_ui_module_1.default)(config, config.organizationMenu));
189
207
  }
190
208
  if (config.editorRightPanel) {
@@ -197,11 +215,11 @@ function addCrowdinEndpoints(app, plainConfig) {
197
215
  app.use('/project-menu-crowdsource', (0, ui_module_1.default)(config, config.projectMenuCrowdsource.allowUnauthorized), (0, render_ui_module_1.default)(config, config.projectMenuCrowdsource));
198
216
  }
199
217
  if (config.projectTools) {
200
- app.get('/logo/tools/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.projectTools) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath || (0, path_1.join)(__dirname, 'logo.png')); });
218
+ app.get('/logo/tools/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.projectTools) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath); });
201
219
  app.use('/tools', (0, ui_module_1.default)(config, config.projectTools.allowUnauthorized), (0, render_ui_module_1.default)(config, config.projectTools));
202
220
  }
203
221
  if (config.projectReports) {
204
- app.get('/logo/reports/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.projectReports) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath || (0, path_1.join)(__dirname, 'logo.png')); });
222
+ app.get('/logo/reports/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.projectReports) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath); });
205
223
  app.use('/reports', (0, ui_module_1.default)(config, config.projectReports.allowUnauthorized), (0, render_ui_module_1.default)(config, config.projectReports));
206
224
  }
207
225
  if (Object.keys(config).some((moduleKey) => {
@@ -233,11 +251,12 @@ function addCrowdinEndpoints(app, plainConfig) {
233
251
  };
234
252
  }
235
253
  exports.addCrowdinEndpoints = addCrowdinEndpoints;
236
- function createApp(config) {
254
+ function createApp(clientConfig) {
237
255
  const app = (0, express_1.default)();
256
+ const config = (0, defaults_1.convertClientConfig)(clientConfig);
238
257
  addCrowdinEndpoints(app, config);
239
258
  /* eslint no-console: "off" */
240
- app.listen(config.port || 3000, () => console.log(`App started on port ${config.port || 3000}`));
259
+ app.listen(config.port, () => console.log(`App started on port ${config.port}`));
241
260
  }
242
261
  exports.createApp = createApp;
243
262
  function handleUncaughtErrors(config) {
@@ -3,7 +3,7 @@ import { JwtPayload, VerifyOptions } from '@crowdin/crowdin-apps-functions';
3
3
  import { Request } from 'express';
4
4
  import { MySQLStorageConfig } from '../storage/mysql';
5
5
  import { PostgreStorageConfig } from '../storage/postgre';
6
- export interface Config extends ImagePath {
6
+ export interface ClientConfig extends ImagePath {
7
7
  /**
8
8
  * Authentication Crowdin App type: "authorization_code", "crowdin_app". Default: "crowdin_app"
9
9
  */
@@ -11,11 +11,11 @@ export interface Config extends ImagePath {
11
11
  /**
12
12
  * client id that we received when registering the app
13
13
  */
14
- clientId: string;
14
+ clientId?: string;
15
15
  /**
16
16
  * client secret that we received when registering the app
17
17
  */
18
- clientSecret: string;
18
+ clientSecret?: string;
19
19
  /**
20
20
  * Secret to encrypt/decrypt credentials (by default @clientSecret will be used)
21
21
  */
@@ -27,7 +27,7 @@ export interface Config extends ImagePath {
27
27
  /**
28
28
  * https url where an app is reachable from the internet (e.g. the one that ngrok generates for us)
29
29
  */
30
- baseUrl: string;
30
+ baseUrl?: string;
31
31
  /**
32
32
  * define custom Crowdin urls (e.g. to work against local Crowdin server)
33
33
  */
@@ -104,6 +104,14 @@ export interface Config extends ImagePath {
104
104
  * reports module
105
105
  */
106
106
  projectReports?: UiModule & ImagePath;
107
+ /**
108
+ * context menu module
109
+ */
110
+ contextMenu?: ContextModule & ModuleContent & Environments;
111
+ /**
112
+ * modal module
113
+ */
114
+ modal?: ModuleContent & Environments;
107
115
  /**
108
116
  * Uninstall hook for cleanup logic
109
117
  */
@@ -127,7 +135,19 @@ export interface Config extends ImagePath {
127
135
  * Configuration of app pricing
128
136
  */
129
137
  pricing?: Pricing;
138
+ filePreImport?: FilePreImportLogic;
139
+ filePostImport?: FilePostImportLogic;
140
+ filePreExport?: FilePreExportLogic;
141
+ filePostExport?: FilePostExportLogic;
130
142
  }
143
+ export type Config = ClientConfig & {
144
+ baseUrl: string;
145
+ clientId: string;
146
+ clientSecret: string;
147
+ port: number;
148
+ dbFolder: string;
149
+ imagePath: string;
150
+ };
131
151
  export declare enum AuthenticationType {
132
152
  CODE = "authorization_code",
133
153
  APP = "crowdin_app"
@@ -484,23 +504,29 @@ export interface CronJob {
484
504
  task: (projectId: number, client: Crowdin, apiCredentials: any, appRootFolder?: SourceFilesModel.Directory, config?: any) => Promise<void>;
485
505
  expression: string;
486
506
  }
487
- export interface CustomFileFormatLogic {
488
- /**
489
- * The type parameter value for a custom file format. Used for a custom format file upload via API.
490
- */
491
- type: string;
507
+ export interface FileProcessLogic {
492
508
  /**
493
509
  * Folder where larger file will be temporary stored (default "{@link dbFolder}/custom-file-format")
494
510
  */
495
511
  filesFolder?: string;
496
- /**
497
- * This parameter is used to combine the content of multiple languages into one request when uploading and downloading translations in your Crowdin project.
498
- */
499
- multilingual?: boolean;
500
512
  /**
501
513
  * Contains fileName and/or fileContent regular expressions used to detect file type when uploading a new source file via UI (or via API without specified type parameter). If the file matches regular expressions, it's labeled as a custom format file.
502
514
  */
503
515
  signaturePatterns?: SignaturePatterns;
516
+ /**
517
+ * Override to store huge responses (by default they will be stored in fs)
518
+ */
519
+ storeFile?: (content: string) => Promise<string>;
520
+ }
521
+ export interface CustomFileFormatLogic extends FileProcessLogic {
522
+ /**
523
+ * The type parameter value for a custom file format. Used for a custom format file upload via API.
524
+ */
525
+ type: string;
526
+ /**
527
+ * This parameter is used to combine the content of multiple languages into one request when uploading and downloading translations in your Crowdin project.
528
+ */
529
+ multilingual?: boolean;
504
530
  /**
505
531
  * Flag to automatically upload translations
506
532
  */
@@ -533,10 +559,19 @@ export interface CustomFileFormatLogic {
533
559
  * Used for strings export
534
560
  */
535
561
  exportStrings?: (req: Omit<ProcessFileRequest, 'jobType'>, strings: ProcessFileString[], client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<BuildFileResponse>;
536
- /**
537
- * Override to store huge responses (by default they will be stored in fs)
538
- */
539
- storeFile?: (content: string) => Promise<string>;
562
+ }
563
+ export type FileImportExportLogic = FilePreImportLogic | FilePostImportLogic | FilePreExportLogic | FilePostExportLogic;
564
+ export interface FilePreImportLogic extends FileProcessLogic {
565
+ fileProcess: (req: ProcessFileRequest, content: string, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<BuildFileResponse>;
566
+ }
567
+ export interface FilePostImportLogic extends FileProcessLogic {
568
+ fileProcess: (req: ProcessFileRequest, content: string, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<ParseFileResponse>;
569
+ }
570
+ export interface FilePreExportLogic extends FileProcessLogic {
571
+ fileProcess: (req: ProcessFileRequest, content: string, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<ParseFileResponse>;
572
+ }
573
+ export interface FilePostExportLogic extends FileProcessLogic {
574
+ fileProcess: (req: ProcessFileRequest, content: string, client: Crowdin, context: CrowdinContextInfo, projectId: number) => Promise<BuildFileResponse>;
540
575
  }
541
576
  export interface SignaturePatterns {
542
577
  fileName?: string;
@@ -559,7 +594,11 @@ export interface ProcessFileRecord {
559
594
  }
560
595
  export declare enum ProcessFileJobType {
561
596
  PARSE_FILE = "parse-file",
562
- BUILD_FILE = "build-file"
597
+ BUILD_FILE = "build-file",
598
+ PRE_IMPORT = "pre-import-file",
599
+ POST_IMPORT = "post-import-file",
600
+ PRE_EXPORT = "pre-export-file",
601
+ POST_EXPORT = "post-export-file"
563
602
  }
564
603
  export interface ParseFileResponse {
565
604
  previewFile?: string;
@@ -568,7 +607,10 @@ export interface ParseFileResponse {
568
607
  }
569
608
  export interface BuildFileResponse {
570
609
  contentFile: string;
610
+ base64EncodedContent?: string;
571
611
  error?: string;
612
+ fileName?: string;
613
+ fileType?: string;
572
614
  }
573
615
  export interface ProcessFileString {
574
616
  previewId?: number;
@@ -582,6 +624,7 @@ export interface ProcessFileString {
582
624
  labels?: string[];
583
625
  text: string | SourceStringsModel.PluralText;
584
626
  translations?: StringTranslations;
627
+ uniqId?: string;
585
628
  }
586
629
  export interface StringTranslations {
587
630
  [language: string]: {
@@ -640,6 +683,30 @@ export declare enum EditorPanelsMode {
640
683
  TRANSLATE = "TRANSLATE",
641
684
  PROOFREAD = "proofread"
642
685
  }
686
+ interface ModuleContent {
687
+ /**
688
+ * relative URL to the content page of the module
689
+ */
690
+ url?: string;
691
+ }
692
+ export interface ContextModule {
693
+ location: ContextOptionsLocations;
694
+ type: ContextOptionsTypes;
695
+ module: string;
696
+ }
697
+ export declare enum ContextOptionsLocations {
698
+ TM = "tm",
699
+ GLOSSARY = "glossary",
700
+ LANGUAGE = "language",
701
+ SCREENSHOT = "screenshot",
702
+ SOURCE_FILE = "source_file",
703
+ TRANSLATED_FILE = "translated_file"
704
+ }
705
+ export declare enum ContextOptionsTypes {
706
+ MODAL = "modal",
707
+ NEW_TAB = "new_tab",
708
+ REDIRECT = "redirect"
709
+ }
643
710
  export interface CrowdinAppUtilities {
644
711
  saveMetadata: (id: string, metadata: any) => Promise<void>;
645
712
  getMetadata: (id: string) => Promise<any | undefined>;