@crowdin/app-project-module 0.82.0 → 0.83.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 CHANGED
@@ -37,6 +37,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  Object.defineProperty(exports, "__esModule", { value: true });
38
38
  exports.addCrowdinEndpoints = exports.createApp = exports.metadataStore = exports.express = exports.UserPermissions = exports.Scope = exports.ProjectPermissions = exports.maskKey = exports.postRequestCredentialsMasker = exports.getRequestCredentialsMasker = void 0;
39
39
  const logsFormatter = __importStar(require("@crowdin/logs-formatter"));
40
+ const express_rate_limit_1 = require("express-rate-limit");
40
41
  const path_1 = require("path");
41
42
  const crowdin_client_1 = __importStar(require("./middlewares/crowdin-client"));
42
43
  const json_response_1 = __importDefault(require("./middlewares/json-response"));
@@ -46,6 +47,7 @@ const install_1 = __importDefault(require("./modules/install"));
46
47
  const manifest_1 = __importDefault(require("./modules/manifest"));
47
48
  const subscription_paid_1 = __importDefault(require("./modules/subscription-paid"));
48
49
  const uninstall_1 = __importDefault(require("./modules/uninstall"));
50
+ const status_1 = __importDefault(require("./modules/status"));
49
51
  const storage = __importStar(require("./storage"));
50
52
  const types_1 = require("./types");
51
53
  const util_1 = require("./util");
@@ -94,6 +96,15 @@ exports.metadataStore = {
94
96
  return storage.getStorage().getMetadata(id);
95
97
  },
