Compare commits
	
		
			506 Commits
		
	
	
		
			v0.5.2-bet
			...
			v0.20.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 2d1bc46c04 | ||
|  | 4bc44605fb | ||
|  | b868990fba | ||
|  | 26c06c9826 | ||
|  | f5b89432a6 | ||
|  | 0e7372adbf | ||
|  | d4fbe28517 | ||
|  | 668528d5eb | ||
|  | 17348a9cfe | ||
|  | 09b610701d | ||
|  | 71e687ad8e | ||
|  | 171877ce08 | ||
|  | 4f1e6ec70f | ||
|  | 1938c317c3 | ||
|  | 99d81059d0 | ||
|  | 59d5a86110 | ||
|  | a5e56ea839 | ||
|  | 44f85224e7 | ||
|  | 0aa08b1c1e | ||
|  | 406d74c4d7 | ||
|  | 7f9a8a55ca | ||
|  | a42bbba0e5 | ||
|  | 145efe67c3 | ||
|  | 513748836e | ||
|  | 427ce3972e | ||
|  | 02c0f9a6cd | ||
|  | 208771216e | ||
|  | 385d97a9b3 | ||
|  | e39d1d08ac | ||
|  | 0f106fb96f | ||
|  | df9acd0504 | ||
|  | dbe0eb3f3a | ||
|  | 4513651e12 | ||
|  | 3204291463 | ||
|  | 510704a074 | ||
|  | f440493e45 | ||
|  | b897c6de13 | ||
|  | acbd18e8fc | ||
|  | ff5b84db10 | ||
|  | 16535f6a73 | ||
|  | 5b657ad961 | ||
|  | bbbc3e9dc4 | ||
|  | f43f0e10a1 | ||
|  | 6d842a65a2 | ||
|  | 50c4de021c | ||
|  | 936d8449f6 | ||
|  | 462bc0edd5 | ||
|  | 35ef3c8470 | ||
|  | 5117d43e29 | ||
|  | 7c9ac488e8 | ||
|  | fec1574447 | ||
|  | f7587de452 | ||
|  | 41a6e777ea | ||
|  | 8fb0de900b | ||
|  | a40bf71fd4 | ||
|  | 2a53bb03ae | ||
|  | a684879b91 | ||
|  | ddbd4f73c8 | ||
|  | b0ed790edf | ||
|  | 3424406ff1 | ||
|  | ce5c385c15 | ||
|  | cd9eef32b0 | ||
|  | 12d82e3b33 | ||
|  | f071d3f651 | ||
|  | 297b536ebc | ||
|  | 7cca2d9247 | ||
|  | 36dc802d16 | ||
|  | c78ddb70cb | ||
|  | 9fb0599c45 | ||
|  | 13f524fb39 | ||
|  | 27be3b4c90 | ||
|  | af4ea66742 | ||
|  | 0f42c396f3 | ||
|  | 9e96272eb3 | ||
|  | 965dbcbc9a | ||
|  | 7ac109e7f7 | ||
|  | ac25770c0e | ||
|  | 5b15424498 | ||
|  | f1240c26bf | ||
|  | 1c0fd243d1 | ||
|  | 3491235533 | ||
|  | 5f36856571 | ||
|  | d3e44b37e9 | ||
|  | 90e9297ec5 | ||
|  | c568ef2f8a | ||
|  | fcf6141cde | ||
|  | 21551d7b77 | ||
|  | 12031d369f | ||
|  | b44c523845 | ||
|  | 49989695ff | ||
|  | a55d3530e9 | ||
|  | 2aab3ad281 | ||
|  | 194ce4f10f | ||
|  | 2089c32839 | ||
|  | f437be7af0 | ||
|  | 96dc56098d | ||
|  | 61987e46f7 | ||
|  | 509093b755 | ||
|  | 097114c0f2 | ||
|  | 040f9185f8 | ||
|  | 6dc934abbe | ||
|  | 2d24bf81dd | ||
|  | 9452fc236b | ||
|  | 365c37604b | ||
|  | 01c7e58d47 | ||
|  | d3d49923b1 | ||
|  | 263ac299d0 | ||
|  | 3d185a5178 | ||
|  | 2ff7a890bc | ||
|  | 2eb1a9705f | ||
|  | ed1381103a | ||
|  | 170d317589 | ||
|  | ededc063df | ||
|  | 986eace1be | ||
|  | 29086d8dfe | ||
|  | 9b3f3fde05 | ||
|  | 6a50afd952 | ||
|  | 697eee2706 | ||
|  | 8a95afd756 | ||
|  | 4d6eda8fe6 | ||
|  | e4f459fa2b | ||
|  | f578e001b0 | ||
|  | 2a08aef885 | ||
|  | 7564bf388c | ||
|  | 7e4d70259f | ||
|  | 5b98c1c0f3 | ||
|  | 02dc7b199b | ||
|  | d39cdbfada | ||
|  | 50bb4a47ee | ||
|  | a4627f2ddb | ||
|  | c8253caae9 | ||
|  | 0ece9bd1be | ||
|  | b6935abcc9 | ||
|  | 37ab7b4641 | ||
|  | 013714cb5c | ||
|  | 1fe7c62f5a | ||
|  | a06618d851 | ||
|  | e7460ca3a9 | ||
|  | 073300bbcd | ||
|  | a201661ce5 | ||
|  | 6235a3c886 | ||
|  | 3972c27e7a | ||
|  | 14cffbbe62 | ||
|  | 599c3c04af | ||
|  | f1412b631d | ||
|  | 41908050bb | ||
|  | f07033423c | ||
|  | daf96fcbf2 | ||
|  | 2bca94529e | ||
|  | b2c9a0da21 | ||
|  | 7a9542b4fc | ||
|  | 3a95c9e1bc | ||
|  | 3d2ef6be01 | ||
|  | d67246699a | ||
|  | 14c704d6db | ||
|  | 4c8eeb2e6f | ||
|  | c1b245c8b1 | ||
|  | 74202d67bb | ||
|  | 26066f39b1 | ||
|  | b255cf190c | ||
|  | bc77b143b0 | ||
|  | 9f0ff6ae7a | ||
|  | 736704c7d6 | ||
|  | 654c116c58 | ||
|  | 89a5cab98f | ||
|  | c39d0be8cd | ||
|  | e75b4cd848 | ||
|  | 378e8f35e5 | ||
|  | bdb5e2f13f | ||
|  | 8211bed449 | ||
|  | b243632483 | ||
|  | e4d2513451 | ||
|  | 385144451b | ||
|  | c8c533844e | ||
|  | 0e69f0c079 | ||
|  | aee60c444f | ||
|  | e7a504c66b | ||
|  | 45d9c7164c | ||
|  | bd913a63a8 | ||
|  | 5a1938c078 | ||
|  | 015cd68756 | ||
|  | 76c0e5b2b8 | ||
|  | 0f8f707acd | ||
|  | 083cccea28 | ||
|  | 31b76b23ce | ||
|  | af529f82e5 | ||
|  | fc6669d254 | ||
|  | c07785be67 | ||
|  | 80d2457b23 | ||
|  | 5dde2752d2 | ||
|  | 8bf4633cd0 | ||
|  | bd66b8a1c8 | ||
|  | be51e533fc | ||
|  | f47ae12019 | ||
|  | cab54a458f | ||
|  | a30734f1bc | ||
|  | 7ad9f7b129 | ||
|  | 40a32e6826 | ||
|  | ab0486aaf1 | ||
|  | 874593a167 | ||
|  | 03bf33630e | ||
|  | 933cce1b94 | ||
|  | 4a6ff573f8 | ||
|  | 1a737f7d19 | ||
|  | cb69914f09 | ||
|  | a372cbb2df | ||
|  | 0ce5caefe8 | ||
|  | 94dabb81f6 | ||
|  | cd45bcfd03 | ||
|  | 49a53f7a45 | ||
|  | 9fa6c0918c | ||
|  | e8d089e37e | ||
|  | a931ce25fa | ||
|  | b507abb4f7 | ||
|  | 66e7c6de62 | ||
|  | 4ce5ea9886 | ||
|  | 8c54b62f07 | ||
|  | 85eb50ed0f | ||
|  | 5ffd621e9d | ||
|  | df93cb09da | ||
|  | bbf04209f0 | ||
|  | 834bfa39c7 | ||
|  | 52b445f70b | ||
|  | 7b9b4fbb0c | ||
|  | 5af0ba1fcb | ||
|  | 85a9748291 | ||
|  | b4005a7ffe | ||
|  | 82de1c88d4 | ||
|  | 1687ed7e0b | ||
|  | c8b9c7d936 | ||
|  | d57057ba28 | ||
|  | 66cee8daa4 | ||
|  | afd7df0942 | ||
|  | bd6ae33d32 | ||
|  | 70660a0d68 | ||
|  | cdad18551a | ||
|  | 592c51d1a5 | ||
|  | 6a57b8a7e7 | ||
|  | 7a94e21c54 | ||
|  | 5b43f321e2 | ||
|  | a4eafb934f | ||
|  | 7b59a665dd | ||
|  | 3d15450ffc | ||
|  | b0c6d52461 | ||
|  | 2dc16dd29f | ||
|  | d8924c536b | ||
|  | 3ebbf2cc46 | ||
|  | f4079604c9 | ||
|  | 1f96a6beab | ||
|  | b277a250e5 | ||
|  | 5b0e1a644d | ||
|  | 6bb3cfa9a3 | ||
|  | 9720868f5a | ||
|  | 8d8ee2a87a | ||
|  | 542e82ee5d | ||
|  | 0104b19502 | ||
|  | 120888b53e | ||
|  | d2e2caed62 | ||
|  | 63066802a8 | ||
|  | 6128bb4ff3 | ||
|  | 982796255d | ||
|  | 36b15f474d | ||
|  | 13f71f8967 | ||
|  | 64336ffbee | ||
|  | b09463d1b2 | ||
|  | b5e6f46b9c | ||
|  | 08af4a0465 | ||
|  | 8c5df6321f | ||
|  | d19f044961 | ||
|  | e378d9f645 | ||
|  | 39dc0f71b4 | ||
|  | 0cef5c6b8c | ||
|  | 9b5a44cef4 | ||
|  | 29769ed91d | ||
|  | 867d794e17 | ||
|  | fdd8458336 | ||
|  | a0bec22e96 | ||
|  | 5aeb5cd214 | ||
|  | e827ddffb9 | ||
|  | 98f80998b9 | ||
|  | 69727d0b12 | ||
|  | 84faf32b98 | ||
|  | 6ed6e27602 | ||
|  | fb54678fef | ||
|  | 2cdcb3af12 | ||
|  | cf7a336ac2 | ||
|  | abfc64af95 | ||
|  | 42dd8d4754 | ||
|  | a4e64350e9 | ||
|  | 6f567e3e10 | ||
|  | c6c76ba360 | ||
|  | 429d3f518e | ||
|  | 26e4ad9bf9 | ||
|  | 6ab0cea4e3 | ||
|  | 277368ab43 | ||
|  | e2921a648d | ||
|  | c765dbc5cf | ||
|  | a066c6fe2b | ||
|  | 311952d4dd | ||
|  | 96dab5d51e | ||
|  | 15d951b04e | ||
|  | 8ba830c04b | ||
|  | acc82f39c4 | ||
|  | fad0ec757b | ||
|  | c9d73c6115 | ||
|  | ab2f28ceef | ||
|  | 87e415992c | ||
|  | 12439d8761 | ||
|  | 4f200c73dc | ||
|  | 5f7e74e15c | ||
|  | e8a5d0ae16 | ||
|  | 088fb00ca9 | ||
|  | 05676f3459 | ||
|  | 5d203b2278 | ||
|  | 795d50f02e | ||
|  | cfe0ae1eda | ||
|  | aa57a64c61 | ||
|  | e36a81e189 | ||
|  | 88c07a9e48 | ||
|  | bfd9f292a6 | ||
|  | 9edee9340b | ||
|  | 8550ed72f2 | ||
|  | efffc29649 | ||
|  | 0ec909fd7a | ||
|  | b10b0048f3 | ||
|  | 9bb188b519 | ||
|  | 7464835058 | ||
|  | 913b6bb6f6 | ||
|  | 000cf99546 | ||
|  | c918267750 | ||
|  | 68921ee59b | ||
|  | 7e856283ee | ||
|  | 9c1b8da573 | ||
|  | cb39b9cca8 | ||
|  | 788ac43ad1 | ||
|  | 57d19f3302 | ||
|  | 68bba623b6 | ||
|  | 35998058ce | ||
|  | cdf94181d2 | ||
|  | 91ee90d827 | ||
|  | d3316cd09c | ||
|  | ac1b06967f | ||
|  | 47eb1e3e02 | ||
|  | a69d8737ce | ||
|  | 341f47f0f2 | ||
|  | 19c605a9a8 | ||
|  | 54e4f54678 | ||
|  | 297a2cd9da | ||
|  | d746d707b5 | ||
|  | 299252b650 | ||
|  | fddd1c278f | ||
|  | f52d7e3c28 | ||
|  | a699210a29 | ||
|  | b3c32a39e9 | ||
|  | df27533b66 | ||
|  | b96a1274c5 | ||
|  | 001a5107dd | ||
|  | c8e456cdb1 | ||
|  | 0f6b00e1c8 | ||
|  | 5ea060a054 | ||
|  | 95bb2cf0bb | ||
|  | 4c472ce78b | ||
|  | 511fb89af0 | ||
|  | 7e524c0cd1 | ||
|  | e3e2dc9fff | ||
|  | 1612e9093d | ||
|  | f8649feea4 | ||
|  | ac978c3fa7 | ||
|  | efcc804149 | ||
|  | db514e8f41 | ||
|  | 9c32f66329 | ||
|  | 0fd5102a26 | ||
|  | 840af15dae | ||
|  | f1b0b3bcdb | ||
|  | e5c0acbb43 | ||
|  | 834661c461 | ||
|  | 5204ab5a7e | ||
|  | 74862536a8 | ||
|  | a24f1f5b95 | ||
|  | 0be76f746a | ||
|  | fad89ff63f | ||
|  | b8ae791191 | ||
|  | ce754cbd91 | ||
|  | 0fdb6af98a | ||
|  | f6c7f6a0f2 | ||
|  | 354999f37a | ||
|  | 348c622845 | ||
|  | 44bcdedaba | ||
|  | 755c0f3ce2 | ||
|  | 895bda41b5 | ||
|  | b2df622cb6 | ||
|  | 9ba6e6d0f5 | ||
|  | a5c9180533 | ||
|  | e86f1e0d05 | ||
|  | b6277049f3 | ||
|  | c831221cc4 | ||
|  | 577a168714 | ||
|  | b0bd27321a | ||
|  | 90c5348ca7 | ||
|  | 8e95b080da | ||
|  | 766a567a32 | ||
|  | 6d0218cb36 | ||
|  | d26170762b | ||
|  | b3209a9bbf | ||
|  | 61c2456cf6 | ||
|  | 1c6fc9029f | ||
|  | 5c91e38dfe | ||
|  | 07bf075894 | ||
|  | ddce5c959e | ||
|  | 3b9d1df05c | ||
|  | d239ef2956 | ||
|  | 7a865a9081 | ||
|  | 83d6c2970f | ||
|  | 8c7d159012 | ||
|  | d169f67901 | ||
|  | 982b723647 | ||
|  | 31d5ac05ff | ||
|  | 72d91d1571 | ||
|  | f4b57f4c57 | ||
|  | ee0833390a | ||
|  | 2acff07368 | ||
|  | bea1d24f07 | ||
|  | adc270c59f | ||
|  | 66064f7a94 | ||
|  | 1501fa8dbf | ||
|  | 60bba46d80 | ||
|  | 12c06ae97e | ||
|  | f0bea9cf71 | ||
|  | a555b6319c | ||
|  | 5dd93e4cdc | ||
|  | 3b4509d833 | ||
|  | 19308bbfbd | ||
|  | 4acc5432c3 | ||
|  | 08b8141fdf | ||
|  | e1200aa308 | ||
|  | 89666eb078 | ||
|  | d5605aa64d | ||
|  | 2582b016f9 | ||
|  | e8c52e25f0 | ||
|  | a149c6a105 | ||
|  | 131af9ab12 | ||
|  | aa2bbc6575 | ||
|  | 78e8c15786 | ||
|  | fda4146150 | ||
|  | ddc885066e | ||
|  | 08bc2afb49 | ||
|  | 1d0220b03d | ||
|  | 3033f7cc08 | ||
|  | 6b9ff47c88 | ||
|  | fd02c6102d | ||
|  | 30c712a6be | ||
|  | 3928c96640 | ||
|  | d86f655658 | ||
|  | abdad1c3ae | ||
|  | 9e5f1a0a87 | ||
|  | cdde6a4d8e | ||
|  | 8028b09351 | ||
|  | ebe66eaed9 | ||
|  | 5bce9a5f94 | ||
|  | dfd9927310 | ||
|  | 9bf1735bde | ||
|  | 2e8eeda5ab | ||
|  | 1cef0ce5f9 | ||
|  | 1efac99828 | ||
|  | 0e9473119e | ||
|  | 7bbfef7af3 | ||
|  | 5cb93509c1 | ||
|  | 89e89e04d8 | ||
|  | 72df0d8861 | ||
|  | 9910aebf45 | ||
|  | f9f8ecb2b1 | ||
|  | 438f7c5b0b | ||
|  | 4b1d1aba74 | ||
|  | 6dea73cfe2 | ||
|  | 58f5d0cf6e | ||
|  | 7b77e40514 | ||
|  | 660908c54b | ||
|  | e970564036 | ||
|  | b3038487f8 | ||
|  | cac98392a6 | ||
|  | dbd28377e3 | ||
|  | c76e4faf5d | ||
|  | e011b9ae63 | ||
|  | 7c74c77a2c | ||
|  | c2a2f195aa | ||
|  | 85d32c66f2 | ||
|  | 4e70cebf70 | ||
|  | 214d2e7659 | ||
|  | f380bb7f65 | ||
|  | 0a9a032daa | ||
|  | 23a2b58b24 | ||
|  | aee64b2522 | ||
|  | 02e07ec03a | ||
|  | 3d2dc8e699 | ||
|  | c84e15c9be | ||
|  | e18d0b9fd4 | ||
|  | 52817504d1 | ||
|  | a3b31fab54 | ||
|  | bc4aa3e40a | ||
|  | 873ea67e9c | ||
|  | 2c5115003b | ||
|  | e8ed913374 | ||
|  | 5bffba4e2f | ||
|  | 05575913db | ||
|  | 31c32ff42c | ||
|  | 6a671a5c02 | 
							
								
								
									
										4
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | node_modules | ||||||
|  | npm-debug.log | ||||||
|  | dist | ||||||
|  | .idea | ||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -7,3 +7,4 @@ yarn-error.log | |||||||
| config.ini | config.ini | ||||||
| cert.key | cert.key | ||||||
| cert.crt | cert.crt | ||||||
|  | docs/ | ||||||
							
								
								
									
										14
									
								
								.idea/dataSources.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,14 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="DataSourceManagerImpl" format="xml" multifile-model="true"> | ||||||
|  |     <data-source source="LOCAL" name="document.db" uuid="a2c75661-f9e2-478f-a69f-6a9409e69997"> | ||||||
|  |       <driver-ref>sqlite.xerial</driver-ref> | ||||||
|  |       <synchronize>true</synchronize> | ||||||
|  |       <jdbc-driver>org.sqlite.JDBC</jdbc-driver> | ||||||
|  |       <jdbc-url>jdbc:sqlite:$USER_HOME$/trilium-data/document.db</jdbc-url> | ||||||
|  |       <driver-properties> | ||||||
|  |         <property name="enable_load_extension" value="true" /> | ||||||
|  |       </driver-properties> | ||||||
|  |     </data-source> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										651
									
								
								.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,651 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <dataSource name="document.db"> | ||||||
