mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 21:19:01 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			201 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const PROTOCOL_VERSION_MAJOR = 1;
 | 
						|
 | 
						|
function isDevEnv() {
 | 
						|
	const manifest = browser.runtime.getManifest();
 | 
						|
 | 
						|
	return manifest.name.endsWith('(dev)');
 | 
						|
}
 | 
						|
 | 
						|
class TriliumServerFacade {
 | 
						|
	constructor() {
 | 
						|
		this.triggerSearchForTrilium();
 | 
						|
 | 
						|
		// continually scan for changes (if e.g. desktop app is started after browser)
 | 
						|
		setInterval(() => this.triggerSearchForTrilium(), 60 * 1000);
 | 
						|
	}
 | 
						|
 | 
						|
	async sendTriliumSearchStatusToPopup() {
 | 
						|
		try {
 | 
						|
			await browser.runtime.sendMessage({
 | 
						|
				name: "trilium-search-status",
 | 
						|
				triliumSearch: this.triliumSearch
 | 
						|
			});
 | 
						|
		}
 | 
						|
		catch (e) {} // nothing might be listening
 | 
						|
	}
 | 
						|
 | 
						|
	setTriliumSearch(ts) {
 | 
						|
		this.triliumSearch = ts;
 | 
						|
 | 
						|
		this.sendTriliumSearchStatusToPopup();
 | 
						|
	}
 | 
						|
 | 
						|
	setTriliumSearchWithVersionCheck(json, resp) {
 | 
						|
		const [major, minor] = json.protocolVersion
 | 
						|
			.split(".")
 | 
						|
			.map(chunk => parseInt(chunk));
 | 
						|
 | 
						|
		// minor version is intended to be used to dynamically limit features provided by extension
 | 
						|
		// if some specific Trilium API is not supported. So far not needed.
 | 
						|
 | 
						|
		if (major !== PROTOCOL_VERSION_MAJOR) {
 | 
						|
			this.setTriliumSearch({
 | 
						|
				status: 'version-mismatch',
 | 
						|
				extensionMajor: PROTOCOL_VERSION_MAJOR,
 | 
						|
				triliumMajor: major
 | 
						|
			});
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			this.setTriliumSearch(resp);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	async triggerSearchForTrilium() {
 | 
						|
		this.setTriliumSearch({ status: 'searching' });
 | 
						|
 | 
						|
		const startingPort = await this.getStartingPort();
 | 
						|
 | 
						|
		for (let testedPort = startingPort; testedPort < startingPort + 10; testedPort++) {
 | 
						|
			try {
 | 
						|
				console.debug('Trying port ' + testedPort);
 | 
						|
 | 
						|
				const resp = await fetch(`http://127.0.0.1:${testedPort}/api/clipper/handshake`);
 | 
						|
 | 
						|
				const text = await resp.text();
 | 
						|
 | 
						|
				console.log("Received response:", text);
 | 
						|
 | 
						|
				const json = JSON.parse(text);
 | 
						|
 | 
						|
				if (json.appName === 'trilium') {
 | 
						|
					this.setTriliumSearchWithVersionCheck(json, {
 | 
						|
						status: 'found-desktop',
 | 
						|
						port: testedPort,
 | 
						|
						url: 'http://127.0.0.1:' + testedPort
 | 
						|
					});
 | 
						|
 | 
						|
					return;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			catch (error) {
 | 
						|
				// continue
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		const {triliumServerUrl} = await browser.storage.sync.get("triliumServerUrl");
 | 
						|
		const {authToken} = await browser.storage.sync.get("authToken");
 | 
						|
 | 
						|
		if (triliumServerUrl && authToken) {
 | 
						|
			try {
 | 
						|
				const resp = await fetch(triliumServerUrl + '/api/clipper/handshake', {
 | 
						|
					headers: {
 | 
						|
						Authorization: authToken
 | 
						|
					}
 | 
						|
				});
 | 
						|
 | 
						|
				const text = await resp.text();
 | 
						|
 | 
						|
				console.log("Received response:", text);
 | 
						|
 | 
						|
				const json = JSON.parse(text);
 | 
						|
 | 
						|
				if (json.appName === 'trilium') {
 | 
						|
					this.setTriliumSearchWithVersionCheck(json, {
 | 
						|
						status: 'found-server',
 | 
						|
						url: triliumServerUrl,
 | 
						|
						token: authToken
 | 
						|
					});
 | 
						|
 | 
						|
					return;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			catch (e) {
 | 
						|
				console.log("Request to the configured server instance failed with:", e);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// if all above fails it's not found
 | 
						|
		this.setTriliumSearch({ status: 'not-found' });
 | 
						|
	}
 | 
						|
 | 
						|
	async waitForTriliumSearch() {
 | 
						|
		return new Promise((res, rej) => {
 | 
						|
			const checkStatus = () => {
 | 
						|
				if (this.triliumSearch.status === "searching") {
 | 
						|
					setTimeout(checkStatus, 500);
 | 
						|
				}
 | 
						|
				else if (this.triliumSearch.status === 'not-found') {
 | 
						|
					rej(new Error("Trilium instance has not been found."));
 | 
						|
				}
 | 
						|
				else {
 | 
						|
					res();
 | 
						|
				}
 | 
						|
			};
 | 
						|
 | 
						|
			checkStatus();
 | 
						|
		});
 | 
						|
	}
 | 
						|
 | 
						|
	async getStartingPort() {
 | 
						|
		const {triliumDesktopPort} = await browser.storage.sync.get("triliumDesktopPort");
 | 
						|
 | 
						|
		if (triliumDesktopPort) {
 | 
						|
			return parseInt(triliumDesktopPort);
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			return isDevEnv() ? 37740 : 37840;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	async callService(method, path, body) {
 | 
						|
		const fetchOptions = {
 | 
						|
			method: method,
 | 
						|
			headers: {
 | 
						|
				'Content-Type': 'application/json'
 | 
						|
			},
 | 
						|
		};
 | 
						|
 | 
						|
		if (body) {
 | 
						|
			fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
 | 
						|
		}
 | 
						|
 | 
						|
		try {
 | 
						|
			await this.waitForTriliumSearch();
 | 
						|
 | 
						|
			fetchOptions.headers.Authorization = this.triliumSearch.token || "";
 | 
						|
			fetchOptions.headers['trilium-local-now-datetime'] = this.localNowDateTime();
 | 
						|
 | 
						|
			const url = this.triliumSearch.url + "/api/clipper/" + path;
 | 
						|
 | 
						|
			console.log(`Sending ${method} request to ${url}`);
 | 
						|
 | 
						|
			const response = await fetch(url, fetchOptions);
 | 
						|
 | 
						|
			if (!response.ok) {
 | 
						|
				throw new Error(await response.text());
 | 
						|
			}
 | 
						|
 | 
						|
			return await response.json();
 | 
						|
		}
 | 
						|
		catch (e) {
 | 
						|
			console.log("Sending request to trilium failed", e);
 | 
						|
 | 
						|
			toast('Your request failed because we could not contact Trilium instance. Please make sure Trilium is running and is accessible.');
 | 
						|
 | 
						|
			return null;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	localNowDateTime() {
 | 
						|
		const date = new Date();
 | 
						|
		const off = date.getTimezoneOffset();
 | 
						|
		const absoff = Math.abs(off);
 | 
						|
		return (new Date(date.getTime() - off * 60 * 1000).toISOString().substr(0,23).replace("T",  " ") +
 | 
						|
			(off > 0 ? '-' : '+') +
 | 
						|
			(absoff / 60).toFixed(0).padStart(2,'0') + ':' +
 | 
						|
			(absoff % 60).toString().padStart(2,'0'));
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
window.triliumServerFacade = new TriliumServerFacade();
 |