etapi improvements and more tests

This commit is contained in:
zadam 2022-01-12 19:32:23 +01:00
parent 42e85aefdc
commit 28df5d4aa2
36 changed files with 449 additions and 335 deletions

View File

@ -53,7 +53,7 @@ CREATE TABLE IF NOT EXISTS "note_revisions" (`noteRevisionId` TEXT NOT NULL PRIM
`noteId` TEXT NOT NULL,
type TEXT DEFAULT '' NOT NULL,
mime TEXT DEFAULT '' NOT NULL,
`title` TEXT,
`title` TEXT NOT NULL,
`isProtected` INT NOT NULL DEFAULT 0,
`utcDateLastEdited` TEXT NOT NULL,
`utcDateCreated` TEXT NOT NULL,
@ -66,7 +66,7 @@ CREATE TABLE IF NOT EXISTS "note_revision_contents" (`noteRevisionId` TEXT NOT N
CREATE TABLE IF NOT EXISTS "options"
(
name TEXT not null PRIMARY KEY,
value TEXT,
value TEXT not null,
isSynced INTEGER default 0 not null,
utcDateModified TEXT NOT NULL
);

301
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "trilium",
"version": "0.49.3",
"version": "0.49.4",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -546,9 +546,9 @@
}
},
"@npmcli/fs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.0.0.tgz",
"integrity": "sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.0.tgz",
"integrity": "sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA==",
"dev": true,
"requires": {
"@gar/promisify": "^1.0.1",
@ -989,9 +989,9 @@
}
},
"agentkeepalive": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz",
"integrity": "sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==",
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.0.tgz",
"integrity": "sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw==",
"dev": true,
"requires": {
"debug": "^4.1.0",
@ -1466,33 +1466,6 @@
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw=="
},
"body-parser": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz",
"integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==",
"requires": {
"bytes": "3.1.1",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.8.1",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.9.6",
"raw-body": "2.4.2",
"type-is": "~1.6.18"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
}
}
},
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@ -1829,11 +1802,6 @@
}
}
},
"bytes": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz",
"integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg=="
},
"cacache": {
"version": "15.3.0",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz",
@ -2224,18 +2192,18 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"color-support": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
"dev": true
},
"colorette": {
"version": "2.0.16",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz",
"integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==",
"dev": true
},
"colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
"dev": true
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@ -2909,9 +2877,9 @@
}
},
"electron": {
"version": "16.0.6",
"resolved": "https://registry.npmjs.org/electron/-/electron-16.0.6.tgz",
"integrity": "sha512-Xs9dYLYhcJf3wXn8m2gDqFTb1L862KEhMxOx9swfFBHj6NoUPPtUgw/RyPQ0tXN1XPxG9vnBkoI0BdcKwrLKuQ==",
"version": "16.0.7",
"resolved": "https://registry.npmjs.org/electron/-/electron-16.0.7.tgz",
"integrity": "sha512-/IMwpBf2svhA1X/7Q58RV+Nn0fvUJsHniG4TizaO7q4iKFYSQ6hBvsLz+cylcZ8hRMKmVy5G1XaMNJID2ah23w==",
"dev": true,
"requires": {
"@electron/get": "^1.13.0",
@ -2937,9 +2905,9 @@
}
},
"@types/node": {
"version": "14.18.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.1.tgz",
"integrity": "sha512-fTFWOFrgAkj737w1o0HLTIgisgYHnsZfeiqhG1Ltrf/iJjudEbUwetQAsfrtVE49JGwvpEzQR+EbMkIqG4227g==",
"version": "14.18.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.5.tgz",
"integrity": "sha512-LMy+vDDcQR48EZdEx5wRX1q/sEl6NdGuHXPnfeL8ixkwCOSZ2qnIyIZmcCbdX0MeRqHhAcHmX+haCbrS8Run+A==",
"dev": true
},
"boolean": {
@ -3554,18 +3522,18 @@
}
},
"electron-rebuild": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/electron-rebuild/-/electron-rebuild-3.2.5.tgz",
"integrity": "sha512-U9dKi10V9w/BdIVB8a8dTKYLK3Q1d2WZ+Yo5qfM3XX/O4jI7KpnwgvWgGoVv0jTWPC2NlebF00ffWS/8NfUAtA==",
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/electron-rebuild/-/electron-rebuild-3.2.7.tgz",
"integrity": "sha512-WvaW1EgRinDQ61khHFZfx30rkPQG5ItaOT0wrI7iJv9A3SbghriQGfZQfHZs25fWLBe6/vkv05LOqg6aDw6Wzw==",
"dev": true,
"requires": {
"@malept/cross-spawn-promise": "^2.0.0",
"colors": "^1.3.3",
"chalk": "^4.0.0",
"debug": "^4.1.1",
"detect-libc": "^1.0.3",
"fs-extra": "^10.0.0",
"got": "^11.7.0",
"lzma-native": "^8.0.1",
"lzma-native": "^8.0.5",
"node-abi": "^3.0.0",
"node-api-version": "^0.1.4",
"node-gyp": "^8.4.0",
@ -3585,9 +3553,9 @@
}
},
"@sindresorhus/is": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz",
"integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.1.tgz",
"integrity": "sha512-BrzrgtaqEre0qfvI8sMTaEvx+bayuhPmfe2rfeUGPPHYr/PLxCOqkOe4TQTDPb+qcqgNcsAtXV/Ew74mcDIE8w==",
"dev": true
},
"@szmarczak/http-timer": {
@ -3599,6 +3567,15 @@
"defer-to-connect": "^2.0.0"
}
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"cacheable-request": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz",
@ -3614,6 +3591,31 @@
"responselike": "^2.0.0"
}
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
@ -3639,9 +3641,9 @@
}
},
"got": {
"version": "11.8.2",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz",
"integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==",
"version": "11.8.3",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.3.tgz",
"integrity": "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==",
"dev": true,
"requires": {
"@sindresorhus/is": "^4.0.0",
@ -3649,7 +3651,7 @@
"@types/cacheable-request": "^6.0.1",
"@types/responselike": "^1.0.0",
"cacheable-lookup": "^5.0.3",
"cacheable-request": "^7.0.1",
"cacheable-request": "^7.0.2",
"decompress-response": "^6.0.0",
"http2-wrapper": "^1.0.0-beta.5.2",
"lowercase-keys": "^2.0.0",
@ -3664,9 +3666,9 @@
"dev": true
},
"keyv": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz",
"integrity": "sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz",
"integrity": "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA==",
"dev": true,
"requires": {
"json-buffer": "3.0.1"
@ -4092,9 +4094,9 @@
"integrity": "sha512-o1JrraDGpMFaPtkuvtZ4cIBC/xuJn90KBGlxRrm3FxcfER1bPaBnBsTnypF65p+CMTXul2KrZodb3Vv3MScB4A=="
},
"express-rate-limit": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.0.5.tgz",
"integrity": "sha512-EB1mRTrzyyPfEsQZIQFXocd8NKZoDZbEwrtbdgkc20Yed6oYg02Xfjza2HHPI/0orp54BrFeHeT92ICB9ydokw=="
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.1.0.tgz",
"integrity": "sha512-OWyJUDYVq/hRxGU3ufTnXDer5bRBwFiq5D35ZSZ9B2EHdjulWO4bwrbg+iIrapodDZse/35obeOj7igRHuP3Zw=="
},
"express-session": {
"version": "1.17.2",
@ -4770,35 +4772,6 @@
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
"dev": true
},
"http-errors": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
"integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.1"
},
"dependencies": {
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
}
}
},
"http-proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
@ -5183,9 +5156,9 @@
}
},
"jasmine": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.0.1.tgz",
"integrity": "sha512-NAf9b80ie0pAXLW2l+Fxc8s0Q6SjVgi81jOyHJRQuZ+fPjbVAnXNfN2nIwf5yoRjoSTROyRiETjr9Cr+nNBTVw==",
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.0.2.tgz",
"integrity": "sha512-YsrgxJQEggxzByYe4j68eQLOiQeSrPDYGv4sHhGBp3c6HHdq+uPXeAQ73kOAQpdLZ3/0zN7x/TZTloqeE1/qIA==",
"dev": true,
"requires": {
"glob": "^7.1.6",
@ -5678,9 +5651,9 @@
}
},
"lzma-native": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-8.0.1.tgz",
"integrity": "sha512-Ryr9X3yDVZhRYOxR8QhUBCNe6GdEfy9BvFDIFtUvEkocvSvnrYt9lRm6FR1z0eQn0QSMenrgrDIJRMgUf9zsKQ==",
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-8.0.5.tgz",
"integrity": "sha512-lEkBBmePuYBycdlK8ul/sKQuZW47FMxAdjeTgDZLY4duX5Q067JJLUueyzN0wCAw6t2Y6YXCcAqHA5A1jQ9ttQ==",
"dev": true,
"requires": {
"node-addon-api": "^3.1.0",
@ -6050,9 +6023,9 @@
}
},
"node-gyp": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.0.tgz",
"integrity": "sha512-Bi/oCm5bH6F+FmzfUxJpPaxMEyIhszULGR3TprmTeku8/dMFcdTcypk120NeZqEt54r1BrgEKtm2jJiuIKE28Q==",
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz",
"integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==",
"dev": true,
"requires": {
"env-paths": "^2.2.0",
@ -6060,11 +6033,95 @@
"graceful-fs": "^4.2.6",
"make-fetch-happen": "^9.1.0",
"nopt": "^5.0.0",
"npmlog": "^4.1.2",
"npmlog": "^6.0.0",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.2",
"which": "^2.0.2"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true
},
"are-we-there-yet": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
"dev": true,
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^3.6.0"
}
},
"gauge": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.0.tgz",
"integrity": "sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.1",
"aproba": "^1.0.3 || ^2.0.0",
"color-support": "^1.1.2",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.1",
"signal-exit": "^3.0.0",
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"wide-align": "^1.1.2"
}
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
"npmlog": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.0.tgz",
"integrity": "sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q==",
"dev": true,
"requires": {
"are-we-there-yet": "^2.0.0",
"console-control-strings": "^1.1.0",
"gauge": "^4.0.0",
"set-blocking": "^2.0.0"
}
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
},
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.1"
}
}
}
},
"node-gyp-build": {
@ -6739,11 +6796,6 @@
"escape-goat": "^2.0.0"
}
},
"qs": {
"version": "6.9.6",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz",
"integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ=="
},
"quick-lru": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
@ -6774,17 +6826,6 @@
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz",
"integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==",
"requires": {
"bytes": "3.1.1",
"http-errors": "1.8.1",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@ -7452,9 +7493,9 @@
}
},
"socks-proxy-agent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.0.tgz",
"integrity": "sha512-57e7lwCN4Tzt3mXz25VxOErJKXlPfXmkMLnk310v/jwW20jWRVcgsOit+xNkN3eIEdB47GwnfAEBLacZ/wVIKg==",
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz",
"integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==",
"dev": true,
"requires": {
"agent-base": "^6.0.2",
@ -7463,9 +7504,9 @@
},
"dependencies": {
"debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dev": true,
"requires": {
"ms": "2.1.2"

View File

@ -28,7 +28,6 @@
"async-mutex": "0.3.2",
"axios": "0.24.0",
"better-sqlite3": "7.4.5",
"body-parser": "1.19.1",
"chokidar": "3.5.2",
"cls-hooked": "4.2.2",
"commonmark": "0.30.0",
@ -43,7 +42,7 @@
"@electron/remote": "2.0.1",
"express": "4.17.2",
"express-partial-content": "^1.0.2",
"express-rate-limit": "6.0.5",
"express-rate-limit": "6.1.0",
"express-session": "1.17.2",
"fs-extra": "10.0.0",
"helmet": "5.0.1",
@ -83,12 +82,12 @@
},
"devDependencies": {
"cross-env": "7.0.3",
"electron": "16.0.6",
"electron": "16.0.7",
"electron-builder": "22.14.5",
"electron-packager": "15.4.0",
"electron-rebuild": "3.2.6",
"electron-rebuild": "3.2.7",
"esm": "3.2.25",
"jasmine": "4.0.1",
"jasmine": "4.0.2",
"jsdoc": "3.6.7",
"lorem-ipsum": "2.0.4",
"rcedit": "3.0.1",

View File

@ -3,7 +3,6 @@ const express = require('express');
const path = require('path');
const favicon = require('serve-favicon');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const helmet = require('helmet');
const session = require('express-session');
const FileStore = require('session-file-store')(session);
@ -20,13 +19,13 @@ app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(helmet({
hidePoweredBy: false, // deactivated because electron 4.0 crashes on this right after startup
hidePoweredBy: false, // errors out in electron
contentSecurityPolicy: false
}));
app.use(bodyParser.text({limit: '500mb'}));
app.use(bodyParser.json({limit: '500mb'}));
app.use(bodyParser.urlencoded({extended: false}));
app.use(express.text({limit: '500mb'}));
app.use(express.json({limit: '500mb'}));
app.use(express.urlencoded({extended: false}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/libraries', express.static(path.join(__dirname, '..', 'libraries')));

View File

@ -2,7 +2,6 @@
const sql = require("../services/sql");
const NoteSet = require("../services/search/note_set");
const EtapiToken = require("./entities/etapi_token");
/**
* Becca is a backend cache of all notes, branches and attributes. There's a similar frontend cache Froca.

View File

@ -51,7 +51,7 @@ class Attribute extends AbstractEntity {
/** @type {int} */
this.position = position;
/** @type {string} */
this.value = value;
this.value = value || "";
/** @type {boolean} */
this.isInheritable = !!isInheritable;
/** @type {string} */

View File

@ -9,6 +9,9 @@ const sql = require("../../services/sql.js");
* Used by:
* - Trilium Sender
* - ETAPI clients
*
* The format user is presented with is "<etapiTokenId>_<tokenHash>". This is also called "authToken" to distinguish it
* from tokenHash and token.
*/
class EtapiToken extends AbstractEntity {
static get entityName() { return "etapi_tokens"; }

View File

@ -2,7 +2,7 @@ const becca = require("../becca/becca");
const eu = require("./etapi_utils");
const mappers = require("./mappers");
const attributeService = require("../services/attributes");
const validators = require("./validators");
const v = require("./validators");
function register(router) {
eu.route(router, 'get', '/etapi/attributes/:attributeId', (req, res, next) => {
@ -11,18 +11,23 @@ function register(router) {
res.json(mappers.mapAttributeToPojo(attribute));
});
const ALLOWED_PROPERTIES_FOR_CREATE_ATTRIBUTE = {
'attributeId': [v.mandatory, v.notNull, v.isValidEntityId],
'noteId': [v.mandatory, v.notNull, v.isNoteId],
'type': [v.mandatory, v.notNull, v.isAttributeType],
'name': [v.mandatory, v.notNull, v.isString],
'value': [v.notNull, v.isString],
'isInheritable': [v.notNull, v.isBoolean]
};
eu.route(router, 'post' ,'/etapi/attributes', (req, res, next) => {
const params = req.body;
eu.getAndCheckNote(params.noteId);
if (params.type === 'relation') {
eu.getAndCheckNote(params.value);
if (req.body.type === 'relation') {
eu.getAndCheckNote(req.body.value);
}
if (params.type !== 'relation' && params.type !== 'label') {
throw new eu.EtapiError(400, eu.GENERIC_CODE, `Only "relation" and "label" are supported attribute types, "${params.type}" given.`);
}
const params = {};
eu.validateAndPatch(params, req.body, ALLOWED_PROPERTIES_FOR_CREATE_ATTRIBUTE);
try {
const attr = attributeService.createAttribute(params);
@ -30,19 +35,25 @@ function register(router) {
res.json(mappers.mapAttributeToPojo(attr));
}
catch (e) {
throw new eu.EtapiError(400, eu.GENERIC_CODE, e.message);
throw new eu.EtapiError(500, eu.GENERIC_CODE, e.message);
}
});
const ALLOWED_PROPERTIES_FOR_PATCH = {
'value': validators.isString
'value': [v.notNull, v.isString]
};
eu.route(router, 'patch' ,'/etapi/attributes/:attributeId', (req, res, next) => {
const attribute = eu.getAndCheckAttribute(req.params.attributeId);
if (attribute.type === 'relation') {
eu.getAndCheckNote(req.body.value);
}
eu.validateAndPatch(attribute, req.body, ALLOWED_PROPERTIES_FOR_PATCH);
attribute.save();
res.json(mappers.mapAttributeToPojo(attribute));
});

View File

@ -5,7 +5,7 @@ const Branch = require("../becca/entities/branch");
const noteService = require("../services/notes");
const TaskContext = require("../services/task_context");
const entityChangesService = require("../services/entity_changes");
const validators = require("./validators");
const v = require("./validators");
function register(router) {
eu.route(router, 'get', '/etapi/branches/:branchId', (req, res, next) => {
@ -14,11 +14,19 @@ function register(router) {
res.json(mappers.mapBranchToPojo(branch));
});
eu.route(router, 'post' ,'/etapi/branches', (req, res, next) => {
const params = req.body;
const ALLOWED_PROPERTIES_FOR_CREATE_BRANCH = {
'branchId': [v.mandatory, v.notNull, v.isValidEntityId],
'noteId': [v.mandatory, v.notNull, v.isNoteId],
'parentNoteId': [v.mandatory, v.notNull, v.isNoteId],
'notePosition': [v.notNull, v.isInteger],
'prefix': [v.isString],
'isExpanded': [v.notNull, v.isBoolean]
};
eu.getAndCheckNote(params.noteId);
eu.getAndCheckNote(params.parentNoteId);
eu.route(router, 'post' ,'/etapi/branches', (req, res, next) => {
const params = {};
eu.validateAndPatch(params, req.body, ALLOWED_PROPERTIES_FOR_CREATE_BRANCH);
const existing = becca.getBranchFromChildAndParent(params.noteId, params.parentNoteId);
@ -41,15 +49,16 @@ function register(router) {
});
const ALLOWED_PROPERTIES_FOR_PATCH = {
'notePosition': validators.isInteger,
'prefix': validators.isStringOrNull,
'isExpanded': validators.isBoolean
'notePosition': [v.notNull, v.isInteger],
'prefix': [v.isString],
'isExpanded': [v.notNull, v.isBoolean]
};
eu.route(router, 'patch' ,'/etapi/branches/:branchId', (req, res, next) => {
const branch = eu.getAndCheckBranch(req.params.branchId);
eu.validateAndPatch(branch, req.body, ALLOWED_PROPERTIES_FOR_PATCH);
branch.save();
res.json(mappers.mapBranchToPojo(branch));
});

View File

@ -591,13 +591,15 @@ components:
type: object
required:
- parentNoteId
- type
- title
- type
- content
properties:
parentNoteId:
$ref: '#/components/schemas/EntityId'
description: Note ID of the parent note in the tree
title:
type: string
type:
type: string
enum:
@ -613,8 +615,6 @@ components:
type: string
description: this needs to be specified only for note types 'code', 'file', 'image'.
example: application/json
title:
type: string
content:
type: string
notePosition:
@ -628,6 +628,9 @@ components:
Prefix is branch (placement) specific title prefix for the note.
Let's say you have your note placed into two different places in the tree,
but you want to change the title a bit in one of the placements. For this you can use prefix.
isExpanded:
type: boolean
description: true if this note (as a folder) should appear expanded
noteId:
$ref: '#/components/schemas/EntityId'
description: DON'T specify unless you want to force a specific noteId
@ -644,7 +647,7 @@ components:
type: string
type:
type: string
enum: [text, code, book, image, file, mermaid, relation-map, render, search, note-map]
enum: [text, code, render, file, image, search, relation-map, book, note-map, mermaid]
mime:
type: string
isProtected:
@ -686,7 +689,6 @@ components:
properties:
branchId:
$ref: '#/components/schemas/EntityId'
readOnly: true
noteId:
$ref: '#/components/schemas/EntityId'
readOnly: true
@ -700,7 +702,7 @@ components:
notePosition:
type: integer
format: int32
isExanded:
isExpanded:
type: boolean
utcDateModified:
$ref: '#/components/schemas/UtcDateTime'
@ -713,7 +715,6 @@ components:
properties:
attributeId:
$ref: '#/components/schemas/EntityId'
readOnly: true
noteId:
$ref: '#/components/schemas/EntityId'
readOnly: true
@ -753,7 +754,7 @@ components:
description: debugging info on parsing the search query enabled with &debug=true parameter
EntityId:
type: string
pattern: '[a-zA-Z0-9]{4,12}'
pattern: '[a-zA-Z0-9]{4,32}'
example: evnnmvHTCgIn
EntityIdList:
type: array

View File

@ -103,27 +103,26 @@ function getAndCheckAttribute(attributeId) {
}
}
function validateAndPatch(entity, props, allowedProperties) {
for (const key of Object.keys(props)) {
function validateAndPatch(target, source, allowedProperties) {
for (const key of Object.keys(source)) {
if (!(key in allowedProperties)) {
throw new EtapiError(400, "PROPERTY_NOT_ALLOWED_FOR_PATCH", `Property '${key}' is not allowed for PATCH.`);
throw new EtapiError(400, "PROPERTY_NOT_ALLOWED", `Property '${key}' is not allowed for PATCH.`);
}
else {
const validator = allowedProperties[key];
const validationResult = validator(props[key]);
for (const validator of allowedProperties[key]) {
const validationResult = validator(source[key]);
if (validationResult) {
throw new EtapiError(400, "PROPERTY_VALIDATION_ERROR", `Validation failed on property '${key}': ${validationResult}`);
}
}
}
// validation passed, let's patch
for (const propName of Object.keys(props)) {
entity[propName] = props[propName];
}
entity.save();
// validation passed, let's patch
for (const propName of Object.keys(source)) {
target[propName] = source[propName];
}
}
module.exports = {

View File

@ -4,7 +4,7 @@ const eu = require("./etapi_utils");
const mappers = require("./mappers");
const noteService = require("../services/notes");
const TaskContext = require("../services/task_context");
const validators = require("./validators");
const v = require("./validators");
const searchService = require("../services/search/services/search");
const SearchContext = require("../services/search/search_context");
@ -39,10 +39,23 @@ function register(router) {
res.json(mappers.mapNoteToPojo(note));
});
eu.route(router, 'post' ,'/etapi/create-note', (req, res, next) => {
const params = req.body;
const ALLOWED_PROPERTIES_FOR_CREATE_NOTE = {
'parentNoteId': [v.mandatory, v.notNull, v.isNoteId],
'title': [v.mandatory, v.notNull, v.isString],
'type': [v.mandatory, v.notNull, v.isNoteType],
'mime': [v.notNull, v.isString],
'content': [v.notNull, v.isString],
'notePosition': [v.notNull, v.isInteger],
'prefix': [v.notNull, v.isInteger],
'isExpanded': [v.notNull, v.isBoolean],
'noteId': [v.notNull, v.isValidEntityId],
'branchId': [v.notNull, v.isValidEntityId],
};
eu.getAndCheckNote(params.parentNoteId);
eu.route(router, 'post' ,'/etapi/create-note', (req, res, next) => {
const params = {};
eu.validateAndPatch(params, req.body, ALLOWED_PROPERTIES_FOR_CREATE_NOTE);
try {
const resp = noteService.createNewNote(params);
@ -53,14 +66,14 @@ function register(router) {
});
}
catch (e) {
return eu.sendError(res, 400, eu.GENERIC_CODE, e.message);
return eu.sendError(res, 500, eu.GENERIC_CODE, e.message);
}
});
const ALLOWED_PROPERTIES_FOR_PATCH = {
'title': validators.isString,
'type': validators.isString,
'mime': validators.isString
'title': [v.notNull, v.isString],
'type': [v.notNull, v.isString],
'mime': [v.notNull, v.isString]
};
eu.route(router, 'patch' ,'/etapi/notes/:noteId', (req, res, next) => {
@ -71,6 +84,7 @@ function register(router) {
}
eu.validateAndPatch(note, req.body, ALLOWED_PROPERTIES_FOR_PATCH);
note.save();
res.json(mappers.mapNoteToPojo(note));
});

View File

@ -1,30 +1,101 @@
const noteTypes = require("../services/note_types");
function mandatory(obj) {
if (obj === undefined ) {
return `mandatory, but not set`;
}
}
function notNull(obj) {
if (obj === null) {
return `cannot be null`;
}
}
function isString(obj) {
if (obj === undefined || obj === null) {
return;
}
if (typeof obj !== 'string') {
return `'${obj}' is not a string`;
}
}
function isStringOrNull(obj) {
if (obj) {
return isString(obj);
}
function isBoolean(obj) {
if (obj === undefined || obj === null) {
return;
}
function isBoolean(obj) {
if (typeof obj !== 'boolean') {
return `'${obj}' is not a boolean`;
}
}
function isInteger(obj) {
if (obj === undefined || obj === null) {
return;
}
if (!Number.isInteger(obj)) {
return `'${obj}' is not an integer`;
}
}
function isNoteId(obj) {
if (obj === undefined || obj === null) {
return;
}
const becca = require('../becca/becca');
if (typeof obj !== 'string') {
return `'${obj}' is not a valid noteId`;
}
if (!(obj in becca.notes)) {
return `Note '${obj}' does not exist`;
}
}
function isNoteType(obj) {
if (obj === undefined || obj === null) {
return;
}
if (!noteTypes.includes(obj)) {
return `'${obj}' is not a valid note type, allowed types are: ` + noteTypes.join(", ");
}
}
function isAttributeType(obj) {
if (obj === undefined || obj === null) {
return;
}
if (!['label', 'relation'].includes(obj)) {
return `'${obj}' is not a valid attribute type, allowed types are: label, relation`;
}
}
function isValidEntityId(obj) {
if (obj === undefined || obj === null) {
return;
}
if (typeof obj !== 'string' || !/^[A-Za-z0-9]{4,32}$/.test(obj)) {
return `'${obj}' is not a valid entityId. Only alphanumeric characters are allowed of length 4 to 32.`;
}
}
module.exports = {
mandatory,
notNull,
isString,
isStringOrNull,
isBoolean,
isInteger
isInteger,
isNoteId,
isNoteType,
isAttributeType,
isValidEntityId
};

View File

@ -91,7 +91,7 @@ export default class LoadResults {
}
const componentIds = this.noteIdToComponentId[noteId];
return componentIds && !!componentIds.find(sId => sId !== componentId);
return componentIds && componentIds.find(sId => sId !== componentId) !== undefined;
}
addNoteContent(noteId, componentId) {

0
src/public/stylesheets/calendar.css Executable file → Normal file
View File

View File

@ -190,7 +190,7 @@ const uploadMiddleware = multer.single('upload');
function register(app) {
route(GET, '/', [auth.checkAuth, csrfMiddleware], indexRoute.index);
route(GET, '/login', [auth.checkAppInitialized, auth.checkPasswordSet], loginRoute.loginPage);
route(GET, '/set-password', [auth.checkAppInitialized], loginRoute.setPasswordPage);
route(GET, '/set-password', [auth.checkAppInitialized, auth.checkPasswordNotSet], loginRoute.setPasswordPage);
const loginRateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
@ -199,7 +199,7 @@ function register(app) {
route(POST, '/login', [loginRateLimiter], loginRoute.login);
route(POST, '/logout', [csrfMiddleware, auth.checkAuth], loginRoute.logout);
route(POST, '/set-password', [auth.checkAppInitialized], loginRoute.setPassword);
route(POST, '/set-password', [auth.checkAppInitialized, auth.checkPasswordNotSet], loginRoute.setPassword);
route(GET, '/setup', [], setupRoute.setupPage);
apiRoute(GET, '/api/tree', treeApiRoute.getTree);

View File

@ -14,6 +14,8 @@ function setupPage(req, res) {
else {
res.redirect('/');
}
return;
}
// we got here because DB is not completely initialized so if schema exists

View File

@ -15,11 +15,7 @@ function checkAuth(req, res, next) {
res.redirect("setup");
}
else if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) {
if (passwordService.isPasswordSet()) {
res.redirect("login");
} else {
res.redirect("set-password");
}
}
else {
next();
@ -63,6 +59,14 @@ function checkPasswordSet(req, res, next) {
}
}
function checkPasswordNotSet(req, res, next) {
if (!utils.isElectron() && passwordService.isPasswordSet()) {
res.redirect("login");
} else {
next();
}
}
function checkAppNotInitialized(req, res, next) {
if (sqlInit.isDbInitialized()) {
reject(req, res, "App already initialized.");
@ -111,6 +115,7 @@ module.exports = {
checkApiAuth,
checkAppInitialized,
checkPasswordSet,
checkPasswordNotSet,
checkAppNotInitialized,
checkApiAuthOrElectron,
checkEtapiToken,

View File

@ -13,6 +13,7 @@ const attributeService = require('./attributes');
const noteRevisionService = require('./note_revisions');
const becca = require("../becca/becca");
const utils = require("../services/utils");
const noteTypes = require("../services/note_types");
class ConsistencyChecks {
constructor(autoFix) {
@ -281,11 +282,13 @@ class ConsistencyChecks {
}
findLogicIssues() {
const noteTypesStr = noteTypes.map(nt => `'${nt}'`).join(", ");
this.findAndFixIssues(`
SELECT noteId, type
FROM notes
WHERE isDeleted = 0
AND type NOT IN ('text', 'code', 'render', 'file', 'image', 'search', 'relation-map', 'book', 'note-map', 'mermaid')`,
AND type NOT IN (${noteTypesStr})`,
({noteId, type}) => {
if (this.autoFix) {
const note = becca.getNote(noteId);

View File

@ -23,7 +23,7 @@ function addEntityChange(origEntityChange) {
ec.changeId = utils.randomString(12);
}
ec.componentId = ec.componentId || cls.getComponentId() || "";
ec.componentId = ec.componentId || cls.getComponentId() || "NA"; // NA = not available
ec.instanceId = ec.instanceId || instanceId;
ec.isSynced = ec.isSynced ? 1 : 0;
ec.isErased = ec.isErased ? 1 : 0;
@ -43,7 +43,7 @@ function addNoteReorderingEntityChange(parentNoteId, componentId) {
utcDateChanged: dateUtils.utcNowDateTime(),
isSynced: true,
componentId,
instanceId: instanceId
instanceId
});
const eventService = require('./events');

View File

@ -12,7 +12,7 @@ function getTokenHash(token) {
}
function createToken(tokenName) {
const token = utils.randomSecureToken();
const token = utils.randomSecureToken(32);
const tokenHash = getTokenHash(token);
const etapiToken = new EtapiToken({

View File

@ -18,8 +18,6 @@ const Branch = require('../becca/entities/branch');
const Note = require('../becca/entities/note');
const Attribute = require('../becca/entities/attribute');
// TODO: patch/put note content
function getNewNotePosition(parentNoteId) {
const note = becca.notes[parentNoteId];

View File

@ -1,22 +1,27 @@
const becca = require('../becca/becca');
const sql = require("./sql");
function getOption(name) {
function getOptionOrNull(name) {
let option;
if (becca.loaded) {
option = becca.getOption(name);
}
else {
} else {
// e.g. in initial sync becca is not loaded because DB is not initialized
option = sql.getRow("SELECT * FROM options WHERE name = ?", name);
}
if (!option) {
return option ? option.value : null;
}
function getOption(name) {
const val = getOptionOrNull(name);
if (val === null) {
throw new Error(`Option "${name}" doesn't exist`);
}
return option.value;
return val;
}
/**
@ -96,5 +101,6 @@ module.exports = {
setOption,
createOption,
getOptions,
getOptionsMap
getOptionsMap,
getOptionOrNull
};

View File

@ -6,7 +6,11 @@ const dataEncryptionService = require('./data_encryption');
function verifyPassword(password) {
const givenPasswordHash = utils.toBase64(myScryptService.getVerificationHash(password));
const dbPasswordHash = optionService.getOption('passwordVerificationHash');
const dbPasswordHash = optionService.getOptionOrNull('passwordVerificationHash');
if (!dbPasswordHash) {
return false;
}
return givenPasswordHash === dbPasswordHash;
}

View File

@ -154,10 +154,6 @@ function sortNotes(parentNoteId, customSortBy = 'title', reverse = false, folder
const topAEl = fetchValue(a, 'top');
const topBEl = fetchValue(b, 'top');
console.log(a.title, topAEl);
console.log(b.title, topBEl);
console.log("comp", compare(topAEl, topBEl) && !reverse);
if (topAEl !== topBEl) {
// since "top" should not be reversible, we'll reverse it once more to nullify this effect
return compare(topAEl, topBEl) * (reverse ? -1 : 1);

0
src/www Executable file → Normal file
View File

View File

@ -6,7 +6,7 @@ Content-Type: application/json
}
> {%
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.status === 200);
client.global.set("authToken", response.body.authToken);
%}

View File

@ -3,6 +3,8 @@ Authorization: {{authToken}}
Content-Type: application/json
{
"noteId": "forcedId{{$randomInt}}",
"branchId": "forcedId{{$randomInt}}",
"parentNoteId": "root",
"title": "Hello",
"type": "text",
@ -10,11 +12,11 @@ Content-Type: application/json
}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.status === 200);
client.assert(response.body.note.noteId.startsWith("forcedId"));
client.assert(response.body.note.title == "Hello");
client.assert(response.body.branch.branchId.startsWith("forcedId"));
client.assert(response.body.branch.parentNoteId == "root");
});
client.log(`Created note ` + response.body.note.noteId + ` and branch ` + response.body.branch.branchId);
@ -29,15 +31,14 @@ Authorization: {{authToken}}
Content-Type: application/json
{
"branchId": "forcedClonedId",
"noteId": "{{createdNoteId}}",
"parentNoteId": "hidden"
}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.status === 200);
client.assert(response.body.parentNoteId == "hidden");
});
client.global.set("clonedBranchId", response.body.branchId);
@ -50,14 +51,12 @@ GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
Authorization: {{authToken}}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.status === 200);
client.assert(response.body.noteId == client.global.get("createdNoteId"));
client.assert(response.body.title == "Hello");
// order is not defined and may fail in the future
client.assert(response.body.parentBranchIds[0] == client.global.get("clonedBranchId"))
client.assert(response.body.parentBranchIds[1] == client.global.get("createdBranchId"));
});
%}
###
@ -66,10 +65,8 @@ GET {{triliumHost}}/etapi/notes/{{createdNoteId}}/content
Authorization: {{authToken}}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.status === 200);
client.assert(response.body == "Hi there!");
});
%}
###
@ -78,11 +75,9 @@ GET {{triliumHost}}/etapi/branches/{{createdBranchId}}
Authorization: {{authToken}}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.status === 200);
client.assert(response.body.branchId == client.global.get("createdBranchId"));
client.assert(response.body.parentNoteId == "root");
});
%}
###
@ -91,11 +86,9 @@ GET {{triliumHost}}/etapi/branches/{{clonedBranchId}}
Authorization: {{authToken}}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.status === 200);
client.assert(response.body.branchId == client.global.get("clonedBranchId"));
client.assert(response.body.parentNoteId == "hidden");
});
%}
###
@ -105,19 +98,17 @@ Content-Type: application/json
Authorization: {{authToken}}
{
"attributeId": "forcedAttributeId{{$randomInt}}",
"noteId": "{{createdNoteId}}",
"type": "label",
"name": "mylabel",
"value": "val",
"isInheritable": "true"
"isInheritable": true
}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
client.log(`Created attribute ` + response.body.attributeId);
client.assert(response.status === 200);
client.assert(response.body.attributeId.startsWith("forcedAttributeId"));
client.global.set("createdAttributeId", response.body.attributeId);
%}
@ -128,8 +119,6 @@ GET {{triliumHost}}/etapi/attributes/{{createdAttributeId}}
Authorization: {{authToken}}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
client.assert(response.status === 200);
client.assert(response.body.attributeId == client.global.get("createdAttributeId"));
});
%}

View File

@ -25,7 +25,7 @@ Content-Type: application/json
"type": "label",
"name": "mylabel",
"value": "val",
"isInheritable": "true"
"isInheritable": true
}
> {% client.global.set("createdAttributeId", response.body.attributeId); %}
@ -35,14 +35,14 @@ Content-Type: application/json
GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %}
> {% client.assert(response.status === 200); %}
###
GET {{triliumHost}}/etapi/branches/{{createdBranchId}}
Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %}
> {% client.assert(response.status === 200); %}
###

View File

@ -32,21 +32,21 @@ Content-Type: application/json
GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %}
> {% client.assert(response.status === 200); %}
###
GET {{triliumHost}}/etapi/branches/{{createdBranchId}}
Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %}
> {% client.assert(response.status === 200); %}
###
GET {{triliumHost}}/etapi/branches/{{clonedBranchId}}
Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %}
> {% client.assert(response.status === 200); %}
###
@ -77,11 +77,11 @@ Authorization: {{authToken}}
GET {{triliumHost}}/etapi/branches/{{clonedBranchId}}
Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %}
> {% client.assert(response.status === 200); %}
###
GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %}
> {% client.assert(response.status === 200); %}

View File

@ -25,7 +25,7 @@ Content-Type: application/json
"type": "label",
"name": "mylabel",
"value": "val",
"isInheritable": "true"
"isInheritable": true
}
> {% client.global.set("createdAttributeId", response.body.attributeId); %}
@ -48,28 +48,28 @@ Content-Type: application/json
GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %}
> {% client.assert(response.status === 200); %}
###
GET {{triliumHost}}/etapi/branches/{{createdBranchId}}
Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %}
> {% client.assert(response.status === 200); %}
###
GET {{triliumHost}}/etapi/branches/{{clonedBranchId}}
Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %}
> {% client.assert(response.status === 200); %}
###
GET {{triliumHost}}/etapi/attributes/{{createdAttributeId}}
Authorization: {{authToken}}
> {% client.assert(response.status === 200, "Response status is not 200"); %}
> {% client.assert(response.status === 200); %}
###

View File

@ -1,22 +1,14 @@
GET {{triliumHost}}/etapi/inbox/2022-01-01
Authorization: {{authToken}}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}
> {% client.assert(response.status === 200); %}
###
GET {{triliumHost}}/etapi/calendar/days/2022-01-01
Authorization: {{authToken}}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}
> {% client.assert(response.status === 200); %}
###
@ -24,10 +16,8 @@ GET {{triliumHost}}/etapi/calendar/days/2022-1
Authorization: {{authToken}}
> {%
client.test("Correct error handling", function() {
client.assert(response.status === 400, "Response status is not 400");
client.assert(response.status === 400);
client.assert(response.body.code === "DATE_INVALID");
});
%}
###
@ -35,11 +25,7 @@ Authorization: {{authToken}}
GET {{triliumHost}}/etapi/calendar/weeks/2022-01-01
Authorization: {{authToken}}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}
> {% client.assert(response.status === 200); %}
###
@ -47,10 +33,8 @@ GET {{triliumHost}}/etapi/calendar/weeks/2022-1
Authorization: {{authToken}}
> {%
client.test("Correct error handling", function() {
client.assert(response.status === 400, "Response status is not 400");
client.assert(response.status === 400);
client.assert(response.body.code === "DATE_INVALID");
});
%}
###
@ -58,11 +42,7 @@ Authorization: {{authToken}}
GET {{triliumHost}}/etapi/calendar/months/2022-01
Authorization: {{authToken}}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}
> {% client.assert(response.status === 200); %}
###
@ -70,10 +50,8 @@ GET {{triliumHost}}/etapi/calendar/months/2022-1
Authorization: {{authToken}}
> {%
client.test("Correct error handling", function() {
client.assert(response.status === 400, "Response status is not 400");
client.assert(response.status === 400);
client.assert(response.body.code === "MONTH_INVALID");
});
%}
###
@ -81,11 +59,7 @@ Authorization: {{authToken}}
GET {{triliumHost}}/etapi/calendar/years/2022
Authorization: {{authToken}}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}
> {% client.assert(response.status === 200); %}
###
@ -93,11 +67,6 @@ GET {{triliumHost}}/etapi/calendar/years/202
Authorization: {{authToken}}
> {%
client.test("Correct error handling", function() {
client.assert(response.status === 400, "Response status is not 400");
client.assert(response.status === 400);
client.assert(response.body.code === "YEAR_INVALID");
});
%}
###

View File

@ -1,8 +1,4 @@
POST {{triliumHost}}/etapi/refresh-note-ordering/root
Authorization: {{authToken}}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}
> {% client.assert(response.status === 200); %}

View File

@ -25,7 +25,7 @@ Content-Type: application/json
"type": "label",
"name": "mylabel",
"value": "val",
"isInheritable": "true"
"isInheritable": true
}
> {% client.global.set("createdAttributeId", response.body.attributeId); %}
@ -61,7 +61,7 @@ Content-Type: application/json
> {%
client.assert(response.status === 400);
client.assert(response.body.code == "PROPERTY_NOT_ALLOWED_FOR_PATCH");
client.assert(response.body.code == "PROPERTY_NOT_ALLOWED");
%}
###

View File

@ -47,7 +47,7 @@ Content-Type: application/json
> {%
client.assert(response.status === 400);
client.assert(response.body.code == "PROPERTY_NOT_ALLOWED_FOR_PATCH");
client.assert(response.body.code == "PROPERTY_NOT_ALLOWED");
%}
###

View File

@ -60,7 +60,7 @@ Content-Type: application/json
> {%
client.assert(response.status === 400);
client.assert(response.body.code == "PROPERTY_NOT_ALLOWED_FOR_PATCH");
client.assert(response.body.code == "PROPERTY_NOT_ALLOWED");
%}
###