2017-11-05 23:41:54 +08:00
const sql = require ( './sql' ) ;
const options = require ( './options' ) ;
const utils = require ( './utils' ) ;
const notes = require ( './notes' ) ;
const audit _category = require ( './audit_category' ) ;
2017-11-15 10:54:12 +08:00
const data _encryption = require ( './data_encryption' ) ;
2017-11-17 10:50:00 +08:00
const sync _table = require ( './sync_table' ) ;
2017-11-05 23:41:54 +08:00
async function createNewNote ( parentNoteId , note , browserId ) {
const noteId = utils . newNoteId ( ) ;
let newNotePos = 0 ;
if ( note . target === 'into' ) {
const res = await sql . getSingleResult ( 'select max(note_pos) as max_note_pos from notes_tree where note_pid = ? and is_deleted = 0' , [ parentNoteId ] ) ;
const maxNotePos = res [ 'max_note_pos' ] ;
if ( maxNotePos === null ) // no children yet
newNotePos = 0 ;
else
newNotePos = maxNotePos + 1
}
else if ( note . target === 'after' ) {
const afterNote = await sql . getSingleResult ( 'select note_pos from notes_tree where note_id = ?' , [ note . target _note _id ] ) ;
newNotePos = afterNote . note _pos + 1 ;
const now = utils . nowTimestamp ( ) ;
await sql . execute ( 'update notes_tree set note_pos = note_pos + 1, date_modified = ? where note_pid = ? and note_pos > ? and is_deleted = 0' , [ now , parentNoteId , afterNote [ 'note_pos' ] ] ) ;
}
else {
throw new Error ( 'Unknown target: ' + note . target ) ;
}
await sql . doInTransaction ( async ( ) => {
await sql . addAudit ( audit _category . CREATE _NOTE , browserId , noteId ) ;
2017-11-17 10:50:00 +08:00
await sync _table . addNoteTreeSync ( noteId ) ;
await sync _table . addNoteSync ( noteId ) ;
2017-11-05 23:41:54 +08:00
const now = utils . nowTimestamp ( ) ;
await sql . insert ( "notes" , {
'note_id' : noteId ,
'note_title' : note . note _title ,
'note_text' : '' ,
'date_created' : now ,
'date_modified' : now ,
2017-11-15 10:54:12 +08:00
'is_protected' : note . is _protected
2017-11-05 23:41:54 +08:00
} ) ;
await sql . insert ( "notes_tree" , {
'note_id' : noteId ,
'note_pid' : parentNoteId ,
'note_pos' : newNotePos ,
'is_expanded' : 0 ,
'date_modified' : utils . nowTimestamp ( ) ,
'is_deleted' : 0
} ) ;
} ) ;
return noteId ;
}
2017-11-15 10:54:12 +08:00
async function encryptNote ( note , ctx ) {
2017-11-16 11:13:45 +08:00
note . detail . note _title = data _encryption . encryptCbc ( ctx . getDataKey ( ) , data _encryption . noteTitleIv ( note . detail . note _id ) , note . detail . note _title ) ;
note . detail . note _text = data _encryption . encryptCbc ( ctx . getDataKey ( ) , data _encryption . noteTextIv ( note . detail . note _id ) , note . detail . note _text ) ;
2017-11-15 10:54:12 +08:00
}
2017-11-05 23:41:54 +08:00
2017-11-15 13:04:26 +08:00
async function protectNoteRecursively ( noteId , dataKey , protect ) {
const note = await sql . getSingleResult ( "SELECT * FROM notes WHERE note_id = ?" , [ noteId ] ) ;
await protectNote ( note , dataKey , protect ) ;
const children = await sql . getFlattenedResults ( "note_id" , "SELECT note_id FROM notes_tree WHERE note_pid = ?" , [ noteId ] ) ;
for ( const childNoteId of children ) {
await protectNoteRecursively ( childNoteId , dataKey , protect ) ;
}
}
async function protectNote ( note , dataKey , protect ) {
let changed = false ;
if ( protect && ! note . is _protected ) {
2017-11-16 11:13:45 +08:00
note . note _title = data _encryption . encryptCbc ( dataKey , data _encryption . noteTitleIv ( note . note _id ) , note . note _title ) ;
note . note _text = data _encryption . encryptCbc ( dataKey , data _encryption . noteTextIv ( note . note _id ) , note . note _text ) ;
2017-11-15 13:04:26 +08:00
note . is _protected = true ;
changed = true ;
}
else if ( ! protect && note . is _protected ) {
2017-11-16 12:39:50 +08:00
note . note _title = data _encryption . decryptCbcString ( dataKey , data _encryption . noteTitleIv ( note . note _id ) , note . note _title ) ;
note . note _text = data _encryption . decryptCbcString ( dataKey , data _encryption . noteTextIv ( note . note _id ) , note . note _text ) ;
2017-11-15 13:04:26 +08:00
note . is _protected = false ;
changed = true ;
}
if ( changed ) {
console . log ( "Updating..." ) ;
await sql . execute ( "UPDATE notes SET note_title = ?, note_text = ?, is_protected = ? WHERE note_id = ?" ,
[ note . note _title , note . note _text , note . is _protected , note . note _id ] ) ;
2017-11-17 10:50:00 +08:00
await sync _table . addNoteSync ( note . note _id ) ;
2017-11-15 13:04:26 +08:00
}
await protectNoteHistory ( note . note _id , dataKey , protect ) ;
}
async function protectNoteHistory ( noteId , dataKey , protect ) {
const historyToChange = await sql . getResults ( "SELECT * FROM notes_history WHERE note_id = ? AND is_protected != ?" , [ noteId , protect ] ) ;
for ( const history of historyToChange ) {
if ( protect ) {
2017-11-16 11:13:45 +08:00
history . note _title = data _encryption . encryptCbc ( dataKey , data _encryption . noteTitleIv ( history . note _history _id ) , history . note _title ) ;
history . note _text = data _encryption . encryptCbc ( dataKey , data _encryption . noteTextIv ( history . note _history _id ) , history . note _text ) ;
2017-11-15 13:04:26 +08:00
history . is _protected = true ;
}
else {
2017-11-16 12:39:50 +08:00
history . note _title = data _encryption . decryptCbcString ( dataKey , data _encryption . noteTitleIv ( history . note _history _id ) , history . note _title ) ;
history . note _text = data _encryption . decryptCbcString ( dataKey , data _encryption . noteTextIv ( history . note _history _id ) , history . note _text ) ;
2017-11-15 13:04:26 +08:00
history . is _protected = false ;
}
await sql . execute ( "UPDATE notes_history SET note_title = ?, note_text = ?, is_protected = ? WHERE note_history_id = ?" ,
[ history . note _title , history . note _text , history . is _protected , history . note _history _id ] ) ;
2017-11-17 10:50:00 +08:00
await sync _table . addNoteHistorySync ( history . note _history _id ) ;
2017-11-15 13:04:26 +08:00
}
}
2017-11-15 10:54:12 +08:00
async function updateNote ( noteId , newNote , ctx ) {
2017-11-16 13:22:00 +08:00
let noteTitleForHistory = newNote . detail . note _title ;
let noteTextForHistory = newNote . detail . note _text ;
2017-11-15 10:54:12 +08:00
if ( newNote . detail . is _protected ) {
await encryptNote ( newNote , ctx ) ;
2017-11-05 23:41:54 +08:00
}
2017-11-15 10:54:12 +08:00
const origNoteDetail = await sql . getSingleResult ( "select * from notes where note_id = ?" , [ noteId ] ) ;
2017-11-05 23:41:54 +08:00
const now = utils . nowTimestamp ( ) ;
const historySnapshotTimeInterval = parseInt ( await options . getOption ( 'history_snapshot_time_interval' ) ) ;
const historyCutoff = now - historySnapshotTimeInterval ;
2017-11-16 13:22:00 +08:00
const existingNoteHistoryId = await sql . getSingleValue ( "select note_history_id from notes_history where note_id = ? and date_modified_from >= ?" , [ noteId , historyCutoff ] ) ;
2017-11-05 23:41:54 +08:00
await sql . doInTransaction ( async ( ) => {
2017-11-16 13:22:00 +08:00
if ( ! existingNoteHistoryId ) {
const newNoteHistoryId = utils . randomString ( 16 ) ;
2017-11-05 23:41:54 +08:00
2017-11-15 10:54:12 +08:00
await sql . execute ( "insert into notes_history (note_history_id, note_id, note_title, note_text, is_protected, date_modified_from, date_modified_to) " +
2017-11-05 23:41:54 +08:00
"values (?, ?, ?, ?, ?, ?, ?)" , [
2017-11-16 13:22:00 +08:00
newNoteHistoryId ,
2017-11-05 23:41:54 +08:00
noteId ,
2017-11-16 13:22:00 +08:00
noteTitleForHistory ,
noteTextForHistory ,
false , // we don't care about encryption - this will be handled in protectNoteHistory()
2017-11-05 23:41:54 +08:00
now ,
now
] ) ;
2017-11-16 13:22:00 +08:00
2017-11-17 10:50:00 +08:00
await sync _table . addNoteHistorySync ( newNoteHistoryId ) ;
2017-11-05 23:41:54 +08:00
}
2017-11-18 07:56:52 +08:00
await protectNoteHistory ( noteId , ctx . getDataKeyOrNull ( ) , newNote . detail . is _protected ) ;
2017-11-15 11:21:56 +08:00
2017-11-15 10:54:12 +08:00
await addNoteAudits ( origNoteDetail , newNote . detail , ctx . browserId ) ;
2017-11-05 23:41:54 +08:00
2017-11-15 10:54:12 +08:00
await sql . execute ( "update notes set note_title = ?, note_text = ?, is_protected = ?, date_modified = ? where note_id = ?" , [
2017-11-05 23:41:54 +08:00
newNote . detail . note _title ,
newNote . detail . note _text ,
2017-11-15 10:54:12 +08:00
newNote . detail . is _protected ,
2017-11-05 23:41:54 +08:00
now ,
noteId ] ) ;
await sql . remove ( "images" , noteId ) ;
for ( const img of newNote . images ) {
img . image _data = atob ( img . image _data ) ;
await sql . insert ( "images" , img ) ;
}
await sql . remove ( "links" , noteId ) ;
for ( const link in newNote . links ) {
2017-11-21 08:43:48 +08:00
//await sql.insert("links", link);
2017-11-05 23:41:54 +08:00
}
2017-11-17 10:50:00 +08:00
await sync _table . addNoteTreeSync ( noteId ) ;
await sync _table . addNoteSync ( noteId ) ;
2017-11-05 23:41:54 +08:00
} ) ;
}
async function addNoteAudits ( origNote , newNote , browserId ) {
2017-11-06 09:37:25 +08:00
const noteId = newNote . note _id ;
2017-11-05 23:41:54 +08:00
2017-11-06 09:37:25 +08:00
if ( ! origNote || newNote . note _title !== origNote . note _title ) {
2017-11-05 23:41:54 +08:00
await sql . deleteRecentAudits ( audit _category . UPDATE _TITLE , browserId , noteId ) ;
await sql . addAudit ( audit _category . UPDATE _TITLE , browserId , noteId ) ;
}
2017-11-06 09:37:25 +08:00
if ( ! origNote || newNote . note _text !== origNote . note _text ) {
2017-11-05 23:41:54 +08:00
await sql . deleteRecentAudits ( audit _category . UPDATE _CONTENT , browserId , noteId ) ;
await sql . addAudit ( audit _category . UPDATE _CONTENT , browserId , noteId ) ;
}
2017-11-15 10:54:12 +08:00
if ( ! origNote || newNote . is _protected !== origNote . is _protected ) {
const origIsProtected = origNote ? origNote . is _protected : null ;
2017-11-06 09:37:25 +08:00
2017-11-15 10:54:12 +08:00
await sql . addAudit ( audit _category . PROTECTED , browserId , noteId , origIsProtected , newNote . is _protected ) ;
2017-11-05 23:41:54 +08:00
}
}
async function deleteNote ( noteId , browserId ) {
const now = utils . nowTimestamp ( ) ;
const children = await sql . getResults ( "select note_id from notes_tree where note_pid = ? and is_deleted = 0" , [ noteId ] ) ;
for ( const child of children ) {
await deleteNote ( child . note _id , browserId ) ;
}
await sql . execute ( "update notes_tree set is_deleted = 1, date_modified = ? where note_id = ?" , [ now , noteId ] ) ;
await sql . execute ( "update notes set is_deleted = 1, date_modified = ? where note_id = ?" , [ now , noteId ] ) ;
2017-11-17 10:50:00 +08:00
await sync _table . addNoteTreeSync ( noteId ) ;
await sync _table . addNoteSync ( noteId ) ;
2017-11-06 09:37:25 +08:00
2017-11-05 23:41:54 +08:00
await sql . addAudit ( audit _category . DELETE _NOTE , browserId , noteId ) ;
}
module . exports = {
createNewNote ,
updateNote ,
addNoteAudits ,
2017-11-15 13:04:26 +08:00
deleteNote ,
protectNoteRecursively
2017-11-06 06:58:55 +08:00
} ;