Compare commits

...

26 Commits

Author SHA1 Message Date
azivner
3972c27e7a release 0.17.0 2018-07-09 21:22:12 +02:00
azivner
14cffbbe62 docker commands with sudo 2018-07-09 21:21:28 +02:00
azivner
599c3c04af correct sourceMappingURL which gets rid of error message, fixes #114 2018-07-08 23:23:49 +02:00
azivner
f1412b631d hide log polluting error message 2018-07-08 23:13:56 +02:00
azivner
41908050bb ctrl+u now doesn't trigger show source since it's occupied by underline 2018-07-08 21:45:05 +02:00
azivner
f07033423c ckeditor upgraded to 10.1.0 plus new plugins (table, strikethrough, underline) 2018-07-08 21:21:52 +02:00
azivner
daf96fcbf2 using zeit/pkg to package easy to use linux server edition 2018-07-06 00:05:06 +02:00
azivner
2bca94529e minor upgrades 2018-07-05 23:17:53 +02:00
azivner
b2c9a0da21 fix note sources, fixes #123 2018-07-04 20:37:23 +02:00
azivner
7a9542b4fc release 0.16.0 2018-06-16 13:34:39 -04:00
azivner
3a95c9e1bc all dialogs are now non-modal because of high cpu usage 2018-06-16 13:31:56 -04:00
azivner
3d2ef6be01 remove optionId, closes #117 2018-06-13 19:10:28 -04:00
azivner
d67246699a Introduced separate sync version (previously DB version was used to check sync compatibility), closes #120 2018-06-10 15:55:29 -04:00
azivner
14c704d6db db upgrades are now handled transparently in the background without bothering the user, closes #119 2018-06-10 15:49:22 -04:00
azivner
4c8eeb2e6f added docker build, closes #106 2018-06-10 15:06:52 -04:00
azivner
c1b245c8b1 fix unnecessary change events, closes #118 2018-06-10 11:51:13 -04:00
azivner
74202d67bb got rid of "Trilium Notes" branding - not necessary and takes valuable space 2018-06-10 10:57:45 -04:00
azivner
26066f39b1 chaged "focused mode" - now title is displayed as well and together with content takes whole window 2018-06-10 10:53:39 -04:00
azivner
b255cf190c fixes for zoom factor setting 2018-06-09 10:34:51 -04:00
azivner
bc77b143b0 darker outlines so inverted dark themes are more visible 2018-06-09 09:48:18 -04:00
azivner
9f0ff6ae7a note actions dropdown sizing 2018-06-09 09:44:40 -04:00
azivner
736704c7d6 fix show paths 2018-06-09 09:32:13 -04:00
azivner
654c116c58 use backgrounds for icon buttons so that dark and black themes look better 2018-06-09 09:28:50 -04:00
azivner
89a5cab98f added too options new tab appearance with possibility to change theme (white, black, dark) and zoom factor 2018-06-08 23:18:53 -04:00
azivner
c39d0be8cd refactoring of icon button styles 2018-06-08 22:17:00 -04:00
azivner
e75b4cd848 execute on script note is icon, closes #116 2018-06-08 21:59:40 -04:00
40 changed files with 3700 additions and 3257 deletions

4
.dockerignore Normal file
View File

@@ -0,0 +1,4 @@
node_modules
npm-debug.log
dist
.idea

View File

