123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- const fs = require("fs");
- const sanitize = require("sanitize-filename");
- const sql = require('./sql.js');
- const decryptService = require('./decrypt.js');
- const dataKeyService = require('./data_key.js');
- const extensionService = require('./extension.js');
- function dumpDocument(documentPath, targetPath, options) {
- const stats = {
- succeeded: 0,
- failed: 0,
- protected: 0,
- deleted: 0
- };
- validatePaths(documentPath, targetPath);
- sql.openDatabase(documentPath);
- const dataKey = dataKeyService.getDataKey(options.password);
- const existingPaths = {};
- const noteIdToPath = {};
- dumpNote(targetPath, 'root');
- printDumpResults(stats, options);
- function dumpNote(targetPath, noteId) {
- console.log(`Reading note '${noteId}'`);
- let childTargetPath, noteRow, fileNameWithPath;
- try {
- noteRow = sql.getRow("SELECT * FROM notes WHERE noteId = ?", [noteId]);
- if (noteRow.isDeleted) {
- stats.deleted++;
- if (!options.includeDeleted) {
- console.log(`Note '${noteId}' is deleted and --include-deleted option is not used, skipping.`);
- return;
- }
- }
- if (noteRow.isProtected) {
- stats.protected++;
- noteRow.title = decryptService.decryptString(dataKey, noteRow.title);
- }
- let safeTitle = sanitize(noteRow.title);
- if (safeTitle.length > 20) {
- safeTitle = safeTitle.substring(0, 20);
- }
- childTargetPath = targetPath + '/' + safeTitle;
- for (let i = 1; i < 100000 && childTargetPath in existingPaths; i++) {
- childTargetPath = targetPath + '/' + safeTitle + '_' + i;
- }
- existingPaths[childTargetPath] = true;
- if (noteRow.noteId in noteIdToPath) {
- const message = `Note '${noteId}' has been already dumped to ${noteIdToPath[noteRow.noteId]}`;
- console.log(message);
- fs.writeFileSync(childTargetPath, message);
- return;
- }
- let {content} = sql.getRow("SELECT content FROM blobs WHERE blobId = ?", [noteRow.blobId]);
- if (content !== null && noteRow.isProtected && dataKey) {
- content = decryptService.decrypt(dataKey, content);
- }
- if (isContentEmpty(content)) {
- console.log(`Note '${noteId}' is empty, skipping.`);
- } else {
- fileNameWithPath = extensionService.getFileName(noteRow, childTargetPath, safeTitle);
- fs.writeFileSync(fileNameWithPath, content);
- stats.succeeded++;
- console.log(`Dumped note '${noteId}' into ${fileNameWithPath} successfully.`);
- }
- noteIdToPath[noteId] = childTargetPath;
- }
- catch (e) {
- console.error(`DUMPERROR: Writing '${noteId}' failed with error '${e.message}':\n${e.stack}`);
- stats.failed++;
- }
- const childNoteIds = sql.getColumn("SELECT noteId FROM branches WHERE parentNoteId = ?", [noteId]);
- if (childNoteIds.length > 0) {
- if (childTargetPath === fileNameWithPath) {
- childTargetPath += '_dir';
- }
- try {
- fs.mkdirSync(childTargetPath, {recursive: true});
- }
- catch (e) {
- console.error(`DUMPERROR: Creating directory ${childTargetPath} failed with error '${e.message}'`);
- }
- for (const childNoteId of childNoteIds) {
- dumpNote(childTargetPath, childNoteId);
- }
- }
- }
- }
- function printDumpResults(stats, options) {
- console.log('\n----------------------- STATS -----------------------');
- console.log('Successfully dumpted notes: ', stats.succeeded.toString().padStart(5, ' '));
- console.log('Protected notes: ', stats.protected.toString().padStart(5, ' '), options.password ? '' : '(skipped)');
- console.log('Failed notes: ', stats.failed.toString().padStart(5, ' '));
- console.log('Deleted notes: ', stats.deleted.toString().padStart(5, ' '), options.includeDeleted ? "(dumped)" : "(at least, skipped)");
- console.log('-----------------------------------------------------');
- if (!options.password && stats.protected > 0) {
- console.log("\nWARNING: protected notes are present in the document but no password has been provided. Protected notes have not been dumped.");
- }
- }
- function isContentEmpty(content) {
- if (!content) {
- return true;
- }
- if (typeof content === "string") {
- return !content.trim() || content.trim() === '<p></p>';
- }
- else if (Buffer.isBuffer(content)) {
- return content.length === 0;
- }
- else {
- return false;
- }
- }
- function validatePaths(documentPath, targetPath) {
- if (!fs.existsSync(documentPath)) {
- console.error(`Path to document '${documentPath}' has not been found. Run with --help to see usage.`);
- process.exit(1);
- }
- if (!fs.existsSync(targetPath)) {
- const ret = fs.mkdirSync(targetPath, {recursive: true});
- if (!ret) {
- console.error(`Target path '${targetPath}' could not be created. Run with --help to see usage.`);
- process.exit(1);
- }
- }
- }
- module.exports = {
- dumpDocument
- };
|