Move app from src-ts to src

This commit is contained in:
Travis Ralston 2017-12-24 02:28:48 -07:00
parent 70d1d23352
commit c5b803343e
84 changed files with 49 additions and 2775 deletions

4
.gitignore vendored
View File

@ -10,8 +10,8 @@ config/integrations/*_development.yaml
config/integrations/*_production.yaml
build/
dimension.db
src-ts/**/*.js
src-ts/**/*.js.map
src/**/*.js
src/**/*.js.map
web/**/*.js
web/**/*.js.map

View File

@ -1,67 +0,0 @@
var express = require("express");
var config = require("config");
var log = require("./util/LogService");
var DimensionStore = require("./storage/DimensionStore");
var bodyParser = require('body-parser');
var path = require("path");
var DimensionApi = require("./DimensionApi");
var ScalarApi = require("./ScalarApi");
var IRCApi = require("./integration/impl/irc/IRCApi");
var URL = require("url");
// TODO: Convert backend to typescript? Would avoid stubbing classes all over the place
/**
* Primary entry point for Dimension
*/
class Dimension {
/**
* Creates a new Dimension
*/
constructor() {
}
/**
* Starts the Dimension service
* @param {DimensionStore} db the store to use
*/
start(db) {
this._db = db;
this._app = express();
this._app.use(express.static('web-dist'));
this._app.use(bodyParser.json());
// Register routes for angular app
this._app.get(['/riot', '/riot/*', '/widgets', '/widgets/*'], (req, res) => {
res.sendFile(path.join(__dirname, "..", "web-dist", "index.html"));
});
// Allow CORS
this._app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
// Logging incoming requests
this._app.use((req, res, next) => {
var parsedUrl = URL.parse(req.url, true);
if (parsedUrl.query && parsedUrl.query["scalar_token"]) {
parsedUrl.query["scalar_token"] = "redacted";
parsedUrl.search = undefined; // to trigger the URL.format to use `query`
}
log.verbose("Dimension", "Incoming: " + req.method + " " + URL.format(parsedUrl));
next();
});
DimensionApi.bootstrap(this._app, this._db);
ScalarApi.bootstrap(this._app, this._db);
IRCApi.bootstrap(this._app, this._db);
this._app.listen(config.get('web.port'), config.get('web.address'));
log.info("Dimension", "API and UI listening on " + config.get("web.address") + ":" + config.get("web.port"));
}
}
module.exports = new Dimension();

View File

@ -1,307 +0,0 @@
const IntegrationImpl = require("./integration/impl/index");
const Integrations = require("./integration/index");
const _ = require("lodash");
const log = require("./util/LogService");
const request = require("request");
const dns = require("dns-then");
const urlParse = require("url");
const Netmask = require("netmask").Netmask;
const config = require("config");
/**
* API handler for the Dimension API
*/
class DimensionApi {
/**
* Creates a new Dimension API
*/
constructor() {
}
/**
* Bootstraps the Dimension API
* @param {*} app the Express application
* @param {DimensionStore} db the store to use
*/
bootstrap(app, db) {
this._db = db;
app.get("/api/v1/dimension/integrations/:roomId", this._getIntegrations.bind(this));
app.delete("/api/v1/dimension/integrations/:roomId/:type/:integrationType", this._removeIntegration.bind(this));
app.put("/api/v1/dimension/integrations/:roomId/:type/:integrationType/state", this._updateIntegrationState.bind(this));
app.get("/api/v1/dimension/integrations/:roomId/:type/:integrationType/state", this._getIntegrationState.bind(this));
app.get("/api/v1/dimension/widgets/embeddable", this._checkEmbeddable.bind(this));
app.get("/api/v1/dimension/integration/:type/:integrationType", this._getIntegration.bind(this));
app.get("/api/v1/dimension/whoami", this._getTokenOwner.bind(this));
}
_getTokenOwner(req, res) {
res.setHeader("Content-Type", "application/json");
var scalarToken = req.query.scalar_token;
if (!scalarToken) {
res.status(400).send({error: 'Missing scalar token'});
return;
}
this._db.getTokenOwner(scalarToken).then(userId => {
res.status(200).send({userId: userId});
}).catch(err => {
log.error("DimensionApi", err);
console.error(err);
res.status(401).send({error: 'Invalid token or other error'});
});
}
_checkEmbeddable(req, res) {
// Unauthed endpoint.
var url = req.query.url;
var parts = urlParse.parse(url);
var processed = false;
// Only allow http and https
if (parts.protocol !== "http:" && parts.protocol !== "https:") {
res.status(400).send({error: "Invalid request scheme " + parts.protocol, canEmbed: false});
processed = true;
return;
}
// Verify the address is permitted for widgets
var hostname = parts.hostname.split(":")[0];
dns.resolve4(hostname).then(addresses => {
log.verbose("DimensionApi", "Hostname " + hostname + " resolves to " + addresses);
if (addresses.length == 0) {
res.status(400).send({error: "Unrecongized address", canEmbed: false});
processed = true;
return;
}
for (var ipOrCidr of config.get("widgetBlacklist")) {
var block = new Netmask(ipOrCidr);
for (var address of addresses) {
if (block.contains(address)) {
res.status(400).send({error: "Address not allowed", canEmbed: false});
processed = true;
return;
}
}
}
}, err => {
log.verbose("DimensionApi", "Error resolving host " + hostname);
log.verbose("DimensionApi", err);
res.status(400).send({error: "DNS error", canEmbed: false});
processed = true;
}).then(() => {
if (processed) return;
// Verify that the content can actually be embedded (CORS)
request(url, (err, response) => {
if (err) {
log.verbose("DimensionApi", "Error contacting host " + hostname);
log.verbose("DimensionApi", err);
res.status(400).send({error: "Host error", canEmbed: false});
return;
}
if (response.statusCode >= 200 && response.statusCode < 300) {
// 200 OK
var headers = response.headers;
var xFrameOptions = (headers['x-frame-options'] || '').toLowerCase();
if (xFrameOptions === 'sameorigin' || xFrameOptions === 'deny') {
res.status(400).send({error: "X-Frame-Options forbids embedding", canEmbed: false});
} else res.status(200).send({canEmbed: true});
} else {
res.status(400).send({error: "Unsuccessful status code: " + response.statusCode, canEmbed: false});
}
});
});
}
_findIntegration(integrationConfig, roomId, scalarToken) {
var factory = IntegrationImpl.getFactory(integrationConfig);
if (!factory) throw new Error("Missing config factory for " + integrationConfig.name);
try {
return factory(this._db, integrationConfig, roomId, scalarToken);
} catch (err) {
throw new Error("Error using factory for " + integrationConfig.name + ". Please either fix the integration settings or disable the integration.", err);
}
}
_getIntegration(req, res) {
res.setHeader("Content-Type", "application/json");
// Unauthed endpoint.
var type = req.params.type;
var integrationType = req.params.integrationType;
if (!type || !integrationType) {
res.status(400).send({error: "Missing integration type or type"});
return;
}
var byIntegrationType = Integrations.byType[type];
if (!byIntegrationType || !byIntegrationType[integrationType]) {
res.status(400).send({error: "Unknown integration"});
return;
}
var integrationConfig = byIntegrationType[integrationType];
res.status(200).send(integrationConfig);
}
_getIntegrations(req, res) {
res.setHeader("Content-Type", "application/json");
var roomId = req.params.roomId;
if (!roomId) {
res.status(400).send({error: 'Missing room ID'});
return;
}
var scalarToken = req.query.scalar_token;
this._db.checkToken(scalarToken).then(() => {
var integrations = _.map(Integrations.all, i => JSON.parse(JSON.stringify(i))); // clone
var promises = [];
var remove = [];
_.forEach(integrations, integration => {
try {
promises.push(this._findIntegration(integration, roomId, scalarToken).then(builtIntegration => {
return builtIntegration.getState().then(state => {
var keys = _.keys(state);
for (var key of keys) {
integration[key] = state[key];
}
return builtIntegration.getUserId();
}).then(userId => {
integration.userId = userId;
});
}));
} catch (err) {
remove.push(integration);
log.error("DimensionApi", err);
}
});
for (var toRemove of remove) {
var idx = integrations.indexOf(toRemove);
if (idx === -1) continue;
log.warn("DimensionApi", "Disabling integration " + toRemove.name + " due to an error encountered in setup");
integrations.splice(idx, 1);
}
Promise.all(promises).then(() => res.send(_.map(integrations, integration => {
// Remove sensitive material
integration.upstream = undefined;
integration.hosted = undefined;
return integration;
})));
}).catch(err => {
log.error("DimensionApi", err);
console.error(err);
res.status(500).send({error: err});
});
}
_removeIntegration(req, res) {
var roomId = req.params.roomId;
var scalarToken = req.query.scalar_token;
var type = req.params.type;
var integrationType = req.params.integrationType;
if (!roomId || !scalarToken || !type || !integrationType) {
res.status(400).send({error: "Missing room, integration type, type, or token"});
return;
}
var integrationConfig = Integrations.byType[type][integrationType];
if (!integrationConfig) {
res.status(400).send({error: "Unknown integration"});
return;
}
log.info("DimensionApi", "Remove requested for " + type + " (" + integrationType + ") in room " + roomId);
this._db.checkToken(scalarToken).then(() => {
return this._findIntegration(integrationConfig, roomId, scalarToken);
}).then(integration => integration.removeFromRoom(roomId)).then(() => {
res.status(200).send({success: true});
}).catch(err => {
log.error("DimensionApi", err);
console.error(err);
res.status(500).send({error: err.message});
});
}
_updateIntegrationState(req, res) {
var roomId = req.params.roomId;
var scalarToken = req.body.scalar_token;
var type = req.params.type;
var integrationType = req.params.integrationType;
if (!roomId || !scalarToken || !type || !integrationType) {
res.status(400).send({error: "Missing room, integration type, type, or token"});
return;
}
var integrationConfig = Integrations.byType[type][integrationType];
if (!integrationConfig) {
res.status(400).send({error: "Unknown integration"});
return;
}
log.info("DimensionApi", "Update state requested for " + type + " (" + integrationType + ") in room " + roomId);
this._db.checkToken(scalarToken).then(() => {
return this._findIntegration(integrationConfig, roomId, scalarToken);
}).then(integration => {
return integration.updateState(req.body.state);
}).then(newState => {
res.status(200).send(newState);
}).catch(err => {
log.error("DimensionApi", err);
console.error(err);
res.status(500).send({error: err.message});
});
}
_getIntegrationState(req, res) {
var roomId = req.params.roomId;
var scalarToken = req.query.scalar_token;
var type = req.params.type;
var integrationType = req.params.integrationType;
if (!roomId || !scalarToken || !type || !integrationType) {
res.status(400).send({error: "Missing room, integration type, type, or token"});
return;
}
var integrationConfig = Integrations.byType[type][integrationType];
if (!integrationConfig) {
res.status(400).send({error: "Unknown integration"});
return;
}
log.info("DimensionApi", "State requested for " + type + " (" + integrationType + ") in room " + roomId);
this._db.checkToken(scalarToken).then(() => {
return this._findIntegration(integrationConfig, roomId, scalarToken);
}).then(integration => {
return integration.getState();
}).then(state => {
res.status(200).send(state);
}).catch(err => {
log.error("DimensionApi", err);
console.error(err);
res.status(500).send({error: err.message});
});
}
}
module.exports = new DimensionApi();