|  |   <database-model serializer="dbm" rdbms="SQLITE" format-version="4.11"> | ||||||
|  |     <root id="1"> | ||||||
|  |       <ServerVersion>3.16.1</ServerVersion> | ||||||
|  |     </root> | ||||||
|  |     <schema id="2" parent="1" name="main"> | ||||||
|  |       <Current>1</Current> | ||||||
|  |       <Visible>1</Visible> | ||||||
|  |     </schema> | ||||||
|  |     <collation id="3" parent="1" name="BINARY"/> | ||||||
|  |     <collation id="4" parent="1" name="NOCASE"/> | ||||||
|  |     <collation id="5" parent="1" name="RTRIM"/> | ||||||
|  |     <table id="6" parent="2" name="api_tokens"/> | ||||||
|  |     <table id="7" parent="2" name="attributes"/> | ||||||
|  |     <table id="8" parent="2" name="branches"/> | ||||||
|  |     <table id="9" parent="2" name="event_log"/> | ||||||
|  |     <table id="10" parent="2" name="images"/> | ||||||
|  |     <table id="11" parent="2" name="note_images"/> | ||||||
|  |     <table id="12" parent="2" name="note_revisions"/> | ||||||
|  |     <table id="13" parent="2" name="notes"/> | ||||||
|  |     <table id="14" parent="2" name="options"/> | ||||||
|  |     <table id="15" parent="2" name="recent_notes"/> | ||||||
|  |     <table id="16" parent="2" name="source_ids"/> | ||||||
|  |     <table id="17" parent="2" name="sqlite_master"> | ||||||
|  |       <System>1</System> | ||||||
|  |     </table> | ||||||
|  |     <table id="18" parent="2" name="sqlite_sequence"> | ||||||
|  |       <System>1</System> | ||||||
|  |     </table> | ||||||
|  |     <table id="19" parent="2" name="sync"/> | ||||||
|  |     <column id="20" parent="6" name="apiTokenId"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="21" parent="6" name="token"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="22" parent="6" name="dateCreated"> | ||||||
|  |       <Position>3</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="23" parent="6" name="isDeleted"> | ||||||
|  |       <Position>4</Position> | ||||||
|  |       <DataType>INT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>0</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="24" parent="6" name="hash"> | ||||||
|  |       <Position>5</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>""</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <index id="25" parent="6" name="sqlite_autoindex_api_tokens_1"> | ||||||
|  |       <NameSurrogate>1</NameSurrogate> | ||||||
|  |       <ColNames>apiTokenId</ColNames> | ||||||
|  |       <Unique>1</Unique> | ||||||
|  |     </index> | ||||||
|  |     <key id="26" parent="6"> | ||||||
|  |       <ColNames>apiTokenId</ColNames> | ||||||
|  |       <Primary>1</Primary> | ||||||
|  |       <UnderlyingIndexName>sqlite_autoindex_api_tokens_1</UnderlyingIndexName> | ||||||
|  |     </key> | ||||||
|  |     <column id="27" parent="7" name="attributeId"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="28" parent="7" name="noteId"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="29" parent="7" name="type"> | ||||||
|  |       <Position>3</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="30" parent="7" name="name"> | ||||||
|  |       <Position>4</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="31" parent="7" name="value"> | ||||||
|  |       <Position>5</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>''</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="32" parent="7" name="position"> | ||||||
|  |       <Position>6</Position> | ||||||
|  |       <DataType>INT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>0</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="33" parent="7" name="dateCreated"> | ||||||
|  |       <Position>7</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="34" parent="7" name="dateModified"> | ||||||
|  |       <Position>8</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="35" parent="7" name="isDeleted"> | ||||||
|  |       <Position>9</Position> | ||||||
|  |       <DataType>INT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="36" parent="7" name="hash"> | ||||||
|  |       <Position>10</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>""</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="37" parent="7" name="isInheritable"> | ||||||
|  |       <Position>11</Position> | ||||||
|  |       <DataType>int|0s</DataType> | ||||||
|  |       <DefaultExpression>0</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <index id="38" parent="7" name="sqlite_autoindex_attributes_1"> | ||||||
|  |       <NameSurrogate>1</NameSurrogate> | ||||||
|  |       <ColNames>attributeId</ColNames> | ||||||
|  |       <Unique>1</Unique> | ||||||
|  |     </index> | ||||||
|  |     <key id="39" parent="7"> | ||||||
|  |       <ColNames>attributeId</ColNames> | ||||||
|  |       <Primary>1</Primary> | ||||||
|  |       <UnderlyingIndexName>sqlite_autoindex_attributes_1</UnderlyingIndexName> | ||||||
|  |     </key> | ||||||
|  |     <column id="40" parent="8" name="branchId"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="41" parent="8" name="noteId"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="42" parent="8" name="parentNoteId"> | ||||||
|  |       <Position>3</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="43" parent="8" name="notePosition"> | ||||||
|  |       <Position>4</Position> | ||||||
|  |       <DataType>INTEGER|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="44" parent="8" name="prefix"> | ||||||
|  |       <Position>5</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="45" parent="8" name="isExpanded"> | ||||||
|  |       <Position>6</Position> | ||||||
|  |       <DataType>BOOLEAN|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="46" parent="8" name="isDeleted"> | ||||||
|  |       <Position>7</Position> | ||||||
|  |       <DataType>INTEGER|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>0</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="47" parent="8" name="dateModified"> | ||||||
|  |       <Position>8</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="48" parent="8" name="hash"> | ||||||
|  |       <Position>9</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>""</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="49" parent="8" name="dateCreated"> | ||||||
|  |       <Position>10</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <index id="50" parent="8" name="sqlite_autoindex_branches_1"> | ||||||
|  |       <NameSurrogate>1</NameSurrogate> | ||||||
|  |       <ColNames>branchId</ColNames> | ||||||
|  |       <Unique>1</Unique> | ||||||
|  |     </index> | ||||||
|  |     <index id="51" parent="8" name="IDX_branches_noteId_parentNoteId"> | ||||||
|  |       <ColNames>noteId | ||||||
|  | parentNoteId</ColNames> | ||||||
|  |     </index> | ||||||
|  |     <index id="52" parent="8" name="IDX_branches_noteId"> | ||||||
|  |       <ColNames>noteId</ColNames> | ||||||
|  |     </index> | ||||||
|  |     <index id="53" parent="8" name="IDX_branches_parentNoteId"> | ||||||
|  |       <ColNames>parentNoteId</ColNames> | ||||||
|  |     </index> | ||||||
|  |     <key id="54" parent="8"> | ||||||
|  |       <ColNames>branchId</ColNames> | ||||||
|  |       <Primary>1</Primary> | ||||||
|  |       <UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName> | ||||||
|  |     </key> | ||||||
|  |     <column id="55" parent="9" name="eventId"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="56" parent="9" name="noteId"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="57" parent="9" name="comment"> | ||||||
|  |       <Position>3</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="58" parent="9" name="dateCreated"> | ||||||
|  |       <Position>4</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <index id="59" parent="9" name="sqlite_autoindex_event_log_1"> | ||||||
|  |       <NameSurrogate>1</NameSurrogate> | ||||||
|  |       <ColNames>eventId</ColNames> | ||||||
|  |       <Unique>1</Unique> | ||||||
|  |     </index> | ||||||
|  |     <key id="60" parent="9"> | ||||||
|  |       <ColNames>eventId</ColNames> | ||||||
|  |       <Primary>1</Primary> | ||||||
|  |       <UnderlyingIndexName>sqlite_autoindex_event_log_1</UnderlyingIndexName> | ||||||
|  |     </key> | ||||||
|  |     <column id="61" parent="10" name="imageId"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="62" parent="10" name="format"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="63" parent="10" name="checksum"> | ||||||
|  |       <Position>3</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="64" parent="10" name="name"> | ||||||
|  |       <Position>4</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="65" parent="10" name="data"> | ||||||
|  |       <Position>5</Position> | ||||||
|  |       <DataType>BLOB|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="66" parent="10" name="isDeleted"> | ||||||
|  |       <Position>6</Position> | ||||||
|  |       <DataType>INT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>0</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="67" parent="10" name="dateModified"> | ||||||
|  |       <Position>7</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="68" parent="10" name="dateCreated"> | ||||||
|  |       <Position>8</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="69" parent="10" name="hash"> | ||||||
|  |       <Position>9</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>""</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <index id="70" parent="10" name="sqlite_autoindex_images_1"> | ||||||
|  |       <NameSurrogate>1</NameSurrogate> | ||||||
|  |       <ColNames>imageId</ColNames> | ||||||
|  |       <Unique>1</Unique> | ||||||
|  |     </index> | ||||||
|  |     <key id="71" parent="10"> | ||||||
|  |       <ColNames>imageId</ColNames> | ||||||
|  |       <Primary>1</Primary> | ||||||
|  |       <UnderlyingIndexName>sqlite_autoindex_images_1</UnderlyingIndexName> | ||||||
|  |     </key> | ||||||
|  |     <column id="72" parent="11" name="noteImageId"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="73" parent="11" name="noteId"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="74" parent="11" name="imageId"> | ||||||
|  |       <Position>3</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="75" parent="11" name="isDeleted"> | ||||||
|  |       <Position>4</Position> | ||||||
|  |       <DataType>INT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>0</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="76" parent="11" name="dateModified"> | ||||||
|  |       <Position>5</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="77" parent="11" name="dateCreated"> | ||||||
|  |       <Position>6</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="78" parent="11" name="hash"> | ||||||
|  |       <Position>7</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>""</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <index id="79" parent="11" name="sqlite_autoindex_note_images_1"> | ||||||
|  |       <NameSurrogate>1</NameSurrogate> | ||||||
|  |       <ColNames>noteImageId</ColNames> | ||||||
|  |       <Unique>1</Unique> | ||||||
|  |     </index> | ||||||
|  |     <index id="80" parent="11" name="IDX_note_images_noteId_imageId"> | ||||||
|  |       <ColNames>noteId | ||||||
|  | imageId</ColNames> | ||||||
|  |     </index> | ||||||
|  |     <index id="81" parent="11" name="IDX_note_images_noteId"> | ||||||
|  |       <ColNames>noteId</ColNames> | ||||||
|  |     </index> | ||||||
|  |     <index id="82" parent="11" name="IDX_note_images_imageId"> | ||||||
|  |       <ColNames>imageId</ColNames> | ||||||
|  |     </index> | ||||||
|  |     <key id="83" parent="11"> | ||||||
|  |       <ColNames>noteImageId</ColNames> | ||||||
|  |       <Primary>1</Primary> | ||||||
|  |       <UnderlyingIndexName>sqlite_autoindex_note_images_1</UnderlyingIndexName> | ||||||
|  |     </key> | ||||||
|  |     <column id="84" parent="12" name="noteRevisionId"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="85" parent="12" name="noteId"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="86" parent="12" name="title"> | ||||||
|  |       <Position>3</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="87" parent="12" name="content"> | ||||||
|  |       <Position>4</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="88" parent="12" name="isProtected"> | ||||||
|  |       <Position>5</Position> | ||||||
|  |       <DataType>INT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>0</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="89" parent="12" name="dateModifiedFrom"> | ||||||
|  |       <Position>6</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="90" parent="12" name="dateModifiedTo"> | ||||||
|  |       <Position>7</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="91" parent="12" name="type"> | ||||||
|  |       <Position>8</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>''</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="92" parent="12" name="mime"> | ||||||
|  |       <Position>9</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>''</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="93" parent="12" name="hash"> | ||||||
|  |       <Position>10</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>""</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <index id="94" parent="12" name="sqlite_autoindex_note_revisions_1"> | ||||||
|  |       <NameSurrogate>1</NameSurrogate> | ||||||
|  |       <ColNames>noteRevisionId</ColNames> | ||||||
|  |       <Unique>1</Unique> | ||||||
|  |     </index> | ||||||
|  |     <index id="95" parent="12" name="IDX_note_revisions_noteId"> | ||||||
|  |       <ColNames>noteId</ColNames> | ||||||
|  |     </index> | ||||||
|  |     <index id="96" parent="12" name="IDX_note_revisions_dateModifiedFrom"> | ||||||
|  |       <ColNames>dateModifiedFrom</ColNames> | ||||||
|  |     </index> | ||||||
|  |     <index id="97" parent="12" name="IDX_note_revisions_dateModifiedTo"> | ||||||
|  |       <ColNames>dateModifiedTo</ColNames> | ||||||
|  |     </index> | ||||||
|  |     <key id="98" parent="12"> | ||||||
|  |       <ColNames>noteRevisionId</ColNames> | ||||||
|  |       <Primary>1</Primary> | ||||||
|  |       <UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName> | ||||||
|  |     </key> | ||||||
|  |     <column id="99" parent="13" name="noteId"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="100" parent="13" name="title"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>"unnamed"</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="101" parent="13" name="content"> | ||||||
|  |       <Position>3</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>""</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="102" parent="13" name="isProtected"> | ||||||
|  |       <Position>4</Position> | ||||||
|  |       <DataType>INT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>0</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="103" parent="13" name="isDeleted"> | ||||||
|  |       <Position>5</Position> | ||||||
|  |       <DataType>INT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>0</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="104" parent="13" name="dateCreated"> | ||||||
|  |       <Position>6</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="105" parent="13" name="dateModified"> | ||||||
|  |       <Position>7</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="106" parent="13" name="type"> | ||||||
|  |       <Position>8</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>'text'</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="107" parent="13" name="mime"> | ||||||
|  |       <Position>9</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>'text/html'</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="108" parent="13" name="hash"> | ||||||
|  |       <Position>10</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>""</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <index id="109" parent="13" name="sqlite_autoindex_notes_1"> | ||||||
|  |       <NameSurrogate>1</NameSurrogate> | ||||||
|  |       <ColNames>noteId</ColNames> | ||||||
|  |       <Unique>1</Unique> | ||||||
|  |     </index> | ||||||
|  |     <index id="110" parent="13" name="IDX_notes_type"> | ||||||
|  |       <ColNames>type</ColNames> | ||||||
|  |     </index> | ||||||
|  |     <key id="111" parent="13"> | ||||||
|  |       <ColNames>noteId</ColNames> | ||||||
|  |       <Primary>1</Primary> | ||||||
|  |       <UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName> | ||||||
|  |     </key> | ||||||
|  |     <column id="112" parent="14" name="name"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="113" parent="14" name="value"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="114" parent="14" name="dateModified"> | ||||||
|  |       <Position>3</Position> | ||||||
|  |       <DataType>INT|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="115" parent="14" name="isSynced"> | ||||||
|  |       <Position>4</Position> | ||||||
|  |       <DataType>INTEGER|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>0</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="116" parent="14" name="hash"> | ||||||
|  |       <Position>5</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>""</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="117" parent="14" name="dateCreated"> | ||||||
|  |       <Position>6</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <index id="118" parent="14" name="sqlite_autoindex_options_1"> | ||||||
|  |       <NameSurrogate>1</NameSurrogate> | ||||||
|  |       <ColNames>name</ColNames> | ||||||
|  |       <Unique>1</Unique> | ||||||
|  |     </index> | ||||||
|  |     <key id="119" parent="14"> | ||||||
|  |       <ColNames>name</ColNames> | ||||||
|  |       <Primary>1</Primary> | ||||||
|  |       <UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName> | ||||||
|  |     </key> | ||||||
|  |     <column id="120" parent="15" name="branchId"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="121" parent="15" name="notePath"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="122" parent="15" name="hash"> | ||||||
|  |       <Position>3</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <DefaultExpression>""</DefaultExpression> | ||||||
|  |     </column> | ||||||
|  |     <column id="123" parent="15" name="dateCreated"> | ||||||
|  |       <Position>4</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="124" parent="15" name="isDeleted"> | ||||||
|  |       <Position>5</Position> | ||||||
|  |       <DataType>INT|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <index id="125" parent="15" name="sqlite_autoindex_recent_notes_1"> | ||||||
|  |       <NameSurrogate>1</NameSurrogate> | ||||||
|  |       <ColNames>branchId</ColNames> | ||||||
|  |       <Unique>1</Unique> | ||||||
|  |     </index> | ||||||
|  |     <key id="126" parent="15"> | ||||||
|  |       <ColNames>branchId</ColNames> | ||||||
|  |       <Primary>1</Primary> | ||||||
|  |       <UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName> | ||||||
|  |     </key> | ||||||
|  |     <column id="127" parent="16" name="sourceId"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="128" parent="16" name="dateCreated"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <index id="129" parent="16" name="sqlite_autoindex_source_ids_1"> | ||||||
|  |       <NameSurrogate>1</NameSurrogate> | ||||||
|  |       <ColNames>sourceId</ColNames> | ||||||
|  |       <Unique>1</Unique> | ||||||
|  |     </index> | ||||||
|  |     <key id="130" parent="16"> | ||||||
|  |       <ColNames>sourceId</ColNames> | ||||||
|  |       <Primary>1</Primary> | ||||||
|  |       <UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName> | ||||||
|  |     </key> | ||||||
|  |     <column id="131" parent="17" name="type"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>text|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="132" parent="17" name="name"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>text|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="133" parent="17" name="tbl_name"> | ||||||
|  |       <Position>3</Position> | ||||||
|  |       <DataType>text|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="134" parent="17" name="rootpage"> | ||||||
|  |       <Position>4</Position> | ||||||
|  |       <DataType>integer|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="135" parent="17" name="sql"> | ||||||
|  |       <Position>5</Position> | ||||||
|  |       <DataType>text|0s</DataType> | ||||||
|  |     </column> | ||||||
|  |     <column id="136" parent="18" name="name"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |     </column> | ||||||
|  |     <column id="137" parent="18" name="seq"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |     </column> | ||||||
|  |     <column id="138" parent="19" name="id"> | ||||||
|  |       <Position>1</Position> | ||||||
|  |       <DataType>INTEGER|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |       <SequenceIdentity>1</SequenceIdentity> | ||||||
|  |     </column> | ||||||
|  |     <column id="139" parent="19" name="entityName"> | ||||||
|  |       <Position>2</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="140" parent="19" name="entityId"> | ||||||
|  |       <Position>3</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="141" parent="19" name="sourceId"> | ||||||
|  |       <Position>4</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <column id="142" parent="19" name="syncDate"> | ||||||
|  |       <Position>5</Position> | ||||||
|  |       <DataType>TEXT|0s</DataType> | ||||||
|  |       <NotNull>1</NotNull> | ||||||
|  |     </column> | ||||||
|  |     <index id="143" parent="19" name="IDX_sync_entityName_entityId"> | ||||||
|  |       <ColNames>entityName | ||||||
|  | entityId</ColNames> | ||||||
|  |       <Unique>1</Unique> | ||||||
|  |     </index> | ||||||
|  |     <index id="144" parent="19" name="IDX_sync_syncDate"> | ||||||
|  |       <ColNames>syncDate</ColNames> | ||||||
|  |     </index> | ||||||
|  |     <key id="145" parent="19"> | ||||||
|  |       <ColNames>id</ColNames> | ||||||
|  |       <Primary>1</Primary> | ||||||
|  |     </key> | ||||||
|  |   </database-model> | ||||||
|  | </dataSource> | ||||||
							
								
								
									
										2
									
								
								.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997/storage_v2/_src_/schema/main.uQUzAA.meta
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | |||||||
|  | #n:main | ||||||
|  | !<md> [0, 0, null, null, -2147483648, -2147483648] | ||||||
							
								
								
									
										10
									
								
								.idea/inspectionProfiles/Project_Default.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,10 @@ | |||||||
|  | <component name="InspectionProjectProfileManager"> | ||||||
|  |   <profile version="1.0"> | ||||||
|  |     <option name="myName" value="Project Default" /> | ||||||
|  |     <inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false"> | ||||||
|  |       <option name="processCode" value="true" /> | ||||||
|  |       <option name="processLiterals" value="true" /> | ||||||
|  |       <option name="processComments" value="true" /> | ||||||
|  |     </inspection_tool> | ||||||
|  |   </profile> | ||||||
|  | </component> | ||||||
							
								
								
									
										9
									
								
								.idea/jsLinters/jslint.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,9 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="JSLintConfiguration"> | ||||||
|  |     <option devel="true" /> | ||||||
|  |     <option es6="true" /> | ||||||
|  |     <option maxerr="50" /> | ||||||
|  |     <option node="true" /> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										9
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,9 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="JavaScriptSettings"> | ||||||
|  |     <option name="languageLevel" value="ES6" /> | ||||||
|  |   </component> | ||||||
|  |   <component name="ProjectRootManager"> | ||||||
|  |     <output url="file://$PROJECT_DIR$/out" /> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,8 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="ProjectModuleManager"> | ||||||
|  |     <modules> | ||||||
|  |       <module fileurl="file://$PROJECT_DIR$/trilium.iml" filepath="$PROJECT_DIR$/trilium.iml" /> | ||||||
|  |     </modules> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										6
									
								
								.idea/sqldialects.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,6 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="SqlDialectMappings"> | ||||||
