mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
refresh OneDrive token
This commit is contained in:
parent
219f43bbaa
commit
afd00eb06c
@ -1,657 +0,0 @@
|
|||||||
require('source-map-support').install();
|
|
||||||
require('babel-plugin-transform-runtime');
|
|
||||||
|
|
||||||
import { FileApi } from 'src/file-api.js';
|
|
||||||
import { FileApiDriverLocal } from 'src/file-api-driver-local.js';
|
|
||||||
import { FileApiDriverMemory } from 'src/file-api-driver-memory.js';
|
|
||||||
import { Database } from 'src/database.js';
|
|
||||||
import { DatabaseDriverNode } from 'src/database-driver-node.js';
|
|
||||||
import { BaseModel } from 'src/base-model.js';
|
|
||||||
import { Folder } from 'src/models/folder.js';
|
|
||||||
import { Note } from 'src/models/note.js';
|
|
||||||
import { Setting } from 'src/models/setting.js';
|
|
||||||
import { Synchronizer } from 'src/synchronizer.js';
|
|
||||||
import { uuid } from 'src/uuid.js';
|
|
||||||
import { sprintf } from 'sprintf-js';
|
|
||||||
import { _ } from 'src/locale.js';
|
|
||||||
|
|
||||||
let db = new Database(new DatabaseDriverNode());
|
|
||||||
let fileDriver = new FileApiDriverLocal();
|
|
||||||
let fileApi = new FileApi('/home/laurent/Temp/TestImport', fileDriver);
|
|
||||||
let synchronizer = new Synchronizer(db, fileApi);
|
|
||||||
const vorpal = require('vorpal')();
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
await db.open({ name: '/home/laurent/Temp/test.sqlite3' });
|
|
||||||
BaseModel.db_ = db;
|
|
||||||
await Setting.load();
|
|
||||||
|
|
||||||
let commands = [];
|
|
||||||
let currentFolder = null;
|
|
||||||
|
|
||||||
function switchCurrentFolder(folder) {
|
|
||||||
currentFolder = folder;
|
|
||||||
updatePrompt();
|
|
||||||
}
|
|
||||||
|
|
||||||
function promptString() {
|
|
||||||
let path = '~';
|
|
||||||
if (currentFolder) {
|
|
||||||
path += '/' + currentFolder.title;
|
|
||||||
}
|
|
||||||
return 'joplin:' + path + '$ ';
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePrompt() {
|
|
||||||
vorpal.delimiter(promptString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now, to go around this issue: https://github.com/dthree/vorpal/issues/114
|
|
||||||
function quotePromptArg(s) {
|
|
||||||
if (s.indexOf(' ') >= 0) {
|
|
||||||
return '"' + s + '"';
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
function autocompleteFolders() {
|
|
||||||
return Folder.all().then((folders) => {
|
|
||||||
let output = [];
|
|
||||||
for (let i = 0; i < folders.length; i++) {
|
|
||||||
output.push(quotePromptArg(folders[i].title));
|
|
||||||
}
|
|
||||||
output.push('..');
|
|
||||||
output.push('.');
|
|
||||||
return output;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function autocompleteItems() {
|
|
||||||
let promise = null;
|
|
||||||
if (!currentFolder) {
|
|
||||||
promise = Folder.all();
|
|
||||||
} else {
|
|
||||||
promise = Note.previews(currentFolder.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return promise.then((items) => {
|
|
||||||
let output = [];
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
output.push(quotePromptArg(items[i].title));
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
process.stdin.on('keypress', (_, key) => {
|
|
||||||
if (key && key.name === 'return') {
|
|
||||||
updatePrompt();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key.name === 'tab') {
|
|
||||||
vorpal.ui.imprint();
|
|
||||||
vorpal.log(vorpal.ui.input());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.push({
|
|
||||||
usage: 'cd <list-title>',
|
|
||||||
description: 'Moved to [list-title] - all further operations will happen within this list. Use `cd ..` to go back one level.',
|
|
||||||
action: function (args, end) {
|
|
||||||
let folderTitle = args['list-title'];
|
|
||||||
|
|
||||||
if (folderTitle == '..') {
|
|
||||||
switchCurrentFolder(null);
|
|
||||||
end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (folderTitle == '.') {
|
|
||||||
end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Folder.loadByField('title', folderTitle).then((folder) => {
|
|
||||||
switchCurrentFolder(folder);
|
|
||||||
end();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
autocomplete: autocompleteFolders,
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.push({
|
|
||||||
usage: 'mklist <list-title>',
|
|
||||||
alias: 'mkdir',
|
|
||||||
description: 'Creates a new list',
|
|
||||||
action: function (args, end) {
|
|
||||||
Folder.save({ title: args['list-title'] }).catch((error) => {
|
|
||||||
this.log(error);
|
|
||||||
}).then((folder) => {
|
|
||||||
switchCurrentFolder(folder);
|
|
||||||
end();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.push({
|
|
||||||
usage: 'mknote <note-title>',
|
|
||||||
alias: 'touch',
|
|
||||||
description: 'Creates a new note',
|
|
||||||
action: function (args, end) {
|
|
||||||
if (!currentFolder) {
|
|
||||||
this.log('Notes can only be created within a list.');
|
|
||||||
end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let note = {
|
|
||||||
title: args['note-title'],
|
|
||||||
parent_id: currentFolder.id,
|
|
||||||
};
|
|
||||||
Note.save(note).catch((error) => {
|
|
||||||
this.log(error);
|
|
||||||
}).then((note) => {
|
|
||||||
end();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.push({
|
|
||||||
usage: 'set <item-title> <prop-name> [prop-value]',
|
|
||||||
description: 'Sets the given <prop-name> of the given item.',
|
|
||||||
action: function (args, end) {
|
|
||||||
let promise = null;
|
|
||||||
let title = args['item-title'];
|
|
||||||
let propName = args['prop-name'];
|
|
||||||
let propValue = args['prop-value'];
|
|
||||||
if (!propValue) propValue = '';
|
|
||||||
|
|
||||||
if (!currentFolder) {
|
|
||||||
promise = Folder.loadByField('title', title);
|
|
||||||
} else {
|
|
||||||
promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.then((item) => {
|
|
||||||
if (!item) {
|
|
||||||
this.log(_('No item with title "%s" found.', title));
|
|
||||||
end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let newItem = {
|
|
||||||
id: item.id,
|
|
||||||
type_: item.type_,
|
|
||||||
};
|
|
||||||
newItem[propName] = propValue;
|
|
||||||
let ItemClass = BaseItem.itemClass();
|
|
||||||
return ItemClass.save(newItem);
|
|
||||||
}).catch((error) => {
|
|
||||||
this.log(error);
|
|
||||||
}).then(() => {
|
|
||||||
end();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
autocomplete: autocompleteItems,
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.push({
|
|
||||||
usage: 'cat <item-title>',
|
|
||||||
description: 'Displays the given item data.',
|
|
||||||
action: function (args, end) {
|
|
||||||
let title = args['item-title'];
|
|
||||||
|
|
||||||
let promise = null;
|
|
||||||
if (!currentFolder) {
|
|
||||||
promise = Folder.loadByField('title', title);
|
|
||||||
} else {
|
|
||||||
promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.then((item) => {
|
|
||||||
if (!item) {
|
|
||||||
this.log(_('No item with title "%s" found.', title));
|
|
||||||
end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentFolder) {
|
|
||||||
this.log(Folder.serialize(item));
|
|
||||||
} else {
|
|
||||||
this.log(Note.serialize(item));
|
|
||||||
}
|
|
||||||
}).catch((error) => {
|
|
||||||
this.log(error);
|
|
||||||
}).then(() => {
|
|
||||||
end();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
autocomplete: autocompleteItems,
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.push({
|
|
||||||
usage: 'rm <item-title>',
|
|
||||||
description: 'Deletes the given item. For a list, all the notes within that list will be deleted.',
|
|
||||||
action: function (args, end) {
|
|
||||||
let title = args['item-title'];
|
|
||||||
|
|
||||||
let promise = null;
|
|
||||||
let itemType = currentFolder ? 'note' : 'folder';
|
|
||||||
if (itemType == 'folder') {
|
|
||||||
promise = Folder.loadByField('title', title);
|
|
||||||
} else {
|
|
||||||
promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.then((item) => {
|
|
||||||
if (!item) {
|
|
||||||
this.log(_('No item with title "%s" found.', title));
|
|
||||||
end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemType == 'folder') {
|
|
||||||
return Folder.delete(item.id);
|
|
||||||
} else {
|
|
||||||
return Note.delete(item.id);
|
|
||||||
}
|
|
||||||
}).catch((error) => {
|
|
||||||
this.log(error);
|
|
||||||
}).then(() => {
|
|
||||||
end();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
autocomplete: autocompleteItems,
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.push({
|
|
||||||
usage: 'ls [list-title]',
|
|
||||||
alias: 'll',
|
|
||||||
description: 'Lists items in [list-title].',
|
|
||||||
action: function (args, end) {
|
|
||||||
let folderTitle = args['list-title'];
|
|
||||||
|
|
||||||
let promise = null;
|
|
||||||
|
|
||||||
if (folderTitle == '..') {
|
|
||||||
promise = Promise.resolve('root');
|
|
||||||
} else if (folderTitle && folderTitle != '.') {
|
|
||||||
promise = Folder.loadByField('title', folderTitle);
|
|
||||||
} else if (currentFolder) {
|
|
||||||
promise = Promise.resolve(currentFolder);
|
|
||||||
} else {
|
|
||||||
promise = Promise.resolve('root');
|
|
||||||
}
|
|
||||||
|
|
||||||
promise.then((folder) => {
|
|
||||||
let p = null
|
|
||||||
let postfix = '';
|
|
||||||
if (folder === 'root') {
|
|
||||||
p = Folder.all();
|
|
||||||
postfix = '/';
|
|
||||||
} else if (!folder) {
|
|
||||||
throw new Error(_('Unknown list: "%s"', folderTitle));
|
|
||||||
} else {
|
|
||||||
p = Note.previews(folder.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return p.then((previews) => {
|
|
||||||
for (let i = 0; i < previews.length; i++) {
|
|
||||||
this.log(previews[i].title + postfix);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).catch((error) => {
|
|
||||||
this.log(error);
|
|
||||||
}).then(() => {
|
|
||||||
end();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
autocomplete: autocompleteFolders,
|
|
||||||
});
|
|
||||||
|
|
||||||
commands.push({
|
|
||||||
usage: 'sync',
|
|
||||||
description: 'Synchronizes with remote storage.',
|
|
||||||
action: function (args, end) {
|
|
||||||
synchronizer.start().catch((error) => {
|
|
||||||
console.error(error);
|
|
||||||
}).then(() => {
|
|
||||||
end();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let i = 0; i < commands.length; i++) {
|
|
||||||
let c = commands[i];
|
|
||||||
let o = vorpal.command(c.usage, c.description);
|
|
||||||
if (c.alias) {
|
|
||||||
o.alias(c.alias);
|
|
||||||
}
|
|
||||||
if (c.autocomplete) {
|
|
||||||
o.autocomplete({
|
|
||||||
data: c.autocomplete,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
o.action(c.action);
|
|
||||||
}
|
|
||||||
|
|
||||||
vorpal.delimiter(promptString()).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
|
|
||||||
// BaseModel.db_ = db;
|
|
||||||
// }).then(() => {
|
|
||||||
// return Setting.load();
|
|
||||||
// }).then(() => {
|
|
||||||
// let commands = [];
|
|
||||||
// let currentFolder = null;
|
|
||||||
|
|
||||||
// function switchCurrentFolder(folder) {
|
|
||||||
// currentFolder = folder;
|
|
||||||
// updatePrompt();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function promptString() {
|
|
||||||
// let path = '~';
|
|
||||||
// if (currentFolder) {
|
|
||||||
// path += '/' + currentFolder.title;
|
|
||||||
// }
|
|
||||||
// return 'joplin:' + path + '$ ';
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function updatePrompt() {
|
|
||||||
// vorpal.delimiter(promptString());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // For now, to go around this issue: https://github.com/dthree/vorpal/issues/114
|
|
||||||
// function quotePromptArg(s) {
|
|
||||||
// if (s.indexOf(' ') >= 0) {
|
|
||||||
// return '"' + s + '"';
|
|
||||||
// }
|
|
||||||
// return s;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function autocompleteFolders() {
|
|
||||||
// return Folder.all().then((folders) => {
|
|
||||||
// let output = [];
|
|
||||||
// for (let i = 0; i < folders.length; i++) {
|
|
||||||
// output.push(quotePromptArg(folders[i].title));
|
|
||||||
// }
|
|
||||||
// output.push('..');
|
|
||||||
// output.push('.');
|
|
||||||
// return output;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function autocompleteItems() {
|
|
||||||
// let promise = null;
|
|
||||||
// if (!currentFolder) {
|
|
||||||
// promise = Folder.all();
|
|
||||||
// } else {
|
|
||||||
// promise = Note.previews(currentFolder.id);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return promise.then((items) => {
|
|
||||||
// let output = [];
|
|
||||||
// for (let i = 0; i < items.length; i++) {
|
|
||||||
// output.push(quotePromptArg(items[i].title));
|
|
||||||
// }
|
|
||||||
// return output;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// process.stdin.on('keypress', (_, key) => {
|
|
||||||
// if (key && key.name === 'return') {
|
|
||||||
// updatePrompt();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (key.name === 'tab') {
|
|
||||||
// vorpal.ui.imprint();
|
|
||||||
// vorpal.log(vorpal.ui.input());
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// commands.push({
|
|
||||||
// usage: 'cd <list-title>',
|
|
||||||
// description: 'Moved to [list-title] - all further operations will happen within this list. Use `cd ..` to go back one level.',
|
|
||||||
// action: function (args, end) {
|
|
||||||
// let folderTitle = args['list-title'];
|
|
||||||
|
|
||||||
// if (folderTitle == '..') {
|
|
||||||
// switchCurrentFolder(null);
|
|
||||||
// end();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (folderTitle == '.') {
|
|
||||||
// end();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Folder.loadByField('title', folderTitle).then((folder) => {
|
|
||||||
// switchCurrentFolder(folder);
|
|
||||||
// end();
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// autocomplete: autocompleteFolders,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// commands.push({
|
|
||||||
// usage: 'mklist <list-title>',
|
|
||||||
// alias: 'mkdir',
|
|
||||||
// description: 'Creates a new list',
|
|
||||||
// action: function (args, end) {
|
|
||||||
// Folder.save({ title: args['list-title'] }).catch((error) => {
|
|
||||||
// this.log(error);
|
|
||||||
// }).then((folder) => {
|
|
||||||
// switchCurrentFolder(folder);
|
|
||||||
// end();
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// commands.push({
|
|
||||||
// usage: 'mknote <note-title>',
|
|
||||||
// alias: 'touch',
|
|
||||||
// description: 'Creates a new note',
|
|
||||||
// action: function (args, end) {
|
|
||||||
// if (!currentFolder) {
|
|
||||||
// this.log('Notes can only be created within a list.');
|
|
||||||
// end();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let note = {
|
|
||||||
// title: args['note-title'],
|
|
||||||
// parent_id: currentFolder.id,
|
|
||||||
// };
|
|
||||||
// Note.save(note).catch((error) => {
|
|
||||||
// this.log(error);
|
|
||||||
// }).then((note) => {
|
|
||||||
// end();
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// commands.push({
|
|
||||||
// usage: 'set <item-title> <prop-name> [prop-value]',
|
|
||||||
// description: 'Sets the given <prop-name> of the given item.',
|
|
||||||
// action: function (args, end) {
|
|
||||||
// let promise = null;
|
|
||||||
// let title = args['item-title'];
|
|
||||||
// let propName = args['prop-name'];
|
|
||||||
// let propValue = args['prop-value'];
|
|
||||||
// if (!propValue) propValue = '';
|
|
||||||
|
|
||||||
// if (!currentFolder) {
|
|
||||||
// promise = Folder.loadByField('title', title);
|
|
||||||
// } else {
|
|
||||||
// promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// promise.then((item) => {
|
|
||||||
// if (!item) {
|
|
||||||
// this.log(_('No item with title "%s" found.', title));
|
|
||||||
// end();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let newItem = {
|
|
||||||
// id: item.id,
|
|
||||||
// type_: item.type_,
|
|
||||||
// };
|
|
||||||
// newItem[propName] = propValue;
|
|
||||||
// let ItemClass = BaseItem.itemClass();
|
|
||||||
// return ItemClass.save(newItem);
|
|
||||||
// }).catch((error) => {
|
|
||||||
// this.log(error);
|
|
||||||
// }).then(() => {
|
|
||||||
// end();
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// autocomplete: autocompleteItems,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// commands.push({
|
|
||||||
// usage: 'cat <item-title>',
|
|
||||||
// description: 'Displays the given item data.',
|
|
||||||
// action: function (args, end) {
|
|
||||||
// let title = args['item-title'];
|
|
||||||
|
|
||||||
// let promise = null;
|
|
||||||
// if (!currentFolder) {
|
|
||||||
// promise = Folder.loadByField('title', title);
|
|
||||||
// } else {
|
|
||||||
// promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// promise.then((item) => {
|
|
||||||
// if (!item) {
|
|
||||||
// this.log(_('No item with title "%s" found.', title));
|
|
||||||
// end();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (!currentFolder) {
|
|
||||||
// this.log(Folder.serialize(item));
|
|
||||||
// } else {
|
|
||||||
// this.log(Note.serialize(item));
|
|
||||||
// }
|
|
||||||
// }).catch((error) => {
|
|
||||||
// this.log(error);
|
|
||||||
// }).then(() => {
|
|
||||||
// end();
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// autocomplete: autocompleteItems,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// commands.push({
|
|
||||||
// usage: 'rm <item-title>',
|
|
||||||
// description: 'Deletes the given item. For a list, all the notes within that list will be deleted.',
|
|
||||||
// action: function (args, end) {
|
|
||||||
// let title = args['item-title'];
|
|
||||||
|
|
||||||
// let promise = null;
|
|
||||||
// let itemType = currentFolder ? 'note' : 'folder';
|
|
||||||
// if (itemType == 'folder') {
|
|
||||||
// promise = Folder.loadByField('title', title);
|
|
||||||
// } else {
|
|
||||||
// promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// promise.then((item) => {
|
|
||||||
// if (!item) {
|
|
||||||
// this.log(_('No item with title "%s" found.', title));
|
|
||||||
// end();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (itemType == 'folder') {
|
|
||||||
// return Folder.delete(item.id);
|
|
||||||
// } else {
|
|
||||||
// return Note.delete(item.id);
|
|
||||||
// }
|
|
||||||
// }).catch((error) => {
|
|
||||||
// this.log(error);
|
|
||||||
// }).then(() => {
|
|
||||||
// end();
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// autocomplete: autocompleteItems,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// commands.push({
|
|
||||||
// usage: 'ls [list-title]',
|
|
||||||
// alias: 'll',
|
|
||||||
// description: 'Lists items in [list-title].',
|
|
||||||
// action: function (args, end) {
|
|
||||||
// let folderTitle = args['list-title'];
|
|
||||||
|
|
||||||
// let promise = null;
|
|
||||||
|
|
||||||
// if (folderTitle == '..') {
|
|
||||||
// promise = Promise.resolve('root');
|
|
||||||
// } else if (folderTitle && folderTitle != '.') {
|
|
||||||
// promise = Folder.loadByField('title', folderTitle);
|
|
||||||
// } else if (currentFolder) {
|
|
||||||
// promise = Promise.resolve(currentFolder);
|
|
||||||
// } else {
|
|
||||||
// promise = Promise.resolve('root');
|
|
||||||
// }
|
|
||||||
|
|
||||||
// promise.then((folder) => {
|
|
||||||
// let p = null
|
|
||||||
// let postfix = '';
|
|
||||||
// if (folder === 'root') {
|
|
||||||
// p = Folder.all();
|
|
||||||
// postfix = '/';
|
|
||||||
// } else if (!folder) {
|
|
||||||
// throw new Error(_('Unknown list: "%s"', folderTitle));
|
|
||||||
// } else {
|
|
||||||
// p = Note.previews(folder.id);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return p.then((previews) => {
|
|
||||||
// for (let i = 0; i < previews.length; i++) {
|
|
||||||
// this.log(previews[i].title + postfix);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }).catch((error) => {
|
|
||||||
// this.log(error);
|
|
||||||
// }).then(() => {
|
|
||||||
// end();
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// autocomplete: autocompleteFolders,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// commands.push({
|
|
||||||
// usage: 'sync',
|
|
||||||
// description: 'Synchronizes with remote storage.',
|
|
||||||
// action: function (args, end) {
|
|
||||||
// synchronizer.start().catch((error) => {
|
|
||||||
// console.error(error);
|
|
||||||
// }).then(() => {
|
|
||||||
// end();
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// for (let i = 0; i < commands.length; i++) {
|
|
||||||
// let c = commands[i];
|
|
||||||
// let o = vorpal.command(c.usage, c.description);
|
|
||||||
// if (c.alias) {
|
|
||||||
// o.alias(c.alias);
|
|
||||||
// }
|
|
||||||
// if (c.autocomplete) {
|
|
||||||
// o.autocomplete({
|
|
||||||
// data: c.autocomplete,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// o.action(c.action);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// vorpal.delimiter(promptString()).show();
|
|
||||||
// });
|
|
384
CliClient/app/main.js
Normal file
384
CliClient/app/main.js
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
require('source-map-support').install();
|
||||||
|
require('babel-plugin-transform-runtime');
|
||||||
|
|
||||||
|
import { FileApi } from 'src/file-api.js';
|
||||||
|
import { FileApiDriverOneDrive } from 'src/file-api-driver-onedrive.js';
|
||||||
|
import { Database } from 'src/database.js';
|
||||||
|
import { DatabaseDriverNode } from 'src/database-driver-node.js';
|
||||||
|
import { BaseModel } from 'src/base-model.js';
|
||||||
|
import { Folder } from 'src/models/folder.js';
|
||||||
|
import { Note } from 'src/models/note.js';
|
||||||
|
import { Setting } from 'src/models/setting.js';
|
||||||
|
import { Synchronizer } from 'src/synchronizer.js';
|
||||||
|
import { uuid } from 'src/uuid.js';
|
||||||
|
import { sprintf } from 'sprintf-js';
|
||||||
|
import { _ } from 'src/locale.js';
|
||||||
|
import os from 'os';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
|
|
||||||
|
let db = new Database(new DatabaseDriverNode());
|
||||||
|
let synchronizer_ = null;
|
||||||
|
const vorpal = require('vorpal')();
|
||||||
|
const APPNAME = 'joplin';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
let dataDir = os.homedir() + '/.local/share/' + APPNAME;
|
||||||
|
await fs.mkdirp(dataDir, 0o755);
|
||||||
|
|
||||||
|
await db.open({ name: dataDir + '/database.sqlite' });
|
||||||
|
BaseModel.db_ = db;
|
||||||
|
await Setting.load();
|
||||||
|
|
||||||
|
let commands = [];
|
||||||
|
let currentFolder = null;
|
||||||
|
|
||||||
|
async function synchronizer(remoteBackend) {
|
||||||
|
if (synchronizer_) return synchronizer_;
|
||||||
|
|
||||||
|
let fileApi = null;
|
||||||
|
|
||||||
|
if (remoteBackend == 'onedrive') {
|
||||||
|
const CLIENT_ID = 'e09fc0de-c958-424f-83a2-e56a721d331b';
|
||||||
|
const CLIENT_SECRET = 'JA3cwsqSGHFtjMwd5XoF5L5';
|
||||||
|
|
||||||
|
let driver = new FileApiDriverOneDrive(CLIENT_ID, CLIENT_SECRET);
|
||||||
|
let auth = Setting.value('sync.onedrive.auth');
|
||||||
|
|
||||||
|
if (auth) {
|
||||||
|
auth = JSON.parse(auth);
|
||||||
|
} else {
|
||||||
|
auth = await driver.api().oauthDance();
|
||||||
|
Setting.setValue('sync.onedrive.auth', JSON.stringify(auth));
|
||||||
|
}
|
||||||
|
|
||||||
|
driver.api().setAuth(auth);
|
||||||
|
|
||||||
|
let appDir = await driver.api().appDirectory();
|
||||||
|
fileApi = new FileApi(appDir, driver);
|
||||||
|
} else {
|
||||||
|
throw new Error('Unknown backend: ' . remoteBackend);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronizer_ = new Synchronizer(db, fileApi);
|
||||||
|
|
||||||
|
return synchronizer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = await synchronizer();
|
||||||
|
return;
|
||||||
|
|
||||||
|
function switchCurrentFolder(folder) {
|
||||||
|
currentFolder = folder;
|
||||||
|
updatePrompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
function promptString() {
|
||||||
|
let path = '~';
|
||||||
|
if (currentFolder) {
|
||||||
|
path += '/' + currentFolder.title;
|
||||||
|
}
|
||||||
|
return 'joplin:' + path + '$ ';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePrompt() {
|
||||||
|
vorpal.delimiter(promptString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now, to go around this issue: https://github.com/dthree/vorpal/issues/114
|
||||||
|
function quotePromptArg(s) {
|
||||||
|
if (s.indexOf(' ') >= 0) {
|
||||||
|
return '"' + s + '"';
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function autocompleteFolders() {
|
||||||
|
return Folder.all().then((folders) => {
|
||||||
|
let output = [];
|
||||||
|
for (let i = 0; i < folders.length; i++) {
|
||||||
|
output.push(quotePromptArg(folders[i].title));
|
||||||
|
}
|
||||||
|
output.push('..');
|
||||||
|
output.push('.');
|
||||||
|
return output;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function autocompleteItems() {
|
||||||
|
let promise = null;
|
||||||
|
if (!currentFolder) {
|
||||||
|
promise = Folder.all();
|
||||||
|
} else {
|
||||||
|
promise = Note.previews(currentFolder.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.then((items) => {
|
||||||
|
let output = [];
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
output.push(quotePromptArg(items[i].title));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdin.on('keypress', (_, key) => {
|
||||||
|
if (key && key.name === 'return') {
|
||||||
|
updatePrompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.name === 'tab') {
|
||||||
|
vorpal.ui.imprint();
|
||||||
|
vorpal.log(vorpal.ui.input());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.push({
|
||||||
|
usage: 'cd <list-title>',
|
||||||
|
description: 'Moved to [list-title] - all further operations will happen within this list. Use `cd ..` to go back one level.',
|
||||||
|
action: function (args, end) {
|
||||||
|
let folderTitle = args['list-title'];
|
||||||
|
|
||||||
|
if (folderTitle == '..') {
|
||||||
|
switchCurrentFolder(null);
|
||||||
|
end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folderTitle == '.') {
|
||||||
|
end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Folder.loadByField('title', folderTitle).then((folder) => {
|
||||||
|
switchCurrentFolder(folder);
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
autocomplete: autocompleteFolders,
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.push({
|
||||||
|
usage: 'mklist <list-title>',
|
||||||
|
alias: 'mkdir',
|
||||||
|
description: 'Creates a new list',
|
||||||
|
action: function (args, end) {
|
||||||
|
Folder.save({ title: args['list-title'] }).catch((error) => {
|
||||||
|
this.log(error);
|
||||||
|
}).then((folder) => {
|
||||||
|
switchCurrentFolder(folder);
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.push({
|
||||||
|
usage: 'mknote <note-title>',
|
||||||
|
alias: 'touch',
|
||||||
|
description: 'Creates a new note',
|
||||||
|
action: function (args, end) {
|
||||||
|
if (!currentFolder) {
|
||||||
|
this.log('Notes can only be created within a list.');
|
||||||
|
end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let note = {
|
||||||
|
title: args['note-title'],
|
||||||
|
parent_id: currentFolder.id,
|
||||||
|
};
|
||||||
|
Note.save(note).catch((error) => {
|
||||||
|
this.log(error);
|
||||||
|
}).then((note) => {
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.push({
|
||||||
|
usage: 'set <item-title> <prop-name> [prop-value]',
|
||||||
|
description: 'Sets the given <prop-name> of the given item.',
|
||||||
|
action: function (args, end) {
|
||||||
|
let promise = null;
|
||||||
|
let title = args['item-title'];
|
||||||
|
let propName = args['prop-name'];
|
||||||
|
let propValue = args['prop-value'];
|
||||||
|
if (!propValue) propValue = '';
|
||||||
|
|
||||||
|
if (!currentFolder) {
|
||||||
|
promise = Folder.loadByField('title', title);
|
||||||
|
} else {
|
||||||
|
promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.then((item) => {
|
||||||
|
if (!item) {
|
||||||
|
this.log(_('No item with title "%s" found.', title));
|
||||||
|
end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newItem = {
|
||||||
|
id: item.id,
|
||||||
|
type_: item.type_,
|
||||||
|
};
|
||||||
|
newItem[propName] = propValue;
|
||||||
|
let ItemClass = BaseItem.itemClass();
|
||||||
|
return ItemClass.save(newItem);
|
||||||
|
}).catch((error) => {
|
||||||
|
this.log(error);
|
||||||
|
}).then(() => {
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
autocomplete: autocompleteItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.push({
|
||||||
|
usage: 'cat <item-title>',
|
||||||
|
description: 'Displays the given item data.',
|
||||||
|
action: function (args, end) {
|
||||||
|
let title = args['item-title'];
|
||||||
|
|
||||||
|
let promise = null;
|
||||||
|
if (!currentFolder) {
|
||||||
|
promise = Folder.loadByField('title', title);
|
||||||
|
} else {
|
||||||
|
promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.then((item) => {
|
||||||
|
if (!item) {
|
||||||
|
this.log(_('No item with title "%s" found.', title));
|
||||||
|
end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentFolder) {
|
||||||
|
this.log(Folder.serialize(item));
|
||||||
|
} else {
|
||||||
|
this.log(Note.serialize(item));
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
this.log(error);
|
||||||
|
}).then(() => {
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
autocomplete: autocompleteItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.push({
|
||||||
|
usage: 'rm <item-title>',
|
||||||
|
description: 'Deletes the given item. For a list, all the notes within that list will be deleted.',
|
||||||
|
action: function (args, end) {
|
||||||
|
let title = args['item-title'];
|
||||||
|
|
||||||
|
let promise = null;
|
||||||
|
let itemType = currentFolder ? 'note' : 'folder';
|
||||||
|
if (itemType == 'folder') {
|
||||||
|
promise = Folder.loadByField('title', title);
|
||||||
|
} else {
|
||||||
|
promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.then((item) => {
|
||||||
|
if (!item) {
|
||||||
|
this.log(_('No item with title "%s" found.', title));
|
||||||
|
end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemType == 'folder') {
|
||||||
|
return Folder.delete(item.id);
|
||||||
|
} else {
|
||||||
|
return Note.delete(item.id);
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
this.log(error);
|
||||||
|
}).then(() => {
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
autocomplete: autocompleteItems,
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.push({
|
||||||
|
usage: 'ls [list-title]',
|
||||||
|
alias: 'll',
|
||||||
|
description: 'Lists items in [list-title].',
|
||||||
|
action: function (args, end) {
|
||||||
|
let folderTitle = args['list-title'];
|
||||||
|
|
||||||
|
let promise = null;
|
||||||
|
|
||||||
|
if (folderTitle == '..') {
|
||||||
|
promise = Promise.resolve('root');
|
||||||
|
} else if (folderTitle && folderTitle != '.') {
|
||||||
|
promise = Folder.loadByField('title', folderTitle);
|
||||||
|
} else if (currentFolder) {
|
||||||
|
promise = Promise.resolve(currentFolder);
|
||||||
|
} else {
|
||||||
|
promise = Promise.resolve('root');
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.then((folder) => {
|
||||||
|
let p = null
|
||||||
|
let postfix = '';
|
||||||
|
if (folder === 'root') {
|
||||||
|
p = Folder.all();
|
||||||
|
postfix = '/';
|
||||||
|
} else if (!folder) {
|
||||||
|
throw new Error(_('Unknown list: "%s"', folderTitle));
|
||||||
|
} else {
|
||||||
|
p = Note.previews(folder.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.then((previews) => {
|
||||||
|
for (let i = 0; i < previews.length; i++) {
|
||||||
|
this.log(previews[i].title + postfix);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
this.log(error);
|
||||||
|
}).then(() => {
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
autocomplete: autocompleteFolders,
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.push({
|
||||||
|
usage: 'sync',
|
||||||
|
description: 'Synchronizes with remote storage.',
|
||||||
|
action: function (args, end) {
|
||||||
|
synchronizer('onedrive').then((s) => {
|
||||||
|
return s.start();
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
}).then(() => {
|
||||||
|
end();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < commands.length; i++) {
|
||||||
|
let c = commands[i];
|
||||||
|
let o = vorpal.command(c.usage, c.description);
|
||||||
|
if (c.alias) {
|
||||||
|
o.alias(c.alias);
|
||||||
|
}
|
||||||
|
if (c.autocomplete) {
|
||||||
|
o.autocomplete({
|
||||||
|
data: c.autocomplete,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
o.action(c.action);
|
||||||
|
}
|
||||||
|
|
||||||
|
vorpal.delimiter(promptString()).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error('Fatal error: ', error);
|
||||||
|
});
|
@ -1,5 +1,6 @@
|
|||||||
require('source-map-support').install();
|
require('source-map-support').install();
|
||||||
require('babel-plugin-transform-runtime');
|
require('babel-plugin-transform-runtime');
|
||||||
|
|
||||||
import { OneDriveApi } from 'src/onedrive-api.js';
|
import { OneDriveApi } from 'src/onedrive-api.js';
|
||||||
|
|
||||||
const fetch = require('node-fetch');
|
const fetch = require('node-fetch');
|
||||||
|
@ -50,10 +50,19 @@ async function main() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
let driver = new FileApiDriverOneDrive(config.oneDriveToken);
|
let driver = new FileApiDriverOneDrive('e09fc0de-c958-424f-83a2-e56a721d331b', 'JA3cwsqSGHFtjMwd5XoF5L5');
|
||||||
|
driver.api().setToken(config.oneDriveToken);
|
||||||
|
|
||||||
|
//config.oneDriveToken);
|
||||||
let api = new FileApi('/joplin', driver);
|
let api = new FileApi('/joplin', driver);
|
||||||
|
|
||||||
await api.delete('eraseme.txt');
|
let appDir = await driver.api().execJson('GET', '/drive/special/approot');
|
||||||
|
|
||||||
|
console.info(appDir);
|
||||||
|
|
||||||
|
// /drive/special/approot
|
||||||
|
|
||||||
|
// await api.delete('eraseme.txt');
|
||||||
|
|
||||||
// let result = await api.list();
|
// let result = await api.list();
|
||||||
// console.info(result);
|
// console.info(result);
|
||||||
@ -63,8 +72,8 @@ async function main() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
let content = await api.get('aaaaaaaaaaaaaaaaa.txt');
|
// let content = await api.get('aaaaaaaaaaaaaaaaa.txt');
|
||||||
console.info(content);
|
// console.info(content);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"query-string": "4.3.4",
|
"query-string": "4.3.4",
|
||||||
"react": "16.0.0-alpha.6",
|
"react": "16.0.0-alpha.6",
|
||||||
"sax": "^1.2.2",
|
"sax": "^1.2.2",
|
||||||
|
"server-destroy": "^1.0.1",
|
||||||
"source-map-support": "^0.4.15",
|
"source-map-support": "^0.4.15",
|
||||||
"sprintf-js": "^1.1.1",
|
"sprintf-js": "^1.1.1",
|
||||||
"sqlite3": "^3.1.8",
|
"sqlite3": "^3.1.8",
|
||||||
|
@ -4,6 +4,6 @@ CLIENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|||||||
rm -f "$CLIENT_DIR/app/src"
|
rm -f "$CLIENT_DIR/app/src"
|
||||||
ln -s "$CLIENT_DIR/../ReactNativeClient/src" "$CLIENT_DIR/app"
|
ln -s "$CLIENT_DIR/../ReactNativeClient/src" "$CLIENT_DIR/app"
|
||||||
|
|
||||||
npm run build && NODE_PATH="$CLIENT_DIR/build/" node build/cmd.js
|
npm run build && NODE_PATH="$CLIENT_DIR/build/" node build/main.js
|
||||||
#npm run build && NODE_PATH="$CLIENT_DIR/build/" node build/test-onedrive.js
|
#npm run build && NODE_PATH="$CLIENT_DIR/build/" node build/test-onedrive.js
|
||||||
#npm run build && NODE_PATH="$CLIENT_DIR/build/" node build/onedrive-server.js
|
#npm run build && NODE_PATH="$CLIENT_DIR/build/" node build/onedrive-server.js
|
@ -4,9 +4,12 @@ import { OneDriveApi } from 'src/onedrive-api.js';
|
|||||||
|
|
||||||
class FileApiDriverOneDrive {
|
class FileApiDriverOneDrive {
|
||||||
|
|
||||||
constructor(token) {
|
constructor(clientId, clientSecret) {
|
||||||
this.api_ = new OneDriveApi('e09fc0de-c958-424f-83a2-e56a721d331b');
|
this.api_ = new OneDriveApi(clientId, clientSecret);
|
||||||
this.api_.setToken(token);
|
}
|
||||||
|
|
||||||
|
api() {
|
||||||
|
return this.api_;
|
||||||
}
|
}
|
||||||
|
|
||||||
listReturnsFullPath() {
|
listReturnsFullPath() {
|
||||||
@ -20,7 +23,7 @@ class FileApiDriverOneDrive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
makePath_(path) {
|
makePath_(path) {
|
||||||
return '/drive/root:' + path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
makeItems_(odItems) {
|
makeItems_(odItems) {
|
||||||
@ -41,7 +44,12 @@ class FileApiDriverOneDrive {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async stat(path) {
|
async stat(path) {
|
||||||
|
try {
|
||||||
let item = await this.api_.execJson('GET', this.makePath_(path), this.itemFilter_());
|
let item = await this.api_.execJson('GET', this.makePath_(path), this.itemFilter_());
|
||||||
|
} catch (error) {
|
||||||
|
if (error.error.code == 'itemNotFound') return null;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
return this.makeItem_(item);
|
return this.makeItem_(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,8 +67,14 @@ class FileApiDriverOneDrive {
|
|||||||
return this.makeItems_(items.value);
|
return this.makeItems_(items.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
get(path) {
|
async get(path) {
|
||||||
return this.api_.execText('GET', this.makePath_(path) + ':/content');
|
try {
|
||||||
|
let content = await this.api_.execText('GET', this.makePath_(path) + ':/content');
|
||||||
|
} catch (error) {
|
||||||
|
if (error.error.code == 'itemNotFound') return null;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
mkdir(path) {
|
mkdir(path) {
|
||||||
|
@ -7,6 +7,10 @@ class FileApi {
|
|||||||
this.driver_ = driver;
|
this.driver_ = driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dlog(s) {
|
||||||
|
console.info('FileApi: ' + s);
|
||||||
|
}
|
||||||
|
|
||||||
fullPath_(path) {
|
fullPath_(path) {
|
||||||
let output = this.baseDir_;
|
let output = this.baseDir_;
|
||||||
if (path != '') output += '/' + path;
|
if (path != '') output += '/' + path;
|
||||||
@ -31,32 +35,35 @@ class FileApi {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
listDirectories() {
|
// listDirectories() {
|
||||||
return this.driver_.list(this.fullPath_('')).then((items) => {
|
// return this.driver_.list(this.fullPath_('')).then((items) => {
|
||||||
let output = [];
|
// let output = [];
|
||||||
for (let i = 0; i < items.length; i++) {
|
// for (let i = 0; i < items.length; i++) {
|
||||||
if (items[i].isDir) output.push(this.scopeItemToBaseDir_(items[i]));
|
// if (items[i].isDir) output.push(this.scopeItemToBaseDir_(items[i]));
|
||||||
}
|
// }
|
||||||
return output;
|
// return output;
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
list() {
|
list() {
|
||||||
|
this.dlog('list');
|
||||||
return this.driver_.list(this.baseDir_).then((items) => {
|
return this.driver_.list(this.baseDir_).then((items) => {
|
||||||
return this.scopeItemsToBaseDir_(items);
|
return this.scopeItemsToBaseDir_(items);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimestamp(path, timestamp) {
|
setTimestamp(path, timestamp) {
|
||||||
|
this.dlog('setTimestamp ' + path);
|
||||||
return this.driver_.setTimestamp(this.fullPath_(path), timestamp);
|
return this.driver_.setTimestamp(this.fullPath_(path), timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
mkdir(path) {
|
// mkdir(path) {
|
||||||
console.info('mkdir ' + path);
|
// this.dlog('delete ' + path);
|
||||||
return this.driver_.mkdir(this.fullPath_(path));
|
// return this.driver_.mkdir(this.fullPath_(path));
|
||||||
}
|
// }
|
||||||
|
|
||||||
stat(path) {
|
stat(path) {
|
||||||
|
this.dlog('stat ' + path);
|
||||||
return this.driver_.stat(this.fullPath_(path)).then((output) => {
|
return this.driver_.stat(this.fullPath_(path)).then((output) => {
|
||||||
if (!output) return output;
|
if (!output) return output;
|
||||||
output.path = path;
|
output.path = path;
|
||||||
@ -65,20 +72,24 @@ class FileApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get(path) {
|
get(path) {
|
||||||
|
this.dlog('get ' + path);
|
||||||
return this.driver_.get(this.fullPath_(path));
|
return this.driver_.get(this.fullPath_(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
put(path, content) {
|
put(path, content) {
|
||||||
|
this.dlog('put ' + path);
|
||||||
return this.driver_.put(this.fullPath_(path), content);
|
return this.driver_.put(this.fullPath_(path), content);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(path) {
|
delete(path) {
|
||||||
|
this.dlog('delete ' + path);
|
||||||
return this.driver_.delete(this.fullPath_(path));
|
return this.driver_.delete(this.fullPath_(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
move(oldPath, newPath) {
|
// move(oldPath, newPath) {
|
||||||
return this.driver_.move(this.fullPath_(oldPath), this.fullPath_(newPath));
|
// this.dlog('move ' + path);
|
||||||
}
|
// return this.driver_.move(this.fullPath_(oldPath), this.fullPath_(newPath));
|
||||||
|
// }
|
||||||
|
|
||||||
format() {
|
format() {
|
||||||
return this.driver_.format();
|
return this.driver_.format();
|
||||||
|
@ -133,6 +133,7 @@ Setting.defaults_ = {
|
|||||||
'sync.lastRevId': { value: 0, type: 'int' }, // DEPRECATED
|
'sync.lastRevId': { value: 0, type: 'int' }, // DEPRECATED
|
||||||
'sync.lastUpdateTime': { value: 0, type: 'int' },
|
'sync.lastUpdateTime': { value: 0, type: 'int' },
|
||||||
'sync.conflictFolderId': { value: '', type: 'string' },
|
'sync.conflictFolderId': { value: '', type: 'string' },
|
||||||
|
'sync.onedrive.auth': { value: '', type: 'string' },
|
||||||
};
|
};
|
||||||
|
|
||||||
export { Setting };
|
export { Setting };
|
@ -1,4 +1,9 @@
|
|||||||
const fetch = require('node-fetch');
|
const fetch = require('node-fetch');
|
||||||
|
const tcpPortUsed = require('tcp-port-used');
|
||||||
|
const http = require("http");
|
||||||
|
const urlParser = require("url");
|
||||||
|
const FormData = require('form-data');
|
||||||
|
const enableServerDestroy = require('server-destroy');
|
||||||
import { stringify } from 'query-string';
|
import { stringify } from 'query-string';
|
||||||
|
|
||||||
class OneDriveApi {
|
class OneDriveApi {
|
||||||
@ -6,10 +11,19 @@ class OneDriveApi {
|
|||||||
constructor(clientId, clientSecret) {
|
constructor(clientId, clientSecret) {
|
||||||
this.clientId_ = clientId;
|
this.clientId_ = clientId;
|
||||||
this.clientSecret_ = clientSecret;
|
this.clientSecret_ = clientSecret;
|
||||||
|
this.auth_ = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setToken(token) {
|
tokenBaseUrl() {
|
||||||
this.token_ = token;
|
return 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
|
||||||
|
}
|
||||||
|
|
||||||
|
setAuth(auth) {
|
||||||
|
this.auth_ = auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
token() {
|
||||||
|
return this.auth_ ? this.auth_.access_token : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
clientId() {
|
clientId() {
|
||||||
@ -20,10 +34,15 @@ class OneDriveApi {
|
|||||||
return this.clientSecret_;
|
return this.clientSecret_;
|
||||||
}
|
}
|
||||||
|
|
||||||
possibleOAuthFlowPorts() {
|
possibleOAuthDancePorts() {
|
||||||
return [1917, 9917, 8917];
|
return [1917, 9917, 8917];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async appDirectory() {
|
||||||
|
let r = await this.execJson('GET', '/drive/special/approot');
|
||||||
|
return r.parentReference.path + '/' + r.name;
|
||||||
|
}
|
||||||
|
|
||||||
authCodeUrl(redirectUri) {
|
authCodeUrl(redirectUri) {
|
||||||
let query = {
|
let query = {
|
||||||
client_id: this.clientId_,
|
client_id: this.clientId_,
|
||||||
@ -40,10 +59,6 @@ class OneDriveApi {
|
|||||||
if (!options) options = {};
|
if (!options) options = {};
|
||||||
if (!options.headers) options.headers = {};
|
if (!options.headers) options.headers = {};
|
||||||
|
|
||||||
if (this.token_) {
|
|
||||||
options.headers['Authorization'] = 'bearer ' + this.token_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method != 'GET') {
|
if (method != 'GET') {
|
||||||
options.method = method;
|
options.method = method;
|
||||||
}
|
}
|
||||||
@ -62,14 +77,24 @@ class OneDriveApi {
|
|||||||
console.info(method + ' ' + url);
|
console.info(method + ' ' + url);
|
||||||
console.info(data);
|
console.info(data);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
options.headers['Authorization'] = 'bearer ' + this.token();
|
||||||
|
|
||||||
let response = await fetch(url, options);
|
let response = await fetch(url, options);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
let error = await response.json();
|
let error = await response.json();
|
||||||
|
|
||||||
|
if (error && error.error && error.error.code == 'InvalidAuthenticationToken') {
|
||||||
|
await this.refreshAccessToken();
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async execJson(method, path, query, data) {
|
async execJson(method, path, query, data) {
|
||||||
let response = await this.exec(method, path, query, data);
|
let response = await this.exec(method, path, query, data);
|
||||||
@ -83,6 +108,114 @@ class OneDriveApi {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async refreshAccessToken() {
|
||||||
|
if (!this.auth_) throw new Error('Cannot refresh token: authentication data is missing');
|
||||||
|
|
||||||
|
let body = new FormData();
|
||||||
|
body.append('client_id', this.clientId());
|
||||||
|
body.append('client_secret', this.clientSecret());
|
||||||
|
body.append('refresh_token', this.auth_.refresh_token);
|
||||||
|
body.append('redirect_uri', 'http://localhost:1917');
|
||||||
|
body.append('grant_type', 'refresh_token');
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
method: 'POST',
|
||||||
|
body: body,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.auth_ = null;
|
||||||
|
|
||||||
|
let response = await fetch(this.tokenBaseUrl(), options);
|
||||||
|
if (!response.ok) {
|
||||||
|
let msg = await response.text();
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.auth_ = await response.json();
|
||||||
|
|
||||||
|
// POST https://login.microsoftonline.com/common/oauth2/v2.0/token
|
||||||
|
// Content-Type: application/x-www-form-urlencoded
|
||||||
|
|
||||||
|
// client_id={client_id}&redirect_uri={redirect_uri}&client_secret={client_secret}
|
||||||
|
// &refresh_token={refresh_token}&grant_type=refresh_token
|
||||||
|
}
|
||||||
|
|
||||||
|
async oauthDance() {
|
||||||
|
this.auth_ = null;
|
||||||
|
|
||||||
|
let ports = this.possibleOAuthDancePorts();
|
||||||
|
let port = null;
|
||||||
|
for (let i = 0; i < ports.length; i++) {
|
||||||
|
let inUse = await tcpPortUsed.check(ports[i]);
|
||||||
|
if (!inUse) {
|
||||||
|
port = ports[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!port) throw new Error('All potential ports are in use - please report the issue at https://github.com/laurent22/joplin');
|
||||||
|
|
||||||
|
let authCodeUrl = this.authCodeUrl('http://localhost:' + port);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let server = http.createServer();
|
||||||
|
let errorMessage = null;
|
||||||
|
|
||||||
|
server.on('request', (request, response) => {
|
||||||
|
const query = urlParser.parse(request.url, true).query;
|
||||||
|
|
||||||
|
function writeResponse(code, message) {
|
||||||
|
response.writeHead(code, {"Content-Type": "text/html"});
|
||||||
|
response.write(message);
|
||||||
|
response.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!query.code) return writeResponse(400, '"code" query parameter is missing');
|
||||||
|
|
||||||
|
let body = new FormData();
|
||||||
|
body.append('client_id', this.clientId());
|
||||||
|
body.append('client_secret', this.clientSecret());
|
||||||
|
body.append('code', query.code ? query.code : '');
|
||||||
|
body.append('redirect_uri', 'http://localhost:' + port.toString());
|
||||||
|
body.append('grant_type', 'authorization_code');
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
method: 'POST',
|
||||||
|
body: body,
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch(this.tokenBaseUrl(), options).then((r) => {
|
||||||
|
if (!r.ok) {
|
||||||
|
errorMessage = 'Could not retrieve auth code: ' + r.status + ': ' + r.statusText;
|
||||||
|
writeResponse(400, errorMessage);
|
||||||
|
server.destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.json().then((json) => {
|
||||||
|
this.auth_ = json;
|
||||||
|
writeResponse(200, 'The application has been authorised - you may now close this browser tab.');
|
||||||
|
server.destroy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('close', () => {
|
||||||
|
if (errorMessage) {
|
||||||
|
reject(new Error(errorMessage));
|
||||||
|
} else {
|
||||||
|
resolve(this.auth_);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(port);
|
||||||
|
|
||||||
|
enableServerDestroy(server);
|
||||||
|
|
||||||
|
console.info('Please open this URL in your browser to authentify the application: ' + authCodeUrl);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { OneDriveApi };
|
export { OneDriveApi };
|
Loading…
Reference in New Issue
Block a user