@@ -142,11 +142,10 @@ parentNoteId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName>
</key>
<column id="42" parent="8" name="id">
<column id="42" parent="8" name="eventId">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<SequenceIdentity>1</SequenceIdentity>
</column>
<column id="43" parent="8" name="noteId">
<Position>2</Position>
@@ -161,505 +160,517 @@ parentNoteId</ColNames>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<key id="46" parent="8">
<ColNames>id</ColNames>
<index id="46" parent="8" name="sqlite_autoindex_event_log_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>eventId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="47" parent="8">
<ColNames>eventId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_event_log_1</UnderlyingIndexName>
</key>
<column id="47" parent="9" name="imageId">
<column id="48" parent="9" name="imageId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="48" parent="9" name="format">
<column id="49" parent="9" name="format">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="49" parent="9" name="checksum">
<column id="50" parent="9" name="checksum">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="50" parent="9" name="name">
<column id="51" parent="9" name="name">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="51" parent="9" name="data">
<column id="52" parent="9" name="data">
<Position>5</Position>
<DataType>BLOB|0s</DataType>
</column>
<column id="52" parent="9" name="isDeleted">
<column id="53" parent="9" name="isDeleted">
<Position>6</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="53" parent="9" name="dateModified">
<column id="54" parent="9" name="dateModified">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="54" parent="9" name="dateCreated">
<column id="55" parent="9" name="dateCreated">
<Position>8</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="55" parent="9" name="hash">
<column id="56" parent="9" name="hash">
<Position>9</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<index id="56" parent="9" name="sqlite_autoindex_images_1">
<index id="57" parent="9" name="sqlite_autoindex_images_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>imageId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="57" parent="9">
<key id="58" parent="9">
<ColNames>imageId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_images_1</UnderlyingIndexName>
</key>
<column id="58" parent="10" name="labelId">
<column id="59" parent="10" name="labelId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="59" parent="10" name="noteId">
<column id="60" parent="10" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="60" parent="10" name="name">
<column id="61" parent="10" name="name">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="61" parent="10" name="value">
<column id="62" parent="10" name="value">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;&apos;</DefaultExpression>
</column>
<column id="62" parent="10" name="position">
<column id="63" parent="10" name="position">
<Position>5</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="63" parent="10" name="dateCreated">
<column id="64" parent="10" name="dateCreated">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="64" parent="10" name="dateModified">
<column id="65" parent="10" name="dateModified">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="65" parent="10" name="isDeleted">
<column id="66" parent="10" name="isDeleted">
<Position>8</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="66" parent="10" name="hash">
<column id="67" parent="10" name="hash">
<Position>9</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<index id="67" parent="10" name="sqlite_autoindex_labels_1">
<index id="68" parent="10" name="sqlite_autoindex_labels_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>labelId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="68" parent="10" name="IDX_labels_noteId">
<index id="69" parent="10" name="IDX_labels_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="69" parent="10" name="IDX_labels_name_value">
<index id="70" parent="10" name="IDX_labels_name_value">
<ColNames>name
value</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="70" parent="10">
<key id="71" parent="10">
<ColNames>labelId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_labels_1</UnderlyingIndexName>
</key>
<column id="71" parent="11" name="noteImageId">
<column id="72" parent="11" name="noteImageId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="72" parent="11" name="noteId">
<column id="73" parent="11" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="73" parent="11" name="imageId">
<column id="74" parent="11" name="imageId">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="74" parent="11" name="isDeleted">
<column id="75" parent="11" name="isDeleted">
<Position>4</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="75" parent="11" name="dateModified">
<column id="76" parent="11" name="dateModified">
<Position>5</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="76" parent="11" name="dateCreated">
<column id="77" parent="11" name="dateCreated">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="77" parent="11" name="hash">
<column id="78" parent="11" name="hash">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<index id="78" parent="11" name="sqlite_autoindex_note_images_1">
<index id="79" parent="11" name="sqlite_autoindex_note_images_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteImageId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="79" parent="11" name="IDX_note_images_noteId_imageId">
<index id="80" parent="11" name="IDX_note_images_noteId_imageId">
<ColNames>noteId
imageId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="80" parent="11" name="IDX_note_images_noteId">
<index id="81" parent="11" name="IDX_note_images_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="81" parent="11" name="IDX_note_images_imageId">
<index id="82" parent="11" name="IDX_note_images_imageId">
<ColNames>imageId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="82" parent="11">
<key id="83" parent="11">
<ColNames>noteImageId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_note_images_1</UnderlyingIndexName>
</key>
<column id="83" parent="12" name="noteRevisionId">
<column id="84" parent="12" name="noteRevisionId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="84" parent="12" name="noteId">
<column id="85" parent="12" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="85" parent="12" name="title">
<column id="86" parent="12" name="title">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="86" parent="12" name="content">
<column id="87" parent="12" name="content">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="87" parent="12" name="isProtected">
<column id="88" parent="12" name="isProtected">
<Position>5</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="88" parent="12" name="dateModifiedFrom">
<column id="89" parent="12" name="dateModifiedFrom">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="89" parent="12" name="dateModifiedTo">
<column id="90" parent="12" name="dateModifiedTo">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="90" parent="12" name="type">
<column id="91" parent="12" name="type">
<Position>8</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;&apos;</DefaultExpression>
</column>
<column id="91" parent="12" name="mime">
<column id="92" parent="12" name="mime">
<Position>9</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;&apos;</DefaultExpression>
</column>
<column id="92" parent="12" name="hash">
<column id="93" parent="12" name="hash">
<Position>10</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<index id="93" parent="12" name="sqlite_autoindex_note_revisions_1">
<index id="94" parent="12" name="sqlite_autoindex_note_revisions_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteRevisionId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="94" parent="12" name="IDX_note_revisions_noteId">
<index id="95" parent="12" name="IDX_note_revisions_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="95" parent="12" name="IDX_note_revisions_dateModifiedFrom">
<index id="96" parent="12" name="IDX_note_revisions_dateModifiedFrom">
<ColNames>dateModifiedFrom</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="96" parent="12" name="IDX_note_revisions_dateModifiedTo">
<index id="97" parent="12" name="IDX_note_revisions_dateModifiedTo">
<ColNames>dateModifiedTo</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="97" parent="12">
<key id="98" parent="12">
<ColNames>noteRevisionId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName>
</key>
<column id="98" parent="13" name="noteId">
<column id="99" parent="13" name="noteId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="99" parent="13" name="title">
<column id="100" parent="13" name="title">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;unnamed&quot;</DefaultExpression>
</column>
<column id="100" parent="13" name="content">
<column id="101" parent="13" name="content">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<column id="101" parent="13" name="isProtected">
<column id="102" parent="13" name="isProtected">
<Position>4</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="102" parent="13" name="isDeleted">
<column id="103" parent="13" name="isDeleted">
<Position>5</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="103" parent="13" name="dateCreated">
<column id="104" parent="13" name="dateCreated">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="104" parent="13" name="dateModified">
<column id="105" parent="13" name="dateModified">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="105" parent="13" name="type">
<column id="106" parent="13" name="type">
<Position>8</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;text&apos;</DefaultExpression>
</column>
<column id="106" parent="13" name="mime">
<column id="107" parent="13" name="mime">
<Position>9</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;text/html&apos;</DefaultExpression>
</column>
<column id="107" parent="13" name="hash">
<column id="108" parent="13" name="hash">
<Position>10</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<index id="108" parent="13" name="sqlite_autoindex_notes_1">
<index id="109" parent="13" name="sqlite_autoindex_notes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="109" parent="13" name="IDX_notes_type">
<index id="110" parent="13" name="IDX_notes_type">
<ColNames>type</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="110" parent="13">
<key id="111" parent="13">
<ColNames>noteId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName>
</key>
<column id="111" parent="14" name="name">
<column id="112" parent="14" name="optionId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="112" parent="14" name="value">
<column id="113" parent="14" name="name">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="113" parent="14" name="dateModified">
<column id="114" parent="14" name="value">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="115" parent="14" name="dateModified">
<Position>4</Position>
<DataType>INT|0s</DataType>
</column>
<column id="114" parent="14" name="isSynced">
<Position>4</Position>
<column id="116" parent="14" name="isSynced">
<Position>5</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="115" parent="14" name="hash">
<Position>5</Position>
<column id="117" parent="14" name="hash">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<column id="116" parent="14" name="dateCreated">
<Position>6</Position>
<column id="118" parent="14" name="dateCreated">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;1970-01-01T00:00:00.000Z&apos;</DefaultExpression>
</column>
<index id="117" parent="14" name="sqlite_autoindex_options_1">
<index id="119" parent="14" name="sqlite_autoindex_options_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>name</ColNames>
<ColNames>optionId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="118" parent="14">
<ColNames>name</ColNames>
<key id="120" parent="14">
<ColNames>optionId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName>
</key>
<column id="119" parent="15" name="branchId">
<column id="121" parent="15" name="branchId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="120" parent="15" name="notePath">
<column id="122" parent="15" name="notePath">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="121" parent="15" name="dateCreated">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="122" parent="15" name="isDeleted">
<Position>4</Position>
<DataType>INT|0s</DataType>
</column>
<column id="123" parent="15" name="hash">
<Position>5</Position>
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<index id="124" parent="15" name="sqlite_autoindex_recent_notes_1">
<column id="124" parent="15" name="dateCreated">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="125" parent="15" name="isDeleted">
<Position>5</Position>
<DataType>INT|0s</DataType>
</column>
<index id="126" parent="15" name="sqlite_autoindex_recent_notes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>branchId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="125" parent="15">
<key id="127" parent="15">
<ColNames>branchId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName>
</key>
<column id="126" parent="16" name="sourceId">
<column id="128" parent="16" name="sourceId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="127" parent="16" name="dateCreated">
<column id="129" parent="16" name="dateCreated">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="128" parent="16" name="sqlite_autoindex_source_ids_1">
<index id="130" parent="16" name="sqlite_autoindex_source_ids_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>sourceId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="129" parent="16">
<key id="131" parent="16">
<ColNames>sourceId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName>
</key>
<column id="130" parent="17" name="type">
<column id="132" parent="17" name="type">
<Position>1</Position>
<DataType>text|0s</DataType>
</column>
<column id="131" parent="17" name="name">
<column id="133" parent="17" name="name">
<Position>2</Position>
<DataType>text|0s</DataType>
</column>
<column id="132" parent="17" name="tbl_name">
<column id="134" parent="17" name="tbl_name">
<Position>3</Position>
<DataType>text|0s</DataType>
</column>
<column id="133" parent="17" name="rootpage">
<column id="135" parent="17" name="rootpage">
<Position>4</Position>
<DataType>integer|0s</DataType>
</column>
<column id="134" parent="17" name="sql">
<column id="136" parent="17" name="sql">
<Position>5</Position>
<DataType>text|0s</DataType>
</column>
<column id="135" parent="18" name="name">
<column id="137" parent="18" name="name">
<Position>1</Position>
</column>
<column id="136" parent="18" name="seq">
<column id="138" parent="18" name="seq">
<Position>2</Position>
</column>
<column id="137" parent="19" name="id">
<column id="139" parent="19" name="id">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
<SequenceIdentity>1</SequenceIdentity>
</column>
<column id="138" parent="19" name="entityName">
<column id="140" parent="19" name="entityName">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="139" parent="19" name="entityId">
<column id="141" parent="19" name="entityId">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="140" parent="19" name="sourceId">
<column id="142" parent="19" name="sourceId">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="141" parent="19" name="syncDate">
<column id="143" parent="19" name="syncDate">
<Position>5</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="142" parent="19" name="IDX_sync_entityName_entityId">
<index id="144" parent="19" name="IDX_sync_entityName_entityId">
<ColNames>entityName
entityId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="143" parent="19" name="IDX_sync_syncDate">
<index id="145" parent="19" name="IDX_sync_syncDate">
<ColNames>syncDate</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="144" parent="19">
<key id="146" parent="19">
<ColNames>id</ColNames>
<Primary>1</Primary>
</key>