View File

@ -1,11 +0,0 @@
/**
* Serves the purpose of being a documentation endpoint
*/
class OpenID {
access_token = "";
token_type = "";
matrix_server_name = "";
expires_in = 0;
}
module.exports = OpenID;

View File

@ -1,115 +0,0 @@
var MatrixLiteClient = require("./matrix/MatrixLiteClient");
var randomString = require("random-string");
var ScalarClient = require("./scalar/ScalarClient.js");
var _ = require("lodash");
var log = require("./util/LogService");
var Promise = require("bluebird");
var UpstreamConfiguration = require("./UpstreamConfiguration");
/**
* API handler for the Scalar API, as required by Riot
*/
class ScalarApi {
/**
* Creates a new Scalar API
*/
constructor() {
}
/**
* Bootstraps the Scalar API
* @param {*} app the Express application
* @param {DimensionStore} db the store to use
*/
bootstrap(app, db) {
this._db = db;
app.post("/api/v1/scalar/register", this._scalarRegister.bind(this));
app.get("/api/v1/scalar/checkToken", this._checkScalarToken.bind(this));
app.get("/api/v1/scalar/widgets/title_lookup", this._getWidgetTitle.bind(this));
}
_getWidgetTitle(req, res) {
res.setHeader("Content-Type", "application/json");
var token = req.query.scalar_token;
var url = req.query.curl;
if (!token || !url) {
res.status(400).send({error: "Missing token or curl"});
return;
}
this._db.checkToken(token).then(() => {
MatrixLiteClient.getUrlPreview(url).then(preview => {
if (!preview["og:title"]) {
res.status(404).send({error:{message:"Could not locate a title for the URL"}});
return;
}
// We need to convert the preview response to what Scalar expects
res.status(200).send({
cached_response: false,
page_title_cache_item: {
expires: null, // unused
cached_response_err: null, // unused
cached_title: preview["og:title"],
}
});
}).catch(err => {
res.status(500).send({error: {message: "Failed to get preview"}});
log.error("ScalarApi", "Failed to get URL preview");
log.error("ScalarApi", err);
});
}).catch(err => {
res.status(401).send({error: {message: "Failed to authenticate token"}});
log.warn("ScalarApi", "Failed to authenticate token");
log.warn("ScalarApi", err);
});
}
_checkScalarToken(req, res) {
var token = req.query.scalar_token;
if (!token) res.sendStatus(400);
else this._db.checkToken(token).then(() => {
res.sendStatus(200);
}).catch(e => {
res.sendStatus(401);
log.warn("ScalarApi", "Failed to authenticate token");
log.verbose("ScalarApi", e);
});
}
_scalarRegister(req, res) {
res.setHeader("Content-Type", "application/json");
var tokenInfo = req.body;
if (!tokenInfo || !tokenInfo['access_token'] || !tokenInfo['token_type'] || !tokenInfo['matrix_server_name'] || !tokenInfo['expires_in']) {
res.status(400).send({error: 'Missing OpenID'});
return;
}
var client = new MatrixLiteClient(tokenInfo);
var scalarToken = randomString({length: 25});
var userId;
client.getSelfMxid().then(mxid => {
userId = mxid;
if (!mxid) throw new Error("Token does not resolve to a matrix user");
// TODO: Make this part more generic for other upstreams (#22)
if (!UpstreamConfiguration.hasUpstream("vector")) return Promise.resolve(null);
return ScalarClient.register(tokenInfo);
}).then(upstreamToken => {
return this._db.createToken(userId, tokenInfo, scalarToken, upstreamToken);
}).then(() => {
res.setHeader("Content-Type", "application/json");
res.send({scalar_token: scalarToken});
}).catch(err => {
log.error("ScalarApi", err);
res.status(500).send({error: err.message});
});
}
}
module.exports = new ScalarApi();

View File

@ -1,50 +0,0 @@
var LogService = require("./util/LogService");
var _ = require("lodash");
var config = require("config");
/**
* Handles all upstream configuration information, such as URLs, tokens, and whether or not they are enabled.
*/
class UpstreamConfiguration {
/**
* Creates a new upstream configuration handler
*/
constructor() {
this._upstreams = {};
this._loadUpstreams();
}
_loadUpstreams() {
for (var upstream of config.upstreams) {
var upstreamConfig = upstream;
if (this._upstreams[upstream.name]) {
LogService.warn("UpstreamConfiguration", "Duplicate upstream " + upstream.name +" - skipping");
continue;
}
this._upstreams[upstream.name] = upstreamConfig;
LogService.info("UpstreamConfiguration", "Loaded upstream '" + upstream.name + "' as: " + JSON.stringify(upstreamConfig));
}
}
/**
* Checks if a particular upstream exists
* @param {string} name the name of the upstream
* @returns {boolean} true if it is enabled and exists
*/
hasUpstream(name) {
return !!this._upstreams[name];
}
/**
* Gets an upstream's configuration
* @param {string} name the upstream name
* @returns {{url:string}} the upstream configuration
*/
getUpstream(name) {
return _.clone(this._upstreams[name]);
}
}
module.exports = new UpstreamConfiguration();

View File

@ -1,22 +0,0 @@
var IntegrationStub = require("./IntegrationStub");
/**
* Represents a bridge. Normally bridges have enhanced configuration and requirements over bots.
*/
class Bridge extends IntegrationStub {
/**
* Creates a new bridge
* @param bridgeConfig the configuration for the bridge
*/
constructor(bridgeConfig) {
super(bridgeConfig);
}
/*override*/
getUserId() {
return null; // bridges don't have bot users we care about
}
}
module.exports = Bridge;

View File

@ -1,18 +0,0 @@
var IntegrationStub = require("./IntegrationStub");
/**
* Represents a bot with additional configuration or setup needs. Normally indicates a bot needs
* more than a simple invite to the room.
*/
class ComplexBot extends IntegrationStub {
/**
* Creates a new complex bot
* @param botConfig the configuration for the bot
*/
constructor(botConfig) {
super(botConfig);
}
}
module.exports = ComplexBot;

View File

@ -1,44 +0,0 @@
/**
* Stub for an Integration
*/
class IntegrationStub {
constructor(botConfig) {
this._config = botConfig;
}
/**
* Gets the user ID for this bot
* @return {Promise<string>} resolves to the user ID
*/
getUserId() {
return Promise.resolve(this._config.userId);
}
/**
* Gets state information that represents how this bot is operating.
* @return {Promise<*>} resolves to the state information
*/
getState() {
return Promise.resolve({});
}
/**
* Removes the integration from the given room
* @param {string} roomId the room ID to remove the integration from
* @returns {Promise<>} resolves when completed
*/
removeFromRoom(roomId) {
throw new Error("Not implemented");
}
/**
* Updates the state information for this integration. The data passed is an implementation detail.
* @param {*} newState the new state
* @returns {Promise<*>} resolves when completed, with the new state of the integration
*/
updateState(newState) {
return Promise.resolve({});
}
}
module.exports = IntegrationStub;

View File

@ -1,22 +0,0 @@
var IntegrationStub = require("./IntegrationStub");
/**
* Represents a widget. Widgets allow for web applications or rich functionality within the room.
*/
class Widget extends IntegrationStub {
/**
* Creates a new widget
* @param widgetConfig the configuration for the widget
*/
constructor(widgetConfig) {
super(widgetConfig);
}
/*override*/
getUserId() {
return null; // widgets don't have bot users we care about
}
}
module.exports = Widget;

View File

@ -1,20 +0,0 @@
var IntegrationStub = require("../generic_types/IntegrationStub");
/**
* Creates an integration using the given
* @param {DimensionStore} db the database
* @param {*} integrationConfig the integration configuration
* @param {string} roomId the room ID
* @param {string} scalarToken the scalar token
* @returns {Promise<*>} resolves to the configured integration
*/
var factory = (db, integrationConfig, roomId, scalarToken) => {
factory.validateConfig(integrationConfig);
return Promise.resolve(new IntegrationStub(integrationConfig));
};
factory.validateConfig = (integrationConfig) => {
// Nothing to do
};
module.exports = factory;

View File

