Compare commits

...

20 Commits

Author SHA1 Message Date
zadam
a1402c7c66 release 0.37.7 2019-12-02 23:09:42 +01:00
zadam
6ba3e5ab7f backport fix from master to avoid doubled attributes inherited from multiple paths 2019-12-02 23:07:19 +01:00
zadam
f740e52ebf correctly respect label @disableVersioning
(cherry picked from commit dc063983ea)
2019-12-02 23:06:06 +01:00
zadam
e9454e4db7 fix SQL console scrolling
(cherry picked from commit 749bb90713)
2019-12-02 23:05:05 +01:00
zadam
bfc7570e14 don't convert MD to HTML if "import markdown as text" is not selected, closes #733 2019-12-01 11:27:22 +01:00
zadam
5de92171a7 use owned attributes where it's a better fit 2019-12-01 10:28:05 +01:00
zadam
29c5e394ab generate document now creates also labels and relations 2019-12-01 10:20:18 +01:00
zadam
07b3d11fe5 fix generate new document script 2019-12-01 09:19:16 +01:00
zadam
67663fba50 fixes 2019-11-30 11:36:36 +01:00
zadam
995ebbf577 removed foreign keys PRAGMAs since foreign key constraints are not used anymore 2019-11-30 10:41:53 +01:00
zadam
d0e6be3e0c entity stat as part of consistency checks 2019-11-30 09:15:08 +01:00
zadam
01370a5968 fix anonymization according to latest schema 2019-11-29 21:42:24 +01:00
zadam
5b30291601 release 0.37.6 2019-11-26 22:50:08 +01:00
zadam
5193f073e9 if there's no updated field use created #725 2019-11-26 22:47:54 +01:00
zadam
6c7d8a9667 preserve dateCreated and dateModified in ENEX import, fixes #725 2019-11-26 22:02:21 +01:00
zadam
5e9bedd903 fixed sidebar switch in the options dialog 2019-11-26 20:46:49 +01:00
zadam
e712990c03 don't remove active tab after deleting note to preserve tab state, fixes #727 2019-11-26 20:42:34 +01:00
zadam
91487b338a make the context menu scrollable when exceeding total window height, closes #723 2019-11-26 19:55:52 +01:00
zadam
3ff24d53e5 fix decrypting note titles on the server installation, closes #724 2019-11-26 19:49:52 +01:00
zadam
94c904fb40 fix context menu over root, closes #726 2019-11-26 19:42:47 +01:00
23 changed files with 147 additions and 74 deletions

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<dataSource name="document.db"> <dataSource name="document.db">
<database-model serializer="dbm" dbms="SQLITE" family-id="SQLITE" format-version="4.16"> <database-model serializer="dbm" dbms="SQLITE" family-id="SQLITE" format-version="4.17">
<root id="1"> <root id="1">
<ServerVersion>3.25.1</ServerVersion> <ServerVersion>3.25.1</ServerVersion>
</root> </root>

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "trilium", "name": "trilium",
"version": "0.37.4", "version": "0.37.6",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -2,7 +2,7 @@
"name": "trilium", "name": "trilium",
"productName": "Trilium Notes", "productName": "Trilium Notes",
"description": "Trilium Notes", "description": "Trilium Notes",
"version": "0.37.5", "version": "0.37.7",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"main": "electron.js", "main": "electron.js",
"bin": { "bin": {

View File

@@ -334,6 +334,11 @@ class Note extends Entity {
// we order by noteId so that attributes from same note stay together. Actual noteId ordering doesn't matter. // we order by noteId so that attributes from same note stay together. Actual noteId ordering doesn't matter.
const filteredAttributes = attributes.filter((attr, index) => { const filteredAttributes = attributes.filter((attr, index) => {
// if this exact attribute already appears then don't include it (can happen via cloning)
if (attributes.findIndex(it => it.attributeId === attr.attributeId) !== index) {
return false;
}
if (attr.isDefinition()) { if (attr.isDefinition()) {
const firstDefinitionIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name); const firstDefinitionIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name);
@@ -788,6 +793,7 @@ class Note extends Entity {
delete pojo.isContentAvailable; delete pojo.isContentAvailable;
delete pojo.__attributeCache; delete pojo.__attributeCache;
delete pojo.content; delete pojo.content;
/** zero references to contentHash, probably can be removed */
delete pojo.contentHash; delete pojo.contentHash;
} }
} }

View File

@@ -93,7 +93,7 @@ export default class SidebarOptions {
this.$sidebarMinWidth.val(options.sidebarMinWidth); this.$sidebarMinWidth.val(options.sidebarMinWidth);
this.$sidebarWidthPercent.val(options.sidebarWidthPercent); this.$sidebarWidthPercent.val(options.sidebarWidthPercent);
if (parseInt(options.showSidebarInNewTab)) { if (options.showSidebarInNewTab === 'true') {
this.$showSidebarInNewTab.attr("checked", "checked"); this.$showSidebarInNewTab.attr("checked", "checked");
} }
else { else {

View File

@@ -273,7 +273,9 @@ async function filterTabs(noteId) {
async function noteDeleted(noteId) { async function noteDeleted(noteId) {
for (const tc of tabContexts) { for (const tc of tabContexts) {
if (tc.notePath && tc.notePath.split("/").includes(noteId)) { // not removing active even if it contains deleted note since that one will move to another note (handled by deletion logic)
// and we would lose tab context state (e.g. sidebar visibility)
if (!tc.isActive() && tc.notePath && tc.notePath.split("/").includes(noteId)) {
await tabRow.removeTab(tc.$tab[0]); await tabRow.removeTab(tc.$tab[0]);
} }
} }

View File

@@ -246,11 +246,15 @@ class TabContext {
} }
setCurrentNotePathToHash() { setCurrentNotePathToHash() {
if (this.$tab[0] === this.tabRow.activeTabEl) { if (this.isActive()) {
document.location.hash = (this.notePath || "") + "-" + this.tabId; document.location.hash = (this.notePath || "") + "-" + this.tabId;
} }
} }
isActive() {
return this.$tab[0] === this.tabRow.activeTabEl;
}
setupClasses() { setupClasses() {
for (const clazz of Array.from(this.$tab[0].classList)) { // create copy to safely iterate over while removing classes for (const clazz of Array.from(this.$tab[0].classList)) { // create copy to safely iterate over while removing classes
if (clazz !== 'note-tab') { if (clazz !== 'note-tab') {

View File

@@ -42,7 +42,7 @@ class TreeContextMenu {
|| (selNodes.length === 1 && selNodes[0] === this.node); || (selNodes.length === 1 && selNodes[0] === this.node);
const notSearch = note.type !== 'search'; const notSearch = note.type !== 'search';
const parentNotSearch = parentNote.type !== 'search'; const parentNotSearch = !parentNote || parentNote.type !== 'search';
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch; const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
return [ return [
@@ -79,7 +79,7 @@ class TreeContextMenu {
{ title: "Paste after", cmd: "pasteAfter", uiIcon: "paste", { title: "Paste after", cmd: "pasteAfter", uiIcon: "paste",
enabled: !clipboard.isClipboardEmpty() && isNotRoot && !isHoisted && parentNotSearch && noSelectedNotes }, enabled: !clipboard.isClipboardEmpty() && isNotRoot && !isHoisted && parentNotSearch && noSelectedNotes },
{ title: "Duplicate note here", cmd: "duplicateNote", uiIcon: "empty", { title: "Duplicate note here", cmd: "duplicateNote", uiIcon: "empty",
enabled: noSelectedNotes && parentNotSearch && (!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) }, enabled: noSelectedNotes && parentNotSearch && isNotRoot && !isHoisted && (!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) },
{ title: "----" }, { title: "----" },
{ title: "Export", cmd: "export", uiIcon: "empty", { title: "Export", cmd: "export", uiIcon: "empty",
enabled: notSearch && noSelectedNotes }, enabled: notSearch && noSelectedNotes },

View File

@@ -89,6 +89,11 @@ body {
font-size: inherit; font-size: inherit;
} }
#context-menu-container {
max-height: 100vh;
overflow: auto; /* make it scrollable when exceeding total height of the window */
}
#context-menu-container, #context-menu-container .dropdown-menu { #context-menu-container, #context-menu-container .dropdown-menu {
padding: 3px 0 0; padding: 3px 0 0;
z-index: 1111; z-index: 1111;

View File

@@ -411,6 +411,10 @@ div.ui-tooltip {
height: 150px; height: 150px;
} }
#sql-console-query .CodeMirror-scroll {
min-height: inherit !important;
}
.btn { .btn {
border-radius: var(--button-border-radius); border-radius: var(--button-border-radius);
} }

View File

@@ -18,7 +18,10 @@ async function anonymize() {
await db.run("UPDATE notes SET title = 'title'"); await db.run("UPDATE notes SET title = 'title'");
await db.run("UPDATE note_contents SET content = 'text'"); await db.run("UPDATE note_contents SET content = 'text'");
await db.run("UPDATE note_revisions SET title = 'title', content = 'text'"); await db.run("UPDATE note_revisions SET title = 'title'");
await db.run("UPDATE note_revision_contents SET content = 'title'");
await db.run("UPDATE attributes SET name = 'name', value = 'value' WHERE type = 'label'");
await db.run("UPDATE attributes SET name = 'name' WHERE type = 'relation'");
await db.run("UPDATE branches SET prefix = 'prefix' WHERE prefix IS NOT NULL"); await db.run("UPDATE branches SET prefix = 'prefix' WHERE prefix IS NOT NULL");
await db.run(`UPDATE options SET value = 'anonymized' WHERE name IN await db.run(`UPDATE options SET value = 'anonymized' WHERE name IN
('documentSecret', 'encryptedDataKey', 'passwordVerificationHash', ('documentSecret', 'encryptedDataKey', 'passwordVerificationHash',

View File

@@ -1 +1 @@
module.exports = { buildDate:"2019-11-25T22:46:15+01:00", buildRevision: "bf9ad976b9bf340db3d47db72ddf0857a04de178" }; module.exports = { buildDate:"2019-12-02T23:09:42+01:00", buildRevision: "6ba3e5ab7f866ac93ecbb48883dabf61723cde98" };

View File

@@ -626,12 +626,31 @@ async function runAllChecks() {
return !unrecoveredConsistencyErrors; return !unrecoveredConsistencyErrors;
} }
async function showEntityStat(name, query) {
const map = await sql.getMap(query);
map[0] = map[0] || 0;
map[1] = map[1] || 0;
log.info(`${name} deleted: ${map[1]}, not deleted ${map[0]}`);
}
async function runDbDiagnostics() {
await showEntityStat("Notes", `SELECT isDeleted, count(noteId) FROM notes GROUP BY isDeleted`);
await showEntityStat("Note revisions", `SELECT isErased, count(noteRevisionId) FROM note_revisions GROUP BY isErased`);
await showEntityStat("Branches", `SELECT isDeleted, count(branchId) FROM branches GROUP BY isDeleted`);
await showEntityStat("Attributes", `SELECT isDeleted, count(attributeId) FROM attributes GROUP BY isDeleted`);
await showEntityStat("API tokens", `SELECT isDeleted, count(apiTokenId) FROM api_tokens GROUP BY isDeleted`);
}
async function runChecks() { async function runChecks() {
let elapsedTimeMs; let elapsedTimeMs;
await syncMutexService.doExclusively(async () => { await syncMutexService.doExclusively(async () => {
const startTime = new Date(); const startTime = new Date();
await runDbDiagnostics();
await runAllChecks(); await runAllChecks();
elapsedTimeMs = Date.now() - startTime.getTime(); elapsedTimeMs = Date.now() - startTime.getTime();
@@ -663,7 +682,7 @@ sqlInit.dbReady.then(() => {
setInterval(cls.wrap(runChecks), 60 * 60 * 1000); setInterval(cls.wrap(runChecks), 60 * 60 * 1000);
// kickoff checks soon after startup (to not block the initial load) // kickoff checks soon after startup (to not block the initial load)
setTimeout(cls.wrap(runChecks), 10 * 1000); setTimeout(cls.wrap(runChecks), 20 * 1000);
}); });
module.exports = {}; module.exports = {};

View File

@@ -81,7 +81,7 @@ eventService.subscribe(eventService.CHILD_NOTE_CREATED, async ({ parentNote, chi
async function processInverseRelations(entityName, entity, handler) { async function processInverseRelations(entityName, entity, handler) {
if (entityName === 'attributes' && entity.type === 'relation') { if (entityName === 'attributes' && entity.type === 'relation') {
const note = await entity.getNote(); const note = await entity.getNote();
const attributes = (await note.getAttributes(entity.name)).filter(relation => relation.type === 'relation-definition'); const attributes = (await note.getOwnedAttributes(entity.name)).filter(relation => relation.type === 'relation-definition');
for (const attribute of attributes) { for (const attribute of attributes) {
const definition = attribute.value; const definition = attribute.value;

View File

@@ -12,9 +12,7 @@ const imageminGifLossy = require('imagemin-giflossy');
const jimp = require('jimp'); const jimp = require('jimp');
const imageType = require('image-type'); const imageType = require('image-type');
const sanitizeFilename = require('sanitize-filename'); const sanitizeFilename = require('sanitize-filename');
const dateUtils = require('./date_utils');
const noteRevisionService = require('./note_revisions.js'); const noteRevisionService = require('./note_revisions.js');
const NoteRevision = require("../entities/note_revision");
async function processImage(uploadBuffer, originalName, shrinkImageSwitch) { async function processImage(uploadBuffer, originalName, shrinkImageSwitch) {
const origImageFormat = imageType(uploadBuffer); const origImageFormat = imageType(uploadBuffer);

View File

@@ -3,6 +3,7 @@ const fileType = require('file-type');
const stream = require('stream'); const stream = require('stream');
const log = require("../log"); const log = require("../log");
const utils = require("../utils"); const utils = require("../utils");
const sql = require("../sql");
const noteService = require("../notes"); const noteService = require("../notes");
const imageService = require("../image"); const imageService = require("../image");
const protectedSessionService = require('../protected_session'); const protectedSessionService = require('../protected_session');
@@ -11,7 +12,7 @@ const protectedSessionService = require('../protected_session');
function parseDate(text) { function parseDate(text) {
// insert - and : to make it ISO format // insert - and : to make it ISO format
text = text.substr(0, 4) + "-" + text.substr(4, 2) + "-" + text.substr(6, 2) text = text.substr(0, 4) + "-" + text.substr(4, 2) + "-" + text.substr(6, 2)
+ "T" + text.substr(9, 2) + ":" + text.substr(11, 2) + ":" + text.substr(13, 2) + "Z"; + " " + text.substr(9, 2) + ":" + text.substr(11, 2) + ":" + text.substr(13, 2) + ".000Z";
return text; return text;
} }
@@ -150,7 +151,7 @@ async function importEnex(taskContext, file, parentNote) {
} else if (currentTag === 'created') { } else if (currentTag === 'created') {
note.utcDateCreated = parseDate(text); note.utcDateCreated = parseDate(text);
} else if (currentTag === 'updated') { } else if (currentTag === 'updated') {
// updated is currently ignored since utcDateModified is updated automatically with each save note.utcDateModified = parseDate(text);
} else if (currentTag === 'tag') { } else if (currentTag === 'tag') {
note.attributes.push({ note.attributes.push({
type: 'label', type: 'label',
@@ -187,9 +188,27 @@ async function importEnex(taskContext, file, parentNote) {
} }
}); });
async function updateDates(noteId, utcDateCreated, utcDateModified) {
// it's difficult to force custom dateCreated and dateModified to Note entity so we do it post-creation with SQL
await sql.execute(`
UPDATE notes
SET dateCreated = ?,
utcDateCreated = ?,
dateModified = ?,
utcDateModified = ?
WHERE noteId = ?`,
[utcDateCreated, utcDateCreated, utcDateModified, utcDateModified, noteId]);
await sql.execute(`
UPDATE note_contents
SET utcDateModified = ?
WHERE noteId = ?`,
[utcDateModified, noteId]);
}
async function saveNote() { async function saveNote() {
// make a copy because stream continues with the next async call and note gets overwritten // make a copy because stream continues with the next async call and note gets overwritten
let {title, content, attributes, resources, utcDateCreated} = note; let {title, content, attributes, resources, utcDateCreated, utcDateModified} = note;
content = extractContent(content); content = extractContent(content);
@@ -201,6 +220,10 @@ async function importEnex(taskContext, file, parentNote) {
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
})).note; })).note;
utcDateCreated = utcDateCreated || noteEntity.utcDateCreated;
// sometime date modified is not present in ENEX, then use date created
utcDateModified = utcDateModified || utcDateCreated;
taskContext.increaseProgressCount(); taskContext.increaseProgressCount();
let noteContent = await noteEntity.getContent(); let noteContent = await noteEntity.getContent();
@@ -224,6 +247,8 @@ async function importEnex(taskContext, file, parentNote) {
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
})).note; })).note;
await updateDates(resourceNote.noteId, utcDateCreated, utcDateModified);
taskContext.increaseProgressCount(); taskContext.increaseProgressCount();
const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`; const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`;
@@ -235,7 +260,9 @@ async function importEnex(taskContext, file, parentNote) {
try { try {
const originalName = "image." + resource.mime.substr(6); const originalName = "image." + resource.mime.substr(6);
const {url} = await imageService.saveImage(noteEntity.noteId, resource.content, originalName, taskContext.data.shrinkImages); const {url, note: imageNote} = await imageService.saveImage(noteEntity.noteId, resource.content, originalName, taskContext.data.shrinkImages);
await updateDates(imageNote.noteId, utcDateCreated, utcDateModified);
const imageLink = `<img src="${url}">`; const imageLink = `<img src="${url}">`;
@@ -257,6 +284,10 @@ async function importEnex(taskContext, file, parentNote) {
// save updated content with links to files/images // save updated content with links to files/images
await noteEntity.setContent(noteContent); await noteEntity.setContent(noteContent);
await noteService.scanForLinks(noteEntity.noteId);
await updateDates(noteEntity.noteId, utcDateCreated, utcDateModified);
} }
saxStream.on("closetag", async tag => { saxStream.on("closetag", async tag => {

View File

@@ -258,7 +258,8 @@ async function importTar(taskContext, fileBuffer, importRootNote) {
content = content.toString("UTF-8"); content = content.toString("UTF-8");
} }
if ((noteMeta && noteMeta.format === 'markdown') || (!noteMeta && ['text/markdown', 'text/x-markdown'].includes(mime))) { if ((noteMeta && noteMeta.format === 'markdown')
|| (!noteMeta && taskContext.data.textImportedAsText && ['text/markdown', 'text/x-markdown'].includes(mime))) {
const parsed = mdReader.parse(content); const parsed = mdReader.parse(content);
content = mdWriter.render(parsed); content = mdWriter.render(parsed);
} }

View File

@@ -43,9 +43,6 @@ async function migrate() {
try { try {
log.info("Attempting migration to version " + mig.dbVersion); log.info("Attempting migration to version " + mig.dbVersion);
// needs to happen outside of the transaction (otherwise it's a NO-OP)
await sql.execute("PRAGMA foreign_keys = OFF");
await sql.transactional(async () => { await sql.transactional(async () => {
if (mig.type === 'sql') { if (mig.type === 'sql') {
const migrationSql = fs.readFileSync(resourceDir.MIGRATIONS_DIR + "/" + mig.file).toString('utf8'); const migrationSql = fs.readFileSync(resourceDir.MIGRATIONS_DIR + "/" + mig.file).toString('utf8');
@@ -76,10 +73,6 @@ async function migrate() {
utils.crash(); utils.crash();
} }
finally {
// make sure foreign keys are enabled even if migration script disables them
await sql.execute("PRAGMA foreign_keys = ON");
}
} }
if (await sqlInit.isDbUpToDate()) { if (await sqlInit.isDbUpToDate()) {

View File

@@ -7,10 +7,6 @@ const dateUtils = require('../services/date_utils');
* @param {Note} note * @param {Note} note
*/ */
async function protectNoteRevisions(note) { async function protectNoteRevisions(note) {
if (await note.hasLabel('disableVersioning')) {
return;
}
for (const revision of await note.getRevisions()) { for (const revision of await note.getRevisions()) {
if (note.isProtected !== revision.isProtected) { if (note.isProtected !== revision.isProtected) {
const content = await revision.getContent(); const content = await revision.getContent();
@@ -30,6 +26,10 @@ async function protectNoteRevisions(note) {
* @return {NoteRevision} * @return {NoteRevision}
*/ */
async function createNoteRevision(note) { async function createNoteRevision(note) {
if (await note.hasLabel("disableVersioning")) {
return;
}
const noteRevision = await new NoteRevision({ const noteRevision = await new NoteRevision({
noteId: note.noteId, noteId: note.noteId,
// title and text should be decrypted now // title and text should be decrypted now

View File

@@ -117,7 +117,7 @@ async function createNewNote(parentNoteId, noteData) {
isExpanded: !!noteData.isExpanded isExpanded: !!noteData.isExpanded
}).save(); }).save();
for (const attr of await parentNote.getAttributes()) { for (const attr of await parentNote.getOwnedAttributes()) {
if (attr.name.startsWith("child:")) { if (attr.name.startsWith("child:")) {
await new Attribute({ await new Attribute({
noteId: note.noteId, noteId: note.noteId,
@@ -308,8 +308,7 @@ async function saveLinks(note, content) {
} }
async function saveNoteRevision(note) { async function saveNoteRevision(note) {
// files and images are immutable, they can't be updated // files and images are versioned separately
// but we don't even version titles which is probably not correct
if (note.type === 'file' || note.type === 'image' || await note.hasLabel('disableVersioning')) { if (note.type === 'file' || note.type === 'image' || await note.hasLabel('disableVersioning')) {
return; return;
} }
@@ -480,7 +479,7 @@ async function eraseDeletedNotes() {
SET content = NULL, SET content = NULL,
utcDateModified = '${utcNowDateTime}' utcDateModified = '${utcNowDateTime}'
WHERE noteRevisionId IN WHERE noteRevisionId IN
(SELECT noteRevisionId FROM note_revisions WHERE isErased = 0 AND noteId IN ((???)))`, noteIdsToErase); (SELECT noteRevisionId FROM note_revisions WHERE isErased = 0 AND noteId IN (???))`, noteIdsToErase);
await sql.executeMany(` await sql.executeMany(`
UPDATE note_revisions UPDATE note_revisions
@@ -514,7 +513,7 @@ async function duplicateNote(noteId, parentNoteId) {
notePosition: origBranch ? origBranch.notePosition + 1 : null notePosition: origBranch ? origBranch.notePosition + 1 : null
}).save(); }).save();
for (const attribute of await origNote.getAttributes()) { for (const attribute of await origNote.getOwnedAttributes()) {
const attr = new Attribute(attribute); const attr = new Attribute(attribute);
attr.attributeId = undefined; // force creation of new attribute attr.attributeId = undefined; // force creation of new attribute
attr.noteId = newNote.noteId; attr.noteId = newNote.noteId;

View File

@@ -37,7 +37,7 @@ function isProtectedSessionAvailable() {
function decryptNotes(notes) { function decryptNotes(notes) {
for (const note of notes) { for (const note of notes) {
if (note.isProtected) { if (note.isProtected) {
note.title = decrypt(note.title); note.title = decryptString(note.title);
} }
} }
} }

View File

@@ -57,8 +57,6 @@ async function initDbConnection() {
return; return;
} }
await sql.execute("PRAGMA foreign_keys = ON");
const currentDbVersion = await getDbVersion(); const currentDbVersion = await getDbVersion();
if (currentDbVersion > appInfo.dbVersion) { if (currentDbVersion > appInfo.dbVersion) {
@@ -175,9 +173,11 @@ async function isDbUpToDate() {
} }
async function dbInitialized() { async function dbInitialized() {
await optionService.setOption('initialized', 'true'); if (!await isDbInitialized()) {
await optionService.setOption('initialized', 'true');
await initDbConnection(); await initDbConnection();
}
} }
dbReady.then(async () => { dbReady.then(async () => {

View File

@@ -1,48 +1,32 @@
const fs = require('fs'); /**
const dataDir = require('../services/data_dir'); * Usage: node src/tools/generate_document.js 1000
* will create 1000 new notes and some clones into a current document.db
fs.unlinkSync(dataDir.DOCUMENT_PATH); */
require('../entities/entity_constructor'); require('../entities/entity_constructor');
const optionService = require('../services/options');
const sqlInit = require('../services/sql_init'); const sqlInit = require('../services/sql_init');
const myScryptService = require('../services/my_scrypt');
const passwordEncryptionService = require('../services/password_encryption');
const utils = require('../services/utils');
const noteService = require('../services/notes'); const noteService = require('../services/notes');
const attributeService = require('../services/attributes');
const cls = require('../services/cls'); const cls = require('../services/cls');
const cloningService = require('../services/cloning'); const cloningService = require('../services/cloning');
const loremIpsum = require('lorem-ipsum'); const loremIpsum = require('lorem-ipsum').loremIpsum;
async function setUserNamePassword() {
const username = "test";
const password = "test";
await optionService.setOption('username', username);
await optionService.setOption('passwordVerificationSalt', utils.randomSecureToken(32));
await optionService.setOption('passwordDerivedKeySalt', utils.randomSecureToken(32));
const passwordVerificationKey = utils.toBase64(await myScryptService.getVerificationHash(password));
await optionService.setOption('passwordVerificationHash', passwordVerificationKey);
await passwordEncryptionService.setDataKey(password, utils.randomSecureToken(16));
await sqlInit.initDbConnection();
}
const noteCount = parseInt(process.argv[2]); const noteCount = parseInt(process.argv[2]);
if (!noteCount) {
console.error(`Please enter number of notes as program parameter.`);
process.exit(1);
}
const notes = ['root']; const notes = ['root'];
function getRandomParentNoteId() { function getRandomNoteId() {
const index = Math.floor(Math.random() * notes.length); const index = Math.floor(Math.random() * notes.length);
return notes[index]; return notes[index];
} }
async function start() { async function start() {
await setUserNamePassword();
for (let i = 0; i < noteCount; i++) { for (let i = 0; i < noteCount; i++) {
const title = loremIpsum({ count: 1, units: 'sentences', sentenceLowerBound: 1, sentenceUpperBound: 10 }); const title = loremIpsum({ count: 1, units: 'sentences', sentenceLowerBound: 1, sentenceUpperBound: 10 });
@@ -50,17 +34,17 @@ async function start() {
const content = loremIpsum({ count: paragraphCount, units: 'paragraphs', sentenceLowerBound: 1, sentenceUpperBound: 15, const content = loremIpsum({ count: paragraphCount, units: 'paragraphs', sentenceLowerBound: 1, sentenceUpperBound: 15,
paragraphLowerBound: 3, paragraphUpperBound: 10, format: 'html' }); paragraphLowerBound: 3, paragraphUpperBound: 10, format: 'html' });
const {note} = await noteService.createNote(getRandomParentNoteId(), title, content); const {note} = await noteService.createNote(getRandomNoteId(), title, content);
console.log(`Created note ${i}: ${title}`); console.log(`Created note ${i}: ${title}`);
notes.push(note.noteId); notes.push(note.noteId);
} }
// we'll create clones for 20% of notes // we'll create clones for 4% of notes
for (let i = 0; i < (noteCount / 50); i++) { for (let i = 0; i < (noteCount / 25); i++) {
const noteIdToClone = getRandomParentNoteId(); const noteIdToClone = getRandomNoteId();
const parentNoteId = getRandomParentNoteId(); const parentNoteId = getRandomNoteId();
const prefix = Math.random() > 0.8 ? "prefix" : null; const prefix = Math.random() > 0.8 ? "prefix" : null;
const result = await cloningService.cloneNoteToParent(noteIdToClone, parentNoteId, prefix); const result = await cloningService.cloneNoteToParent(noteIdToClone, parentNoteId, prefix);
@@ -68,6 +52,30 @@ async function start() {
console.log(`Cloning ${i}:`, result.success ? "succeeded" : "FAILED"); console.log(`Cloning ${i}:`, result.success ? "succeeded" : "FAILED");
} }
for (let i = 0; i < noteCount; i++) {
await attributeService.createAttribute({
noteId: getRandomNoteId(),
type: 'label',
name: 'label',
value: 'value',
isInheritable: Math.random() > 0.1 // 10% are inheritable
});
console.log(`Creating label ${i}`);
}
for (let i = 0; i < noteCount; i++) {
await attributeService.createAttribute({
noteId: getRandomNoteId(),
type: 'relation',
name: 'relation',
value: getRandomNoteId(),
isInheritable: Math.random() > 0.1 // 10% are inheritable
});
console.log(`Creating relation ${i}`);
}
process.exit(0); process.exit(0);
} }