You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Improved handling of external links and resources
This commit is contained in:
		| @@ -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() { | ||||
|   | ||||
| @@ -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() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user