@ -1,53 +0,0 @@
var ComplexBot = require("../../generic_types/ComplexBot");
/**
* Represents a CircleCI bot
*/
class CircleCiBot extends ComplexBot {
/**
* Creates a new CircleCI bot
* @param botConfig the bot configuration
* @param backbone the backbone powering this bot
*/
constructor(botConfig, backbone) {
super(botConfig);
this._backbone = backbone;
}
/*override*/
getUserId() {
return this._backbone.getUserId();
}
/*override*/
getState() {
var response = {
repoTemplates: [],
immutableRepoTemplates: [],
webhookUrl: ""
};
return this._backbone.getRepos().then(templates => {
response.repoTemplates = templates;
return this._backbone.getImmutableRepos();
}).then(immutable => {
response.immutableRepoTemplates = immutable;
return this._backbone.getWebhookUrl();
}).then(url => {
response.webhookUrl = url;
return response;
});
}
/*override*/
removeFromRoom(roomId) {
return this._backbone.removeFromRoom(roomId);
}
/*override*/
updateState(newState) {
return this._backbone.setRepos(newState.repoTemplates).then(() => this.getState());
}
}
module.exports = CircleCiBot;

View File

@ -1,20 +0,0 @@
var CircleCiBot = require("./CircleCiBot");
var VectorCircleCiBackbone = require("./VectorCircleCiBackbone");
var UpstreamConfiguration = require("../../../UpstreamConfiguration");
var factory = (db, integrationConfig, roomId, scalarToken) => {
factory.validateConfig(integrationConfig);
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
var backbone = new VectorCircleCiBackbone(roomId, upstreamToken);
return new CircleCiBot(integrationConfig, backbone);
});
};
factory.validateConfig = (integrationConfig) => {
if (!integrationConfig.upstream) throw new Error("Unsupported configuration");
if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream");
if (!UpstreamConfiguration.hasUpstream("vector")) throw new Error("Vector upstream not specified");
};
module.exports = factory;

View File

@ -1,63 +0,0 @@
/**
* Stubbed/placeholder CircleCI backbone
*/
class StubbedCircleCiBackbone {
/**
* Creates a new stubbed CircleCI backbone
*/
constructor() {
}
/**
* Gets the user ID for this backbone
* @returns {Promise<string>} resolves to the user ID
*/
getUserId() {
throw new Error("Not implemented");
}
/**
* Gets the repository templates for this backbone
* @returns {Promise<{repoKey:string,template:string}[]>} resolves to the collection of repositories and their templates
*/
getRepos() {
throw new Error("Not implemented");
}
/**
* Gets the immutable repository templates for this backbone (set by other users)
* @returns {Promise<{repoKey:string,template:string,ownerId:string}[]>} resolves to the collection of repositories and their templates
*/
getImmutableRepos() {
throw new Error("Not implemented");
}
/**
* Sets the new repository templates for this backbone
* @param {{repoKey:string,template:string}[]} newRepos the new templates for the repositories
* @returns {Promise<>} resolves when complete
*/
setRepos(newRepos) {
throw new Error("Not implemented");
}
/**
* Gets the webhook url for this backbone
* @returns {Promise<string>} resolves to the webhook URL
*/
getWebhookUrl() {
throw new Error("Not implemented");
}
/**
* Removes the bot from the given room
* @param {string} roomId the room ID to remove the bot from
* @returns {Promise<>} resolves when completed
*/
removeFromRoom(roomId) {
throw new Error("Not implemented");
}
}
module.exports = StubbedCircleCiBackbone;

View File

@ -1,108 +0,0 @@
var StubbedCircleCiBackbone = require("./StubbedCircleCiBackbone");
var VectorScalarClient = require("../../../scalar/VectorScalarClient");
var _ = require("lodash");
var log = require("../../../util/LogService");
/**
* Backbone for CircleCI bots running on vector.im through scalar
*/
class VectorCircleCiBackbone extends StubbedCircleCiBackbone {
/**
* Creates a new Vector CircleCI backbone
* @param {string} roomId the room ID to manage
* @param {string} upstreamScalarToken the vector scalar token
*/
constructor(roomId, upstreamScalarToken) {
super();
this._roomId = roomId;
this._scalarToken = upstreamScalarToken;
this._info = null;
this._otherTemplates = [];
}
/*override*/
getUserId() {
return (this._info ? Promise.resolve() : this._getInfo()).then(() => {
return this._info.bot_user_id;
});
}
/*override*/
getRepos() {
return (this._info ? Promise.resolve() : this._getInfo()).then(() => {
if (this._info.integrations.length == 0) return [];
var rooms = _.keys(this._info.integrations[0].config.rooms);
if (rooms.indexOf(this._roomId) === -1) return [];
var repos = _.keys(this._info.integrations[0].config.rooms[this._roomId].repos);
return _.map(repos, r => {
return {repoKey: r, template: this._info.integrations[0].config.rooms[this._roomId].repos[r].template};
});
});
}
/*override*/
getImmutableRepos() {
return (this._info ? Promise.resolve() : this._getInfo()).then(() => {
return this._otherTemplates;
});
}
/*override*/
setRepos(newRepos) {
var config = {};
config[this._roomId] = {repos: {}};
for (var repo of newRepos) config[this._roomId].repos[repo.repoKey] = {template: repo.template};
return VectorScalarClient.configureIntegration("circleci", this._scalarToken, {
rooms: config
});
}
/*override*/
getWebhookUrl() {
// string
return (this._info ? Promise.resolve() : this._getInfo()).then(() => {
if (this._info.integrations.length == 0) return "";
return this._info.integrations[0].config.webhook_url;
});
}
_getInfo() {
return VectorScalarClient.getIntegrationsForRoom(this._roomId, this._scalarToken).then(integrations => {
this._otherTemplates = [];
for (var integration of integrations) {
if (integration.self) continue; // skip - we're not looking for ones we know about
if (integration.type == "circleci") {
var roomIds = _.keys(integration.config.rooms);
if (roomIds.length === 0) continue;
if (roomIds.length !== 1) log.warn("VectorCircleCiBackbone", "Expected 1 room but found " + roomIds.length);
var roomConfig = integration.config.rooms[roomIds[0]];
var repositories = _.keys(roomConfig.repos);
for (var repo of repositories) {
this._otherTemplates.push({
repoKey: repo,
template: roomConfig.repos[repo].template,
ownerId: integration.user_id
});
}
}
}
return VectorScalarClient.getIntegration("circleci", this._roomId, this._scalarToken);
}).then(info => {
this._info = info;
});
}
/*override*/
removeFromRoom(roomId) {
return VectorScalarClient.removeIntegration("circleci", roomId, this._scalarToken);
}
}
module.exports = VectorCircleCiBackbone;

View File

@ -1,46 +0,0 @@
var log = require("../../util/LogService");
var StubbedFactory = require("./StubbedFactory");
var SimpleBotFactory = require("./simple_bot/SimpleBotFactory");
var RSSFactory = require("./rss/RSSFactory");
var IRCFactory = require("./irc/IRCFactory");
var TravisCiFactory = require("./travisci/TravisCiFactory");
var CircleCiFactory = require("./circleci/CircleCiFactory");
var SimpleWidgetFactory = require("./simple_widget/SimpleWidgetFactory");
var mapping = {
"complex-bot": {
"rss": RSSFactory,
"travisci": TravisCiFactory,
"circleci": CircleCiFactory,
},
"bridge": {
"irc": IRCFactory,
}
};
var defaultFactories = {
"complex-bot": null,
"bot": SimpleBotFactory,
"bridge": null,
"widget": SimpleWidgetFactory,
};
module.exports = {
getFactory: (integrationConfig) => {
var opts = mapping[integrationConfig.type];
if (!opts) {
log.verbose("IntegrationImpl", "No option set available for " + integrationConfig.type + " - will attempt defaults");
}
var factory = null;
if (!opts) factory = defaultFactories[integrationConfig.type];
else factory = opts[integrationConfig.integrationType];
if (!factory) {
log.verbose("IntegrationImpl", "No factory available for " + integrationConfig.type + " (" + integrationConfig.integrationType + ") - using stub");
factory = StubbedFactory;
}
return factory;
}
};

View File

@ -1,114 +0,0 @@
var Integrations = require("../../index");
var IntegrationImpl = require("../index");
var log = require("../../../util/LogService");
/**
* API Handler for the IRC integration
*/
class IRCApi {
/**
* Creates a new IRC API
*/
constructor() {
}
/**
* Bootstraps the IRC API
* @param {*} app the Express application
* @param {DimensionStore} db the store to use
*/
bootstrap(app, db) {
if (!Integrations.byType["bridge"] || !Integrations.byType["bridge"]["irc"]) {
log.info("IRCApi", "IRC Bridge not enabled - not setting up the API");
return;
} else log.info("IRCApi", "Setting up IRC API");
this._db = db;
app.get("/api/v1/irc/:roomId/ops/:network/:channel", this._getChannelOps.bind(this));
app.put("/api/v1/irc/:roomId/channels/:network/:channel", this._addChannel.bind(this));
app.delete("/api/v1/irc/:roomId/channels/:network/:channel", this._deleteChannel.bind(this));
}
_getChannelOps(req, res) {
this._generalProcessing(req, res).then(ircBridge => {
var network = req.params.network;
var channel = req.params.channel;
return ircBridge.getChannelOps(network, channel).catch(err => {
log.error("IRCApi", err);
console.error(err);
res.status(500).send({error: err});
return null;
});
}).then(ops => {
if (ops !== null) res.status(200).send(ops);
}).catch(() => null);
}
_addChannel(req, res) {
this._generalProcessing(req, res).then(ircBridge => {
var network = req.params.network;
var channel = req.params.channel;
var op = req.query.op;
return ircBridge.addChannel(network, channel, op).catch(err => {
log.error("IRCApi", err);
console.error(err);
res.status(500).send({error: err});
return null;
});
}).then(result => {
if (result !== null) res.status(200).send({successful: true});
}).catch(() => null);
}
_deleteChannel(req, res) {
this._generalProcessing(req, res).then(ircBridge => {
var network = req.params.network;
var channel = req.params.channel;
return ircBridge.removeChannel(network, channel).catch(err => {
log.error("IRCApi", err);
console.error(err);
res.status(500).send({error: err});
return null;
});
}).then(result => {
if (result !== null) res.status(200).send({successful: true});
}).catch(() => null);
}
_generalProcessing(req, res) {
return new Promise((resolve, reject) => {
res.setHeader("Content-Type", "application/json");
var roomId = req.params.roomId;
var network = req.params.network;
var channel = req.params.channel;
if (!roomId || !network || !channel) {
res.status(400).send({error: 'Missing room ID, network, or channel'});
reject();
return;
}
var scalarToken = req.query.scalar_token;
this._db.checkToken(scalarToken).then(() => {
var conf = Integrations.byType["bridge"]["irc"];
var factory = IntegrationImpl.getFactory(conf);
factory(this._db, conf, roomId, scalarToken).then(resolve).catch(err => {
log.error("IRCApi", err);
console.error(err);
res.status(500).send({error: err});
reject();
});
}).catch(err => {
log.error("IRCApi", err);
console.error(err);
res.status(500).send({error: err});
reject();
});
});
}
}
module.exports = new IRCApi();

