mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	decoupled protected session holder from presentation stuff and similar things
This commit is contained in:
		| @@ -1,6 +1,7 @@ | ||||
| import treeService from '../services/tree.js'; | ||||
| import messagingService from '../services/messaging.js'; | ||||
| import server from '../services/server.js'; | ||||
| import utils from "../services/utils"; | ||||
|  | ||||
| const $dialog = $("#recent-notes-dialog"); | ||||
| const $searchInput = $('#recent-notes-search-input'); | ||||
| @@ -92,6 +93,14 @@ async function showDialog() { | ||||
|  | ||||
| setTimeout(reload, 100); | ||||
|  | ||||
| messagingService.subscribeToMessages(syncData => { | ||||
|     if (syncData.some(sync => sync.entityName === 'recent_notes')) { | ||||
|         console.log(utils.now(), "Reloading recent notes because of background changes"); | ||||
|  | ||||
|         reload(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| export default { | ||||
|     showDialog, | ||||
|     addRecentNote, | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| "use strict"; | ||||
|  | ||||
| import protectedSessionService from '../services/protected_session.js'; | ||||
| import protectedSessionHolder from '../services/protected_session_holder.js'; | ||||
| import utils from '../services/utils.js'; | ||||
| import server from '../services/server.js'; | ||||
|  | ||||
| @@ -77,7 +77,7 @@ addModule((function() { | ||||
|                 alert("Password has been changed. Trilium will be reloaded after you press OK."); | ||||
|  | ||||
|                 // password changed so current protected session is invalid and needs to be cleared | ||||
|                 protectedSessionService.resetProtectedSession(); | ||||
|                 protectedSessionHolder.resetProtectedSession(); | ||||
|             } | ||||
|             else { | ||||
|                 utils.showError(result.message); | ||||
| @@ -105,7 +105,7 @@ addModule((function() { | ||||
|         const protectedSessionTimeout = $protectedSessionTimeout.val(); | ||||
|  | ||||
|         settings.saveSettings(settingName, protectedSessionTimeout).then(() => { | ||||
|             protectedSessionService.setProtectedSessionTimeout(protectedSessionTimeout); | ||||
|             protectedSessionHolder.setProtectedSessionTimeout(protectedSessionTimeout); | ||||
|         }); | ||||
|  | ||||
|         return false; | ||||
|   | ||||
							
								
								
									
										13
									
								
								src/public/javascripts/services/bundle.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/public/javascripts/services/bundle.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| import ScriptContext from "./script_context"; | ||||
|  | ||||
| async function executeBundle(bundle) { | ||||
|     const apiContext = ScriptContext(bundle.note, bundle.allNotes); | ||||
|  | ||||
|     return await (function () { | ||||
|         return eval(`const apiContext = this; (async function() { ${bundle.script}\r\n})()`); | ||||
|     }.call(apiContext)); | ||||
| } | ||||
|  | ||||
| export default { | ||||
|     executeBundle | ||||
| } | ||||
| @@ -1,10 +1,10 @@ | ||||
| import treeService from './tree.js'; | ||||
| import protectedSessionService from './protected_session.js'; | ||||
| import protectedSessionHolder from './protected_session_holder.js'; | ||||
| import utils from './utils.js'; | ||||
|  | ||||
| function exportSubTree(noteId) { | ||||
|     const url = utils.getHost() + "/api/export/" + noteId + "?protectedSessionId=" | ||||
|         + encodeURIComponent(protectedSessionService.getProtectedSessionId()); | ||||
|         + encodeURIComponent(protectedSessionHolder.getProtectedSessionId()); | ||||
|  | ||||
|     utils.download(url); | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import noteDetailService from './note_detail.js'; | ||||
| import treeUtils from './tree_utils.js'; | ||||
| import utils from './utils.js'; | ||||
| import server from './server.js'; | ||||
| import bundleService from './bundle.js'; | ||||
|  | ||||
| // hot keys are active also inside inputs and content editables | ||||
| jQuery.hotkeys.options.filterInputAcceptingElements = false; | ||||
| @@ -209,7 +210,7 @@ $("#logout-button").toggle(!utils.isElectron()); | ||||
| $(document).ready(() => { | ||||
|     server.get("script/startup").then(scriptBundles => { | ||||
|         for (const bundle of scriptBundles) { | ||||
|             utils.executeBundle(bundle); | ||||
|             bundleService.executeBundle(bundle); | ||||
|         } | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -1,10 +1,9 @@ | ||||
| import treeService from './tree.js'; | ||||
| import noteDetailService from './note_detail.js'; | ||||
| import utils from './utils.js'; | ||||
| import recentNotesDialog from '../dialogs/recent_notes.js'; | ||||
|  | ||||
| const $changesToPushCount = $("#changes-to-push-count"); | ||||
|  | ||||
| const messageHandlers = []; | ||||
|  | ||||
| let ws; | ||||
| let lastSyncId; | ||||
| let lastPingTs; | ||||
| @@ -21,7 +20,11 @@ function logError(message) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| function messageHandler(event) { | ||||
| function subscribeToMessages(messageHandler) { | ||||
|     messageHandlers.push(messageHandler); | ||||
| } | ||||
|  | ||||
| function handleMessage(event) { | ||||
|     const message = JSON.parse(event.data); | ||||
|  | ||||
|     if (message.type === 'sync') { | ||||
| @@ -35,29 +38,10 @@ function messageHandler(event) { | ||||
|  | ||||
|         const syncData = message.data.filter(sync => sync.sourceId !== glob.sourceId); | ||||
|  | ||||
|         if (syncData.some(sync => sync.entityName === 'branches') | ||||
|             || syncData.some(sync => sync.entityName === 'notes')) { | ||||
|  | ||||
|             console.log(utils.now(), "Reloading tree because of background changes"); | ||||
|  | ||||
|             treeService.reload(); | ||||
|         for (const messageHandler of messageHandlers) { | ||||
|             messageHandler(syncData); | ||||
|         } | ||||
|  | ||||
|         if (syncData.some(sync => sync.entityName === 'notes' && sync.entityId === noteDetailService.getCurrentNoteId())) { | ||||
|             utils.showMessage('Reloading note because of background changes'); | ||||
|  | ||||
|             noteDetailService.reload(); | ||||
|         } | ||||
|  | ||||
|         if (syncData.some(sync => sync.entityName === 'recent_notes')) { | ||||
|             console.log(utils.now(), "Reloading recent notes because of background changes"); | ||||
|  | ||||
|             recentNotesDialog.reload(); | ||||
|         } | ||||
|  | ||||
|         // we don't detect image changes here since images themselves are immutable and references should be | ||||
|         // updated in note detail as well | ||||
|  | ||||
|         $changesToPushCount.html(message.changesToPushCount); | ||||
|     } | ||||
|     else if (message.type === 'sync-hash-check-failed') { | ||||
| @@ -74,7 +58,7 @@ function connectWebSocket() { | ||||
|     // use wss for secure messaging | ||||
|     const ws = new WebSocket(protocol + "://" + location.host); | ||||
|     ws.onopen = event => console.log(utils.now(), "Connected to server with WebSocket"); | ||||
|     ws.onmessage = messageHandler; | ||||
|     ws.onmessage = handleMessage; | ||||
|     ws.onclose = function(){ | ||||
|         // Try to reconnect in 5 seconds | ||||
|         setTimeout(() => connectWebSocket(), 5000); | ||||
| @@ -118,5 +102,6 @@ setTimeout(() => { | ||||
| }, 1000); | ||||
|  | ||||
| export default { | ||||
|     logError | ||||
|     logError, | ||||
|     subscribeToMessages | ||||
| }; | ||||
| @@ -1,8 +1,11 @@ | ||||
| import treeService from './tree.js'; | ||||
| import noteTypeService from './note_type.js'; | ||||
| import protectedSessionService from './protected_session.js'; | ||||
| import protectedSessionHolder from './protected_session_holder.js'; | ||||
| import utils from './utils.js'; | ||||
| import server from './server.js'; | ||||
| import messagingService from "./messaging.js"; | ||||
| import bundleService from "./bundle.js"; | ||||
|  | ||||
| const $noteTitle = $("#note-title"); | ||||
|  | ||||
| @@ -78,7 +81,7 @@ async function saveNoteIfChanged() { | ||||
|     await saveNoteToServer(note); | ||||
|  | ||||
|     if (note.detail.isProtected) { | ||||
|         protectedSessionService.touchProtectedSession(); | ||||
|         protectedSessionHolder.touchProtectedSession(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -222,7 +225,7 @@ async function loadNoteToEditor(noteId) { | ||||
|     await protectedSessionService.ensureProtectedSession(currentNote.detail.isProtected, false); | ||||
|  | ||||
|     if (currentNote.detail.isProtected) { | ||||
|         protectedSessionService.touchProtectedSession(); | ||||
|         protectedSessionHolder.touchProtectedSession(); | ||||
|     } | ||||
|  | ||||
|     // this might be important if we focused on protected note when not in protected note and we got a dialog | ||||
| @@ -251,7 +254,7 @@ async function loadNoteToEditor(noteId) { | ||||
|  | ||||
|         $noteDetailRender.html(bundle.html); | ||||
|  | ||||
|         utils.executeBundle(bundle); | ||||
|         bundleService.executeBundle(bundle); | ||||
|     } | ||||
|     else if (currentNote.detail.type === 'file') { | ||||
|         $noteDetailAttachment.show(); | ||||
| @@ -333,7 +336,7 @@ async function executeCurrentNote() { | ||||
|         if (currentNote.detail.mime.endsWith("env=frontend")) { | ||||
|             const bundle = await server.get('script/bundle/' + getCurrentNoteId()); | ||||
|  | ||||
|             utils.executeBundle(bundle); | ||||
|             bundleService.executeBundle(bundle); | ||||
|         } | ||||
|  | ||||
|         if (currentNote.detail.mime.endsWith("env=backend")) { | ||||
| @@ -360,9 +363,17 @@ $attachmentOpen.click(() => { | ||||
| function getAttachmentUrl() { | ||||
|     // electron needs absolute URL so we extract current host, port, protocol | ||||
|     return utils.getHost() + "/api/attachments/download/" + getCurrentNoteId() | ||||
|         + "?protectedSessionId=" + encodeURIComponent(protectedSessionService.getProtectedSessionId()); | ||||
|         + "?protectedSessionId=" + encodeURIComponent(protectedSessionHolder.getProtectedSessionId()); | ||||
| } | ||||
|  | ||||
| messagingService.subscribeToMessages(syncData => { | ||||
|     if (syncData.some(sync => sync.entityName === 'notes' && sync.entityId === getCurrentNoteId())) { | ||||
|         utils.showMessage('Reloading note because of background changes'); | ||||
|  | ||||
|         reload(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| $(document).ready(() => { | ||||
|     $noteTitle.on('input', () => { | ||||
|         noteChanged(); | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import treeService from './tree.js'; | ||||
| import noteDetail from './note_detail.js'; | ||||
| import utils from './utils.js'; | ||||
| import server from './server.js'; | ||||
| import protectedSessionHolder from './protected_session_holder.js'; | ||||
|  | ||||
| const $dialog = $("#protected-session-password-dialog"); | ||||
| const $passwordForm = $("#protected-session-password-form"); | ||||
| @@ -11,22 +12,11 @@ const $protectButton = $("#protect-button"); | ||||
| const $unprotectButton = $("#unprotect-button"); | ||||
|  | ||||
| let protectedSessionDeferred = null; | ||||
| let lastProtectedSessionOperationDate = null; | ||||
| let protectedSessionTimeout = null; | ||||
| let protectedSessionId = null; | ||||
|  | ||||
| $(document).ready(() => { | ||||
|     server.get('settings/all').then(settings => protectedSessionTimeout = settings.protected_session_timeout); | ||||
| }); | ||||
|  | ||||
| function setProtectedSessionTimeout(encSessTimeout) { | ||||
|     protectedSessionTimeout = encSessTimeout; | ||||
| } | ||||
|  | ||||
| function ensureProtectedSession(requireProtectedSession, modal) { | ||||
|     const dfd = $.Deferred(); | ||||
|  | ||||
|     if (requireProtectedSession && !isProtectedSessionAvailable()) { | ||||
|     if (requireProtectedSession && !protectedSessionHolder.isProtectedSessionAvailable()) { | ||||
|         protectedSessionDeferred = dfd; | ||||
|  | ||||
|         if (treeService.getCurrentNode().data.isProtected) { | ||||
| @@ -62,7 +52,7 @@ async function setupProtectedSession() { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     protectedSessionId = response.protectedSessionId; | ||||
|     protectedSessionHolder.setProtectedSessionId(response.protectedSessionId); | ||||
|  | ||||
|     $dialog.dialog("close"); | ||||
|  | ||||
| @@ -96,22 +86,6 @@ async function enterProtectedSession(password) { | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function getProtectedSessionId() { | ||||
|     return protectedSessionId; | ||||
| } | ||||
|  | ||||
| function resetProtectedSession() { | ||||
|     protectedSessionId = null; | ||||
|  | ||||
|     // most secure solution - guarantees nothing remained in memory | ||||
|     // since this expires because user doesn't use the app, it shouldn't be disruptive | ||||
|     utils.reloadApp(); | ||||
| } | ||||
|  | ||||
| function isProtectedSessionAvailable() { | ||||
|     return protectedSessionId !== null; | ||||
| } | ||||
|  | ||||
| async function protectNoteAndSendToServer() { | ||||
|     await ensureProtectedSession(true, true); | ||||
|  | ||||
| @@ -144,12 +118,6 @@ async function unprotectNoteAndSendToServer() { | ||||
|     noteDetail.setNoteBackgroundIfProtected(note); | ||||
| } | ||||
|  | ||||
| function touchProtectedSession() { | ||||
|     if (isProtectedSessionAvailable()) { | ||||
|         lastProtectedSessionOperationDate = new Date(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| async function protectSubTree(noteId, protect) { | ||||
|     await ensureProtectedSession(true, true); | ||||
|  | ||||
| @@ -167,24 +135,13 @@ $passwordForm.submit(() => { | ||||
|     return false; | ||||
| }); | ||||
|  | ||||
| setInterval(() => { | ||||
|     if (lastProtectedSessionOperationDate !== null && new Date().getTime() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) { | ||||
|         resetProtectedSession(); | ||||
|     } | ||||
| }, 5000); | ||||
|  | ||||
| $protectButton.click(protectNoteAndSendToServer); | ||||
| $unprotectButton.click(unprotectNoteAndSendToServer); | ||||
|  | ||||
| export default { | ||||
|     setProtectedSessionTimeout, | ||||
|     ensureProtectedSession, | ||||
|     resetProtectedSession, | ||||
|     isProtectedSessionAvailable, | ||||
|     protectNoteAndSendToServer, | ||||
|     unprotectNoteAndSendToServer, | ||||
|     getProtectedSessionId, | ||||
|     touchProtectedSession, | ||||
|     protectSubTree, | ||||
|     ensureDialogIsClosed | ||||
| }; | ||||
							
								
								
									
										55
									
								
								src/public/javascripts/services/protected_session_holder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/public/javascripts/services/protected_session_holder.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| import utils from "./utils.js"; | ||||
| import server from "./server.js"; | ||||
|  | ||||
| let lastProtectedSessionOperationDate = null; | ||||
| let protectedSessionTimeout = null; | ||||
| let protectedSessionId = null; | ||||
|  | ||||
| $(document).ready(() => { | ||||
|     server.get('settings/all').then(settings => protectedSessionTimeout = settings.protected_session_timeout); | ||||
| }); | ||||
|  | ||||
| setInterval(() => { | ||||
|     if (lastProtectedSessionOperationDate !== null && new Date().getTime() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) { | ||||
|         resetProtectedSession(); | ||||
|     } | ||||
| }, 5000); | ||||
|  | ||||
| function setProtectedSessionTimeout(encSessTimeout) { | ||||
|     protectedSessionTimeout = encSessTimeout; | ||||
| } | ||||
|  | ||||
| function getProtectedSessionId() { | ||||
|     return protectedSessionId; | ||||
| } | ||||
|  | ||||
| function setProtectedSessionId(id) { | ||||
|     protectedSessionId = id; | ||||
| } | ||||
|  | ||||
| function resetProtectedSession() { | ||||
|     protectedSessionId = null; | ||||
|  | ||||
|     // most secure solution - guarantees nothing remained in memory | ||||
|     // since this expires because user doesn't use the app, it shouldn't be disruptive | ||||
|     utils.reloadApp(); | ||||
| } | ||||
|  | ||||
| function isProtectedSessionAvailable() { | ||||
|     return protectedSessionId !== null; | ||||
| } | ||||
|  | ||||
| function touchProtectedSession() { | ||||
|     if (isProtectedSessionAvailable()) { | ||||
|         lastProtectedSessionOperationDate = new Date(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default { | ||||
|     getProtectedSessionId, | ||||
|     setProtectedSessionId, | ||||
|     resetProtectedSession, | ||||
|     isProtectedSessionAvailable, | ||||
|     setProtectedSessionTimeout, | ||||
|     touchProtectedSession | ||||
| }; | ||||
| @@ -1,11 +1,11 @@ | ||||
| import protectedSessionService from './protected_session.js'; | ||||
| import protectedSessionHolder from './protected_session_holder.js'; | ||||
| import utils from './utils.js'; | ||||
|  | ||||
| function getHeaders() { | ||||
|     let protectedSessionId = null; | ||||
|  | ||||
|     try { // this is because protected session might not be declared in some cases - like when it's included in migration page | ||||
|         protectedSessionId = protectedSessionService.getProtectedSessionId(); | ||||
|         protectedSessionId = protectedSessionHolder.getProtectedSessionId(); | ||||
|     } | ||||
|     catch(e) {} | ||||
|  | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import dragAndDropSetup from './drag_and_drop.js'; | ||||
| import linkService from './link.js'; | ||||
| import messagingService from './messaging.js'; | ||||
| import noteDetailService from './note_detail.js'; | ||||
| import protectedSessionService from './protected_session.js'; | ||||
| import protectedSessionHolder from './protected_session_holder.js'; | ||||
| import treeChangesService from './tree_changes.js'; | ||||
| import treeUtils from './tree_utils.js'; | ||||
| import utils from './utils.js'; | ||||
| @@ -777,7 +777,7 @@ async function createNote(node, parentNoteId, target, isProtected) { | ||||
|  | ||||
|     // if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted | ||||
|     // but this is quite weird since user doesn't see WHERE the note is being created so it shouldn't occur often | ||||
|     if (!isProtected || !protectedSessionService.isProtectedSessionAvailable()) { | ||||
|     if (!isProtected || !protectedSessionHolder.isProtectedSessionAvailable()) { | ||||
|         isProtected = false; | ||||
|     } | ||||
|  | ||||
| @@ -857,6 +857,16 @@ async function getBranch(branchId) { | ||||
|     return await treeCache.getBranch(branchId); | ||||
| } | ||||
|  | ||||
| messagingService.subscribeToMessages(syncData => { | ||||
|     if (syncData.some(sync => sync.entityName === 'branches') | ||||
|         || syncData.some(sync => sync.entityName === 'notes')) { | ||||
|  | ||||
|         console.log(utils.now(), "Reloading tree because of background changes"); | ||||
|  | ||||
|         reload(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| $(document).bind('keydown', 'ctrl+o', e => { | ||||
|     const node = getCurrentNode(); | ||||
|     const parentNoteId = node.data.parentNoteId; | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| //import messagingService from './messaging.js'; | ||||
| //import ScriptContext from './script_context.js'; | ||||
| import messagingService from './messaging.js'; | ||||
|  | ||||
| function reloadApp() { | ||||
|     window.location.reload(true); | ||||
| @@ -116,14 +115,6 @@ async function stopWatch(what, func) { | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| async function executeBundle(bundle) { | ||||
|     const apiContext = ScriptContext(bundle.note, bundle.allNotes); | ||||
|  | ||||
|     return await (function () { | ||||
|         return eval(`const apiContext = this; (async function() { ${bundle.script}\r\n})()`); | ||||
|     }.call(apiContext)); | ||||
| } | ||||
|  | ||||
| function formatValueWithWhitespace(val) { | ||||
|     return /[^\w_-]/.test(val) ? '"' + val + '"' : val; | ||||
| } | ||||
| @@ -263,7 +254,6 @@ export default { | ||||
|     isRootNode, | ||||
|     escapeHtml, | ||||
|     stopWatch, | ||||
|     executeBundle, | ||||
|     formatValueWithWhitespace, | ||||
|     formatLabel, | ||||
|     requireLibrary, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user