mirror of
https://github.com/leanote/desktop-app.git
synced 2025-10-14 15:11:24 +00:00
1212 lines
33 KiB
JavaScript
1212 lines
33 KiB
JavaScript
var db = require('db');
|
||
var async = require('async');
|
||
var Common = require('common');
|
||
var User = require('user');
|
||
var Tag = require('tag');
|
||
var Tags = db.tags;
|
||
var needle = require('needle');
|
||
var fs = require('fs');
|
||
var Api = require('api');
|
||
var Notebook = require('notebook');
|
||
var Note = require('note');
|
||
var Web = require('web');
|
||
|
||
function log(o) {
|
||
// console.log(o);
|
||
}
|
||
|
||
// 同步服务
|
||
/*
|
||
syncProgress 设置
|
||
两阶段, 各个50%
|
||
1. <-
|
||
notebook 10 -> 10%
|
||
note 30 -> 40%
|
||
tag 10 -> 50%
|
||
|
||
2. ->
|
||
notebook 10 -> 60%
|
||
note 30 -> 90%
|
||
tag 10 -> 100%
|
||
*/
|
||
var Sync = {
|
||
// 同步的信息, 返回给调用者
|
||
_syncInfo: {
|
||
notebook: {changeAdds: [], changeConflicts: [], adds: [], deletes: [], updates: [], changeUpdates: []},
|
||
note: {changeAdds: [], changeConflicts: [], adds: [], deletes: [], updates: [], conflicts: [], errors: []},
|
||
tag: {}
|
||
},
|
||
/*
|
||
_sendInfo: {
|
||
notebook: {adds: [], deletes: [], updates: []},
|
||
note: {adds: [], deletes: [], updates: [], conflicts: []},
|
||
tag: {}
|
||
},
|
||
*/
|
||
_needIncrSyncAgain: false,
|
||
|
||
// notebook
|
||
_syncNotebookIsLastChunk: false,
|
||
_totalSyncNotebookNum: 0, // 需要同步的数量
|
||
_tocalHasSyncNotebookNum: 0, // 已同步的数量
|
||
_notebookMaxEntry: 200,
|
||
|
||
// note
|
||
_syncNoteIsLastChunk: false,
|
||
_totalSyncNoteNum: 0, // 需要同步的数量
|
||
_noteMaxEntry: 200,
|
||
|
||
// tag
|
||
_syncTagIsLastChunk: false,
|
||
_totalSyncTagNum: 0, // 需要同步的数量
|
||
_tagMaxEntry: 200,
|
||
|
||
_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;
|
||
|
||
// tag
|
||
me._syncTagIsLastChunk = false;
|
||
me._totalSyncTagNum = 0;
|
||
me._totalHasSyncTagNum = 0;
|
||
me._lockTag = 1;
|
||
|
||
// 同步信息
|
||
me._syncInfo = {
|
||
notebook: {ok: false, changeAdds: [], changeConflicts: [], changeNeedAdds: [], adds: [], deletes: [], updates: [], changeUpdates: []},
|
||
note: {ok: false, adds: [], changeAdds: [], changeConflicts: [], changeUpdates:[], changeNeedAdds: [], deletes: [], updates: [], conflicts: [], errors: []},
|
||
tag: {ok: false, adds: [], changeAdds: [], changeConflicts: [], changeNeedAdds: [], deletes: [], updates: [], conflicts: []},
|
||
};
|
||
|
||
// 发送改变信息
|
||
/*
|
||
me._sendInfo = {
|
||
notebook: {ok: false, adds: [], deletes: [], updates: []},
|
||
note: {ok: false, adds: [], deletes: [], updates: [], conflicts: []},
|
||
tag: {ok: false}
|
||
};
|
||
*/
|
||
// 是否还要来一次增量同步 ?
|
||
me._needIncrSyncAgain = false;
|
||
},
|
||
|
||
// 停止同步
|
||
// 第一次,note.html -> login.html(使得_stop: true), -> note.html fullSync,一看,是stop
|
||
_stop: false,
|
||
stop: function() {
|
||
var me = this;
|
||
me._stop = true;
|
||
},
|
||
isStop: function() {
|
||
var me = this;
|
||
if(me._stop) {
|
||
me._stop = false;
|
||
return true;
|
||
}
|
||
return 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();
|
||
// console.log(me._syncNotebookIsLastChunk);
|
||
// console.log(me._totalHasSyncNotebookNum + ' ' + me._totalSyncNotebookNum);
|
||
// console.log(me._syncInfo.notebook.ok);
|
||
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];
|
||
// 得到本地的, 与之对比
|
||
|
||
Web.syncProcess('notebook', notebook.Title);
|
||
|
||
(function(notebook) {
|
||
|
||
var usn = notebook.Usn;
|
||
var notebookId = notebook.NotebookId; // 服务器端的
|
||
|
||
// 1) 服务器端删除了, 本地肯定删除
|
||
if(notebook.IsDeleted) {
|
||
// console.log('delete: ');
|
||
// console.log(notebook);
|
||
Notebook.getNotebookIdByServerNotebookId(notebookId, function(localNotebookId) {
|
||
Notebook.deleteNotebookForce(notebookId, function() {
|
||
// me._syncInfo.notebook.deletes.push(localNotebookId);
|
||
me.fixSynced('notebook', 'deletes', localNotebookId);
|
||
canCall();
|
||
})
|
||
});
|
||
return;
|
||
}
|
||
// 2) 查看本地的, 与本地合并
|
||
Notebook.getNotebookByServerNotebookId(notebookId, function(notebookLocal) {
|
||
// 2.1 本地没有, 表示是新建
|
||
if(!notebookLocal) {
|
||
console.log(' add addNotebookForce...', notebook.Title)
|
||
// TODO
|
||
Notebook.addNotebookForce(notebook, function(notebook) {
|
||
// 要最后一起添加, 因为有层级关系
|
||
console.log(' ', notebook.Title)
|
||
me._syncInfo.notebook.adds.push(notebook);
|
||
// me.fixSynced('notebook', 'adds', notebook);
|
||
canCall();
|
||
});
|
||
} else {
|
||
// 如果Usn一样, 表示服务器端并没有修改
|
||
if(notebookLocal.Usn === notebook.Usn) {
|
||
console.log('notebookLocal 如果Usn一样, 表示服务器端并没有修改');
|
||
return canCall();
|
||
}
|
||
|
||
// 2.2 本地是否修改了, 需要合并, 使用服务端的数据
|
||
if(notebook.IsDirty) {
|
||
console.log('冲突....')
|
||
// 2.3 服务器是最新的, 用服务器的
|
||
} else {
|
||
}
|
||
// 这里都是用服务器端的数据, 不处理冲突
|
||
Notebook.updateNotebookForce(notebook, notebookLocal, function(notebook) {
|
||
if(notebook) {
|
||
// 前端一起渲染
|
||
me._syncInfo.notebook.updates.push(notebook);
|
||
// me.fixSynced('notebook', 'updates', notebook);
|
||
}
|
||
canCall();
|
||
})
|
||
}
|
||
});
|
||
})(notebook);
|
||
}
|
||
},
|
||
|
||
syncNotebook: function(afterUsn, callback) {
|
||
var me = this;
|
||
if(me.isStop()) {
|
||
return;
|
||
}
|
||
Api.getSyncNotebooks(afterUsn, me._notebookMaxEntry, function(notebooks) {
|
||
console.log(' syncNotebook', notebooks)
|
||
// console.log(notebooks);
|
||
if(Common.isOk(notebooks)) {
|
||
me._totalSyncNotebookNum += notebooks.length;
|
||
// 证明可能还有要同步的
|
||
if(notebooks.length == me._notebookMaxEntry) {
|
||
me._syncNotebookToLocal(notebooks, callback);
|
||
var last = notebooks[notebooks.length-1];
|
||
me.syncNotebook(last.Usn, callback);
|
||
|
||
// 更新Usn
|
||
me.updateSyncUsn('NotebookUsn', last.Usn);
|
||
|
||
} else {
|
||
console.log(' no more notebooks');
|
||
me._syncNotebookIsLastChunk = true;
|
||
me._syncNotebookToLocal(notebooks, callback);
|
||
|
||
if (notebooks.length) {
|
||
var last = notebooks[notebooks.length-1];
|
||
me.updateSyncUsn('NotebookUsn', last.Usn);
|
||
}
|
||
}
|
||
} 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];
|
||
// 得到本地的, 与之对比
|
||
Web.syncProcess('note', note.Title);
|
||
|
||
(function(note) {
|
||
|
||
var usn = note.Usn;
|
||
var noteId = note.NoteId;
|
||
|
||
// 1) 服务器端删除了, 本地肯定删除
|
||
if(note.IsDeleted) {
|
||
console.log(' delete: ', note);
|
||
Note.getNoteIdByServerNoteId(noteId, function(localNoteId) {
|
||
Note.deleteNoteForce(noteId, function() {
|
||
// me._syncInfo.note.deletes.push(localNoteId);
|
||
me.fixSynced('note', 'deletes', localNoteId);
|
||
canCall();
|
||
});
|
||
});
|
||
return;
|
||
}
|
||
// 2) 查看本地的, 与本地合并
|
||
Note.getNoteByServerNoteId(noteId, function(noteLocal) {
|
||
// 2.1 本地没有, 表示是新建
|
||
if(!noteLocal) {
|
||
console.log(' add note', note);
|
||
Note.addNoteForce(note, function(note) {
|
||
// me._syncInfo.note.adds.push(note);
|
||
me.fixSynced('note', 'adds', note);
|
||
return canCall();
|
||
});
|
||
} else {
|
||
// 如果Usn一样, 表示服务器端并没有修改
|
||
if(noteLocal.Usn === note.Usn) {
|
||
console.log(' note 如果Usn一样, 表示服务器端并没有修改');
|
||
return canCall();
|
||
}
|
||
|
||
// 2.2 本地是否修改了, 冲突, 报告给前端, 前端处理
|
||
// 冲突, 将本地修改的笔记复制一份(设置冲突字段, ConflictNoteId), 远程的覆盖本地的
|
||
// 新方法: 冲突后, 得到最新内容, 看是否与本地内容一致, 如果一致, 则不冲突, 其它数据用服务器上的
|
||
if(noteLocal.IsDirty) {
|
||
console.log(' note 冲突 serverNoteId: ' + noteId, note.Title);
|
||
// console.log(noteLocal.NoteId);
|
||
// console.log(noteLocal.IsDirty);
|
||
// console.log(noteLocal);
|
||
// note.ServerNoteId = note.NoteId;
|
||
// note.NoteId = noteLocal.NoteId;
|
||
|
||
Note.getNoteContentFromServer(noteId, function (content) {
|
||
// 表示没有获取到content, 则只能标志为冲突了
|
||
// 内容不一样, 也标为冲突
|
||
if (content === false || content != noteLocal.Content) {
|
||
me._syncInfo.note.conflicts.push({server: note, local: noteLocal});
|
||
// me.fixSynced('note', 'conflicts', {server: note, local: noteLocal});
|
||
}
|
||
// 否则, 内容一样, 标为不冲突, 需要更新
|
||
else {
|
||
|
||
// 2.3 服务器是最新的, 用服务器的
|
||
// 服务器是最新的, 本地没动过, 则覆盖之
|
||
Note.updateNoteForce(note, function(note) {
|
||
if(note) {
|
||
// me._syncInfo.note.updates.push(note);
|
||
me.fixSynced('note', 'updates', note);
|
||
}
|
||
canCall();
|
||
}, false);
|
||
}
|
||
});
|
||
|
||
return canCall();
|
||
// 2.3 服务器是最新的, 用服务器的
|
||
} else {
|
||
// 服务器是最新的, 本地没动过, 则覆盖之
|
||
Note.updateNoteForce(note, function(note) {
|
||
if(note) {
|
||
// me._syncInfo.note.updates.push(note);
|
||
me.fixSynced('note', 'updates', note);
|
||
}
|
||
canCall();
|
||
});
|
||
}
|
||
}
|
||
});
|
||
})(note);
|
||
}
|
||
},
|
||
|
||
syncNote: function(afterUsn, callback) {
|
||
var me = this;
|
||
if(me.isStop()) {
|
||
return;
|
||
}
|
||
|
||
console.log(' pull notes from server...');
|
||
Api.getSyncNotes(afterUsn, me._noteMaxEntry, function(notes) {
|
||
// console.log('syncNote---');
|
||
console.log(' notes:', notes);
|
||
if(Common.isOk(notes)) {
|
||
me._totalSyncNoteNum += notes.length;
|
||
// 证明可能还有要同步的
|
||
if(notes.length == me._noteMaxEntry) {
|
||
me._syncNoteToLocal(notes, callback);
|
||
var last = notes[notes.length-1];
|
||
|
||
// 500ms延迟
|
||
setTimeout(function () {
|
||
me.syncNote(last.Usn, callback);
|
||
}, 500);
|
||
|
||
// 更新Usn
|
||
me.updateSyncUsn('NoteUsn', last.Usn);
|
||
|
||
} else {
|
||
console.log(' no more notes');
|
||
me._syncNoteIsLastChunk = true;
|
||
me._syncNoteToLocal(notes, callback);
|
||
|
||
if (notes.length) {
|
||
var last = notes[notes.length-1];
|
||
me.updateSyncUsn('NoteUsn', last.Usn);
|
||
}
|
||
}
|
||
} else {
|
||
// 同步失败
|
||
me._syncInfo.note.ok = false;
|
||
me._syncInfo.note.msg = "cann't get all chunks";
|
||
console.error(' pull notes error', notes)
|
||
callback && callback(false);
|
||
}
|
||
});
|
||
},
|
||
|
||
|
||
//----------------
|
||
// 同步标签
|
||
// ---------------
|
||
// 增加, 有锁
|
||
_lockTag: 1,
|
||
_addSyncTagNum: function() {
|
||
var me = this;
|
||
if(me._lockTag) {
|
||
me._lockTag = 0;
|
||
me._totalHasSyncTagNum++;
|
||
me._lockTag = 1;
|
||
} else {
|
||
me._addSyncTagNum();
|
||
}
|
||
},
|
||
|
||
// 同步标签到本地
|
||
_syncTagToLocal: function(tags, callback) {
|
||
var me = this;
|
||
function canCall(isEmpty) {
|
||
// 为空时来判断是最后一次了, 可以之前的还没处理完
|
||
if(isEmpty && me._totalHasSyncTagNum < me._totalSyncTagNum) {
|
||
return;
|
||
}
|
||
|
||
// 是最后一块, 且
|
||
me._addSyncTagNum();
|
||
// log('tags: ' + me._totalHasSyncNoteNum + ' ' + me._totalSyncNoteNum + ' ' + me._syncNoteIsLastChunk);
|
||
if(me._syncTagIsLastChunk && me._totalHasSyncTagNum >= me._totalSyncTagNum) {
|
||
// 防止多次callback
|
||
if(!me._syncInfo.tag.ok) {
|
||
log('tag->next');
|
||
me._syncInfo.tag.ok = true;
|
||
callback && callback(true);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 为什么会出现最后 > 的情况, 是因为这里length == 0 也判断了
|
||
if(!tags || tags.length == 0) {
|
||
return canCall(true);
|
||
}
|
||
|
||
for(var i in tags) {
|
||
var tag = tags[i];
|
||
// 得到本地的, 与之对比
|
||
Web.syncProcess('tag', tag.Tag);
|
||
|
||
(function(tag) {
|
||
|
||
var usn = tag.Usn;
|
||
var tagId = tag.TagId;
|
||
|
||
// 1) 服务器端删除了, 本地肯定删除
|
||
if(tag.IsDeleted) {
|
||
console.log(' delete tag: ', tag);
|
||
Tag.deleteTag(tag.Tag, function() {
|
||
// me._syncInfo.tag.deletes.push(tag.Tag);
|
||
me.fixSynced('tag', 'deletes', tag.Tag);
|
||
canCall();
|
||
}, true, me.fullSyncStart);
|
||
return;
|
||
}
|
||
// 2) 查看本地的, 与本地合并
|
||
Tag.getTag(tag.Tag, function(tagLocal) {
|
||
// 2.1 本地没有, 表示是新建
|
||
if(!tagLocal) {
|
||
console.log(' add tag: ...')
|
||
Tag.addOrUpdateTag(tag.Tag, function(tagAdded) {
|
||
// me._syncInfo.tag.adds.push(tagAdded);
|
||
me.fixSynced('tag', 'adds', tagAdded);
|
||
return canCall();
|
||
}, true, usn);
|
||
} else {
|
||
// 本地有, 不用操作
|
||
Tag.setNotDirtyAndUsn(tag.Tag, tag.Usn);
|
||
return canCall();
|
||
}
|
||
});
|
||
})(tag);
|
||
}
|
||
},
|
||
syncTag: function(afterUsn, callback) {
|
||
var me = this;
|
||
if(me.isStop()) {
|
||
return;
|
||
}
|
||
Api.getSyncTags(afterUsn, me._tagMaxEntry, function(tags) {
|
||
// console.log(afterUsn + ' syncTags------------------------------------')
|
||
// console.log(tags);
|
||
if(Common.isOk(tags)) {
|
||
me._totalSyncTagNum += tags.length;
|
||
// 证明可能还有要同步的
|
||
if(tags.length == me._tagMaxEntry) {
|
||
me._syncTagToLocal(tags, callback);
|
||
var last = tags[tags.length-1];
|
||
me.syncTag(last.Usn, callback);
|
||
|
||
// 更新Usn
|
||
me.updateSyncUsn('TagUsn', last.Usn);
|
||
} else {
|
||
log('no more');
|
||
me._syncTagIsLastChunk = true;
|
||
me._syncTagToLocal(tags, callback);
|
||
|
||
if (tags.length) {
|
||
var last = tags[tags.length-1];
|
||
me.updateSyncUsn('TagUsn', last.Usn);
|
||
}
|
||
}
|
||
} else {
|
||
// 同步失败
|
||
me._syncInfo.tag.ok = false;
|
||
me._syncInfo.tag.msg = "cann't get all chunks";
|
||
console.error(' sync tags error...')
|
||
// console.log(tags);
|
||
callback && callback(false);
|
||
}
|
||
});
|
||
},
|
||
|
||
//----------
|
||
|
||
// 记录LastSyncUsn, LastUpdateTime 同步时间
|
||
updateLastSyncState: function(callback) {
|
||
var me = this;
|
||
User.updateLastSyncState(function() {
|
||
callback();
|
||
});
|
||
},
|
||
|
||
// 为了避免全都重新来过, 这里保存每次
|
||
updateSyncUsn: function (type, usn) {
|
||
// console.error('---')
|
||
// console.log(type + ' = ' + usn);
|
||
User.updateEachSyncState(type, usn, function() {});
|
||
},
|
||
|
||
// 全量同步
|
||
fullSync: function(callback) {
|
||
var me = this;
|
||
me._stop = false;
|
||
me._initSyncInfo();
|
||
me.fullSyncStart = true;
|
||
|
||
User.getAllLastSyncState(function(lastUsn, notebookUsn, noteUsn, tagUsn) {
|
||
// 不可能会有lastUsn吧
|
||
if (lastUsn && lastUsn > 0) {
|
||
notebookUsn = -1;
|
||
noteUsn = -1;
|
||
tagUsn = -1;
|
||
}
|
||
|
||
if (!notebookUsn) {
|
||
notebookUsn = -1;
|
||
}
|
||
if (!noteUsn) {
|
||
noteUsn = -1;
|
||
}
|
||
if (!tagUsn) {
|
||
tagUsn = -1;
|
||
}
|
||
|
||
console.log('fullSync ' + notebookUsn + ' ' + noteUsn + ' ' + tagUsn);
|
||
|
||
// Web.syncNotebookFinish();
|
||
// 同步笔记本
|
||
me.syncNotebook(notebookUsn, function(ok) {
|
||
if(ok) {
|
||
// Web.syncNoteFinish();
|
||
// console.log('------------------')
|
||
// 同步笔记
|
||
me.syncNote(noteUsn, function(ok) {
|
||
if(ok) {
|
||
// Web.syncTagFinish();
|
||
// 同步标签
|
||
me.syncTag(tagUsn, function(ok) {
|
||
if (ok) {
|
||
me.fullSyncStart = false;
|
||
// 更新上次同步时间
|
||
me.updateLastSyncState(function() {
|
||
// send changes
|
||
// me.sendChanges();
|
||
callback && callback(me._syncInfo, true);
|
||
});
|
||
}
|
||
else {
|
||
me.fullSyncStart = false;
|
||
console.error('syncTag error....');
|
||
callback && callback(me._syncInfo, false);
|
||
}
|
||
});
|
||
} else {
|
||
me.fullSyncStart = false;
|
||
console.error('syncNote error.... 跳过tag');
|
||
callback && callback(me._syncInfo, false);
|
||
}
|
||
});
|
||
} else {
|
||
me.fullSyncStart = false;
|
||
console.error('syncNotebook error.... 跳过note,tag');
|
||
callback && callback(me._syncInfo, false);
|
||
}
|
||
});
|
||
|
||
});
|
||
},
|
||
|
||
// 处理同步好的之后的
|
||
// mainType == notebook, note, tag
|
||
// type = changeAdds, changeConflicts, adds, deletes, updates, conflicts, errors,
|
||
fixSynced: function (mainType, type, data) {
|
||
var o = {};
|
||
if (!Common.isArray(data)) {
|
||
data = [data];
|
||
}
|
||
o[type] = data;
|
||
if (mainType == 'notebook') {
|
||
Notebook.fixConflicts(o);
|
||
} else if (mainType == 'note') {
|
||
Note.fixConflicts(o);
|
||
} else {
|
||
Web.addOrDeleteTagFromSync(o);
|
||
}
|
||
},
|
||
|
||
// 前端重新渲染
|
||
fixSyncedNotebook: function () {
|
||
var me = this;
|
||
if(me.incrSyncStart) {
|
||
console.log(' fixSyncedNotebook')
|
||
Notebook.fixConflicts(me._syncInfo.notebook);
|
||
}
|
||
},
|
||
|
||
// 处理冲突
|
||
fixConflicts: function(callback) {
|
||
var me = this;
|
||
var afterInfo = me._syncInfo;
|
||
// log('处理冲突....');
|
||
// log(me._syncInfo);
|
||
// log(me._syncInfo.tag);
|
||
var tag = me._syncInfo.tag;
|
||
|
||
// 如果是incSync, 则要前端处理
|
||
// 不是fullSync
|
||
if(me.incrSyncStart) {
|
||
|
||
// Notebook.fixConflicts(me._syncInfo.notebook);
|
||
|
||
Note.fixConflicts(me._syncInfo.note, function() {
|
||
// 避免无限循环, 别send changes了
|
||
if(!me._needIncrSyncAgain) {
|
||
// alert("?")
|
||
console.log(" not needIncrSyncAgain")
|
||
// send changes
|
||
callback && callback();
|
||
} else {
|
||
|
||
}
|
||
});
|
||
|
||
// 添加或删除一些tag
|
||
// console.log('怎么可能?')
|
||
// console.error(me._syncInfo.tag); // 为空, 不知道原因
|
||
// console.error();
|
||
// console.error(tag);
|
||
Web.addOrDeleteTagFromSync(tag);
|
||
}
|
||
},
|
||
|
||
fixConflictsForSendChanges: function(callback) {
|
||
var me = this;
|
||
me.fixConflicts(function() {
|
||
callback();
|
||
// 已结束
|
||
me.setSyncFinished();
|
||
})
|
||
},
|
||
// 同步状态
|
||
syncProcessStatus: 0,
|
||
// 添加
|
||
addSyncProcessStatus: function(n) {
|
||
var me = this;
|
||
me.syncProcessStatus += n;
|
||
if(me.syncProcessStatus >= 100) {
|
||
me.syncProcessStatus = 98;
|
||
}
|
||
Web.syncProgress(me.syncProcessStatus);
|
||
},
|
||
// 增量同步
|
||
incrSyncStart: false,
|
||
// 如果第一次insync, 网络错误导致incrSyncStart不结束, 第二次就会永远转动
|
||
setSyncFinished: function(hasError) {
|
||
var me = this;
|
||
me.incrSyncStart = false;
|
||
// Web.syncProgress(0);
|
||
Web.syncFinished(hasError);
|
||
},
|
||
incrSync: function(again) {
|
||
if (User.isLocal()) {
|
||
console.log('no sync for local account');
|
||
return;
|
||
}
|
||
var me = this;
|
||
me._stop = false;
|
||
me._initSyncInfo();
|
||
|
||
// again表示重来
|
||
if(!again && me.incrSyncStart) {
|
||
console.log('上一sync未结束!!');
|
||
return;
|
||
}
|
||
|
||
me.incrSyncStart = true;
|
||
me.syncProcessStatus = 0;
|
||
|
||
console.log('inc sync start');
|
||
if(again) {
|
||
console.log('again >>');
|
||
}
|
||
|
||
// 得到当前LastSyncUsn
|
||
User.getLastSyncState(function(lastSyncUsn, lastSyncTime) {
|
||
console.log('%cstep1 getLastSyncState', 'color: #68bc7a')
|
||
|
||
// 没有上次同步的时间, 则需要进行一次全量同步, 不可能会发生
|
||
if(!lastSyncUsn) {
|
||
console.error(' getLastSyncState error!!');
|
||
me.setSyncFinished();
|
||
return;
|
||
}
|
||
// 先从服务器上得到usn, 与本地的判断, 是否需要pull
|
||
Api.getLastSyncState(function(serverState) {
|
||
if(!Common.isOk(serverState)) {
|
||
console.error(' get Server LastSyncState error!!');
|
||
me.setSyncFinished(true);
|
||
return;
|
||
}
|
||
console.log(' get Server LastSyncState ret', serverState.LastSyncUsn + ' ' + lastSyncUsn);
|
||
if(serverState.LastSyncUsn > lastSyncUsn) {
|
||
// 需要同步笔记本
|
||
console.log('%cstep2 pull', 'color: #68bc7a')
|
||
// 同步笔记本
|
||
me.syncNotebook(lastSyncUsn, function(ok) {
|
||
if(ok) {
|
||
me.fixSyncedNotebook();
|
||
console.log(' incr notebook ok', lastSyncUsn);
|
||
|
||
me.addSyncProcessStatus(10);
|
||
console.log(' incr note start');
|
||
|
||
// 同步笔记
|
||
me.syncNote(lastSyncUsn, function(ok) {
|
||
if(ok) {
|
||
console.log(' incr note ok', lastSyncUsn);
|
||
me.addSyncProcessStatus(30);
|
||
// 同步标签
|
||
me.syncTag(lastSyncUsn, function() {
|
||
console.log(' incr tag ok', lastSyncUsn);
|
||
me.addSyncProcessStatus(10);
|
||
// 更新上次同步时间
|
||
me.updateLastSyncState(function() {
|
||
// send changes
|
||
me.sendChanges(again);
|
||
});
|
||
});
|
||
} else {
|
||
console.log(' incr note not ok')
|
||
me.fixConflicts();
|
||
}
|
||
});
|
||
} else {
|
||
me.fixConflicts();
|
||
}
|
||
});
|
||
|
||
} else {
|
||
console.log('%cstep2 不需要pull, skip', 'color: #68bc7a')
|
||
me.addSyncProcessStatus(50);
|
||
me.sendChanges(again);
|
||
}
|
||
});
|
||
});
|
||
},
|
||
|
||
//---------
|
||
// 发送改变
|
||
//---------
|
||
|
||
// 发送笔记本的更改
|
||
sendNotebookChanges: function(callback) {
|
||
var me = this;
|
||
console.log(' 3.1: sendNotebookChanges')
|
||
// 获取所有笔记本的更改
|
||
Notebook.getDirtyNotebooks(function(notebooks) {
|
||
if (!Common.isEmpty(notebooks)) {
|
||
console.log(' dirty notebooks:', notebooks);
|
||
} else {
|
||
console.log(' no dirty notebooks');
|
||
}
|
||
if(!notebooks) {
|
||
callback && callback();
|
||
} else {
|
||
// 调api, 所有执行后再callback();
|
||
// 一个一个同步执行, 因为要有
|
||
async.eachSeries(notebooks, function(notebook, cb) {
|
||
var api = Api.updateNotebook;
|
||
|
||
// 本地既是新的, 又是删除的, 删除本地的, 不要同步
|
||
// 5/4
|
||
if(notebook.LocalIsNew && notebook.LocalIsDelete) {
|
||
console.log(' 笔记本既是新的, 又是删除的, 不同步, 直接删除本地的');
|
||
Notebook.deleteLocalNotebook(notebook.NotebookId);
|
||
return cb();
|
||
}
|
||
|
||
if(notebook.LocalIsNew) {
|
||
api = Api.addNotebook;
|
||
} else if(notebook.LocalIsDelete) {
|
||
api = Api.deleteNotebook;
|
||
}
|
||
|
||
api.call(Api, notebook, function(newNotebook) {
|
||
// 更新失败
|
||
if(!newNotebook) {
|
||
return cb();
|
||
}
|
||
|
||
// 删除操作
|
||
if(notebook.LocalIsDelete) {
|
||
return cb();
|
||
}
|
||
|
||
// 更新成功, 是否有冲突?
|
||
// newNotebook是服务器上的笔记本
|
||
// 没有更新成功
|
||
if(!newNotebook.NotebookId) {
|
||
if(newNotebook.Msg == 'conflict') {
|
||
// 没用, 前端不会处理的, 按理不会出现这种情况, 因为先sync
|
||
// me._syncInfo.notebook.conflicts.push(newNotebook);
|
||
me.fixSynced('notebook', 'conflicts', newNotebook);
|
||
} else if(newNotebook.Msg == 'notExists') {
|
||
// 服务器端没有, 那么要作为添加
|
||
// 可能服务器上已删除, 此时应该要作为添加而不是更新
|
||
// me._syncInfo.notebook.changeNeedAdds.push(notebook);
|
||
me.fixSynced('notebook', 'changeNeedAdds', notebook);
|
||
}
|
||
|
||
// me.checkNeedIncSyncAgain(newNotebook.Usn);
|
||
return cb();
|
||
}
|
||
else {
|
||
// 更新
|
||
// TODO 后端updateNotebook只要传Usn回来即可
|
||
console.log(" 返回来的notebook " + newNotebook.Title)
|
||
|
||
Notebook.updateNotebookForceForSendChange(notebook.NotebookId, newNotebook, function() {
|
||
if(notebook.LocalIsNew) {
|
||
// me._syncInfo.notebook.changeAdds.push(newNotebook);
|
||
me.fixSynced('notebook', 'changeAdds', newNotebook);
|
||
} else {
|
||
// me._syncInfo.notebook.changeUpdates.push(newNotebook);
|
||
me.fixSynced('notebook', 'changeUpdates', newNotebook);
|
||
}
|
||
|
||
// 这里才cb(), 因为先添加父, 再添加子
|
||
me.checkNeedIncSyncAgain(newNotebook.Usn);
|
||
cb();
|
||
});
|
||
}
|
||
|
||
});
|
||
}, function() {
|
||
callback && callback();
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
checkNeedIncSyncAgain: function(usn) {
|
||
var me = this;
|
||
// 如果之前都很正常
|
||
if(!me._needIncrSyncAgain) {
|
||
// 检查是否有问题
|
||
if(User.getLastSyncUsn() + 1 == usn) {
|
||
// 更新到本地lastSyncUsn
|
||
User.updateLastSyncUsn(usn);
|
||
} else {
|
||
// newNote.Usn > User.getLastSyncUsn + 1, 表示服务器端在本次同步后, sendChanges之前有更新
|
||
// 那么, 还需要来一次incrSync, 之后
|
||
if(User.getLastSyncUsn() > usn) { // 添加标签时如果标签已经存在, 则返回的是旧的
|
||
return;
|
||
}
|
||
console.error('---?? checkNeedIncSyncAgain ??------' + usn)
|
||
console.trace();
|
||
me._needIncrSyncAgain = true;
|
||
}
|
||
}
|
||
},
|
||
|
||
// 发送笔记改变
|
||
// 发送笔记本的更改
|
||
sendNoteChanges: function(callback) {
|
||
var me = this;
|
||
console.log(' 3.2: sendNoteChanges');
|
||
// 获取所有笔记本的更改
|
||
Note.getDirtyNotes(function(notes) {
|
||
if (!Common.isEmpty(notes)) {
|
||
console.log(' dirty notes:', notes);
|
||
} else {
|
||
console.log(' no dirty notes');
|
||
}
|
||
if(!notes) {
|
||
callback && callback();
|
||
} else {
|
||
// 调api, 所有执行后再callback();
|
||
// 一个一个同步执行, 因为要有
|
||
async.eachSeries(notes, function(note, cb) {
|
||
|
||
if (note.InitSync) {
|
||
console.log(' InitSync is Dirty', note);
|
||
return cb();
|
||
}
|
||
|
||
if (note.ConflictNoteId && !note.ConflictFixed) {
|
||
console.log(' 未解决的冲突不同步', note.Title);
|
||
return cb();
|
||
}
|
||
|
||
if(note.LocalIsNew) {
|
||
// 是新的, 且不是trash和删除的
|
||
if(!note.IsTrash && !note.LocalIsDelete) {
|
||
// 添加, newNote的返回不会很多值(server端)
|
||
Api.addNote(note, function(err, newNote) {
|
||
if(err || !Common.isOk(newNote)) {
|
||
console.log(' 添加笔记失败!', err, newNote);
|
||
me._syncInfo.note.errors.push({err: err, ret: newNote, note: note});
|
||
return cb();
|
||
}
|
||
console.log(' 添加笔记成功!', note.Title);
|
||
|
||
newNote.ServerNoteId = newNote.NoteId;
|
||
newNote.NoteId = note.NoteId;
|
||
|
||
newNote.IsBlog = note.IsBlog; // 前端要用
|
||
|
||
// me._syncInfo.note.changeAdds.push(newNote);
|
||
me.fixSynced('note', 'changeAdds', newNote);
|
||
|
||
Note.updateNoteForceForSendChange(newNote, true);
|
||
|
||
// 这里
|
||
me.checkNeedIncSyncAgain(newNote.Usn);
|
||
|
||
cb();
|
||
});
|
||
}
|
||
// 5/4
|
||
// 本地已经删除了, 则彻底删除之
|
||
else if(note.LocalIsDelete) {
|
||
console.log(' 既是新的, 又是删除的, 则删除笔记的');
|
||
Note.deleteLocalNote(note.NoteId);
|
||
return cb();
|
||
}
|
||
// isTrash, 不同步, 不删除
|
||
else {
|
||
console.log(' 既是新的, 又是trash的, 不要同步');
|
||
return cb();
|
||
}
|
||
}
|
||
else if(note.LocalIsDelete) {
|
||
// 删除, 不管它了
|
||
// TODO
|
||
Api.deleteTrash(note, function(ret) {
|
||
if(Common.isOk(ret)) {
|
||
me.checkNeedIncSyncAgain(ret.Usn);
|
||
|
||
// 本地删除了的, 服务端没有, 直接删除本地的
|
||
} else if (typeof ret == 'object' && ret.Msg == 'notExists') {
|
||
console.log( '本地删除了的, 服务端没有, 直接删除本地的');
|
||
Note.deleteLocalNote(note.NoteId);
|
||
}
|
||
return cb();
|
||
});
|
||
}
|
||
else {
|
||
// 更新
|
||
Api.updateNote(note, function(err, ret) {
|
||
if(err || !Common.isOk(ret)) {
|
||
console.log(' update error:' + note.Title, ret);
|
||
if(typeof ret == 'object') {
|
||
if(ret.Msg == 'conflict') {
|
||
console.error(' updateNote 冲突');
|
||
|
||
// 这种情况有很少见, 原因是先pull, 肯定会pull过来
|
||
// 处理方法和pull一样, 看内容是否一样
|
||
// 如果一样, 则不标志为冲突, 修改本Usn为serverUsn, 等下次再send changes
|
||
Note.getNoteContentFromServer(note.ServerNoteId, function (content) {
|
||
if (content === false || content != note.LocalContent) {
|
||
me._syncInfo.note.changeConflicts.push(note);
|
||
cb();
|
||
}
|
||
else {
|
||
// 不冲突, 修改之Usn
|
||
Api.getNote(note.ServerNoteId, function (serverNote) {
|
||
// 取不到, 当作冲突
|
||
if (!serverNote) {
|
||
me._syncInfo.note.changeConflicts.push(note);
|
||
cb();
|
||
}
|
||
else {
|
||
Note.updateNoteUsn(note.NoteId, serverNote.Usn);
|
||
cb();
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
else if(ret.Msg == 'notExists') {
|
||
Note.setError(note.NoteId, err, ret);
|
||
me.fixSynced('note', 'errors', {err: err, ret: ret, note: note});
|
||
// me._syncInfo.note.errors.push({err: err, ret: ret, note: note});
|
||
// 可能服务器上已删除, 此时应该要作为添加而不是更新
|
||
// me._syncInfo.note.changeNeedAdds.push(note);
|
||
cb();
|
||
}
|
||
// 其它错误, 不加, notImage 导致不能终止
|
||
else {
|
||
Note.setError(note.NoteId, err, ret);
|
||
console.error(' updateNote error', err, ret);
|
||
me.fixSynced('note', 'errors', {err: err, ret: ret, note: note});
|
||
cb();
|
||
}
|
||
}
|
||
// 更新失败了, 服务器返回异常
|
||
else {
|
||
Note.setError(note.NoteId, err, ret);
|
||
console.error(' updateNote error', err);
|
||
me.fixSynced('note', 'errors', {err: err, ret: ret, note: note});
|
||
cb();
|
||
}
|
||
}
|
||
// 更新成功
|
||
else {
|
||
console.log(' 更新成功: ', note.Title);
|
||
ret.ServerNoteId = ret.NoteId;
|
||
ret.NoteId = note.NoteId;
|
||
Note.updateNoteForceForSendChange(ret, false);
|
||
// me._syncInfo.note.changeUpdates.push(note);
|
||
me.fixSynced('note', 'changeUpdates', note);
|
||
|
||
me.checkNeedIncSyncAgain(ret.Usn);
|
||
|
||
return cb();
|
||
}
|
||
});
|
||
}
|
||
}, function() {
|
||
callback && callback();
|
||
});
|
||
}
|
||
});
|
||
},
|
||
// 发送标签改变
|
||
sendTagChanges: function(callback) {
|
||
console.log(' 3.3: sendTagChanges');
|
||
var me = this;
|
||
// 获取所有笔记本的更改
|
||
Tag.getDirtyTags(function(tags) {
|
||
if (!Common.isEmpty(tags)) {
|
||
console.log(' dirty tags:', tags);
|
||
} else {
|
||
console.log(' no dirty tags');
|
||
}
|
||
if(!tags) {
|
||
callback && callback();
|
||
} else {
|
||
// 调api, 所有执行后再callback();
|
||
// 一个一个同步执行, 因为要有
|
||
async.eachSeries(tags, function(tag, cb) {
|
||
if(tag.IsDirty) {
|
||
if(!tag.LocalIsDelete) {
|
||
// 添加
|
||
Api.addTag(tag.Tag, function(newTag) {
|
||
if(!Common.isOk(newTag)) {
|
||
return cb();
|
||
}
|
||
// 更新, 添加usn
|
||
Tag.setNotDirtyAndUsn(tag.Tag, newTag.Usn);
|
||
|
||
me._syncInfo.tag.changeAdds.push(newTag); // 之前是note.changeAdds
|
||
// Tag.updateTagForce(newTag);
|
||
me.checkNeedIncSyncAgain(newTag.Usn);
|
||
cb();
|
||
});
|
||
} else {
|
||
// 删除, 不管它了
|
||
Api.deleteTag(tag, function(ret) {
|
||
if(Common.isOk(ret)) {
|
||
Tag.setNotDirty(tag.Tag);
|
||
me.checkNeedIncSyncAgain(ret.Usn);
|
||
// 本地删除了的, 服务端没有, 直接删除本地的
|
||
} else if (typeof ret == 'object') {
|
||
if (ret.Msg == 'notExists') {
|
||
console.log( 'tag本地删除了的, 服务端没有, 直接删除本地的');
|
||
Tag.deleteLocalTag(tag.Tag);
|
||
} else if(ret.Msg == 'conflict') {
|
||
Tag.setNotDirty(tag.Tag);
|
||
}
|
||
}
|
||
return cb();
|
||
});
|
||
}
|
||
}
|
||
}, function() {
|
||
callback && callback();
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
// again, 再一次sync, 不要send changes
|
||
sendChanges: function(again) {
|
||
var me = this;
|
||
console.log('%cstep3 send changes', 'color: #68bc7a')
|
||
// 先处理冲突, 可以同时进行
|
||
if(again) {
|
||
console.log(' send changes again....');
|
||
me.fixConflictsForSendChanges(function(){});
|
||
return;
|
||
}
|
||
me.fixConflicts(function() {
|
||
// send changes
|
||
console.log(' send changes');
|
||
me._initSyncInfo(); // 重新初始化[]
|
||
async.series([
|
||
function(cb) {
|
||
me.sendNotebookChanges(cb);
|
||
},
|
||
function(cb) {
|
||
me.addSyncProcessStatus(10);
|
||
me.sendNoteChanges(cb);
|
||
},
|
||
function(cb) {
|
||
me.addSyncProcessStatus(30);
|
||
me.sendTagChanges(cb);
|
||
}
|
||
], function() {
|
||
me.addSyncProcessStatus(10);
|
||
|
||
// 重新再来一次增量同步
|
||
if(me._needIncrSyncAgain) {
|
||
console.error(' needIncrSyncAgain')
|
||
me.fixConflictsForSendChanges(function() {
|
||
me.incrSync(true);
|
||
});
|
||
} else {
|
||
console.log(' send changes 后解决冲突');
|
||
me.fixConflictsForSendChanges(function() {
|
||
});
|
||
}
|
||
});
|
||
});
|
||
}
|
||
};
|
||
|
||
module.exports = Sync; |