UNPKG

@crowdin/app-project-module

Version:

Module that generates for you all common endpoints for serving standalone Crowdin App

345 lines (344 loc) 18.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.prepareIntegrationCredentials = exports.prepareCrowdinClient = void 0; const crowdin_api_client_1 = __importDefault(require("@crowdin/crowdin-api-client")); const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions"); const axios_1 = __importDefault(require("axios")); const _1 = require("."); const storage_1 = require("../storage"); const types_1 = require("../types"); const axios_2 = require("./axios"); const logger_1 = require("./logger"); const os = __importStar(require("os")); const defaults_1 = require("../modules/integration/util/defaults"); const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions")); const axiosCustom = new axios_2.AxiosProvider().axios; function prepareCrowdinClient({ config, credentials, autoRenew = false, context, }) { var _a, _b; return __awaiter(this, void 0, void 0, function* () { let userAgent; if (config.crowdinApiUserAgent) { userAgent = config.crowdinApiUserAgent; } else { userAgent = `app/${config.name} node/v${process.versions.node} ${os.type()}/${os.platform()}/${os.release()}`; } //2 min as an extra buffer const isExpired = +credentials.expire - 120 < Date.now() / 1000; const organization = credentials.type === types_1.AccountType.ENTERPRISE ? credentials.id : undefined; let token; let crowdinCreds; if (!isExpired) { token = (0, _1.decryptData)(config, credentials.accessToken); crowdinCreds = credentials; } else { const res = yield refreshCrowdinCreds({ config, credentials }); token = res.token; crowdinCreds = res.credentials; } if (!autoRenew) { return { client: new crowdin_api_client_1.default({ token, organization, baseUrl: (_a = config.crowdinUrls) === null || _a === void 0 ? void 0 : _a.apiUrl }, { userAgent }), token, }; } const getHttpConfig = (config) => { if (!config) { return; } const headers = Object.assign(Object.assign({}, config.headers), { Authorization: `Bearer ${token}` }); if (userAgent) { headers['User-Agent'] = userAgent; } return { headers }; }; const renewToken = () => __awaiter(this, void 0, void 0, function* () { const res = yield refreshCrowdinCreds({ config, credentials: crowdinCreds }); token = res.token; crowdinCreds = res.credentials; }); const isExpiredCause = (error) => { var _a, _b; const errorObj = (_b = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.error; return (errorObj === null || errorObj === void 0 ? void 0 : errorObj.code) === 401 && (errorObj === null || errorObj === void 0 ? void 0 : errorObj.message) === 'Unauthorized'; }; //providing custom http client to handle expirations and renew token on the fly //needed for long running jobs with single Crowdin client instance return { client: new crowdin_api_client_1.default({ token, organization, baseUrl: (_b = config.crowdinUrls) === null || _b === void 0 ? void 0 : _b.apiUrl, }, { httpClient: { get(url, httpConfig) { return __awaiter(this, void 0, void 0, function* () { try { const res = yield axiosCustom.get(url, getHttpConfig(httpConfig)); return res.data; } catch (e) { if (isExpiredCause(e)) { yield renewToken(); const res = yield axiosCustom.get(url, getHttpConfig(httpConfig)); return res.data; } (0, logger_1.logError)(e, context); throw e; } }); }, delete(url, httpConfig) { return __awaiter(this, void 0, void 0, function* () { try { const res = yield axiosCustom.delete(url, getHttpConfig(httpConfig)); return res.data; } catch (e) { if (isExpiredCause(e)) { yield renewToken(); const res = yield axiosCustom.delete(url, getHttpConfig(httpConfig)); return res.data; } (0, logger_1.logError)(e, context); throw e; } }); }, head(url, httpConfig) { return __awaiter(this, void 0, void 0, function* () { try { const res = yield axiosCustom.head(url, getHttpConfig(httpConfig)); return res.data; } catch (e) { if (isExpiredCause(e)) { yield renewToken(); const res = yield axiosCustom.head(url, getHttpConfig(httpConfig)); return res.data; } (0, logger_1.logError)(e, context); throw e; } }); }, patch(url, data, httpConfig) { return __awaiter(this, void 0, void 0, function* () { try { const res = yield axiosCustom.patch(url, data, getHttpConfig(httpConfig)); return res.data; } catch (e) { if (isExpiredCause(e)) { yield renewToken(); const res = yield axiosCustom.patch(url, data, getHttpConfig(httpConfig)); return res.data; } (0, logger_1.logError)(e, context); throw e; } }); }, post(url, data, httpConfig) { return __awaiter(this, void 0, void 0, function* () { try { const res = yield axiosCustom.post(url, data, getHttpConfig(httpConfig)); return res.data; } catch (e) { if (isExpiredCause(e)) { yield renewToken(); const res = yield axiosCustom.post(url, data, getHttpConfig(httpConfig)); return res.data; } (0, logger_1.logError)(e, context); throw e; } }); }, put(url, data, httpConfig) { return __awaiter(this, void 0, void 0, function* () { try { const res = yield axiosCustom.put(url, data, getHttpConfig(httpConfig)); return res.data; } catch (e) { if (isExpiredCause(e)) { yield renewToken(); const res = yield axiosCustom.put(url, data, getHttpConfig(httpConfig)); return res.data; } (0, logger_1.logError)(e, context); throw e; } }); }, }, userAgent, }), token, }; }); } exports.prepareCrowdinClient = prepareCrowdinClient; function prepareIntegrationCredentials(config, integration, integrationCredentials) { var _a, _b, _c, _d, _e, _f, _g; return __awaiter(this, void 0, void 0, function* () { const credentials = JSON.parse((0, _1.decryptData)(config, integrationCredentials.credentials)); credentials.ownerId = crowdinAppFunctions.parseCrowdinId(integrationCredentials.id).userId; const oauthLogin = integration.oauthLogin; const integrationLogin = integration.loginForm; if ((oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.refresh) || (integrationLogin === null || integrationLogin === void 0 ? void 0 : integrationLogin.refresh)) { (0, logger_1.log)('Checking if integration credentials need to be refreshed'); const { expireIn } = credentials; //2 min as an extra buffer const isExpired = !expireIn || expireIn - 120 < Date.now() / 1000; const performRefreshTokenRequest = (oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.performRefreshTokenRequest) || (integrationLogin === null || integrationLogin === void 0 ? void 0 : integrationLogin.performRefreshTokenRequest); if (isExpired) { (0, logger_1.log)('Integration credentials have expired. Requesting a new credentials'); let newCredentials; if (performRefreshTokenRequest) { const loginForm = yield (0, storage_1.getStorage)().getMetadata((0, defaults_1.getOAuthLoginFormId)(integrationCredentials.id)); newCredentials = yield performRefreshTokenRequest(credentials, loginForm); } else if (oauthLogin) { const url = oauthLogin.refreshTokenUrl || oauthLogin.accessTokenUrl; const request = {}; request[((_a = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _a === void 0 ? void 0 : _a.clientId) || 'client_id'] = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.clientId; request[((_b = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _b === void 0 ? void 0 : _b.clientSecret) || 'client_secret'] = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.clientSecret; request[((_c = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _c === void 0 ? void 0 : _c.refreshToken) || 'refresh_token'] = credentials.refreshToken; if (oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.extraRefreshTokenParameters) { Object.entries(oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.extraRefreshTokenParameters).forEach(([key, value]) => (request[key] = value)); } newCredentials = (yield axios_1.default.post(url || '', request, { headers: { Accept: 'application/json' }, })).data; } else { return credentials; } credentials.accessToken = newCredentials[((_d = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _d === void 0 ? void 0 : _d.accessToken) || 'access_token']; credentials.expireIn = Number(newCredentials[((_e = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _e === void 0 ? void 0 : _e.expiresIn) || 'expires_in']) + Date.now() / 1000; if (newCredentials[((_f = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _f === void 0 ? void 0 : _f.refreshToken) || 'refresh_token']) { credentials.refreshToken = newCredentials[((_g = oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.fieldsMapping) === null || _g === void 0 ? void 0 : _g.refreshToken) || 'refresh_token']; } (0, logger_1.log)('Saving updated integration credentials in the database'); yield (0, storage_1.getStorage)().updateIntegrationCredentials(integrationCredentials.id, (0, _1.encryptData)(config, JSON.stringify(credentials))); } } return credentials; }); } exports.prepareIntegrationCredentials = prepareIntegrationCredentials; function refreshCrowdinCreds({ config, credentials }) { return __awaiter(this, void 0, void 0, function* () { (0, logger_1.log)('Crowdin credentials have expired. Requesting a new credentials'); const newCredentials = yield refreshToken(config, credentials); (0, logger_1.log)('Saving updated crowdin credentials in the database'); const newCrowdinCredentials = { id: credentials.id, appSecret: credentials.appSecret, domain: credentials.domain, userId: credentials.userId, agentId: credentials.agentId, organizationId: credentials.organizationId, baseUrl: credentials.baseUrl, refreshToken: newCredentials.refreshToken, accessToken: newCredentials.accessToken, expire: (new Date().getTime() / 1000 + newCredentials.expiresIn).toString(), type: credentials.type, }; yield (0, storage_1.getStorage)().updateCrowdinCredentials(newCrowdinCredentials); const token = (0, _1.decryptData)(config, newCredentials.accessToken); return { credentials: newCrowdinCredentials, token, }; }); } function refreshToken(config, credentials) { var _a, _b, _c; return __awaiter(this, void 0, void 0, function* () { if (config.authenticationType === types_1.AuthenticationType.CODE) { const token = yield (0, crowdin_apps_functions_1.refreshOAuthToken)({ clientId: config.clientId, clientSecret: config.clientSecret, refreshToken: (0, _1.decryptData)(config, credentials.refreshToken), url: (_a = config.crowdinUrls) === null || _a === void 0 ? void 0 : _a.accountUrl, }); return { accessToken: (0, _1.encryptData)(config, token.accessToken), refreshToken: (0, _1.encryptData)(config, token.refreshToken), expiresIn: token.expiresIn, }; } if (config.authenticationType === types_1.AuthenticationType.AGENT) { const token = yield (0, crowdin_apps_functions_1.fetchAgentToken)({ appId: config.identifier, appSecret: credentials.appSecret, clientId: config.clientId, clientSecret: config.clientSecret, domain: credentials.domain || '', userId: credentials.userId, agentId: credentials.agentId, url: (_b = config.crowdinUrls) === null || _b === void 0 ? void 0 : _b.accountUrl, }); return { accessToken: (0, _1.encryptData)(config, token.accessToken), expiresIn: token.expiresIn, refreshToken: '', }; } const token = yield (0, crowdin_apps_functions_1.fetchAppToken)({ appId: config.identifier, appSecret: credentials.appSecret, clientId: config.clientId, clientSecret: config.clientSecret, domain: credentials.domain || '', userId: credentials.userId, url: (_c = config.crowdinUrls) === null || _c === void 0 ? void 0 : _c.accountUrl, }); return { accessToken: (0, _1.encryptData)(config, token.accessToken), expiresIn: token.expiresIn, refreshToken: '', }; }); }