@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 +36 -0
- package/out/handlers/install.js +22 -5
- package/out/handlers/main.js +5 -16
- package/out/handlers/manifest.js +1 -1
- package/out/handlers/oauth-login.js +1 -1
- package/out/handlers/oauth-url.d.ts +4 -0
- package/out/handlers/oauth-url.js +30 -0
- package/out/index.js +2 -0
- package/out/models/index.d.ts +17 -1
- package/out/models/index.js +6 -1
- package/out/storage/mysql.js +2 -2
- package/out/storage/postgre.js +2 -2
- package/out/util/connection.js +29 -32
- package/out/util/defaults.d.ts +1 -0
- package/out/util/defaults.js +16 -1
- package/out/views/login.handlebars +82 -58
- package/package.json +2 -2
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
|
package/out/handlers/install.js
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
30
|
-
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
|
};
|
package/out/handlers/main.js
CHANGED
|
@@ -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.
|
|
46
|
-
|
|
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) {
|
package/out/handlers/manifest.js
CHANGED
|
@@ -113,7 +113,7 @@ function handle(config) {
|
|
|
113
113
|
logo: '/logo.png',
|
|
114
114
|
baseUrl: config.baseUrl,
|
|
115
115
|
authentication: {
|
|
116
|
-
type:
|
|
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 => {
|
package/out/models/index.d.ts
CHANGED
|
@@ -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
|
|
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
|
*/
|
package/out/models/index.js
CHANGED
|
@@ -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";
|
package/out/storage/mysql.js
CHANGED
|
@@ -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) {
|
package/out/storage/postgre.js
CHANGED
|
@@ -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) {
|
package/out/util/connection.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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:
|
|
64
|
-
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
|
-
|
|
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
|
|
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
|
|
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
|
package/out/util/defaults.d.ts
CHANGED
|
@@ -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;
|
package/out/util/defaults.js
CHANGED
|
@@ -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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
46
|
+
{{#each options}}
|
|
47
|
+
<option {{#ifeq ../defaultValue value}} selected {{/ifeq}} value="{{value}}">{{label}}</option>
|
|
48
|
+
{{/each}}
|
|
49
|
+
</crowdin-select>
|
|
32
50
|
{{else}}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
{{
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
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.
|
|
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
|
|
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",
|