View File

@ -1,75 +0,0 @@
var Bridge = require("../../generic_types/Bridge");
/**
* Represents an IRC bridge
*/
class IRCBridge extends Bridge {
/**
* Creates a new IRC bridge
* @param bridgeConfig the bridge configuration
* @param backbone the backbone powering this bridge
*/
constructor(bridgeConfig, backbone) {
super(bridgeConfig);
this._backbone = backbone;
}
/*override*/
getState() {
var response = {
availableNetworks: [],
channels: {}
};
return this._backbone.getNetworks().then(networks => {
response.availableNetworks = networks;
return this._backbone.getLinkedChannels();
}).then(channels => {
response.channels = channels;
return response;
});
}
/*override*/
removeFromRoom(roomId) {
return this._backbone.removeFromRoom(roomId);
}
/*override*/
updateState(newState) {
throw new Error("State cannot be updated for an IRC bridge. Use the IRC API instead.");
}
/**
* Gets a list of operators available in a particular channel on a particular network
* @param {string} network the network to look at
* @param {string} channel the channel to look in (without prefixed #)
* @returns {Promise<string[]>} resolves to a list of operators
*/
getChannelOps(network, channel) {
return this._backbone.getChannelOps(network, channel);
}
/**
* Links a channel to the room this bridge controls
* @param {string} network the network to link to
* @param {string} channel the channel to link to
* @param {string} op the channel operator to request permission from
* @returns {Promise<>} resolves when complete
*/
addChannel(network, channel, op) {
return this._backbone.addChannel(network, channel, op);
}
/**
* Unlinks a channel from the room this bridge controls
* @param {string} network the network to unlink from
* @param {string} channel the channel to unlink
* @returns {Promise<>} resolves when complete
*/
removeChannel(network, channel) {
return this._backbone.removeChannel(network, channel);
}
}
module.exports = IRCBridge;

View File

@ -1,20 +0,0 @@
var IRCBridge = require("./IRCBridge");
var VectorIrcBackbone = require("./VectorIrcBackbone");
var UpstreamConfiguration = require("../../../UpstreamConfiguration");
var factory = (db, integrationConfig, roomId, scalarToken) => {
factory.validateConfig(integrationConfig);
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
var backbone = new VectorIrcBackbone(roomId, upstreamToken);
return new IRCBridge(integrationConfig, backbone);
});
};
factory.validateConfig = (integrationConfig) => {
if (!integrationConfig.upstream) throw new Error("Unsupported configuration");
if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream");
if (!UpstreamConfiguration.hasUpstream("vector")) throw new Error("Vector upstream not specified");
};
module.exports = factory;

View File

@ -1,60 +0,0 @@
/**
* Stubbed/placeholder IRC backbone
*/
class StubbedIrcBackbone {
/**
* Creates a new stubbed IRC backbone
*/
constructor() {
}
/**
* Gets a list of all available networks
* @returns {Promise<{name: string, id: string}[]>} resolves to the list of available networks
*/
getNetworks() {
return Promise.resolve([]);
}
/**
* Gets a network representation of the linked channels
* @returns {Promise<{[string]: string[]}>} resolves to the network representation of linked channels
*/
getLinkedChannels() {
return Promise.resolve({});
}
/**
* Gets a list of operators available in a particular channel on a particular network
* @param {string} network the network to look at
* @param {string} channel the channel to look in (without prefixed #)
* @returns {Promise<string[]>} resolves to a list of operators
*/
getChannelOps(network, channel) {
return Promise.resolve([]);
}
/**
* Links a channel to the room this backbone controls
* @param {string} network the network to link to
* @param {string} channel the channel to link to
* @param {string} op the channel operator to request permission from
* @returns {Promise<>} resolves when complete
*/
addChannel(network, channel, op) {
throw new Error("Not implemented");
}
/**
* Unlinks a channel from the room this backbone controls
* @param {string} network the network to unlink from
* @param {string} channel the channel to unlink
* @returns {Promise<>} resolves when complete
*/
removeChannel(network, channel) {
throw new Error("Not implemented");
}
}
module.exports = StubbedIrcBackbone;

View File

@ -1,120 +0,0 @@
var StubbedIrcBackbone = require("./StubbedIrcBackbone");
var VectorScalarClient = require("../../../scalar/VectorScalarClient");
var _ = require("lodash");
var log = require("../../../util/LogService");
/**
* Backbone for IRC bridges running on vector.im through scalar
*/
class VectorIrcBackbone extends StubbedIrcBackbone {
/**
* Creates a new Vector IRC backbone
* @param {string} roomId the room ID to manage
* @param {string} upstreamScalarToken the vector scalar token
*/
constructor(roomId, upstreamScalarToken) {
super();
this._roomId = roomId;
this._scalarToken = upstreamScalarToken;
this._lastNetworkResponse = null;
}
/*override*/
getNetworks() {
return this._getNetworks().then(networks => _.map(networks, n => {
return {name: n.title, id: n.id};
}));
}
/*override*/
getLinkedChannels() {
var networks;
return this._getNetworks().then(n => {
networks = n;
return VectorScalarClient.getIrcLinks(this._roomId, this._scalarToken);
}).then(links => {
var container = {};
var ridToServerId = {};
for (var network of networks) {
ridToServerId[network.rid] = network.id;
container[network.id] = [];
}
for (var link of links) {
var server = ridToServerId[link.rid];
if (!server) {
log.error("VectorIrcBackbone", "Could not find network for RID " + link.rid);
throw new Error("Unexpected RID");
}
container[server].push(link.channel);
}
return container;
});
}
/*override*/
getChannelOps(network, channel) {
return this._getNetworks().then(networks => {
var networkServer = null;
var rid = null;
for (var n of networks) {
if (n.id === network) {
networkServer = n.domain;
rid = n.rid;
break;
}
}
return VectorScalarClient.getIrcOperators(rid, networkServer, '#' + channel, this._scalarToken);
});
}
/*override*/
addChannel(network, channel, op) {
return this._getNetworks().then(networks => {
var networkServer = null;
var rid = null;
for (var n of networks) {
if (n.id === network) {
networkServer = n.domain;
rid = n.rid;
break;
}
}
return VectorScalarClient.addIrcLink(rid, this._roomId, networkServer, '#' + channel, op, this._scalarToken);
});
}
/*override*/
removeChannel(network, channel) {
return this._getNetworks().then(networks => {
var networkServer = null;
var rid = null;
for (var n of networks) {
if (n.id === network) {
networkServer = n.domain;
rid = n.rid;
break;
}
}
return VectorScalarClient.removeIrcLink(rid, this._roomId, networkServer, '#' + channel, this._scalarToken);
});
}
_getNetworks() {
if (this._lastNetworkResponse !== null) return Promise.resolve(this._lastNetworkResponse);
return VectorScalarClient.getIrcNetworks(this._scalarToken).then(networks => {
this._lastNetworkResponse = networks;
return networks;
});
}
}
module.exports = VectorIrcBackbone;

View File

@ -1,49 +0,0 @@
var ComplexBot = require("../../generic_types/ComplexBot");
/**
* Represents an RSS bot
*/
class RSSBot extends ComplexBot {
/**
* Creates a new RSS bot
* @param botConfig the bot configuration
* @param backbone the backbone powering this bot
*/
constructor(botConfig, backbone) {
super(botConfig);
this._backbone = backbone;
}
/*override*/
getUserId() {
return this._backbone.getUserId();
}
/*override*/
getState() {
var response = {
feeds: [],
immutableFeeds: []
};
return this._backbone.getFeeds().then(feeds => {
response.feeds = feeds;
return this._backbone.getImmutableFeeds();
}).then(feeds => {
response.immutableFeeds = feeds;
return response;
});
}
/*override*/
removeFromRoom(roomId) {
return this._backbone.removeFromRoom(roomId);
}
/*override*/
updateState(newState) {
return this._backbone.setFeeds(newState.feeds).then(() => this.getState());
}
}
module.exports = RSSBot;

View File

@ -1,20 +0,0 @@
var RSSBot = require("./RSSBot");
var VectorRssBackbone = require("./VectorRssBackbone");
var UpstreamConfiguration = require("../../../UpstreamConfiguration");
var factory = (db, integrationConfig, roomId, scalarToken) => {
factory.validateConfig(integrationConfig);
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
var backbone = new VectorRssBackbone(roomId, upstreamToken);
return new RSSBot(integrationConfig, backbone);
});
};
factory.validateConfig = (integrationConfig) => {
if (!integrationConfig.upstream) throw new Error("Unsupported configuration");
if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream");
if (!UpstreamConfiguration.hasUpstream("vector")) throw new Error("Vector upstream not specified");
};
module.exports = factory;

