mirror of
https://github.com/zadam/trilium.git
synced 2025-10-30 01:36:24 +01:00
port dump-db and other tools to TS
This commit is contained in:
@@ -14,10 +14,10 @@ npm install
|
||||
|
||||
## Running
|
||||
|
||||
See output of `node dump-db.js --help`:
|
||||
See output of `npx esrun dump.ts --help`:
|
||||
|
||||
```
|
||||
dump-db.js <path_to_document> <target_directory>
|
||||
dump-db.ts <path_to_document> <target_directory>
|
||||
|
||||
dump the contents of document.db into the target directory
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const yargs = require('yargs/yargs')
|
||||
const { hideBin } = require('yargs/helpers')
|
||||
const dumpService = require('./inc/dump.js');
|
||||
import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
import dumpService from './inc/dump.js';
|
||||
|
||||
yargs(hideBin(process.argv))
|
||||
.command('$0 <path_to_document> <target_directory>', 'dump the contents of document.db into the target directory', (yargs) => {
|
||||
return yargs
|
||||
.positional('path_to_document', { describe: 'path to the document.db' })
|
||||
.positional('target_directory', { describe: 'path of the directory into which the notes should be dumped' })
|
||||
.option('path_to_document', { alias: 'p', describe: 'path to the document.db', type: 'string', demandOption: true })
|
||||
.option('target_directory', { alias: 't', describe: 'path of the directory into which the notes should be dumped', type: 'string', demandOption: true });
|
||||
}, (argv) => {
|
||||
try {
|
||||
dumpService.dumpDocument(argv.path_to_document, argv.target_directory, {
|
||||
@@ -1,8 +1,8 @@
|
||||
const crypto = require("crypto");
|
||||
const sql = require('./sql');
|
||||
const decryptService = require('./decrypt.js');
|
||||
import crypto from 'crypto';
|
||||
import sql from './sql.js';
|
||||
import decryptService from './decrypt.js';
|
||||
|
||||
function getDataKey(password) {
|
||||
function getDataKey(password: any) {
|
||||
if (!password) {
|
||||
return null;
|
||||
}
|
||||
@@ -16,28 +16,28 @@ function getDataKey(password) {
|
||||
|
||||
return decryptedDataKey;
|
||||
}
|
||||
catch (e) {
|
||||
catch (e: any) {
|
||||
throw new Error(`Cannot read data key, the entered password might be wrong. The underlying error: '${e.message}', stack:\n${e.stack}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getPasswordDerivedKey(password) {
|
||||
function getPasswordDerivedKey(password: any) {
|
||||
const salt = getOption('passwordDerivedKeySalt');
|
||||
|
||||
return getScryptHash(password, salt);
|
||||
}
|
||||
|
||||
function getScryptHash(password, salt) {
|
||||
function getScryptHash(password: any, salt: any) {
|
||||
const hashed = crypto.scryptSync(password, salt, 32,
|
||||
{N: 16384, r:8, p:1});
|
||||
{ N: 16384, r: 8, p: 1 });
|
||||
|
||||
return hashed;
|
||||
}
|
||||
|
||||
function getOption(name) {
|
||||
function getOption(name: string) {
|
||||
return sql.getValue("SELECT value FROM options WHERE name = ?", [name]);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
getDataKey
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
const crypto = require("crypto");
|
||||
import crypto from 'crypto';
|
||||
|
||||
function decryptString(dataKey, cipherText) {
|
||||
function decryptString(dataKey: any, cipherText: any) {
|
||||
const buffer = decrypt(dataKey, cipherText);
|
||||
|
||||
if (buffer === null) {
|
||||
@@ -16,7 +16,7 @@ function decryptString(dataKey, cipherText) {
|
||||
return str;
|
||||
}
|
||||
|
||||
function decrypt(key, cipherText, ivLength = 13) {
|
||||
function decrypt(key: any, cipherText: any, ivLength = 13) {
|
||||
if (cipherText === null) {
|
||||
return null;
|
||||
}
|
||||
@@ -46,11 +46,10 @@ function decrypt(key, cipherText, ivLength = 13) {
|
||||
|
||||
return payload;
|
||||
}
|
||||
catch (e) {
|
||||
catch (e: any) {
|
||||
// recovery from https://github.com/zadam/trilium/issues/510
|
||||
if (e.message?.includes("WRONG_FINAL_BLOCK_LENGTH") || e.message?.includes("wrong final block length")) {
|
||||
log.info("Caught WRONG_FINAL_BLOCK_LENGTH, returning cipherText instead");
|
||||
|
||||
console.log("Caught WRONG_FINAL_BLOCK_LENGTH, returning cipherText instead");
|
||||
return cipherText;
|
||||
}
|
||||
else {
|
||||
@@ -59,7 +58,7 @@ function decrypt(key, cipherText, ivLength = 13) {
|
||||
}
|
||||
}
|
||||
|
||||
function pad(data) {
|
||||
function pad(data: any) {
|
||||
if (data.length > 16) {
|
||||
data = data.slice(0, 16);
|
||||
}
|
||||
@@ -72,7 +71,7 @@ function pad(data) {
|
||||
return Buffer.from(data);
|
||||
}
|
||||
|
||||
function arraysIdentical(a, b) {
|
||||
function arraysIdentical(a: any, b: any) {
|
||||
let i = a.length;
|
||||
if (i !== b.length) return false;
|
||||
while (i--) {
|
||||
@@ -81,12 +80,12 @@ function arraysIdentical(a, b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function shaArray(content) {
|
||||
function shaArray(content: any) {
|
||||
// we use this as simple checksum and don't rely on its security so SHA-1 is good enough
|
||||
return crypto.createHash('sha1').update(content).digest();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
decrypt,
|
||||
decryptString
|
||||
};
|
||||
@@ -1,11 +1,11 @@
|
||||
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');
|
||||
import fs from 'fs';
|
||||
import sanitize from 'sanitize-filename';
|
||||
import sql from './sql.js';
|
||||
import decryptService from './decrypt.js';
|
||||
import dataKeyService from './data_key.js';
|
||||
import extensionService from './extension.js';
|
||||
|
||||
function dumpDocument(documentPath, targetPath, options) {
|
||||
function dumpDocument(documentPath: string, targetPath: string, options: { password: any; includeDeleted: any; }) {
|
||||
const stats = {
|
||||
succeeded: 0,
|
||||
failed: 0,
|
||||
@@ -19,14 +19,14 @@ function dumpDocument(documentPath, targetPath, options) {
|
||||
|
||||
const dataKey = dataKeyService.getDataKey(options.password);
|
||||
|
||||
const existingPaths = {};
|
||||
const noteIdToPath = {};
|
||||
const existingPaths: Record<string, any> = {};
|
||||
const noteIdToPath: Record<string, any> = {};
|
||||
|
||||
dumpNote(targetPath, 'root');
|
||||
|
||||
printDumpResults(stats, options);
|
||||
|
||||
function dumpNote(targetPath, noteId) {
|
||||
function dumpNote(targetPath: any, noteId: any) {
|
||||
console.log(`Reading note '${noteId}'`);
|
||||
|
||||
let childTargetPath, noteRow, fileNameWithPath;
|
||||
@@ -94,7 +94,7 @@ function dumpDocument(documentPath, targetPath, options) {
|
||||
|
||||
noteIdToPath[noteId] = childTargetPath;
|
||||
}
|
||||
catch (e) {
|
||||
catch (e: any) {
|
||||
console.error(`DUMPERROR: Writing '${noteId}' failed with error '${e.message}':\n${e.stack}`);
|
||||
|
||||
stats.failed++;
|
||||
@@ -108,9 +108,9 @@ function dumpDocument(documentPath, targetPath, options) {
|
||||
}
|
||||
|
||||
try {
|
||||
fs.mkdirSync(childTargetPath, { recursive: true });
|
||||
fs.mkdirSync(childTargetPath as string, { recursive: true });
|
||||
}
|
||||
catch (e) {
|
||||
catch (e: any) {
|
||||
console.error(`DUMPERROR: Creating directory ${childTargetPath} failed with error '${e.message}'`);
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ function dumpDocument(documentPath, targetPath, options) {
|
||||
}
|
||||
}
|
||||
|
||||
function printDumpResults(stats, options) {
|
||||
function printDumpResults(stats: any, options: any) {
|
||||
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)');
|
||||
@@ -134,7 +134,7 @@ function printDumpResults(stats, options) {
|
||||
}
|
||||
}
|
||||
|
||||
function isContentEmpty(content) {
|
||||
function isContentEmpty(content: any) {
|
||||
if (!content) {
|
||||
return true;
|
||||
}
|
||||
@@ -150,7 +150,7 @@ function isContentEmpty(content) {
|
||||
}
|
||||
}
|
||||
|
||||
function validatePaths(documentPath, targetPath) {
|
||||
function validatePaths(documentPath: string, targetPath: string) {
|
||||
if (!fs.existsSync(documentPath)) {
|
||||
console.error(`Path to document '${documentPath}' has not been found. Run with --help to see usage.`);
|
||||
process.exit(1);
|
||||
@@ -166,6 +166,6 @@ function validatePaths(documentPath, targetPath) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
dumpDocument
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
const path = require("path");
|
||||
const mimeTypes = require("mime-types");
|
||||
import path from "path";
|
||||
import mimeTypes from "mime-types";
|
||||
|
||||
function getFileName(note, childTargetPath, safeTitle) {
|
||||
function getFileName(note: any, childTargetPath: string, safeTitle: string) {
|
||||
let existingExtension = path.extname(safeTitle).toLowerCase();
|
||||
let newExtension;
|
||||
|
||||
@@ -29,6 +29,6 @@ function getFileName(note, childTargetPath, safeTitle) {
|
||||
return fileNameWithPath;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
getFileName
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
const Database = require("better-sqlite3");
|
||||
let dbConnection;
|
||||
|
||||
const openDatabase = (documentPath) => { dbConnection = new Database(documentPath, { readonly: true }) };
|
||||
|
||||
const getRow = (query, params = []) => dbConnection.prepare(query).get(params);
|
||||
const getRows = (query, params = []) => dbConnection.prepare(query).all(params);
|
||||
const getValue = (query, params = []) => dbConnection.prepare(query).pluck().get(params);
|
||||
const getColumn = (query, params = []) => dbConnection.prepare(query).pluck().all(params);
|
||||
|
||||
module.exports = {
|
||||
openDatabase,
|
||||
getRow,
|
||||
getRows,
|
||||
getValue,
|
||||
getColumn
|
||||
};
|
||||
18
dump-db/inc/sql.ts
Normal file
18
dump-db/inc/sql.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import Database, { Database as DatabaseType } from "better-sqlite3";
|
||||
|
||||
let dbConnection: DatabaseType;
|
||||
|
||||
const openDatabase = (documentPath: string) => { dbConnection = new Database(documentPath, { readonly: true }) };
|
||||
|
||||
const getRow = (query: string, params: string[] = []): Record<string, any> => dbConnection.prepare(query).get(params) as Record<string, any>;
|
||||
const getRows = (query: string, params = []) => dbConnection.prepare(query).all(params);
|
||||
const getValue = (query: string, params: string[] = []) => dbConnection.prepare(query).pluck().get(params);
|
||||
const getColumn = (query: string, params: string[] = []) => dbConnection.prepare(query).pluck().all(params);
|
||||
|
||||
export default {
|
||||
openDatabase,
|
||||
getRow,
|
||||
getRows,
|
||||
getValue,
|
||||
getColumn
|
||||
};
|
||||
1513
dump-db/package-lock.json
generated
1513
dump-db/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,24 +2,30 @@
|
||||
"name": "dump-db",
|
||||
"version": "1.0.0",
|
||||
"description": "Standalone tool to dump contents of Trilium document.db file into a directory tree of notes",
|
||||
"main": "dump-db.js",
|
||||
"main": "dump-db.ts",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/zadam/trilium.git"
|
||||
"url": "git+https://github.com/TriliumNext/Notes.git"
|
||||
},
|
||||
"author": "zadam",
|
||||
"author": "TriliumNext",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/zadam/trilium/issues"
|
||||
"url": "https://github.com/TriliumNext/Notes/issues"
|
||||
},
|
||||
"homepage": "https://github.com/zadam/trilium/dump-db#readme",
|
||||
"homepage": "https://github.com/TriliumNext/Notes/blob/master/dump-db/README.md",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "7.5.0",
|
||||
"mime-types": "2.1.34",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"yargs": "17.3.1"
|
||||
"better-sqlite3": "^11.1.2",
|
||||
"esrun": "^3.2.26",
|
||||
"mime-types": "^2.1.34",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"yargs": "^17.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/better-sqlite3": "^7.6.11",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/yargs": "^17.0.33"
|
||||
}
|
||||
}
|
||||
|
||||
10
dump-db/tsconfig.json
Normal file
10
dump-db/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "ES6",
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user