#98, sync is now configured in the options

This commit is contained in:
azivner 2018-07-22 22:21:16 +02:00
parent 073300bbcd
commit e7460ca3a9
15 changed files with 115 additions and 46 deletions

View File

@ -9,9 +9,3 @@ https=false
# path to certificate (run "bash generate-cert.sh" to generate self-signed certificate). Relevant only if https=true # path to certificate (run "bash generate-cert.sh" to generate self-signed certificate). Relevant only if https=true
certPath= certPath=
keyPath= keyPath=
[Sync]
syncServerHost=
syncServerTimeout=10000
syncProxy=
syncServerCertificate=

View File

@ -0,0 +1,8 @@
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
VALUES ('syncServerHost', '', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0);
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
VALUES ('syncServerTimeout', '5000', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0);
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
VALUES ('syncProxy', '', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0);

View File

@ -61,6 +61,7 @@
"simple-node-logger": "^0.93.37", "simple-node-logger": "^0.93.37",
"sqlite": "^2.9.2", "sqlite": "^2.9.2",
"tar-stream": "^1.6.1", "tar-stream": "^1.6.1",
"tmp-promise": "^1.0.5",
"unescape": "^1.0.1", "unescape": "^1.0.1",
"ws": "^5.2.1", "ws": "^5.2.1",
"xml2js": "^0.4.19" "xml2js": "^0.4.19"

View File

