mirror of
https://github.com/zadam/trilium.git
synced 2025-01-18 04:59:56 +08:00
decoupled protected session holder from presentation stuff and similar things
This commit is contained in:
parent
47eb1e3e02
commit
ac1b06967f
12 changed files with 130 additions and 99 deletions
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue