botpress
Version:
The world's first CMS for bots. Easily create, manage and extend chatbots.
299 lines (234 loc) • 8.98 kB
JavaScript
;
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _bluebird = require('bluebird');
var _bluebird2 = _interopRequireDefault(_bluebird);
var _prompt = require('prompt');
var _prompt2 = _interopRequireDefault(_prompt);
var _chalk = require('chalk');
var _chalk2 = _interopRequireDefault(_chalk);
var _validUrl = require('valid-url');
var _validUrl2 = _interopRequireDefault(_validUrl);
var _axios = require('axios');
var _axios2 = _interopRequireDefault(_axios);
var _promptConfirm = require('prompt-confirm');
var _promptConfirm2 = _interopRequireDefault(_promptConfirm);
var _mkdirp = require('mkdirp');
var _mkdirp2 = _interopRequireDefault(_mkdirp);
var _os = require('os');
var _os2 = _interopRequireDefault(_os);
var _get = require('lodash/get');
var _get2 = _interopRequireDefault(_get);
var _util = require('../util');
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new _bluebird2.default(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return _bluebird2.default.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
const AUTH_FILE = '.auth.json';
// TODO: this part replicates `start.js`, refactor later
const getDataDir = () => {
const projectPath = _path2.default.resolve('.');
const botfile = _path2.default.join(projectPath, 'botfile.js');
if (!_fs2.default.existsSync(botfile)) {
_util2.default.print.error(`(fatal) No ${_chalk2.default.bold('botfile.js')} file found at: ` + botfile);
process.exit(1);
}
const bf = require(botfile);
return _util2.default.getDataLocation(bf.dataDir, projectPath);
};
const getCloudAuthFile = () => _path2.default.join(_os2.default.homedir(), '.botpress', AUTH_FILE);
const getAuthFile = () => _path2.default.join(getDataDir(), AUTH_FILE);
const readJsonFile = file => {
try {
const json = _fs2.default.readFileSync(file, 'utf-8');
return JSON.parse(json);
} catch (err) {
if (err.code !== 'ENOENT') {
_util2.default.print.warn(err.message || 'Unknown error', `while reading ${file}.`);
}
}
return {};
};
const writeJsonFile = (file, content) => {
_mkdirp2.default.sync(_path2.default.dirname(file));
_fs2.default.writeFileSync(file, JSON.stringify(content, null, 2));
};
const readBotAuth = () => readJsonFile(getAuthFile());
const readCloudAuth = () => readJsonFile(getCloudAuthFile());
const writeBotAuth = auth => writeJsonFile(getAuthFile(), auth);
const writeCloudAuth = auth => writeJsonFile(getCloudAuthFile(), auth);
const AUTH_DISABLED = '[AUTH DISABLED]';
const refreshToken = (() => {
var _ref = _asyncToGenerator(function* (botUrl) {
const auth = readBotAuth();
const token = auth[botUrl];
// this method is only called if the auth is enabled
// in which case it doesn't make sense even to try refreshing the fake token
// we might have saved before when the auth was disabled on this server
if (!token || token === AUTH_DISABLED) {
return;
}
try {
const response = yield _axios2.default.request({
url: `${botUrl}/api/auth/refresh_token`,
method: 'POST',
headers: {
Authorization: `Bearer ${token}`
}
});
return response.data;
} catch (err) {
return null;
}
});
return function refreshToken(_x) {
return _ref.apply(this, arguments);
};
})();
const doRootLogin = (() => {
var _ref2 = _asyncToGenerator(function* (botUrl) {
// try refreshing token before attempting the new login
const token = yield refreshToken(botUrl);
if (token) {
return { token, kind: 'refresh' };
}
const schema = {
properties: {
user: {
description: _chalk2.default.white('User:'),
required: true
},
password: {
description: _chalk2.default.white('Password:'),
hidden: true,
required: true
}
}
};
_prompt2.default.message = '';
_prompt2.default.delimiter = '';
_prompt2.default.start();
const { user, password } = yield _bluebird2.default.fromCallback(function (cb) {
return _prompt2.default.get(schema, cb);
});
const result = yield _axios2.default.post(`${botUrl}/api/login`, { user, password });
if (result.data.success) {
return { token: result.data.token, kind: 'login' };
}
throw new Error(result.data.reason);
});
return function doRootLogin(_x2) {
return _ref2.apply(this, arguments);
};
})();
const doCloudLogin = (() => {
var _ref3 = _asyncToGenerator(function* (botUrl, botInfo) {
const userAuthUrl = `${botInfo.endpoint}/me/cli`;
const loginUrl = `${botInfo.endpoint}/api/login/bot/${botInfo.botId}/${botInfo.botEnv}`;
const cloudAuth = readCloudAuth();
if (!cloudAuth[botInfo.endpoint]) {
const schema = {
properties: {
token: {
description: _chalk2.default.white('API Token:'),
required: true
}
}
};
_prompt2.default.message = `You need to authenticate using Botpress Cloud for this bot.\r\nPlease visit ${userAuthUrl} and copy/paste your API token here.\r\n`;
_prompt2.default.delimiter = '';
_prompt2.default.start();
const { token: apiToken } = yield _bluebird2.default.fromCallback(function (cb) {
return _prompt2.default.get(schema, cb);
});
if (!apiToken.startsWith('cli__')) {
throw new Error('Invalid API Token, expected token starting with "cli__"');
}
cloudAuth[botInfo.endpoint] = apiToken;
writeCloudAuth(cloudAuth);
}
try {
const authorization = `Bearer ${cloudAuth[botInfo.endpoint]}`;
const { data } = yield _axios2.default.get(loginUrl, { headers: { authorization } });
return { token: (0, _get2.default)(data, 'payload.token'), kind: 'refresh' };
} catch (err) {
delete cloudAuth[botInfo.endpoint];
writeCloudAuth(cloudAuth);
const msg = (0, _get2.default)(err, 'response.data.message') || err.message;
throw new Error('Could not authenticate to bot using Botpress Cloud, please try again. (' + msg + ')');
}
});
return function doCloudLogin(_x3, _x4) {
return _ref3.apply(this, arguments);
};
})();
const doLogin = (() => {
var _ref4 = _asyncToGenerator(function* (botUrl) {
const res = yield _axios2.default.get(`${botUrl}/api/auth/info`);
const data = res.data || {};
if (!data.type === 'none') {
return { token: AUTH_DISABLED, kind: 'no-auth' };
} else if (data.type === 'cloud') {
return doCloudLogin(botUrl, data);
} else if (data.type === 'root') {
return doRootLogin(botUrl);
} else {
throw new Error('Unknown login type: ' + data.type);
}
});
return function doLogin(_x5) {
return _ref4.apply(this, arguments);
};
})();
exports.login = (() => {
var _ref5 = _asyncToGenerator(function* (botUrl) {
botUrl = botUrl.replace(/\/+$/, '');
if (!_validUrl2.default.isUri(botUrl)) {
_util2.default.print.error(`Doesn't look like valid URL: ${botUrl}`);
return;
}
try {
const { token, kind } = yield doLogin(botUrl);
const auth = readBotAuth();
auth[botUrl] = token;
writeBotAuth(auth);
if (kind === 'login') {
_util2.default.print.success(`Logged in successfully. Auth token saved in ${getAuthFile()}.`);
} else if (kind === 'refresh') {
_util2.default.print.success(`Auth token refreshed and saved in ${getAuthFile()}.`);
} else if (kind === 'no-auth') {
_util2.default.print.info(`Auth is disabled at ${botUrl}, no need to login.`);
}
return token;
} catch (err) {
_util2.default.print.error(err.message || 'Unknown');
return;
}
});
return function (_x6) {
return _ref5.apply(this, arguments);
};
})();
exports.logout = botUrl => {
if (!botUrl) {
new _promptConfirm2.default("You're about to delete all saved auth tokens in the current folder. Are you sure?").run().then(answer => {
if (!answer) {
return;
}
writeBotAuth({});
writeCloudAuth({});
});
return;
}
botUrl = botUrl.replace(/\/+$/, '');
const auth = readBotAuth();
if (!auth[botUrl]) {
_util2.default.print.warn(`No saved token for ${botUrl}, nothing to do.`);
return;
}
delete auth[botUrl];
writeBotAuth(auth);
_util2.default.print.success('Logged out successfully.');
};
//# sourceMappingURL=auth.js.map