Add widget title API

Adds #130

This is backed by a homeserver's media repository.
This commit is contained in:
Travis Ralston 2017-12-11 13:17:54 -07:00
parent 41e6dd078d
commit c0336f65ca
4 changed files with 80 additions and 3 deletions

View File

@ -25,6 +25,11 @@ upstreams:
- name: vector
url: "https://scalar.vector.im/api"
# Homeserver configuration (used to proxy some requests to the homeserver for processing)
homeserver:
name: "t2bot.io"
accessToken: "something"
# IPs and CIDR ranges listed here will be blocked from being widgets.
# Note: Widgets may still be embedded with restricted content, although not through Dimension directly.
widgetBlacklist:

View File

@ -441,3 +441,20 @@ None of these are officially documented, and are subject to change.
```
*Note*: Invite person-specific bot on first repository
## GET `/api/widgets/title_lookup?curl=https://t2bot.io&scalar_token=...`
**Response**
```
{
"page_title_cache_item": {
"expires": "2017-12-12T20:11:00.70212938Z",
"cached_title": "t2bot.io",
"cached_response_err": ""
},
"error": {
"message": ""
},
"cached_response": false
}
```

View File

@ -27,6 +27,46 @@ class ScalarApi {
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) {

View File

@ -2,6 +2,7 @@ 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
@ -27,8 +28,22 @@ class MatrixLiteClient {
});
}
/**
* 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) {
var serverName = this._openId.matrix_server_name;
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 => {
@ -43,7 +58,7 @@ class MatrixLiteClient {
log.verbose("MatrixLiteClient", "Performing request: " + url);
if (!qs) qs = {};
qs['access_token'] = this._openId.access_token;
if (accessToken) qs['access_token'] = accessToken;
var params = {
url: url,