decoupled protected session holder from presentation stuff and similar things

This commit is contained in:
azivner 2018-03-25 21:16:57 -04:00
parent 47eb1e3e02
commit ac1b06967f
12 changed files with 130 additions and 99 deletions

View file

@ -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,

View file

@ -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;

View 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
}

View file

@ -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);
}

View file

@ -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);
}
});
});

View file

@ -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
};

View file

@ -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();

View file

@ -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
};

View 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
};

View file

@ -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) {}

View file

@ -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;

View file

@ -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,