View File

@ -1,55 +0,0 @@
/**
* Stubbed/placeholder RSS backbone
*/
class StubbedRssBackbone {
/**
* Creates a new stubbed RSS backbone
*/
constructor() {
}
/**
* Gets the user ID for this backbone
* @returns {Promise<string>} resolves to the user ID
*/
getUserId() {
throw new Error("Not implemented");
}
/**
* Gets the feeds for this backbone
* @returns {Promise<string[]>} resolves to the collection of feeds
*/
getFeeds() {
throw new Error("Not implemented");
}
/**
* Sets the new feeds for this backbone
* @param {string[]} newFeeds the new feed URLs
* @returns {Promise<>} resolves when complete
*/
setFeeds(newFeeds) {
throw new Error("Not implemented");
}
/**
* Gets the immutable feeds for this backbone
* @returns {Promise<{url:string,ownerId:string}>} resolves to the collection of immutable feeds
*/
getImmutableFeeds() {
throw new Error("Not implemented");
}
/**
* Removes the bot from the given room
* @param {string} roomId the room ID to remove the bot from
* @returns {Promise<>} resolves when completed
*/
removeFromRoom(roomId) {
throw new Error("Not implemented");
}
}
module.exports = StubbedRssBackbone;

View File

@ -1,82 +0,0 @@
var StubbedRssBackbone = require("./StubbedRssBackbone");
var VectorScalarClient = require("../../../scalar/VectorScalarClient");
var _ = require("lodash");
var log = require("../../../util/LogService");
/**
* Backbone for RSS bots running on vector.im through scalar
*/
class VectorRssBackbone extends StubbedRssBackbone {
/**
* Creates a new Vector RSS backbone
* @param {string} roomId the room ID to manage
* @param {string} upstreamScalarToken the vector scalar token
*/
constructor(roomId, upstreamScalarToken) {
super();
this._roomId = roomId;
this._scalarToken = upstreamScalarToken;
this._info = null;
this._otherFeeds = [];
}
/*override*/
getUserId() {
return (this._info ? Promise.resolve() : this._getInfo()).then(() => {
return this._info.bot_user_id;
});
}
/*override*/
getFeeds() {
return (this._info ? Promise.resolve() : this._getInfo()).then(() => {
if (this._info.integrations.length == 0) return [];
return _.keys(this._info.integrations[0].config.feeds);
});
}
/*override*/
setFeeds(newFeeds) {
var feedConfig = {};
for (var feed of newFeeds) feedConfig[feed] = {};
return VectorScalarClient.configureIntegration("rssbot", this._scalarToken, {
feeds: feedConfig,
room_id: this._roomId
});
}
/*override*/
getImmutableFeeds() {
return (this._info ? Promise.resolve() : this._getInfo()).then(() => {
return this._otherFeeds;
});
}
_getInfo() {
return VectorScalarClient.getIntegrationsForRoom(this._roomId, this._scalarToken).then(integrations => {
this._otherFeeds = [];
for (var integration of integrations) {
if (integration.self) continue; // skip - we're not looking for ones we know about
if (integration.type == "rssbot") {
var urls = _.keys(integration.config.feeds);
for (var url of urls) {
this._otherFeeds.push({url: url, ownerId: integration.user_id});
}
}
}
return VectorScalarClient.getIntegration("rssbot", this._roomId, this._scalarToken);
}).then(info => {
this._info = info;
});
}
/*override*/
removeFromRoom(roomId) {
return VectorScalarClient.removeIntegration("rssbot", roomId, this._scalarToken);
}
}
module.exports = VectorRssBackbone;

View File

@ -1,31 +0,0 @@
var sdk = require("matrix-js-sdk");
var log = require("../../../util/LogService");
var StubbedSimpleBackbone = require("./StubbedSimpleBackbone");
/**
* Standalone (matrix) backbone for simple bots
*/
class HostedSimpleBackbone extends StubbedSimpleBackbone {
/**
* Creates a new standalone bot backbone
* @param {*} botConfig the configuration for the bot
*/
constructor(botConfig) {
super(botConfig);
this._config = botConfig;
this._client = sdk.createClient({
baseUrl: this._config.hosted.homeserverUrl,
accessToken: this._config.hosted.accessToken,
userId: this._config.userId,
});
}
/*override*/
removeFromRoom(roomId) {
log.info("HostedSimpleBackbone", "Removing " + this._config.userId + " from " + roomId);
return this._client.leave(roomId);
}
}
module.exports = HostedSimpleBackbone;

View File

@ -1,24 +0,0 @@
var IntegrationStub = require("../../generic_types/IntegrationStub");
/**
* Represents an RSS bot
*/
class SimpleBot extends IntegrationStub {
/**
* Creates a new RSS bot
* @param botConfig the bot configuration
* @param backbone the backbone powering this bot
*/
constructor(botConfig, backbone) {
super(botConfig);
this._backbone = backbone;
}
/*override*/
removeFromRoom(roomId) {
return this._backbone.removeFromRoom(roomId);
}
}
module.exports = SimpleBot;

View File

@ -1,27 +0,0 @@
var SimpleBot = require("./SimpleBot");
var VectorSimpleBackbone = require("./VectorSimpleBackbone");
var HostedSimpleBackbone = require("./HostedSimpleBackbone");
var UpstreamConfiguration = require("../../../UpstreamConfiguration");
var factory = (db, integrationConfig, roomId, scalarToken) => {
factory.validateConfig(integrationConfig);
if (integrationConfig.upstream) {
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
var backbone = new VectorSimpleBackbone(integrationConfig, upstreamToken);
return new SimpleBot(integrationConfig, backbone);
});
} else if (integrationConfig.hosted) {
var backbone = new HostedSimpleBackbone(integrationConfig);
return Promise.resolve(new SimpleBot(integrationConfig, backbone));
}
};
factory.validateConfig = (integrationConfig) => {
if (integrationConfig.upstream) {
if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream");
if (!UpstreamConfiguration.hasUpstream("vector")) throw new Error("Vector upstream not specified");
} else if (!integrationConfig.hosted) throw new Error("Unsupported configuration");
};
module.exports = factory;

View File

@ -1,24 +0,0 @@
/**
* Stubbed backbone for simple bots
*/
class StubbedSimpleBackbone {
/**
* Creates a new stubbed bot backbone
* @param {*} botConfig the configuration for the bot
*/
constructor(botConfig) {
this._config = botConfig;
}
/**
* Leaves a given Matrix room
* @param {string} roomId the room to leave
* @returns {Promise<>} resolves when completed
*/
removeFromRoom(roomId) {
throw new Error("Not implemented");
}
}
module.exports = StubbedSimpleBackbone;

View File

@ -1,28 +0,0 @@
var VectorScalarClient = require("../../../scalar/VectorScalarClient");
var log = require("../../../util/LogService");
var StubbedSimpleBackbone = require("./StubbedSimpleBackbone");
/**
* Vector backbone for simple bots
*/
class VectorSimpleBackbone extends StubbedSimpleBackbone {
/**
* Creates a new vector bot backbone
* @param {*} botConfig the configuration for the bot
* @param {string} upstreamScalarToken the upstream scalar token
*/
constructor(botConfig, upstreamScalarToken) {
super(botConfig);
this._config = botConfig;
this._upstreamToken = upstreamScalarToken;
}
/*override*/
removeFromRoom(roomId) {
log.info("VectorSimpleBackbone", "Removing " + this._config.userId + " from " + roomId);
return VectorScalarClient.removeIntegration(this._config.upstream.id, roomId, this._upstreamToken);
}
}
module.exports = VectorSimpleBackbone;

View File

@ -1,17 +0,0 @@
var Widget = require("../../generic_types/Widget");
/**
* Represents a simple widget
*/
class SimpleWidget extends Widget {
/**
* Creates a new simple widget
* @param widgetConfig the widget configuration
*/
constructor(widgetConfig) {
super(widgetConfig);
}
}
module.exports = SimpleWidget;

View File

@ -1,13 +0,0 @@
var SimpleWidget = require("./SimpleWidget");
var Promise = require("bluebird");
var factory = (db, integrationConfig, roomId, scalarToken) => {
factory.validateConfig(integrationConfig);
return Promise.resolve(new SimpleWidget(integrationConfig, roomId));
};
factory.validateConfig = (integrationConfig) => {
// Nothing to do
};
module.exports = factory;

View File

@ -1,63 +0,0 @@
/**
* Stubbed/placeholder Travis CI backbone
*/
class StubbedTravisCiBackbone {
/**
* Creates a new stubbed RSS backbone
*/
constructor() {
}
/**
* Gets the user ID for this backbone
* @returns {Promise<string>} resolves to the user ID
*/
getUserId() {
throw new Error("Not implemented");
}
/**
* Gets the repository templates for this backbone
* @returns {Promise<{repoKey:string,template:string}[]>} resolves to the collection of repositories and their templates
*/
getRepos() {
throw new Error("Not implemented");
}
/**
* Gets the immutable repository templates for this backbone (set by other users)
* @returns {Promise<{repoKey:string,template:string,ownerId:string}[]>} resolves to the collection of repositories and their templates
*/
getImmutableRepos() {
throw new Error("Not implemented");
}
/**
* Sets the new repository templates for this backbone
* @param {{repoKey:string,template:string}[]} newRepos the new templates for the repositories
* @returns {Promise<>} resolves when complete
*/
setRepos(newRepos) {
throw new Error("Not implemented");
}
/**
* Gets the webhook url for this backbone
* @returns {Promise<string>} resolves to the webhook URL
*/
getWebhookUrl() {
throw new Error("Not implemented");
}
/**
* Removes the bot from the given room
* @param {string} roomId the room ID to remove the bot from
* @returns {Promise<>} resolves when completed
*/
removeFromRoom(roomId) {
throw new Error("Not implemented");
}
}
module.exports = StubbedTravisCiBackbone;

