@crowdin/app-project-module 0.20.11 → 0.22.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/README.md CHANGED
@@ -340,6 +340,42 @@ configuration.projectIntegration.oauthLogin = {
340
340
  }
341
341
  ```
342
342
 
343
+ In addition you can specify extra fields on login screen that you can use to dynamically build authorization url.
344
+
345
+ ```javascript
346
+ configuration.projectIntegration.oauthLogin.loginFields = [
347
+ {
348
+ key: 'region',
349
+ label: 'Region',
350
+ type: 'select',
351
+ defaultValue: 'NA',
352
+ options: [
353
+ {
354
+ value: 'NA',
355
+ label: 'North American'
356
+ },
357
+ {
358
+ value: 'EU',
359
+ label: 'Europe'
360
+ },
361
+ {
362
+ value: 'AZURE_NA',
363
+ label: 'Azure North American'
364
+ }
365
+ ]
366
+ }
367
+ ];
368
+
369
+ configuration.projectIntegration.oauthLogin.getAuthorizationUrl = (redirectUrl, loginForm) => {
370
+ const region = loginForm.region;
371
+ if (region === 'EU') {
372
+ return `https://eu-region.com?client_id=<client-id>&redirect_uri=${redirectUrl}`;
373
+ } else {
374
+ return `https://default-region.com?client_id=<client-id>&redirect_uri=${redirectUrl}`;
375
+ }
376
+ };
377
+ ```
378
+
343
379
  Please refer to jsdoc for more details.
344
380
 
345
381
  ## Storage
@@ -13,12 +13,29 @@ const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
13
13
  const models_1 = require("../models");
14
14
  const storage_1 = require("../storage");
15
15
  const util_1 = require("../util");
16
+ function fetchToken(config, event) {
17
+ var _a, _b;
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ if (config.authenticationType === models_1.AuthenticationType.APP) {
20
+ const token = yield (0, crowdin_apps_functions_1.fetchAppToken)(config.identifier, event.appSecret, config.clientId, config.clientSecret, event.domain || '', event.userId, (_a = config.crowdinUrls) === null || _a === void 0 ? void 0 : _a.accountUrl);
21
+ return {
22
+ accessToken: (0, util_1.encryptData)(config, token.accessToken),
23
+ expiresIn: token.expiresIn,
24
+ refreshToken: '',
25
+ };
26
+ }
27
+ const token = yield (0, crowdin_apps_functions_1.generateOAuthToken)(config.clientId, config.clientSecret, event.code || '', (_b = config.crowdinUrls) === null || _b === void 0 ? void 0 : _b.accountUrl);
28
+ return {
29
+ accessToken: (0, util_1.encryptData)(config, token.accessToken),
30
+ refreshToken: (0, util_1.encryptData)(config, token.refreshToken),
31
+ expiresIn: token.expiresIn,
32
+ };
33
+ });
34
+ }
16
35
  function handle(config) {
17
36
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
18
- var _a;
19
37
  const event = req.body;
20
- (0, util_1.log)(`Recieved install request ${JSON.stringify(event, null, 2)}`, config.logger);
21
- const token = yield (0, crowdin_apps_functions_1.generateOAuthToken)(config.clientId, config.clientSecret, event.code, (_a = config.crowdinUrls) === null || _a === void 0 ? void 0 : _a.accountUrl);
38
+ const token = yield fetchToken(config, event);
22
39
  const credentials = {
23
40
  id: (event.domain || event.organizationId).toString(),
24
41
  appSecret: event.appSecret,
@@ -26,8 +43,8 @@ function handle(config) {
26
43
  userId: event.userId,
27
44
  organizationId: event.organizationId,
28
45
  baseUrl: event.baseUrl,
29
- accessToken: (0, util_1.encryptData)(config, token.accessToken),
30
- refreshToken: (0, util_1.encryptData)(config, token.refreshToken),
46
+ accessToken: token.accessToken,
47
+ refreshToken: token.refreshToken,
31
48
  expire: (new Date().getTime() / 1000 + token.expiresIn).toString(),
32
49
  type: event.domain ? models_1.AccountType.ENTERPRISE : models_1.AccountType.NORMAL,
33
50
  };
@@ -12,20 +12,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const util_1 = require("../util");
13
13
  const connection_1 = require("../util/connection");
14
14
  const defaults_1 = require("../util/defaults");
15
- function constructOauthUrl(config, integration) {
16
- var _a, _b, _c;
17
- const oauth = integration.oauthLogin;
18
- let url = (oauth === null || oauth === void 0 ? void 0 : oauth.authorizationUrl) || '';
19
- url += `?${((_a = oauth === null || oauth === void 0 ? void 0 : oauth.fieldsMapping) === null || _a === void 0 ? void 0 : _a.clientId) || 'client_id'}=${oauth === null || oauth === void 0 ? void 0 : oauth.clientId}`;
20
- url += `&${((_b = oauth === null || oauth === void 0 ? void 0 : oauth.fieldsMapping) === null || _b === void 0 ? void 0 : _b.redirectUri) || 'redirect_uri'}=${config.baseUrl}${(0, defaults_1.getOauthRoute)(integration)}`;
21
- if (oauth === null || oauth === void 0 ? void 0 : oauth.scope) {
22
- url += `&${((_c = oauth === null || oauth === void 0 ? void 0 : oauth.fieldsMapping) === null || _c === void 0 ? void 0 : _c.scope) || 'scope'}=${oauth === null || oauth === void 0 ? void 0 : oauth.scope}`;
23
- }
24
- if (oauth === null || oauth === void 0 ? void 0 : oauth.extraAutorizationUrlParameters) {
25
- Object.entries(oauth === null || oauth === void 0 ? void 0 : oauth.extraAutorizationUrlParameters).forEach(([key, value]) => (url += `&${key}=${value}`));
26
- }
27
- return url;
28
- }
29
15
  function handle(config, integration) {
30
16
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
31
17
  var _a;
@@ -42,8 +28,11 @@ function handle(config, integration) {
42
28
  view = 'login';
43
29
  options.loginFields = (_a = integration.loginForm) === null || _a === void 0 ? void 0 : _a.fields;
44
30
  if (integration.oauthLogin) {
45
- options.oauthUrl = constructOauthUrl(config, integration);
46
- (0, util_1.log)(`Adding oauth url ${options.oauthUrl}`, config.logger);
31
+ options.loginFields = integration.oauthLogin.loginFields || [];
32
+ options.oauthUrl = integration.oauthLogin.authorizationUrl
33
+ ? (0, defaults_1.constructOauthUrl)(config, integration)
34
+ : undefined;
35
+ options.oauthLogin = true;
47
36
  }
48
37
  }
49
38
  else if (integration.getConfiguration) {
@@ -113,7 +113,7 @@ function handle(config) {
113
113
  logo: '/logo.png',
114
114
  baseUrl: config.baseUrl,
115
115
  authentication: {
116
- type: 'authorization_code',
116
+ type: config.authenticationType || models_1.AuthenticationType.CODE,
117
117
  clientId: config.clientId,
118
118
  },
119
119
  events,
@@ -44,7 +44,7 @@ function handle(config, integration) {
44
44
  headers: { Accept: 'application/json' },
45
45
  })).data;
46
46
  }
47
- const oauthCredentials = {};
47
+ const oauthCredentials = { originalUrl: req.originalUrl };
48
48
  oauthCredentials.accessToken = credentials[((_h = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _h === void 0 ? void 0 : _h.accessToken) || 'access_token'];
49
49
  if (oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.refresh) {
50
50
  oauthCredentials.refreshToken = credentials[((_j = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _j === void 0 ? void 0 : _j.refreshToken) || 'refresh_token'];
@@ -0,0 +1,4 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ import { Config, IntegrationLogic } from '../models';
4
+ export default function handle(config: Config, integration: IntegrationLogic): (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,30 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const util_1 = require("../util");
13
+ const defaults_1 = require("../util/defaults");
14
+ function handle(config, integration) {
15
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
+ (0, util_1.log)('Recieved OAuth login url request', config.logger);
17
+ const { oauthLogin } = integration;
18
+ if (!oauthLogin) {
19
+ (0, util_1.log)('OAuth login url request is not supported', config.logger);
20
+ res.status(400).end();
21
+ return;
22
+ }
23
+ const { loginForm } = req.body;
24
+ const url = oauthLogin.getAuthorizationUrl
25
+ ? oauthLogin.getAuthorizationUrl(`${config.baseUrl}${(0, defaults_1.getOauthRoute)(integration)}`, loginForm)
26
+ : (0, defaults_1.constructOauthUrl)(config, integration);
27
+ res.send({ url });
28
+ }), config.onError);
29
+ }
30
+ exports.default = handle;
package/out/index.js CHANGED
@@ -51,6 +51,7 @@ const integration_update_1 = __importDefault(require("./handlers/integration-upd
51
51
  const main_1 = __importDefault(require("./handlers/main"));
52
52
  const manifest_1 = __importDefault(require("./handlers/manifest"));
53
53
  const oauth_login_1 = __importDefault(require("./handlers/oauth-login"));
54
+ const oauth_url_1 = __importDefault(require("./handlers/oauth-url"));
54
55
  const settings_save_1 = __importDefault(require("./handlers/settings-save"));
55
56
  const subscription_info_1 = __importDefault(require("./handlers/subscription-info"));
56
57
  const subscription_paid_1 = __importDefault(require("./handlers/subscription-paid"));
@@ -117,6 +118,7 @@ function addCrowdinEndpoints(app, config) {
117
118
  app.post('/api/sync-settings', json_response_1.default, (0, crowdin_client_1.default)(config), (0, integration_credentials_1.default)(config, integrationLogic), (0, sync_settings_save_1.default)(config));
118
119
  if (integrationLogic.oauthLogin) {
119
120
  app.get((0, defaults_1.getOauthRoute)(integrationLogic), (0, oauth_login_1.default)(config, integrationLogic));
121
+ app.post('/api/oauth-url', json_response_1.default, (0, crowdin_client_1.default)(config, false, false), (0, oauth_url_1.default)(config, integrationLogic));
120
122
  }
121
123
  if (integrationLogic.cronJobs) {
122
124
  integrationLogic.cronJobs.forEach(job => {
@@ -4,6 +4,10 @@ import { Request } from 'express';
4
4
  import { MySQLStorageConfig } from '../storage/mysql';
5
5
  import { PostgreStorageConfig } from '../storage/postgre';
6
6
  export interface Config extends ImagePath {
7
+ /**
8
+ * Authentication Crowdin App type: "authorization_code", "crowdin_app". Default: "authorization_code"
9
+ */
10
+ authenticationType?: AuthenticationType;
7
11
  /**
8
12
  * client id that we received when registering the app
9
13
  */
@@ -116,6 +120,10 @@ export interface Config extends ImagePath {
116
120
  */
117
121
  pricing?: Pricing;
118
122
  }
123
+ export declare enum AuthenticationType {
124
+ CODE = "authorization_code",
125
+ APP = "crowdin_app"
126
+ }
119
127
  export interface CrowdinUrls {
120
128
  apiUrl?: string;
121
129
  accountUrl?: string;
@@ -238,10 +246,18 @@ export interface LoginForm {
238
246
  fields: FormField[];
239
247
  }
240
248
  export interface OAuthLogin {
249
+ /**
250
+ * Extra field for login form
251
+ */
252
+ loginFields?: FormField[];
241
253
  /**
242
254
  * Authorization url (e.g. https://github.com/login/oauth/authorize or https://accounts.google.com/o/oauth2/v2/auth)
243
255
  */
244
- authorizationUrl: string;
256
+ authorizationUrl?: string;
257
+ /**
258
+ * Authorization url getter
259
+ */
260
+ getAuthorizationUrl?: (redirectUrl: string, loginForm?: any) => string;
245
261
  /**
246
262
  * Access token url (e.g. https://github.com/login/oauth/access_token)
247
263
  */
@@ -1,6 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EditorPanelsMode = exports.ProcessFileJobType = exports.SubscriptionInfoType = exports.AccountType = exports.Scope = void 0;
3
+ exports.EditorPanelsMode = exports.ProcessFileJobType = exports.SubscriptionInfoType = exports.AccountType = exports.Scope = exports.AuthenticationType = void 0;
4
+ var AuthenticationType;
5
+ (function (AuthenticationType) {
6
+ AuthenticationType["CODE"] = "authorization_code";
7
+ AuthenticationType["APP"] = "crowdin_app";
8
+ })(AuthenticationType = exports.AuthenticationType || (exports.AuthenticationType = {}));
4
9
  var Scope;
5
10
  (function (Scope) {
6
11
  Scope["ALL_SCOPES"] = "all";
@@ -227,13 +227,13 @@ class MySQLStorage {
227
227
  saveMetadata(id, metadata) {
228
228
  return __awaiter(this, void 0, void 0, function* () {
229
229
  yield this.dbPromise;
230
- yield this.executeQuery(connection => connection.execute('INSERT INTO app_metadata(id, data) VALUES (?, ?)', [id, metadata]));
230
+ yield this.executeQuery(connection => connection.execute('INSERT INTO app_metadata(id, data) VALUES (?, ?)', [id, JSON.stringify(metadata)]));
231
231
  });
232
232
  }
233
233
  updateMetadata(id, metadata) {
234
234
  return __awaiter(this, void 0, void 0, function* () {
235
235
  yield this.dbPromise;
236
- yield this.executeQuery(connection => connection.execute('UPDATE app_metadata SET data = ? WHERE id = ?', [id, metadata]));
236
+ yield this.executeQuery(connection => connection.execute('UPDATE app_metadata SET data = ? WHERE id = ?', [id, JSON.stringify(metadata)]));
237
237
  });
238
238
  }
239
239
  getMetadata(id) {
@@ -223,13 +223,13 @@ class PostgreStorage {
223
223
  saveMetadata(id, metadata) {
224
224
  return __awaiter(this, void 0, void 0, function* () {
225
225
  yield this.dbPromise;
226
- yield this.executeQuery(client => client.query('INSERT INTO app_metadata(id, data) VALUES ($1, $2)', [id, metadata]));
226
+ yield this.executeQuery(client => client.query('INSERT INTO app_metadata(id, data) VALUES ($1, $2)', [id, JSON.stringify(metadata)]));
227
227
  });
228
228
  }
229
229
  updateMetadata(id, metadata) {
230
230
  return __awaiter(this, void 0, void 0, function* () {
231
231
  yield this.dbPromise;
232
- yield this.executeQuery(client => client.query('UPDATE app_metadata SET data = $1 WHERE id = $2', [id, metadata]));
232
+ yield this.executeQuery(client => client.query('UPDATE app_metadata SET data = $1 WHERE id = $2', [id, JSON.stringify(metadata)]));
233
233
  });
234
234
  }
235
235
  getMetadata(id) {
@@ -1,23 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
- }) : (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- o[k2] = m[k];
8
- }));
9
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
- Object.defineProperty(o, "default", { enumerable: true, value: v });
11
- }) : function(o, v) {
12
- o["default"] = v;
13
- });
14
- var __importStar = (this && this.__importStar) || function (mod) {
15
- if (mod && mod.__esModule) return mod;
16
- var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
- __setModuleDefault(result, mod);
19
- return result;
20
- };
21
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
22
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
23
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -33,13 +14,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
33
14
  Object.defineProperty(exports, "__esModule", { value: true });
34
15
  exports.checkSubscription = exports.isAppFree = exports.clearCache = exports.prepareIntegrationCredentials = exports.prepareCrowdinClient = void 0;
35
16
  const crowdin_api_client_1 = __importDefault(require("@crowdin/crowdin-api-client"));
36
- const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
17
+ const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
37
18
  const axios_1 = __importDefault(require("axios"));
38
19
  const _1 = require(".");
39
20
  const models_1 = require("../models");
40
21
  const storage_1 = require("../storage");
22
+ function refreshToken(config, credentials) {
23
+ var _a, _b;
24
+ return __awaiter(this, void 0, void 0, function* () {
25
+ if (config.authenticationType === models_1.AuthenticationType.APP) {
26
+ const token = yield (0, crowdin_apps_functions_1.fetchAppToken)(config.identifier, credentials.appSecret, config.clientId, config.clientSecret, credentials.domain || '', credentials.userId, (_a = config.crowdinUrls) === null || _a === void 0 ? void 0 : _a.accountUrl);
27
+ return {
28
+ accessToken: (0, _1.encryptData)(config, token.accessToken),
29
+ expiresIn: token.expiresIn,
30
+ refreshToken: '',
31
+ };
32
+ }
33
+ const token = yield (0, crowdin_apps_functions_1.refreshOAuthToken)(config.clientId, config.clientSecret, (0, _1.decryptData)(config, credentials.refreshToken), (_b = config.crowdinUrls) === null || _b === void 0 ? void 0 : _b.accountUrl);
34
+ return {
35
+ accessToken: (0, _1.encryptData)(config, token.accessToken),
36
+ refreshToken: (0, _1.encryptData)(config, token.refreshToken),
37
+ expiresIn: token.expiresIn,
38
+ };
39
+ });
40
+ }
41
41
  function prepareCrowdinClient(config, credentials) {
42
- var _a, _b, _c;
42
+ var _a, _b;
43
43
  return __awaiter(this, void 0, void 0, function* () {
44
44
  const isExpired = +credentials.expire < +new Date().getTime() / 1000;
45
45
  const organization = credentials.type === models_1.AccountType.ENTERPRISE ? credentials.id : undefined;
@@ -51,7 +51,7 @@ function prepareCrowdinClient(config, credentials) {
51
51
  };
52
52
  }
53
53
  (0, _1.log)('Crowdin credentials have expired. Requesting a new credentials', config.logger);
54
- const newCredentials = yield crowdinAppFunctions.refreshOAuthToken(config.clientId, config.clientSecret, (0, _1.decryptData)(config, credentials.refreshToken), (_b = config.crowdinUrls) === null || _b === void 0 ? void 0 : _b.accountUrl);
54
+ const newCredentials = yield refreshToken(config, credentials);
55
55
  (0, _1.log)('Saving updated crowdin credentials in the database', config.logger);
56
56
  yield (0, storage_1.getStorage)().updateCrowdinCredentials({
57
57
  id: credentials.id,
@@ -60,18 +60,15 @@ function prepareCrowdinClient(config, credentials) {
60
60
  userId: credentials.userId,
61
61
  organizationId: credentials.organizationId,
62
62
  baseUrl: credentials.baseUrl,
63
- refreshToken: (0, _1.encryptData)(config, newCredentials.refreshToken),
64
- accessToken: (0, _1.encryptData)(config, newCredentials.accessToken),
63
+ refreshToken: newCredentials.refreshToken,
64
+ accessToken: newCredentials.accessToken,
65
65
  expire: (new Date().getTime() / 1000 + newCredentials.expiresIn).toString(),
66
66
  type: credentials.type,
67
67
  });
68
+ const token = (0, _1.decryptData)(config, credentials.accessToken);
68
69
  return {
69
- client: new crowdin_api_client_1.default({
70
- token: newCredentials.accessToken,
71
- organization,
72
- baseUrl: (_c = config.crowdinUrls) === null || _c === void 0 ? void 0 : _c.apiUrl,
73
- }),
74
- token: newCredentials.accessToken,
70
+ client: new crowdin_api_client_1.default({ token, organization, baseUrl: (_b = config.crowdinUrls) === null || _b === void 0 ? void 0 : _b.apiUrl }),
71
+ token: token,
75
72
  };
76
73
  });
77
74
  }
@@ -170,7 +167,7 @@ function checkSubscription(config, token, organization, accountType) {
170
167
  }
171
168
  }
172
169
  try {
173
- const subscription = yield crowdinAppFunctions.getSubscription({
170
+ const subscription = yield (0, crowdin_apps_functions_1.getSubscription)({
174
171
  appIdentifier,
175
172
  organization: accountType === models_1.AccountType.ENTERPRISE ? organization : undefined,
176
173
  token,
@@ -183,7 +180,7 @@ function checkSubscription(config, token, organization, accountType) {
183
180
  return { expired, daysLeft, type: models_1.SubscriptionInfoType.SUBSCRIPTION };
184
181
  }
185
182
  catch (e) {
186
- if (e instanceof crowdinAppFunctions.PaymentRequiredError) {
183
+ if (e instanceof crowdin_apps_functions_1.PaymentRequiredError) {
187
184
  const { initializedAt, subscribeLink } = e;
188
185
  (0, _1.log)(`Recieved 402 payment error. initializedAt ${initializedAt}`, config.logger);
189
186
  //default 2 weeks
@@ -3,3 +3,4 @@ import { Config, IntegrationLogic } from '../models';
3
3
  export declare function getRootFolder(config: Config, integration: IntegrationLogic, client: Crowdin, projectId: number): Promise<SourceFilesModel.Directory | undefined>;
4
4
  export declare function getOauthRoute(integration: IntegrationLogic): string;
5
5
  export declare function applyDefaults(config: Config, integration: IntegrationLogic): void;
6
+ export declare function constructOauthUrl(config: Config, integration: IntegrationLogic): string;
@@ -28,7 +28,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
28
28
  });
29
29
  };
30
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
- exports.applyDefaults = exports.getOauthRoute = exports.getRootFolder = void 0;
31
+ exports.constructOauthUrl = exports.applyDefaults = exports.getOauthRoute = exports.getRootFolder = void 0;
32
32
  const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
33
33
  function getRootFolder(config, integration, client, projectId) {
34
34
  return __awaiter(this, void 0, void 0, function* () {
@@ -151,3 +151,18 @@ function applyDefaults(config, integration) {
151
151
  }
152
152
  }
153
153
  exports.applyDefaults = applyDefaults;
154
+ function constructOauthUrl(config, integration) {
155
+ var _a, _b, _c;
156
+ const oauth = integration.oauthLogin;
157
+ let url = (oauth === null || oauth === void 0 ? void 0 : oauth.authorizationUrl) || '';
158
+ url += `?${((_a = oauth === null || oauth === void 0 ? void 0 : oauth.fieldsMapping) === null || _a === void 0 ? void 0 : _a.clientId) || 'client_id'}=${oauth === null || oauth === void 0 ? void 0 : oauth.clientId}`;
159
+ url += `&${((_b = oauth === null || oauth === void 0 ? void 0 : oauth.fieldsMapping) === null || _b === void 0 ? void 0 : _b.redirectUri) || 'redirect_uri'}=${config.baseUrl}${getOauthRoute(integration)}`;
160
+ if (oauth === null || oauth === void 0 ? void 0 : oauth.scope) {
161
+ url += `&${((_c = oauth === null || oauth === void 0 ? void 0 : oauth.fieldsMapping) === null || _c === void 0 ? void 0 : _c.scope) || 'scope'}=${oauth === null || oauth === void 0 ? void 0 : oauth.scope}`;
162
+ }
163
+ if (oauth === null || oauth === void 0 ? void 0 : oauth.extraAutorizationUrlParameters) {
164
+ Object.entries(oauth === null || oauth === void 0 ? void 0 : oauth.extraAutorizationUrlParameters).forEach(([key, value]) => (url += `&${key}=${value}`));
165
+ }
166
+ return url;
167
+ }
168
+ exports.constructOauthUrl = constructOauthUrl;
@@ -8,70 +8,67 @@
8
8
  <crowdin-card is-shadowed is-padding-lg class="login">
9
9
  <img alt='{{ name }} logo' src='logo.png' />
10
10
  <crowdin-h4 id="integration-name">{{ name }}</crowdin-h4>
11
- {{#if oauthUrl}}
12
- {{else}}
13
- <div class="inputs">
14
- {{#each loginFields}}
15
- {{#ifeq type "checkbox"}}
16
- <crowdin-checkbox
11
+ <div class="inputs">
12
+ {{#each loginFields}}
13
+ {{#ifeq type "checkbox"}}
14
+ <crowdin-checkbox
15
+ id="{{key}}"
16
+ label="{{label}}"
17
+ value="false"
18
+ use-switch
19
+ {{#if helpText}}
20
+ help-text="{{helpText}}"
21
+ {{/if}}
22
+ {{#if helpTextHtml}}
23
+ help-text-html="{{helpTextHtml}}"
24
+ {{/if}}
25
+ {{#ifeq defaultValue true}}
26
+ checked="{{defaultValue}}"
27
+ {{/ifeq}}
28
+ >
29
+ </crowdin-checkbox>
30
+ {{else}}
31
+ {{#ifeq type "select"}}
32
+ <crowdin-select
33
+ {{#if isMulti}}
34
+ is-multi
35
+ close-on-select="false"
36
+ {{/if}}
17
37
  id="{{key}}"
18
38
  label="{{label}}"
19
- value="false"
20
- use-switch
21
39
  {{#if helpText}}
22
40
  help-text="{{helpText}}"
23
41
  {{/if}}
24
42
  {{#if helpTextHtml}}
25
43
  help-text-html="{{helpTextHtml}}"
26
44
  {{/if}}
27
- {{#ifeq defaultValue true}}
28
- checked="{{defaultValue}}"
29
- {{/ifeq}}
30
45
  >
31
- </crowdin-checkbox>
46
+ {{#each options}}
47
+ <option {{#ifeq ../defaultValue value}} selected {{/ifeq}} value="{{value}}">{{label}}</option>
48
+ {{/each}}
49
+ </crowdin-select>
32
50
  {{else}}
33
- {{#ifeq type "select"}}
34
- <crowdin-select
35
- {{#if isMulti}}
36
- is-multi
37
- close-on-select="false"
38
- {{/if}}
39
- id="{{key}}"
40
- label="{{label}}"
41
- {{#if helpText}}
42
- help-text="{{helpText}}"
43
- {{/if}}
44
- {{#if helpTextHtml}}
45
- help-text-html="{{helpTextHtml}}"
46
- {{/if}}
47
- >
48
- {{#each options}}
49
- <option {{#ifeq ../defaultValue value}} selected {{/ifeq}} value="{{value}}">{{label}}</option>
50
- {{/each}}
51
- </crowdin-select>
52
- {{else}}
53
- <crowdin-input
54
- id="{{key}}"
55
- label="{{label}}"
56
- {{#if helpText}}
57
- help-text="{{helpText}}"
58
- {{/if}}
59
- {{#if helpTextHtml}}
60
- help-text-html="{{helpTextHtml}}"
61
- {{/if}}
62
- {{#if type}}
63
- type="{{type}}"
64
- {{/if}}
65
- value="{{#if defaultValue}}{{defaultValue}}{{/if}}">
66
- </crowdin-input>
67
- {{/ifeq}}
51
+ <crowdin-input
52
+ id="{{key}}"
53
+ label="{{label}}"
54
+ {{#if helpText}}
55
+ help-text="{{helpText}}"
56
+ {{/if}}
57
+ {{#if helpTextHtml}}
58
+ help-text-html="{{helpTextHtml}}"
59
+ {{/if}}
60
+ {{#if type}}
61
+ type="{{type}}"
62
+ {{/if}}
63
+ value="{{#if defaultValue}}{{defaultValue}}{{/if}}">
64
+ </crowdin-input>
68
65
  {{/ifeq}}
66
+ {{/ifeq}}
69
67
  {{/each}}
70
68
  </div>
71
- {{/if}}
72
69
  <crowdin-button
73
70
  outlined icon-after="arrow_forward"
74
- {{#if oauthUrl}}
71
+ {{#if oauthLogin}}
75
72
  onclick="oauthLogin()"
76
73
  {{else}}
77
74
  onclick="integrationLogin()"
@@ -88,7 +85,30 @@
88
85
  const loginButton = document.querySelector('crowdin-button');
89
86
 
90
87
  function oauthLogin() {
88
+ {{#if oauthUrl}}
91
89
  const url = '{{{ oauthUrl }}}';
90
+ {{else}}
91
+ const url = undefined;
92
+ {{/if}}
93
+ if (url) {
94
+ openOAuthPopup(url);
95
+ return;
96
+ }
97
+ loginButton.setAttribute('disabled', true);
98
+ loginButton.setAttribute('is-loading', true);
99
+ checkOrigin()
100
+ .then(queryParams =>
101
+ fetch(`api/oauth-url${queryParams}`, {
102
+ method: 'POST',
103
+ headers: { 'Content-Type': 'application/json' },
104
+ body: JSON.stringify({ loginForm: getLoginForm() })
105
+ })
106
+ )
107
+ .then(checkResponse)
108
+ .then(res => openOAuthPopup(res.url));
109
+ }
110
+
111
+ function openOAuthPopup(url) {
92
112
  const oauthWindow = window.open(url, '{{ name }}', 'location=0,status=0,width=800,height=400');
93
113
  postPromises['oauth_popup'] = {
94
114
  resolve: (data) => {
@@ -107,15 +127,7 @@
107
127
  }
108
128
 
109
129
  function integrationLogin(oauthCredentials) {
110
- const credentials = oauthCredentials || {
111
- {{#each loginFields}}
112
- {{#ifeq type "checkbox"}}
113
- '{{key}}': !!document.querySelector('#{{key}}').checked,
114
- {{else}}
115
- '{{key}}': document.querySelector('#{{key}}').getAttribute('value'),
116
- {{/ifeq}}
117
- {{/each}}
118
- };
130
+ const credentials = oauthCredentials || getLoginForm();
119
131
  loginButton.setAttribute('disabled', true);
120
132
  loginButton.setAttribute('is-loading', true);
121
133
  checkOrigin()
@@ -134,6 +146,18 @@
134
146
  loginButton.setAttribute('is-loading', false);
135
147
  });
136
148
  }
149
+
150
+ function getLoginForm() {
151
+ return {
152
+ {{#each loginFields}}
153
+ {{#ifeq type "checkbox"}}
154
+ '{{key}}': !!document.querySelector('#{{key}}').checked,
155
+ {{else}}
156
+ '{{key}}': document.querySelector('#{{key}}').getAttribute('value'),
157
+ {{/ifeq}}
158
+ {{/each}}
159
+ };
160
+ }
137
161
  </script>
138
162
 
139
163
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.20.11",
3
+ "version": "0.22.0",
4
4
  "description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
5
5
  "main": "out/index.js",
6
6
  "types": "out/index.d.ts",
@@ -12,7 +12,7 @@
12
12
  "test": "echo \"test not implemented\""
13
13
  },
14
14
  "dependencies": {
15
- "@crowdin/crowdin-apps-functions": "0.1.6",
15
+ "@crowdin/crowdin-apps-functions": "~0.2.1",
16
16
  "@types/pg": "^8.6.5",
17
17
  "crypto-js": "^4.0.0",
18
18
  "express": "4.17.1",