mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
sync WIP
This commit is contained in:
parent
5253f680f6
commit
1c733fbfab
89
bin/www
89
bin/www
@ -1,26 +1,28 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
process.on('unhandledRejection', (reason, p) => {
|
process.on('unhandledRejection', (reason, p) => {
|
||||||
|
const message = 'Unhandled Rejection at: Promise' + p + ', reason:' + reason;
|
||||||
// this makes sure that stacktrace of failed promise is printed out
|
// this makes sure that stacktrace of failed promise is printed out
|
||||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
console.log(message);
|
||||||
|
|
||||||
|
// but also try to log it into file
|
||||||
|
require('../services/log').error(message);
|
||||||
});
|
});
|
||||||
|
|
||||||
var app = require('../app');
|
const app = require('../app');
|
||||||
var debug = require('debug')('node:server');
|
const debug = require('debug')('node:server');
|
||||||
var http = require('http');
|
const http = require('http');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get port from environment and store in Express.
|
* Get port from environment and store in Express.
|
||||||
*/
|
*/
|
||||||
|
const port = normalizePort(process.env.PORT || '3000');
|
||||||
var port = normalizePort(process.env.PORT || '3000');
|
|
||||||
app.set('port', port);
|
app.set('port', port);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create HTTP server.
|
* Create HTTP server.
|
||||||
*/
|
*/
|
||||||
|
const server = http.createServer(app);
|
||||||
var server = http.createServer(app);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listen on provided port, on all network interfaces.
|
* Listen on provided port, on all network interfaces.
|
||||||
@ -35,19 +37,19 @@ server.on('listening', onListening);
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function normalizePort(val) {
|
function normalizePort(val) {
|
||||||
var port = parseInt(val, 10);
|
const port = parseInt(val, 10);
|
||||||
|
|
||||||
if (isNaN(port)) {
|
if (isNaN(port)) {
|
||||||
// named pipe
|
// named pipe
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (port >= 0) {
|
if (port >= 0) {
|
||||||
// port number
|
// port number
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,27 +57,29 @@ function normalizePort(val) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function onError(error) {
|
function onError(error) {
|
||||||
if (error.syscall !== 'listen') {
|
if (error.syscall !== 'listen') {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
var bind = typeof port === 'string'
|
const bind = typeof port === 'string'
|
||||||
? 'Pipe ' + port
|
? 'Pipe ' + port
|
||||||
: 'Port ' + port;
|
: 'Port ' + port;
|
||||||
|
|
||||||
// handle specific listen errors with friendly messages
|
// handle specific listen errors with friendly messages
|
||||||
switch (error.code) {
|
switch (error.code) {
|
||||||
case 'EACCES':
|
case 'EACCES':
|
||||||
console.error(bind + ' requires elevated privileges');
|
console.error(bind + ' requires elevated privileges');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
break;
|
break;
|
||||||
case 'EADDRINUSE':
|
|
||||||
console.error(bind + ' is already in use');
|
case 'EADDRINUSE':
|
||||||
process.exit(1);
|
console.error(bind + ' is already in use');
|
||||||
break;
|
process.exit(1);
|
||||||
default:
|
break;
|
||||||
throw error;
|
|
||||||
}
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,9 +87,10 @@ function onError(error) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function onListening() {
|
function onListening() {
|
||||||
var addr = server.address();
|
const addr = server.address();
|
||||||
var bind = typeof addr === 'string'
|
const bind = typeof addr === 'string'
|
||||||
? 'pipe ' + addr
|
? 'pipe ' + addr
|
||||||
: 'port ' + addr.port;
|
: 'port ' + addr.port;
|
||||||
debug('Listening on ' + bind);
|
|
||||||
|
debug('Listening on ' + bind);
|
||||||
}
|
}
|
1
migrations/0011__add_last_synced_option.sql
Normal file
1
migrations/0011__add_last_synced_option.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
INSERT INTO options (opt_name, opt_value) VALUES ('last_synced', 0)
|
@ -22,6 +22,8 @@
|
|||||||
"fs-extra": "^4.0.2",
|
"fs-extra": "^4.0.2",
|
||||||
"helmet": "^3.9.0",
|
"helmet": "^3.9.0",
|
||||||
"ini": "^1.3.4",
|
"ini": "^1.3.4",
|
||||||
|
"request": "^2.83.0",
|
||||||
|
"request-promise": "^4.2.2",
|
||||||
"scrypt": "^6.0.3",
|
"scrypt": "^6.0.3",
|
||||||
"serve-favicon": "~2.4.5",
|
"serve-favicon": "~2.4.5",
|
||||||
"session-file-store": "^1.1.2",
|
"session-file-store": "^1.1.2",
|
||||||
|
@ -10,7 +10,21 @@ router.get('/changed/:since', auth.checkApiAuth, async (req, res, next) => {
|
|||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
'tree': await sql.getResults("select * from notes_tree where date_modified >= ?", [since]),
|
'tree': await sql.getResults("select * from notes_tree where date_modified >= ?", [since]),
|
||||||
'notes': await sql.getFlattenedResults('note_id', "select note_id from notes where date_modified >= ?", [since])
|
'notes': await sql.getFlattenedResults('note_id', "select note_id from notes where date_modified >= ?", [since]),
|
||||||
|
'audit_log': await sql.getResults("select * from audit_log where date_modified >= ?", [since])
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/note/:noteId/:since', auth.checkApiAuth, async (req, res, next) => {
|
||||||
|
const noteId = req.params.noteId;
|
||||||
|
const since = parseInt(req.params.since);
|
||||||
|
|
||||||
|
const detail = await sql.getSingleResult("select * from notes where note_id = ?", [noteId]);
|
||||||
|
|
||||||
|
res.send({
|
||||||
|
'detail': detail,
|
||||||
|
'images': await sql.getResults("select * from images where note_id = ? order by note_offset", [noteId]),
|
||||||
|
'history': await sql.getResults("select * from notes_history where note_id = ? and date_modified_to >= ?", [noteId, since])
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,14 +7,7 @@ const migration = require('../services/migration');
|
|||||||
const sql = require('../services/sql');
|
const sql = require('../services/sql');
|
||||||
|
|
||||||
router.get('', auth.checkAuth, async (req, res, next) => {
|
router.get('', auth.checkAuth, async (req, res, next) => {
|
||||||
const dbVersion = parseInt(await sql.getOption('db_version'))
|
res.render('index', {});
|
||||||
|
|
||||||
if (dbVersion < migration.APP_DB_VERSION) {
|
|
||||||
res.redirect("migration");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
res.render('index', {});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
@ -1,19 +1,31 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function checkAuth(req, res, next) {
|
const migration = require('./migration');
|
||||||
|
|
||||||
|
async function checkAuth(req, res, next) {
|
||||||
if (!req.session.loggedIn) {
|
if (!req.session.loggedIn) {
|
||||||
res.redirect("login");
|
res.redirect("login");
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (await migration.isDbUpToDate()) {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
res.redirect("migration");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkApiAuth(req, res, next) {
|
async function checkApiAuth(req, res, next) {
|
||||||
if (!req.session.loggedIn) {
|
if (!req.session.loggedIn && req.header("auth") !== "sync") {
|
||||||
res.sendStatus(401);
|
res.sendStatus(401);
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (await migration.isDbUpToDate()) {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
res.sendStatus(409); // need better response than that
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -3,7 +3,7 @@ const sql = require('./sql');
|
|||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const log = require('./log');
|
const log = require('./log');
|
||||||
|
|
||||||
const APP_DB_VERSION = 10;
|
const APP_DB_VERSION = 11;
|
||||||
const MIGRATIONS_DIR = "./migrations";
|
const MIGRATIONS_DIR = "./migrations";
|
||||||
|
|
||||||
async function migrate() {
|
async function migrate() {
|
||||||
@ -67,7 +67,14 @@ async function migrate() {
|
|||||||
return migrations;
|
return migrations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function isDbUpToDate() {
|
||||||
|
const dbVersion = parseInt(await sql.getOption('db_version'));
|
||||||
|
|
||||||
|
return dbVersion >= APP_DB_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
migrate,
|
migrate,
|
||||||
|
isDbUpToDate,
|
||||||
APP_DB_VERSION
|
APP_DB_VERSION
|
||||||
};
|
};
|
@ -4,11 +4,19 @@ const db = require('sqlite');
|
|||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const log = require('./log');
|
const log = require('./log');
|
||||||
|
|
||||||
async function insert(table_name, rec) {
|
async function insert(table_name, rec, replace = false) {
|
||||||
const columns = Object.keys(rec).join(", ");
|
const keys = Object.keys(rec);
|
||||||
const questionMarks = Object.keys(rec).map(p => "?").join(", ");
|
if (keys.length === 0) {
|
||||||
|
log.error("Can't insert empty object into table " + table_name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const res = await execute("INSERT INTO " + table_name + "(" + columns + ") VALUES (" + questionMarks + ")", Object.values(rec));
|
const columns = keys.join(", ");
|
||||||
|
const questionMarks = keys.map(p => "?").join(", ");
|
||||||
|
|
||||||
|
const query = "INSERT " + (replace ? "OR REPLACE" : "") + " INTO " + table_name + "(" + columns + ") VALUES (" + questionMarks + ")";
|
||||||
|
|
||||||
|
const res = await execute(query, Object.values(rec));
|
||||||
|
|
||||||
return res.lastID;
|
return res.lastID;
|
||||||
}
|
}
|
||||||
@ -21,6 +29,10 @@ async function commit() {
|
|||||||
return await db.run("COMMIT");
|
return await db.run("COMMIT");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function rollback() {
|
||||||
|
return await db.run("ROLLBACK");
|
||||||
|
}
|
||||||
|
|
||||||
async function getOption(optName) {
|
async function getOption(optName) {
|
||||||
const row = await getSingleResult("SELECT opt_value FROM options WHERE opt_name = ?", [optName]);
|
const row = await getSingleResult("SELECT opt_value FROM options WHERE opt_name = ?", [optName]);
|
||||||
|
|
||||||
@ -94,6 +106,7 @@ module.exports = {
|
|||||||
setOption,
|
setOption,
|
||||||
beginTransaction,
|
beginTransaction,
|
||||||
commit,
|
commit,
|
||||||
|
rollback,
|
||||||
addAudit,
|
addAudit,
|
||||||
deleteRecentAudits,
|
deleteRecentAudits,
|
||||||
remove
|
remove
|
||||||
|
@ -1,7 +1,94 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function sync() {
|
const log = require('./log');
|
||||||
|
const rp = require('request-promise');
|
||||||
|
const sql = require('./sql');
|
||||||
|
const migration = require('./migration');
|
||||||
|
|
||||||
|
const SYNC_SERVER = 'http://localhost:3000';
|
||||||
|
|
||||||
|
|
||||||
|
let syncInProgress = false;
|
||||||
|
|
||||||
|
async function sync() {
|
||||||
|
try {
|
||||||
|
syncInProgress = true;
|
||||||
|
|
||||||
|
if (!await migration.isDbUpToDate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastSynced = parseInt(await sql.getOption('last_synced'));
|
||||||
|
|
||||||
|
const resp = await rp({
|
||||||
|
uri: SYNC_SERVER + '/api/sync/changed/' + lastSynced,
|
||||||
|
headers: {
|
||||||
|
auth: 'sync'
|
||||||
|
},
|
||||||
|
json: true
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
sql.beginTransaction();
|
||||||
|
|
||||||
|
for (const treeItem of resp.tree) {
|
||||||
|
delete treeItem['id'];
|
||||||
|
|
||||||
|
await sql.insert("notes_tree", treeItem, true);
|
||||||
|
|
||||||
|
log.info("Syncing notes_tree " + treeItem.note_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const audit of resp.audit_log) {
|
||||||
|
delete audit['id'];
|
||||||
|
|
||||||
|
await sql.insert("audit_log", audit, true);
|
||||||
|
|
||||||
|
log.info("Syncing audit_log for noteId=" + audit.note_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const noteId of resp.notes) {
|
||||||
|
const note = await rp({
|
||||||
|
uri: SYNC_SERVER + "/api/sync/note/" + noteId + "/" + lastSynced,
|
||||||
|
headers: {
|
||||||
|
auth: 'sync'
|
||||||
|
},
|
||||||
|
json: true
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(noteId);
|
||||||
|
|
||||||
|
await sql.insert("notes", note.detail, true);
|
||||||
|
|
||||||
|
await sql.remove("images", noteId);
|
||||||
|
|
||||||
|
for (const image of note.images) {
|
||||||
|
await sql.insert("images", image);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const history of note.history) {
|
||||||
|
delete history['id'];
|
||||||
|
|
||||||
|
await sql.insert("notes_history", history);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sql.commit();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
sql.rollback();
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
log.error("sync failed: " + e.stack);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
syncInProgress = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setInterval(sync, 60000);
|
setInterval(sync, 60000);
|
||||||
|
|
||||||
|
setTimeout(sync, 1000);
|
@ -7,7 +7,7 @@ function randomToken(length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function newNoteId() {
|
function newNoteId() {
|
||||||
return randomString(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
|
return randomString(22, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
|
||||||
}
|
}
|
||||||
|
|
||||||
function randomString(length, chars) {
|
function randomString(length, chars) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user