mirror of
https://github.com/leanote/desktop-app.git
synced 2025-10-15 07:31:33 +00:00
674 lines
18 KiB
JavaScript
674 lines
18 KiB
JavaScript
var async = require('async');
|
|
var db = require('db');
|
|
var fs = require('fs');
|
|
var File = require('file');
|
|
var Evt = require('evt');
|
|
var User = require('user');
|
|
var Notebook = require('notebook');
|
|
var Tag = require('tag');
|
|
// var Api = require('api');
|
|
var Server = require('server');
|
|
var Common = require('common');
|
|
var Notes = db.notes;
|
|
|
|
var Api = null; // require('api')
|
|
|
|
function log(o) {
|
|
console.log(o);
|
|
}
|
|
|
|
// 笔记服务
|
|
var Note = {
|
|
/*
|
|
type NoteOrContent struct {
|
|
NotebookId string
|
|
NoteId string
|
|
UserId string
|
|
Title string
|
|
Desc string
|
|
ImgSrc string
|
|
Tags []string
|
|
Content string
|
|
Abstract string
|
|
IsNew bool
|
|
IsMarkdown bool
|
|
FromUserId string // 为共享而新建
|
|
IsBlog bool // 是否是blog, 更新note不需要修改, 添加note时才有可能用到, 此时需要判断notebook是否设为Blog
|
|
}
|
|
*/
|
|
// 更新笔记
|
|
updateNoteOrContent: function(noteOrContent, callback) {
|
|
var userId = User.getCurActiveUserId();
|
|
noteOrContent['UserId'] = userId;
|
|
log('update');
|
|
var date = new Date();
|
|
noteOrContent.UpdatedTime = date;
|
|
|
|
noteOrContent['IsDirty'] = true; // 已修改
|
|
|
|
// 新建笔记, IsNew还是保存着
|
|
if(noteOrContent.IsNew) {
|
|
noteOrContent.CreatedTime = date;
|
|
noteOrContent['IsTrash'] = false;
|
|
delete noteOrContent['IsNew'];
|
|
noteOrContent['LocalIsNew'] = true;
|
|
Notes.insert(noteOrContent, function (err, newDoc) { // Callback is optional
|
|
if(err) {
|
|
console.log(err);
|
|
callback && callback(false);
|
|
} else {
|
|
// 为什么又设置成true, 因为js的对象是共享的, callback用到了noteOrContent.IsNew来做判断
|
|
noteOrContent['IsNew'] = true;
|
|
callback && callback(newDoc);
|
|
|
|
// 重新统计笔记本的笔记数量
|
|
Notebook.reCountNotebookNumberNotes(noteOrContent.NotebookId);
|
|
// 标签
|
|
if(noteOrContent.Tags && noteOrContent.Tags.length > 0) {
|
|
Tag.addTags(noteOrContent.Tags);
|
|
}
|
|
}
|
|
});
|
|
// 更新笔记
|
|
} else {
|
|
var updateFields = ['Desc', 'ImgSrc', 'Title', 'Tags', 'Content'];
|
|
var updates = {};
|
|
var needUpdate = false;
|
|
for(var i in updateFields) {
|
|
var field = updateFields[i];
|
|
if(field in noteOrContent) {
|
|
updates[field] = noteOrContent[field];
|
|
needUpdate = true;
|
|
}
|
|
}
|
|
if('Content' in updateFields) {
|
|
// 内容更新了, 因为内容是以后才从远程获取的, 获取内容后改变ContentIsDirty=false
|
|
noteOrContent['ContentIsDirty'] = true;
|
|
}
|
|
if(needUpdate) {
|
|
updates['IsDirty'] = true;
|
|
updates.UpdatedTime = date;
|
|
// Set an existing field's value
|
|
Notes.update({NoteId: noteOrContent.NoteId}, { $set: updates }, {}, function (err, numReplaced) {
|
|
if(err) {
|
|
log(err);
|
|
callback && callback(false);
|
|
} else {
|
|
callback && callback(noteOrContent);
|
|
// 标签
|
|
if(noteOrContent.Tags && noteOrContent.Tags.length > 0) {
|
|
Tag.addTags(noteOrContent.Tags);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
|
|
|
|
// 获取笔记列表
|
|
getNotes: function(notebookId, callback) {
|
|
var me = this;
|
|
me._getNotes(notebookId, false, callback);
|
|
},
|
|
// 获取trash笔记
|
|
getTrashNotes: function(callback) {
|
|
var me = this;
|
|
me._getNotes('', true, callback);
|
|
},
|
|
_getNotes: function(notebookId, isTrash, callback) {
|
|
var userId = User.getCurActiveUserId();
|
|
var query = {
|
|
UserId: userId,
|
|
IsTrash: false,
|
|
};
|
|
if(notebookId) {
|
|
query['NotebookId'] = notebookId;
|
|
}
|
|
if(isTrash) {
|
|
query['IsTrash'] = true;
|
|
}
|
|
Notes.find(query).sort({'UpdatedTime': -1}).exec(function(err, notes) {
|
|
if(err) {
|
|
log(err);
|
|
return callback && callback(false);
|
|
}
|
|
return callback && callback(notes);
|
|
});
|
|
},
|
|
|
|
deleteNote: function(noteId, callback) {
|
|
Notes.update({NoteId: noteId}, {$set: {IsTrash: true, IsDirty: true}}, function(err, n) {
|
|
if(err || !n) {
|
|
callback(false);
|
|
} else {
|
|
callback(true);
|
|
}
|
|
});
|
|
},
|
|
deleteTrash: function(noteId, callback) {
|
|
Notes.update({NoteId: noteId}, {$set: {IsDirty: true, LocalIsDelete: true}}, function(err, n) {
|
|
if(err || !n) {
|
|
callback(false);
|
|
} else {
|
|
callback(true);
|
|
}
|
|
});
|
|
},
|
|
|
|
// 得到笔记
|
|
getNote: function(noteId, callback) {
|
|
var me = this;
|
|
Notes.findOne({NoteId: noteId}, function(err, doc) {
|
|
if(err || !doc) {
|
|
log('不存在');
|
|
callback && callback(false);
|
|
} else {
|
|
callback && callback(doc);
|
|
}
|
|
});
|
|
},
|
|
|
|
// 服务器上的数据到本地
|
|
fixNoteContent: function(content) {
|
|
if(!content) {
|
|
return content;
|
|
}
|
|
|
|
var reg = new RegExp(Evt.leanoteUrl + '/api/file/getImage', 'g');
|
|
content = content.replace(reg, Server.localUrl + '/api/file/getImage');
|
|
|
|
var reg2 = new RegExp(Evt.leanoteUrl + '/api/file/getAttach', 'g');
|
|
content = content.replace(reg, Evt.localUrl + '/api/file/getAttach');
|
|
|
|
var reg3 = new RegExp(Evt.leanoteUrl + '/api/file/getAllAttach', 'g');
|
|
content = content.replace(reg, Evt.localUrl + '/api/file/getAllAttach');
|
|
|
|
return content;
|
|
},
|
|
|
|
// 将本地的url改下, 发送数据到服务器上
|
|
fixNoteContentForSend: function(content) {
|
|
if(!content) {
|
|
return content;
|
|
}
|
|
console.log(Evt.localUrl + '/api/file/getImage');
|
|
var reg = new RegExp(Evt.localUrl + '/api/file/getImage', 'g');
|
|
content = content.replace(reg, Evt.leanoteUrl + '/api/file/getImage');
|
|
|
|
var reg2 = new RegExp(Evt.localUrl + '/api/file/getAttach', 'g');
|
|
content = content.replace(reg, Evt.leanoteUrl + '/api/file/getAttach');
|
|
|
|
var reg3 = new RegExp(Evt.localUrl + '/api/file/getAllAttach', 'g');
|
|
content = content.replace(reg, Evt.leanoteUrl + '/api/file/getAllAttach');
|
|
|
|
return content;
|
|
},
|
|
|
|
// 远程修改本地内容
|
|
updateNoteContentForce: function(noteId, content, callback) {
|
|
var me = this;
|
|
|
|
content = me.fixNoteContent(content);
|
|
|
|
Notes.update({NoteId: noteId}, { $set: {Content: content, InitSync: false, IsContentDirty: false} }, {}, function (err, numReplaced) {
|
|
if(err) {
|
|
log(err);
|
|
callback && callback(false);
|
|
} else {
|
|
callback && callback(content);
|
|
}
|
|
});
|
|
},
|
|
|
|
/*
|
|
// 同步内容
|
|
updateNoteContentForce: function(noteId, content, callback) {
|
|
// 将笔记内容中
|
|
|
|
Notes.update({NoteId: noteId}, { $set: {Content: content, InitSync: false} }, {}, function (err, numReplaced) {
|
|
if(err) {
|
|
log(err);
|
|
callback && callback(false);
|
|
} else {
|
|
callback && callback(content);
|
|
}
|
|
});
|
|
},
|
|
*/
|
|
|
|
// 得到笔记内容
|
|
// noteId是本地Id
|
|
getNoteContent: function(noteId, callback) {
|
|
var me = this;
|
|
log('getNoteContent------')
|
|
me.getNote(noteId, function(note) {
|
|
if(!Common.isOk(note)) {
|
|
log('not ok');
|
|
log(note);
|
|
callback && callback(false);
|
|
} else {
|
|
// 如果笔记是刚同步过来的, 那么内容要重新获取
|
|
if(note.InitSync) {
|
|
log('need load from server');
|
|
|
|
if(!Api) {
|
|
Api = require('api')
|
|
}
|
|
|
|
// 远程获取
|
|
me.getServerNoteIdByNoteId(noteId, function(serverNoteId) {
|
|
if(!serverNoteId) {
|
|
return callback && callback(false);
|
|
}
|
|
|
|
Api.getNoteContent(serverNoteId, function(noteContent) {
|
|
// 同步到本地
|
|
if(Common.isOk(noteContent)) {
|
|
me.updateNoteContentForce(noteId, noteContent.Content, function(content) {
|
|
noteContent.Content = content;
|
|
noteContent.NoteId = noteId;
|
|
callback && callback(noteContent);
|
|
});
|
|
} else {
|
|
callback && callback(false);
|
|
}
|
|
});
|
|
|
|
});
|
|
} else {
|
|
log('not need');
|
|
callback && callback(note);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
//----------------
|
|
// 同步
|
|
//----------------
|
|
|
|
getNoteByServerNoteId: function(noteId, callback) {
|
|
var me = this;
|
|
Notes.findOne({ServerNoteId: noteId}, function(err, doc) {
|
|
if(err || !doc) {
|
|
log('getNoteByServerNoteId 不存在' + noteId);
|
|
callback && callback(false);
|
|
} else {
|
|
callback && callback(doc);
|
|
}
|
|
});
|
|
},
|
|
getNoteIdByServerNoteId: function(noteId, callback) {
|
|
var me = this;
|
|
Notes.findOne({ServerNoteId: noteId}, function(err, doc) {
|
|
if(err || !doc) {
|
|
log('getNoteIdByServerNoteId 不存在' + noteId);
|
|
callback && callback(false);
|
|
} else {
|
|
callback && callback(doc.NoteId);
|
|
}
|
|
});
|
|
},
|
|
getServerNoteIdByNoteId: function(noteId, callback) {
|
|
var me = this;
|
|
Notes.findOne({NoteId: noteId}, function(err, doc) {
|
|
if(err || !doc) {
|
|
log('getServerNoteIdByNoteId 不存在');
|
|
callback && callback(false);
|
|
} else {
|
|
callback && callback(doc.ServerNoteId);
|
|
}
|
|
});
|
|
},
|
|
|
|
// 强制删除
|
|
// TODO 是否真的删除 ?
|
|
// 有可能服务器上删除了是误删 ?
|
|
deleteNoteForce: function(noteId, callback) {
|
|
var me = this;
|
|
Notes.remove({ServerNoteId: noteId}, function(err, n) {
|
|
if(err) {
|
|
callback && callback(false);
|
|
} else {
|
|
callback && callback(true);
|
|
}
|
|
});
|
|
},
|
|
// 添加笔记本, note object
|
|
// note是服务器传过来的, 需要处理下fix
|
|
// NoteId, ServerNoteId, NotebookId(本地的)
|
|
addNoteForce: function(note, callback) {
|
|
note.InitSync = true; // 刚同步完, 表示content, images, attach没有同步
|
|
note.IsDirty = false;
|
|
note.ServerNoteId = note.NoteId;
|
|
note.NoteId = Common.objectId();
|
|
Notebook.getNotebookIdByServerNotebookId(note.NotebookId, function(localNotebookId) {
|
|
note.NotebookId = localNotebookId;
|
|
Notes.insert(note, function (err, newDoc) { // Callback is optional
|
|
if(err) {
|
|
console.log(err);
|
|
callback && callback(false);
|
|
} else {
|
|
callback && callback(newDoc);
|
|
}
|
|
});
|
|
});
|
|
},
|
|
// 更新笔记本, 合并之, 内容要重新获取
|
|
// note是服务器传过来的, 需要处理下fix
|
|
updateNoteForce: function(note, callback) {
|
|
var me = this;
|
|
note.IsDirty = false;
|
|
note.InitSync = true;
|
|
note.LocalIsNew = false;
|
|
me.getNoteIdByServerNoteId(note.NoteId, function(localNoteId) {
|
|
note.NoteId = localNoteId;
|
|
Notebook.getNotebookIdByServerNotebookId(note.NotebookId, function(localNotebookId) {
|
|
note['NotebookId'] = localNotebookId;
|
|
Notes.update({NoteId: localNoteId}, {$set: note}, {}, function (err, cnt) { // Callback is optional
|
|
if(err) {
|
|
console.log(err);
|
|
callback && callback(false);
|
|
} else {
|
|
log('强制更新...');
|
|
callback && callback(note);
|
|
}
|
|
});
|
|
});
|
|
|
|
});
|
|
},
|
|
|
|
// addNote, updateNote后的操作
|
|
// 添加修改ServerNoteId; 更新修改usn
|
|
updateNoteForceForSendChange: function(note, isAdd, callback) {
|
|
var me = this;
|
|
note.IsDirty = false;
|
|
note.InitSync = false;
|
|
note.LocalIsNew = false;
|
|
|
|
// 如果是添加的, 因为不会传内容
|
|
if(isAdd) {
|
|
delete note['content'];
|
|
}
|
|
|
|
// 修改LocalFileId <=> FileId的映射
|
|
File.updateFileForce(note.Files);
|
|
|
|
Notes.update({NoteId: note.NoteId}, {$set: note}, function(err, n) {
|
|
if(err || !n) {
|
|
log('updateNoteForceForSendChange err');
|
|
log(err);
|
|
return callback && callback(false);
|
|
}
|
|
return callback && callback(true);
|
|
});
|
|
},
|
|
|
|
// 服务器上的数据
|
|
// 为冲突更新, note已有有NoteId, ServerNoteId, 但NotebookId是服务器端的
|
|
updateNoteForceForConflict: function(note, callback) {
|
|
var me = this;
|
|
note.IsDirty = false;
|
|
note.InitSync = true;
|
|
note.LocalIsNew = false;
|
|
Notebook.getNotebookIdByServerNotebookId(note.NotebookId, function(localNotebookId) {
|
|
note['NotebookId'] = localNotebookId;
|
|
Notes.update({NoteId: note.NoteId}, {$set: note}, {}, function (err, cnt) { // Callback is optional
|
|
if(err) {
|
|
console.log(err);
|
|
callback && callback(false);
|
|
} else {
|
|
log('强制更新...');
|
|
callback && callback(note);
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
// 将本地冲突的笔记复制一份
|
|
// serverNoteId
|
|
copyNoteForConfict: function(noteId, callback) {
|
|
var me = this;
|
|
me.getNote(noteId, function(note) {
|
|
if(!note) {
|
|
return;
|
|
}
|
|
// 新Id
|
|
delete note['_id'];
|
|
note.NoteId = Common.objectId();
|
|
note.ConflictNoteId = noteId; // 与noteId有冲突
|
|
note.ConflictTime = new Date(); // 发生冲突时间
|
|
note.ConflictFixed = false; // 冲突未解决
|
|
note.IsDirty = true;
|
|
note.LocalIsNew = true;
|
|
note.InitSync = false; // 都是本地的, 相当于新建的笔记
|
|
|
|
Notes.insert(note, function(err, newNote) {
|
|
if(err) {
|
|
callback(false);
|
|
} else {
|
|
callback(newNote);
|
|
}
|
|
});
|
|
|
|
});
|
|
},
|
|
|
|
// 处理冲突
|
|
// notes是服务器的数据, 与本地的有冲突
|
|
// 1) 将本地的note复制一份
|
|
// 2) 服务器替换之前
|
|
fixConflicts: function(noteSyncInfo, noteWeb, callback) {
|
|
var me = this;
|
|
// 处理冲突
|
|
if(!noteWeb) {
|
|
return callback && callback();
|
|
}
|
|
|
|
var conflictNotes = noteSyncInfo.conflicts;
|
|
log('fix note conflicts');
|
|
log(conflictNotes);
|
|
// 这里为什么要同步? 因为fixConflicts后要进行send changes, 这些有冲突的不能发送changes
|
|
if(conflictNotes) {
|
|
async.eachSeries(conflictNotes, function(note, cb) { // note是服务器上最新的
|
|
var noteId = note.NoteId; // 本地noteId
|
|
// 复制一份
|
|
me.copyNoteForConfict(noteId, function(newNote) {
|
|
if(newNote) {
|
|
// 更新之前的
|
|
me.updateNoteForceForConflict(note, function(note2) {
|
|
if(note2) {
|
|
// 前端来处理, 全量sync时不用前端一个个处理
|
|
noteWeb && noteWeb.fixSyncConflict(note2, newNote);
|
|
}
|
|
cb();
|
|
});
|
|
} else {
|
|
cb();
|
|
}
|
|
});
|
|
}, function() {
|
|
// 最后调用
|
|
callback && callback();
|
|
});
|
|
} else {
|
|
callback && callback();
|
|
}
|
|
|
|
// 发送改变的冲突
|
|
// 复制一份
|
|
// [待测]
|
|
var changeConflicts = noteSyncInfo.changeConflicts;
|
|
for(var i in changeConflicts) {
|
|
var note = changeConflicts[i]; // note是本地的note
|
|
// 复制一份
|
|
me.copyNoteForConfict(note.NoteId, function(newNote) {
|
|
if(newNote) {
|
|
// 更新之前的, 要先从服务器上得到服务版的
|
|
// 这里的note是本地的, 所以将服务器上的覆盖它
|
|
if(!Api) {
|
|
Api = require('api');
|
|
}
|
|
Api.getNote(note.ServerNoteId, function(serverNote) {
|
|
serverNote.ServerNoteId = serverNote.NoteId;
|
|
serverNote.NoteId = note.NoteId;
|
|
me.updateNoteForceForConflict(serverNote, function(err, note2) {
|
|
if(!err) {
|
|
// 前端来处理, 全量sync时不用前端一个个处理
|
|
noteWeb && noteWeb.fixSyncConflict(note2, newNote);
|
|
}
|
|
});
|
|
});
|
|
} else {
|
|
}
|
|
});
|
|
}
|
|
|
|
// 服务器没有, 但是是发送更新的, 所以需要作为添加以后再send changes
|
|
if(noteSyncInfo.changeNeedAdds) {
|
|
var needAddNotes = noteSyncInfo.changeNeedAdds;
|
|
for(var i in needAddNotes) {
|
|
log('need add ');
|
|
var note = needAddNotes[i];
|
|
me.setIsNew(note.NoteId);
|
|
}
|
|
}
|
|
|
|
// 处理添加的
|
|
var addNotes = noteSyncInfo.adds;
|
|
log('has add...');
|
|
log(addNotes);
|
|
noteWeb.addSync(addNotes);
|
|
|
|
log('has updates...');
|
|
log(noteSyncInfo);
|
|
log(noteSyncInfo.updates);
|
|
// 处理更新的
|
|
noteWeb.updateSync(noteSyncInfo.updates);
|
|
|
|
// 处理删除的
|
|
noteWeb.deleteSync(noteSyncInfo.deletes);
|
|
},
|
|
|
|
// 得到所有文件要传的基本信息和传送的数据
|
|
getFilesPostInfo: function(files, callback) {
|
|
var needPostFilesAttr = [];
|
|
var needTransferFiles = {};
|
|
if(!files || files.length == 0) {
|
|
return callback(needPostFilesAttr, needTransferFiles);
|
|
}
|
|
|
|
async.eachSeries(files, function(file, cb) {
|
|
// var file = files[i];
|
|
var needFile = {
|
|
FileId: file.ServerFileId,
|
|
LocalFileId: file.FileId,
|
|
Type: file.Type,
|
|
HasBody: false
|
|
}
|
|
// console.log(file);
|
|
// 要传数据的
|
|
if(file.IsDirty) {
|
|
fs.exists(file.Path, function(isExists) {
|
|
needTransferFiles[file.FileId] = {
|
|
file: file.Path,
|
|
content_type: 'image/' + file.Type // TODO
|
|
}
|
|
needFile.HasBody = true;
|
|
needPostFilesAttr.push(needFile);
|
|
return cb();
|
|
});
|
|
} else {
|
|
needPostFilesAttr.push(needFile);
|
|
return cb();
|
|
}
|
|
}, function() {
|
|
callback(needPostFilesAttr, needTransferFiles);
|
|
});
|
|
},
|
|
|
|
// 获得用户修改的笔记
|
|
getDirtyNotes: function(callback) {
|
|
var me = this;
|
|
Notes.find({UserId: User.getCurActiveUserId(), IsDirty: true}, function(err, notes) {
|
|
if(err) {
|
|
log(err);
|
|
return callback && callback(false);
|
|
} else {
|
|
// 每一个笔记得到图片, 附件信息和数据
|
|
async.eachSeries(notes, function(note, cb) {
|
|
me.getNoteFiles(note.NoteId, note.Content, function(files) {
|
|
note.Content = me.fixNoteContentForSend(note.Content);
|
|
// note.Files = files || [];
|
|
me.getFilesPostInfo(files, function(attrs, fileDatas) {
|
|
note.Files = attrs;
|
|
note.FileDatas = fileDatas;
|
|
cb();
|
|
});
|
|
});
|
|
}, function() {
|
|
callback(notes);
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
// 得到笔记的文件
|
|
getNoteFiles: function(noteId, content, callback) {
|
|
// 得到图片信息, 通过内容
|
|
// http://localhost:8002/api/file/getImage?fileId=xxxxxx, 得到fileId, 查询数据库, 得到图片
|
|
// console.log(content);
|
|
// console.log(Evt.localUrl + '/api/file/getImage?fileId=([0-9a-zA-Z]{24})');
|
|
var reg = new RegExp(Evt.localUrl + "/api/file/getImage\\?fileId=([0-9a-zA-Z]{24})", 'g');
|
|
var fileIds = [];
|
|
// var fileIdsMap = {}; // 防止多个
|
|
while((result = reg.exec(content)) != null) {
|
|
// result = [所有, 子表达式1, 子表达式2]
|
|
if(result && result.length > 1) {
|
|
// console.log(result);
|
|
var fileId = result[1];
|
|
fileIds.push(fileId);
|
|
}
|
|
}
|
|
var files = []; // {localFileId: "must", fileId: "", hasBody: true, filename: "xx.png"}
|
|
if(fileIds.length > 0) {
|
|
// 得到所有的图片
|
|
File.getAllImages(fileIds, function(images) {
|
|
callback(images);
|
|
/*
|
|
if(!images) {
|
|
return;
|
|
}
|
|
for(var i in images) {
|
|
var image = images[i];
|
|
files.push({
|
|
localFileId: image.FileId,
|
|
fileId: image.ServerFileId,
|
|
hasBody: image.IsDirty,
|
|
filename: image.Name,
|
|
});
|
|
}
|
|
*/
|
|
});
|
|
} else {
|
|
callback(false);
|
|
}
|
|
},
|
|
|
|
// 在send delete笔记时成功
|
|
setNotDirty: function(noteId) {
|
|
Notes.update({NoteId: noteId}, {$set:{IsDirty: false}})
|
|
},
|
|
// 在send delete笔记时有冲突, 设为不删除
|
|
setNotDirtyNotDelete: function(noteId) {
|
|
Notes.update({NoteId: noteId}, {$set:{IsDirty: false, LocalIsDelete: false}})
|
|
},
|
|
setIsNew: function(noteId) {
|
|
Notes.update({NoteId: noteId}, {$set:{LocalIsNew: true, IsDirty: true}})
|
|
}
|
|
};
|
|
|
|
module.exports = Note; |