96
98
  saveMetadata: (id, metadata, crowdinId) => __awaiter(void 0, void 0, void 0, function* () {
99
+ if (!crowdinId) {
100
+ console.warn('Warning: The crowdinId parameter in saveMetadata will be required. Please update your code to provide this parameter.');
101
+ }
102
+ else {
103
+ const crowdinCredentials = yield storage.getStorage().getCrowdinCredentials(crowdinId);
104
+ if (!crowdinCredentials) {
105
+ console.error('Invalid crowdinId parameter: You can get your crowdinId from the JWT payload');
106
+ }
107
+ }
97
108
  const existing = yield storage.getStorage().getMetadata(id);
98
109
  if (existing) {
99
110
  yield storage.getStorage().updateMetadata(id, metadata, crowdinId);
@@ -121,6 +132,7 @@ function createApp(clientConfig) {
121
132
  }
122
133
  exports.createApp = createApp;
123
134
  function addCrowdinEndpoints(app, clientConfig) {
135
+ var _a, _b, _c;
124
136
  const config = convertClientConfig(clientConfig);
125
137
  if (!config.disableGlobalErrorHandling) {
126
138
  handleUncaughtErrors();
@@ -146,6 +158,21 @@ function addCrowdinEndpoints(app, clientConfig) {
146
158
  app.set('view engine', 'handlebars');
147
159
  app.get((0, util_1.getLogoUrl)(), (req, res) => res.sendFile(config.imagePath));
148
160
  app.get('/manifest.json', json_response_1.default, (0, manifest_1.default)(config));
161
+ if (((_a = config === null || config === void 0 ? void 0 : config.enableStatusPage) === null || _a === void 0 ? void 0 : _a.database) || ((_b = config === null || config === void 0 ? void 0 : config.enableStatusPage) === null || _b === void 0 ? void 0 : _b.filesystem)) {
162
+ app.set('trust proxy', 1);
163
+ const limiter = (0, express_rate_limit_1.rateLimit)({
164
+ windowMs: 60 * 1000,
165
+ limit: ((_c = config.enableStatusPage) === null || _c === void 0 ? void 0 : _c.rateLimit) || 10,
166
+ standardHeaders: true,
167
+ legacyHeaders: false,
168
+ message: {
169
+ status: 429,
170
+ error: 'Too Many Requests',
171
+ message: 'Rate limit exceeded. Please try again later.',
172
+ },
173
+ });
174
+ app.get('/status', limiter, (0, status_1.default)(config));
175
+ }
149
176
  if (!(0, subscription_1.isAppFree)(config)) {
150
177
  app.post('/subscription-paid', (0, subscription_paid_1.default)());
151
178
  }
@@ -220,28 +247,26 @@ function addCrowdinEndpoints(app, clientConfig) {
220
247
  }
221
248
  exports.addCrowdinEndpoints = addCrowdinEndpoints;
222
249
  function addFormSchema({ app, config }) {
223
- let moduleConfigWithForm = null;
224
250
  for (const moduleKey of Object.keys(config)) {
225
251
  const moduleConfig = config[moduleKey];
226
252
  if ((0, form_schema_1.hasFormSchema)(moduleConfig)) {
227
- moduleConfigWithForm = (0, form_schema_1.getLowCodeUiConfigFromModuleConfig)(moduleConfig);
228
- break;
253
+ const moduleConfigWithForm = (0, form_schema_1.getLowCodeUiConfigFromModuleConfig)(moduleConfig);
254
+ if (moduleConfigWithForm) {
255
+ app.get(`/api/${moduleConfigWithForm.key}/form-data`, json_response_1.default, (0, crowdin_client_1.default)({
256
+ config,
257
+ optional: false,
258
+ checkSubscriptionExpiration: true,
259
+ moduleKey: moduleConfigWithForm.key,
260
+ }), (0, credentials_masker_1.getRequestCredentialsMasker)({ moduleConfig: moduleConfigWithForm }), (0, form_data_display_1.default)(config));
261
+ app.post(`/api/${moduleConfigWithForm.key}/form-data`, (0, crowdin_client_1.default)({
262
+ config,
263
+ optional: false,
264
+ checkSubscriptionExpiration: true,
265
+ moduleKey: moduleConfigWithForm.key,
266
+ }), (0, credentials_masker_1.postRequestCredentialsMasker)(moduleConfigWithForm), (0, form_data_save_1.default)(config));
267
+ }
229
268
  }
230
269
  }
231
- if (moduleConfigWithForm) {
232
- app.get('/api/form-data', json_response_1.default, (0, crowdin_client_1.default)({
233
- config,
234
- optional: false,
235
- checkSubscriptionExpiration: true,
236
- moduleKey: moduleConfigWithForm.key,
237
- }), (0, credentials_masker_1.getRequestCredentialsMasker)({ moduleConfig: moduleConfigWithForm }), (0, form_data_display_1.default)(config));
238
- app.post('/api/form-data', (0, crowdin_client_1.default)({
239
- config,
240
- optional: false,
241
- checkSubscriptionExpiration: true,
242
- moduleKey: moduleConfigWithForm.key,
243
- }), (0, credentials_masker_1.postRequestCredentialsMasker)(moduleConfigWithForm), (0, form_data_save_1.default)(config));
244
- }
245
270
  }
246
271
  function convertClientConfig(clientConfig) {
247
272
  const baseUrl = clientConfig.baseUrl || process.env.URL;
@@ -260,6 +285,7 @@ function convertClientConfig(clientConfig) {
260
285
  if (clientConfig.projectIntegration) {
261
286
  clientConfig.api = Object.assign({ default: true }, clientConfig.api);
262
287
  }
288
+ clientConfig.enableStatusPage = Object.assign({ database: true, filesystem: true }, (clientConfig.enableStatusPage || {}));
263
289
  return Object.assign(Object.assign({}, clientConfig), { baseUrl: baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl, clientId,
264
290
  clientSecret, awsConfig: {
265
291
  tmpBucketName,
@@ -18,8 +18,12 @@ function handle(moduleConfig) {
18
18
  return (0, util_1.runAsyncWrapper)((req, res, next) => __awaiter(this, void 0, void 0, function* () {
19
19
  if (moduleConfig.formSchema) {
20
20
  return res.render('form', {
21
- formGetDataUrl: moduleConfig.formGetDataUrl ? moduleConfig.formGetDataUrl : '/api/form-data',
22
- formPostDataUrl: moduleConfig.formPostDataUrl ? moduleConfig.formPostDataUrl : '/api/form-data',
21
+ formGetDataUrl: moduleConfig.formGetDataUrl
22
+ ? moduleConfig.formGetDataUrl
23
+ : `/api/${moduleConfig.key}/form-data`,
24
+ formPostDataUrl: moduleConfig.formPostDataUrl
25
+ ? moduleConfig.formPostDataUrl
26
+ : `/api/${moduleConfig.key}/form-data`,
23
27
  formSchema: JSON.stringify(moduleConfig.formSchema),
24
28
  formUiSchema: moduleConfig.formUiSchema ? JSON.stringify(moduleConfig.formUiSchema) : '{}',
25
29
  });
@@ -17,6 +17,7 @@ const util_1 = require("../../../util");
17
17
  const defaults_1 = require("../util/defaults");
18
18
  const logger_1 = require("../../../util/logger");
19
19
  const storage_1 = require("../../../storage");
20
+ const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
20
21
  function handle(config, integration) {
21
22
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
22
23
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
@@ -28,6 +29,7 @@ function handle(config, integration) {
28
29
  (0, logger_1.log)(`Received request from OAuth login callback. Code ${code}`);
29
30
  (0, logger_1.log)(`Received request from OAuth login callback. State ${state}`);
30
31
  const clientId = Buffer.from(state, 'base64').toString();
32
+ const { organization } = (0, crowdin_apps_functions_1.parseCrowdinId)(clientId);
31
33
  const redirectUri = `${config.baseUrl}${(0, defaults_1.getOauthRoute)(integration)}`;
32
34
  try {
33
35
  const oauthLogin = integration.oauthLogin;
@@ -61,7 +63,7 @@ function handle(config, integration) {
61
63
  message.data = oauthCredentials;
62
64
  if (((_o = integration.oauthLogin) === null || _o === void 0 ? void 0 : _o.mode) === 'polling') {
63
65
  yield (0, storage_1.getStorage)().deleteMetadata((0, defaults_1.getOAuthPollingId)(clientId));
64
- yield (0, storage_1.getStorage)().saveMetadata((0, defaults_1.getOAuthPollingId)(clientId), oauthCredentials);
66
+ yield (0, storage_1.getStorage)().saveMetadata((0, defaults_1.getOAuthPollingId)(clientId), oauthCredentials, organization);
65
67
  }
66
68
  return res.render('oauth', { message: JSON.stringify(message), oauthMode: (_p = integration.oauthLogin) === null || _p === void 0 ? void 0 : _p.mode });
67
69
  }
@@ -23,7 +23,7 @@ function handle(config, integration) {
23
23
  }
24
24
  const { loginForm } = req.body;
25
25
  yield (0, storage_1.getStorage)().deleteMetadata((0, defaults_1.getOAuthLoginFormId)(req.crowdinContext.clientId));
26
- yield (0, storage_1.getStorage)().saveMetadata((0, defaults_1.getOAuthLoginFormId)(req.crowdinContext.clientId), loginForm);
26
+ yield (0, storage_1.getStorage)().saveMetadata((0, defaults_1.getOAuthLoginFormId)(req.crowdinContext.clientId), loginForm, req.crowdinContext.crowdinId);
27
27
  const url = (0, defaults_1.constructOauthUrl)({ config, integration, clientId: req.crowdinContext.clientId, loginForm });
28
28
  res.send({ url });
29
29
  }));
@@ -130,7 +130,7 @@ exports.runUpdateProviderJob = runUpdateProviderJob;
130
130
  function filesCron({ config, integration, period, }) {
131
131
  return __awaiter(this, void 0, void 0, function* () {
132
132
  (0, logger_1.log)(`Starting files cron job with period [${period}]`);
133
- const syncSettingsList = yield (0, storage_1.getStorage)().getAllSyncSettingsByType('schedule');
133
+ const syncSettingsList = yield (0, storage_1.getStorage)().getSyncSettingsBySchedule('schedule', period);
134
134
  const crowdinSyncSettings = syncSettingsList.filter((syncSettings) => syncSettings.provider === types_1.Provider.CROWDIN);
135
135
  const integrationSyncSettings = syncSettingsList.filter((syncSettings) => syncSettings.provider === types_1.Provider.INTEGRATION);
136
136
  yield Promise.all(crowdinSyncSettings.map((syncSettings) => processSyncSettings({ config, integration, period, syncSettings })));
@@ -359,7 +359,7 @@ function handle(config) {
359
359
  const manifest = Object.assign(Object.assign(Object.assign(Object.assign({ identifier: config.identifier, name: config.name, logo: (0, util_1.getLogoUrl)(), baseUrl: config.baseUrl, authentication: {
360
360
  type: config.authenticationType || types_1.AuthenticationType.APP,
361
361
  clientId: config.clientId,
362
- } }, (config.agent && { agent: config.agent })), { events, scopes: config.scopes ? config.scopes : defaultScopes }), (config.defaultPermissions && { default_permissions: config.defaultPermissions })), { modules });
362
+ }, restrictAiToSameApp: config.restrictAiToSameApp }, (config.agent && { agent: config.agent })), { events, scopes: config.scopes ? config.scopes : defaultScopes }), (config.defaultPermissions && { default_permissions: config.defaultPermissions })), { modules });
363
363
  res.send(manifest);
364
364
  };
365
365
  }
@@ -0,0 +1,4 @@
1
+ /// <reference types="qs" />
2
+ import { Request, Response } from 'express';
3
+ import { Config, UnauthorizedConfig } from '../types';
4
+ export default function handle(config: Config | UnauthorizedConfig): (req: import("../types").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;
@@ -0,0 +1,74 @@
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 util_1 = require("../util");
16
+ const logger_1 = require("../util/logger");
17
+ const storage_1 = require("../storage");
18
+ const promises_1 = __importDefault(require("fs/promises"));
19
+ const path_1 = __importDefault(require("path"));
20
+ const os_1 = __importDefault(require("os"));
21
+ function handle(config) {
22
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
23
+ var _a, _b;
24
+ const response = {
25
+ status: 'ok',
26
+ timestamp: new Date().toISOString(),
27
+ };
28
+ let hasError = false;
29
+ if (((_a = config === null || config === void 0 ? void 0 : config.enableStatusPage) === null || _a === void 0 ? void 0 : _a.database) === true) {
30
+ let status = 'ok';
31
+ let message = 'Database connection successful';
32
+ try {
33
+ yield (0, storage_1.getStorage)().getAllMetadata();
34
+ }
35
+ catch (e) {
36
+ status = 'error';
37
+ message = (e === null || e === void 0 ? void 0 : e.message) || 'Database connection error';
38
+ hasError = true;
39
+ (0, logger_1.logError)(e);
40
+ }
41
+ response.database = {
42
+ status,
43
+ message,
44
+ };
45
+ }
46
+ if (((_b = config === null || config === void 0 ? void 0 : config.enableStatusPage) === null || _b === void 0 ? void 0 : _b.filesystem) === true) {
47
+ let status = 'ok';
48
+ let message = 'Filesystem access successful';
49
+ try {
50
+ const testFile = path_1.default.join(os_1.default.tmpdir(), 'status-check.txt');
51
+ yield promises_1.default.writeFile(testFile, 'test');
52
+ yield promises_1.default.unlink(testFile);
53
+ }
54
+ catch (e) {
55
+ status = 'error';
56
+ message = (e === null || e === void 0 ? void 0 : e.message) || 'Filesystem error';
57
+ hasError = true;
58
+ (0, logger_1.logError)(e);
59
+ }
60
+ response.filesystem = {
61
+ status,
62
+ message,
63
+ };
64
+ }
65
+ if (hasError) {
66
+ response.status = 'error';
67
+ res.status(503).json(response);
68
+ }
69
+ else {
70
+ res.json(response);
71
+ }
72
+ }));
73
+ }
74
+ exports.default = handle;