View File

@ -1,53 +0,0 @@
var ComplexBot = require("../../generic_types/ComplexBot");
/**
* Represents a Travis CI bot
*/
class TravisCiBot extends ComplexBot {
/**
* Creates a new Travis CI bot
* @param botConfig the bot configuration
* @param backbone the backbone powering this bot
*/
constructor(botConfig, backbone) {
super(botConfig);
this._backbone = backbone;
}
/*override*/
getUserId() {
return this._backbone.getUserId();
}
/*override*/
getState() {
var response = {
repoTemplates: [],
immutableRepoTemplates: [],
webhookUrl: ""
};
return this._backbone.getRepos().then(templates => {
response.repoTemplates = templates;
return this._backbone.getImmutableRepos();
}).then(immutable => {
response.immutableRepoTemplates = immutable;
return this._backbone.getWebhookUrl();
}).then(url => {
response.webhookUrl = url;
return response;
});
}
/*override*/
removeFromRoom(roomId) {
return this._backbone.removeFromRoom(roomId);
}
/*override*/
updateState(newState) {
return this._backbone.setRepos(newState.repoTemplates).then(() => this.getState());
}
}
module.exports = TravisCiBot;

View File

@ -1,20 +0,0 @@
var TravisCiBot = require("./TravisCiBot");
var VectorTravisCiBackbone = require("./VectorTravisCiBackbone");
var UpstreamConfiguration = require("../../../UpstreamConfiguration");
var factory = (db, integrationConfig, roomId, scalarToken) => {
factory.validateConfig(integrationConfig);
return db.getUpstreamToken(scalarToken).then(upstreamToken => {
var backbone = new VectorTravisCiBackbone(roomId, upstreamToken);
return new TravisCiBot(integrationConfig, backbone);
});
};
factory.validateConfig = (integrationConfig) => {
if (!integrationConfig.upstream) throw new Error("Unsupported configuration");
if (integrationConfig.upstream.type !== "vector") throw new Error("Unsupported upstream");
if (!UpstreamConfiguration.hasUpstream("vector")) throw new Error("Vector upstream not specified");
};
module.exports = factory;

View File

@ -1,108 +0,0 @@
var StubbedTravisCiBackbone = require("./StubbedTravisCiBackbone");
var VectorScalarClient = require("../../../scalar/VectorScalarClient");
var _ = require("lodash");
var log = require("../../../util/LogService");
/**
* Backbone for Travis CI bots running on vector.im through scalar
*/
class VectorTravisCiBackbone extends StubbedTravisCiBackbone {
/**
* Creates a new Vector Travis CI backbone
* @param {string} roomId the room ID to manage
* @param {string} upstreamScalarToken the vector scalar token
*/
constructor(roomId, upstreamScalarToken) {
super();
this._roomId = roomId;
this._scalarToken = upstreamScalarToken;
this._info = null;
this._otherTemplates = [];
}
/*override*/
getUserId() {
return (this._info ? Promise.resolve() : this._getInfo()).then(() => {
return this._info.bot_user_id;
});
}
/*override*/
getRepos() {
return (this._info ? Promise.resolve() : this._getInfo()).then(() => {
if (this._info.integrations.length == 0) return [];
var rooms = _.keys(this._info.integrations[0].config.rooms);
if (rooms.indexOf(this._roomId) === -1) return [];
var repos = _.keys(this._info.integrations[0].config.rooms[this._roomId].repos);
return _.map(repos, r => {
return {repoKey: r, template: this._info.integrations[0].config.rooms[this._roomId].repos[r].template};
});
});
}
/*override*/
getImmutableRepos() {
return (this._info ? Promise.resolve() : this._getInfo()).then(() => {
return this._otherTemplates;
});
}
/*override*/
setRepos(newRepos) {
var config = {};
config[this._roomId] = {repos: {}};
for (var repo of newRepos) config[this._roomId].repos[repo.repoKey] = {template: repo.template};
return VectorScalarClient.configureIntegration("travis-ci", this._scalarToken, {
rooms: config
});
}
/*override*/
getWebhookUrl() {
// string
return (this._info ? Promise.resolve() : this._getInfo()).then(() => {
if (this._info.integrations.length == 0) return "";
return this._info.integrations[0].config.webhook_url;
});
}
_getInfo() {
return VectorScalarClient.getIntegrationsForRoom(this._roomId, this._scalarToken).then(integrations => {
this._otherTemplates = [];
for (var integration of integrations) {
if (integration.self) continue; // skip - we're not looking for ones we know about
if (integration.type == "travis-ci") {
var roomIds = _.keys(integration.config.rooms);
if (roomIds.length === 0) continue;
if (roomIds.length !== 1) log.warn("VectorTravisCiBackbone", "Expected 1 room but found " + roomIds.length);
var roomConfig = integration.config.rooms[roomIds[0]];
var repositories = _.keys(roomConfig.repos);
for (var repo of repositories) {
this._otherTemplates.push({
repoKey: repo,
template: roomConfig.repos[repo].template,
ownerId: integration.user_id
});
}
}
}
return VectorScalarClient.getIntegration("travis-ci", this._roomId, this._scalarToken);
}).then(info => {
this._info = info;
});
}
/*override*/
removeFromRoom(roomId) {
return VectorScalarClient.removeIntegration("travis-ci", roomId, this._scalarToken);
}
}
module.exports = VectorTravisCiBackbone;

View File

@ -1,78 +0,0 @@
var config = require("config");
var log = require("../util/LogService");
var fs = require("fs");
var path = require("path");
var _ = require("lodash");
var IntegrationImpl = require("./impl");
log.info("Integrations", "Discovering integrations");
var searchPath = path.join(process.cwd(), "config", "integrations");
var files = _.filter(fs.readdirSync(searchPath), f => !fs.statSync(path.join(searchPath, f)).isDirectory() && f.endsWith(".yaml"));
var currentEnv = config.util.initParam("NODE_ENV", "development");
if (currentEnv !== "development" && currentEnv !== "production")
throw new Error("Unknown node environment: " + currentEnv);
var configs = {};
for (var file of files) {
if (file.endsWith("_development.yaml") || file.endsWith("_production.yaml")) {
if (!file.endsWith("_" + currentEnv + ".yaml")) continue;
var fileName = file.replace("_development.yaml", "").replace("_production.yaml", "") + ".yaml";
if (!configs[fileName]) configs[fileName] = {};
configs[fileName]["alt"] = config.util.parseFile(path.join(searchPath, file)) || {};
} else {
if (!configs[file]) configs[file] = {};
configs[file]["defaults"] = config.util.parseFile(path.join(searchPath, file)) || {};
}
}
var keys = _.keys(configs);
log.info("Integrations", "Discovered " + keys.length + " integrations. Parsing definitions...");
var linear = [];
var byUserId = {};
var byType = {};
for (var key of keys) {
log.info("Integrations", "Preparing " + key);
if (!configs[key].defaults) configs[key].defaults = {};
var merged = config.util.extendDeep(configs[key].defaults, configs[key].alt);
if (!merged['enabled']) {
log.warn("Integrations", "Integration " + key + " is not enabled - skipping");
continue;
}
var factory = IntegrationImpl.getFactory(merged);
if (!factory) {
log.warn("Integrations", "Integration " + key + " does not have an associated factory - skipping");
continue;
}
try {
factory.validateConfig(merged);
} catch (err) {
log.error("Integrations", "Error while validating integration " + key + " - skipping");
log.error("Integrations", err);
continue;
}
linear.push(merged);
if (merged['userId'])
byUserId[merged['userId']] = merged;
if (!byType[merged['type']])
byType[merged['type']] = {};
if (byType[merged['type']][merged['integrationType']])
throw new Error("Duplicate type " + merged['type'] + " (" + merged['integrationType'] + ") at key " + key);
byType[merged['type']][merged['integrationType']] = merged;
}
log.info("Integrations", "Loaded " + linear.length + " integrations");
module.exports = {
all: linear,
byUserId: byUserId,
byType: byType
};

View File

@ -1,66 +0,0 @@
var sdk = require("matrix-js-sdk");
var log = require("../util/LogService");
/**
* Dimension demo bot. Doesn't do anything except show how to add a self-hosted bot to Dimension
*/
class DemoBot {
constructor(homeserverUrl, userId, accessToken) {
this._rooms = [];
log.info("DemoBot", "Constructing bot as " + userId);
this._client = sdk.createClient({
baseUrl: homeserverUrl,
accessToken: accessToken,
userId: userId,
});
this._client.on('event', event => {
if (event.getType() !== "m.room.member") return;
if (event.getStateKey() != this._client.credentials.userId) return;
if (event.getContent().membership === 'invite') {
if (this._rooms.indexOf(event.getRoomId()) !== -1) return;
log.info("DemoBot", "Joining " + event.getRoomId());
this._client.joinRoom(event.getRoomId()).then(() => {
this._recalculateRooms();
this._client.sendMessage(event.getRoomId(), {
msgtype: "m.notice",
body: "Hello! I'm a small bot that acts as an example for how to set up your own bot on Dimension."
});
});
} else this._recalculateRooms();
});
}
start() {
log.info("DemoBot", "Starting bot");
this._client.startClient();
this._client.on('sync', state => {
if (state == 'PREPARED') this._recalculateRooms();
});
}
_recalculateRooms() {
var rooms = this._client.getRooms();
this._rooms = [];
for (var room of rooms) {
var me = room.getMember(this._client.credentials.userId);
if (!me) continue;
if (me.membership == "invite") {
this._client.joinRoom(room.roomId);
continue;
}
if (me.membership != "join") continue;
this._rooms.push(room.roomId);
}
log.verbose("DemoBot", "Currently in " + this._rooms.length + " rooms");
}
}
module.exports = DemoBot;

View File

