vtex
Version:
The platform for e-commerce apps
143 lines (142 loc) • 5.14 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const async_retry_1 = tslib_1.__importDefault(require("async-retry"));
const co_body_1 = tslib_1.__importDefault(require("co-body"));
const get_port_1 = tslib_1.__importDefault(require("get-port"));
const koa_1 = tslib_1.__importDefault(require("koa"));
const api_1 = require("../../../../api");
const VTEXID_1 = require("../../../../api/clients/IOClients/external/VTEXID");
const ErrorReport_1 = require("../../../../api/error/ErrorReport");
const SUCCESS_PAGE = `
<!doctype html>
<html>
<head>
<title>Success</title>
<meta charset="utf-8">
</head>
<body>
<p> Você já pode fechar essa janela. </p>
<p> You may now close this window. </p>
<p> Ahora puedes cerrar esta ventana. </p>
</body>
</html>`;
class LoginServer {
constructor(loginConfig) {
this.loginConfig = loginConfig;
this.app = new koa_1.default();
this.registerLoginHandler();
this.tokenPromise = new Promise((resolve, reject) => {
this.resolveTokenPromise = resolve;
this.rejectTokenPromise = reject;
});
}
static async create(loginConfig) {
const loginServer = new LoginServer(loginConfig);
await loginServer.start();
return loginServer;
}
get token() {
return this.tokenPromise;
}
get loginCallbackUrl() {
if (!this.port) {
throw new Error('LoginServer not initialized');
}
return `http://127.0.0.1:${this.port}${LoginServer.LOGIN_CALLBACK_PATH}`;
}
setLoginState(val) {
this.loginState = val;
}
start() {
return async_retry_1.default(async (bail) => {
try {
const port = await get_port_1.default({
port: get_port_1.default.makeRange(3000, 3050),
});
this.server = await this.initServer(port);
this.port = port;
api_1.logger.debug(`LoginServer started on http://localhost:${this.port}`);
}
catch (err) {
api_1.logger.debug(`LoginServer failed to start on port:${this.port}. Reason: ${err.message}.`);
if (err.code !== 'EADDRINUSE') {
return bail(err);
}
}
}, { retries: 2, maxTimeout: 50, minTimeout: 50 });
}
close() {
this.server.unref();
this.server.close();
}
initServer(port) {
return new Promise((resolve, reject) => {
this.app.on('error', reject);
const server = this.app.listen(port, () => {
server.on('connection', socket => {
socket.unref();
});
resolve(server);
});
});
}
registerLoginHandler() {
this.app.use(async (ctx) => {
var _a;
ctx.set('connection', 'close');
if (ctx.path !== LoginServer.LOGIN_CALLBACK_PATH) {
ctx.status = 404;
ctx.body = 'Not found';
return;
}
ctx.socket.ref();
api_1.logger.debug(`Received ${ctx.method} login callback`);
if (!this.loginState) {
return this.handleError(ctx, new Error('Received login callback before setting login state'));
}
let body;
if (ctx.method.toLowerCase() === 'post') {
try {
body = await co_body_1.default(ctx.req);
}
catch (err) {
return this.handleError(ctx, err);
}
}
else {
body = { ott: (_a = ctx.query) === null || _a === void 0 ? void 0 : _a.ott };
}
if (!body.ott) {
return this.handleError(ctx, new Error('Missing ott on VTEX ID callback call'), { body });
}
const vtexId = VTEXID_1.VTEXID.createClient({ account: this.loginConfig.account });
try {
const { token } = await vtexId.validateToolbeltLogin({
account: this.loginConfig.account,
secret: this.loginConfig.secret,
ott: body.ott,
state: this.loginState,
});
this.resolveTokenPromise(token);
ctx.status = 200;
ctx.set('content-type', 'text/html');
ctx.body = SUCCESS_PAGE;
}
catch (err) {
return this.handleError(ctx, err);
}
});
}
handleError(ctx, err, details) {
const errReport = ErrorReport_1.ErrorReport.createAndMaybeRegisterOnTelemetry({ originalError: err, details });
ctx.status = 500;
ctx.body = {
errorId: errReport.metadata.errorId,
message: errReport.message,
};
this.rejectTokenPromise(errReport);
}
}
exports.LoginServer = LoginServer;
LoginServer.LOGIN_CALLBACK_PATH = '/login_callback';