@ -34,8 +34,8 @@ async function showDialog() {
} }
} }
async function saveOptions(optionName, optionValue) { async function saveOptions(options) {
await server.put('options/' + encodeURIComponent(optionName) + '/' + encodeURIComponent(optionValue)); await server.put('options', options);
infoService.showMessage("Options change have been saved."); infoService.showMessage("Options change have been saved.");
} }
@ -129,16 +129,15 @@ addTabHandler((function() {
addTabHandler((function() { addTabHandler((function() {
const $form = $("#protected-session-timeout-form"); const $form = $("#protected-session-timeout-form");
const $protectedSessionTimeout = $("#protected-session-timeout-in-seconds"); const $protectedSessionTimeout = $("#protected-session-timeout-in-seconds");
const optionName = 'protectedSessionTimeout';
function optionsLoaded(options) { function optionsLoaded(options) {
$protectedSessionTimeout.val(options[optionName]); $protectedSessionTimeout.val(options['protectedSessionTimeout']);
} }
$form.submit(() => { $form.submit(() => {
const protectedSessionTimeout = $protectedSessionTimeout.val(); const protectedSessionTimeout = $protectedSessionTimeout.val();
saveOptions(optionName, protectedSessionTimeout).then(() => { saveOptions({ 'protectedSessionTimeout': protectedSessionTimeout }).then(() => {
protectedSessionHolder.setProtectedSessionTimeout(protectedSessionTimeout); protectedSessionHolder.setProtectedSessionTimeout(protectedSessionTimeout);
}); });
@ -153,14 +152,13 @@ addTabHandler((function() {
addTabHandler((function () { addTabHandler((function () {
const $form = $("#note-revision-snapshot-time-interval-form"); const $form = $("#note-revision-snapshot-time-interval-form");
const $timeInterval = $("#note-revision-snapshot-time-interval-in-seconds"); const $timeInterval = $("#note-revision-snapshot-time-interval-in-seconds");
const optionName = 'noteRevisionSnapshotTimeInterval';
function optionsLoaded(options) { function optionsLoaded(options) {
$timeInterval.val(options[optionName]); $timeInterval.val(options['noteRevisionSnapshotTimeInterval']);
} }
$form.submit(() => { $form.submit(() => {
saveOptions(optionName, $timeInterval.val()); saveOptions({ 'noteRevisionSnapshotTimeInterval': $timeInterval.val() });
return false; return false;
}); });
@ -189,6 +187,33 @@ addTabHandler((async function () {
return {}; return {};
})()); })());
addTabHandler((function() {
const $form = $("#sync-setup-form");
const $syncServerHost = $("#sync-server-host");
const $syncServerTimeout = $("#sync-server-timeout");
const $syncProxy = $("#sync-proxy");
function optionsLoaded(options) {
$syncServerHost.val(options['syncServerHost']);
$syncServerTimeout.val(options['syncServerTimeout']);
$syncProxy.val(options['syncProxy']);
}
$form.submit(() => {
saveOptions({
'syncServerHost': $syncServerHost.val(),
'syncServerTimeout': $syncServerTimeout.val(),
'syncProxy': $syncProxy.val()
});
return false;
});
return {
optionsLoaded
};
})());
addTabHandler((async function () { addTabHandler((async function () {
const $forceFullSyncButton = $("#force-full-sync-button"); const $forceFullSyncButton = $("#force-full-sync-button");
const $fillSyncRowsButton = $("#fill-sync-rows-button"); const $fillSyncRowsButton = $("#fill-sync-rows-button");

View File

@ -5,7 +5,8 @@ const optionService = require('../../services/options');
const log = require('../../services/log'); const log = require('../../services/log');
// options allowed to be updated directly in options dialog // options allowed to be updated directly in options dialog
const ALLOWED_OPTIONS = ['protectedSessionTimeout', 'noteRevisionSnapshotTimeInterval', 'zoomFactor', 'theme']; const ALLOWED_OPTIONS = ['protectedSessionTimeout', 'noteRevisionSnapshotTimeInterval',
'zoomFactor', 'theme', 'syncServerHost', 'syncServerTimeout', 'syncProxy'];
async function getOptions() { async function getOptions() {
const options = await sql.getMap("SELECT name, value FROM options WHERE name IN (" const options = await sql.getMap("SELECT name, value FROM options WHERE name IN ("
@ -17,16 +18,35 @@ async function getOptions() {
async function updateOption(req) { async function updateOption(req) {
const {name, value} = req.params; const {name, value} = req.params;
if (!update(name, value)) {
return [400, "not allowed option to change"];
}
}
async function updateOptions(req) {
for (const optionName in req.body) {
if (!update(optionName, req.body[optionName])) {
// this should be improved
// it should return 400 instead of current 500, but at least it now rollbacks transaction
throw new Error(`${optionName} is not allowed to change`);
}
}
}
async function update(name, value) {
if (!ALLOWED_OPTIONS.includes(name)) { if (!ALLOWED_OPTIONS.includes(name)) {
return [400, "not allowed option to set"]; return false;
} }
log.info(`Updating option ${name} to ${value}`); log.info(`Updating option ${name} to ${value}`);
await optionService.setOption(name, value); await optionService.setOption(name, value);
return true;
} }
module.exports = { module.exports = {
getOptions, getOptions,
updateOption updateOption,
updateOptions
}; };

View File

@ -144,6 +144,7 @@ function register(app) {
apiRoute(GET, '/api/options', optionsApiRoute.getOptions); apiRoute(GET, '/api/options', optionsApiRoute.getOptions);
apiRoute(PUT, '/api/options/:name/:value', optionsApiRoute.updateOption); apiRoute(PUT, '/api/options/:name/:value', optionsApiRoute.updateOption);
apiRoute(PUT, '/api/options', optionsApiRoute.updateOptions);
apiRoute(POST, '/api/password/change', passwordApiRoute.changePassword); apiRoute(POST, '/api/password/change', passwordApiRoute.changePassword);

View File

@ -3,7 +3,7 @@
const build = require('./build'); const build = require('./build');
const packageJson = require('../../package'); const packageJson = require('../../package');
const APP_DB_VERSION = 100; const APP_DB_VERSION = 101;
const SYNC_VERSION = 1; const SYNC_VERSION = 1;
module.exports = { module.exports = {

View File

@ -73,7 +73,7 @@ async function sendPing(client, lastSentSyncId) {
await sendMessage(client, { await sendMessage(client, {
type: 'sync', type: 'sync',
data: syncData, data: syncData,
changesToPushCount: syncSetup.isSyncSetup ? changesToPushCount : 0 changesToPushCount: await syncSetup.isSyncSetup() ? changesToPushCount : 0
}); });
} }

View File

@ -85,7 +85,7 @@ async function migrate() {
} }
} }
if (sqlInit.isDbUpToDate()) { if (await sqlInit.isDbUpToDate()) {
await sqlInit.initDbConnection(); await sqlInit.initDbConnection();
} }

View File

@ -34,6 +34,10 @@ async function initOptions(startNotePath, username, password) {
await optionService.createOption('encryptedDataKeyIv', ''); await optionService.createOption('encryptedDataKeyIv', '');
await passwordEncryptionService.setDataKey(password, utils.randomSecureToken(16)); await passwordEncryptionService.setDataKey(password, utils.randomSecureToken(16));
await optionService.createOption('syncServerHost', '', false);
await optionService.createOption('syncServerTimeout', 5000, false);
await optionService.createOption('syncProxy', '', false);
} }
module.exports = { module.exports = {

View File

@ -31,8 +31,8 @@ async function initDbConnection() {
await sql.execute("PRAGMA foreign_keys = ON"); await sql.execute("PRAGMA foreign_keys = ON");
if (isDbInitialized()) { if (!await isDbInitialized()) {
log.info("DB not found, please visit setup page to initialize Trilium."); log.info("DB not initialized, please visit setup page to initialize Trilium.");
return; return;
} }

View File

@ -17,7 +17,6 @@ const syncMutexService = require('./sync_mutex');
const cls = require('./cls'); const cls = require('./cls');
let proxyToggle = true; let proxyToggle = true;
let syncServerCertificate = null;
async function sync() { async function sync() {
try { try {
@ -169,7 +168,7 @@ async function checkContentHash(syncContext) {
} }
async function syncRequest(syncContext, method, uri, body) { async function syncRequest(syncContext, method, uri, body) {
const fullUri = syncSetup.SYNC_SERVER + uri; const fullUri = await syncSetup.getSyncServer() + uri;
try { try {
const options = { const options = {
@ -178,15 +177,13 @@ async function syncRequest(syncContext, method, uri, body) {
jar: syncContext.cookieJar, jar: syncContext.cookieJar,
json: true, json: true,
body: body, body: body,
timeout: syncSetup.SYNC_TIMEOUT timeout: await syncSetup.getSyncTimeout()
}; };
if (syncServerCertificate) { const syncProxy = await syncSetup.getSyncProxy();
options.ca = syncServerCertificate;
}
if (syncSetup.SYNC_PROXY && proxyToggle) { if (syncProxy && proxyToggle) {
options.proxy = syncSetup.SYNC_PROXY; options.proxy = syncProxy;
} }
return await rp(options); return await rp(options);
@ -270,18 +267,14 @@ async function setLastSyncedPush(lastSyncedPush) {
await optionService.setOption('lastSyncedPush', lastSyncedPush); await optionService.setOption('lastSyncedPush', lastSyncedPush);
} }
sqlInit.dbReady.then(() => { sqlInit.dbReady.then(async () => {
if (syncSetup.isSyncSetup) { if (await syncSetup.isSyncSetup()) {
log.info("Setting up sync to " + syncSetup.SYNC_SERVER + " with timeout " + syncSetup.SYNC_TIMEOUT); log.info("Setting up sync to " + await syncSetup.getSyncServer() + " with timeout " + await syncSetup.getSyncTimeout());
if (syncSetup.SYNC_PROXY) { const syncProxy = await syncSetup.getSyncProxy();
log.info("Sync proxy: " + syncSetup.SYNC_PROXY);
}
if (syncSetup.SYNC_CERT_PATH) { if (syncProxy) {
log.info('Sync certificate: ' + syncSetup.SYNC_CERT_PATH); log.info("Sync proxy: " + syncProxy);
syncServerCertificate = fs.readFileSync(syncSetup.SYNC_CERT_PATH);
} }
setInterval(cls.wrap(sync), 60000); setInterval(cls.wrap(sync), 60000);

View File

@ -1,11 +1,11 @@
"use strict"; "use strict";
const config = require('./config'); const config = require('./config');
const optionService = require('./options');
module.exports = { module.exports = {
SYNC_SERVER: config['Sync']['syncServerHost'], getSyncServer: async () => await optionService.getOption('syncServerHost'),
isSyncSetup: !!config['Sync']['syncServerHost'], isSyncSetup: async () => !!await optionService.getOption('syncServerHost'),
SYNC_TIMEOUT: config['Sync']['syncServerTimeout'] || 5000, getSyncTimeout: async () => await optionService.getOption('syncServerTimeout'),
SYNC_PROXY: config['Sync']['syncProxy'], getSyncProxy: async () => await optionService.getOption('syncProxy')
SYNC_CERT_PATH: config['Sync']['syncServerCertificate']
}; };

View File

@ -54,7 +54,7 @@ async function addEntitySync(entityName, entityId, sourceId) {
sourceId: sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId() sourceId: sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId()
}); });
if (!syncSetup.isSyncSetup) { if (!await syncSetup.isSyncSetup()) {
// this is because the "server" instances shouldn't have outstanding pushes // this is because the "server" instances shouldn't have outstanding pushes
// useful when you fork the DB for new "client" instance, it won't try to sync the whole DB // useful when you fork the DB for new "client" instance, it won't try to sync the whole DB
await sql.execute("UPDATE options SET value = (SELECT MAX(id) FROM sync) WHERE name IN('lastSyncedPush', 'lastSyncedPull')"); await sql.execute("UPDATE options SET value = (SELECT MAX(id) FROM sync) WHERE name IN('lastSyncedPush', 'lastSyncedPull')");

View File

@ -334,6 +334,7 @@
<li><a href="#change-password">Change password</a></li> <li><a href="#change-password">Change password</a></li>
<li><a href="#protected-session-timeout">Protected session</a></li> <li><a href="#protected-session-timeout">Protected session</a></li>
<li><a href="#note-revision-snapshot-time-interval">Note revisions</a></li> <li><a href="#note-revision-snapshot-time-interval">Note revisions</a></li>
<li><a href="#sync-setup">Sync</a></li>
<li><a href="#advanced">Advanced</a></li> <li><a href="#advanced">Advanced</a></li>
<li><a href="#about">About Trilium</a></li> <li><a href="#about">About Trilium</a></li>
</ul> </ul>
@ -404,6 +405,28 @@
<button class="btn btn-sm">Save</button> <button class="btn btn-sm">Save</button>
</form> </form>
</div> </div>
<div id="sync-setup">
<h4 style="margin-top: 0px;">Sync</h4>
<form id="sync-setup-form">
<div class="form-group">
<label for="sync-server-host">Sync server host</label>
<input class="form-control" id="sync-server-host" placeholder="https://<host>:<port>">
</div>
<div class="form-group">
<label for="sync-server-timeout">Sync timeout (milliseconds)</label>
<input class="form-control" id="sync-server-timeout" min="1" max="10000000" type="number">
</div>
<div class="form-group">
<label for="sync-proxy">Sync proxy server (optional)</label>
<input class="form-control" id="sync-proxy" placeholder="https://<host>:<port>">
</div>
<button class="btn btn-sm">Save</button>
</form>
</div>
<div id="advanced"> <div id="advanced">
<h4 style="margin-top: 0px;">Sync</h4> <h4 style="margin-top: 0px;">Sync</h4>
<button id="force-full-sync-button" class="btn btn-sm">Force full sync</button> <button id="force-full-sync-button" class="btn btn-sm">Force full sync</button>