1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Improved logging handling

This commit is contained in:
Laurent Cozic 2017-06-23 22:32:24 +01:00
parent ec9dacc2c0
commit 45ac9daebe
13 changed files with 235 additions and 59 deletions

View File

@ -10,20 +10,34 @@ import { Folder } from 'src/models/folder.js';
import { Note } from 'src/models/note.js'; import { Note } from 'src/models/note.js';
import { Setting } from 'src/models/setting.js'; import { Setting } from 'src/models/setting.js';
import { Synchronizer } from 'src/synchronizer.js'; import { Synchronizer } from 'src/synchronizer.js';
import { Logger } from 'src/logger.js';
import { uuid } from 'src/uuid.js'; import { uuid } from 'src/uuid.js';
import { sprintf } from 'sprintf-js'; import { sprintf } from 'sprintf-js';
import { _ } from 'src/locale.js'; import { _ } from 'src/locale.js';
import os from 'os'; import os from 'os';
import fs from 'fs-extra'; import fs from 'fs-extra';
const APPNAME = 'joplin';
const dataDir = os.homedir() + '/.local/share/' + APPNAME;
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});
const logger = new Logger();
logger.addTarget('file', { path: dataDir + '/log.txt' });
logger.setLevel(Logger.LEVEL_DEBUG);
const syncLogger = new Logger();
syncLogger.addTarget('file', { path: dataDir + '/log-sync.txt' });
syncLogger.setLevel(Logger.LEVEL_DEBUG);
let db = new Database(new DatabaseDriverNode()); let db = new Database(new DatabaseDriverNode());
db.setLogger(logger);
let synchronizer_ = null; let synchronizer_ = null;
const vorpal = require('vorpal')(); const vorpal = require('vorpal')();
const APPNAME = 'joplin';
async function main() { async function main() {
let dataDir = os.homedir() + '/.local/share/' + APPNAME;
await fs.mkdirp(dataDir, 0o755); await fs.mkdirp(dataDir, 0o755);
await db.open({ name: dataDir + '/database.sqlite' }); await db.open({ name: dataDir + '/database.sqlite' });
@ -58,17 +72,23 @@ async function main() {
}); });
let appDir = await driver.api().appDirectory(); let appDir = await driver.api().appDirectory();
console.info('App dir: ' + appDir); logger.info('App dir: ' + appDir);
fileApi = new FileApi(appDir, driver); fileApi = new FileApi(appDir, driver);
fileApi.setLogger(logger);
} else { } else {
throw new Error('Unknown backend: ' + remoteBackend); throw new Error('Unknown backend: ' + remoteBackend);
} }
synchronizer_ = new Synchronizer(db, fileApi); synchronizer_ = new Synchronizer(db, fileApi);
synchronizer_.setLogger(syncLogger);
return synchronizer_; return synchronizer_;
} }
// let s = await synchronizer('onedrive');
// await synchronizer_.start();
// return;
function switchCurrentFolder(folder) { function switchCurrentFolder(folder) {
currentFolder = folder; currentFolder = folder;
updatePrompt(); updatePrompt();
@ -356,7 +376,7 @@ async function main() {
synchronizer('onedrive').then((s) => { synchronizer('onedrive').then((s) => {
return s.start(); return s.start();
}).catch((error) => { }).catch((error) => {
console.error(error); logger.error(error);
}).then(() => { }).then(() => {
end(); end();
}); });

View File

@ -101,22 +101,18 @@ describe('Synchronizer', function() {
let note2 = await Note.load(note1.id); let note2 = await Note.load(note1.id);
note2.title = "Updated on client 2"; note2.title = "Updated on client 2";
await Note.save(note2); await Note.save(note2);
note2 = await Note.load(note2.id); note2 = await Note.load(note2.id);
await synchronizer().start(); await synchronizer().start();
let files = await fileApi().list();
await switchClient(1); await switchClient(1);
await synchronizer().start(); await synchronizer().start();
note1 = await Note.load(note1.id); let all = await Folder.all(true);
let files = await fileApi().list();
expect(!!note1).toBe(true); await localItemsSameAsRemote(all, expect);
expect(note1.title).toBe(note2.title);
expect(note1.body).toBe(note2.body);
done(); done();
}); });

View File

@ -90,7 +90,7 @@ function db(id = null) {
function synchronizer(id = null) { function synchronizer(id = null) {
if (id === null) id = currentClient_; if (id === null) id = currentClient_;
console.info('SYNC', id); //console.info('SYNC', id);
return synchronizers_[id]; return synchronizers_[id];
} }

View File

@ -129,7 +129,6 @@ class BaseModel {
static loadByField(fieldName, fieldValue) { static loadByField(fieldName, fieldValue) {
return this.modelSelectOne('SELECT * FROM ' + this.tableName() + ' WHERE `' + fieldName + '` = ?', [fieldValue]); return this.modelSelectOne('SELECT * FROM ' + this.tableName() + ' WHERE `' + fieldName + '` = ?', [fieldValue]);
//return this.db().selectOne('SELECT * FROM ' + this.tableName() + ' WHERE `' + fieldName + '` = ?', [fieldValue]);
} }
static applyPatch(model, patch) { static applyPatch(model, patch) {

View File

@ -1,6 +1,6 @@
import { Log } from 'src/log.js';
import { uuid } from 'src/uuid.js'; import { uuid } from 'src/uuid.js';
import { promiseChain } from 'src/promise-utils.js'; import { promiseChain } from 'src/promise-utils.js';
import { Logger } from 'src/logger.js'
import { _ } from 'src/locale.js' import { _ } from 'src/locale.js'
const structureSql = ` const structureSql = `
@ -115,6 +115,30 @@ class Database {
this.tableFields_ = null; this.tableFields_ = null;
this.driver_ = driver; this.driver_ = driver;
this.inTransaction_ = false; this.inTransaction_ = false;
this.logger_ = new Logger();
this.logger_.addTarget('console');
this.logger_.setLevel(Logger.LEVEL_DEBUG);
}
// Converts the SQLite error to a regular JS error
// so that it prints a stacktrace when passed to
// console.error()
sqliteErrorToJsError(error, sql, params = null) {
let msg = sql;
if (params) msg += ': ' + JSON.stringify(params);
msg += ': ' + error.toString();
let output = new Error(msg);
if (error.code) output.code = error.code;
return output;
}
setLogger(l) {
this.logger_ = l;
}
logger() {
return this.logger_;
} }
setDebugEnabled(v) { setDebugEnabled(v) {
@ -136,26 +160,32 @@ class Database {
open(options) { open(options) {
return this.driver().open(options).then((db) => { return this.driver().open(options).then((db) => {
Log.info('Database was open successfully'); this.logger().info('Database was open successfully');
return this.initialize(); return this.initialize();
}).catch((error) => { }).catch((error) => {
Log.error('Cannot open database: ', error); this.logger().error('Cannot open database: ', error);
}); });
} }
selectOne(sql, params = null) { selectOne(sql, params = null) {
this.logQuery(sql, params); this.logQuery(sql, params);
return this.driver().selectOne(sql, params); return this.driver().selectOne(sql, params).catch((error) => {
throw this.sqliteErrorToJsError(error, sql, params);
});
} }
selectAll(sql, params = null) { selectAll(sql, params = null) {
this.logQuery(sql, params); this.logQuery(sql, params);
return this.driver().selectAll(sql, params); return this.driver().selectAll(sql, params).catch((error) => {
throw this.sqliteErrorToJsError(error, sql, params);
});
} }
exec(sql, params = null) { exec(sql, params = null) {
this.logQuery(sql, params); this.logQuery(sql, params);
return this.driver().exec(sql, params); return this.driver().exec(sql, params).catch((error) => {
throw this.sqliteErrorToJsError(error, sql, params);
});
} }
transactionExecBatch(queries) { transactionExecBatch(queries) {
@ -254,9 +284,9 @@ class Database {
logQuery(sql, params = null) { logQuery(sql, params = null) {
if (!this.debugMode()) return; if (!this.debugMode()) return;
if (params !== null) { if (params !== null) {
Log.debug('DB: ' + sql, JSON.stringify(params)); this.logger().debug('DB: ' + sql, JSON.stringify(params));
} else { } else {
Log.debug('DB: ' + sql); this.logger().debug('DB: ' + sql);
} }
} }
@ -329,7 +359,7 @@ class Database {
} }
refreshTableFields() { refreshTableFields() {
Log.info('Initializing tables...'); this.logger().info('Initializing tables...');
let queries = []; let queries = [];
queries.push(this.wrapQuery('DELETE FROM table_fields')); queries.push(this.wrapQuery('DELETE FROM table_fields'));
@ -367,10 +397,10 @@ class Database {
} }
initialize() { initialize() {
Log.info('Checking for database schema update...'); this.logger().info('Checking for database schema update...');
return this.selectOne('SELECT * FROM version LIMIT 1').then((row) => { return this.selectOne('SELECT * FROM version LIMIT 1').then((row) => {
Log.info('Current database version', row); this.logger().info('Current database version', row);
// TODO: version update logic // TODO: version update logic
// TODO: only do this if db has been updated: // TODO: only do this if db has been updated:
@ -409,9 +439,8 @@ class Database {
// return p; // return p;
}).catch((error) => { }).catch((error) => {
//console.info(error.code);
if (error && error.code != 0 && error.code != 'SQLITE_ERROR') { if (error && error.code != 0 && error.code != 'SQLITE_ERROR') {
Log.error(error); this.logger().error(error);
return; return;
} }
@ -420,14 +449,14 @@ class Database {
// which means the database is empty and the tables need to be created. // which means the database is empty and the tables need to be created.
// If it's any other error there's nothing we can do anyway. // If it's any other error there's nothing we can do anyway.
Log.info('Database is new - creating the schema...'); this.logger().info('Database is new - creating the schema...');
let queries = this.wrapQueries(this.sqlStringToLines(structureSql)); let queries = this.wrapQueries(this.sqlStringToLines(structureSql));
queries.push(this.wrapQuery('INSERT INTO settings (`key`, `value`, `type`) VALUES ("clientId", "' + uuid.create() + '", "' + Database.enumId('settings', 'string') + '")')); queries.push(this.wrapQuery('INSERT INTO settings (`key`, `value`, `type`) VALUES ("clientId", "' + uuid.create() + '", "' + Database.enumId('settings', 'string') + '")'));
queries.push(this.wrapQuery('INSERT INTO folders (`id`, `title`, `is_default`, `created_time`) VALUES ("' + uuid.create() + '", "' + _('Default list') + '", 1, ' + Math.round((new Date()).getTime() / 1000) + ')')); queries.push(this.wrapQuery('INSERT INTO folders (`id`, `title`, `is_default`, `created_time`) VALUES ("' + uuid.create() + '", "' + _('Default list') + '", 1, ' + Math.round((new Date()).getTime() / 1000) + ')'));
return this.transactionExecBatch(queries).then(() => { return this.transactionExecBatch(queries).then(() => {
Log.info('Database schema created successfully'); this.logger().info('Database schema created successfully');
// Calling initialize() now that the db has been created will make it go through // Calling initialize() now that the db has been created will make it go through
// the normal db update process (applying any additional patch). // the normal db update process (applying any additional patch).
return this.refreshTableFields(); return this.refreshTableFields();

View File

@ -1,5 +1,6 @@
import moment from 'moment'; import moment from 'moment';
import { time } from 'src/time-utils.js'; import { time } from 'src/time-utils.js';
import { dirname, basename } from 'src/path-utils.js';
import { OneDriveApi } from 'src/onedrive-api.js'; import { OneDriveApi } from 'src/onedrive-api.js';
class FileApiDriverOneDrive { class FileApiDriverOneDrive {
@ -40,8 +41,9 @@ class FileApiDriverOneDrive {
} }
async stat(path) { async stat(path) {
let item = null;
try { try {
let item = await this.api_.execJson('GET', this.makePath_(path), this.itemFilter_()); item = await this.api_.execJson('GET', this.makePath_(path), this.itemFilter_());
} catch (error) { } catch (error) {
if (error.error.code == 'itemNotFound') return null; if (error.error.code == 'itemNotFound') return null;
throw error; throw error;
@ -64,8 +66,9 @@ class FileApiDriverOneDrive {
} }
async get(path) { async get(path) {
let content = null;
try { try {
let content = await this.api_.execText('GET', this.makePath_(path) + ':/content'); content = await this.api_.execText('GET', this.makePath_(path) + ':/content');
} catch (error) { } catch (error) {
if (error.error.code == 'itemNotFound') return null; if (error.error.code == 'itemNotFound') return null;
throw error; throw error;
@ -73,8 +76,17 @@ class FileApiDriverOneDrive {
return content; return content;
} }
mkdir(path) { async mkdir(path) {
throw new Error('Not implemented'); let item = await this.stat(path);
if (item) return item;
let parentPath = dirname(path);
item = await this.api_.execJson('POST', this.makePath_(parentPath) + ':/children', this.itemFilter_(), {
name: basename(path),
folder: {},
});
return this.makeItem_(item);
} }
put(path, content) { put(path, content) {
@ -88,8 +100,16 @@ class FileApiDriverOneDrive {
return this.api_.exec('DELETE', this.makePath_(path)); return this.api_.exec('DELETE', this.makePath_(path));
} }
move(oldPath, newPath) { async move(oldPath, newPath) {
throw new Error('Not implemented'); let newDir = dirname(newPath);
let newName = basename(newPath);
let item = await this.api_.execJson('PATCH', this.makePath_(oldPath), this.itemFilter_(), {
name: newName,
parentReference: { path: newDir },
});
return this.makeItem_(item);
} }
format() { format() {

View File

@ -1,14 +1,20 @@
import { isHidden } from 'src/path-utils.js'; import { isHidden } from 'src/path-utils.js';
import { Logger } from 'src/logger.js';
class FileApi { class FileApi {
constructor(baseDir, driver) { constructor(baseDir, driver) {
this.baseDir_ = baseDir; this.baseDir_ = baseDir;
this.driver_ = driver; this.driver_ = driver;
this.logger_ = new Logger();
} }
dlog(s) { setLogger(l) {
console.info('FileApi: ' + s); this.logger_ = l;
}
logger() {
return this.logger_;
} }
fullPath_(path) { fullPath_(path) {
@ -21,7 +27,7 @@ class FileApi {
if (!options) options = {}; if (!options) options = {};
if (!('includeHidden' in options)) options.includeHidden = false; if (!('includeHidden' in options)) options.includeHidden = false;
this.dlog('list'); this.logger().debug('list');
return this.driver_.list(this.baseDir_).then((items) => { return this.driver_.list(this.baseDir_).then((items) => {
if (!options.includeHidden) { if (!options.includeHidden) {
let temp = []; let temp = [];
@ -35,17 +41,17 @@ class FileApi {
} }
setTimestamp(path, timestamp) { setTimestamp(path, timestamp) {
this.dlog('setTimestamp ' + path); this.logger().debug('setTimestamp ' + path);
return this.driver_.setTimestamp(this.fullPath_(path), timestamp); return this.driver_.setTimestamp(this.fullPath_(path), timestamp);
} }
mkdir(path) { mkdir(path) {
this.dlog('delete ' + path); this.logger().debug('mkdir ' + path);
return this.driver_.mkdir(this.fullPath_(path)); return this.driver_.mkdir(this.fullPath_(path));
} }
stat(path) { stat(path) {
this.dlog('stat ' + path); this.logger().debug('stat ' + path);
return this.driver_.stat(this.fullPath_(path)).then((output) => { return this.driver_.stat(this.fullPath_(path)).then((output) => {
if (!output) return output; if (!output) return output;
output.path = path; output.path = path;
@ -54,22 +60,22 @@ class FileApi {
} }
get(path) { get(path) {
this.dlog('get ' + path); this.logger().debug('get ' + path);
return this.driver_.get(this.fullPath_(path)); return this.driver_.get(this.fullPath_(path));
} }
put(path, content) { put(path, content) {
this.dlog('put ' + path); this.logger().debug('put ' + path);
return this.driver_.put(this.fullPath_(path), content); return this.driver_.put(this.fullPath_(path), content);
} }
delete(path) { delete(path) {
this.dlog('delete ' + path); this.logger().debug('delete ' + path);
return this.driver_.delete(this.fullPath_(path)); return this.driver_.delete(this.fullPath_(path));
} }
move(oldPath, newPath) { move(oldPath, newPath) {
this.dlog('move ' + oldPath + ' => ' + newPath); this.logger().debug('move ' + oldPath + ' => ' + newPath);
return this.driver_.move(this.fullPath_(oldPath), this.fullPath_(newPath)); return this.driver_.move(this.fullPath_(oldPath), this.fullPath_(newPath));
} }

View File

@ -0,0 +1,76 @@
import moment from 'moment';
import fs from 'fs-extra';
class Logger {
constructor() {
this.targets_ = [];
this.level_ = Logger.LEVEL_ERROR;
}
setLevel(level) {
this.level_ = level;
}
level() {
return this.level_;
}
clearTargets() {
this.targets_.clear();
}
addTarget(type, options = null) {
let target = { type: type };
for (let n in options) {
if (!options.hasOwnProperty(n)) continue;
target[n] = options[n];
}
this.targets_.push(target);
}
log(level, object) {
if (this.level() < level || !this.targets_.length) return;
let levelString = '';
if (this.level() == Logger.LEVEL_INFO) levelString = '[info] ';
if (this.level() == Logger.LEVEL_WARN) levelString = '[warn] ';
if (this.level() == Logger.LEVEL_ERROR) levelString = '[error] ';
let line = moment().format('YYYY-MM-DD HH:mm:ss') + ': ' + levelString;
for (let i = 0; i < this.targets_.length; i++) {
let t = this.targets_[i];
if (t.type == 'console') {
let fn = 'debug';
if (level = Logger.LEVEL_ERROR) fn = 'error';
if (level = Logger.LEVEL_WARN) fn = 'warn';
if (level = Logger.LEVEL_INFO) fn = 'info';
if (typeof object === 'object') {
console[fn](line, object);
} else {
console[fn](line + object);
}
} else if (t.type == 'file') {
if (typeof object === 'object') object = JSON.stringify(object);
fs.appendFile(t.path, line + object + "\n", (error) => {
if (error) throw error;
});
}
}
}
error(object) { return this.log(Logger.LEVEL_ERROR, object); }
warn(object) { return this.log(Logger.LEVEL_WARN, object); }
info(object) { return this.log(Logger.LEVEL_INFO, object); }
debug(object) { return this.log(Logger.LEVEL_DEBUG, object); }
}
Logger.LEVEL_NONE = 0;
Logger.LEVEL_ERROR = 10;
Logger.LEVEL_WARN = 20;
Logger.LEVEL_INFO = 30;
Logger.LEVEL_DEBUG = 40;
export { Logger };

View File

@ -44,11 +44,11 @@ class Note extends BaseItem {
} }
static previews(parentId) { static previews(parentId) {
return this.modelSelectAll('SELECT ' + this.previewFieldsSql() + ' FROM is_conflict = 0 AND notes WHERE parent_id = ?', [parentId]); return this.modelSelectAll('SELECT ' + this.previewFieldsSql() + ' FROM notes WHERE is_conflict = 0 AND parent_id = ?', [parentId]);
} }
static preview(noteId) { static preview(noteId) {
return this.modelSelectOne('SELECT ' + this.previewFieldsSql() + ' FROM is_conflict = 0 AND notes WHERE id = ?', [noteId]); return this.modelSelectOne('SELECT ' + this.previewFieldsSql() + ' FROM notes WHERE is_conflict = 0 AND id = ?', [noteId]);
} }
static conflictedNotes() { static conflictedNotes() {

View File

@ -77,7 +77,7 @@ class OneDriveApi {
options.method = method; options.method = method;
} }
if (method == 'PATCH') { if (method == 'PATCH' || method == 'POST') {
options.headers['Content-Type'] = 'application/json'; options.headers['Content-Type'] = 'application/json';
if (data) data = JSON.stringify(data); if (data) data = JSON.stringify(data);
} }
@ -88,8 +88,8 @@ class OneDriveApi {
if (data) options.body = data; if (data) options.body = data;
console.info(method + ' ' + url); // console.info(method + ' ' + url);
console.info(data); // console.info(data);
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
options.headers['Authorization'] = 'bearer ' + this.token(); options.headers['Authorization'] = 'bearer ' + this.token();
@ -98,8 +98,6 @@ class OneDriveApi {
if (!response.ok) { if (!response.ok) {
let error = await response.json(); let error = await response.json();
console.info(error);
if (error && error.error && error.error.code == 'InvalidAuthenticationToken') { if (error && error.error && error.error.code == 'InvalidAuthenticationToken') {
await this.refreshAccessToken(); await this.refreshAccessToken();
continue; continue;

View File

@ -1,3 +1,10 @@
function dirname(path) {
if (!path) throw new Error('Path is empty');
let s = path.split('/');
s.pop();
return s.join('/');
}
function basename(path) { function basename(path) {
if (!path) throw new Error('Path is empty'); if (!path) throw new Error('Path is empty');
let s = path.split('/'); let s = path.split('/');
@ -10,4 +17,4 @@ function isHidden(path) {
return b[0] === '.'; return b[0] === '.';
} }
export { basename, isHidden }; export { basename, dirname, isHidden };

View File

@ -6,7 +6,8 @@ import { Note } from 'src/models/note.js';
import { BaseModel } from 'src/base-model.js'; import { BaseModel } from 'src/base-model.js';
import { sprintf } from 'sprintf-js'; import { sprintf } from 'sprintf-js';
import { time } from 'src/time-utils.js'; import { time } from 'src/time-utils.js';
import { Log } from 'src/log.js' import { Logger } from 'src/logger.js'
import moment from 'moment';
class Synchronizer { class Synchronizer {
@ -14,6 +15,7 @@ class Synchronizer {
this.db_ = db; this.db_ = db;
this.api_ = api; this.api_ = api;
this.syncDirName_ = '.sync'; this.syncDirName_ = '.sync';
this.logger_ = new Logger();
} }
db() { db() {
@ -24,6 +26,14 @@ class Synchronizer {
return this.api_; return this.api_;
} }
setLogger(l) {
this.logger_ = l;
}
logger() {
return this.logger_;
}
async createWorkDir() { async createWorkDir() {
if (this.syncWorkDir_) return this.syncWorkDir_; if (this.syncWorkDir_) return this.syncWorkDir_;
let dir = await this.api().mkdir(this.syncDirName_); let dir = await this.api().mkdir(this.syncDirName_);
@ -36,6 +46,8 @@ class Synchronizer {
// last sync and apply the changes to remote. // last sync and apply the changes to remote.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
this.logger().info('Starting synchronization...');
await this.createWorkDir(); await this.createWorkDir();
let donePaths = []; let donePaths = [];
@ -74,7 +86,7 @@ class Synchronizer {
} }
} }
console.info('Sync action (1): ' + action); this.logger().debug('Sync action (1): ' + action);
if (action == 'createRemote' || action == 'updateRemote') { if (action == 'createRemote' || action == 'updateRemote') {
@ -132,7 +144,7 @@ class Synchronizer {
for (let i = 0; i < deletedItems.length; i++) { for (let i = 0; i < deletedItems.length; i++) {
let item = deletedItems[i]; let item = deletedItems[i];
let path = BaseItem.systemPath(item.item_id) let path = BaseItem.systemPath(item.item_id)
console.info('Sync action (2): deleteRemote'); this.logger().debug('Sync action (2): deleteRemote');
await this.api().delete(path); await this.api().delete(path);
await BaseModel.remoteDeletedItem(item.item_id); await BaseModel.remoteDeletedItem(item.item_id);
} }
@ -155,23 +167,27 @@ class Synchronizer {
if (donePaths.indexOf(path) > 0) continue; if (donePaths.indexOf(path) > 0) continue;
let action = null; let action = null;
let reason = '';
let local = await BaseItem.loadItemByPath(path); let local = await BaseItem.loadItemByPath(path);
if (!local) { if (!local) {
action = 'createLocal'; action = 'createLocal';
reason = 'Local exists but remote does not';
} else { } else {
if (remote.updated_time > local.updated_time) { if (remote.updated_time > local.updated_time) {
action = 'updateLocal'; action = 'updateLocal';
reason = sprintf('Remote (%s) is more recent than local (%s)', time.unixMsToIso(remote.updated_time), time.unixMsToIso(local.updated_time));
} }
} }
if (!action) continue; if (!action) continue;
console.info('Sync action (3): ' + action); this.logger().debug('Sync action (3): ' + action);
this.logger().debug('Reason: ' + reason);
if (action == 'createLocal' || action == 'updateLocal') { if (action == 'createLocal' || action == 'updateLocal') {
let content = await this.api().get(path); let content = await this.api().get(path);
if (!content) { if (!content) {
Log.warn('Remote item has been deleted between now and the list() call? In that case it will handled during the next sync: ' + path); this.logger().warn('Remote item has been deleted between now and the list() call? In that case it will handled during the next sync: ' + path);
continue; continue;
} }
content = BaseItem.unserialize(content); content = BaseItem.unserialize(content);
@ -192,11 +208,14 @@ class Synchronizer {
let noteIds = await Folder.syncedNoteIds(); let noteIds = await Folder.syncedNoteIds();
for (let i = 0; i < noteIds.length; i++) { for (let i = 0; i < noteIds.length; i++) {
if (remoteIds.indexOf(noteIds[i]) < 0) { if (remoteIds.indexOf(noteIds[i]) < 0) {
console.info('Sync action (4): deleteLocal: ' + noteIds[i]); this.logger().debug('Sync action (4): deleteLocal: ' + noteIds[i]);
await Note.delete(noteIds[i], { trackDeleted: false }); await Note.delete(noteIds[i], { trackDeleted: false });
} }
} }
// Number of sync items (Created, updated, deleted Local/Remote)
// Total number of items
return Promise.resolve(); return Promise.resolve();
} }

View File

@ -1,3 +1,5 @@
import moment from 'moment';
let time = { let time = {
unix() { unix() {
@ -10,7 +12,11 @@ let time = {
unixMsToS(ms) { unixMsToS(ms) {
return Math.floor(ms / 1000); return Math.floor(ms / 1000);
} },
unixMsToIso(ms) {
return moment.unix(ms / 1000).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z';
},
} }