21
Dockerfile Normal file
View 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" ]

8
bin/build-docker.sh Executable file
View 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 .

9
bin/push-docker-image.sh Normal file
View 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

View File

@@ -47,6 +47,7 @@ bin/package.sh
LINUX_X64_BUILD=trilium-linux-x64-$VERSION.7z
LINUX_IA32_BUILD=trilium-linux-ia32-$VERSION.7z
WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.7z
SERVER_BUILD=trilium-linux-x64-server.elf
echo "Creating release in GitHub"
@@ -75,4 +76,21 @@ github-release upload \
--name "$WINDOWS_X64_BUILD" \
--file "dist/$WINDOWS_X64_BUILD"
echo "Packaging server version"
npm run build-pkg
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!"

View 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);

View 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;

View File

@@ -115,9 +115,10 @@ CREATE INDEX IDX_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
, hash TEXT DEFAULT "" NOT NULL);
);
CREATE TABLE IF NOT EXISTS "event_log" (
`eventId` TEXT NOT NULL PRIMARY KEY,
`noteId` TEXT,
@@ -126,8 +127,7 @@ CREATE TABLE IF NOT EXISTS "event_log" (
);
CREATE TABLE IF NOT EXISTS "options"
(
optionId TEXT NOT NULL PRIMARY KEY,
name TEXT not null,
name TEXT not null PRIMARY KEY,
value TEXT,
dateModified INT,
isSynced INTEGER default 0 not null,

5992
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,12 @@
{
"name": "trilium",
"description": "Trilium Notes",
"version": "0.15.0",
"version": "0.17.0",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
"trilium": "./src/www"
},
"repository": {
"type": "git",
"url": "https://github.com/zadam/trilium.git"
@@ -17,7 +20,8 @@
"start-forge": "electron-forge start",
"package-forge": "electron-forge package",
"make-forge": "electron-forge make",
"publish-forge": "electron-forge publish"
"publish-forge": "electron-forge publish",
"build-pkg": "pkg . --targets node8-linux-x64 --output dist/trilium-linux-x64-server.elf"
},
"dependencies": {
"async-mutex": "^0.1.3",
@@ -28,7 +32,7 @@
"debug": "~3.1.0",
"devtron": "^1.4.0",
"ejs": "~2.6.1",
"electron-debug": "^1.5.0",
"electron-debug": "^2.0.0",
"electron-dl": "^1.12.0",
"electron-in-page-search": "^1.3.2",
"express": "~4.16.3",
@@ -43,8 +47,8 @@
"imagemin-pngquant": "^5.1.0",
"ini": "^1.3.5",
"jimp": "^0.2.28",
"moment": "^2.22.1",
"multer": "^1.3.0",
"moment": "^2.22.2",
"multer": "^1.3.1",
"open": "0.0.5",
"rand-token": "^0.4.0",
"rcedit": "^1.1.0",
@@ -59,18 +63,19 @@
"sqlite": "^2.9.2",
"tar-stream": "^1.6.1",
"unescape": "^1.0.1",
"ws": "^5.2.0",
"ws": "^5.2.1",
"xml2js": "^0.4.19"
},
"devDependencies": {
"electron": "^2.0.1",
"electron-compile": "^6.4.2",
"electron": "^2.0.4",
"electron-compile": "^6.4.3",
"electron-packager": "^12.1.0",
"electron-prebuilt-compile": "2.0.0",
"electron-rebuild": "^1.7.3",
"lorem-ipsum": "^1.0.4",
"tape": "^4.9.0",
"xo": "^0.21.1"
"electron-prebuilt-compile": "2.0.4",
"electron-rebuild": "^1.8.1",
"lorem-ipsum": "^1.0.5",
"tape": "^4.9.1",
"xo": "^0.21.1",
"pkg": "^4.3.3"
},
"config": {
"forge": {
@@ -109,5 +114,12 @@
"node",
"browser"
]
},
"pkg": {
"assets": [
"./db/**/*",
"./src/public/**/*",
"./src/views/**/*"
]
}
}

View File

@@ -21,10 +21,7 @@ class Entity {
contentToHash += "|" + this[propertyName];
}
// this IF is to ease the migration from before hashed options, can be later removed
if (this.constructor.tableName !== 'options' || this.isSynced) {
this["hash"] = utils.hash(contentToHash).substr(0, 10);
}
this["hash"] = utils.hash(contentToHash).substr(0, 10);
}
async save() {

View File

@@ -5,8 +5,8 @@ const dateUtils = require('../services/date_utils');
class Option extends Entity {
static get tableName() { return "options"; }
static get primaryKeyName() { return "optionId"; }
static get hashedProperties() { return ["optionId", "name", "value"]; }
static get primaryKeyName() { return "name"; }
static get hashedProperties() { return ["name", "value"]; }
beforeSaving() {
super.beforeSaving();

View File

@@ -29,7 +29,7 @@ function formatNode(node, level) {
const indentAfter = new Array(level - 1).join(' ');
let textNode;
for (const i = 0; i < node.children.length; i++) {
for (let i = 0; i < node.children.length; i++) {
textNode = document.createTextNode('\n' + indentBefore);
node.insertBefore(textNode, node.children[i]);

View File

@@ -1,9 +1,10 @@
"use strict";
import protectedSessionHolder from '../services/protected_session_holder.js';
import utils from '../services/utils.js';
import server from '../services/server.js';
import infoService from "../services/info.js";
import zoomService from "../services/zoom.js";
import utils from "../services/utils.js";
const $dialog = $("#options-dialog");
const $tabs = $("#options-tabs");
@@ -44,6 +45,41 @@ export default {
saveOptions
};
addTabHandler((function() {
const $themeSelect = $("#theme-select");
const $zoomFactorSelect = $("#zoom-factor-select");
const $html = $("html");
function optionsLoaded(options) {
$themeSelect.val(options.theme);
if (utils.isElectron()) {
$zoomFactorSelect.val(options.zoomFactor);
}
else {
$zoomFactorSelect.prop('disabled', true);
}
}
$themeSelect.change(function() {
const newTheme = $(this).val();
$html.attr("class", "theme-" + newTheme);
server.put('options/theme/' + newTheme);
});
$zoomFactorSelect.change(function() {
const newZoomFactor = $(this).val();
zoomService.setZoomFactorAndSave(newZoomFactor);
});
return {
optionsLoaded
};
})());
addTabHandler((function() {
const $form = $("#change-password-form");
const $oldPassword = $("#old-password");
@@ -137,6 +173,7 @@ addTabHandler((function () {
addTabHandler((async function () {
const $appVersion = $("#app-version");
const $dbVersion = $("#db-version");
const $syncVersion = $("#sync-version");
const $buildDate = $("#build-date");
const $buildRevision = $("#build-revision");
@@ -144,6 +181,7 @@ addTabHandler((async function () {
$appVersion.html(appInfo.appVersion);
$dbVersion.html(appInfo.dbVersion);
$syncVersion.html(appInfo.syncVersion);
$buildDate.html(appInfo.buildDate);
$buildRevision.html(appInfo.buildRevision);
$buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision);

View File

@@ -1,46 +0,0 @@
import server from './services/server.js';
$(document).ready(async () => {
const {appDbVersion, dbVersion} = await server.get('migration');
console.log("HI", {appDbVersion, dbVersion});
if (appDbVersion === dbVersion) {
$("#up-to-date").show();
}
else {
$("#need-to-migrate").show();
$("#app-db-version").html(appDbVersion);
$("#db-version").html(dbVersion);
}
});
$("#run-migration").click(async () => {
$("#run-migration").prop("disabled", true);
$("#migration-result").show();
const result = await server.post('migration');
for (const migration of result.migrations) {
const row = $('<tr>')
.append($('<td>').html(migration.dbVersion))
.append($('<td>').html(migration.name))
.append($('<td>').html(migration.success ? 'Yes' : 'No'))
.append($('<td>').html(migration.success ? 'N/A' : migration.error));
if (!migration.success) {
row.addClass("danger");
}
$("#migration-table").append(row);
}
});
// copy of this shortcut to be able to debug migration problems
$(document).bind('keydown', 'ctrl+shift+i', () => {
require('electron').remote.getCurrentWindow().toggleDevTools();
return false;
});

View File

@@ -47,7 +47,12 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
let message = "Uncaught error: ";
if (string.indexOf("script error") > -1){
if (string.includes("Cannot read property 'defaultView' of undefined")) {
// ignore this specific error which is very common but we don't know where it comes from
// and it seems to be harmless
return true;
}
else if (string.includes("script error")) {
message += 'No details available';
}
else {

View File

@@ -29,7 +29,6 @@ function registerEntrypoints() {
$("#show-note-revisions-button").click(noteRevisionsDialog.showCurrentNoteRevisions);
$("#show-source-button").click(noteSourceDialog.showDialog);
utils.bindShortcut('ctrl+u', noteSourceDialog.showDialog);
$("#recent-changes-button").click(recentChangesDialog.showDialog);
@@ -58,7 +57,13 @@ function registerEntrypoints() {
utils.bindShortcut('alt+right', window.history.forward);
}
utils.bindShortcut('alt+m', e => $(".hide-toggle").toggleClass("suppressed"));
utils.bindShortcut('alt+m', e => {
$(".hide-toggle").toggle();
// when hiding switch display to block, otherwise grid still tries to display columns which shows
// left empty column
$("#container").css("display", $("#container").css("display") === "grid" ? "block" : "grid");
});
// hide (toggle) everything except for the note content for distraction free writing
utils.bindShortcut('alt+t', e => {

View File

@@ -11,7 +11,13 @@ async function show() {
textEditor = await BalloonEditor.create($noteDetailText[0], {});
textEditor.model.document.on('change', noteDetailService.noteChanged);
textEditor.model.document.on('change', () => {
// change is triggered on just marker/selection changes which is not interesting for us
if (textEditor.model.document.differ.getChanges().length > 0) {
noteDetailService.noteChanged();
}
}
);
}
textEditor.setData(noteDetailService.getCurrentNote().content);

View File

@@ -39,7 +39,10 @@ function ensureProtectedSession(requireProtectedSession, modal) {
}
$dialog.dialog({
modal: modal,
// modal: modal,
// everything is now non-modal, because modal dialog caused weird high CPU usage on opening
// and tearing of text input
modal: false,
width: 400,
open: () => {
if (!modal) {

View File

@@ -5,7 +5,7 @@ import infoService from "./info.js";
function getHeaders() {
let protectedSessionId = null;
try { // this is because protected session might not be declared in some cases - like when it's included in migration page
try { // this is because protected session might not be declared in some cases
protectedSessionId = protectedSessionHolder.getProtectedSessionId();
}
catch(e) {}

View File

@@ -183,7 +183,7 @@ async function getRunPath(notePath) {
return effectivePath.reverse();
}
async function showParentList(noteId, node) {
async function showPaths(noteId, node) {
utils.assertArguments(noteId, node);
const note = await treeCache.getNote(noteId);
@@ -191,26 +191,21 @@ async function showParentList(noteId, node) {
$notePathCount.html(parents.length + " path" + (parents.length > 0 ? "s" : ""));
if (parents.length <= 1) {
}
else {
//$notePathList.show();
$notePathList.empty();
$notePathList.empty();
for (const parentNote of parents) {
const parentNotePath = await getSomeNotePath(parentNote);
// this is to avoid having root notes leading '/'
const notePath = parentNotePath ? (parentNotePath + '/' + noteId) : noteId;
const title = await treeUtils.getNotePathTitle(notePath);
for (const parentNote of parents) {
const parentNotePath = await getSomeNotePath(parentNote);
// this is to avoid having root notes leading '/'
const notePath = parentNotePath ? (parentNotePath + '/' + noteId) : noteId;
const title = await treeUtils.getNotePathTitle(notePath);
const item = $("<li/>").append(await linkService.createNoteLink(notePath, title));
const item = $("<li/>").append(await linkService.createNoteLink(notePath, title));
if (node.getParent().data.noteId === parentNote.noteId) {
item.addClass("current");
}
$notePathList.append(item);
if (node.getParent().data.noteId === parentNote.noteId) {
item.addClass("current");
}
$notePathList.append(item);
}
}
@@ -323,7 +318,7 @@ function initFancyTree(tree) {
noteDetailService.switchToNote(node.noteId);
showParentList(node.noteId, data.node);
showPaths(node.noteId, data.node);
},
expand: (event, data) => setExpandedToServer(data.node.data.branchId, true),
collapse: (event, data) => setExpandedToServer(data.node.data.branchId, false),

View File

@@ -2,26 +2,15 @@ import server from "./server.js";
import utils from "./utils.js";
import optionsInitService from "./options_init.js";
function decreaseZoomFactor() {
const webFrame = require('electron').webFrame;
const MIN_ZOOM = 0.5;
const MAX_ZOOM = 2.0;
if (webFrame.getZoomFactor() > 0.2) {
const webFrame = require('electron').webFrame;
const newZoomFactor = webFrame.getZoomFactor() - 0.1;
webFrame.setZoomFactor(newZoomFactor);
server.put('options/zoomFactor/' + newZoomFactor);
}
async function decreaseZoomFactor() {
await setZoomFactorAndSave(getCurrentZoom() - 0.1);
}
function increaseZoomFactor() {
const webFrame = require('electron').webFrame;
const newZoomFactor = webFrame.getZoomFactor() + 0.1;
webFrame.setZoomFactor(newZoomFactor);
server.put('options/zoomFactor/' + newZoomFactor);
async function increaseZoomFactor() {
await setZoomFactorAndSave(getCurrentZoom() + 0.1);
}
function setZoomFactor(zoomFactor) {
@@ -31,6 +20,25 @@ function setZoomFactor(zoomFactor) {
webFrame.setZoomFactor(zoomFactor);
}
async function setZoomFactorAndSave(zoomFactor) {
if (!utils.isElectron()) {
return;
}
if (zoomFactor >= MIN_ZOOM && zoomFactor <= MAX_ZOOM) {
setZoomFactor(zoomFactor);
await server.put('options/zoomFactor/' + zoomFactor);
}
else {
console.log(`Zoom factor ${zoomFactor} outside of the range, ignored.`);
}
}
function getCurrentZoom() {
return require('electron').webFrame.getZoomFactor();
}
if (utils.isElectron()) {
optionsInitService.optionsReady.then(options => setZoomFactor(options.zoomFactor))
}
@@ -38,5 +46,6 @@ if (utils.isElectron()) {
export default {
decreaseZoomFactor,
increaseZoomFactor,
setZoomFactor
setZoomFactor,
setZoomFactorAndSave
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -20,6 +20,7 @@
background-color: #f1f1f1;
display: flex;
align-items: center;
padding: 4px;
}
#note-detail-wrapper {
@@ -244,7 +245,7 @@ div.ui-tooltip {
}
.suppressed {
filter: opacity(7%);
display: none;
}
#note-type .dropdown-menu li:not(.divider) {
@@ -253,7 +254,7 @@ div.ui-tooltip {
}
.dropdown-menu li:not(.divider):hover, .dropdown-menu li:not(.divider) a:hover {
background-color: #eee !important;
background-color: #ccc !important;
cursor: pointer;
}
@@ -371,8 +372,8 @@ div.ui-tooltip {
display: flex;
}
.btn:not(.btn-primary) {
border-color: #ddd;
.btn:not(.btn-primary):not(.btn-danger) {
border-color: #bbb;
background-color: #eee;
}
@@ -382,4 +383,41 @@ div.ui-tooltip {
#note-path-list .current a {
font-weight: bold;
}
button.icon-button {
height: 28px;
width: 28px;
background: no-repeat center;
}
#note-actions {
margin-left: 10px;
margin-right: 10px;
}
#note-actions .dropdown-menu {
width: 15em;
}
/* Themes */
html.theme-black, html.theme-black img, html.theme-black video {
filter: invert(100%) hue-rotate(180deg);
}
html.theme-black body {
background: black;
}
html.theme-dark {
filter: invert(90%) hue-rotate(180deg);
}
html.theme-dark img, html.theme-dark video {
filter: invert(100%) hue-rotate(180deg);
}
html.theme-dark body {
background: #191819;
}

View File

@@ -21,10 +21,10 @@ async function loginSync(req) {
return [400, { message: 'Auth request time is out of sync' }];
}
const dbVersion = req.body.dbVersion;
const syncVersion = req.body.syncVersion;
if (dbVersion !== appInfo.dbVersion) {
return [400, { message: 'Non-matching db versions, local is version ' + appInfo.dbVersion }];
if (syncVersion !== appInfo.syncVersion) {
return [400, { message: 'Non-matching sync versions, local is version ' + appInfo.syncVersion }];
}
const documentSecret = await options.getOption('documentSecret');

View File

@@ -1,25 +0,0 @@
"use strict";
const optionService = require('../../services/options');
const migrationService = require('../../services/migration');
const appInfo = require('../../services/app_info');
async function getMigrationInfo() {
return {
dbVersion: parseInt(await optionService.getOption('dbVersion')),
appDbVersion: appInfo.dbVersion
};
}
async function executeMigration() {
const migrations = await migrationService.migrate();
return {
migrations: migrations
};
}
module.exports = {
getMigrationInfo,
executeMigration
};

View File

@@ -5,7 +5,7 @@ const optionService = require('../../services/options');
const log = require('../../services/log');
// options allowed to be updated directly in options dialog
const ALLOWED_OPTIONS = ['protectedSessionTimeout', 'noteRevisionSnapshotTimeInterval', 'zoomFactor'];
const ALLOWED_OPTIONS = ['protectedSessionTimeout', 'noteRevisionSnapshotTimeInterval', 'zoomFactor', 'theme'];
async function getOptions() {
const options = await sql.getMap("SELECT name, value FROM options WHERE name IN ("

View File

@@ -4,9 +4,11 @@ const sourceIdService = require('../services/source_id');
const sql = require('../services/sql');
const labelService = require('../services/labels');
const config = require('../services/config');
const optionService = require('../services/options');
async function index(req, res) {
res.render('index', {
theme: await optionService.getOption('theme'),
sourceId: await sourceIdService.generateSourceId(),
maxSyncIdAtLoad: await sql.getValue("SELECT MAX(id) FROM sync"),
instanceName: config.General ? config.General.instanceName : null,

View File

@@ -1,9 +0,0 @@
"use strict";
function migrationPage(req, res) {
res.render('migration', {});
}
module.exports = {
migrationPage
};

View File

@@ -1,6 +1,5 @@
const indexRoute = require('./index');
const loginRoute = require('./login');
const migrationRoute = require('./migration');
const setupRoute = require('./setup');
const multer = require('multer')();
@@ -14,7 +13,6 @@ const noteRevisionsApiRoute = require('./api/note_revisions');
const recentChangesApiRoute = require('./api/recent_changes');
const optionsApiRoute = require('./api/options');
const passwordApiRoute = require('./api/password');
const migrationApiRoute = require('./api/migration');
const syncApiRoute = require('./api/sync');
const loginApiRoute = require('./api/login');
const eventLogRoute = require('./api/event_log');
@@ -96,7 +94,6 @@ function register(app) {
route(GET, '/login', [], loginRoute.loginPage);
route(POST, '/login', [], loginRoute.login);
route(POST, '/logout', [auth.checkAuth], loginRoute.logout);
route(GET, '/migration', [auth.checkAuthForMigrationPage], migrationRoute.migrationPage);
route(GET, '/setup', [auth.checkAppNotInitialized], setupRoute.setupPage);
apiRoute(GET, '/api/tree', treeApiRoute.getTree);
@@ -180,9 +177,6 @@ function register(app) {
apiRoute(GET, '/api/search/:searchString', searchRoute.searchNotes);
apiRoute(POST, '/api/search/:searchString', searchRoute.saveSearchToNote);
route(GET, '/api/migration', [auth.checkApiAuthForMigrationPage], migrationApiRoute.getMigrationInfo, apiResultHandler);
route(POST, '/api/migration', [auth.checkApiAuthForMigrationPage], migrationApiRoute.executeMigration, apiResultHandler);
route(POST, '/api/login/sync', [], loginApiRoute.loginSync, apiResultHandler);
// this is for entering protected mode so user has to be already logged-in (that's the reason we don't require username)
apiRoute(POST, '/api/login/protected', loginApiRoute.loginToProtectedSession);

View File

@@ -3,11 +3,13 @@
const build = require('./build');
const packageJson = require('../../package');
const APP_DB_VERSION = 98;
const APP_DB_VERSION = 100;
const SYNC_VERSION = 1;
module.exports = {
appVersion: packageJson.version,
dbVersion: APP_DB_VERSION,
syncVersion: SYNC_VERSION,
buildDate: build.buildDate,
buildRevision: build.buildRevision
};

View File

@@ -12,18 +12,6 @@ async function checkAuth(req, res, next) {
else if (!req.session.loggedIn && !utils.isElectron()) {
res.redirect("login");
}
else if (!await sqlInit.isDbUpToDate()) {
res.redirect("migration");
}
else {
next();
}
}
async function checkAuthForMigrationPage(req, res, next) {
if (!req.session.loggedIn && !utils.isElectron()) {
res.redirect("login");
}
else {
next();
}
@@ -35,27 +23,12 @@ async function checkApiAuthOrElectron(req, res, next) {
if (!req.session.loggedIn && !utils.isElectron()) {
res.status(401).send("Not authorized");
}
else if (await sqlInit.isDbUpToDate()) {
next();
}
else {
res.status(409).send("Mismatched app versions"); // need better response than that
next();
}
}
async function checkApiAuth(req, res, next) {
if (!req.session.loggedIn) {
res.status(401).send("Not authorized");
}
else if (await sqlInit.isDbUpToDate()) {
next();
}
else {
res.status(409).send("Mismatched app versions"); // need better response than that
}
}
async function checkApiAuthForMigrationPage(req, res, next) {
if (!req.session.loggedIn) {
res.status(401).send("Not authorized");
}
@@ -79,19 +52,14 @@ async function checkSenderToken(req, res, next) {
if (await sql.getValue("SELECT COUNT(*) FROM api_tokens WHERE isDeleted = 0 AND token = ?", [token]) === 0) {
res.status(401).send("Not authorized");
}
else if (await sqlInit.isDbUpToDate()) {
next();
}
else {
res.status(409).send("Mismatched app versions"); // need better response than that
next();
}
}
module.exports = {
checkAuth,
checkAuthForMigrationPage,
checkApiAuth,
checkApiAuthForMigrationPage,
checkAppNotInitialized,
checkApiAuthOrElectron,
checkSenderToken

View File

@@ -1 +1 @@
module.exports = { buildDate:"2018-06-07T23:09:21-04:00", buildRevision: "bdb5e2f13f379f6b2a6fe06624239d2cf43a086f" };
module.exports = { buildDate:"2018-07-09T21:22:12+02:00", buildRevision: "14cffbbe625036d7284056f6a37a5e748e397148" };

View File

@@ -2,7 +2,6 @@ const repository = require('./repository');
const utils = require('./utils');
const dateUtils = require('./date_utils');
const appInfo = require('./app_info');
const Option = require('../entities/option');
async function getOption(name) {
const option = await repository.getOption(name);
@@ -27,6 +26,9 @@ async function setOption(name, value) {
}
async function createOption(name, value, isSynced) {
// to avoid circular dependency, need to find better solution
const Option = require('../entities/option');
await new Option({
name: name,
value: value,
@@ -55,6 +57,7 @@ async function initOptions(startNotePath) {
await createOption('lastSyncedPush', 0, false);
await createOption('zoomFactor', 1.0, false);
await createOption('theme', 'white', false);
}
module.exports = {

View File

@@ -42,7 +42,10 @@ const dbReady = new Promise((resolve, reject) => {
}
if (!await isDbUpToDate()) {
return;
// avoiding circular dependency
const migrationService = require('./migration');
await migrationService.migrate();
}
resolve(db);

View File

@@ -22,13 +22,6 @@ let syncServerCertificate = null;
async function sync() {
try {
await syncMutexService.doExclusively(async () => {
if (!await sqlInit.isDbUpToDate()) {
return {
success: false,
message: "DB not up to date"
};
}
const syncContext = await login();
await pushSync(syncContext);
@@ -76,7 +69,7 @@ async function login() {
const resp = await syncRequest(syncContext, 'POST', '/api/login/sync', {
timestamp: timestamp,
dbVersion: appInfo.dbVersion,
syncVersion: appInfo.syncVersion,
hash: hash
});
@@ -212,7 +205,7 @@ const primaryKeys = {
"note_images": "noteImageId",
"labels": "labelId",
"api_tokens": "apiTokenId",
"options": "optionId"
"options": "name"
};
async function getEntityRow(entityName, entityId) {

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="theme-<%= theme %>">
<head>
<meta charset="utf-8">
<title>Trilium Notes</title>
@@ -7,12 +7,6 @@
<body>
<div id="container" style="display:none;">
<div id="header" class="hide-toggle">
<div id="header-title">
<img src="/images/app-icons/png/24x24.png">
Trilium Notes
</div>
<div id="history-navigation" style="display: none;">
<a id="history-back-button" title="Go to previous note." class="icon-action"
style="background: url('/images/icons/back-24.png')"></a>
@@ -76,21 +70,18 @@
<div id="search-box" style="display: none; padding: 10px; margin-top: 10px;">
<div style="display: flex; align-items: center;">
<input name="search-text" placeholder="Search text, labels" style="flex-grow: 100; margin-left: 5px; margin-right: 5px;" autocomplete="off">
<button id="do-search-button" class="btn btn-sm" title="Search (enter)" style="padding: 4px;">
<img src="/images/icons/search-20.png" alt="Search"/>
</button>
<button id="do-search-button" class="btn btn-sm icon-button" title="Search (enter)"
style="background-image: url('/images/icons/search-20.png');"></button>
&nbsp;
<button id="save-search-button" class="btn btn-sm" title="Save search" style="padding: 4px;">
<img src="/images/icons/save-20.png" alt="Save search"/>
</button>
<button id="save-search-button" class="btn btn-sm icon-button" title="Save search"
style="background-image: url('/images/icons/save-20.png');"></button>
&nbsp;
<button id="close-search-button" class="btn btn-sm" title="Close search" style="padding: 4px;">
<img src="/images/icons/x-20.png" alt="Close search"/>
</button>
<button id="close-search-button" class="btn btn-sm icon-button" title="Close search"
style="background-image: url('/images/icons/x-20.png');"></button>
</div>
</div>
@@ -108,8 +99,8 @@
</div>
<div style="grid-area: title;">
<div class="hide-toggle" style="display: flex; align-items: center;">
<div class="dropdown">
<div style="display: flex; align-items: center;">
<div class="dropdown hide-toggle">
<button id="note-path-list-button" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm dropdown-toggle">
<span id="note-path-count">1 path</span>
@@ -121,75 +112,69 @@
<input autocomplete="off" value="" id="note-title" style="margin-left: 15px; font-size: x-large; border: 0; flex-grow: 100;" tabindex="1">
<span id="note-id-display" title="Note ID"></span>
<div class="hide-toggle" style="display: flex; align-items: center;">
<span id="note-id-display" title="Note ID"></span>
<button class="btn btn-sm"
style="display: none; margin-right: 10px; padding: 4px;"
title="Toggle edit"
id="toggle-edit-button">
<img src="/images/icons/edit-20.png" alt="Toggle edit"/>
</button>
<button class="btn btn-sm icon-button"
style="display: none; margin-right: 10px; background-image: url('/images/icons/edit-20.png');"
title="Toggle edit"
id="toggle-edit-button"></button>
<button class="btn btn-sm"
style="display: none; margin-right: 10px; padding: 4px;"
title="Render (Ctrl+Enter)"
id="render-button">
<img src="/images/icons/play-20.png" alt="Render"/>
</button>
<button class="btn btn-sm icon-button"
style="display: none; margin-right: 10px; background-image: url('/images/icons/play-20.png');"
title="Render (Ctrl+Enter)"
id="render-button"></button>
<button class="btn btn-sm"
style="display: none; margin-right: 10px"
id="execute-script-button">Execute <kbd>Ctrl+Enter</kbd></button>
<button class="btn btn-sm icon-button"
style="display: none; margin-right: 10px; background-image: url('/images/icons/play-20.png');"
title="Execute (Ctrl+Enter)"
id="execute-script-button"></button>
<div>
<div class="btn-group btn-group-sm">
<div>
<button type="button"
class="btn"
class="btn btn-sm icon-button"
id="protect-button"
title="Protected note can be viewed and edited only after entering password"
style="padding: 4px;">
<img src="/images/icons/shield-20.png"/>
</button>
<button type="button"
class="btn"
style="background-image: url('/images/icons/shield-20.png');">
</button><button type="button"
class="btn btn-sm icon-button"
id="unprotect-button"
title="Not protected note can be viewed without entering password"
style="padding: 4px;">
<img src="/images/icons/shield-off-20.png"/>
style="background-image: url('/images/icons/shield-off-20.png');">
</button>
</div>
</div>
&nbsp; &nbsp;
&nbsp; &nbsp;
<div class="dropdown" id="note-type" data-bind="visible: type() != 'search'">
<button data-bind="disable: isDisabled()" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm">
Type: <span data-bind="text: typeString()"></span>
<span class="caret"></span>
</button>
<ul id="note-type-dropdown" class="dropdown-menu dropdown-menu-right">
<li data-bind="click: selectText, css: { selected: type() == 'text' }"><span class="check">&check;</span> <strong>Text</strong></li>
<li role="separator" class="divider"></li>
<li data-bind="click: selectRender, css: { selected: type() == 'render' && mime() == '' }"><span class="check">&check;</span> <strong>Render HTML note</strong></li>
<li role="separator" class="divider"></li>
<li data-bind="click: selectCode, css: { selected: type() == 'code' && mime() == '' }"><span class="check">&check;</span> <strong>Code</strong></li>
<!-- ko foreach: codeMimeTypes -->
<li data-bind="click: $parent.selectCodeMime, css: { selected: $parent.type() == 'code' && $parent.mime() == mime }"><span class="check">&check;</span> <span data-bind="text: title"></span></li>
<!-- /ko -->
</ul>
</div>
<div class="dropdown" id="note-type" data-bind="visible: type() != 'search'">
<button data-bind="disable: isDisabled()" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm">
Type: <span data-bind="text: typeString()"></span>
<span class="caret"></span>
</button>
<ul id="note-type-dropdown" class="dropdown-menu dropdown-menu-right">
<li data-bind="click: selectText, css: { selected: type() == 'text' }"><span class="check">&check;</span> <strong>Text</strong></li>
<li role="separator" class="divider"></li>
<li data-bind="click: selectRender, css: { selected: type() == 'render' && mime() == '' }"><span class="check">&check;</span> <strong>Render HTML note</strong></li>
<li role="separator" class="divider"></li>
<li data-bind="click: selectCode, css: { selected: type() == 'code' && mime() == '' }"><span class="check">&check;</span> <strong>Code</strong></li>
<!-- ko foreach: codeMimeTypes -->
<li data-bind="click: $parent.selectCodeMime, css: { selected: $parent.type() == 'code' && $parent.mime() == mime }"><span class="check">&check;</span> <span data-bind="text: title"></span></li>
<!-- /ko -->
</ul>
</div>
<div class="dropdown" style="margin-left: 10px; margin-right: 10px;">
<button type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm">
Note actions
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li><a id="show-note-revisions-button">Note revisions</a></li>
<li><a class="show-labels-button"><kbd>Alt+L</kbd> Labels</a></li>
<li><a id="show-source-button"><kbd>Ctrl+U</kbd> HTML source</a></li>
<li><a id="upload-file-button">Upload file</a></li>
</ul>
<div class="dropdown" id="note-actions">
<button type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm">
Note actions
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li><a id="show-note-revisions-button">Note revisions</a></li>
<li><a class="show-labels-button"><kbd>Alt+L</kbd> Labels</a></li>
<li><a id="show-source-button">HTML source</a></li>
<li><a id="upload-file-button">Upload file</a></li>
</ul>
</div>
</div>
</div>
</div>
@@ -345,12 +330,35 @@
<div id="options-dialog" title="Options" style="display: none;">
<div id="options-tabs">
<ul>
<li><a href="#appearance">Apperance</a></li>
<li><a href="#change-password">Change password</a></li>
<li><a href="#protected-session-timeout">Protected session</a></li>
<li><a href="#note-revision-snapshot-time-interval">Note revisions</a></li>
<li><a href="#advanced">Advanced</a></li>
<li><a href="#about">About Trilium</a></li>
</ul>
<div id="appearance">
<p>Settings on this options tab are saved automatically after each change.</p>
<form>
<div class="form-group">
<label for="theme-select">Theme</label>
<select class="form-control" id="theme-select">
<option value="white">White</option>
<option value="dark">Dark</option>
<option value="black">Black</option>
</select>
</div>
<div class="form-group">
<label for="zoom-factor-select">Zoom factor (desktop build only)</label>
<input type="number" class="form-control" id="zoom-factor-select" min="0.3" max="2.0" step="0.1"/>
</div>
<p>Zooming can be controlled with CTRL-+ and CTRL-= shortcuts as well.</p>
</form>
</div>
<div id="change-password">
<form id="change-password-form">
<div class="form-group">
@@ -439,12 +447,14 @@
<th>App version:</th>
<td id="app-version"></td>
</tr>
<tr>
<th>DB version:</th>
<td id="db-version"></td>
</tr>
<tr>
<th>Sync version:</th>
<td id="sync-version"></td>
</tr>
<tr>
<th>Build date:</th>
<td id="build-date"></td>

View File

@@ -1,72 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Migration</title>
</head>
<body>
<div style="width: 800px; margin: auto;">
<h1>Migration</h1>
<div id="up-to-date" style="display:none;">
<p>Your database is up-to-date with the application.</p>
<a href="/" class="btn btn-success">Continue to app</a>
</div>
<div id="need-to-migrate" style="display:none;">
<p>Your database needs to be migrated to new version before you can use the application again.
Database will be backed up before migration in case of something going wrong.</p>
<table class="table table-bordered" style="width: 200px;">
<tr>
<th>Application version:</th>
<td id="app-db-version" style="text-align: right;"></td>
<tr>
<th>Database version:</th>
<td id="db-version" style="text-align: right;"></td>
</tr>
</table>
<button class="btn btn-warning" id="run-migration">Run migration</button>
</div>
<div id="migration-result" style="display:none;">
<h2>Migration result</h2>
<table id="migration-table" class="table">
<tr>
<th>Database version</th>
<th>Name</th>
<th>Success</th>
<th>Error</th>
</tr>
</table>
<a href="/" class="btn btn-success">Continue to app</a>
</div>
</div>
<script type="text/javascript">
const baseApiUrl = 'api/';
</script>
<!-- Required for correct loading of scripts in Electron -->
<script>
if (typeof module === 'object') {
window.module = module; module = undefined;
}
const glob = {
sourceId: ''
};
</script>
<script src="libraries/jquery.min.js"></script>
<link href="libraries/bootstrap/css/bootstrap.css" rel="stylesheet">
<script src="libraries/bootstrap/js/bootstrap.js"></script>
<script src="javascripts/migration.js" type="module"></script>
</body>
</html>