mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 01:36:22 +00:00
integrate all methods
This commit is contained in:
@@ -1,566 +1,20 @@
|
||||
const keygen = require('browser/lib/keygen')
|
||||
const CSON = require('season')
|
||||
const path = require('path')
|
||||
const _ = require('lodash')
|
||||
const sander = require('sander')
|
||||
const consts = require('browser/lib/consts')
|
||||
const dataApi = {
|
||||
init: require('./init'),
|
||||
addStorage: require('./addStorage'),
|
||||
renameStorage: require('./renameStorage'),
|
||||
removeStorage: require('./removeStorage'),
|
||||
createFolder: require('./createFolder'),
|
||||
updateFolder: require('./updateFolder'),
|
||||
deleteFolder: require('./deleteFolder'),
|
||||
createNote: require('./createNote'),
|
||||
updateNote: require('./updateNote'),
|
||||
deleteNote: require('./deleteNote'),
|
||||
moveNote: require('./moveNote'),
|
||||
migrateFromV5Storage: require('./migrateFromV5Storage'),
|
||||
|
||||
let storages = []
|
||||
let notes = []
|
||||
|
||||
let queuedTasks = []
|
||||
|
||||
function queueSaveFolder (storageKey, folderKey) {
|
||||
let storage = _.find(storages, {key: storageKey})
|
||||
if (storage == null) throw new Error('Failed to queue: Storage doesn\'t exist.')
|
||||
|
||||
let targetTasks = queuedTasks.filter((task) => task.storage === storageKey && task.folder === folderKey)
|
||||
targetTasks.forEach((task) => {
|
||||
clearTimeout(task.timer)
|
||||
})
|
||||
queuedTasks = queuedTasks.filter((task) => task.storage !== storageKey || task.folder !== folderKey)
|
||||
let newTimer = setTimeout(() => {
|
||||
let folderNotes = notes.filter((note) => note.storage === storageKey && note.folder === folderKey)
|
||||
sander
|
||||
.writeFile(path.join(storage.cache.path, folderKey, 'data.json'), JSON.stringify({
|
||||
notes: folderNotes.map((note) => {
|
||||
let json = note.toJSON()
|
||||
delete json.storage
|
||||
return json
|
||||
})
|
||||
}))
|
||||
}, 1500)
|
||||
|
||||
queuedTasks.push({
|
||||
storage: storageKey,
|
||||
folder: folderKey,
|
||||
timer: newTimer
|
||||
})
|
||||
_migrateFromV6Storage: require('./migrateFromV6Storage'),
|
||||
_resolveStorageData: require('./resolveStorageData'),
|
||||
_resolveStorageNotes: require('./resolveStorageNotes')
|
||||
}
|
||||
|
||||
class Storage {
|
||||
constructor (cache) {
|
||||
this.key = cache.key
|
||||
this.cache = cache
|
||||
}
|
||||
|
||||
loadJSONData () {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
let data = CSON.readFileSync(path.join(this.cache.path, 'boostnote.json'))
|
||||
this.data = data
|
||||
resolve(this)
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
return Object.assign({}, this.cache, this.data)
|
||||
}
|
||||
|
||||
initStorage () {
|
||||
return this.loadJSONData()
|
||||
.catch((err) => {
|
||||
console.error(err.code)
|
||||
if (err.code === 'ENOENT') {
|
||||
let initialStorage = {
|
||||
folders: []
|
||||
}
|
||||
|
||||
return sander.writeFile(path.join(this.cache.path, 'boostnote.json'), JSON.stringify(initialStorage))
|
||||
} else throw err
|
||||
})
|
||||
.then(() => this.loadJSONData())
|
||||
}
|
||||
|
||||
saveData () {
|
||||
return sander
|
||||
.writeFile(path.join(this.cache.path, 'boostnote.json'), JSON.stringify(this.data))
|
||||
.then(() => this)
|
||||
}
|
||||
|
||||
saveCache () {
|
||||
_saveCaches()
|
||||
}
|
||||
|
||||
static forge (cache) {
|
||||
let instance = new this(cache)
|
||||
return instance
|
||||
}
|
||||
}
|
||||
|
||||
class Note {
|
||||
constructor (note) {
|
||||
this.storage = note.storage
|
||||
this.folder = note.folder
|
||||
this.key = note.key
|
||||
this.uniqueKey = `${note.storage}-${note.folder}-${note.key}`
|
||||
this.data = note
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
return Object.assign({}, this.data, {
|
||||
uniqueKey: this.uniqueKey
|
||||
})
|
||||
}
|
||||
|
||||
save () {
|
||||
let storage = _.find(storages, {key: this.storage})
|
||||
if (storage == null) return Promise.reject(new Error('Storage doesn\'t exist.'))
|
||||
let folder = _.find(storage.data.folders, {key: this.folder})
|
||||
if (folder == null) return Promise.reject(new Error('Storage doesn\'t exist.'))
|
||||
|
||||
// FS MUST BE MANIPULATED BY ASYNC METHOD
|
||||
queueSaveFolder(storage.key, folder.key)
|
||||
return Promise.resolve(this)
|
||||
}
|
||||
|
||||
static forge (note) {
|
||||
let instance = new this(note)
|
||||
|
||||
return Promise.resolve(instance)
|
||||
}
|
||||
}
|
||||
|
||||
function init () {
|
||||
let fetchStorages = function () {
|
||||
let caches
|
||||
try {
|
||||
caches = JSON.parse(localStorage.getItem('storages'))
|
||||
if (!_.isArray(caches)) throw new Error('Cached data is not valid.')
|
||||
} catch (e) {
|
||||
throw e
|
||||
console.error(e)
|
||||
caches = []
|
||||
localStorage.setItem('storages', JSON.stringify(caches))
|
||||
}
|
||||
|
||||
return caches.map((cache) => {
|
||||
return Storage
|
||||
.forge(cache)
|
||||
.loadJSONData()
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
console.error('Failed to load a storage JSON File: %s', cache)
|
||||
return null
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let fetchNotes = function (storages) {
|
||||
let notes = []
|
||||
let modifiedStorages = []
|
||||
storages
|
||||
.forEach((storage) => {
|
||||
storage.data.folders.forEach((folder) => {
|
||||
let dataPath = path.join(storage.cache.path, folder.key, 'data.json')
|
||||
let data
|
||||
try {
|
||||
data = CSON.readFileSync(dataPath)
|
||||
} catch (e) {
|
||||
// Remove folder if fetching failed.
|
||||
console.error('Failed to load data: %s', dataPath)
|
||||
storage.data.folders = storage.data.folders.filter((_folder) => _folder.key !== folder.key)
|
||||
if (modifiedStorages.some((modified) => modified.key === storage.key)) modifiedStorages.push(storage)
|
||||
return
|
||||
}
|
||||
data.notes.forEach((note) => {
|
||||
note.storage = storage.key
|
||||
note.folder = folder.key
|
||||
notes.push(Note.forge(note))
|
||||
})
|
||||
})
|
||||
}, [])
|
||||
return Promise
|
||||
.all(modifiedStorages.map((storage) => storage.saveData()))
|
||||
.then(() => Promise.all(notes))
|
||||
}
|
||||
|
||||
return Promise.all(fetchStorages())
|
||||
.then((_storages) => {
|
||||
storages = _storages.filter((storage) => {
|
||||
if (!_.isObject(storage)) return false
|
||||
return true
|
||||
})
|
||||
_saveCaches()
|
||||
|
||||
return storages
|
||||
})
|
||||
.then(fetchNotes)
|
||||
.then((_notes) => {
|
||||
notes = _notes
|
||||
return {
|
||||
storages: storages.map((storage) => storage.toJSON()),
|
||||
notes: notes.map((note) => note.toJSON())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function _saveCaches () {
|
||||
localStorage.setItem('storages', JSON.stringify(storages.map((storage) => storage.cache)))
|
||||
}
|
||||
|
||||
function addStorage (input) {
|
||||
if (!_.isString(input.path)) {
|
||||
return Promise.reject(new Error('Path must be a string.'))
|
||||
}
|
||||
|
||||
let key = keygen()
|
||||
while (storages.some((storage) => storage.key === key)) {
|
||||
key = keygen()
|
||||
}
|
||||
|
||||
return Storage
|
||||
.forge({
|
||||
name: input.name,
|
||||
key: key,
|
||||
type: input.type,
|
||||
path: input.path
|
||||
})
|
||||
.initStorage()
|
||||
.then((storage) => {
|
||||
let _notes = []
|
||||
let isFolderRemoved = false
|
||||
storage.data.folders.forEach((folder) => {
|
||||
let dataPath = path.join(storage.cache.path, folder.key, 'data.json')
|
||||
let data
|
||||
try {
|
||||
data = CSON.readFileSync(dataPath)
|
||||
} catch (e) {
|
||||
// Remove folder if fetching failed.
|
||||
console.error('Failed to load data: %s', dataPath)
|
||||
storage.data.folders = storage.data.folders.filter((_folder) => _folder.key !== folder.key)
|
||||
isFolderRemoved = true
|
||||
return true
|
||||
}
|
||||
data.notes.forEach((note) => {
|
||||
note.storage = storage.key
|
||||
note.folder = folder.key
|
||||
_notes.push(Note.forge(note))
|
||||
})
|
||||
})
|
||||
|
||||
return Promise.all(_notes)
|
||||
.then((_notes) => {
|
||||
notes = notes.concat(_notes)
|
||||
let data = {
|
||||
storage: storage,
|
||||
notes: _notes
|
||||
}
|
||||
return isFolderRemoved
|
||||
? storage.saveData().then(() => data)
|
||||
: data
|
||||
})
|
||||
})
|
||||
.then((data) => {
|
||||
storages = storages.filter((storage) => storage.key !== data.storage.key)
|
||||
storages.push(data.storage)
|
||||
_saveCaches()
|
||||
|
||||
if (data.storage.data.folders.length < 1) {
|
||||
return createFolder(data.storage.key, {
|
||||
name: 'Default',
|
||||
color: consts.FOLDER_COLORS[0]
|
||||
}).then(() => data)
|
||||
}
|
||||
|
||||
return data
|
||||
})
|
||||
.then((data) => {
|
||||
return {
|
||||
storage: data.storage.toJSON(),
|
||||
notes: data.notes.map((note) => note.toJSON())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function removeStorage (key) {
|
||||
storages = storages.filter((storage) => storage.key !== key)
|
||||
_saveCaches()
|
||||
notes = notes.filter((note) => note.storage !== key)
|
||||
return Promise.resolve(true)
|
||||
}
|
||||
|
||||
function renameStorage (key, name) {
|
||||
let storage = _.find(storages, {key: key})
|
||||
if (storage == null) throw new Error('Storage doesn\'t exist.')
|
||||
storage.cache.name = name
|
||||
storage.saveCache()
|
||||
|
||||
return Promise.resolve(storage.toJSON())
|
||||
}
|
||||
|
||||
function migrateFromV5 (key, data) {
|
||||
let oldFolders = data.folders
|
||||
let oldArticles = data.articles
|
||||
let storage = _.find(storages, {key: key})
|
||||
if (storage == null) throw new Error('Storage doesn\'t exist.')
|
||||
|
||||
let migrateFolders = oldFolders.map((oldFolder) => {
|
||||
let folderKey = keygen()
|
||||
while (storage.data.folders.some((folder) => folder.key === folderKey)) {
|
||||
folderKey = keygen()
|
||||
}
|
||||
let newFolder = {
|
||||
key: folderKey,
|
||||
name: oldFolder.name,
|
||||
color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7]
|
||||
}
|
||||
storage.data.folders.push(newFolder)
|
||||
let articles = oldArticles.filter((article) => article.FolderKey === oldFolder.key)
|
||||
let folderNotes = []
|
||||
articles.forEach((article) => {
|
||||
let noteKey = keygen()
|
||||
while (notes.some((note) => note.storage === key && note.folder === folderKey && note.key === noteKey)) {
|
||||
key = keygen()
|
||||
}
|
||||
if (article.mode === 'markdown') {
|
||||
let newNote = new Note({
|
||||
tags: article.tags,
|
||||
createdAt: article.createdAt,
|
||||
updatedAt: article.updatedAt,
|
||||
folder: folderKey,
|
||||
storage: key,
|
||||
type: 'MARKDOWN_NOTE',
|
||||
isStarred: false,
|
||||
title: article.title,
|
||||
content: '# ' + article.title + '\n\n' + article.content,
|
||||
key: noteKey
|
||||
})
|
||||
notes.push(newNote)
|
||||
folderNotes.push(newNote)
|
||||
} else {
|
||||
let newNote = new Note({
|
||||
tags: article.tags,
|
||||
createdAt: article.createdAt,
|
||||
updatedAt: article.updatedAt,
|
||||
folder: folderKey,
|
||||
storage: key,
|
||||
type: 'SNIPPET_NOTE',
|
||||
isStarred: false,
|
||||
title: article.title,
|
||||
description: article.title,
|
||||
key: noteKey,
|
||||
snippets: [{
|
||||
name: article.mode,
|
||||
mode: article.mode,
|
||||
content: article.content
|
||||
}]
|
||||
})
|
||||
notes.push(newNote)
|
||||
folderNotes.push(newNote)
|
||||
}
|
||||
})
|
||||
|
||||
return sander
|
||||
.writeFile(path.join(storage.cache.path, folderKey, 'data.json'), JSON.stringify({
|
||||
notes: folderNotes.map((note) => {
|
||||
let json = note.toJSON()
|
||||
delete json.storage
|
||||
return json
|
||||
})
|
||||
}))
|
||||
})
|
||||
return Promise.all(migrateFolders)
|
||||
.then(() => storage.saveData())
|
||||
.then(() => {
|
||||
return {
|
||||
storage: storage.toJSON(),
|
||||
notes: notes.filter((note) => note.storage === key)
|
||||
.map((note) => note.toJSON())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function createFolder (key, input) {
|
||||
let storage = _.find(storages, {key: key})
|
||||
if (storage == null) throw new Error('Storage doesn\'t exist.')
|
||||
|
||||
let folderKey = keygen()
|
||||
while (storage.data.folders.some((folder) => folder.key === folderKey)) {
|
||||
folderKey = keygen()
|
||||
}
|
||||
|
||||
let newFolder = {
|
||||
key: folderKey,
|
||||
name: input.name,
|
||||
color: input.color
|
||||
}
|
||||
|
||||
const defaultData = {notes: []}
|
||||
// FS MUST BE MANIPULATED BY ASYNC METHOD
|
||||
return sander
|
||||
.writeFile(path.join(storage.cache.path, folderKey, 'data.json'), JSON.stringify(defaultData))
|
||||
.then(() => {
|
||||
storage.data.folders.push(newFolder)
|
||||
return storage
|
||||
.saveData()
|
||||
.then((storage) => storage.toJSON())
|
||||
})
|
||||
}
|
||||
|
||||
function updateFolder (storageKey, folderKey, input) {
|
||||
let storage = _.find(storages, {key: storageKey})
|
||||
if (storage == null) throw new Error('Storage doesn\'t exist.')
|
||||
let folder = _.find(storage.data.folders, {key: folderKey})
|
||||
folder.color = input.color
|
||||
folder.name = input.name
|
||||
|
||||
return storage
|
||||
.saveData()
|
||||
.then((storage) => storage.toJSON())
|
||||
}
|
||||
|
||||
function removeFolder (storageKey, folderKey) {
|
||||
let storage = _.find(storages, {key: storageKey})
|
||||
if (storage == null) throw new Error('Storage doesn\'t exist.')
|
||||
storage.data.folders = storage.data.folders.filter((folder) => folder.key !== folderKey)
|
||||
notes = notes.filter((note) => note.storage !== storageKey || note.folder !== folderKey)
|
||||
|
||||
// FS MUST BE MANIPULATED BY ASYNC METHOD
|
||||
return sander
|
||||
.rimraf(path.join(storage.cache.path, folderKey))
|
||||
.catch((err) => {
|
||||
if (err.code === 'ENOENT') return true
|
||||
else throw err
|
||||
})
|
||||
.then(() => storage.saveData())
|
||||
.then((storage) => storage.toJSON())
|
||||
}
|
||||
|
||||
function createMarkdownNote (storageKey, folderKey, input) {
|
||||
let key = keygen()
|
||||
while (notes.some((note) => note.storage === storageKey && note.folder === folderKey && note.key === key)) {
|
||||
key = keygen()
|
||||
}
|
||||
|
||||
let newNote = new Note(Object.assign({
|
||||
tags: [],
|
||||
title: '',
|
||||
content: ''
|
||||
}, input, {
|
||||
type: 'MARKDOWN_NOTE',
|
||||
storage: storageKey,
|
||||
folder: folderKey,
|
||||
key: key,
|
||||
isStarred: false,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
}))
|
||||
notes.push(newNote)
|
||||
|
||||
return newNote
|
||||
.save()
|
||||
.then(() => newNote.toJSON())
|
||||
}
|
||||
|
||||
function createSnippetNote (storageKey, folderKey, input) {
|
||||
let key = keygen()
|
||||
while (notes.some((note) => note.storage === storageKey && note.folder === folderKey && note.key === key)) {
|
||||
key = keygen()
|
||||
}
|
||||
|
||||
let newNote = new Note(Object.assign({
|
||||
tags: [],
|
||||
title: '',
|
||||
description: '',
|
||||
snippets: [{
|
||||
name: '',
|
||||
mode: 'text',
|
||||
content: ''
|
||||
}]
|
||||
}, input, {
|
||||
type: 'SNIPPET_NOTE',
|
||||
storage: storageKey,
|
||||
folder: folderKey,
|
||||
key: key,
|
||||
isStarred: false,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
}))
|
||||
notes.push(newNote)
|
||||
|
||||
return newNote
|
||||
.save()
|
||||
.then(() => newNote.toJSON())
|
||||
}
|
||||
|
||||
function updateNote (storageKey, folderKey, noteKey, input) {
|
||||
let note = _.find(notes, {
|
||||
key: noteKey,
|
||||
storage: storageKey,
|
||||
folder: folderKey
|
||||
})
|
||||
|
||||
switch (note.data.type) {
|
||||
case 'MARKDOWN_NOTE':
|
||||
note.data.title = input.title
|
||||
note.data.tags = input.tags
|
||||
note.data.content = input.content
|
||||
note.data.updatedAt = input.updatedAt
|
||||
break
|
||||
case 'SNIPPET_NOTE':
|
||||
note.data.title = input.title
|
||||
note.data.tags = input.tags
|
||||
note.data.description = input.description
|
||||
note.data.snippets = input.snippets
|
||||
note.data.updatedAt = input.updatedAt
|
||||
}
|
||||
|
||||
return note.save()
|
||||
.then(() => note.toJSON())
|
||||
}
|
||||
|
||||
function removeNote (storageKey, folderKey, noteKey) {
|
||||
notes = notes.filter((note) => note.storage !== storageKey || note.folder !== folderKey || note.key !== noteKey)
|
||||
queueSaveFolder(storageKey, folderKey)
|
||||
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
|
||||
function moveNote (storageKey, folderKey, noteKey, newStorageKey, newFolderKey) {
|
||||
let note = _.find(notes, {
|
||||
key: noteKey,
|
||||
storage: storageKey,
|
||||
folder: folderKey
|
||||
})
|
||||
if (note == null) throw new Error('Note doesn\'t exist.')
|
||||
|
||||
let storage = _.find(storages, {key: newStorageKey})
|
||||
if (storage == null) throw new Error('Storage doesn\'t exist.')
|
||||
let folder = _.find(storage.data.folders, {key: newFolderKey})
|
||||
if (folder == null) throw new Error('Folder doesn\'t exist.')
|
||||
note.storage = storage.key
|
||||
note.data.storage = storage.key
|
||||
note.folder = folder.key
|
||||
note.data.folder = folder.key
|
||||
let key = note.key
|
||||
while (notes.some((note) => note.storage === storage.key && note.folder === folder.key && note.key === key)) {
|
||||
key = keygen()
|
||||
}
|
||||
note.key = key
|
||||
note.data.key = key
|
||||
note.uniqueKey = `${note.storage}-${note.folder}-${note.key}`
|
||||
console.log(note.uniqueKey)
|
||||
queueSaveFolder(storageKey, folderKey)
|
||||
return note.save()
|
||||
.then(() => note.toJSON())
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init,
|
||||
addStorage,
|
||||
removeStorage,
|
||||
renameStorage,
|
||||
createFolder,
|
||||
updateFolder,
|
||||
removeFolder,
|
||||
createMarkdownNote,
|
||||
createSnippetNote,
|
||||
updateNote,
|
||||
removeNote,
|
||||
moveNote,
|
||||
migrateFromV5
|
||||
}
|
||||
module.exports = dataApi
|
||||
|
||||
Reference in New Issue
Block a user