@ -1,83 +1,17 @@
var request = require('request');
var log = require("../util/LogService");
var dns = require("dns-then");
var Promise = require("bluebird");
var config = require("config");
/**
* Represents a lightweight matrix client with minimal functionality
*/
class MatrixLiteClient {
/**
* Creates a new matrix client
* @param {OpenID} openId the open ID to use
*/
constructor(openId) {
this._openId = openId;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var helpers_1 = require("./helpers");
var MatrixLiteClient = /** @class */ (function () {
function MatrixLiteClient(homeserverName, accessToken) {
this.homeserverName = homeserverName;
this.accessToken = accessToken;
}
/**
* Gets the Matrix User ID that owns this open ID
* @return {Promise<string>} resolves to the mxid
*/
getSelfMxid() {
return this._do("GET", "/_matrix/federation/v1/openid/userinfo", /*qs=*/null, /*body=*/null, /*allowSelfSigned=*/true).then((response, body) => {
var json = JSON.parse(response.body);
return json['sub'];
MatrixLiteClient.prototype.getUrlPreview = function (url) {
return helpers_1.doFederatedApiCall("GET", this.homeserverName, "/_matrix/media/r0/preview_url", { access_token: this.accessToken, url: url }).then(function (response) {
return response;
});
}
/**
* Gets a URL preview from the media repo (as provided by the default homeserver)
* @param {string} url The URL to get the preview of
* @return {Promise<*>} resolves to the raw URL preview
*/
static getUrlPreview(url) {
return MatrixLiteClient._do(config.homeserver.name, config.homeserver.accessToken, "GET", "/_matrix/media/r0/preview_url", {url: url}).then((response, body) => {
return JSON.parse(response.body);
});
}
_do(method, endpoint, qs = null, body = null, allowSelfSigned = false) {
return MatrixLiteClient._do(this._openId.matrix_server_name, this._openId.access_token, method, endpoint, qs, body, allowSelfSigned);
}
static _do(serverName, accessToken, method, endpoint, qs = null, body = null, allowSelfSigned = false) {
// HACK: We have to wrap the dns promise in a Bluebird promise just to make sure it works
var dnsPromise = dns.resolveSrv("_matrix._tcp." + serverName);
return Promise.resolve(dnsPromise).then(records => {
if (records && records.length > 0)
serverName = records[0].name + ":" + records[0].port;
}, err => {
log.warn("MatrixLiteClient", "Failed to lookup SRV for " + serverName + " - assuming none available.");
log.warn("MatrixLiteClient", err);
}).then(() => {
var url = "https://" + serverName + endpoint;
log.verbose("MatrixLiteClient", "Performing request: " + url);
if (!qs) qs = {};
if (accessToken) qs['access_token'] = accessToken;
var params = {
url: url,
method: method,
form: body,
qs: qs,
rejectUnauthorized: !allowSelfSigned
};
return new Promise((resolve, reject) => {
request(params, (err, response, body) => {
if (err) {
log.error("MatrixLiteClient", err);
reject(err);
} else resolve(response, body);
});
});
});
}
}
module.exports = MatrixLiteClient;
};
return MatrixLiteClient;
}());
exports.MatrixLiteClient = MatrixLiteClient;
//# sourceMappingURL=MatrixLiteClient.js.map

View File

@ -1,58 +1,36 @@
var request = require('request');
var log = require("../util/LogService");
var config = require("config");
var UpstreamConfiguration = require("../UpstreamConfiguration");
/**
* Represents a scalar client
*/
class ScalarClient {
/**
* Creates a new Scalar client
*/
constructor() {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Promise = require("bluebird");
var request = require("request");
var matrix_js_snippets_1 = require("matrix-js-snippets");
var ScalarClient = /** @class */ (function () {
function ScalarClient(upstream) {
this.upstream = upstream;
}
/**
* Registers for a scalar token
* @param {OpenID} openId the open ID to register
* @returns {Promise<string>} resolves to a scalar token
*/
register(openId) {
return this._do("POST", "/register", null, openId).then((response, body) => {
if (response.statusCode !== 200) {
log.error("ScalarClient", response.body);
return Promise.reject(response.body);
}
return response.body['scalar_token'];
});
}
// TODO: Merge this, VectorScalarClient, and MatrixLiteClient into a base class
_do(method, endpoint, qs = null, body = null) {
// TODO: Generify URL
var url = UpstreamConfiguration.getUpstream("vector").url + endpoint;
log.verbose("ScalarClient", "Performing request: " + url);
var params = {
url: url,
method: method,
json: body,
qs: qs
};
return new Promise((resolve, reject) => {
request(params, (err, response, body) => {
ScalarClient.prototype.register = function (openId) {
var _this = this;
return new Promise(function (resolve, reject) {
request({
method: "POST",
url: _this.upstream.scalarUrl + "/register",
json: openId,
}, function (err, res, _body) {
if (err) {
log.error("ScalarClient", err);
matrix_js_snippets_1.LogService.error("ScalarClient", "Error registering for token");
matrix_js_snippets_1.LogService.error("ScalarClient", err);
reject(err);
} else resolve(response, body);
}
else if (res.statusCode !== 200) {
matrix_js_snippets_1.LogService.error("ScalarClient", "Got status code " + res.statusCode + " while registering for token");
reject(new Error("Could not get token"));
}
else {
resolve(res.body);
}
});
});
}
}
module.exports = new ScalarClient();
};
return ScalarClient;
}());
exports.ScalarClient = ScalarClient;
//# sourceMappingURL=ScalarClient.js.map

View File

@ -1,260 +0,0 @@
var request = require('request');
var log = require("../util/LogService");
var config = require("config");
var UpstreamConfiguration = require("../UpstreamConfiguration");
/**
* Represents a scalar client for vector.im
*/
class VectorScalarClient {
/**
* Creates a new vector.im Scalar client
*/
constructor() {
}
/**
* Registers for a scalar token
* @param {OpenID} openId the open ID to register
* @returns {Promise<string>} resolves to a scalar token
*/
register(openId) {
return this._do("POST", "/register", null, openId).then((response, body) => {
var json = JSON.parse(response.body);
return json['scalar_token'];
});
}
/**
* Removes a scalar integration
* @param {string} type the type of integration to remove
* @param {string} roomId the room ID to remove it from
* @param {string} scalarToken the upstream scalar token
* @return {Promise<>} resolves when complete
*/
removeIntegration(type, roomId, scalarToken) {
return this._do("POST", "/removeIntegration", {scalar_token: scalarToken}, {
type: type,
room_id: roomId
}).then((response, body) => {
if (response.statusCode !== 200) {
log.error("VectorScalarClient", response.body);
return Promise.reject(response.body);
}
// no success processing
});
}
/**
* Configures an Integration on Vector
* @param {string} type the integration tpye
* @param {string} scalarToken the scalar token
* @param {*} config the config to POST to the service
* @return {Promise<>} resolves when completed
*/
configureIntegration(type, scalarToken, config) {
return this._do("POST", "/integrations/" + type + "/configureService", {scalar_token: scalarToken}, config).then((response, body) => {
if (response.statusCode !== 200) {
log.error("VectorScalarClient", response.body);
return Promise.reject(response.body);
}
// no success processing
});
}
/**
* Gets all of the integrations currently in a room
* @param {string} roomId the room ID
* @param {string} scalarToken the scalar token to use
* @returns {Promise<*[]>} resolves a collection of integrations
*/
getIntegrationsForRoom(roomId, scalarToken) {
return this._do("POST", "/integrations", {scalar_token: scalarToken}, {RoomId: roomId}).then((response, body) => {
if (response.statusCode !== 200) {
log.error("VectorScalarClient", response.body);
return Promise.reject(response.body);
}
return response.body.integrations;
});
}
/**
* Gets information for an integration
* @param {string} type the type to lookup
* @param {string} roomId the room ID to look in
* @param {string} scalarToken the scalar token
* @return {Promise<{bot_user_id:string,integrations:[]}>} resolves to the integration information
*/
getIntegration(type, roomId, scalarToken) {
return this._do("POST", "/integrations/" + type, {scalar_token: scalarToken}, {room_id: roomId}).then((response, body) => {
if (response.statusCode !== 200) {
log.error("VectorScalarClient", response.body);
return Promise.reject(response.body);
}
return response.body;
});
}
/**
* Gets a list of supported IRC networks
* @param {string} scalarToken the scalar token
* @returns {Promise<{rid: string, title: string, domain: string, id: string}[]>} resolves to the list of IRC networks
*/
getIrcNetworks(scalarToken) {
return this._do("GET", "/bridges/irc/_matrix/provision/querynetworks", {scalar_token: scalarToken}).then((response, body) => {
if (response.statusCode !== 200) {
log.error("VectorScalarClient", response.body);
return Promise.reject(response.body);
}
response.body = JSON.parse(response.body);
var results = [];
for (var network of response.body["replies"]) {
var result = {
rid: network["rid"],
// Assumption: All networks have 1 server from vector
id: network["response"]["servers"][0]["network_id"],
title: network["response"]["servers"][0]["desc"],
domain: network["response"]["servers"][0]["fields"]["domain"]
};
results.push(result);
}
return results;
});
}
/**
* Gets a list of all linked IRC channels for a given room
* @param {string} roomId the room ID to look in
* @param {string} scalarToken the scalar token
* @returns {Promise<{rid: string, server: string, channel: string}>} resolves to a list of linked channels
*/
getIrcLinks(roomId, scalarToken) {
return this._do("GET", "/bridges/irc/_matrix/provision/listlinks/" + roomId, {scalar_token: scalarToken}).then((response, body) => {
if (response.statusCode !== 200) {
log.error("VectorScalarClient", response.body);
return Promise.reject(response.body);
}
response.body = JSON.parse(response.body);
var results = [];
for (var linkContainer of response.body["replies"]) {
for (var link of linkContainer["response"]) {
results.push({
rid: linkContainer["rid"],
server: link["remote_room_server"],
channel: link["remote_room_channel"]
});
}
}
return results;
});
}
/**
* Gets a list of operators in a particular channel on a particular network
* @param {string} rid the network ID
* @param {string} networkServer the server that has the channel on it
* @param {string} channel the channel to look up, with prefix
* @param {string} scalarToken the scalar token
* @returns {Promise<string[]>} resolves to a list of operators in the channel
*/
getIrcOperators(rid, networkServer, channel, scalarToken) {
return this._do("POST", "/bridges/irc/_matrix/provision/querylink", {scalar_token: scalarToken, rid: rid}, {
remote_room_server: networkServer,
remote_room_channel: channel
}).then((response, body) => {
if (response.statusCode !== 200) {
log.error("VectorScalarClient", response.body);
return Promise.reject(response.body);
}
if (response.body["replies"]) {
return response.body["replies"][0]["response"]["operators"];
} else return Promise.reject("No operators could be found");
});
}
/**
* Requests an operator for permission to link an IRC channel to a matrix room
* @param {string} rid the network ID
* @param {string} roomId the matrix room ID
* @param {string} networkServer the server that has the channel on it
* @param {string} channel the channel to look up, with prefix
* @param {string} operator the channel operator's nick
* @param {string} scalarToken the scalar token
* @returns {Promise<>} resolves when completed
*/
addIrcLink(rid, roomId, networkServer, channel, operator, scalarToken) {
return this._do("POST", "/bridges/irc/_matrix/provision/link", {rid: rid, scalar_token: scalarToken}, {
matrix_room_id: roomId,
remote_room_channel: channel,
remote_room_server: networkServer,
op_nick: operator
}).then((response, body) => {
if (response.statusCode !== 200) {
log.error("VectorScalarClient", response.body);
return Promise.reject(response.body);
}
return {status: 'ok'};
})
}
/**
* Removes a channel link from a Matrix room
* @param {string} rid the network ID
* @param {string} roomId the matrix room ID
* @param {string} networkServer the server that has the channel on it
* @param {string} channel the channel to remove, with prefix
* @param {string} scalarToken the scalar token
* @returns {Promise<>} resolves when completed
*/
removeIrcLink(rid, roomId, networkServer, channel, scalarToken) {
return this._do("POST", "/bridges/irc/_matrix/provision/unlink", {rid: rid, scalar_token: scalarToken}, {
matrix_room_id: roomId,
remote_room_channel: channel,
remote_room_server: networkServer
}).then((response, body) => {
if (response.statusCode !== 200) {
log.error("VectorScalarClient", response.body);
return Promise.reject(response.body);
}
return {status: 'ok'};
})
}
_do(method, endpoint, qs = null, body = null) {
var url = UpstreamConfiguration.getUpstream("vector").url + endpoint;
log.verbose("VectorScalarClient", "Performing request: " + url);
var params = {
url: url,
method: method,
json: body,
qs: qs
};
return new Promise((resolve, reject) => {
request(params, (err, response, body) => {
if (err) {
log.error("VectorScalarClient", err);
reject(err);
} else resolve(response, body);
});
});
}
}
module.exports = new VectorScalarClient();

View File

@ -1,120 +0,0 @@
var DBMigrate = require("db-migrate");
var log = require("./../util/LogService");
var Sequelize = require('sequelize');
var dbConfig = require("../../config/database.json");
var moment = require("moment");
/**
* Primary storage for Dimension.
*/
class DimensionStore {
constructor() {
this._orm = null;
}
/**
* Prepares the store for use
*/
prepare() {
var env = process.env.NODE_ENV || "development";
log.info("DimensionStore", "Running migrations");
return new Promise((resolve, reject)=> {
var dbMigrate = DBMigrate.getInstance(true, {
config: "./config/database.json",
env: env
});
dbMigrate.up().then(() => {
var dbConfigEnv = dbConfig[env];
if (!dbConfigEnv) throw new Error("Could not find DB config for " + env);
var opts = {
host: dbConfigEnv.host || 'localhost',
dialect: 'sqlite',
pool: {
max: 5,
min: 0,
idle: 10000
},
storage: dbConfigEnv.filename,
logging: i => log.verbose("DimensionStore [SQL]", i)
};
this._orm = new Sequelize(dbConfigEnv.database || 'dimension', dbConfigEnv.username, dbConfigEnv.password, opts);
this._bindModels();
resolve();
}, err => {
log.error("DimensionStore", err);
reject(err);
}).catch(err => {
log.error("DimensionStore", err);
reject(err);
});
});
}
_bindModels() {
// Models
this.__Tokens = this._orm.import(__dirname + "/models/tokens");
// Relationships
}
/**
* Creates a new Scalar token
* @param {string} mxid the matrix user id
* @param {OpenID} openId the open ID
* @param {string} scalarToken the token associated with the user
* @param {String?} upstreamToken the upstream scalar token (optional)
* @returns {Promise<>} resolves when complete
*/
createToken(mxid, openId, scalarToken, upstreamToken) {
return this.__Tokens.create({
matrixUserId: mxid,
matrixServerName: openId.matrix_server_name,
matrixAccessToken: openId.access_token,
scalarToken: scalarToken,
upstreamToken: upstreamToken,
expires: moment().add(openId.expires_in, 'seconds').toDate()
});
}
/**
* Checks to determine if a token is valid or not
* @param {string} scalarToken the scalar token to check
* @returns {Promise<>} resolves if valid, rejected otherwise
*/
checkToken(scalarToken) {
return this.__Tokens.find({where: {scalarToken: scalarToken}}).then(token => {
if (!token) return Promise.reject(new Error("Token not found"));
//if (moment().isAfter(moment(token.expires))) return this.__Tokens.destroy({where: {id: token.id}}).then(() => Promise.reject());
return Promise.resolve();
});
}
/**
* Gets the user ID that owns a given token, returning a falsey value if no one owns the token.
* @param {string} scalarToken the scalar token to check
* @returns {Promise<String>} resolves to the user ID, or a falsey value if no user ID was found
*/
getTokenOwner(scalarToken) {
return this.__Tokens.find({where:{scalarToken: scalarToken}}).then(token => {
if (!token) return Promise.reject(new Error("Token not found"));
return Promise.resolve(token.matrixUserId);
});
}
/**
* Gets the upstream token for a given scalar token
* @param {string} scalarToken the scalar token to lookup
* @returns {Promise<string>} resolves to the upstream token, or null if not found
*/
getUpstreamToken(scalarToken) {
return this.__Tokens.find({where: {scalarToken: scalarToken}}).then(token => {
if (!token) return null;
return token.upstreamToken;
});
}
}
module.exports = DimensionStore;

View File

@ -1,45 +0,0 @@
module.exports = function (sequelize, DataTypes) {
return sequelize.define('tokens', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true,
field: 'id'
},
matrixUserId: {
type: DataTypes.STRING,
allowNull: false,
field: 'matrixUserId'
},
matrixServerName: {
type: DataTypes.STRING,
allowNull: false,
field: 'matrixServerName'
},
matrixAccessToken: {
type: DataTypes.STRING,
allowNull: false,
field: 'matrixAccessToken'
},
scalarToken: {
type: DataTypes.STRING,
allowNull: false,
field: 'scalarToken'
},
upstreamToken: {
type: DataTypes.STRING,
allowNull: true,
field: 'upstreamToken'
},
expires: {
type: DataTypes.TIME,
allowNull: false,
field: 'expires'
}
}, {
tableName: 'tokens',
underscored: false,
timestamps: false
});
};

View File

@ -1,100 +0,0 @@
var winston = require("winston");
var chalk = require("chalk");
var config = require("config");
var fs = require('fs');
var moment = require('moment');
try {
fs.mkdirSync('logs')
} catch (err) {
if (err.code !== 'EEXIST') throw err
}
const TERM_COLORS = {
error: "red",
warn: "yellow",
info: "blue",
verbose: "white",
silly: "grey",
};
function winstonColorFormatter(options) {
options.level = chalk[TERM_COLORS[options.level]](options.level);
return winstonFormatter(options);
}
function winstonFormatter(options) {
return options.timestamp() + ' ' + options.level + ' ' + (options.message ? options.message : '') +
(options.meta && Object.keys(options.meta).length ? '\n\t' + JSON.stringify(options.meta) : '' );
}
function getTimestamp() {
return moment().format('MMM-D-YYYY HH:mm:ss.SSS Z');
}
var loggingConfig = config.get('logging');
var transports = [];
transports.push(new (winston.transports.File)({
json: false,
name: "file",
filename: loggingConfig.file,
timestamp: getTimestamp,
formatter: winstonFormatter,
level: loggingConfig.fileLevel,
maxsize: loggingConfig.rotate.size,
maxFiles: loggingConfig.rotate.count,
zippedArchive: false
}));
if (loggingConfig.console) {
transports.push(new (winston.transports.Console)({
json: false,
name: "console",
timestamp: getTimestamp,
formatter: winstonColorFormatter,
level: loggingConfig.consoleLevel
}));
}
var log = new winston.Logger({
transports: transports,
levels: {
error: 0,
warn: 1,
info: 2,
verbose: 3,
silly: 4
}
});
function doLog(level, module, messageOrObject) {
if (typeof(messageOrObject) === 'object' && !(messageOrObject instanceof Error))
messageOrObject = JSON.stringify(messageOrObject);
var message = "[" + module + "] " + messageOrObject;
log.log(level, message);
}
class LogService {
static info(module, message) {
doLog('info', module, message);
}
static warn(module, message) {
doLog('warn', module, message);
}
static error(module, message) {
doLog('error', module, message);
}
static verbose(module, message) {
doLog('verbose', module, message);
}
static silly(module, message) {
doLog('silly', module, message);
}
}
module.exports = LogService;

View File

@ -13,6 +13,6 @@
]
},
"include": [
"./src-ts/**/*"
"./src/**/*"
]
}