|  |     <file url="PROJECT" dialect="SQLite" /> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,6 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="VcsDirectoryMappings"> | ||||||
|  |     <mapping directory="" vcs="Git" /> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										21
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | |||||||
|  | FROM node:8.11.2 | ||||||
|  |  | ||||||
|  | RUN apt-get update && apt-get install -y nasm | ||||||
|  |  | ||||||
|  | # Create app directory | ||||||
|  | WORKDIR /usr/src/app | ||||||
|  |  | ||||||
|  | # Install app dependencies | ||||||
|  | # A wildcard is used to ensure both package.json AND package-lock.json are copied | ||||||
|  | # where available (npm@5+) | ||||||
|  | COPY package*.json ./ | ||||||
|  |  | ||||||
|  | RUN npm install --production | ||||||
|  | # If you are building your code for production | ||||||
|  | # RUN npm install --only=production | ||||||
|  |  | ||||||
|  | # Bundle app source | ||||||
|  | COPY . . | ||||||
|  |  | ||||||
|  | EXPOSE 8080 | ||||||
|  | CMD [ "node", "src/www" ] | ||||||
| @@ -1,4 +1,6 @@ | |||||||
| # Trilium Notes | # Trilium Notes | ||||||
|  |  | ||||||
|  | [](https://gitter.im/trilium-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||||||
| Trilium Notes is a hierarchical note taking application. Picture tells a thousand words: | Trilium Notes is a hierarchical note taking application. Picture tells a thousand words: | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -10,6 +12,7 @@ Trilium Notes is a hierarchical note taking application. Picture tells a thousan | |||||||
| * WYSIWYG (What You See Is What You Get) editing | * WYSIWYG (What You See Is What You Get) editing | ||||||
| * Fast and easy [navigation between notes](https://github.com/zadam/trilium/wiki/Note-navigation) | * Fast and easy [navigation between notes](https://github.com/zadam/trilium/wiki/Note-navigation) | ||||||
| * Seamless note versioning | * Seamless note versioning | ||||||
|  | * Note labels can be used to tag/label notes as an alternative note organization and querying | ||||||
| * Can be deployed as web application and / or desktop application with offline access (electron based) | * Can be deployed as web application and / or desktop application with offline access (electron based) | ||||||
| * [Synchronization with](https://github.com/zadam/trilium/wiki/Synchronization) self-hosted sync server | * [Synchronization with](https://github.com/zadam/trilium/wiki/Synchronization) self-hosted sync server | ||||||
| * Strong [note encryption](https://github.com/zadam/trilium/wiki/Protected-notes) | * Strong [note encryption](https://github.com/zadam/trilium/wiki/Protected-notes) | ||||||
| @@ -34,10 +37,12 @@ List of documentation pages: | |||||||
| * [Installation as webapp](https://github.com/zadam/trilium/wiki/Installation-as-webapp) | * [Installation as webapp](https://github.com/zadam/trilium/wiki/Installation-as-webapp) | ||||||
| * [Note navigation](https://github.com/zadam/trilium/wiki/Note-navigation) | * [Note navigation](https://github.com/zadam/trilium/wiki/Note-navigation) | ||||||
| * [Tree manipulation](https://github.com/zadam/trilium/wiki/Tree-manipulation) | * [Tree manipulation](https://github.com/zadam/trilium/wiki/Tree-manipulation) | ||||||
|  | * [Labels](https://github.com/zadam/trilium/wiki/Labels) | ||||||
| * [Links](https://github.com/zadam/trilium/wiki/Links) | * [Links](https://github.com/zadam/trilium/wiki/Links) | ||||||
| * [Cloning notes](https://github.com/zadam/trilium/wiki/Cloning-notes) | * [Cloning notes](https://github.com/zadam/trilium/wiki/Cloning-notes) | ||||||
| * [Protected notes](https://github.com/zadam/trilium/wiki/Protected-notes) | * [Protected notes](https://github.com/zadam/trilium/wiki/Protected-notes) | ||||||
| * [Synchronization](https://github.com/zadam/trilium/wiki/Synchronization) | * [Synchronization](https://github.com/zadam/trilium/wiki/Synchronization) | ||||||
| * [Document](https://github.com/zadam/trilium/wiki/Document) | * [Document](https://github.com/zadam/trilium/wiki/Document) | ||||||
|  | * [Theming](https://github.com/zadam/trilium/wiki/Theming) | ||||||
| * [Keyboard shortcuts](https://github.com/zadam/trilium/wiki/Keyboard-shortcuts) | * [Keyboard shortcuts](https://github.com/zadam/trilium/wiki/Keyboard-shortcuts) | ||||||
| * [Troubleshooting](https://github.com/zadam/trilium/wiki/Troubleshooting) | * [Troubleshooting](https://github.com/zadam/trilium/wiki/Troubleshooting) | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								bin/build-docker.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,8 @@ | |||||||
|  | #!/usr/bin/env bash | ||||||
|  |  | ||||||
|  | if [[ $# -eq 0 ]] ; then | ||||||
|  |     echo "Missing argument of new version" | ||||||
|  |     exit 1 | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | sudo docker build -t zadam/trilium:latest -t zadam/trilium:$1 . | ||||||
							
								
								
									
										23
									
								
								bin/build-pkg.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,23 @@ | |||||||
|  | #!/usr/bin/env bash | ||||||
|  |  | ||||||
|  | if [[ $# -eq 0 ]] ; then | ||||||
|  |     echo "Missing argument of new version" | ||||||
|  |     exit 1 | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | VERSION=$1 | ||||||
|  |  | ||||||
|  | PKG_DIR=dist/trilium-linux-x64-server | ||||||
|  |  | ||||||
|  | mkdir $PKG_DIR | ||||||
|  |  | ||||||
|  | pkg . --targets node8-linux-x64 --output ${PKG_DIR}/trilium | ||||||
|  |  | ||||||
|  | chmod +x ${PKG_DIR}/trilium | ||||||
|  |  | ||||||
|  | cp node_modules/sqlite3/lib/binding/node-v57-linux-x64/node_sqlite3.node ${PKG_DIR}/ | ||||||
|  | cp node_modules/scrypt/build/Release/scrypt.node ${PKG_DIR}/ | ||||||
|  |  | ||||||
|  | cd dist | ||||||
|  |  | ||||||
|  | 7z a trilium-linux-x64-${VERSION}-server.7z trilium-linux-x64-server | ||||||
							
								
								
									
										9
									
								
								bin/push-docker-image.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,9 @@ | |||||||
|  | #!/usr/bin/env bash | ||||||
|  |  | ||||||
|  | if [[ $# -eq 0 ]] ; then | ||||||
|  |     echo "Missing argument of new version" | ||||||
|  |     exit 1 | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | sudo docker push zadam/trilium:latest | ||||||
|  | sudo docker push zadam/trilium:$1 | ||||||
| @@ -24,9 +24,9 @@ jq '.version = "'$VERSION'"' package.json|sponge package.json | |||||||
|  |  | ||||||
| git add package.json | git add package.json | ||||||
|  |  | ||||||
| echo 'module.exports = { build_date:"'`date --iso-8601=seconds`'", build_revision: "'`git log -1 --format="%H"`'" };' > services/build.js | echo 'module.exports = { buildDate:"'`date --iso-8601=seconds`'", buildRevision: "'`git log -1 --format="%H"`'" };' > src/services/build.js | ||||||
|  |  | ||||||
| git add services/build.js | git add src/services/build.js | ||||||
|  |  | ||||||
| TAG=v$VERSION | TAG=v$VERSION | ||||||
|  |  | ||||||
| @@ -47,6 +47,7 @@ bin/package.sh | |||||||
| LINUX_X64_BUILD=trilium-linux-x64-$VERSION.7z | LINUX_X64_BUILD=trilium-linux-x64-$VERSION.7z | ||||||
| LINUX_IA32_BUILD=trilium-linux-ia32-$VERSION.7z | LINUX_IA32_BUILD=trilium-linux-ia32-$VERSION.7z | ||||||
| WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.7z | WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.7z | ||||||
|  | SERVER_BUILD=trilium-linux-x64-server-$VERSION.7z | ||||||
|  |  | ||||||
| echo "Creating release in GitHub" | echo "Creating release in GitHub" | ||||||
|  |  | ||||||
| @@ -75,4 +76,21 @@ github-release upload \ | |||||||
|     --name "$WINDOWS_X64_BUILD" \ |     --name "$WINDOWS_X64_BUILD" \ | ||||||
|     --file "dist/$WINDOWS_X64_BUILD" |     --file "dist/$WINDOWS_X64_BUILD" | ||||||
|  |  | ||||||
|  | echo "Packaging server version" | ||||||
|  |  | ||||||
|  | bin/build-pkg.sh $VERSION | ||||||
|  |  | ||||||
|  | github-release upload \ | ||||||
|  |     --tag $TAG \ | ||||||
|  |     --name "$SERVER_BUILD" \ | ||||||
|  |     --file "dist/$SERVER_BUILD" | ||||||
|  |  | ||||||
|  | echo "Building docker image" | ||||||
|  |  | ||||||
|  | bin/build-docker.sh $VERSION | ||||||
|  |  | ||||||
|  | echo "Pushing docker image to dockerhub" | ||||||
|  |  | ||||||
|  | bin/push-docker-image.sh $VERSION | ||||||
|  |  | ||||||
| echo "Release finished!" | echo "Release finished!" | ||||||
| @@ -1,13 +1,12 @@ | |||||||
|  | [General] | ||||||
|  | # Instance name can be used to distinguish between different instances | ||||||
|  | instanceName= | ||||||
|  |  | ||||||
| [Network] | [Network] | ||||||
|  | # port setting is relevant only for web deployments, desktop builds run on random free port | ||||||
| port=8080 | port=8080 | ||||||
| # true for TLS/SSL/HTTPS (secure), false for HTTP (unsecure). | # true for TLS/SSL/HTTPS (secure), false for HTTP (unsecure). | ||||||
| https=false | https=false | ||||||
| # path to certificate (run "bash generate-cert.sh" to generate self-signed certificate). Relevant only if https=true | # path to certificate (run "bash bin/generate-cert.sh" to generate self-signed certificate). Relevant only if https=true | ||||||
| certPath= | certPath= | ||||||
| keyPath= | keyPath= | ||||||
|  |  | ||||||
| [Sync] |  | ||||||
| syncServerHost= |  | ||||||
| syncServerTimeout=10000 |  | ||||||
| syncProxy= |  | ||||||
| syncServerCertificate= |  | ||||||
							
								
								
									
										53
									
								
								db/main_branches.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,53 @@ | |||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('root', 'root', 'none', 0, null, 1, 0, '2018-01-01T00:00:00.000Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('dLgtLUFn3GoN', '1Heh2acXfPNt', 'root', 21, null, 1, 0, '2017-12-23T00:46:39.304Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('QLfS835GSfIh', '3RkyK9LI18dO', '1Heh2acXfPNt', 1, null, 1, 0, '2017-12-23T01:20:04.181Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('QJAcYJ1gGUh9', 'L1Ox40M1aEyy', '3RkyK9LI18dO', 0, null, 0, 0, '2017-12-23T01:20:45.365Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('wLTa2l3lYi83', 'HJusZTbBU494', '3RkyK9LI18dO', 2, null, 1, 0, '2017-12-23T01:20:50.709Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('yMhwsE7uvEij', '3oldoiMUPOlr', 'HJusZTbBU494', 1, null, 1, 0, '2017-12-23T01:20:55.775Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('EjQTcVVHFmmZ', 'MG0wntwILQW6', '3oldoiMUPOlr', 1, null, 1, 0, '2017-12-23T01:21:10.517Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('jvhKcwz4pYTr', 'ZC78NlmdXeC6', 'WdWZFuWNVDZk', 0, null, 1, 0, '2017-12-23T04:06:21.579Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('CarTrwkGVcPz', 'NncfGH8dyNjJ', 'WdWZFuWNVDZk', 1, null, 0, 0, '2017-12-23T04:06:24.012Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('6M7qPlr7at6N', 'eouCLkjbruai', 'NncfGH8dyNjJ', 0, null, 0, 0, '2017-12-23T01:23:28.291Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('tQgognnAH9WI', 'C44aq4mkaX67', 'NncfGH8dyNjJ', 1, null, 0, 0, '2017-12-23T01:23:31.879Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('xyAi7MmgvAgR', 'C44aq4mkaX67', 'ZC78NlmdXeC6', 1, null, 0, 0, '2017-12-23T01:23:47.756Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('xQ3fjRp9yaPq', 'I6Cw88AirBBl', 'C44aq4mkaX67', 0, null, 0, 0, '2017-12-23T01:24:04.681Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('2GOsNT5LsvTP', 'mcEwFMSjhlvL', 'C44aq4mkaX67', 1, null, 0, 0, '2017-12-23T01:29:35.974Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('RxUiraiR655R', 'CF2lUIJAr6Ey', 'NncfGH8dyNjJ', 2, null, 0, 0, '2017-12-23T01:34:37.658Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('mZuSrZ18Zmv0', 'xkXwueRoDNeN', 'MG0wntwILQW6', 0, null, 0, 0, '2017-12-23T01:35:40.306Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('hbcWTnEnXPwF', 'eXHZAKsMYgur', '1Heh2acXfPNt', 3, null, 1, 0, '2017-12-23T03:32:42.868Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('8a3aNxjG0nu7', '2WU27ekfy07E', 'eXHZAKsMYgur', 0, null, 0, 0, '2017-12-23T03:32:49.379Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('4Tu6vaPdCxCM', 'TjWEndYCCg7g', 'eXHZAKsMYgur', 1, null, 0, 0, '2017-12-23T03:33:23.584Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('lBPOmhP12egP', '8nRNDJGyGs2Z', 'TjWEndYCCg7g', 0, null, 0, 0, '2017-12-23T03:33:37.327Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('C5ipVqeDWySp', '9zSwD89vgzNO', '8nRNDJGyGs2Z', 0, null, 0, 0, '2017-12-23T03:37:04.912Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('uSitzbGcSATJ', 'u5t1EvWa3CMO', 'TjWEndYCCg7g', 1, null, 0, 0, '2017-12-23T03:39:21.918Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('GZ6aRI8rdSJt', '8nRNDJGyGs2Z', 'MG0wntwILQW6', 1, '', 0, 0, '2017-12-23T03:42:28.310Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('HsN4600rQoL9', 'Iha4YwchR413', '3oldoiMUPOlr', 0, null, 1, 0, '2017-12-23T03:44:30.945Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('uipfvAfwWRgx', '6ZuXjCSWgjB4', 'HJusZTbBU494', 0, null, 0, 0, '2017-12-23T03:44:54.096Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('nMRpPWWH8WRk', 'GpGnjmcAPeWG', '6ZuXjCSWgjB4', 0, null, 1, 0, '2017-12-23T03:44:57.036Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('c4wt27WNjepw', '21K84UqGhqlt', 'GpGnjmcAPeWG', 0, null, 0, 0, '2017-12-23T03:45:10.933Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('0fpnraUGs9Kl', 'rz5t0r9Qr2WC', 'HJusZTbBU494', 2, null, 1, 0, '2017-12-23T03:45:20.914Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('d8L8zYlLTbym', 'R6pheWjdwmNU', 'rz5t0r9Qr2WC', 0, null, 1, 0, '2017-12-23T03:45:28.002Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('T4USGzfllu5t', '5v5Dx6LMHXIO', 'Iha4YwchR413', 0, null, 0, 0, '2017-12-23T03:45:44.184Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('c4JgFNIobvQW', 'MLQjmREtcnJ3', 'R6pheWjdwmNU', 0, null, 0, 0, '2017-12-23T03:47:48.208Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('nfWjptAU2ZDg', 'pTTjrxgnvURB', 'R6pheWjdwmNU', 1, null, 0, 0, '2017-12-23T03:47:55.932Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('T2ToYBfyPy0g', 'cFK9sGYZaMWs', 'rz5t0r9Qr2WC', 1, null, 0, 0, '2017-12-23T03:49:32.210Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('NG4gbKOnsM3v', '21K84UqGhqlt', 'MLQjmREtcnJ3', 0, '28. 11. 2017', 0, 0, '2017-12-23T03:53:38.110Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('Fstg4tkccO4N', '5v5Dx6LMHXIO', 'MLQjmREtcnJ3', 1, '21. 12. 2017', 0, 0, '2017-12-23T03:53:49.737Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('MN8B7qXDUViO', 'xkXwueRoDNeN', 'MLQjmREtcnJ3', 2, '22. 12. 2017', 0, 0, '2017-12-23T03:53:57.486Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('gSRkHpB7Bu3D', 'pOFVzbXLmzhX', 'R6pheWjdwmNU', 2, null, 0, 0, '2017-12-23T03:54:46.138Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('6brdjeWDOB6w', '0xtvjqrcGiRB', 'ZC78NlmdXeC6', 0, null, 0, 0, '2017-12-23T04:02:06.650Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('AqKUM2zUVFUF', 'Zl69uXBSen0w', 'ZC78NlmdXeC6', 2, null, 1, 0, '2017-12-23T04:02:16.685Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('Ez7NN2WVzRc4', '62BKAQMVP2KW', 'Zl69uXBSen0w', 1, null, 0, 0, '2017-12-23T04:02:39.164Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('t3vVElqMIQVa', 'h4OfLEAYspud', 'WdWZFuWNVDZk', 2, null, 1, 0, '2017-12-23T04:06:25.769Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('O983DHtLpgmr', '1hASbLRDL7oo', 'h4OfLEAYspud', 0, null, 0, 0, '2017-12-23T16:42:26.347Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('RsvL795Mk1bp', '1hASbLRDL7oo', 'GpGnjmcAPeWG', 1, '', 0, 0, '2017-12-23T04:04:56.830Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('79e4hrHLFmx6', 'jyqG9GucsMdn', 'Iha4YwchR413', 1, null, 0, 0, '2017-12-23T04:05:16.439Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('oWO8rctUjf7d', 'WdWZFuWNVDZk', '1Heh2acXfPNt', 5, null, 1, 0, '2017-12-23T04:06:16.179Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('GOxcrZrxalFN', 'yK4SBJfwD3tY', '1Heh2acXfPNt', 8, null, 1, 0, '2017-12-23T04:06:32.833Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('bSPmEvjLzQKU', 'r4BnsmSQeVr1', 'yK4SBJfwD3tY', 0, null, 0, 0, '2017-12-23T04:06:37.427Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('bMtxCD6cwNR9', 'QbL3pTvhgzM8', 'yK4SBJfwD3tY', 2, null, 0, 0, '2017-12-23T04:06:43.841Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('o4ycR7xIi4oI', 'moMbTKwN15Ps', 'yK4SBJfwD3tY', 3, null, 1, 0, '2017-12-23T04:06:49.331Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('abTEhnOsAsSg', 'PEGQGg0In3Ar', 'GpGnjmcAPeWG', 2, null, 0, 0, '2017-12-23T16:44:35.900Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('bryQseMhyzaI', 'IlULcDiOTI4K', '1Heh2acXfPNt', 0, null, 0, 0, '2017-12-23T18:04:26.439Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('ccslPJf3wQV3', 'vBv6ovBupfTj', 'IlULcDiOTI4K', 0, null, 0, 0, '2017-12-23T18:04:50.904Z'); | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('5Dt9YCMn59sY', 'mw4f2xB4J5fV', 'IlULcDiOTI4K', 1, null, 0, 0, '2017-12-23T18:05:24.868Z'); | ||||||
| @@ -1,52 +0,0 @@ | |||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('dLgtLUFn3GoN', '1Heh2acXfPNt', 'root', 21, null, 1, 0, '2017-12-23T00:46:39.304Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('QLfS835GSfIh', '3RkyK9LI18dO', '1Heh2acXfPNt', 1, null, 1, 0, '2017-12-23T01:20:04.181Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('QJAcYJ1gGUh9', 'L1Ox40M1aEyy', '3RkyK9LI18dO', 0, null, 0, 0, '2017-12-23T01:20:45.365Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('wLTa2l3lYi83', 'HJusZTbBU494', '3RkyK9LI18dO', 2, null, 1, 0, '2017-12-23T01:20:50.709Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('yMhwsE7uvEij', '3oldoiMUPOlr', 'HJusZTbBU494', 1, null, 1, 0, '2017-12-23T01:20:55.775Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('EjQTcVVHFmmZ', 'MG0wntwILQW6', '3oldoiMUPOlr', 1, null, 1, 0, '2017-12-23T01:21:10.517Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('jvhKcwz4pYTr', 'ZC78NlmdXeC6', 'WdWZFuWNVDZk', 0, null, 1, 0, '2017-12-23T04:06:21.579Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('CarTrwkGVcPz', 'NncfGH8dyNjJ', 'WdWZFuWNVDZk', 1, null, 0, 0, '2017-12-23T04:06:24.012Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('6M7qPlr7at6N', 'eouCLkjbruai', 'NncfGH8dyNjJ', 0, null, 0, 0, '2017-12-23T01:23:28.291Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('tQgognnAH9WI', 'C44aq4mkaX67', 'NncfGH8dyNjJ', 1, null, 0, 0, '2017-12-23T01:23:31.879Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('xyAi7MmgvAgR', 'C44aq4mkaX67', 'ZC78NlmdXeC6', 1, null, 0, 0, '2017-12-23T01:23:47.756Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('xQ3fjRp9yaPq', 'I6Cw88AirBBl', 'C44aq4mkaX67', 0, null, 0, 0, '2017-12-23T01:24:04.681Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('2GOsNT5LsvTP', 'mcEwFMSjhlvL', 'C44aq4mkaX67', 1, null, 0, 0, '2017-12-23T01:29:35.974Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('RxUiraiR655R', 'CF2lUIJAr6Ey', 'NncfGH8dyNjJ', 2, null, 0, 0, '2017-12-23T01:34:37.658Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('mZuSrZ18Zmv0', 'xkXwueRoDNeN', 'MG0wntwILQW6', 0, null, 0, 0, '2017-12-23T01:35:40.306Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('hbcWTnEnXPwF', 'eXHZAKsMYgur', '1Heh2acXfPNt', 3, null, 1, 0, '2017-12-23T03:32:42.868Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('8a3aNxjG0nu7', '2WU27ekfy07E', 'eXHZAKsMYgur', 0, null, 0, 0, '2017-12-23T03:32:49.379Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('4Tu6vaPdCxCM', 'TjWEndYCCg7g', 'eXHZAKsMYgur', 1, null, 0, 0, '2017-12-23T03:33:23.584Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('lBPOmhP12egP', '8nRNDJGyGs2Z', 'TjWEndYCCg7g', 0, null, 0, 0, '2017-12-23T03:33:37.327Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('C5ipVqeDWySp', '9zSwD89vgzNO', '8nRNDJGyGs2Z', 0, null, 0, 0, '2017-12-23T03:37:04.912Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('uSitzbGcSATJ', 'u5t1EvWa3CMO', 'TjWEndYCCg7g', 1, null, 0, 0, '2017-12-23T03:39:21.918Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('GZ6aRI8rdSJt', '8nRNDJGyGs2Z', 'MG0wntwILQW6', 1, '', 0, 0, '2017-12-23T03:42:28.310Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('HsN4600rQoL9', 'Iha4YwchR413', '3oldoiMUPOlr', 0, null, 1, 0, '2017-12-23T03:44:30.945Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('uipfvAfwWRgx', '6ZuXjCSWgjB4', 'HJusZTbBU494', 0, null, 0, 0, '2017-12-23T03:44:54.096Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('nMRpPWWH8WRk', 'GpGnjmcAPeWG', '6ZuXjCSWgjB4', 0, null, 1, 0, '2017-12-23T03:44:57.036Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('c4wt27WNjepw', '21K84UqGhqlt', 'GpGnjmcAPeWG', 0, null, 0, 0, '2017-12-23T03:45:10.933Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('0fpnraUGs9Kl', 'rz5t0r9Qr2WC', 'HJusZTbBU494', 2, null, 1, 0, '2017-12-23T03:45:20.914Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('d8L8zYlLTbym', 'R6pheWjdwmNU', 'rz5t0r9Qr2WC', 0, null, 1, 0, '2017-12-23T03:45:28.002Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('T4USGzfllu5t', '5v5Dx6LMHXIO', 'Iha4YwchR413', 0, null, 0, 0, '2017-12-23T03:45:44.184Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('c4JgFNIobvQW', 'MLQjmREtcnJ3', 'R6pheWjdwmNU', 0, null, 0, 0, '2017-12-23T03:47:48.208Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('nfWjptAU2ZDg', 'pTTjrxgnvURB', 'R6pheWjdwmNU', 1, null, 0, 0, '2017-12-23T03:47:55.932Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('T2ToYBfyPy0g', 'cFK9sGYZaMWs', 'rz5t0r9Qr2WC', 1, null, 0, 0, '2017-12-23T03:49:32.210Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('NG4gbKOnsM3v', '21K84UqGhqlt', 'MLQjmREtcnJ3', 0, '28. 11. 2017', 0, 0, '2017-12-23T03:53:38.110Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('Fstg4tkccO4N', '5v5Dx6LMHXIO', 'MLQjmREtcnJ3', 1, '21. 12. 2017', 0, 0, '2017-12-23T03:53:49.737Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('MN8B7qXDUViO', 'xkXwueRoDNeN', 'MLQjmREtcnJ3', 2, '22. 12. 2017', 0, 0, '2017-12-23T03:53:57.486Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('gSRkHpB7Bu3D', 'pOFVzbXLmzhX', 'R6pheWjdwmNU', 2, null, 0, 0, '2017-12-23T03:54:46.138Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('6brdjeWDOB6w', '0xtvjqrcGiRB', 'ZC78NlmdXeC6', 0, null, 0, 0, '2017-12-23T04:02:06.650Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('AqKUM2zUVFUF', 'Zl69uXBSen0w', 'ZC78NlmdXeC6', 2, null, 1, 0, '2017-12-23T04:02:16.685Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('Ez7NN2WVzRc4', '62BKAQMVP2KW', 'Zl69uXBSen0w', 1, null, 0, 0, '2017-12-23T04:02:39.164Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('t3vVElqMIQVa', 'h4OfLEAYspud', 'WdWZFuWNVDZk', 2, null, 1, 0, '2017-12-23T04:06:25.769Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('O983DHtLpgmr', '1hASbLRDL7oo', 'h4OfLEAYspud', 0, null, 0, 0, '2017-12-23T16:42:26.347Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('RsvL795Mk1bp', '1hASbLRDL7oo', 'GpGnjmcAPeWG', 1, '', 0, 0, '2017-12-23T04:04:56.830Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('79e4hrHLFmx6', 'jyqG9GucsMdn', 'Iha4YwchR413', 1, null, 0, 0, '2017-12-23T04:05:16.439Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('oWO8rctUjf7d', 'WdWZFuWNVDZk', '1Heh2acXfPNt', 5, null, 1, 0, '2017-12-23T04:06:16.179Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('GOxcrZrxalFN', 'yK4SBJfwD3tY', '1Heh2acXfPNt', 8, null, 1, 0, '2017-12-23T04:06:32.833Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('bSPmEvjLzQKU', 'r4BnsmSQeVr1', 'yK4SBJfwD3tY', 0, null, 0, 0, '2017-12-23T04:06:37.427Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('bMtxCD6cwNR9', 'QbL3pTvhgzM8', 'yK4SBJfwD3tY', 2, null, 0, 0, '2017-12-23T04:06:43.841Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('o4ycR7xIi4oI', 'moMbTKwN15Ps', 'yK4SBJfwD3tY', 3, null, 1, 0, '2017-12-23T04:06:49.331Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('abTEhnOsAsSg', 'PEGQGg0In3Ar', 'GpGnjmcAPeWG', 2, null, 0, 0, '2017-12-23T16:44:35.900Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('bryQseMhyzaI', 'IlULcDiOTI4K', '1Heh2acXfPNt', 0, null, 0, 0, '2017-12-23T18:04:26.439Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('ccslPJf3wQV3', 'vBv6ovBupfTj', 'IlULcDiOTI4K', 0, null, 0, 0, '2017-12-23T18:04:50.904Z'); |  | ||||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('5Dt9YCMn59sY', 'mw4f2xB4J5fV', 'IlULcDiOTI4K', 1, null, 0, 0, '2017-12-23T18:05:24.868Z'); |  | ||||||
| @@ -0,0 +1 @@ | |||||||
|  | DROP INDEX IDX_attributes_noteId_name; | ||||||
							
								
								
									
										1
									
								
								db/migrations/0073__add_isDeleted_to_attributes.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | ALTER TABLE attributes ADD COLUMN isDeleted INT NOT NULL DEFAULT 0; | ||||||
							
								
								
									
										1
									
								
								db/migrations/0074__add_position_to_attribute.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | ALTER TABLE attributes ADD COLUMN position INT NOT NULL DEFAULT 0; | ||||||
							
								
								
									
										7
									
								
								db/migrations/0075__add_api_token.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | |||||||
|  | CREATE TABLE IF NOT EXISTS "api_tokens" | ||||||
|  | ( | ||||||
|  |   apiTokenId TEXT PRIMARY KEY NOT NULL, | ||||||
|  |   token TEXT NOT NULL, | ||||||
|  |   dateCreated TEXT NOT NULL, | ||||||
|  |   isDeleted INT NOT NULL DEFAULT 0 | ||||||
|  | ); | ||||||
							
								
								
									
										1
									
								
								db/migrations/0076__add_attribute_name_index.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | CREATE INDEX IDX_attributes_name_value ON attributes (name, value); | ||||||
							
								
								
									
										23
									
								
								db/migrations/0077__non_null_attribute_value.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,23 @@ | |||||||
|  | UPDATE attributes SET value = '' WHERE value IS NULL; | ||||||
|  |  | ||||||
|  | CREATE TABLE IF NOT EXISTS "attributes_mig" | ||||||
|  | ( | ||||||
|  |   attributeId TEXT PRIMARY KEY NOT NULL, | ||||||
|  |   noteId TEXT NOT NULL, | ||||||
|  |   name TEXT NOT NULL, | ||||||
|  |   value TEXT NOT NULL DEFAULT '', | ||||||
|  |   position INT NOT NULL DEFAULT 0, | ||||||
|  |   dateCreated TEXT NOT NULL, | ||||||
|  |   dateModified TEXT NOT NULL, | ||||||
|  |   isDeleted INT NOT NULL | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | INSERT INTO attributes_mig (attributeId, noteId, name, value, position, dateCreated, dateModified, isDeleted) | ||||||
|  |     SELECT attributeId, noteId, name, value, position, dateCreated, dateModified, isDeleted FROM attributes; | ||||||
|  |  | ||||||
|  | DROP TABLE attributes; | ||||||
|  |  | ||||||
|  | ALTER TABLE attributes_mig RENAME TO attributes; | ||||||
|  |  | ||||||
|  | CREATE INDEX IDX_attributes_noteId ON attributes (noteId); | ||||||
|  | CREATE INDEX IDX_attributes_name_value ON attributes (name, value); | ||||||
							
								
								
									
										1
									
								
								db/migrations/0078__javascript_type.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | UPDATE notes SET mime = 'application/javascript;env=frontend' WHERE type = 'code' AND mime = 'application/javascript'; | ||||||
							
								
								
									
										38
									
								
								db/migrations/0079__rename_note_tree.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,38 @@ | |||||||
|  | CREATE TABLE "branches" ( | ||||||
|  |   `branchId`	TEXT NOT NULL, | ||||||
|  |   `noteId`	TEXT NOT NULL, | ||||||
|  |   `parentNoteId`	TEXT NOT NULL, | ||||||
|  |   `notePosition`	INTEGER NOT NULL, | ||||||
|  |   `prefix`	TEXT, | ||||||
|  |   `isExpanded`	BOOLEAN, | ||||||
|  |   `isDeleted`	INTEGER NOT NULL DEFAULT 0, | ||||||
|  |   `dateModified`	TEXT NOT NULL, | ||||||
|  |   PRIMARY KEY(`branchId`) | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) | ||||||
|  |     SELECT noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified FROM note_tree; | ||||||
|  |  | ||||||
|  | DROP TABLE note_tree; | ||||||
|  |  | ||||||
|  | CREATE INDEX `IDX_branches_noteId` ON `branches` ( | ||||||
|  |   `noteId` | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` ( | ||||||
|  |   `noteId`, | ||||||
|  |   `parentNoteId` | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | CREATE TABLE `recent_notes_mig` ( | ||||||
|  |   `branchId` TEXT NOT NULL PRIMARY KEY, | ||||||
|  |   `notePath` TEXT NOT NULL, | ||||||
|  |   `dateAccessed` TEXT NOT NULL, | ||||||
|  |   isDeleted INT | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | INSERT INTO recent_notes_mig (branchId, notePath, dateAccessed, isDeleted) | ||||||
|  |     SELECT noteTreeId, notePath, dateAccessed, isDeleted FROM recent_notes; | ||||||
|  |  | ||||||
|  | DROP TABLE recent_notes; | ||||||
|  | ALTER TABLE recent_notes_mig RENAME TO recent_notes; | ||||||
							
								
								
									
										22
									
								
								db/migrations/0080__rename_attributes_to_labels.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,22 @@ | |||||||
|  | create table labels | ||||||
|  | ( | ||||||
|  |   labelId  TEXT not null primary key, | ||||||
|  |   noteId       TEXT not null, | ||||||
|  |   name         TEXT not null, | ||||||
|  |   value        TEXT default '' not null, | ||||||
|  |   position     INT  default 0 not null, | ||||||
|  |   dateCreated  TEXT not null, | ||||||
|  |   dateModified TEXT not null, | ||||||
|  |   isDeleted    INT  not null | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | create index IDX_labels_name_value | ||||||
|  |   on labels (name, value); | ||||||
|  |  | ||||||
|  | create index IDX_labels_noteId | ||||||
|  |   on labels (noteId); | ||||||
|  |  | ||||||
|  | INSERT INTO labels (labelId, noteId, name, "value", "position", dateCreated, dateModified, isDeleted) | ||||||
|  |   SELECT attributeId, noteId, name, "value", "position", dateCreated, dateModified, isDeleted FROM attributes; | ||||||
|  |  | ||||||
|  | DROP TABLE attributes; | ||||||
| @@ -0,0 +1 @@ | |||||||
|  | UPDATE options SET name = 'note_revision_snapshot_time_interval' WHERE name = 'history_snapshot_time_interval'; | ||||||
							
								
								
									
										14
									
								
								db/migrations/0082__camelCase_options.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,14 @@ | |||||||
|  | UPDATE "options" SET "name" = 'passwordVerificationHash' WHERE "name" = 'password_verification_hash'; | ||||||
|  | UPDATE "options" SET "name" = 'dbVersion' WHERE "name" = 'db_version'; | ||||||
|  | UPDATE "options" SET "name" = 'passwordDerivedKeySalt' WHERE "name" = 'password_derived_key_salt'; | ||||||
|  | UPDATE "options" SET "name" = 'documentId' WHERE "name" = 'document_id'; | ||||||
|  | UPDATE "options" SET "name" = 'lastSyncedPull' WHERE "name" = 'last_synced_pull'; | ||||||
|  | UPDATE "options" SET "name" = 'startNotePath' WHERE "name" = 'start_note_path'; | ||||||
|  | UPDATE "options" SET "name" = 'lastSyncedPush' WHERE "name" = 'last_synced_push'; | ||||||
|  | UPDATE "options" SET "name" = 'documentSecret' WHERE "name" = 'document_secret'; | ||||||
|  | UPDATE "options" SET "name" = 'lastBackupDate' WHERE "name" = 'last_backup_date'; | ||||||
|  | UPDATE "options" SET "name" = 'noteRevisionSnapshotTimeInterval' WHERE "name" = 'note_revision_snapshot_time_interval'; | ||||||
|  | UPDATE "options" SET "name" = 'protectedSessionTimeout' WHERE "name" = 'protected_session_timeout'; | ||||||
|  | UPDATE "options" SET "name" = 'encryptedDataKey' WHERE "name" = 'encrypted_data_key'; | ||||||
|  | UPDATE "options" SET "name" = 'encryptedDataKeyIv' WHERE "name" = 'encrypted_data_key_iv'; | ||||||
|  | UPDATE "options" SET "name" = 'passwordVerificationSalt' WHERE "name" = 'password_verification_salt'; | ||||||
							
								
								
									
										7
									
								
								db/migrations/0083__camelCase_labels.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | |||||||
|  | UPDATE labels SET name = 'disableVersioning' WHERE name = 'disable_versioning'; | ||||||
|  | UPDATE labels SET name = 'calendarRoot' WHERE name = 'calendar_root'; | ||||||
|  | UPDATE labels SET name = 'hideInAutocomplete' WHERE name = 'hide_in_autocomplete'; | ||||||
|  | UPDATE labels SET name = 'excludeFromExport' WHERE name = 'exclude_from_export'; | ||||||
|  | UPDATE labels SET name = 'manualTransactionHandling' WHERE name = 'manual_transaction_handling'; | ||||||
|  | UPDATE labels SET name = 'disableInclusion' WHERE name = 'disable_inclusion'; | ||||||
|  | UPDATE labels SET name = 'appCss' WHERE name = 'app_css'; | ||||||
							
								
								
									
										4
									
								
								db/migrations/0084__camelCase_reddit_ids.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | UPDATE labels SET name = 'redditId' WHERE name = 'reddit_id'; | ||||||
|  | UPDATE labels SET name = 'redditKind' WHERE name = 'reddit_kind'; | ||||||
|  | UPDATE labels SET name = 'redditCreatedUtc' WHERE name = 'reddit_created_utc'; | ||||||
|  | UPDATE labels SET name = 'redditDateNote' WHERE name = 'reddit_date_note'; | ||||||
							
								
								
									
										2
									
								
								db/migrations/0085__camelCase_run_values.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | |||||||
|  | UPDATE labels SET value = 'frontendStartup' WHERE value = 'frontend_startup'; | ||||||
|  | UPDATE labels SET value = 'backendStartup' WHERE value = 'backend_startup'; | ||||||
							
								
								
									
										7
									
								
								db/migrations/0086__camelCase_custom_labels.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | |||||||
|  | UPDATE labels SET name = 'dateData' WHERE name = 'date_data'; | ||||||
|  | UPDATE labels SET name = 'dateNote' WHERE name = 'date_note'; | ||||||
|  | UPDATE labels SET name = 'fileSize' WHERE name = 'file_size'; | ||||||
|  | UPDATE labels SET name = 'hideInAutocomplete' WHERE name = 'hide_in_autocomplete'; | ||||||
|  | UPDATE labels SET name = 'monthNote' WHERE name = 'month_note'; | ||||||
|  | UPDATE labels SET name = 'originalFileName' WHERE name = 'original_file_name'; | ||||||
|  | UPDATE labels SET name = 'yearNote' WHERE name = 'year_note'; | ||||||
							
								
								
									
										5
									
								
								db/migrations/0087__add_type_mime_to_note_revision.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,5 @@ | |||||||
|  | ALTER TABLE note_revisions ADD type TEXT DEFAULT '' NOT NULL; | ||||||
|  | ALTER TABLE note_revisions ADD mime TEXT DEFAULT '' NOT NULL; | ||||||
|  |  | ||||||
|  | UPDATE note_revisions SET type = (SELECT type FROM notes WHERE notes.noteId = note_revisions.noteId); | ||||||
|  | UPDATE note_revisions SET mime = (SELECT mime FROM notes WHERE notes.noteId = note_revisions.noteId); | ||||||
							
								
								
									
										34
									
								
								db/migrations/0088__non_null_note_title_content.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,34 @@ | |||||||
|  | CREATE TABLE event_logc027 | ||||||
|  | ( | ||||||
|  |   id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, | ||||||
|  |   noteId TEXT, | ||||||
|  |   comment TEXT, | ||||||
|  |   dateAdded TEXT NOT NULL | ||||||
|  | ); | ||||||
|  | INSERT INTO event_logc027(id, noteId, comment, dateAdded) SELECT id, noteId, comment, dateAdded FROM event_log; | ||||||
|  | DROP TABLE event_log; | ||||||
|  | ALTER TABLE event_logc027 RENAME TO event_log; | ||||||
|  |  | ||||||
|  | CREATE TABLE IF NOT EXISTS "notes_mig" ( | ||||||
|  |   `noteId`	TEXT NOT NULL, | ||||||
|  |   `title`	TEXT NOT NULL DEFAULT "unnamed", | ||||||
|  |   `content`	TEXT NOT NULL DEFAULT "", | ||||||
|  |   `isProtected`	INT NOT NULL DEFAULT 0, | ||||||
|  |   `isDeleted`	INT NOT NULL DEFAULT 0, | ||||||
|  |   `dateCreated`	TEXT NOT NULL, | ||||||
|  |   `dateModified`	TEXT NOT NULL, | ||||||
|  |   type TEXT NOT NULL DEFAULT 'text', | ||||||
|  |   mime TEXT NOT NULL DEFAULT 'text/html', | ||||||
|  |   PRIMARY KEY(`noteId`) | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | INSERT INTO notes_mig (noteId, title, content, isProtected, isDeleted, dateCreated, dateModified, type, mime) | ||||||
|  |     SELECT noteId, title, content, isProtected, isDeleted, dateCreated, dateModified, type, mime FROM notes; | ||||||
|  |  | ||||||
|  | DROP TABLE notes; | ||||||
|  |  | ||||||
|  | ALTER TABLE notes_mig RENAME TO notes; | ||||||
|  |  | ||||||
|  | CREATE INDEX `IDX_notes_isDeleted` ON `notes` ( | ||||||
|  |   `isDeleted` | ||||||
|  | ); | ||||||
							
								
								
									
										5
									
								
								db/migrations/0089__add_root_branch.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,5 @@ | |||||||
|  | INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, dateModified) | ||||||
|  |     VALUES ('root', 'root', 'none', 0, null, 1, '2018-01-01T00:00:00.000Z'); | ||||||
|  |  | ||||||
|  | INSERT INTO sync (entityName, entityId, sourceId, syncDate) | ||||||
|  |     VALUES ('branches' ,'root', 'SYNC_FILL', '2018-01-01T00:00:00.000Z'); | ||||||
							
								
								
									
										1
									
								
								db/migrations/0090__branch_index.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId); | ||||||
							
								
								
									
										2
									
								
								db/migrations/0091__drop_isDeleted_index.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | |||||||
|  | -- index confuses planner and is mostly useless anyway since we're mostly used in non-deleted notes (which are presumably majority) | ||||||
|  | DROP INDEX IDX_notes_isDeleted; | ||||||
							
								
								
									
										2
									
								
								db/migrations/0092__add_type_index.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | |||||||
|  | create index IDX_notes_type | ||||||
|  |   on notes (type); | ||||||
							
								
								
									
										9
									
								
								db/migrations/0093__add_hash_field.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,9 @@ | |||||||
|  | ALTER TABLE notes ADD hash TEXT DEFAULT "" NOT NULL; | ||||||
|  | ALTER TABLE branches ADD hash TEXT DEFAULT "" NOT NULL; | ||||||
|  | ALTER TABLE note_revisions ADD hash TEXT DEFAULT "" NOT NULL; | ||||||
|  | ALTER TABLE recent_notes ADD hash TEXT DEFAULT "" NOT NULL; | ||||||
|  | ALTER TABLE options ADD hash TEXT DEFAULT "" NOT NULL; | ||||||
|  | ALTER TABLE note_images ADD hash TEXT DEFAULT "" NOT NULL; | ||||||
|  | ALTER TABLE images ADD hash TEXT DEFAULT "" NOT NULL; | ||||||
|  | ALTER TABLE labels ADD hash TEXT DEFAULT "" NOT NULL; | ||||||
|  | ALTER TABLE api_tokens ADD hash TEXT DEFAULT "" NOT NULL; | ||||||
							
								
								
									
										30
									
								
								db/migrations/0094__unify_auditing_fields.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,30 @@ | |||||||
|  | ALTER TABLE branches ADD dateCreated TEXT NOT NULL DEFAULT '1970-01-01T00:00:00.000Z'; | ||||||
|  |  | ||||||
|  | CREATE TABLE `event_log_mig` ( | ||||||
|  |   `id`	INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, | ||||||
|  |   `noteId`	TEXT, | ||||||
|  |   `comment`	TEXT, | ||||||
|  |   `dateCreated`	TEXT NOT NULL | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | INSERT INTO event_log_mig (id, noteId, comment, dateCreated) | ||||||
|  | SELECT id, noteId, comment, dateAdded FROM event_log; | ||||||
|  |  | ||||||
|  | DROP TABLE event_log; | ||||||
|  | ALTER TABLE event_log_mig RENAME TO event_log; | ||||||
|  |  | ||||||
|  | ALTER TABLE options ADD dateCreated TEXT NOT NULL DEFAULT '1970-01-01T00:00:00.000Z'; | ||||||
|  |  | ||||||
|  | CREATE TABLE `recent_notes_mig` ( | ||||||
|  |   `branchId` TEXT NOT NULL PRIMARY KEY, | ||||||
|  |   `notePath` TEXT NOT NULL, | ||||||
|  |   hash TEXT DEFAULT "" NOT NULL, | ||||||
|  |   `dateCreated` TEXT NOT NULL, | ||||||
|  |   isDeleted INT | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | INSERT INTO recent_notes_mig (branchId, notePath, hash, dateCreated, isDeleted) | ||||||
|  | SELECT branchId, notePath, hash, dateAccessed, isDeleted FROM recent_notes; | ||||||
|  |  | ||||||
|  | DROP TABLE recent_notes; | ||||||
|  | ALTER TABLE recent_notes_mig RENAME TO recent_notes; | ||||||
							
								
								
									
										1
									
								
								db/migrations/0095__mime_type_for_render.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | UPDATE notes SET mime = 'text/html' WHERE type = 'render'; | ||||||
							
								
								
									
										29
									
								
								db/migrations/0096__unify_surrogate_keys.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,29 @@ | |||||||
|  | CREATE TABLE `event_log_mig` ( | ||||||
|  |   `eventId`	TEXT NOT NULL PRIMARY KEY, | ||||||
|  |   `noteId`	TEXT, | ||||||
|  |   `comment`	TEXT, | ||||||
|  |   `dateCreated`	TEXT NOT NULL | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | INSERT INTO event_log_mig (eventId, noteId, comment, dateCreated) | ||||||
|  | SELECT id, noteId, comment, dateCreated FROM event_log; | ||||||
|  |  | ||||||
|  | DROP TABLE event_log; | ||||||
|  | ALTER TABLE event_log_mig RENAME TO event_log; | ||||||
|  |  | ||||||
|  | create table options_mig | ||||||
|  | ( | ||||||
|  |   optionId TEXT NOT NULL PRIMARY KEY, | ||||||
|  |   name TEXT not null, | ||||||
|  |   value TEXT, | ||||||
|  |   dateModified INT, | ||||||
|  |   isSynced INTEGER default 0 not null, | ||||||
|  |   hash TEXT default "" not null, | ||||||
|  |   dateCreated TEXT default '1970-01-01T00:00:00.000Z' not null | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | INSERT INTO options_mig (optionId, name, value, dateModified, isSynced, hash, dateCreated) | ||||||
|  |   SELECT name || "_key", name, value, dateModified, isSynced, hash, dateCreated FROM options; | ||||||
|  |  | ||||||
|  | DROP TABLE options; | ||||||
|  | ALTER TABLE options_mig RENAME TO options; | ||||||
							
								
								
									
										2
									
								
								db/migrations/0097__add_zoomFactor.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | |||||||
|  | INSERT INTO options (optionId, name, value, dateCreated, dateModified, isSynced) | ||||||
|  | VALUES ('zoomFactor_key', 'zoomFactor', '1.0', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0); | ||||||
							
								
								
									
										1
									
								
								db/migrations/0098__rename_hideInAutocomplete.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | UPDATE labels SET name = 'archived' WHERE name = 'hideInAutocomplete' | ||||||
							
								
								
									
										2
									
								
								db/migrations/0099__add_theme_option.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | |||||||
|  | INSERT INTO options (optionId, name, value, dateCreated, dateModified, isSynced) | ||||||
|  | VALUES ('theme_key', 'theme', 'white', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0); | ||||||
							
								
								
									
										15
									
								
								db/migrations/0100__remove_optionId.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,15 @@ | |||||||
|  | create table options_mig | ||||||
|  | ( | ||||||
|  |   name TEXT not null PRIMARY KEY, | ||||||
|  |   value TEXT, | ||||||
|  |   dateModified INT, | ||||||
|  |   isSynced INTEGER default 0 not null, | ||||||
|  |   hash TEXT default "" not null, | ||||||
|  |   dateCreated TEXT default '1970-01-01T00:00:00.000Z' not null | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | INSERT INTO options_mig (name, value, dateModified, isSynced, hash, dateCreated) | ||||||
|  | SELECT name, value, dateModified, isSynced, hash, dateCreated FROM options; | ||||||
|  |  | ||||||
|  | DROP TABLE options; | ||||||
|  | ALTER TABLE options_mig RENAME TO options; | ||||||
							
								
								
									
										8
									
								
								db/migrations/0101__add_sync_options.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,8 @@ | |||||||
|  | INSERT INTO options (name, value, dateCreated, dateModified, isSynced) | ||||||
|  | VALUES ('syncServerHost', '', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0); | ||||||
|  |  | ||||||
|  | INSERT INTO options (name, value, dateCreated, dateModified, isSynced) | ||||||
|  | VALUES ('syncServerTimeout', '5000', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0); | ||||||
|  |  | ||||||
|  | INSERT INTO options (name, value, dateCreated, dateModified, isSynced) | ||||||
|  | VALUES ('syncProxy', '', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0); | ||||||
							
								
								
									
										2
									
								
								db/migrations/0102__fix_sync_entityIds.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | |||||||
|  | DELETE FROM sync WHERE entityName = 'note_tree'; | ||||||
|  | DELETE FROM sync WHERE entityName = 'attributes'; | ||||||
							
								
								
									
										2
									
								
								db/migrations/0103__add_initialized_option.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | |||||||
|  | INSERT INTO options (name, value, dateCreated, dateModified, isSynced) | ||||||
|  | VALUES ('initialized', 'true', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0); | ||||||
							
								
								
									
										4
									
								
								db/migrations/0104__fill_sync_rows_for_options.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | const syncTableService = require('../../src/services/sync_table'); | ||||||
|  |  | ||||||
|  | // options has not been filled so far which caused problems with clean-slate sync. | ||||||
|  | module.exports = async () => await syncTableService.fillAllSyncRows(); | ||||||
| @@ -0,0 +1,2 @@ | |||||||
|  | UPDATE notes SET content = '' WHERE isDeleted = 1; | ||||||
|  | UPDATE note_revisions SET content = '' WHERE (SELECT isDeleted FROM notes WHERE noteId = note_revisions.noteId) = 1; | ||||||
							
								
								
									
										15
									
								
								db/migrations/0106__add_relations_table.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,15 @@ | |||||||
|  | CREATE TABLE relations | ||||||
|  | ( | ||||||
|  |     relationId  TEXT not null primary key, | ||||||
|  |     sourceNoteId       TEXT not null, | ||||||
|  |     name         TEXT not null, | ||||||
|  |     targetNoteId        TEXT not null, | ||||||
|  |     position     INT  default 0 not null, | ||||||
|  |     dateCreated  TEXT not null, | ||||||
|  |     dateModified TEXT not null, | ||||||
|  |     isDeleted    INT  not null | ||||||
|  |   , hash TEXT DEFAULT "" NOT NULL); | ||||||
|  | CREATE INDEX IDX_relation_sourceNoteId | ||||||
|  |   on relations (sourceNoteId); | ||||||
|  | CREATE INDEX IDX_relation_targetNoteId | ||||||
|  |   on relations (targetNoteId); | ||||||
| @@ -0,0 +1 @@ | |||||||
|  | ALTER TABLE relations ADD isInheritable int DEFAULT 0 NULL; | ||||||
							
								
								
									
										9
									
								
								db/migrations/0108__new_backup_options.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,9 @@ | |||||||
|  | UPDATE options SET name = 'lastDailyBackupDate' WHERE name = 'lastBackupDate'; | ||||||
|  |  | ||||||
|  | INSERT INTO options (name, value, dateCreated, dateModified, isSynced) | ||||||
|  | VALUES ('lastWeeklyBackupDate', '2018-07-29T18:31:00.874Z', '2018-07-29T18:31:00.874Z', '2018-07-29T18:31:00.874Z', 0); | ||||||
|  |  | ||||||
|  | INSERT INTO options (name, value, dateCreated, dateModified, isSynced) | ||||||
|  | VALUES ('lastMonthlyBackupDate', '2018-07-29T18:31:00.874Z', '2018-07-29T18:31:00.874Z', '2018-07-29T18:31:00.874Z', 0); | ||||||
|  |  | ||||||
|  | -- these options are not synced so no need to fix sync rows | ||||||
							
								
								
									
										27
									
								
								db/migrations/0109__create_attributes.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,27 @@ | |||||||
|  | create table attributes | ||||||
|  | ( | ||||||
|  |   attributeId      TEXT not null primary key, | ||||||
|  |   noteId       TEXT not null, | ||||||
|  |   type         TEXT not null, | ||||||
|  |   name         TEXT not null, | ||||||
|  |   value        TEXT default '' not null, | ||||||
|  |   position     INT  default 0 not null, | ||||||
|  |   dateCreated  TEXT not null, | ||||||
|  |   dateModified TEXT not null, | ||||||
|  |   isDeleted    INT  not null, | ||||||
|  |   hash         TEXT default "" not null); | ||||||
|  |  | ||||||
|  | create index IDX_attributes_name_value | ||||||
|  |   on attributes (name, value); | ||||||
|  |  | ||||||
|  | create index IDX_attributes_value | ||||||
|  |   on attributes (value); | ||||||
|  |  | ||||||
|  | create index IDX_attributes_noteId | ||||||
|  |   on attributes (noteId); | ||||||
|  |  | ||||||
|  | INSERT INTO attributes (attributeId, noteId, type, name, value, position, dateCreated, dateModified, isDeleted, hash) | ||||||
|  | SELECT labelId, noteId, 'label', name, value, position, dateCreated, dateModified, isDeleted, hash FROM labels; | ||||||
|  |  | ||||||
|  | INSERT INTO attributes (attributeId, noteId, type, name, value, position, dateCreated, dateModified, isDeleted, hash) | ||||||
|  | SELECT relationId, sourceNoteId, 'relation', name, targetNoteId, position, dateCreated, dateModified, isDeleted, hash FROM relations; | ||||||
							
								
								
									
										1
									
								
								db/migrations/0110__add_isInheritable_to_attributes.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | ALTER TABLE attributes ADD isInheritable int DEFAULT 0 NULL; | ||||||
							
								
								
									
										4
									
								
								db/migrations/0111__cleanup_labels_and_relations.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | DROP TABLE relations; | ||||||
|  | DROP TABLE labels; | ||||||
|  |  | ||||||
|  | DELETE FROM sync WHERE entityName = 'relations' OR entityName = 'labels'; | ||||||
							
								
								
									
										1
									
								
								db/migrations/0112__rename_inheritAttributes.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | UPDATE attributes SET name = 'template' WHERE name = 'inheritAttributes'; | ||||||
							
								
								
									
										220
									
								
								db/schema.sql
									
									
									
									
									
								
							
							
						
						| @@ -1,119 +1,133 @@ | |||||||
| CREATE TABLE IF NOT EXISTS "options" ( |  | ||||||
| 	`opt_name`	TEXT NOT NULL PRIMARY KEY, |  | ||||||
| 	`opt_value`	TEXT, |  | ||||||
| 	`date_modified` INT |  | ||||||
| , is_synced INTEGER NOT NULL DEFAULT 0); |  | ||||||
| CREATE TABLE IF NOT EXISTS "sync" ( | CREATE TABLE IF NOT EXISTS "sync" ( | ||||||
|     `id`	INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, |   `id`	INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, | ||||||
|     `entity_name`	TEXT NOT NULL, |   `entityName`	TEXT NOT NULL, | ||||||
|     `entity_id`	TEXT NOT NULL, |   `entityId`	TEXT NOT NULL, | ||||||
|     `source_id` TEXT NOT NULL, |   `sourceId` TEXT NOT NULL, | ||||||
|     `sync_date`	TEXT NOT NULL); |   `syncDate`	TEXT NOT NULL); | ||||||
| CREATE UNIQUE INDEX `IDX_sync_entity_name_id` ON `sync` ( | CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` ( | ||||||
|   `entity_name`, |   `entityName`, | ||||||
|   `entity_id` |   `entityId` | ||||||
| ); | ); | ||||||
| CREATE INDEX `IDX_sync_sync_date` ON `sync` ( | CREATE INDEX `IDX_sync_syncDate` ON `sync` ( | ||||||
|   `sync_date` |   `syncDate` | ||||||
| ); | ); | ||||||
| CREATE TABLE `source_ids` ( | CREATE TABLE IF NOT EXISTS "source_ids" ( | ||||||
|   `source_id`	TEXT NOT NULL, |   `sourceId`	TEXT NOT NULL, | ||||||
|   `date_created`	TEXT NOT NULL, |   `dateCreated`	TEXT NOT NULL, | ||||||
|   PRIMARY KEY(`source_id`) |   PRIMARY KEY(`sourceId`) | ||||||
| ); | ); | ||||||
| CREATE TABLE IF NOT EXISTS "notes" ( | CREATE TABLE IF NOT EXISTS "note_revisions" ( | ||||||
|     `note_id`	TEXT NOT NULL, |   `noteRevisionId`	TEXT NOT NULL PRIMARY KEY, | ||||||
|     `note_title`	TEXT, |   `noteId`	TEXT NOT NULL, | ||||||
|     `note_text`	TEXT, |   `title`	TEXT, | ||||||
|     `is_protected`	INT NOT NULL DEFAULT 0, |   `content`	TEXT, | ||||||
|     `is_deleted`	INT NOT NULL DEFAULT 0, |   `isProtected`	INT NOT NULL DEFAULT 0, | ||||||
|     `date_created`	TEXT NOT NULL, |   `dateModifiedFrom` TEXT NOT NULL, | ||||||
|     `date_modified`	TEXT NOT NULL, type TEXT NOT NULL DEFAULT 'text', mime TEXT NOT NULL DEFAULT 'text/html', |   `dateModifiedTo` TEXT NOT NULL | ||||||
|     PRIMARY KEY(`note_id`) | , type TEXT DEFAULT '' NOT NULL, mime TEXT DEFAULT '' NOT NULL, hash TEXT DEFAULT "" NOT NULL); | ||||||
|  | CREATE INDEX `IDX_note_revisions_noteId` ON `note_revisions` ( | ||||||
|  |   `noteId` | ||||||
| ); | ); | ||||||
| CREATE INDEX `IDX_notes_is_deleted` ON `notes` ( | CREATE INDEX `IDX_note_revisions_dateModifiedFrom` ON `note_revisions` ( | ||||||
|     `is_deleted` |   `dateModifiedFrom` | ||||||
| ); | ); | ||||||
| CREATE TABLE `event_log` ( | CREATE INDEX `IDX_note_revisions_dateModifiedTo` ON `note_revisions` ( | ||||||
|     `id`	INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, |   `dateModifiedTo` | ||||||
|     `note_id`	TEXT, |  | ||||||
|     `comment`	TEXT, |  | ||||||
|     `date_added`	TEXT NOT NULL, |  | ||||||
|     FOREIGN KEY(note_id) REFERENCES notes(note_id) |  | ||||||
| ); | ); | ||||||
| CREATE TABLE IF NOT EXISTS "notes_tree" ( | CREATE TABLE IF NOT EXISTS "images" | ||||||
|   `note_tree_id`	TEXT NOT NULL, |  | ||||||
|   `note_id`	TEXT NOT NULL, |  | ||||||
|   `parent_note_id`	TEXT NOT NULL, |  | ||||||
|   `note_position`	INTEGER NOT NULL, |  | ||||||
|   `prefix`	TEXT, |  | ||||||
|   `is_expanded`	BOOLEAN, |  | ||||||
|   `is_deleted`	INTEGER NOT NULL DEFAULT 0, |  | ||||||
|   `date_modified`	TEXT NOT NULL, |  | ||||||
|   PRIMARY KEY(`note_tree_id`) |  | ||||||
| ); |  | ||||||
| CREATE INDEX `IDX_notes_tree_note_id` ON `notes_tree` ( |  | ||||||
|   `note_id` |  | ||||||
| ); |  | ||||||
| CREATE TABLE IF NOT EXISTS "notes_history" ( |  | ||||||
|   `note_history_id`	TEXT NOT NULL PRIMARY KEY, |  | ||||||
|   `note_id`	TEXT NOT NULL, |  | ||||||
|   `note_title`	TEXT, |  | ||||||
|   `note_text`	TEXT, |  | ||||||
|   `is_protected`	INT NOT NULL DEFAULT 0, |  | ||||||
|   `date_modified_from` TEXT NOT NULL, |  | ||||||
|   `date_modified_to` TEXT NOT NULL |  | ||||||
| ); |  | ||||||
| CREATE INDEX `IDX_notes_history_note_id` ON `notes_history` ( |  | ||||||
|   `note_id` |  | ||||||
| ); |  | ||||||
| CREATE INDEX `IDX_notes_history_note_date_modified_from` ON `notes_history` ( |  | ||||||
|   `date_modified_from` |  | ||||||
| ); |  | ||||||
| CREATE INDEX `IDX_notes_history_note_date_modified_to` ON `notes_history` ( |  | ||||||
|   `date_modified_to` |  | ||||||
| ); |  | ||||||
| CREATE TABLE `recent_notes` ( |  | ||||||
|   `note_tree_id` TEXT NOT NULL PRIMARY KEY, |  | ||||||
|   `note_path` TEXT NOT NULL, |  | ||||||
|   `date_accessed` TEXT NOT NULL, |  | ||||||
|   is_deleted INT |  | ||||||
| ); |  | ||||||
| CREATE INDEX `IDX_notes_tree_note_id_parent_note_id` ON `notes_tree` ( |  | ||||||
|   `note_id`, |  | ||||||
|   `parent_note_id` |  | ||||||
| ); |  | ||||||
| CREATE TABLE images |  | ||||||
| ( | ( | ||||||
|   image_id TEXT PRIMARY KEY NOT NULL, |   imageId TEXT PRIMARY KEY NOT NULL, | ||||||
|   format TEXT NOT NULL, |   format TEXT NOT NULL, | ||||||
|   checksum TEXT NOT NULL, |   checksum TEXT NOT NULL, | ||||||
|   name TEXT NOT NULL, |   name TEXT NOT NULL, | ||||||
|   data BLOB, |   data BLOB, | ||||||
|   is_deleted INT NOT NULL DEFAULT 0, |   isDeleted INT NOT NULL DEFAULT 0, | ||||||
|   date_modified TEXT NOT NULL, |   dateModified TEXT NOT NULL, | ||||||
|   date_created TEXT NOT NULL |   dateCreated TEXT NOT NULL | ||||||
| ); | , hash TEXT DEFAULT "" NOT NULL); | ||||||
| CREATE TABLE notes_image | CREATE TABLE note_images | ||||||
| ( | ( | ||||||
|   note_image_id TEXT PRIMARY KEY NOT NULL, |   noteImageId TEXT PRIMARY KEY NOT NULL, | ||||||
|   note_id TEXT NOT NULL, |   noteId TEXT NOT NULL, | ||||||
|   image_id TEXT NOT NULL, |   imageId TEXT NOT NULL, | ||||||
|   is_deleted INT NOT NULL DEFAULT 0, |   isDeleted INT NOT NULL DEFAULT 0, | ||||||
|   date_modified TEXT NOT NULL, |   dateModified TEXT NOT NULL, | ||||||
|   date_created TEXT NOT NULL |   dateCreated TEXT NOT NULL | ||||||
|  | , hash TEXT DEFAULT "" NOT NULL); | ||||||
|  | CREATE INDEX IDX_note_images_noteId ON note_images (noteId); | ||||||
|  | CREATE INDEX IDX_note_images_imageId ON note_images (imageId); | ||||||
|  | CREATE INDEX IDX_note_images_noteId_imageId ON note_images (noteId, imageId); | ||||||
|  | CREATE TABLE IF NOT EXISTS "api_tokens" | ||||||
|  | ( | ||||||
|  |   apiTokenId TEXT PRIMARY KEY NOT NULL, | ||||||
|  |   token TEXT NOT NULL, | ||||||
|  |   dateCreated TEXT NOT NULL, | ||||||
|  |   isDeleted INT NOT NULL DEFAULT 0 | ||||||
|  | , hash TEXT DEFAULT "" NOT NULL); | ||||||
|  | CREATE TABLE IF NOT EXISTS "branches" ( | ||||||
|  |   `branchId`	TEXT NOT NULL, | ||||||
|  |   `noteId`	TEXT NOT NULL, | ||||||
|  |   `parentNoteId`	TEXT NOT NULL, | ||||||
|  |   `notePosition`	INTEGER NOT NULL, | ||||||
|  |   `prefix`	TEXT, | ||||||
|  |   `isExpanded`	BOOLEAN, | ||||||
|  |   `isDeleted`	INTEGER NOT NULL DEFAULT 0, | ||||||
|  |   `dateModified`	TEXT NOT NULL, hash TEXT DEFAULT "" NOT NULL, dateCreated TEXT NOT NULL DEFAULT '1970-01-01T00:00:00.000Z', | ||||||
|  |   PRIMARY KEY(`branchId`) | ||||||
|  | ); | ||||||
|  | CREATE INDEX `IDX_branches_noteId` ON `branches` ( | ||||||
|  |   `noteId` | ||||||
|  | ); | ||||||
|  | CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` ( | ||||||
|  |   `noteId`, | ||||||
|  |   `parentNoteId` | ||||||
|  | ); | ||||||
|  | CREATE TABLE IF NOT EXISTS "notes" ( | ||||||
|  |   `noteId`	TEXT NOT NULL, | ||||||
|  |   `title`	TEXT NOT NULL DEFAULT "unnamed", | ||||||
|  |   `content`	TEXT NOT NULL DEFAULT "", | ||||||
|  |   `isProtected`	INT NOT NULL DEFAULT 0, | ||||||
|  |   `isDeleted`	INT NOT NULL DEFAULT 0, | ||||||
|  |   `dateCreated`	TEXT NOT NULL, | ||||||
|  |   `dateModified`	TEXT NOT NULL, | ||||||
|  |   type TEXT NOT NULL DEFAULT 'text', | ||||||
|  |   mime TEXT NOT NULL DEFAULT 'text/html', hash TEXT DEFAULT "" NOT NULL, | ||||||
|  |   PRIMARY KEY(`noteId`) | ||||||
|  | ); | ||||||
|  | CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId); | ||||||
|  | CREATE INDEX IDX_notes_type | ||||||
|  |   on notes (type); | ||||||
|  | CREATE TABLE IF NOT EXISTS "recent_notes" ( | ||||||
|  |   `branchId` TEXT NOT NULL PRIMARY KEY, | ||||||
|  |   `notePath` TEXT NOT NULL, | ||||||
|  |   hash TEXT DEFAULT "" NOT NULL, | ||||||
|  |   `dateCreated` TEXT NOT NULL, | ||||||
|  |   isDeleted INT | ||||||
|  | ); | ||||||
|  | CREATE TABLE IF NOT EXISTS "event_log" ( | ||||||
|  |   `eventId`	TEXT NOT NULL PRIMARY KEY, | ||||||
|  |   `noteId`	TEXT, | ||||||
|  |   `comment`	TEXT, | ||||||
|  |   `dateCreated`	TEXT NOT NULL | ||||||
|  | ); | ||||||
|  | CREATE TABLE IF NOT EXISTS "options" | ||||||
|  | ( | ||||||
|  |   name TEXT not null PRIMARY KEY, | ||||||
|  |   value TEXT, | ||||||
|  |   dateModified INT, | ||||||
|  |   isSynced INTEGER default 0 not null, | ||||||
|  |   hash TEXT default "" not null, | ||||||
|  |   dateCreated TEXT default '1970-01-01T00:00:00.000Z' not null | ||||||
| ); | ); | ||||||
| CREATE INDEX notes_image_note_id_index ON notes_image (note_id); |  | ||||||
| CREATE INDEX notes_image_image_id_index ON notes_image (image_id); |  | ||||||
| CREATE INDEX notes_image_note_id_image_id_index ON notes_image (note_id, image_id); |  | ||||||
| CREATE TABLE attributes | CREATE TABLE attributes | ||||||
| ( | ( | ||||||
|   attribute_id TEXT PRIMARY KEY NOT NULL, |   attributeId      TEXT not null primary key, | ||||||
|   note_id TEXT NOT NULL, |   noteId       TEXT not null, | ||||||
|   name TEXT NOT NULL, |   type         TEXT not null, | ||||||
|   value TEXT, |   name         TEXT not null, | ||||||
|   date_created TEXT NOT NULL, |   value        TEXT default '' not null, | ||||||
|   date_modified TEXT NOT NULL |   position     INT  default 0 not null, | ||||||
| ); |   dateCreated  TEXT not null, | ||||||
| CREATE INDEX attributes_note_id_index ON attributes (note_id); |   dateModified TEXT not null, | ||||||
| CREATE UNIQUE INDEX attributes_note_id_name_index ON attributes (note_id, name); |   isDeleted    INT  not null, | ||||||
|  |   hash         TEXT default "" not null, isInheritable int DEFAULT 0 NULL); | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								electron.js
									
									
									
									
									
								
							
							
						
						| @@ -2,10 +2,12 @@ | |||||||
|  |  | ||||||
| const electron = require('electron'); | const electron = require('electron'); | ||||||
| const path = require('path'); | const path = require('path'); | ||||||
| const config = require('./src/services/config'); | const log = require('./src/services/log'); | ||||||
| const url = require("url"); | const url = require("url"); | ||||||
|  | const port = require('./src/services/port'); | ||||||
|  |  | ||||||
| const app = electron.app; | const app = electron.app; | ||||||
|  | const globalShortcut = electron.globalShortcut; | ||||||
|  |  | ||||||
| // Adds debug features like hotkeys for triggering dev tools and reload | // Adds debug features like hotkeys for triggering dev tools and reload | ||||||
| require('electron-debug')(); | require('electron-debug')(); | ||||||
| @@ -13,28 +15,28 @@ require('electron-debug')(); | |||||||
| // Prevent window being garbage collected | // Prevent window being garbage collected | ||||||
| let mainWindow; | let mainWindow; | ||||||
|  |  | ||||||
|  | require('electron-dl')({ saveAs: true }); | ||||||
|  |  | ||||||
| function onClosed() { | function onClosed() { | ||||||
|     // Dereference the window |     // Dereference the window | ||||||
|     // For multiple windows store them in an array |     // For multiple windows store them in an array | ||||||
|     mainWindow = null; |     mainWindow = null; | ||||||
| } | } | ||||||
|  |  | ||||||
| function createMainWindow() { | async function createMainWindow() { | ||||||
|     const win = new electron.BrowserWindow({ |     const win = new electron.BrowserWindow({ | ||||||
|         width: 1200, |         width: 1200, | ||||||
|         height: 900, |         height: 900, | ||||||
|         title: 'Trilium Notes', |         title: 'Trilium Notes', | ||||||
|         icon: path.join(__dirname, 'public/images/app-icons/png/256x256.png') |         icon: path.join(__dirname, 'src/public/images/app-icons/png/256x256.png') | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     const port = config['Network']['port'] || '3000'; |  | ||||||
|  |  | ||||||
|     win.setMenu(null); |     win.setMenu(null); | ||||||
|     win.loadURL('http://localhost:' + port); |     win.loadURL('http://localhost:' + await port); | ||||||
|     win.on('closed', onClosed); |     win.on('closed', onClosed); | ||||||
|  |  | ||||||
|     win.webContents.on('new-window', (e, url) => { |     win.webContents.on('new-window', (e, url) => { | ||||||
|         if (url !== mainWindow.webContents.getURL()) { |         if (url !== win.webContents.getURL()) { | ||||||
|             e.preventDefault(); |             e.preventDefault(); | ||||||
|             require('electron').shell.openExternal(url); |             require('electron').shell.openExternal(url); | ||||||
|         } |         } | ||||||
| @@ -67,6 +69,26 @@ app.on('activate', () => { | |||||||
|  |  | ||||||
| app.on('ready', () => { | app.on('ready', () => { | ||||||
|     mainWindow = createMainWindow(); |     mainWindow = createMainWindow(); | ||||||
|  |  | ||||||
|  |     const result = globalShortcut.register('CommandOrControl+Alt+P', async () => { | ||||||
|  |         const dateNoteService = require('./src/services/date_notes'); | ||||||
|  |         const dateUtils = require('./src/services/date_utils'); | ||||||
|  |  | ||||||
|  |         const parentNote = await dateNoteService.getDateNote(dateUtils.nowDate()); | ||||||
|  |  | ||||||
|  |         // window may be hidden / not in focus | ||||||
|  |         mainWindow.focus(); | ||||||
|  |  | ||||||
|  |         mainWindow.webContents.send('create-day-sub-note', parentNote.noteId); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     if (!result) { | ||||||
|  |         log.error("Could not register global shortcut CTRL+ALT+P"); | ||||||
|  |     } | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | app.on('will-quit', () => { | ||||||
|  |     globalShortcut.unregisterAll(); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| require('./src/www'); | require('./src/www'); | ||||||
|   | |||||||
							
								
								
									
										7534
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
							
								
								
									
										120
									
								
								package.json
									
									
									
									
									
								
							
							
						
						| @@ -1,70 +1,84 @@ | |||||||
| { | { | ||||||
|   "name": "trilium", |   "name": "trilium", | ||||||
|   "description": "Trilium Notes", |   "description": "Trilium Notes", | ||||||
|   "version": "0.5.2-beta", |   "version": "0.20.0", | ||||||
|   "license": "AGPL-3.0-only", |   "license": "AGPL-3.0-only", | ||||||
|   "main": "electron.js", |   "main": "electron.js", | ||||||
|  |   "bin": { | ||||||
|  |     "trilium": "./src/www" | ||||||
|  |   }, | ||||||
|   "repository": { |   "repository": { | ||||||
|     "type": "git", |     "type": "git", | ||||||
|     "url": "https://github.com/zadam/trilium.git" |     "url": "https://github.com/zadam/trilium.git" | ||||||
|   }, |   }, | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "start": "node ./bin/www", |     "start": "node ./src/www", | ||||||
|     "test-electron": "xo", |     "test-electron": "xo", | ||||||
|     "rebuild-electron": "electron-rebuild", |     "rebuild-electron": "electron-rebuild", | ||||||
|     "start-electron": "electron src/electron", |     "start-electron": "electron . --disable-gpu", | ||||||
|     "build-electron": "electron-packager . --out=dist --asar --overwrite --all", |     "build-electron": "electron-packager . --out=dist --asar --overwrite --platform=win32,linux --arch=ia32,x64 --app-version=", | ||||||
|     "start-forge": "electron-forge start", |     "start-forge": "electron-forge start", | ||||||
|     "package-forge": "electron-forge package", |     "package-forge": "electron-forge package", | ||||||
|     "make-forge": "electron-forge make", |     "make-forge": "electron-forge make", | ||||||
|     "publish-forge": "electron-forge publish" |     "publish-forge": "electron-forge publish", | ||||||
|  |     "build-backend-docs": "jsdoc -d ./docs/backend_api src/entities/*.js src/services/backend_script_api.js", | ||||||
|  |     "build-frontend-docs": "jsdoc -d ./docs/frontend_api src/public/javascripts/entities/*.js src/public/javascripts/services/frontend_script_api.js", | ||||||
|  |     "build-docs": "npm run build-backend-docs && npm run build-frontend-docs" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "async-mutex": "^0.1.3", |     "async-mutex": "0.1.3", | ||||||
|     "axios": "^0.17.1", |     "axios": "0.18", | ||||||
|     "body-parser": "~1.18.2", |     "body-parser": "1.18.3", | ||||||
|     "cookie-parser": "~1.4.3", |     "cls-hooked": "4.2.2", | ||||||
|     "debug": "~3.1.0", |     "cookie-parser": "1.4.3", | ||||||
|     "devtron": "^1.4.0", |     "debug": "3.1.0", | ||||||
|     "ejs": "~2.5.7", |     "devtron": "1.4.0", | ||||||
|     "electron": "^1.8.2-beta.4", |     "ejs": "2.6.1", | ||||||
|     "electron-debug": "^1.5.0", |     "electron-debug": "2.0.0", | ||||||
|     "electron-in-page-search": "^1.2.4", |     "electron-dl": "1.12.0", | ||||||
|     "express": "~4.16.2", |     "electron-in-page-search": "1.3.2", | ||||||
|     "express-promise-wrap": "^0.2.2", |     "express": "4.16.3", | ||||||
|     "express-session": "^1.15.6", |     "express-session": "1.15.6", | ||||||
|     "fs-extra": "^4.0.2", |     "fs-extra": "7.0.0", | ||||||
|     "helmet": "^3.9.0", |     "get-port": "4.0.0", | ||||||
|     "html": "^1.0.0", |     "helmet": "3.13.0", | ||||||
|     "image-type": "^3.0.0", |     "html": "1.0.0", | ||||||
|     "imagemin": "^5.3.1", |     "image-type": "3.0.0", | ||||||
|     "imagemin-giflossy": "^5.1.10", |     "imagemin": "6.0.0", | ||||||
|     "imagemin-mozjpeg": "^7.0.0", |     "imagemin-giflossy": "5.1.10", | ||||||
|     "imagemin-pngquant": "^5.0.1", |     "imagemin-mozjpeg": "7.0.0", | ||||||
|     "ini": "^1.3.4", |     "imagemin-pngquant": "6.0.0", | ||||||
|     "jimp": "^0.2.28", |     "ini": "1.3.5", | ||||||
|     "moment": "^2.20.1", |     "jimp": "0.3.5", | ||||||
|     "multer": "^1.3.0", |     "moment": "2.22.2", | ||||||
|     "rand-token": "^0.4.0", |     "multer": "1.3.1", | ||||||
|     "request": "^2.83.0", |     "open": "0.0.5", | ||||||
|     "request-promise": "^4.2.2", |     "rand-token": "0.4.0", | ||||||
|     "rimraf": "^2.6.2", |     "rcedit": "1.1.0", | ||||||
|     "sanitize-filename": "^1.6.1", |     "request": "2.88.0", | ||||||
|     "scrypt": "^6.0.3", |     "request-promise": "4.2.2", | ||||||
|     "serve-favicon": "~2.4.5", |     "rimraf": "2.6.2", | ||||||
|     "session-file-store": "^1.1.2", |     "sanitize-filename": "1.6.1", | ||||||
|     "simple-node-logger": "^0.93.30", |     "scrypt": "6.0.3", | ||||||
|     "sqlite": "^2.9.0", |     "serve-favicon": "2.5.0", | ||||||
|     "unescape": "^1.0.1", |     "session-file-store": "1.2.0", | ||||||
|     "ws": "^3.3.2" |     "simple-node-logger": "0.93.37", | ||||||
|  |     "sqlite": "3.0.0", | ||||||
|  |     "tar-stream": "1.6.1", | ||||||
|  |     "unescape": "1.0.1", | ||||||
|  |     "ws": "6.0.0", | ||||||
|  |     "xml2js": "0.4.19" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "electron-compile": "^6.4.2", |     "electron": "2.0.7", | ||||||
|     "electron-packager": "^10.1.1", |     "electron-compile": "6.4.3", | ||||||
|     "electron-prebuilt-compile": "1.8.2-beta.4", |     "electron-packager": "12.1.1", | ||||||
|     "electron-rebuild": "^1.7.3", |     "electron-prebuilt-compile": "2.0.7", | ||||||
|     "tape": "^4.8.0", |     "electron-rebuild": "1.8.2", | ||||||
|     "xo": "^0.18.0" |     "lorem-ipsum": "1.0.5", | ||||||
|  |     "tape": "4.9.1", | ||||||
|  |     "xo": "0.22.0", | ||||||
|  |     "pkg": "4.3.4" | ||||||
|   }, |   }, | ||||||
|   "config": { |   "config": { | ||||||
|     "forge": { |     "forge": { | ||||||
| @@ -103,5 +117,15 @@ | |||||||
|       "node", |       "node", | ||||||
|       "browser" |       "browser" | ||||||
|     ] |     ] | ||||||
|  |   }, | ||||||
|  |   "pkg": { | ||||||
|  |     "assets": [ | ||||||
|  |       "./db/**/*", | ||||||
|  |       "./src/public/**/*", | ||||||
|  |       "./src/views/**/*", | ||||||
|  |       "./node_modules/mozjpeg/vendor/*", | ||||||
|  |       "./node_modules/pngquant-bin/vendor/*", | ||||||
|  |       "./node_modules/giflossy/vendor/*" | ||||||
|  |     ] | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								src/app.js
									
									
									
									
									
								
							
							
						
						| @@ -9,6 +9,9 @@ const session = require('express-session'); | |||||||
| const FileStore = require('session-file-store')(session); | const FileStore = require('session-file-store')(session); | ||||||
| const os = require('os'); | const os = require('os'); | ||||||
| const sessionSecret = require('./services/session_secret'); | const sessionSecret = require('./services/session_secret'); | ||||||
|  | const cls = require('./services/cls'); | ||||||
|  | require('./entities/entity_constructor'); | ||||||
|  | require('./services/handlers'); | ||||||
|  |  | ||||||
| const app = express(); | const app = express(); | ||||||
|  |  | ||||||
| @@ -23,6 +26,17 @@ app.use((req, res, next) => { | |||||||
|     next(); |     next(); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | app.use((req, res, next) => { | ||||||
|  |     cls.namespace.bindEmitter(req); | ||||||
|  |     cls.namespace.bindEmitter(res); | ||||||
|  |  | ||||||
|  |     cls.init(() => { | ||||||
|  |         cls.namespace.set("Hi"); | ||||||
|  |  | ||||||
|  |         next(); | ||||||
|  |     }); | ||||||
|  | }); | ||||||
|  |  | ||||||
| app.use(bodyParser.json({limit: '50mb'})); | app.use(bodyParser.json({limit: '50mb'})); | ||||||
| app.use(bodyParser.urlencoded({extended: false})); | app.use(bodyParser.urlencoded({extended: false})); | ||||||
| app.use(cookieParser()); | app.use(cookieParser()); | ||||||
| @@ -34,7 +48,7 @@ const sessionParser = session({ | |||||||
|     cookie: { |     cookie: { | ||||||
|         //    path: "/", |         //    path: "/", | ||||||
|         httpOnly: true, |         httpOnly: true, | ||||||
|         maxAge:  1800000 |         maxAge:  24 * 60 * 60 * 1000 // in milliseconds | ||||||
|     }, |     }, | ||||||
|     store: new FileStore({ |     store: new FileStore({ | ||||||
|         ttl: 30 * 24 * 3600, |         ttl: 30 * 24 * 3600, | ||||||
| @@ -73,7 +87,7 @@ require('./services/backup'); | |||||||
| // trigger consistency checks timer | // trigger consistency checks timer | ||||||
| require('./services/consistency_checks'); | require('./services/consistency_checks'); | ||||||
|  |  | ||||||
| require('./plugins/reddit'); | require('./services/scheduler'); | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|     app, |     app, | ||||||
|   | |||||||
							
								
								
									
										34
									
								
								src/entities/api_token.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,34 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | const Entity = require('./entity'); | ||||||
|  | const dateUtils = require('../services/date_utils'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * ApiToken is an entity representing token used to authenticate against Trilium API from client applications. Currently used only by Trilium Sender. | ||||||
|  |  * | ||||||
|  |  * @param {string} apiTokenId - primary key | ||||||
|  |  * @param {string} token | ||||||
|  |  * @param {boolean} isDeleted - true if API token is deleted | ||||||
|  |  * @param {string} dateCreated | ||||||
|  |  * | ||||||
|  |  * @extends Entity | ||||||
|  |  */ | ||||||
|  | class ApiToken extends Entity { | ||||||
|  |     static get entityName() { return "api_tokens"; } | ||||||
|  |     static get primaryKeyName() { return "apiTokenId"; } | ||||||
|  |     static get hashedProperties() { return ["apiTokenId", "token", "dateCreated", "isDeleted"]; } | ||||||
|  |  | ||||||
|  |     beforeSaving() { | ||||||
|  |         if (!this.isDeleted) { | ||||||
|  |             this.isDeleted = false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.dateCreated) { | ||||||
|  |             this.dateCreated = dateUtils.nowDate(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         super.beforeSaving(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = ApiToken; | ||||||
| @@ -1,13 +1,92 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
|  |  | ||||||
| const Entity = require('./entity'); | const Entity = require('./entity'); | ||||||
|  | const repository = require('../services/repository'); | ||||||
|  | const dateUtils = require('../services/date_utils'); | ||||||
|  | const sql = require('../services/sql'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Attribute is key value pair owned by a note. | ||||||
|  |  * | ||||||
|  |  * @param {string} attributeId | ||||||
|  |  * @param {string} noteId | ||||||
|  |  * @param {string} type | ||||||
|  |  * @param {string} name | ||||||
|  |  * @param {string} value | ||||||
|  |  * @param {int} position | ||||||
|  |  * @param {boolean} isInheritable | ||||||
|  |  * @param {boolean} isDeleted | ||||||
|  |  * @param {string} dateCreated | ||||||
|  |  * @param {string} dateModified | ||||||
|  |  * | ||||||
|  |  * @extends Entity | ||||||
|  |  */ | ||||||
| class Attribute extends Entity { | class Attribute extends Entity { | ||||||
|     static get tableName() { return "attributes"; } |     static get entityName() { return "attributes"; } | ||||||
|     static get primaryKeyName() { return "attributeId"; } |     static get primaryKeyName() { return "attributeId"; } | ||||||
|  |     static get hashedProperties() { return ["attributeId", "noteId", "type", "name", "value", "isInheritable", "isDeleted", "dateCreated"]; } | ||||||
|  |  | ||||||
|  |     constructor(row) { | ||||||
|  |         super(row); | ||||||
|  |  | ||||||
|  |         this.isInheritable = !!this.isInheritable; | ||||||
|  |  | ||||||
|  |         if (this.isDefinition()) { | ||||||
|  |             try { | ||||||
|  |                 this.value = JSON.parse(this.value); | ||||||
|  |             } | ||||||
|  |             catch (e) { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     async getNote() { |     async getNote() { | ||||||
|         return this.repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); |         return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async getTargetNote() { | ||||||
|  |         if (this.type !== 'relation') { | ||||||
|  |             throw new Error(`Attribute ${this.attributeId} is not relation`); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.value) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.value]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     isDefinition() { | ||||||
|  |         return this.type === 'label-definition' || this.type === 'relation-definition'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async beforeSaving() { | ||||||
|  |         if (!this.value) { | ||||||
|  |             // null value isn't allowed | ||||||
|  |             this.value = ""; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this.position === undefined) { | ||||||
|  |             this.position = 1 + await sql.getValue(`SELECT COALESCE(MAX(position), 0) FROM attributes WHERE noteId = ?`, [this.noteId]); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.isInheritable) { | ||||||
|  |             this.isInheritable = false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.isDeleted) { | ||||||
|  |             this.isDeleted = false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.dateCreated) { | ||||||
|  |             this.dateCreated = dateUtils.nowDate(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         super.beforeSaving(); | ||||||
|  |  | ||||||
|  |         if (this.isChanged) { | ||||||
|  |             this.dateModified = dateUtils.nowDate(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										56
									
								
								src/entities/branch.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,56 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | const Entity = require('./entity'); | ||||||
|  | const dateUtils = require('../services/date_utils'); | ||||||
|  | const repository = require('../services/repository'); | ||||||
|  | const sql = require('../services/sql'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Branch represents note's placement in the tree - it's essentially pair of noteId and parentNoteId. | ||||||
|  |  * Each note can have multiple (at least one) branches, meaning it can be placed into multiple places in the tree. | ||||||
|  |  * | ||||||
|  |  * @param {string} branchId - primary key | ||||||
|  |  * @param {string} noteId | ||||||
|  |  * @param {string} parentNoteId | ||||||
|  |  * @param {int} notePosition | ||||||
|  |  * @param {string} prefix | ||||||
|  |  * @param {boolean} isExpanded | ||||||
|  |  * @param {boolean} isDeleted | ||||||
|  |  * @param {string} dateModified | ||||||
|  |  * @param {string} dateCreated | ||||||
|  |  * | ||||||
|  |  * @extends Entity | ||||||
|  |  */ | ||||||
|  | class Branch extends Entity { | ||||||
|  |     static get entityName() { return "branches"; } | ||||||
|  |     static get primaryKeyName() { return "branchId"; } | ||||||
|  |     // notePosition is not part of hash because it would produce a lot of updates in case of reordering | ||||||
|  |     static get hashedProperties() { return ["branchId", "noteId", "parentNoteId", "isDeleted", "prefix"]; } | ||||||
|  |  | ||||||
|  |     async getNote() { | ||||||
|  |         return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async beforeSaving() { | ||||||
|  |         if (this.notePosition === undefined) { | ||||||
|  |             const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [this.parentNoteId]); | ||||||
|  |             this.notePosition = maxNotePos === null ? 0 : maxNotePos + 1; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.isDeleted) { | ||||||
|  |             this.isDeleted = false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.dateCreated) { | ||||||
|  |             this.dateCreated = dateUtils.nowDate(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         super.beforeSaving(); | ||||||
|  |  | ||||||
|  |         if (this.isChanged) { | ||||||
|  |             this.dateModified = dateUtils.nowDate(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = Branch; | ||||||
| @@ -3,14 +3,45 @@ | |||||||
| const utils = require('../services/utils'); | const utils = require('../services/utils'); | ||||||
|  |  | ||||||
| class Entity { | class Entity { | ||||||
|     constructor(repository, row) { |     /** | ||||||
|         utils.assertArguments(repository, row); |      * @param {object} [row] - database row representing given entity | ||||||
|  |      */ | ||||||
|         this.repository = repository; |     constructor(row = {}) { | ||||||
|  |  | ||||||
|         for (const key in row) { |         for (const key in row) { | ||||||
|             this[key] = row[key]; |             this[key] = row[key]; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         if ('isDeleted' in this) { | ||||||
|  |             this.isDeleted = !!this.isDeleted; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     beforeSaving() { | ||||||
|  |         if (!this[this.constructor.primaryKeyName]) { | ||||||
|  |             this[this.constructor.primaryKeyName] = utils.newEntityId(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const origHash = this.hash; | ||||||
|  |  | ||||||
|  |         this.hash = this.generateHash(); | ||||||
|  |  | ||||||
|  |         this.isChanged = origHash !== this.hash; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     generateHash() { | ||||||
|  |         let contentToHash = ""; | ||||||
|  |  | ||||||
|  |         for (const propertyName of this.constructor.hashedProperties) { | ||||||
|  |             contentToHash += "|" + this[propertyName]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return utils.hash(contentToHash).substr(0, 10); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async save() { | ||||||
|  |         await require('../services/repository').updateEntity(this); | ||||||
|  |  | ||||||
|  |         return this; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										74
									
								
								src/entities/entity_constructor.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,74 @@ | |||||||
|  | const Note = require('../entities/note'); | ||||||
|  | const NoteRevision = require('../entities/note_revision'); | ||||||
|  | const Image = require('../entities/image'); | ||||||
|  | const NoteImage = require('../entities/note_image'); | ||||||
|  | const Branch = require('../entities/branch'); | ||||||
|  | const Attribute = require('../entities/attribute'); | ||||||
|  | const RecentNote = require('../entities/recent_note'); | ||||||
|  | const ApiToken = require('../entities/api_token'); | ||||||
|  | const Option = require('../entities/option'); | ||||||
|  | const repository = require('../services/repository'); | ||||||
|  |  | ||||||
|  | const ENTITY_NAME_TO_ENTITY = { | ||||||
|  |     "attributes": Attribute, | ||||||
|  |     "images": Image, | ||||||
|  |     "note_images": NoteImage, | ||||||
|  |     "branches": Branch, | ||||||
|  |     "notes": Note, | ||||||
|  |     "note_revisions": NoteRevision, | ||||||
|  |     "recent_notes": RecentNote, | ||||||
|  |     "options": Option, | ||||||
|  |     "api_tokens": ApiToken | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | function getEntityFromEntityName(entityName) { | ||||||
|  |     if (!(entityName in ENTITY_NAME_TO_ENTITY)) { | ||||||
|  |         throw new Error(`Entity for table ${entityName} not found!`); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return ENTITY_NAME_TO_ENTITY[entityName]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function createEntityFromRow(row) { | ||||||
|  |     let entity; | ||||||
|  |  | ||||||
|  |     if (row.attributeId) { | ||||||
|  |         entity = new Attribute(row); | ||||||
|  |     } | ||||||
|  |     else if (row.noteRevisionId) { | ||||||
|  |         entity = new NoteRevision(row); | ||||||
|  |     } | ||||||
|  |     else if (row.noteImageId) { | ||||||
|  |         entity = new NoteImage(row); | ||||||
|  |     } | ||||||
|  |     else if (row.imageId) { | ||||||
|  |         entity = new Image(row); | ||||||
|  |     } | ||||||
|  |     else if (row.branchId && row.notePath) { | ||||||
|  |         entity = new RecentNote(row); | ||||||
|  |     } | ||||||
|  |     else if (row.apiTokenId) { | ||||||
|  |         entity = new ApiToken(row); | ||||||
|  |     } | ||||||
|  |     else if (row.branchId) { | ||||||
|  |         entity = new Branch(row); | ||||||
|  |     } | ||||||
|  |     else if (row.noteId) { | ||||||
|  |         entity = new Note(row); | ||||||
|  |     } | ||||||
|  |     else if (row.name) { | ||||||
|  |         entity = new Option(row); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         throw new Error('Unknown entity type for row: ' + JSON.stringify(row)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return entity; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = { | ||||||
|  |     createEntityFromRow, | ||||||
|  |     getEntityFromEntityName | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | repository.setEntityConstructor(module.exports); | ||||||
							
								
								
									
										42
									
								
								src/entities/image.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,42 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | const Entity = require('./entity'); | ||||||
|  | const dateUtils = require('../services/date_utils'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This class represents image data. | ||||||
|  |  * | ||||||
|  |  * @param {string} imageId | ||||||
|  |  * @param {string} format | ||||||
|  |  * @param {string} checksum | ||||||
|  |  * @param {string} name | ||||||
|  |  * @param {blob} data | ||||||
|  |  * @param {boolean} isDeleted | ||||||
|  |  * @param {string} dateModified | ||||||
|  |  * @param {string} dateCreated | ||||||
|  |  * | ||||||
|  |  * @extends Entity | ||||||
|  |  */ | ||||||
|  | class Image extends Entity { | ||||||
|  |     static get entityName() { return "images"; } | ||||||
|  |     static get primaryKeyName() { return "imageId"; } | ||||||
|  |     static get hashedProperties() { return ["imageId", "format", "checksum", "name", "isDeleted", "dateCreated"]; } | ||||||
|  |  | ||||||
|  |     beforeSaving() { | ||||||
|  |         if (!this.isDeleted) { | ||||||
|  |             this.isDeleted = false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.dateCreated) { | ||||||
|  |             this.dateCreated = dateUtils.nowDate(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         super.beforeSaving(); | ||||||
|  |  | ||||||
|  |         if (this.isChanged) { | ||||||
|  |             this.dateModified = dateUtils.nowDate(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = Image; | ||||||
| @@ -1,49 +1,534 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
|  |  | ||||||
| const Entity = require('./entity'); | const Entity = require('./entity'); | ||||||
| const protected_session = require('../services/protected_session'); | const Attribute = require('./attribute'); | ||||||
|  | const protectedSessionService = require('../services/protected_session'); | ||||||
|  | const repository = require('../services/repository'); | ||||||
|  | const dateUtils = require('../services/date_utils'); | ||||||
|  |  | ||||||
|  | const LABEL = 'label'; | ||||||
|  | const RELATION = 'relation'; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This represents a Note which is a central object in the Trilium Notes project. | ||||||
|  |  * | ||||||
|  |  * @property {string} noteId - primary key | ||||||
|  |  * @property {string} type - one of "text", "code", "file" or "render" | ||||||
|  |  * @property {string} mime - MIME type, e.g. "text/html" | ||||||
|  |  * @property {string} title - note title | ||||||
|  |  * @property {string} content - note content - e.g. HTML text for text notes, file payload for files | ||||||
|  |  * @property {boolean} isProtected - true if note is protected | ||||||
|  |  * @property {boolean} isDeleted - true if note is deleted | ||||||
|  |  * @property {string} dateCreated | ||||||
|  |  * @property {string} dateModified | ||||||
|  |  * | ||||||
|  |  * @extends Entity | ||||||
|  |  */ | ||||||
| class Note extends Entity { | class Note extends Entity { | ||||||
|     static get tableName() { return "notes"; } |     static get entityName() { return "notes"; } | ||||||
|     static get primaryKeyName() { return "noteId"; } |     static get primaryKeyName() { return "noteId"; } | ||||||
|  |     static get hashedProperties() { return ["noteId", "title", "content", "type", "isProtected", "isDeleted"]; } | ||||||
|  |  | ||||||
|     constructor(repository, row) { |     /** | ||||||
|         super(repository, row); |      * @param row - object containing database row from "notes" table | ||||||
|  |      */ | ||||||
|  |     constructor(row) { | ||||||
|  |         super(row); | ||||||
|  |  | ||||||
|         if (this.isProtected) { |         this.isProtected = !!this.isProtected; | ||||||
|             protected_session.decryptNote(this.dataKey, this); |  | ||||||
|  |         // check if there's noteId, otherwise this is a new entity which wasn't encrypted yet | ||||||
|  |         if (this.isProtected && this.noteId) { | ||||||
|  |             protectedSessionService.decryptNote(this); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (this.isJson()) { |         this.setContent(this.content); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     setContent(content) { | ||||||
|  |         this.content = content; | ||||||
|  |  | ||||||
|  |         try { | ||||||
|             this.jsonContent = JSON.parse(this.content); |             this.jsonContent = JSON.parse(this.content); | ||||||
|         } |         } | ||||||
|  |         catch(e) {} | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** @returns {boolean} true if this note is the root of the note tree. Root note has "root" noteId */ | ||||||
|  |     isRoot() { | ||||||
|  |         return this.noteId === 'root'; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** @returns {boolean} true if this note is of application/json content type */ | ||||||
|     isJson() { |     isJson() { | ||||||
|         return this.type === "code" && this.mime === "application/json"; |         return this.mime === "application/json"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** @returns {boolean} true if this note is JavaScript (code or attachment) */ | ||||||
|  |     isJavaScript() { | ||||||
|  |         return (this.type === "code" || this.type === "file") | ||||||
|  |             && (this.mime.startsWith("application/javascript") || this.mime === "application/x-javascript"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** @returns {boolean} true if this note is HTML */ | ||||||
|  |     isHtml() { | ||||||
|  |         return (this.type === "code" || this.type === "file" || this.type === "render") && this.mime === "text/html"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** @returns {string} JS script environment - either "frontend" or "backend" */ | ||||||
|  |     getScriptEnv() { | ||||||
|  |         if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith('env=frontend'))) { | ||||||
|  |             return "frontend"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this.type === 'render') { | ||||||
|  |             return "frontend"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (this.isJavaScript() && this.mime.endsWith('env=backend')) { | ||||||
|  |             return "backend"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @returns {Promise<Attribute[]>} attributes belonging to this specific note (excludes inherited attributes) | ||||||
|  |      */ | ||||||
|  |     async getOwnedAttributes() { | ||||||
|  |         return await repository.getEntities(`SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ?`, [this.noteId]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** @returns {Promise<Attribute[]>} all note's attributes, including inherited ones */ | ||||||
|     async getAttributes() { |     async getAttributes() { | ||||||
|         return this.repository.getEntities("SELECT * FROM attributes WHERE noteId = ?", [this.noteId]); |         if (!this.__attributeCache) { | ||||||
|  |             await this.loadAttributesToCache(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return this.__attributeCache; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async getAttribute(name) { |     /** @returns {Promise<Attribute[]>} all note's labels (attributes with type label), including inherited ones */ | ||||||
|         return this.repository.getEntity("SELECT * FROM attributes WHERE noteId = ? AND name = ?", [this.noteId, name]); |     async getLabels() { | ||||||
|  |         return (await this.getAttributes()).filter(attr => attr.type === LABEL); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** @returns {Promise<Attribute[]>} all note's relations (attributes with type relation), including inherited ones */ | ||||||
|  |     async getRelations() { | ||||||
|  |         return (await this.getAttributes()).filter(attr => attr.type === RELATION); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Clear note's attributes cache to force fresh reload for next attribute request. | ||||||
|  |      * Cache is note instance scoped. | ||||||
|  |      */ | ||||||
|  |     invalidateAttributeCache() { | ||||||
|  |         this.__attributeCache = null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** @returns {Promise<void>} */ | ||||||
|  |     async loadAttributesToCache() { | ||||||
|  |         const attributes = await repository.getEntities(` | ||||||
|  |             WITH RECURSIVE | ||||||
|  |             tree(noteId, level) AS ( | ||||||
|  |                 SELECT ?, 0 | ||||||
|  |                 UNION | ||||||
|  |                 SELECT branches.parentNoteId, tree.level + 1 FROM branches | ||||||
|  |                     JOIN tree ON branches.noteId = tree.noteId | ||||||
|  |                     JOIN notes ON notes.noteId = branches.parentNoteId | ||||||
|  |                 WHERE notes.isDeleted = 0 | ||||||
|  |                   AND branches.isDeleted = 0 | ||||||
|  |             ), | ||||||
|  |             treeWithAttrs(noteId, level) AS ( | ||||||
|  |                 SELECT * FROM tree | ||||||
|  |                 UNION | ||||||
|  |                 SELECT attributes.value, treeWithAttrs.level + 1 FROM attributes | ||||||
|  |                      JOIN treeWithAttrs ON treeWithAttrs.noteId = attributes.noteId | ||||||
|  |                 WHERE attributes.isDeleted = 0 | ||||||
|  |                   AND attributes.type = 'relation' | ||||||
|  |                   AND attributes.name = 'template' | ||||||
|  |                   AND (attributes.noteId = ? OR attributes.isInheritable = 1) | ||||||
|  |                 ) | ||||||
|  |             SELECT attributes.* FROM attributes JOIN treeWithAttrs ON attributes.noteId = treeWithAttrs.noteId | ||||||
|  |             WHERE attributes.isDeleted = 0 AND (attributes.isInheritable = 1 OR attributes.noteId = ?) | ||||||
|  |             ORDER BY level, noteId, position`, [this.noteId, this.noteId, this.noteId]); | ||||||
|  |         // attributes are ordered so that "closest" attributes are first | ||||||
|  |         // we order by noteId so that attributes from same note stay together. Actual noteId ordering doesn't matter. | ||||||
|  |  | ||||||
|  |         const filteredAttributes = attributes.filter((attr, index) => { | ||||||
|  |             if (attr.isDefinition()) { | ||||||
|  |                 const firstDefinitionIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name); | ||||||
|  |  | ||||||
|  |                 // keep only if this element is the first definition for this type & name | ||||||
|  |                 return firstDefinitionIndex === index; | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 const definitionAttr = attributes.find(el => el.type === attr.type + '-definition' && el.name === attr.name); | ||||||
|  |  | ||||||
|  |                 if (!definitionAttr) { | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 const definition = definitionAttr.value; | ||||||
|  |  | ||||||
|  |                 if (definition.multiplicityType === 'multivalue') { | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     const firstAttrIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name); | ||||||
|  |  | ||||||
|  |                     // in case of single-valued attribute we'll keep it only if it's first (closest) | ||||||
|  |                     return firstAttrIndex === index; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         for (const attr of filteredAttributes) { | ||||||
|  |             attr.isOwned = attr.noteId === this.noteId; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         this.__attributeCache = filteredAttributes; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|  |      * @param {string} name - attribute name | ||||||
|  |      * @returns {Promise<boolean>} true if note has an attribute with given type and name (including inherited) | ||||||
|  |      */ | ||||||
|  |     async hasAttribute(type, name) { | ||||||
|  |         return !!await this.getAttribute(type, name); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|  |      * @param {string} name - attribute name | ||||||
|  |      * @returns {Promise<Attribute>} attribute of given type and name. If there's more such attributes, first is  returned. Returns null if there's no such attribute belonging to this note. | ||||||
|  |      */ | ||||||
|  |     async getAttribute(type, name) { | ||||||
|  |         const attributes = await this.getAttributes(); | ||||||
|  |  | ||||||
|  |         return attributes.find(attr => attr.type === type && attr.name === name); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|  |      * @param {string} name - attribute name | ||||||
|  |      * @returns {Promise<string>} attribute value of given type and name or null if no such attribute exists. | ||||||
|  |      */ | ||||||
|  |     async getAttributeValue(type, name) { | ||||||
|  |         const attr = await this.getAttribute(type, name); | ||||||
|  |  | ||||||
|  |         return attr ? attr.value : null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Based on enabled, attribute is either set or removed. | ||||||
|  |      * | ||||||
|  |      * @param {string} type - attribute type ('relation', 'label' etc.) | ||||||
|  |      * @param {boolean} enabled - toggle On or Off | ||||||
|  |      * @param {string} name - attribute name | ||||||
|  |      * @param {string} [value] - attribute value (optional) | ||||||
|  |      * @returns {Promise<void>} | ||||||
|  |      */ | ||||||
|  |     async toggleAttribute(type, enabled, name, value) { | ||||||
|  |         if (enabled) { | ||||||
|  |             await this.setAttribute(type, name, value); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             await this.removeAttribute(type, name, value); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Creates given attribute name-value pair if it doesn't exist. | ||||||
|  |      * | ||||||
|  |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|  |      * @param {string} name - attribute name | ||||||
|  |      * @param {string} [value] - attribute value (optional) | ||||||
|  |      * @returns {Promise<void>} | ||||||
|  |      */ | ||||||
|  |     async setAttribute(type, name, value) { | ||||||
|  |         const attributes = await this.getOwnedAttributes(); | ||||||
|  |         let attr = attributes.find(attr => attr.type === type && (value === undefined || attr.value === value)); | ||||||
|  |  | ||||||
|  |         if (!attr) { | ||||||
|  |             attr = new Attribute({ | ||||||
|  |                 noteId: this.noteId, | ||||||
|  |                 type: type, | ||||||
|  |                 name: name, | ||||||
|  |                 value: value !== undefined ? value : "" | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             await attr.save(); | ||||||
|  |  | ||||||
|  |             this.invalidateAttributeCache(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Removes given attribute name-value pair if it exists. | ||||||
|  |      * | ||||||
|  |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|  |      * @param {string} name - attribute name | ||||||
|  |      * @param {string} [value] - attribute value (optional) | ||||||
|  |      * @returns {Promise<void>} | ||||||
|  |      */ | ||||||
|  |     async removeAttribute(type, name, value) { | ||||||
|  |         const attributes = await this.getOwnedAttributes(); | ||||||
|  |  | ||||||
|  |         for (const attribute of attributes) { | ||||||
|  |             if (attribute.type === type && (value === undefined || value === attribute.value)) { | ||||||
|  |                 attribute.isDeleted = true; | ||||||
|  |                 await attribute.save(); | ||||||
|  |  | ||||||
|  |                 this.invalidateAttributeCache(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param {string} name - label name | ||||||
|  |      * @returns {Promise<boolean>} true if label exists (including inherited) | ||||||
|  |      */ | ||||||
|  |     async hasLabel(name) { return await this.hasAttribute(LABEL, name); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param {string} name - relation name | ||||||
|  |      * @returns {Promise<boolean>} true if relation exists (including inherited) | ||||||
|  |      */ | ||||||
|  |     async hasRelation(name) { return await this.hasAttribute(RELATION, name); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param {string} name - label name | ||||||
|  |      * @returns {Promise<Attribute>} label if it exists, null otherwise | ||||||
|  |      */ | ||||||
|  |     async getLabel(name) { return await this.getAttribute(LABEL, name); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param {string} name - relation name | ||||||
|  |      * @returns {Promise<Attribute>} relation if it exists, null otherwise | ||||||
|  |      */ | ||||||
|  |     async getRelation(name) { return await this.getAttribute(RELATION, name); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param {string} name - label name | ||||||
|  |      * @returns {Promise<string>} label value if label exists, null otherwise | ||||||
|  |      */ | ||||||
|  |     async getLabelValue(name) { return await this.getAttributeValue(LABEL, name); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param {string} name - relation name | ||||||
|  |      * @returns {Promise<string>} relation value if relation exists, null otherwise | ||||||
|  |      */ | ||||||
|  |     async getRelationValue(name) { return await this.getAttributeValue(RELATION, name); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Based on enabled, label is either set or removed. | ||||||
|  |      * | ||||||
|  |      * @param {boolean} enabled - toggle On or Off | ||||||
|  |      * @param {string} name - label name | ||||||
|  |      * @param {string} [value] - label value (optional) | ||||||
|  |      * @returns {Promise<void>} | ||||||
|  |      */ | ||||||
|  |     async toggleLabel(enabled, name, value) { return await this.toggleAttribute(LABEL, enabled, name, value); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Based on enabled, relation is either set or removed. | ||||||
|  |      * | ||||||
|  |      * @param {boolean} enabled - toggle On or Off | ||||||
|  |      * @param {string} name - relation name | ||||||
|  |      * @param {string} [value] - relation value (noteId) | ||||||
|  |      * @returns {Promise<void>} | ||||||
|  |      */ | ||||||
|  |     async toggleRelation(enabled, name, value) { return await this.toggleAttribute(RELATION, enabled, name, value); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Create label name-value pair if it doesn't exist yet. | ||||||
|  |      * | ||||||
|  |      * @param {string} name - label name | ||||||
|  |      * @param {string} [value] - label value | ||||||
|  |      * @returns {Promise<void>} | ||||||
|  |      */ | ||||||
|  |     async setLabel(name, value) { return await this.setAttribute(LABEL, name, value); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Create relation name-value pair if it doesn't exist yet. | ||||||
|  |      * | ||||||
|  |      * @param {string} name - relation name | ||||||
|  |      * @param {string} [value] - relation value (noteId) | ||||||
|  |      * @returns {Promise<void>} | ||||||
|  |      */ | ||||||
|  |     async setRelation(name, value) { return await this.setAttribute(RELATION, name, value); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Remove label name-value pair, if it exists. | ||||||
|  |      * | ||||||
|  |      * @param {string} name - label name | ||||||
|  |      * @param {string} [value] - label value | ||||||
|  |      * @returns {Promise<void>} | ||||||
|  |      */ | ||||||
|  |     async removeLabel(name, value) { return await this.removeAttribute(LABEL, name, value); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Remove relation name-value pair, if it exists. | ||||||
|  |      * | ||||||
|  |      * @param {string} name - relation name | ||||||
|  |      * @param {string} [value] - relation value (noteId) | ||||||
|  |      * @returns {Promise<void>} | ||||||
|  |      */ | ||||||
|  |     async removeRelation(name, value) { return await this.removeAttribute(RELATION, name, value); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param {string} name | ||||||
|  |      * @returns {Promise<Note>|null} target note of the relation or null (if target is empty or note was not found) | ||||||
|  |      */ | ||||||
|  |     async getRelationTarget(name) { | ||||||
|  |         const relation = await this.getRelation(name); | ||||||
|  |  | ||||||
|  |         return relation ? await repository.getNote(relation.value) : null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Finds notes with given attribute name and value. Only own attributes are considered, not inherited ones | ||||||
|  |      * | ||||||
|  |      * @param {string} type - attribute type (label, relation, etc.) | ||||||
|  |      * @param {string} name - attribute name | ||||||
|  |      * @param {string} [value] - attribute value | ||||||
|  |      * @returns {Promise<Note[]>} | ||||||
|  |      */ | ||||||
|  |     async findNotesWithAttribute(type, name, value) { | ||||||
|  |         const params = [this.noteId, name]; | ||||||
|  |         let valueCondition = ""; | ||||||
|  |  | ||||||
|  |         if (value !== undefined) { | ||||||
|  |             params.push(value); | ||||||
|  |             valueCondition = " AND attributes.value = ?"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         const notes = await repository.getEntities(` | ||||||
|  |             WITH RECURSIVE | ||||||
|  |             tree(noteId) AS ( | ||||||
|  |                 SELECT ? | ||||||
|  |                 UNION | ||||||
|  |                 SELECT branches.noteId FROM branches | ||||||
|  |                     JOIN tree ON branches.parentNoteId = tree.noteId | ||||||
|  |                     JOIN notes ON notes.noteId = branches.noteId | ||||||
|  |                 WHERE notes.isDeleted = 0 | ||||||
|  |                   AND branches.isDeleted = 0 | ||||||
|  |             ) | ||||||
|  |             SELECT notes.* FROM notes  | ||||||
|  |             JOIN tree ON tree.noteId = notes.noteId | ||||||
|  |             JOIN attributes ON attributes.noteId = notes.noteId | ||||||
|  |             WHERE attributes.isDeleted = 0  | ||||||
|  |               AND attributes.name = ? | ||||||
|  |               ${valueCondition}  | ||||||
|  |             ORDER BY noteId, position`, params); | ||||||
|  |  | ||||||
|  |         return notes; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Finds notes with given label name and value. Only own labels are considered, not inherited ones | ||||||
|  |      * | ||||||
|  |      * @param {string} name - label name | ||||||
|  |      * @param {string} [value] - label value | ||||||
|  |      * @returns {Promise<Note[]>} | ||||||
|  |      */ | ||||||
|  |     async findNotesWithLabel(name, value) { return await this.findNotesWithAttribute(LABEL, name, value); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Finds notes with given relation name and value. Only own relations are considered, not inherited ones | ||||||
|  |      * | ||||||
|  |      * @param {string} name - relation name | ||||||
|  |      * @param {string} [value] - relation value | ||||||
|  |      * @returns {Promise<Note[]>} | ||||||
|  |      */ | ||||||
|  |     async findNotesWithRelation(name, value) { return await this.findNotesWithAttribute(RELATION, name, value); } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Returns note revisions of this note. | ||||||
|  |      * | ||||||
|  |      * @returns {Promise<NoteRevision[]>} | ||||||
|  |      */ | ||||||
|     async getRevisions() { |     async getRevisions() { | ||||||
|         return this.repository.getEntities("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId]); |         return await repository.getEntities("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async getTrees() { |     /** | ||||||
|         return this.repository.getEntities("SELECT * FROM note_tree WHERE isDeleted = 0 AND noteId = ?", [this.noteId]); |      * @returns {Promise<NoteImage[]>} | ||||||
|  |      */ | ||||||
|  |     async getNoteImages() { | ||||||
|  |         return await repository.getEntities("SELECT * FROM note_images WHERE noteId = ? AND isDeleted = 0", [this.noteId]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @returns {Promise<Branch[]>} | ||||||
|  |      */ | ||||||
|  |     async getBranches() { | ||||||
|  |         return await repository.getEntities("SELECT * FROM branches WHERE isDeleted = 0 AND noteId = ?", [this.noteId]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @returns {Promise<Note[]>} child notes of this note | ||||||
|  |      */ | ||||||
|  |     async getChildNotes() { | ||||||
|  |         return await repository.getEntities(` | ||||||
|  |           SELECT notes.*  | ||||||
|  |           FROM branches  | ||||||
|  |             JOIN notes USING(noteId)  | ||||||
|  |           WHERE notes.isDeleted = 0 | ||||||
|  |                 AND branches.isDeleted = 0 | ||||||
|  |                 AND branches.parentNoteId = ? | ||||||
|  |           ORDER BY branches.notePosition`, [this.noteId]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @returns {Promise<Branch[]>} child branches of this note | ||||||
|  |      */ | ||||||
|  |     async getChildBranches() { | ||||||
|  |         return await repository.getEntities(` | ||||||
|  |           SELECT branches.*  | ||||||
|  |           FROM branches  | ||||||
|  |           WHERE branches.isDeleted = 0 | ||||||
|  |                 AND branches.parentNoteId = ? | ||||||
|  |           ORDER BY branches.notePosition`, [this.noteId]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @returns {Promise<Note[]>} parent notes of this note (note can have multiple parents because of cloning) | ||||||
|  |      */ | ||||||
|  |     async getParentNotes() { | ||||||
|  |         return await repository.getEntities(` | ||||||
|  |           SELECT parent_notes.*  | ||||||
|  |           FROM  | ||||||
|  |             branches AS child_tree  | ||||||
|  |             JOIN notes AS parent_notes ON parent_notes.noteId = child_tree.parentNoteId  | ||||||
|  |           WHERE child_tree.noteId = ? | ||||||
|  |                 AND child_tree.isDeleted = 0 | ||||||
|  |                 AND parent_notes.isDeleted = 0`, [this.noteId]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     beforeSaving() { |     beforeSaving() { | ||||||
|         this.content = JSON.stringify(this.jsonContent, null, '\t'); |         if (this.isJson() && this.jsonContent) { | ||||||
|  |             this.content = JSON.stringify(this.jsonContent, null, '\t'); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         if (this.isProtected) { |         if (this.isProtected) { | ||||||
|             protected_session.encryptNote(this.dataKey, this); |             protectedSessionService.encryptNote(this); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.isDeleted) { | ||||||
|  |             this.isDeleted = false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.dateCreated) { | ||||||
|  |             this.dateCreated = dateUtils.nowDate(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         super.beforeSaving(); | ||||||
|  |  | ||||||
|  |         if (this.isChanged) { | ||||||
|  |             this.dateModified = dateUtils.nowDate(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										49
									
								
								src/entities/note_image.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,49 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | const Entity = require('./entity'); | ||||||
|  | const repository = require('../services/repository'); | ||||||
|  | const dateUtils = require('../services/date_utils'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This class represents image's placement in the note(s). One image may be placed into several notes. | ||||||
|  |  * | ||||||
|  |  * @param {string} noteImageId | ||||||
|  |  * @param {string} noteId | ||||||
|  |  * @param {string} imageId | ||||||
|  |  * @param {boolean} isDeleted | ||||||
|  |  * @param {string} dateModified | ||||||
|  |  * @param {string} dateCreated | ||||||
|  |  * | ||||||
|  |  * @extends Entity | ||||||
|  |  */ | ||||||
|  | class NoteImage extends Entity { | ||||||
|  |     static get entityName() { return "note_images"; } | ||||||
|  |     static get primaryKeyName() { return "noteImageId"; } | ||||||
|  |     static get hashedProperties() { return ["noteImageId", "noteId", "imageId", "isDeleted", "dateCreated"]; } | ||||||
|  |  | ||||||
|  |     async getNote() { | ||||||
|  |         return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async getImage() { | ||||||
|  |         return await repository.getEntity("SELECT * FROM images WHERE imageId = ?", [this.imageId]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     beforeSaving() { | ||||||
|  |         if (!this.isDeleted) { | ||||||
|  |             this.isDeleted = false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.dateCreated) { | ||||||
|  |             this.dateCreated = dateUtils.nowDate(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         super.beforeSaving(); | ||||||
|  |  | ||||||
|  |         if (this.isChanged) { | ||||||
|  |             this.dateModified = dateUtils.nowDate(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = NoteImage; | ||||||
| @@ -1,13 +1,49 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
|  |  | ||||||
| const Entity = require('./entity'); | const Entity = require('./entity'); | ||||||
|  | const protectedSessionService = require('../services/protected_session'); | ||||||
|  | const repository = require('../services/repository'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * NoteRevision represents snapshot of note's title and content at some point in the past. It's used for seamless note versioning. | ||||||
|  |  * | ||||||
|  |  * @param {string} noteRevisionId | ||||||
|  |  * @param {string} noteId | ||||||
|  |  * @param {string} type | ||||||
|  |  * @param {string} mime | ||||||
|  |  * @param {string} title | ||||||
|  |  * @param {string} content | ||||||
|  |  * @param {string} isProtected | ||||||
|  |  * @param {string} dateModifiedFrom | ||||||
|  |  * @param {string} dateModifiedTo | ||||||
|  |  * | ||||||
|  |  * @extends Entity | ||||||
|  |  */ | ||||||
| class NoteRevision extends Entity { | class NoteRevision extends Entity { | ||||||
|     static get tableName() { return "note_revisions"; } |     static get entityName() { return "note_revisions"; } | ||||||
|     static get primaryKeyName() { return "noteRevisionId"; } |     static get primaryKeyName() { return "noteRevisionId"; } | ||||||
|  |     static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "content", "isProtected", "dateModifiedFrom", "dateModifiedTo"]; } | ||||||
|  |  | ||||||
|  |     constructor(row) { | ||||||
|  |         super(row); | ||||||
|  |  | ||||||
|  |         this.isProtected = !!this.isProtected; | ||||||
|  |  | ||||||
|  |         if (this.isProtected) { | ||||||
|  |             protectedSessionService.decryptNoteRevision(this); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     async getNote() { |     async getNote() { | ||||||
|         return this.repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); |         return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     beforeSaving() { | ||||||
|  |         if (this.isProtected) { | ||||||
|  |             protectedSessionService.encryptNoteRevision(this); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         super.beforeSaving(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,18 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
|  |  | ||||||
| const Entity = require('./entity'); |  | ||||||
|  |  | ||||||
| class NoteTree extends Entity { |  | ||||||
|     static get tableName() { return "note_tree"; } |  | ||||||
|     static get primaryKeyName() { return "noteTreeId"; } |  | ||||||
|  |  | ||||||
|     async getNote() { |  | ||||||
|         return this.repository.getEntity("SELECT * FROM note_tree WHERE isDeleted = 0 AND noteId = ?", [this.noteId]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async getParentNote() { |  | ||||||
|         return this.repository.getEntity("SELECT * FROM note_tree WHERE isDeleted = 0 AND parentNoteId = ?", [this.parentNoteId]); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = NoteTree; |  | ||||||
							
								
								
									
										37
									
								
								src/entities/option.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,37 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | const Entity = require('./entity'); | ||||||
|  | const dateUtils = require('../services/date_utils'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Option represents name-value pair, either directly configurable by the user or some system property. | ||||||
|  |  * | ||||||
|  |  * @param {string} name | ||||||
|  |  * @param {string} value | ||||||
|  |  * @param {boolean} isSynced | ||||||
|  |  * @param {string} dateModified | ||||||
|  |  * @param {string} dateCreated | ||||||
|  |  * | ||||||
|  |  * @extends Entity | ||||||
|  |  */ | ||||||
|  | class Option extends Entity { | ||||||
|  |     static get entityName() { return "options"; } | ||||||
|  |     static get primaryKeyName() { return "name"; } | ||||||
|  |     static get hashedProperties() { return ["name", "value"]; } | ||||||
|  |  | ||||||
|  |     constructor(row) { | ||||||
|  |         super(row); | ||||||
|  |  | ||||||
|  |         this.isSynced = !!this.isSynced; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     beforeSaving() { | ||||||
|  |         super.beforeSaving(); | ||||||
|  |  | ||||||
|  |         if (this.isChanged) { | ||||||
|  |             this.dateModified = dateUtils.nowDate(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = Option; | ||||||
							
								
								
									
										34
									
								
								src/entities/recent_note.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,34 @@ | |||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | const Entity = require('./entity'); | ||||||
|  | const dateUtils = require('../services/date_utils'); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * RecentNote represents recently visited note. | ||||||
|  |  * | ||||||
|  |  * @param {string} branchId | ||||||
|  |  * @param {string} notePath | ||||||
|  |  * @param {boolean} isDeleted | ||||||
|  |  * @param {string} dateModified | ||||||
|  |  * | ||||||
|  |  * @extends Entity | ||||||
|  |  */ | ||||||
|  | class RecentNote extends Entity { | ||||||
|  |     static get entityName() { return "recent_notes"; } | ||||||
|  |     static get primaryKeyName() { return "branchId"; } | ||||||
|  |     static get hashedProperties() { return ["branchId", "notePath", "dateCreated", "isDeleted"]; } | ||||||
|  |  | ||||||
|  |     beforeSaving() { | ||||||
|  |         if (!this.isDeleted) { | ||||||
|  |             this.isDeleted = false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!this.dateCreated) { | ||||||
|  |             this.dateCreated = dateUtils.nowDate(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         super.beforeSaving(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = RecentNote; | ||||||
| @@ -1,143 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
|  |  | ||||||
| const sql = require('../services/sql'); |  | ||||||
| const notes = require('../services/notes'); |  | ||||||
| const axios = require('axios'); |  | ||||||
| const log = require('../services/log'); |  | ||||||
| const utils = require('../services/utils'); |  | ||||||
| const unescape = require('unescape'); |  | ||||||
| const attributes = require('../services/attributes'); |  | ||||||
| const sync_mutex = require('../services/sync_mutex'); |  | ||||||
| const config = require('../services/config'); |  | ||||||
| const date_notes = require('../services/date_notes'); |  | ||||||
|  |  | ||||||
| // "reddit" date note is subnote of date note which contains all reddit comments from that date |  | ||||||
| const REDDIT_DATE_ATTRIBUTE = 'reddit_date_note'; |  | ||||||
|  |  | ||||||
| async function createNote(parentNoteId, noteTitle, noteText) { |  | ||||||
|     return (await notes.createNewNote(parentNoteId, { |  | ||||||
|         title: noteTitle, |  | ||||||
|         content: noteText, |  | ||||||
|         target: 'into', |  | ||||||
|         isProtected: false |  | ||||||
|     })).noteId; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function redditId(kind, id) { |  | ||||||
|     return kind + "_" + id; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function getDateNoteIdForReddit(dateTimeStr, rootNoteId) { |  | ||||||
|     const dateStr = dateTimeStr.substr(0, 10); |  | ||||||
|  |  | ||||||
|     let redditDateNoteId = await attributes.getNoteIdWithAttribute(REDDIT_DATE_ATTRIBUTE, dateStr); |  | ||||||
|  |  | ||||||
|     if (!redditDateNoteId) { |  | ||||||
|         const dateNoteId = await date_notes.getDateNoteId(dateTimeStr, rootNoteId); |  | ||||||
|  |  | ||||||
|         redditDateNoteId = await createNote(dateNoteId, "Reddit"); |  | ||||||
|  |  | ||||||
|         await attributes.createAttribute(redditDateNoteId, REDDIT_DATE_ATTRIBUTE, dateStr); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return redditDateNoteId; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function importComments(rootNoteId, accountName, afterId = null) { |  | ||||||
|     let url = `https://www.reddit.com/user/${accountName}.json`; |  | ||||||
|  |  | ||||||
|     if (afterId) { |  | ||||||
|         url += "?after=" + afterId; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const response = await axios.get(url); |  | ||||||
|     const listing = response.data; |  | ||||||
|  |  | ||||||
|     if (listing.kind !== 'Listing') { |  | ||||||
|         log.info(`Reddit: Unknown object kind ${listing.kind}`); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const children = listing.data.children; |  | ||||||
|  |  | ||||||
|     let importedComments = 0; |  | ||||||
|  |  | ||||||
|     for (const child of children) { |  | ||||||
|         const comment = child.data; |  | ||||||
|  |  | ||||||
|         let commentNoteId = await attributes.getNoteIdWithAttribute('reddit_id', redditId(child.kind, comment.id)); |  | ||||||
|  |  | ||||||
|         if (commentNoteId) { |  | ||||||
|             continue; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         const dateTimeStr = utils.dateStr(new Date(comment.created_utc * 1000)); |  | ||||||
|  |  | ||||||
|         const permaLink = 'https://reddit.com' + comment.permalink; |  | ||||||
|  |  | ||||||
|         const noteText = |  | ||||||
| `<p><a href="${permaLink}">${permaLink}</a></p> |  | ||||||
| <p>author: <a href="https://reddit.com/u/${comment.author}">${comment.author}</a>,  |  | ||||||
| subreddit: <a href="https://reddit.com/r/${comment.subreddit}">${comment.subreddit}</a>,  |  | ||||||
| karma: ${comment.score}, created at ${dateTimeStr}</p><p></p>` |  | ||||||
|             + unescape(comment.body_html); |  | ||||||
|  |  | ||||||
|         let parentNoteId = await getDateNoteIdForReddit(dateTimeStr, rootNoteId); |  | ||||||
|  |  | ||||||
|         await sql.doInTransaction(async () => { |  | ||||||
|             commentNoteId = await createNote(parentNoteId, comment.link_title, noteText); |  | ||||||
|  |  | ||||||
|             log.info("Reddit: Imported comment to note " + commentNoteId); |  | ||||||
|             importedComments++; |  | ||||||
|  |  | ||||||
|             await attributes.createAttribute(commentNoteId, "reddit_kind", child.kind); |  | ||||||
|             await attributes.createAttribute(commentNoteId, "reddit_id", redditId(child.kind, comment.id)); |  | ||||||
|             await attributes.createAttribute(commentNoteId, "reddit_created_utc", comment.created_utc); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // if there have been no imported comments on this page, there shouldn't be any to import |  | ||||||
|     // on the next page since those are older |  | ||||||
|     if (listing.data.after && importedComments > 0) { |  | ||||||
|         importedComments += await importComments(rootNoteId, accountName, listing.data.after); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return importedComments; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| let redditAccounts = []; |  | ||||||
|  |  | ||||||
| async function runImport() { |  | ||||||
|     const rootNoteId = await date_notes.getRootNoteId(); |  | ||||||
|  |  | ||||||
|     // technically mutex shouldn't be necessary but we want to avoid doing potentially expensive import |  | ||||||
|     // concurrently with sync |  | ||||||
|     await sync_mutex.doExclusively(async () => { |  | ||||||
|         let importedComments = 0; |  | ||||||
|  |  | ||||||
|         for (const account of redditAccounts) { |  | ||||||
|             importedComments += await importComments(rootNoteId, account); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         log.info(`Reddit: Imported ${importedComments} comments.`); |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| sql.dbReady.then(async () => { |  | ||||||
|     if (!config['Reddit'] || config['Reddit']['enabled'] !== true) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     const redditAccountsStr = config['Reddit']['accounts']; |  | ||||||
|  |  | ||||||
|     if (!redditAccountsStr) { |  | ||||||
|         log.info("Reddit: No reddit accounts defined in option 'reddit_accounts'"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     redditAccounts = redditAccountsStr.split(",").map(s => s.trim()); |  | ||||||
|  |  | ||||||
|     const pollingIntervalInSeconds = config['Reddit']['pollingIntervalInSeconds'] || (4 * 3600); |  | ||||||
|  |  | ||||||
|     setInterval(runImport, pollingIntervalInSeconds * 1000); |  | ||||||
|     setTimeout(runImport, 10000); // 10 seconds after startup - intentionally after initial sync |  | ||||||
| }); |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/back-24.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 511 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/clock-16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 381 B | 
| Before Width: | Height: | Size: 245 B After Width: | Height: | Size: 245 B | 
| Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 339 B | 
| Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 463 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/edit-20.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 312 B | 
| Before Width: | Height: | Size: 288 B After Width: | Height: | Size: 288 B | 
| Before Width: | Height: | Size: 284 B After Width: | Height: | Size: 284 B | 
| Before Width: | Height: | Size: 292 B After Width: | Height: | Size: 292 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/forward-24.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 511 B | 
| Before Width: | Height: | Size: 155 B After Width: | Height: | Size: 155 B | 
| Before Width: | Height: | Size: 323 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/paperclip-16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 358 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/play-16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 252 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/play-20.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 288 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/save-20.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 388 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/search-20.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 431 B | 
| Before Width: | Height: | Size: 419 B After Width: | Height: | Size: 419 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/search-small-16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 354 B |