mirror of
https://github.com/leanote/desktop-app.git
synced 2025-10-13 22:57:22 +00:00
535 lines
13 KiB
JavaScript
535 lines
13 KiB
JavaScript
var db = require('db');
|
|
var async = require('async');
|
|
var Common = require('common');
|
|
var User = require('user');
|
|
var Tags = db.tags;
|
|
var needle = require('needle');
|
|
var fs = require('fs');
|
|
var Api = require('api');
|
|
var Notebook = require('notebook');
|
|
var Note = require('note');
|
|
|
|
function log(o) {
|
|
console.log(o);
|
|
}
|
|
|
|
// 同步服务
|
|
var Sync = {
|
|
// 同步的信息, 返回给调用者
|
|
_syncInfo: {
|
|
notebook: {changeAdds: [], adds: [], deletes: [], updates: []},
|
|
note: {changeAdds: [], adds: [], deletes: [], updates: [], conflicts: []},
|
|
tag: {}
|
|
},
|
|
/*
|
|
_sendInfo: {
|
|
notebook: {adds: [], deletes: [], updates: []},
|
|
note: {adds: [], deletes: [], updates: [], conflicts: []},
|
|
tag: {}
|
|
},
|
|
*/
|
|
_needIncrSyncAgain: false,
|
|
|
|
// notebook
|
|
_syncNotebookIsLastChunk: false,
|
|
_totalSyncNotebookNum: 0, // 需要同步的数量
|
|
_tocalHasSyncNotebookNum: 0, // 已同步的数量
|
|
_notebookMaxEntry: 1,
|
|
|
|
// note
|
|
_syncNoteIsLastChunk: false,
|
|
_totalSyncNoteNum: 0, // 需要同步的数量
|
|
_noteMaxEntry: 1,
|
|
|
|
_initSyncInfo: function() {
|
|
var me = this;
|
|
|
|
// notebook
|
|
me._syncNotebookIsLastChunk = false;
|
|
me._totalSyncNotebookNum = 0;
|
|
me._totalHasSyncNotebookNum = 0;
|
|
me._lockNotebook = 1;
|
|
|
|
// note
|
|
me._syncNoteIsLastChunk = false;
|
|
me._totalSyncNoteNum = 0;
|
|
me._totalHasSyncNoteNum = 0;
|
|
me._lockNote = 1;
|
|
|
|
// 同步信息
|
|
me._syncInfo = {
|
|
notebook: {ok: false, changeAdds: [], adds: [], deletes: [], updates: []},
|
|
note: {ok: false, adds: [], changeAdds: [], deletes: [], updates: [], conflicts: []},
|
|
tag: {ok: false, adds: [], changeAdds: [], deletes: [], updates: [], conflicts: []},
|
|
};
|
|
|
|
// 发送改变信息
|
|
/*
|
|
me._sendInfo = {
|
|
notebook: {ok: false, adds: [], deletes: [], updates: []},
|
|
note: {ok: false, adds: [], deletes: [], updates: [], conflicts: []},
|
|
tag: {ok: false}
|
|
};
|
|
*/
|
|
// 是否还要来一次增量同步 ?
|
|
me._needIncrSyncAgain = false;
|
|
},
|
|
|
|
//---------------
|
|
// notebook
|
|
//---------------
|
|
|
|
// 增加, 有锁
|
|
_lockNotebook: 1,
|
|
_addSyncNotebookNum: function() {
|
|
var me = this;
|
|
if(me._lockNotebook) {
|
|
me._lockNotebook = 0;
|
|
me._totalHasSyncNotebookNum++;
|
|
me._lockNotebook = 1;
|
|
} else {
|
|
me._addSyncNotebookNum();
|
|
}
|
|
},
|
|
|
|
// 同步笔记本
|
|
_syncNotebookToLocal: function(notebooks, callback) {
|
|
var me = this;
|
|
function canCall() {
|
|
// 是最后一块, 且
|
|
me._addSyncNotebookNum();
|
|
log(me._totalHasSyncNotebookNum + ' ' + me._totalSyncNotebookNum);
|
|
if(me._syncNotebookIsLastChunk && me._totalHasSyncNotebookNum >= me._totalSyncNotebookNum) {
|
|
// 防止多次callback
|
|
if(!me._syncInfo.notebook.ok) {
|
|
me._syncInfo.notebook.ok = true;
|
|
callback && callback(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!notebooks || notebooks.length == 0) {
|
|
return canCall();
|
|
}
|
|
|
|
for(var i in notebooks) {
|
|
var notebook = notebooks[i];
|
|
// 得到本地的, 与之对比
|
|
|
|
(function(notebook) {
|
|
|
|
var usn = notebook.Usn;
|
|
var notebookId = notebook.NotebookId;
|
|
|
|
// 1) 服务器端删除了, 本地肯定删除
|
|
if(notebook.IsDeleted) {
|
|
log('delete: ');
|
|
log(notebook);
|
|
Notebook.deleteNotebookForce(notebookId, function() {
|
|
me._syncInfo.notebook.deletes.push(notebookId);
|
|
canCall();
|
|
});
|
|
return;
|
|
}
|
|
// 2) 查看本地的, 与本地合并
|
|
Notebook.getNotebook(notebookId, function(notebookLocal) {
|
|
// 2.1 本地没有, 表示是新建
|
|
if(!notebookLocal) {
|
|
log('add: ...')
|
|
// TODO
|
|
Notebook.addNotebookForce(notebook, function(notebook) {
|
|
me._syncInfo.notebook.adds.push(notebook);
|
|
canCall();
|
|
});
|
|
} else {
|
|
// 2.2 本地是否修改了, 需要合并, 使用服务端的数据
|
|
if(notebook.IsDirty) {
|
|
log('冲突....')
|
|
// 2.3 服务器是最新的, 用服务器的
|
|
} else {
|
|
}
|
|
// 这里都是用服务器端的数据, 不处理冲突
|
|
Notebook.updateNotebookForce(notebook, function(notebook) {
|
|
if(notebook) {
|
|
me._syncInfo.notebook.updates.push(notebook);
|
|
}
|
|
canCall();
|
|
})
|
|
}
|
|
});
|
|
})(notebook);
|
|
}
|
|
},
|
|
|
|
syncNotebook: function(afterUsn, callback) {
|
|
var me = this;
|
|
Api.getSyncNotebooks(afterUsn, me._notebookMaxEntry, function(notebooks) {
|
|
log('syncNotebook')
|
|
log(notebooks);
|
|
if(Common.isOk(notebooks)) {
|
|
me._totalSyncNotebookNum += notebooks.length;
|
|
// 证明可能还有要同步的
|
|
if(notebooks.length == me._notebookMaxEntry) {
|
|
me._syncNotebookToLocal(notebooks);
|
|
var last = notebooks[notebooks.length-1];
|
|
me.syncNotebook(last.Usn, callback);
|
|
} else {
|
|
log('no more');
|
|
me._syncNotebookIsLastChunk = true;
|
|
me._syncNotebookToLocal(notebooks, callback);
|
|
}
|
|
} else {
|
|
// 同步失败
|
|
me._syncInfo.notebook.ok = false;
|
|
me._syncInfo.notebook.msg = "cann't get all chunks";
|
|
callback && callback(false);
|
|
}
|
|
});
|
|
},
|
|
|
|
|
|
//-------------
|
|
// note
|
|
//-------------
|
|
|
|
// 增加, 有锁
|
|
_lockNote: 1,
|
|
_addSyncNoteNum: function() {
|
|
var me = this;
|
|
if(me._lockNote) {
|
|
me._lockNote = 0;
|
|
me._totalHasSyncNoteNum++;
|
|
me._lockNote = 1;
|
|
} else {
|
|
me._addSyncNoteNum();
|
|
}
|
|
},
|
|
|
|
// 同步笔记到本地
|
|
_syncNoteToLocal: function(notes, callback) {
|
|
var me = this;
|
|
function canCall(isEmpty) {
|
|
// 为空时来判断是最后一次了, 可以之前的还没处理完
|
|
if(isEmpty && me._totalHasSyncNoteNum < me._totalSyncNoteNum) {
|
|
return;
|
|
}
|
|
|
|
// 是最后一块, 且
|
|
me._addSyncNoteNum();
|
|
// log('notes: ' + me._totalHasSyncNoteNum + ' ' + me._totalSyncNoteNum + ' ' + me._syncNoteIsLastChunk);
|
|
if(me._syncNoteIsLastChunk && me._totalHasSyncNoteNum >= me._totalSyncNoteNum) {
|
|
// 防止多次callback
|
|
if(!me._syncInfo.note.ok) {
|
|
log('note->next');
|
|
me._syncInfo.note.ok = true;
|
|
callback && callback(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 为什么会出现最后 > 的情况, 是因为这里length == 0 也判断了
|
|
if(!notes || notes.length == 0) {
|
|
return canCall(true);
|
|
}
|
|
|
|
for(var i in notes) {
|
|
var note = notes[i];
|
|
// 得到本地的, 与之对比
|
|
|
|
(function(note) {
|
|
|
|
var usn = note.Usn;
|
|
var noteId = note.NoteId;
|
|
|
|
// 1) 服务器端删除了, 本地肯定删除
|
|
if(note.IsDeleted) {
|
|
log('delete: ');
|
|
log(note);
|
|
Note.deleteNoteForce(noteId, function() {
|
|
me._syncInfo.note.deletes.push(noteId);
|
|
canCall();
|
|
});
|
|
return;
|
|
}
|
|
// 2) 查看本地的, 与本地合并
|
|
Note.getNote(noteId, function(noteLocal) {
|
|
// 2.1 本地没有, 表示是新建
|
|
if(!noteLocal) {
|
|
log('add: ...')
|
|
Note.addNoteForce(note, function(note) {
|
|
me._syncInfo.note.adds.push(note);
|
|
return canCall();
|
|
});
|
|
} else {
|
|
// 2.2 本地是否修改了, 冲突, 报告给前端, 前端处理
|
|
// 冲突, 将本地修改的笔记复制一份(设置冲突字段, ConflictNoteId), 远程的覆盖本地的
|
|
if(noteLocal.IsDirty) {
|
|
log('冲突....')
|
|
me._syncInfo.note.conflicts.push(note);
|
|
return canCall();
|
|
// 2.3 服务器是最新的, 用服务器的
|
|
} else {
|
|
// 服务器是最新的, 本地没动过, 则覆盖之
|
|
Note.updateNoteForce(note, function(note) {
|
|
if(note) {
|
|
me._syncInfo.note.updates.push(note);
|
|
}
|
|
canCall();
|
|
});
|
|
}
|
|
}
|
|
});
|
|
})(note);
|
|
}
|
|
},
|
|
|
|
syncNote: function(afterUsn, callback) {
|
|
var me = this;
|
|
Api.getSyncNotes(afterUsn, me._noteMaxEntry, function(notes) {
|
|
log('syncNote')
|
|
log(notes);
|
|
if(Common.isOk(notes)) {
|
|
me._totalSyncNoteNum += notes.length;
|
|
// 证明可能还有要同步的
|
|
if(notes.length == me._noteMaxEntry) {
|
|
me._syncNoteToLocal(notes, callback);
|
|
var last = notes[notes.length-1];
|
|
me.syncNote(last.Usn, callback);
|
|
} else {
|
|
log('no more');
|
|
me._syncNoteIsLastChunk = true;
|
|
me._syncNoteToLocal(notes, callback);
|
|
}
|
|
} else {
|
|
// 同步失败
|
|
me._syncInfo.note.ok = false;
|
|
me._syncInfo.note.msg = "cann't get all chunks";
|
|
callback && callback(false);
|
|
}
|
|
});
|
|
},
|
|
|
|
|
|
// 同步标签
|
|
syncTag: function(afterUsn, callback) {
|
|
callback && callback(true);
|
|
},
|
|
|
|
// 记录LastSyncUsn, LastUpdateTime 同步时间
|
|
updateLastSyncState: function(callback) {
|
|
var me = this;
|
|
User.updateLastSyncState(function() {
|
|
callback();
|
|
});
|
|
},
|
|
|
|
// 全量同步
|
|
fullSync: function(callback) {
|
|
var me = this;
|
|
me._initSyncInfo();
|
|
|
|
// 同步笔记本
|
|
me.syncNotebook(-1, function(ok) {
|
|
if(ok) {
|
|
log('------------------')
|
|
// 同步笔记
|
|
me.syncNote(-1, function(ok) {
|
|
if(ok) {
|
|
// 同步标签
|
|
me.syncTag(-1, function() {
|
|
// 更新上次同步时间
|
|
me.updateLastSyncState(function() {
|
|
// send changes
|
|
me.sendChanges();
|
|
});
|
|
});
|
|
} else {
|
|
callback && callback(me._syncInfo);
|
|
}
|
|
});
|
|
} else {
|
|
log('no-------')
|
|
callback && callback(me._syncInfo);
|
|
}
|
|
});
|
|
},
|
|
|
|
_notebookWeb: null,
|
|
_noteWeb: null,
|
|
// 处理冲突
|
|
fixConflicts: function(callback) {
|
|
var me = this;
|
|
var afterInfo = me._syncInfo;
|
|
log('处理冲突....')
|
|
log(me._syncInfo);
|
|
Notebook.fixConflicts(me._syncInfo.notebook, me._notebookWeb);
|
|
Note.fixConflicts(me._syncInfo.note, me._noteWeb, function() {
|
|
// 避免无限循环, 别send changes了
|
|
if(!me._needIncrSyncAgain) {
|
|
// send changes
|
|
callback && callback();
|
|
}
|
|
});
|
|
},
|
|
|
|
// 增量同步
|
|
incrSync: function(notebookWeb, noteWeb) {
|
|
var me = this;
|
|
me._notebookWeb = notebookWeb;
|
|
me._noteWeb = noteWeb;
|
|
me._initSyncInfo();
|
|
log('inc sync start');
|
|
|
|
// 得到当前LastSyncUsn
|
|
User.getLastSyncState(function(lastSyncUsn, lastSyncTime) {
|
|
// 没有上次同步的时间, 则需要进行一次全量同步, 不可能会发生
|
|
if(!lastSyncUsn) {
|
|
log('error!!');
|
|
return;
|
|
}
|
|
|
|
// 同步笔记本
|
|
me.syncNotebook(lastSyncUsn, function(ok) {
|
|
if(ok) {
|
|
log('-------incr notebook ok-----------')
|
|
// 同步笔记
|
|
me.syncNote(lastSyncUsn, function(ok) {
|
|
if(ok) {
|
|
log('-------incr note ok-----------')
|
|
// 同步标签
|
|
me.syncTag(lastSyncUsn, function() {
|
|
log('-------incr tag ok-----------')
|
|
// 更新上次同步时间
|
|
me.updateLastSyncState(function() {
|
|
// send changes
|
|
me.sendChanges();
|
|
});
|
|
});
|
|
} else {
|
|
log('-------incr note not ok-----------')
|
|
me.fixConflicts();
|
|
}
|
|
});
|
|
} else {
|
|
me.fixConflicts();
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
//---------
|
|
// 发送改变
|
|
//---------
|
|
|
|
// 发送笔记本的更改
|
|
sendNotebookChanges: function(callback) {
|
|
var me = this;
|
|
// 获取所有笔记本的更改
|
|
Notebook.getDirtyNotebooks(function(notebooks) {
|
|
log('dirty notebooks');
|
|
log(notebooks);
|
|
if(!notebooks) {
|
|
callback && callback();
|
|
} else {
|
|
// 调api, 所有执行后再callback();
|
|
// 一个一个同步执行, 因为要有
|
|
async.eachSeries(notebooks, function(notebook, cb) {
|
|
var api = Api.updateNotebook;
|
|
if(notebook.LocalIsNew) {
|
|
api = Api.addNotebook;
|
|
} else if(notebook.LocalIsDelete) {
|
|
api = Api.deleteNotebook;
|
|
}
|
|
|
|
api.call(Api, notebook, function(newNotebook) {
|
|
// 更新失败
|
|
if(!newNotebook) {
|
|
return cb();
|
|
}
|
|
// 更新成功, 是否有冲突?
|
|
// newNotebook是服务器上的笔记本
|
|
// 没有更新成功
|
|
if(!newNotebook.NotebookId) {
|
|
if(newNotebook.Msg == 'conflict') {
|
|
me._syncInfo.notebook.conflicts.push(newNotebook);
|
|
// me._sendInfo.notebook.conflicts.push(newNotebook);
|
|
}
|
|
}
|
|
else {
|
|
// 更新
|
|
Notebook.updateNotebookForceForSendChange(notebook.NotebookId, newNotebook);
|
|
|
|
if(notebook.LocalIsNew) {
|
|
newNotebook.OldNotebookId = notebook.NotebookId;
|
|
me._syncInfo.notebook.changeAdds.push(newNotebook);
|
|
// me._sendInfo.notebook.adds.push(newNotebook);
|
|
} else {
|
|
me._syncInfo.notebook.updates.push(newNotebook);
|
|
// me._sendInfo.notebook.updates.push(newNotebook);
|
|
}
|
|
}
|
|
// 如果之前都很正常
|
|
if(!me._needIncrSyncAgain) {
|
|
// 检查是否有问题
|
|
if(User.getLastSyncUsn() + 1 == newNotebook.Usn) {
|
|
// 更新到本地lastSyncUsn
|
|
User.updateLastSyncUsn(newNotebook.Usn);
|
|
} else {
|
|
// newNotebook.Usn > User.getLastSyncUsn + 1, 表示服务器端在本次同步后, sendChanges之前有更新
|
|
// 那么, 还需要来一次incrSync, 之后
|
|
me._needIncrSyncAgain = true;
|
|
}
|
|
}
|
|
cb();
|
|
});
|
|
}, function() {
|
|
callback && callback();
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 发送笔记改变
|
|
sendNoteChanges: function(callback) {
|
|
log('send note changes');
|
|
callback && callback();
|
|
},
|
|
// 发送标签改变
|
|
sendTagChanges: function(callback) {
|
|
log('send note changes');
|
|
callback && callback();
|
|
},
|
|
|
|
sendChanges: function() {
|
|
var me = this;
|
|
log('send changes before...')
|
|
// 先处理冲突, 可以同时进行
|
|
me.fixConflicts(function() {
|
|
// send changes
|
|
log('send changes');
|
|
me._initSyncInfo(); // 重新初始化[]
|
|
async.series([
|
|
function(cb) {
|
|
me.sendNotebookChanges(cb);
|
|
},
|
|
function(cb) {
|
|
me.sendNoteChanges(cb);
|
|
},
|
|
function(cb) {
|
|
me.sendTagChanges(cb);
|
|
}
|
|
], function() {
|
|
// 重新再来一次增量同步
|
|
if(me._needIncrSyncAgain) {
|
|
log('-- _needIncrSyncAgain -- ')
|
|
me.incrSync(me._notebookWeb, me._noteWeb);
|
|
} else {
|
|
log('send changes 后解决冲突');
|
|
me.fixConflicts();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
module.exports = Sync; |