Improved handling of external links and resources

pull/41/head
Laurent Cozic 2017-10-26 20:58:27 +01:00
parent 31bfa72bca
commit bd8926958f
2 changed files with 66 additions and 35 deletions

View File

@ -13,6 +13,8 @@ class ResourceServer {
this.server_ = null;
this.logger_ = new Logger();
this.port_ = null;
this.linkHandler_ = null;
this.started_ = false;
}
setLogger(logger) {
@ -23,11 +25,19 @@ class ResourceServer {
return this.logger_;
}
started() {
return this.started_;
}
baseUrl() {
if (!this.port_) return '';
return 'http://127.0.0.1:' + this.port_;
}
setLinkHandler(handler) {
this.linkHandler_ = handler;
}
async start() {
this.port_ = await netUtils.findAvailablePort([9167, 9267, 8167, 8267]);
if (!this.port_) {
@ -52,18 +62,18 @@ class ResourceServer {
}
resourceId = resourceId[1];
if (!this.linkHandler_) throw new Error('No link handler is defined');
try {
let resource = await Resource.loadByPartialId(resourceId);
if (!resource.length) throw new Error('No resource with ID ' + resourceId);
if (resource.length > 2) throw new Error('More than one resource match ID ' + resourceId); // That's very unlikely
resource = resource[0];
if (resource.mime) response.setHeader('Content-Type', resource.mime);
writeResponse(await Resource.content(resource));
const done = await this.linkHandler_(resourceId, response);
if (!done) throw new Error('Unhandled resource: ' + resourceId);
} catch (error) {
response.setHeader('Content-Type', 'text/plain');
response.statusCode = 400;
writeResponse('Error: could not retrieve resource: ' + error.message);
response.write(error.message);
}
response.end();
});
this.server_.on('error', (error) => {
@ -73,6 +83,8 @@ class ResourceServer {
this.server_.listen(this.port_);
enableServerDestroy(this.server_);
this.started_ = true;
}
stop() {

View File

@ -3,6 +3,7 @@ import { Folder } from 'lib/models/folder.js';
import { Tag } from 'lib/models/tag.js';
import { BaseModel } from 'lib/base-model.js';
import { Note } from 'lib/models/note.js';
import { Resource } from 'lib/models/resource.js';
import { cliUtils } from './cli-utils.js';
import { reducer, defaultState } from 'lib/reducer.js';
import { reg } from 'lib/registry.js';
@ -46,10 +47,6 @@ class AppGui {
this.renderer_ = new Renderer(this.term(), this.rootWidget_);
this.renderer_.on('renderDone', async (event) => {
//if (this.widget('console').hasFocus) this.widget('console').resetCursor();
});
this.app_.on('modelAction', async (event) => {
await this.handleModelAction(event.action);
});
@ -609,48 +606,70 @@ class AppGui {
if (msg !== '') this.widget('statusBar').setItemAt(0, msg);
}
setupResourceServer() {
async setupResourceServer() {
const linkStyle = chalk.blue.underline;
const noteTextWidget = this.widget('noteText');
const resourceIdRegex = /^:\/[a-f0-9]+$/i
const regularUrlRenderer = (url) => {
if (!url) return url;
const l = url.toLowerCase();
if (l.indexOf('http://') === 0 || l.indexOf('https://') === 0 || l.indexOf('www.') === 0) {
return linkStyle(url);
}
return url;
}
const noteLinks = {};
// By default, before the server is started, only the regular
// URLs appear in blue.
noteTextWidget.markdownRendererOptions = {
linkUrlRenderer: (url) => {
linkUrlRenderer: (index, url) => {
if (resourceIdRegex.test(url)) {
return url;
} else if (url.indexOf('http://') === 0 || url.indexOf('https://') === 0) {
return linkStyle(url);
} else {
return regularUrlRenderer(url);
return url;
}
},
};
this.resourceServer_ = new ResourceServer();
this.resourceServer_.setLogger(this.app().logger());
this.resourceServer_.start().then(() => {
if (this.resourceServer_.baseUrl()) {
noteTextWidget.markdownRendererOptions = {
linkUrlRenderer: (url) => {
if (resourceIdRegex.test(url)) {
const resourceId = url.substr(2);
return linkStyle(this.resourceServer_.baseUrl() + '/' + resourceId.substr(0, 7));
} else {
return regularUrlRenderer(url);
}
},
};
this.resourceServer_.setLinkHandler(async (path, response) => {
const link = noteLinks[path];
if (link.type === 'url') {
response.writeHead(302, { 'Location': link.url });
return true;
}
if (link.type === 'resource') {
const resourceId = link.id;
let resource = await Resource.load(resourceId);
if (!resource) throw new Error('No resource with ID ' + resourceId); // Should be nearly impossible
if (resource.mime) response.setHeader('Content-Type', resource.mime);
response.write(await Resource.content(resource));
return true;
}
return false;
});
await this.resourceServer_.start();
if (!this.resourceServer_.started()) return;
noteTextWidget.markdownRendererOptions = {
linkUrlRenderer: (index, url) => {
if (!url) return url;
if (resourceIdRegex.test(url)) {
noteLinks[index] = {
type: 'resource',
id: url.substr(2),
};
} else {
noteLinks[index] = {
type: 'url',
url: url,
};
}
return linkStyle(this.resourceServer_.baseUrl() + '/' + index);
},
};
}
async start() {