Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
4
.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
dist
|
||||||
|
.idea
|
||||||
@@ -142,11 +142,10 @@ parentNoteId</ColNames>
|
|||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="42" parent="8" name="id">
|
<column id="42" parent="8" name="eventId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>INTEGER|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
|
||||||
</column>
|
</column>
|
||||||
<column id="43" parent="8" name="noteId">
|
<column id="43" parent="8" name="noteId">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
@@ -161,505 +160,517 @@ parentNoteId</ColNames>
|
|||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<key id="46" parent="8">
|
<index id="46" parent="8" name="sqlite_autoindex_event_log_1">
|
||||||
<ColNames>id</ColNames>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
|
<ColNames>eventId</ColNames>
|
||||||
|
<ColumnCollations></ColumnCollations>
|
||||||
|
<Unique>1</Unique>
|
||||||
|
</index>
|
||||||
|
<key id="47" parent="8">
|
||||||
|
<ColNames>eventId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
|
<UnderlyingIndexName>sqlite_autoindex_event_log_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="47" parent="9" name="imageId">
|
<column id="48" parent="9" name="imageId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="48" parent="9" name="format">
|
<column id="49" parent="9" name="format">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="49" parent="9" name="checksum">
|
<column id="50" parent="9" name="checksum">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="50" parent="9" name="name">
|
<column id="51" parent="9" name="name">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="51" parent="9" name="data">
|
<column id="52" parent="9" name="data">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>BLOB|0s</DataType>
|
<DataType>BLOB|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="52" parent="9" name="isDeleted">
|
<column id="53" parent="9" name="isDeleted">
|
||||||
<Position>6</Position>
|
<Position>6</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="53" parent="9" name="dateModified">
|
<column id="54" parent="9" name="dateModified">
|
||||||
<Position>7</Position>
|
<Position>7</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="54" parent="9" name="dateCreated">
|
<column id="55" parent="9" name="dateCreated">
|
||||||
<Position>8</Position>
|
<Position>8</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="55" parent="9" name="hash">
|
<column id="56" parent="9" name="hash">
|
||||||
<Position>9</Position>
|
<Position>9</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>""</DefaultExpression>
|
<DefaultExpression>""</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<index id="56" parent="9" name="sqlite_autoindex_images_1">
|
<index id="57" parent="9" name="sqlite_autoindex_images_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>imageId</ColNames>
|
<ColNames>imageId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="57" parent="9">
|
<key id="58" parent="9">
|
||||||
<ColNames>imageId</ColNames>
|
<ColNames>imageId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_images_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_images_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="58" parent="10" name="labelId">
|
<column id="59" parent="10" name="labelId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="59" parent="10" name="noteId">
|
<column id="60" parent="10" name="noteId">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="60" parent="10" name="name">
|
<column id="61" parent="10" name="name">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="61" parent="10" name="value">
|
<column id="62" parent="10" name="value">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>''</DefaultExpression>
|
<DefaultExpression>''</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="62" parent="10" name="position">
|
<column id="63" parent="10" name="position">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="63" parent="10" name="dateCreated">
|
<column id="64" parent="10" name="dateCreated">
|
||||||
<Position>6</Position>
|
<Position>6</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="64" parent="10" name="dateModified">
|
<column id="65" parent="10" name="dateModified">
|
||||||
<Position>7</Position>
|
<Position>7</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="65" parent="10" name="isDeleted">
|
<column id="66" parent="10" name="isDeleted">
|
||||||
<Position>8</Position>
|
<Position>8</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="66" parent="10" name="hash">
|
<column id="67" parent="10" name="hash">
|
||||||
<Position>9</Position>
|
<Position>9</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>""</DefaultExpression>
|
<DefaultExpression>""</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<index id="67" parent="10" name="sqlite_autoindex_labels_1">
|
<index id="68" parent="10" name="sqlite_autoindex_labels_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>labelId</ColNames>
|
<ColNames>labelId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="68" parent="10" name="IDX_labels_noteId">
|
<index id="69" parent="10" name="IDX_labels_noteId">
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="69" parent="10" name="IDX_labels_name_value">
|
<index id="70" parent="10" name="IDX_labels_name_value">
|
||||||
<ColNames>name
|
<ColNames>name
|
||||||
value</ColNames>
|
value</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="70" parent="10">
|
<key id="71" parent="10">
|
||||||
<ColNames>labelId</ColNames>
|
<ColNames>labelId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_labels_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_labels_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="71" parent="11" name="noteImageId">
|
<column id="72" parent="11" name="noteImageId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="72" parent="11" name="noteId">
|
<column id="73" parent="11" name="noteId">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="73" parent="11" name="imageId">
|
<column id="74" parent="11" name="imageId">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="74" parent="11" name="isDeleted">
|
<column id="75" parent="11" name="isDeleted">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="75" parent="11" name="dateModified">
|
<column id="76" parent="11" name="dateModified">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="76" parent="11" name="dateCreated">
|
<column id="77" parent="11" name="dateCreated">
|
||||||
<Position>6</Position>
|
<Position>6</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="77" parent="11" name="hash">
|
<column id="78" parent="11" name="hash">
|
||||||
<Position>7</Position>
|
<Position>7</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>""</DefaultExpression>
|
<DefaultExpression>""</DefaultExpression>
|
||||||
</column>
|
</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>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>noteImageId</ColNames>
|
<ColNames>noteImageId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</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
|
<ColNames>noteId
|
||||||
imageId</ColNames>
|
imageId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="80" parent="11" name="IDX_note_images_noteId">
|
<index id="81" parent="11" name="IDX_note_images_noteId">
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="81" parent="11" name="IDX_note_images_imageId">
|
<index id="82" parent="11" name="IDX_note_images_imageId">
|
||||||
<ColNames>imageId</ColNames>
|
<ColNames>imageId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="82" parent="11">
|
<key id="83" parent="11">
|
||||||
<ColNames>noteImageId</ColNames>
|
<ColNames>noteImageId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_note_images_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_note_images_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="83" parent="12" name="noteRevisionId">
|
<column id="84" parent="12" name="noteRevisionId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="84" parent="12" name="noteId">
|
<column id="85" parent="12" name="noteId">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="85" parent="12" name="title">
|
<column id="86" parent="12" name="title">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="86" parent="12" name="content">
|
<column id="87" parent="12" name="content">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="87" parent="12" name="isProtected">
|
<column id="88" parent="12" name="isProtected">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="88" parent="12" name="dateModifiedFrom">
|
<column id="89" parent="12" name="dateModifiedFrom">
|
||||||
<Position>6</Position>
|
<Position>6</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="89" parent="12" name="dateModifiedTo">
|
<column id="90" parent="12" name="dateModifiedTo">
|
||||||
<Position>7</Position>
|
<Position>7</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="90" parent="12" name="type">
|
<column id="91" parent="12" name="type">
|
||||||
<Position>8</Position>
|
<Position>8</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>''</DefaultExpression>
|
<DefaultExpression>''</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="91" parent="12" name="mime">
|
<column id="92" parent="12" name="mime">
|
||||||
<Position>9</Position>
|
<Position>9</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>''</DefaultExpression>
|
<DefaultExpression>''</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="92" parent="12" name="hash">
|
<column id="93" parent="12" name="hash">
|
||||||
<Position>10</Position>
|
<Position>10</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>""</DefaultExpression>
|
<DefaultExpression>""</DefaultExpression>
|
||||||
</column>
|
</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>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>noteRevisionId</ColNames>
|
<ColNames>noteRevisionId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="94" parent="12" name="IDX_note_revisions_noteId">
|
<index id="95" parent="12" name="IDX_note_revisions_noteId">
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="95" parent="12" name="IDX_note_revisions_dateModifiedFrom">
|
<index id="96" parent="12" name="IDX_note_revisions_dateModifiedFrom">
|
||||||
<ColNames>dateModifiedFrom</ColNames>
|
<ColNames>dateModifiedFrom</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<index id="96" parent="12" name="IDX_note_revisions_dateModifiedTo">
|
<index id="97" parent="12" name="IDX_note_revisions_dateModifiedTo">
|
||||||
<ColNames>dateModifiedTo</ColNames>
|
<ColNames>dateModifiedTo</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="97" parent="12">
|
<key id="98" parent="12">
|
||||||
<ColNames>noteRevisionId</ColNames>
|
<ColNames>noteRevisionId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="98" parent="13" name="noteId">
|
<column id="99" parent="13" name="noteId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="99" parent="13" name="title">
|
<column id="100" parent="13" name="title">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>"unnamed"</DefaultExpression>
|
<DefaultExpression>"unnamed"</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="100" parent="13" name="content">
|
<column id="101" parent="13" name="content">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>""</DefaultExpression>
|
<DefaultExpression>""</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="101" parent="13" name="isProtected">
|
<column id="102" parent="13" name="isProtected">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="102" parent="13" name="isDeleted">
|
<column id="103" parent="13" name="isDeleted">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="103" parent="13" name="dateCreated">
|
<column id="104" parent="13" name="dateCreated">
|
||||||
<Position>6</Position>
|
<Position>6</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="104" parent="13" name="dateModified">
|
<column id="105" parent="13" name="dateModified">
|
||||||
<Position>7</Position>
|
<Position>7</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="105" parent="13" name="type">
|
<column id="106" parent="13" name="type">
|
||||||
<Position>8</Position>
|
<Position>8</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>'text'</DefaultExpression>
|
<DefaultExpression>'text'</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="106" parent="13" name="mime">
|
<column id="107" parent="13" name="mime">
|
||||||
<Position>9</Position>
|
<Position>9</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>'text/html'</DefaultExpression>
|
<DefaultExpression>'text/html'</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="107" parent="13" name="hash">
|
<column id="108" parent="13" name="hash">
|
||||||
<Position>10</Position>
|
<Position>10</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>""</DefaultExpression>
|
<DefaultExpression>""</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<index id="108" parent="13" name="sqlite_autoindex_notes_1">
|
<index id="109" parent="13" name="sqlite_autoindex_notes_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="109" parent="13" name="IDX_notes_type">
|
<index id="110" parent="13" name="IDX_notes_type">
|
||||||
<ColNames>type</ColNames>
|
<ColNames>type</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="110" parent="13">
|
<key id="111" parent="13">
|
||||||
<ColNames>noteId</ColNames>
|
<ColNames>noteId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="111" parent="14" name="name">
|
<column id="112" parent="14" name="optionId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="112" parent="14" name="value">
|
<column id="113" parent="14" name="name">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="113" parent="14" name="dateModified">
|
<column id="114" parent="14" name="value">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
|
<DataType>TEXT|0s</DataType>
|
||||||
|
</column>
|
||||||
|
<column id="115" parent="14" name="dateModified">
|
||||||
|
<Position>4</Position>
|
||||||
<DataType>INT|0s</DataType>
|
<DataType>INT|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="114" parent="14" name="isSynced">
|
<column id="116" parent="14" name="isSynced">
|
||||||
<Position>4</Position>
|
<Position>5</Position>
|
||||||
<DataType>INTEGER|0s</DataType>
|
<DataType>INTEGER|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>0</DefaultExpression>
|
<DefaultExpression>0</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="115" parent="14" name="hash">
|
<column id="117" parent="14" name="hash">
|
||||||
<Position>5</Position>
|
<Position>6</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>""</DefaultExpression>
|
<DefaultExpression>""</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<column id="116" parent="14" name="dateCreated">
|
<column id="118" parent="14" name="dateCreated">
|
||||||
<Position>6</Position>
|
<Position>7</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression>
|
<DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression>
|
||||||
</column>
|
</column>
|
||||||
<index id="117" parent="14" name="sqlite_autoindex_options_1">
|
<index id="119" parent="14" name="sqlite_autoindex_options_1">
|
||||||
<NameSurrogate>1</NameSurrogate>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>name</ColNames>
|
<ColNames>optionId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="118" parent="14">
|
<key id="120" parent="14">
|
||||||
<ColNames>name</ColNames>
|
<ColNames>optionId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="119" parent="15" name="branchId">
|
<column id="121" parent="15" name="branchId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="120" parent="15" name="notePath">
|
<column id="122" parent="15" name="notePath">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</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">
|
<column id="123" parent="15" name="hash">
|
||||||
<Position>5</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<DefaultExpression>""</DefaultExpression>
|
<DefaultExpression>""</DefaultExpression>
|
||||||
</column>
|
</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>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>branchId</ColNames>
|
<ColNames>branchId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="125" parent="15">
|
<key id="127" parent="15">
|
||||||
<ColNames>branchId</ColNames>
|
<ColNames>branchId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="126" parent="16" name="sourceId">
|
<column id="128" parent="16" name="sourceId">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="127" parent="16" name="dateCreated">
|
<column id="129" parent="16" name="dateCreated">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</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>
|
<NameSurrogate>1</NameSurrogate>
|
||||||
<ColNames>sourceId</ColNames>
|
<ColNames>sourceId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<key id="129" parent="16">
|
<key id="131" parent="16">
|
||||||
<ColNames>sourceId</ColNames>
|
<ColNames>sourceId</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
<UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName>
|
<UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName>
|
||||||
</key>
|
</key>
|
||||||
<column id="130" parent="17" name="type">
|
<column id="132" parent="17" name="type">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>text|0s</DataType>
|
<DataType>text|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="131" parent="17" name="name">
|
<column id="133" parent="17" name="name">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>text|0s</DataType>
|
<DataType>text|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="132" parent="17" name="tbl_name">
|
<column id="134" parent="17" name="tbl_name">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>text|0s</DataType>
|
<DataType>text|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="133" parent="17" name="rootpage">
|
<column id="135" parent="17" name="rootpage">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>integer|0s</DataType>
|
<DataType>integer|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="134" parent="17" name="sql">
|
<column id="136" parent="17" name="sql">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>text|0s</DataType>
|
<DataType>text|0s</DataType>
|
||||||
</column>
|
</column>
|
||||||
<column id="135" parent="18" name="name">
|
<column id="137" parent="18" name="name">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
</column>
|
</column>
|
||||||
<column id="136" parent="18" name="seq">
|
<column id="138" parent="18" name="seq">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
</column>
|
</column>
|
||||||
<column id="137" parent="19" name="id">
|
<column id="139" parent="19" name="id">
|
||||||
<Position>1</Position>
|
<Position>1</Position>
|
||||||
<DataType>INTEGER|0s</DataType>
|
<DataType>INTEGER|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
<SequenceIdentity>1</SequenceIdentity>
|
<SequenceIdentity>1</SequenceIdentity>
|
||||||
</column>
|
</column>
|
||||||
<column id="138" parent="19" name="entityName">
|
<column id="140" parent="19" name="entityName">
|
||||||
<Position>2</Position>
|
<Position>2</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="139" parent="19" name="entityId">
|
<column id="141" parent="19" name="entityId">
|
||||||
<Position>3</Position>
|
<Position>3</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="140" parent="19" name="sourceId">
|
<column id="142" parent="19" name="sourceId">
|
||||||
<Position>4</Position>
|
<Position>4</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<column id="141" parent="19" name="syncDate">
|
<column id="143" parent="19" name="syncDate">
|
||||||
<Position>5</Position>
|
<Position>5</Position>
|
||||||
<DataType>TEXT|0s</DataType>
|
<DataType>TEXT|0s</DataType>
|
||||||
<NotNull>1</NotNull>
|
<NotNull>1</NotNull>
|
||||||
</column>
|
</column>
|
||||||
<index id="142" parent="19" name="IDX_sync_entityName_entityId">
|
<index id="144" parent="19" name="IDX_sync_entityName_entityId">
|
||||||
<ColNames>entityName
|
<ColNames>entityName
|
||||||
entityId</ColNames>
|
entityId</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
<Unique>1</Unique>
|
<Unique>1</Unique>
|
||||||
</index>
|
</index>
|
||||||
<index id="143" parent="19" name="IDX_sync_syncDate">
|
<index id="145" parent="19" name="IDX_sync_syncDate">
|
||||||
<ColNames>syncDate</ColNames>
|
<ColNames>syncDate</ColNames>
|
||||||
<ColumnCollations></ColumnCollations>
|
<ColumnCollations></ColumnCollations>
|
||||||
</index>
|
</index>
|
||||||
<key id="144" parent="19">
|
<key id="146" parent="19">
|
||||||
<ColNames>id</ColNames>
|
<ColNames>id</ColNames>
|
||||||
<Primary>1</Primary>
|
<Primary>1</Primary>
|
||||||
</key>
|
</key>
|
||||||
|
|||||||
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" ]
|
||||||
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
|
||||||
|
|
||||||
|
docker build -t zadam/trilium:latest -t zadam/trilium:$1 .
|
||||||
9
bin/push-docker-image.sh
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [[ $# -eq 0 ]] ; then
|
||||||
|
echo "Missing argument of new version"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker push zadam/trilium:latest
|
||||||
|
docker push zadam/trilium:$1
|
||||||
@@ -75,4 +75,8 @@ github-release upload \
|
|||||||
--name "$WINDOWS_X64_BUILD" \
|
--name "$WINDOWS_X64_BUILD" \
|
||||||
--file "dist/$WINDOWS_X64_BUILD"
|
--file "dist/$WINDOWS_X64_BUILD"
|
||||||
|
|
||||||
|
bin/build-docker.sh $VERSION
|
||||||
|
|
||||||
|
bin/push-docker-image.sh $VERSION
|
||||||
|
|
||||||
echo "Release finished!"
|
echo "Release finished!"
|
||||||
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;
|
||||||
@@ -115,9 +115,10 @@ CREATE INDEX IDX_notes_type
|
|||||||
CREATE TABLE IF NOT EXISTS "recent_notes" (
|
CREATE TABLE IF NOT EXISTS "recent_notes" (
|
||||||
`branchId` TEXT NOT NULL PRIMARY KEY,
|
`branchId` TEXT NOT NULL PRIMARY KEY,
|
||||||
`notePath` TEXT NOT NULL,
|
`notePath` TEXT NOT NULL,
|
||||||
|
hash TEXT DEFAULT "" NOT NULL,
|
||||||
`dateCreated` TEXT NOT NULL,
|
`dateCreated` TEXT NOT NULL,
|
||||||
isDeleted INT
|
isDeleted INT
|
||||||
, hash TEXT DEFAULT "" NOT NULL);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS "event_log" (
|
CREATE TABLE IF NOT EXISTS "event_log" (
|
||||||
`eventId` TEXT NOT NULL PRIMARY KEY,
|
`eventId` TEXT NOT NULL PRIMARY KEY,
|
||||||
`noteId` TEXT,
|
`noteId` TEXT,
|
||||||
@@ -126,8 +127,7 @@ CREATE TABLE IF NOT EXISTS "event_log" (
|
|||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS "options"
|
CREATE TABLE IF NOT EXISTS "options"
|
||||||
(
|
(
|
||||||
optionId TEXT NOT NULL PRIMARY KEY,
|
name TEXT not null PRIMARY KEY,
|
||||||
name TEXT not null,
|
|
||||||
value TEXT,
|
value TEXT,
|
||||||
dateModified INT,
|
dateModified INT,
|
||||||
isSynced INTEGER default 0 not null,
|
isSynced INTEGER default 0 not null,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"description": "Trilium Notes",
|
"description": "Trilium Notes",
|
||||||
"version": "0.14.1",
|
"version": "0.16.0",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"main": "electron.js",
|
"main": "electron.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -21,10 +21,7 @@ class Entity {
|
|||||||
contentToHash += "|" + this[propertyName];
|
contentToHash += "|" + this[propertyName];
|
||||||
}
|
}
|
||||||
|
|
||||||
// this IF is to ease the migration from before hashed options, can be later removed
|
this["hash"] = utils.hash(contentToHash).substr(0, 10);
|
||||||
if (this.constructor.tableName !== 'options' || this.isSynced) {
|
|
||||||
this["hash"] = utils.hash(contentToHash).substr(0, 10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ const dateUtils = require('../services/date_utils');
|
|||||||
|
|
||||||
class Option extends Entity {
|
class Option extends Entity {
|
||||||
static get tableName() { return "options"; }
|
static get tableName() { return "options"; }
|
||||||
static get primaryKeyName() { return "optionId"; }
|
static get primaryKeyName() { return "name"; }
|
||||||
static get hashedProperties() { return ["optionId", "name", "value"]; }
|
static get hashedProperties() { return ["name", "value"]; }
|
||||||
|
|
||||||
beforeSaving() {
|
beforeSaving() {
|
||||||
super.beforeSaving();
|
super.beforeSaving();
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 511 B After Width: | Height: | Size: 511 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 |
|
Before Width: | Height: | Size: 511 B After Width: | Height: | Size: 511 B |
|
Before Width: | Height: | Size: 155 B After Width: | Height: | Size: 155 B |
|
Before Width: | Height: | Size: 323 B |
|
Before Width: | Height: | Size: 358 B After Width: | Height: | Size: 358 B |
|
Before Width: | Height: | Size: 252 B 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 |
|
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 354 B |
BIN
src/public/images/icons/shield-20.png
Normal file
|
After Width: | Height: | Size: 388 B |
BIN
src/public/images/icons/shield-off-20.png
Normal file
|
After Width: | Height: | Size: 462 B |
|
Before Width: | Height: | Size: 240 B After Width: | Height: | Size: 240 B |
|
Before Width: | Height: | Size: 337 B |
BIN
src/public/images/icons/x-20.png
Normal file
|
After Width: | Height: | Size: 259 B |
@@ -57,7 +57,15 @@ async function showDialog() {
|
|||||||
source: async function(request, response) {
|
source: async function(request, response) {
|
||||||
const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term));
|
const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term));
|
||||||
|
|
||||||
response(result);
|
if (result.length > 0) {
|
||||||
|
response(result);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
response([{
|
||||||
|
label: "No results",
|
||||||
|
value: "No results"
|
||||||
|
}]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
minLength: 2,
|
minLength: 2,
|
||||||
change: async () => {
|
change: async () => {
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import treeService from '../services/tree.js';
|
import treeService from '../services/tree.js';
|
||||||
import linkService from '../services/link.js';
|
import linkService from '../services/link.js';
|
||||||
import server from '../services/server.js';
|
import server from '../services/server.js';
|
||||||
|
import searchNotesService from '../services/search_notes.js';
|
||||||
|
|
||||||
const $dialog = $("#jump-to-note-dialog");
|
const $dialog = $("#jump-to-note-dialog");
|
||||||
const $autoComplete = $("#jump-to-note-autocomplete");
|
const $autoComplete = $("#jump-to-note-autocomplete");
|
||||||
const $form = $("#jump-to-note-form");
|
const $form = $("#jump-to-note-form");
|
||||||
|
const $jumpToNoteButton = $("#jump-to-note-button");
|
||||||
|
const $showInFullTextButton = $("#show-in-full-text-button");
|
||||||
|
|
||||||
async function showDialog() {
|
async function showDialog() {
|
||||||
glob.activeDialog = $dialog;
|
glob.activeDialog = $dialog;
|
||||||
@@ -20,7 +23,18 @@ async function showDialog() {
|
|||||||
source: async function(request, response) {
|
source: async function(request, response) {
|
||||||
const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term));
|
const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term));
|
||||||
|
|
||||||
response(result);
|
if (result.length > 0) {
|
||||||
|
response(result);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
response([{
|
||||||
|
label: "No results",
|
||||||
|
value: "No results"
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
focus: function(event, ui) {
|
||||||
|
return $(ui.item).val() !== 'No results';
|
||||||
},
|
},
|
||||||
minLength: 2
|
minLength: 2
|
||||||
});
|
});
|
||||||
@@ -41,12 +55,32 @@ function goToNote() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showInFullText(e) {
|
||||||
|
// stop from propagating upwards (dangerous especially with ctrl+enter executable javascript notes)
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const searchText = $autoComplete.val();
|
||||||
|
|
||||||
|
searchNotesService.resetSearch();
|
||||||
|
searchNotesService.showSearch();
|
||||||
|
searchNotesService.doSearch(searchText);
|
||||||
|
|
||||||
|
$dialog.dialog('close');
|
||||||
|
}
|
||||||
|
|
||||||
$form.submit(() => {
|
$form.submit(() => {
|
||||||
goToNote();
|
goToNote();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$jumpToNoteButton.click(goToNote);
|
||||||
|
|
||||||
|
$showInFullTextButton.click(showInFullText);
|
||||||
|
|
||||||
|
$dialog.bind('keydown', 'ctrl+return', showInFullText);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
showDialog
|
showDialog
|
||||||
};
|
};
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import protectedSessionHolder from '../services/protected_session_holder.js';
|
import protectedSessionHolder from '../services/protected_session_holder.js';
|
||||||
import utils from '../services/utils.js';
|
|
||||||
import server from '../services/server.js';
|
import server from '../services/server.js';
|
||||||
import infoService from "../services/info.js";
|
import infoService from "../services/info.js";
|
||||||
|
import zoomService from "../services/zoom.js";
|
||||||
|
import utils from "../services/utils.js";
|
||||||
|
|
||||||
const $dialog = $("#options-dialog");
|
const $dialog = $("#options-dialog");
|
||||||
const $tabs = $("#options-tabs");
|
const $tabs = $("#options-tabs");
|
||||||
@@ -44,6 +45,41 @@ export default {
|
|||||||
saveOptions
|
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() {
|
addTabHandler((function() {
|
||||||
const $form = $("#change-password-form");
|
const $form = $("#change-password-form");
|
||||||
const $oldPassword = $("#old-password");
|
const $oldPassword = $("#old-password");
|
||||||
@@ -137,6 +173,7 @@ addTabHandler((function () {
|
|||||||
addTabHandler((async function () {
|
addTabHandler((async function () {
|
||||||
const $appVersion = $("#app-version");
|
const $appVersion = $("#app-version");
|
||||||
const $dbVersion = $("#db-version");
|
const $dbVersion = $("#db-version");
|
||||||
|
const $syncVersion = $("#sync-version");
|
||||||
const $buildDate = $("#build-date");
|
const $buildDate = $("#build-date");
|
||||||
const $buildRevision = $("#build-revision");
|
const $buildRevision = $("#build-revision");
|
||||||
|
|
||||||
@@ -144,6 +181,7 @@ addTabHandler((async function () {
|
|||||||
|
|
||||||
$appVersion.html(appInfo.appVersion);
|
$appVersion.html(appInfo.appVersion);
|
||||||
$dbVersion.html(appInfo.dbVersion);
|
$dbVersion.html(appInfo.dbVersion);
|
||||||
|
$syncVersion.html(appInfo.syncVersion);
|
||||||
$buildDate.html(appInfo.buildDate);
|
$buildDate.html(appInfo.buildDate);
|
||||||
$buildRevision.html(appInfo.buildRevision);
|
$buildRevision.html(appInfo.buildRevision);
|
||||||
$buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision);
|
$buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision);
|
||||||
|
|||||||
@@ -1,47 +1,18 @@
|
|||||||
import treeService from '../services/tree.js';
|
import treeService from '../services/tree.js';
|
||||||
import messagingService from '../services/messaging.js';
|
|
||||||
import server from '../services/server.js';
|
import server from '../services/server.js';
|
||||||
import utils from "../services/utils.js";
|
|
||||||
import treeUtils from "../services/tree_utils.js";
|
|
||||||
|
|
||||||
const $dialog = $("#recent-notes-dialog");
|
const $dialog = $("#recent-notes-dialog");
|
||||||
const $searchInput = $('#recent-notes-search-input');
|
const $searchInput = $('#recent-notes-search-input');
|
||||||
|
|
||||||
// list of recent note paths
|
|
||||||
let list = [];
|
|
||||||
|
|
||||||
async function reload() {
|
|
||||||
const result = await server.get('recent-notes');
|
|
||||||
|
|
||||||
list = result.map(r => r.notePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addRecentNote(branchId, notePath) {
|
function addRecentNote(branchId, notePath) {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||||
if (notePath && notePath === treeService.getCurrentNotePath()) {
|
if (notePath && notePath === treeService.getCurrentNotePath()) {
|
||||||
const result = await server.put('recent-notes/' + branchId + '/' + encodeURIComponent(notePath));
|
const result = await server.put('recent-notes/' + branchId + '/' + encodeURIComponent(notePath));
|
||||||
|
|
||||||
list = result.map(r => r.notePath);
|
|
||||||
}
|
}
|
||||||
}, 1500);
|
}, 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNoteTitle(notePath) {
|
|
||||||
let noteTitle;
|
|
||||||
|
|
||||||
try {
|
|
||||||
noteTitle = await treeUtils.getNotePathTitle(notePath);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
noteTitle = "[error - can't find note title]";
|
|
||||||
|
|
||||||
messagingService.logError("Could not find title for notePath=" + notePath + ", stack=" + e.stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
return noteTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showDialog() {
|
async function showDialog() {
|
||||||
glob.activeDialog = $dialog;
|
glob.activeDialog = $dialog;
|
||||||
|
|
||||||
@@ -54,16 +25,17 @@ async function showDialog() {
|
|||||||
|
|
||||||
$searchInput.val('');
|
$searchInput.val('');
|
||||||
|
|
||||||
// remove the current note
|
const result = await server.get('recent-notes');
|
||||||
const recNotes = list.filter(note => note !== treeService.getCurrentNotePath());
|
|
||||||
const items = [];
|
|
||||||
|
|
||||||
for (const notePath of recNotes) {
|
// remove the current note
|
||||||
items.push({
|
const recNotes = result.filter(note => note.notePath !== treeService.getCurrentNotePath());
|
||||||
label: await getNoteTitle(notePath),
|
|
||||||
value: notePath
|
const items = recNotes.map(rn => {
|
||||||
});
|
return {
|
||||||
}
|
label: rn.title,
|
||||||
|
value: rn.notePath
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
$searchInput.autocomplete({
|
$searchInput.autocomplete({
|
||||||
source: items,
|
source: items,
|
||||||
@@ -96,18 +68,7 @@ async function showDialog() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(reload, 100);
|
|
||||||
|
|
||||||
messagingService.subscribeToMessages(syncData => {
|
|
||||||
if (syncData.some(sync => sync.entityName === 'recent_notes')) {
|
|
||||||
console.log(utils.now(), "Reloading recent notes because of background changes");
|
|
||||||
|
|
||||||
reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
showDialog,
|
showDialog,
|
||||||
addRecentNote,
|
addRecentNote
|
||||||
reload
|
|
||||||
};
|
};
|
||||||
@@ -6,7 +6,7 @@ class NoteShort {
|
|||||||
this.isProtected = row.isProtected;
|
this.isProtected = row.isProtected;
|
||||||
this.type = row.type;
|
this.type = row.type;
|
||||||
this.mime = row.mime;
|
this.mime = row.mime;
|
||||||
this.hideInAutocomplete = row.hideInAutocomplete;
|
this.archived = row.archived;
|
||||||
}
|
}
|
||||||
|
|
||||||
isJson() {
|
isJson() {
|
||||||
@@ -59,7 +59,7 @@ class NoteShort {
|
|||||||
get dto() {
|
get dto() {
|
||||||
const dto = Object.assign({}, this);
|
const dto = Object.assign({}, this);
|
||||||
delete dto.treeCache;
|
delete dto.treeCache;
|
||||||
delete dto.hideInAutocomplete;
|
delete dto.archived;
|
||||||
|
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
});
|
|
||||||
2
src/public/javascripts/services/bootstrap.js
vendored
@@ -17,7 +17,7 @@ import messagingService from './messaging.js';
|
|||||||
import noteDetailService from './note_detail.js';
|
import noteDetailService from './note_detail.js';
|
||||||
import noteType from './note_type.js';
|
import noteType from './note_type.js';
|
||||||
import protected_session from './protected_session.js';
|
import protected_session from './protected_session.js';
|
||||||
import searchTreeService from './search_tree.js';
|
import searchNotesService from './search_notes.js';
|
||||||
import ScriptApi from './script_api.js';
|
import ScriptApi from './script_api.js';
|
||||||
import ScriptContext from './script_context.js';
|
import ScriptContext from './script_context.js';
|
||||||
import sync from './sync.js';
|
import sync from './sync.js';
|
||||||
|
|||||||
@@ -114,13 +114,14 @@ const contextMenuOptions = {
|
|||||||
// Modify menu entries depending on node status
|
// Modify menu entries depending on node status
|
||||||
$tree.contextmenu("enableEntry", "insertNoteHere", isNotRoot && parentNote.type !== 'search');
|
$tree.contextmenu("enableEntry", "insertNoteHere", isNotRoot && parentNote.type !== 'search');
|
||||||
$tree.contextmenu("enableEntry", "insertChildNote", note.type !== 'search');
|
$tree.contextmenu("enableEntry", "insertChildNote", note.type !== 'search');
|
||||||
$tree.contextmenu("enableEntry", "delete", isNotRoot);
|
$tree.contextmenu("enableEntry", "delete", isNotRoot && parentNote.type !== 'search');
|
||||||
$tree.contextmenu("enableEntry", "copy", isNotRoot);
|
$tree.contextmenu("enableEntry", "copy", isNotRoot);
|
||||||
$tree.contextmenu("enableEntry", "cut", isNotRoot);
|
$tree.contextmenu("enableEntry", "cut", isNotRoot);
|
||||||
$tree.contextmenu("enableEntry", "pasteAfter", clipboardIds.length > 0 && isNotRoot && parentNote.type !== 'search');
|
$tree.contextmenu("enableEntry", "pasteAfter", clipboardIds.length > 0 && isNotRoot && parentNote.type !== 'search');
|
||||||
$tree.contextmenu("enableEntry", "pasteInto", clipboardIds.length > 0 && note.type !== 'search');
|
$tree.contextmenu("enableEntry", "pasteInto", clipboardIds.length > 0 && note.type !== 'search');
|
||||||
$tree.contextmenu("enableEntry", "importBranch", note.type !== 'search');
|
$tree.contextmenu("enableEntry", "importBranch", note.type !== 'search');
|
||||||
$tree.contextmenu("enableEntry", "exportBranch", note.type !== 'search');
|
$tree.contextmenu("enableEntry", "exportBranch", note.type !== 'search');
|
||||||
|
$tree.contextmenu("enableEntry", "editBranchPrefix", parentNote.type !== 'search');
|
||||||
|
|
||||||
// Activate node on right-click
|
// Activate node on right-click
|
||||||
node.setActive();
|
node.setActive();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import utils from "./utils.js";
|
|||||||
import treeService from "./tree.js";
|
import treeService from "./tree.js";
|
||||||
import linkService from "./link.js";
|
import linkService from "./link.js";
|
||||||
import fileService from "./file.js";
|
import fileService from "./file.js";
|
||||||
|
import zoomService from "./zoom.js";
|
||||||
import noteRevisionsDialog from "../dialogs/note_revisions.js";
|
import noteRevisionsDialog from "../dialogs/note_revisions.js";
|
||||||
import optionsDialog from "../dialogs/options.js";
|
import optionsDialog from "../dialogs/options.js";
|
||||||
import addLinkDialog from "../dialogs/add_link.js";
|
import addLinkDialog from "../dialogs/add_link.js";
|
||||||
@@ -10,7 +11,7 @@ import jumpToNoteDialog from "../dialogs/jump_to_note.js";
|
|||||||
import noteSourceDialog from "../dialogs/note_source.js";
|
import noteSourceDialog from "../dialogs/note_source.js";
|
||||||
import recentChangesDialog from "../dialogs/recent_changes.js";
|
import recentChangesDialog from "../dialogs/recent_changes.js";
|
||||||
import sqlConsoleDialog from "../dialogs/sql_console.js";
|
import sqlConsoleDialog from "../dialogs/sql_console.js";
|
||||||
import searchTreeService from "./search_tree.js";
|
import searchNotesService from "./search_notes.js";
|
||||||
import labelsDialog from "../dialogs/labels.js";
|
import labelsDialog from "../dialogs/labels.js";
|
||||||
import protectedSessionService from "./protected_session.js";
|
import protectedSessionService from "./protected_session.js";
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ function registerEntrypoints() {
|
|||||||
|
|
||||||
utils.bindShortcut('ctrl+l', addLinkDialog.showDialog);
|
utils.bindShortcut('ctrl+l', addLinkDialog.showDialog);
|
||||||
|
|
||||||
$("#jump-to-note-button").click(jumpToNoteDialog.showDialog);
|
$("#jump-to-note-dialog-button").click(jumpToNoteDialog.showDialog);
|
||||||
utils.bindShortcut('ctrl+j', jumpToNoteDialog.showDialog);
|
utils.bindShortcut('ctrl+j', jumpToNoteDialog.showDialog);
|
||||||
|
|
||||||
$("#show-note-revisions-button").click(noteRevisionsDialog.showCurrentNoteRevisions);
|
$("#show-note-revisions-button").click(noteRevisionsDialog.showCurrentNoteRevisions);
|
||||||
@@ -38,8 +39,8 @@ function registerEntrypoints() {
|
|||||||
$("#recent-notes-button").click(recentNotesDialog.showDialog);
|
$("#recent-notes-button").click(recentNotesDialog.showDialog);
|
||||||
utils.bindShortcut('ctrl+e', recentNotesDialog.showDialog);
|
utils.bindShortcut('ctrl+e', recentNotesDialog.showDialog);
|
||||||
|
|
||||||
$("#toggle-search-button").click(searchTreeService.toggleSearch);
|
$("#toggle-search-button").click(searchNotesService.toggleSearch);
|
||||||
utils.bindShortcut('ctrl+s', searchTreeService.toggleSearch);
|
utils.bindShortcut('ctrl+s', searchNotesService.toggleSearch);
|
||||||
|
|
||||||
$(".show-labels-button").click(labelsDialog.showDialog);
|
$(".show-labels-button").click(labelsDialog.showDialog);
|
||||||
utils.bindShortcut('alt+l', labelsDialog.showDialog);
|
utils.bindShortcut('alt+l', labelsDialog.showDialog);
|
||||||
@@ -57,7 +58,13 @@ function registerEntrypoints() {
|
|||||||
utils.bindShortcut('alt+right', window.history.forward);
|
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
|
// hide (toggle) everything except for the note content for distraction free writing
|
||||||
utils.bindShortcut('alt+t', e => {
|
utils.bindShortcut('alt+t', e => {
|
||||||
@@ -109,27 +116,10 @@ function registerEntrypoints() {
|
|||||||
$("#note-detail-text").focus();
|
$("#note-detail-text").focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).bind('keydown', 'ctrl+-', () => {
|
if (utils.isElectron()) {
|
||||||
if (utils.isElectron()) {
|
$(document).bind('keydown', 'ctrl+-', zoomService.decreaseZoomFactor);
|
||||||
const webFrame = require('electron').webFrame;
|
$(document).bind('keydown', 'ctrl+=', zoomService.increaseZoomFactor);
|
||||||
|
}
|
||||||
if (webFrame.getZoomFactor() > 0.2) {
|
|
||||||
webFrame.setZoomFactor(webFrame.getZoomFactor() - 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).bind('keydown', 'ctrl+=', () => {
|
|
||||||
if (utils.isElectron()) {
|
|
||||||
const webFrame = require('electron').webFrame;
|
|
||||||
|
|
||||||
webFrame.setZoomFactor(webFrame.getZoomFactor() + 0.1);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#note-title").bind('keydown', 'return', () => $("#note-detail-text").focus());
|
$("#note-title").bind('keydown', 'return', () => $("#note-detail-text").focus());
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const $noteDetailComponents = $(".note-detail-component");
|
|||||||
const $protectButton = $("#protect-button");
|
const $protectButton = $("#protect-button");
|
||||||
const $unprotectButton = $("#unprotect-button");
|
const $unprotectButton = $("#unprotect-button");
|
||||||
const $noteDetailWrapper = $("#note-detail-wrapper");
|
const $noteDetailWrapper = $("#note-detail-wrapper");
|
||||||
|
const $noteDetailComponentWrapper = $("#note-detail-component-wrapper");
|
||||||
const $noteIdDisplay = $("#note-id-display");
|
const $noteIdDisplay = $("#note-id-display");
|
||||||
const $labelList = $("#label-list");
|
const $labelList = $("#label-list");
|
||||||
const $labelListInner = $("#label-list-inner");
|
const $labelListInner = $("#label-list-inner");
|
||||||
@@ -116,9 +117,9 @@ async function saveNoteIfChanged() {
|
|||||||
function setNoteBackgroundIfProtected(note) {
|
function setNoteBackgroundIfProtected(note) {
|
||||||
const isProtected = !!note.isProtected;
|
const isProtected = !!note.isProtected;
|
||||||
|
|
||||||
$noteDetailWrapper.toggleClass("protected", isProtected);
|
$noteDetailComponentWrapper.toggleClass("protected", isProtected);
|
||||||
$protectButton.toggle(!isProtected);
|
$protectButton.toggleClass("active", isProtected);
|
||||||
$unprotectButton.toggle(isProtected);
|
$unprotectButton.toggleClass("active", !isProtected);
|
||||||
}
|
}
|
||||||
|
|
||||||
let isNewNoteCreated = false;
|
let isNewNoteCreated = false;
|
||||||
@@ -150,6 +151,8 @@ async function loadNoteDetail(noteId) {
|
|||||||
|
|
||||||
$noteIdDisplay.html(noteId);
|
$noteIdDisplay.html(noteId);
|
||||||
|
|
||||||
|
setNoteBackgroundIfProtected(currentNote);
|
||||||
|
|
||||||
await handleProtectedSession();
|
await handleProtectedSession();
|
||||||
|
|
||||||
$noteDetailWrapper.show();
|
$noteDetailWrapper.show();
|
||||||
@@ -170,7 +173,6 @@ async function loadNoteDetail(noteId) {
|
|||||||
noteChangeDisabled = false;
|
noteChangeDisabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setNoteBackgroundIfProtected(currentNote);
|
|
||||||
treeService.setBranchBackgroundBasedOnProtectedStatus(noteId);
|
treeService.setBranchBackgroundBasedOnProtectedStatus(noteId);
|
||||||
|
|
||||||
// after loading new note make sure editor is scrolled to the top
|
// after loading new note make sure editor is scrolled to the top
|
||||||
|
|||||||
@@ -11,7 +11,13 @@ async function show() {
|
|||||||
|
|
||||||
textEditor = await BalloonEditor.create($noteDetailText[0], {});
|
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);
|
textEditor.setData(noteDetailService.getCurrentNote().content);
|
||||||
|
|||||||
9
src/public/javascripts/services/options_init.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import server from "./server.js";
|
||||||
|
|
||||||
|
const optionsReady = new Promise((resolve, reject) => {
|
||||||
|
$(document).ready(() => server.get('options').then(resolve));
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
optionsReady
|
||||||
|
}
|
||||||
@@ -39,7 +39,10 @@ function ensureProtectedSession(requireProtectedSession, modal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$dialog.dialog({
|
$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,
|
width: 400,
|
||||||
open: () => {
|
open: () => {
|
||||||
if (!modal) {
|
if (!modal) {
|
||||||
@@ -80,11 +83,10 @@ async function setupProtectedSession() {
|
|||||||
$noteDetailWrapper.show();
|
$noteDetailWrapper.show();
|
||||||
|
|
||||||
protectedSessionDeferred.resolve();
|
protectedSessionDeferred.resolve();
|
||||||
|
protectedSessionDeferred = null;
|
||||||
|
|
||||||
$protectedSessionOnButton.addClass('active');
|
$protectedSessionOnButton.addClass('active');
|
||||||
$protectedSessionOffButton.removeClass('active');
|
$protectedSessionOffButton.removeClass('active');
|
||||||
|
|
||||||
protectedSessionDeferred = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +107,10 @@ async function enterProtectedSessionOnServer(password) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function protectNoteAndSendToServer() {
|
async function protectNoteAndSendToServer() {
|
||||||
|
if (noteDetailService.getCurrentNote().isProtected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await ensureProtectedSession(true, true);
|
await ensureProtectedSession(true, true);
|
||||||
|
|
||||||
const note = noteDetailService.getCurrentNote();
|
const note = noteDetailService.getCurrentNote();
|
||||||
@@ -118,6 +124,10 @@ async function protectNoteAndSendToServer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function unprotectNoteAndSendToServer() {
|
async function unprotectNoteAndSendToServer() {
|
||||||
|
if (!noteDetailService.getCurrentNote().isProtected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await ensureProtectedSession(true, true);
|
await ensureProtectedSession(true, true);
|
||||||
|
|
||||||
const note = noteDetailService.getCurrentNote();
|
const note = noteDetailService.getCurrentNote();
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import utils from "./utils.js";
|
import utils from "./utils.js";
|
||||||
import server from "./server.js";
|
import optionsInitService from './options_init.js';
|
||||||
|
|
||||||
let lastProtectedSessionOperationDate = null;
|
let lastProtectedSessionOperationDate = null;
|
||||||
let protectedSessionTimeout = null;
|
let protectedSessionTimeout = null;
|
||||||
let protectedSessionId = null;
|
let protectedSessionId = null;
|
||||||
|
|
||||||
$(document).ready(() => {
|
optionsInitService.optionsReady.then(options => protectedSessionTimeout = options.protectedSessionTimeout);
|
||||||
server.get('options').then(options => protectedSessionTimeout = options.protectedSessionTimeout);
|
|
||||||
});
|
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
if (lastProtectedSessionOperationDate !== null && new Date().getTime() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) {
|
if (lastProtectedSessionOperationDate !== null && new Date().getTime() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import treeService from './tree.js';
|
import treeService from './tree.js';
|
||||||
import server from './server.js';
|
import server from './server.js';
|
||||||
|
import treeUtils from "./tree_utils.js";
|
||||||
|
|
||||||
const $tree = $("#tree");
|
const $tree = $("#tree");
|
||||||
const $searchInput = $("input[name='search-text']");
|
const $searchInput = $("input[name='search-text']");
|
||||||
@@ -7,40 +8,62 @@ const $resetSearchButton = $("#reset-search-button");
|
|||||||
const $doSearchButton = $("#do-search-button");
|
const $doSearchButton = $("#do-search-button");
|
||||||
const $saveSearchButton = $("#save-search-button");
|
const $saveSearchButton = $("#save-search-button");
|
||||||
const $searchBox = $("#search-box");
|
const $searchBox = $("#search-box");
|
||||||
|
const $searchResults = $("#search-results");
|
||||||
|
const $searchResultsInner = $("#search-results-inner");
|
||||||
|
const $closeSearchButton = $("#close-search-button");
|
||||||
|
|
||||||
|
function showSearch() {
|
||||||
|
$searchBox.show();
|
||||||
|
$searchInput.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideSearch() {
|
||||||
|
resetSearch();
|
||||||
|
|
||||||
|
$searchResults.hide();
|
||||||
|
$searchBox.hide();
|
||||||
|
}
|
||||||
|
|
||||||
function toggleSearch() {
|
function toggleSearch() {
|
||||||
if ($searchBox.is(":hidden")) {
|
if ($searchBox.is(":hidden")) {
|
||||||
$searchBox.show();
|
showSearch();
|
||||||
$searchInput.focus();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resetSearch();
|
hideSearch();
|
||||||
|
|
||||||
$searchBox.hide();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetSearch() {
|
function resetSearch() {
|
||||||
$searchInput.val("");
|
$searchInput.val("");
|
||||||
|
|
||||||
getTree().clearFilter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTree() {
|
function getTree() {
|
||||||
return $tree.fancytree('getTree');
|
return $tree.fancytree('getTree');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doSearch() {
|
async function doSearch(searchText) {
|
||||||
const searchText = $searchInput.val();
|
if (searchText) {
|
||||||
|
$searchInput.val(searchText);
|
||||||
const noteIds = await server.get('search/' + encodeURIComponent(searchText));
|
}
|
||||||
|
else {
|
||||||
for (const noteId of noteIds) {
|
searchText = $searchInput.val();
|
||||||
await treeService.expandToNote(noteId, {noAnimation: true, noEvents: true});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass a string to perform case insensitive matching
|
const results = await server.get('search/' + encodeURIComponent(searchText));
|
||||||
getTree().filterBranches(node => noteIds.includes(node.data.noteId));
|
|
||||||
|
$searchResultsInner.empty();
|
||||||
|
$searchResults.show();
|
||||||
|
|
||||||
|
for (const result of results) {
|
||||||
|
const link = $('<a>', {
|
||||||
|
href: 'javascript:',
|
||||||
|
text: result.title
|
||||||
|
}).attr('action', 'note').attr('note-path', result.path);
|
||||||
|
|
||||||
|
const $result = $('<li>').append(link);
|
||||||
|
|
||||||
|
$searchResultsInner.append($result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveSearch() {
|
async function saveSearch() {
|
||||||
@@ -71,6 +94,11 @@ $resetSearchButton.click(resetSearch);
|
|||||||
|
|
||||||
$saveSearchButton.click(saveSearch);
|
$saveSearchButton.click(saveSearch);
|
||||||
|
|
||||||
|
$closeSearchButton.click(hideSearch);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
toggleSearch
|
toggleSearch,
|
||||||
|
resetSearch,
|
||||||
|
showSearch,
|
||||||
|
doSearch
|
||||||
};
|
};
|
||||||
@@ -5,7 +5,7 @@ import infoService from "./info.js";
|
|||||||
function getHeaders() {
|
function getHeaders() {
|
||||||
let protectedSessionId = null;
|
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();
|
protectedSessionId = protectedSessionHolder.getProtectedSessionId();
|
||||||
}
|
}
|
||||||
catch(e) {}
|
catch(e) {}
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ async function getRunPath(notePath) {
|
|||||||
return effectivePath.reverse();
|
return effectivePath.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showParentList(noteId, node) {
|
async function showPaths(noteId, node) {
|
||||||
utils.assertArguments(noteId, node);
|
utils.assertArguments(noteId, node);
|
||||||
|
|
||||||
const note = await treeCache.getNote(noteId);
|
const note = await treeCache.getNote(noteId);
|
||||||
@@ -191,26 +191,21 @@ async function showParentList(noteId, node) {
|
|||||||
|
|
||||||
$notePathCount.html(parents.length + " path" + (parents.length > 0 ? "s" : ""));
|
$notePathCount.html(parents.length + " path" + (parents.length > 0 ? "s" : ""));
|
||||||
|
|
||||||
if (parents.length <= 1) {
|
$notePathList.empty();
|
||||||
}
|
|
||||||
else {
|
|
||||||
//$notePathList.show();
|
|
||||||
$notePathList.empty();
|
|
||||||
|
|
||||||
for (const parentNote of parents) {
|
for (const parentNote of parents) {
|
||||||
const parentNotePath = await getSomeNotePath(parentNote);
|
const parentNotePath = await getSomeNotePath(parentNote);
|
||||||
// this is to avoid having root notes leading '/'
|
// this is to avoid having root notes leading '/'
|
||||||
const notePath = parentNotePath ? (parentNotePath + '/' + noteId) : noteId;
|
const notePath = parentNotePath ? (parentNotePath + '/' + noteId) : noteId;
|
||||||
const title = await treeUtils.getNotePathTitle(notePath);
|
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) {
|
if (node.getParent().data.noteId === parentNote.noteId) {
|
||||||
item.addClass("current");
|
item.addClass("current");
|
||||||
}
|
|
||||||
|
|
||||||
$notePathList.append(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$notePathList.append(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +318,7 @@ function initFancyTree(tree) {
|
|||||||
|
|
||||||
noteDetailService.switchToNote(node.noteId);
|
noteDetailService.switchToNote(node.noteId);
|
||||||
|
|
||||||
showParentList(node.noteId, data.node);
|
showPaths(node.noteId, data.node);
|
||||||
},
|
},
|
||||||
expand: (event, data) => setExpandedToServer(data.node.data.branchId, true),
|
expand: (event, data) => setExpandedToServer(data.node.data.branchId, true),
|
||||||
collapse: (event, data) => setExpandedToServer(data.node.data.branchId, false),
|
collapse: (event, data) => setExpandedToServer(data.node.data.branchId, false),
|
||||||
|
|||||||
@@ -74,14 +74,21 @@ async function prepareRealBranch(parentNote) {
|
|||||||
|
|
||||||
async function prepareSearchBranch(note) {
|
async function prepareSearchBranch(note) {
|
||||||
const fullNote = await noteDetailService.loadNote(note.noteId);
|
const fullNote = await noteDetailService.loadNote(note.noteId);
|
||||||
const noteIds = await server.get('search/' + encodeURIComponent(fullNote.jsonContent.searchString));
|
const results = await server.get('search/' + encodeURIComponent(fullNote.jsonContent.searchString));
|
||||||
|
|
||||||
|
const noteIds = results.map(res => res.noteId);
|
||||||
|
|
||||||
|
// force to load all the notes at once instead of one by one
|
||||||
|
await treeCache.getNotes(noteIds);
|
||||||
|
|
||||||
|
for (const result of results) {
|
||||||
|
const origBranch = await treeCache.getBranch(result.branchId);
|
||||||
|
|
||||||
for (const noteId of noteIds) {
|
|
||||||
const branch = new Branch(treeCache, {
|
const branch = new Branch(treeCache, {
|
||||||
branchId: "virt" + utils.randomString(10),
|
branchId: "virt" + utils.randomString(10),
|
||||||
noteId: noteId,
|
noteId: result.noteId,
|
||||||
parentNoteId: note.noteId,
|
parentNoteId: note.noteId,
|
||||||
prefix: '',
|
prefix: origBranch.prefix,
|
||||||
virtual: true
|
virtual: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -52,6 +52,15 @@ async function getNotePathTitle(notePath) {
|
|||||||
|
|
||||||
const titlePath = [];
|
const titlePath = [];
|
||||||
|
|
||||||
|
if (notePath.startsWith('root/')) {
|
||||||
|
notePath = notePath.substr(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// special case when we want just root's title
|
||||||
|
if (notePath === 'root') {
|
||||||
|
return await getNoteTitle(notePath);
|
||||||
|
}
|
||||||
|
|
||||||
let parentNoteId = 'root';
|
let parentNoteId = 'root';
|
||||||
|
|
||||||
for (const noteId of notePath.split('/')) {
|
for (const noteId of notePath.split('/')) {
|
||||||
|
|||||||
51
src/public/javascripts/services/zoom.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import server from "./server.js";
|
||||||
|
import utils from "./utils.js";
|
||||||
|
import optionsInitService from "./options_init.js";
|
||||||
|
|
||||||
|
const MIN_ZOOM = 0.5;
|
||||||
|
const MAX_ZOOM = 2.0;
|
||||||
|
|
||||||
|
async function decreaseZoomFactor() {
|
||||||
|
await setZoomFactorAndSave(getCurrentZoom() - 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function increaseZoomFactor() {
|
||||||
|
await setZoomFactorAndSave(getCurrentZoom() + 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setZoomFactor(zoomFactor) {
|
||||||
|
zoomFactor = parseFloat(zoomFactor);
|
||||||
|
|
||||||
|
const webFrame = require('electron').webFrame;
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
decreaseZoomFactor,
|
||||||
|
increaseZoomFactor,
|
||||||
|
setZoomFactor,
|
||||||
|
setZoomFactorAndSave
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
background-color: #f1f1f1;
|
background-color: #f1f1f1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#note-detail-wrapper {
|
#note-detail-wrapper {
|
||||||
@@ -51,7 +52,7 @@
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#note-detail-wrapper.protected, #note-detail-wrapper.protected .CodeMirror {
|
#note-detail-component-wrapper.protected, #note-detail-component-wrapper.protected .CodeMirror {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,31 +67,31 @@ ul.fancytree-container {
|
|||||||
|
|
||||||
/* icons from https://feathericons.com */
|
/* icons from https://feathericons.com */
|
||||||
span.fancytree-node > span.fancytree-icon {
|
span.fancytree-node > span.fancytree-icon {
|
||||||
background: url("../images/icons/file.png") 0 0;
|
background: url("../images/icons/file-16.png") 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.fancytree-node.fancytree-folder > span.fancytree-icon {
|
span.fancytree-node.fancytree-folder > span.fancytree-icon {
|
||||||
background: url("../images/icons/folder.png") 0 0;
|
background: url("../images/icons/folder-16.png") 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.fancytree-node.code > span.fancytree-icon {
|
span.fancytree-node.code > span.fancytree-icon {
|
||||||
background: url("../images/icons/code.png") 0 0;
|
background: url("../images/icons/code-16.png") 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.fancytree-node.fancytree-folder.code > span.fancytree-icon {
|
span.fancytree-node.fancytree-folder.code > span.fancytree-icon {
|
||||||
background: url("../images/icons/code-folder.png") 0 0;
|
background: url("../images/icons/code-folder-16.png") 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.fancytree-node.file > span.fancytree-icon {
|
span.fancytree-node.file > span.fancytree-icon {
|
||||||
background: url("../images/icons/paperclip.png") 0 0;
|
background: url("../images/icons/paperclip-16.png") 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.fancytree-node.render > span.fancytree-icon {
|
span.fancytree-node.render > span.fancytree-icon {
|
||||||
background: url("../images/icons/play.png") 0 0;
|
background: url("../images/icons/play-16.png") 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.fancytree-node.search > span.fancytree-icon {
|
span.fancytree-node.search > span.fancytree-icon {
|
||||||
background: url("../images/icons/search-small.png") 0 0;
|
background: url("../images/icons/search-small-16.png") 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.fancytree-node.protected > span.fancytree-icon {
|
span.fancytree-node.protected > span.fancytree-icon {
|
||||||
@@ -106,7 +107,7 @@ span.fancytree-node.fancytree-active-clone:not(.fancytree-active) .fancytree-tit
|
|||||||
}
|
}
|
||||||
|
|
||||||
span.fancytree-node.tree-root > span.fancytree-icon {
|
span.fancytree-node.tree-root > span.fancytree-icon {
|
||||||
background: url("../images/icons/tree-root.png") 0 0;
|
background: url("../images/icons/tree-root-16.png") 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* first nesting level has lower left padding to avoid extra left padding. Other levels are not affected */
|
/* first nesting level has lower left padding to avoid extra left padding. Other levels are not affected */
|
||||||
@@ -138,10 +139,6 @@ span.fancytree-active:not(.fancytree-focused) .fancytree-title {
|
|||||||
width: 24px;
|
width: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#protect-button, #unprotect-button {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-widget-content a:not(.ui-tabs-anchor) {
|
.ui-widget-content a:not(.ui-tabs-anchor) {
|
||||||
color: #337ab7 !important;
|
color: #337ab7 !important;
|
||||||
}
|
}
|
||||||
@@ -170,11 +167,27 @@ div.ui-tooltip {
|
|||||||
|
|
||||||
#tree {
|
#tree {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
flex-grow: 100;
|
flex-grow: 1;
|
||||||
flex-shrink: 100;
|
flex-shrink: 1;
|
||||||
|
flex-basis: 60%;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#search-results {
|
||||||
|
padding: 0 5px 5px 15px;
|
||||||
|
flex-basis: 40%;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
|
margin-top: 10px;
|
||||||
|
display: none;
|
||||||
|
overflow: auto;
|
||||||
|
border-bottom: 2px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-results ul {
|
||||||
|
padding: 5px 5px 5px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* .electron-in-page-search-window is a class specified to default
|
* .electron-in-page-search-window is a class specified to default
|
||||||
* <webview> element for search window.
|
* <webview> element for search window.
|
||||||
@@ -232,7 +245,7 @@ div.ui-tooltip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.suppressed {
|
.suppressed {
|
||||||
filter: opacity(7%);
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#note-type .dropdown-menu li:not(.divider) {
|
#note-type .dropdown-menu li:not(.divider) {
|
||||||
@@ -241,7 +254,7 @@ div.ui-tooltip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu li:not(.divider):hover, .dropdown-menu li:not(.divider) a:hover {
|
.dropdown-menu li:not(.divider):hover, .dropdown-menu li:not(.divider) a:hover {
|
||||||
background-color: #eee !important;
|
background-color: #ccc !important;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,14 +372,52 @@ div.ui-tooltip {
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn:not(.btn-primary):not(.btn-danger) {
|
||||||
border-color: #ddd;
|
border-color: #bbb;
|
||||||
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn.active {
|
.btn.active:not(.btn-primary) {
|
||||||
background-color: #ddd;
|
background-color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#note-path-list .current a {
|
#note-path-list .current a {
|
||||||
font-weight: bold;
|
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;
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const autocompleteService = require('../../services/autocomplete');
|
const noteCacheService = require('../../services/note_cache');
|
||||||
|
|
||||||
async function getAutocomplete(req) {
|
async function getAutocomplete(req) {
|
||||||
const query = req.query.query;
|
const query = req.query.query;
|
||||||
|
|
||||||
const results = autocompleteService.getResults(query);
|
const results = noteCacheService.findNotes(query);
|
||||||
|
|
||||||
return results.map(res => {
|
return results.map(res => {
|
||||||
return {
|
return {
|
||||||
value: res.title + ' (' + res.path + ')',
|
value: res.title + ' (' + res.path + ')',
|
||||||
title: res.title
|
label: res.title
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ async function loginSync(req) {
|
|||||||
return [400, { message: 'Auth request time is out of sync' }];
|
return [400, { message: 'Auth request time is out of sync' }];
|
||||||
}
|
}
|
||||||
|
|
||||||
const dbVersion = req.body.dbVersion;
|
const syncVersion = req.body.syncVersion;
|
||||||
|
|
||||||
if (dbVersion !== appInfo.dbVersion) {
|
if (syncVersion !== appInfo.syncVersion) {
|
||||||
return [400, { message: 'Non-matching db versions, local is version ' + appInfo.dbVersion }];
|
return [400, { message: 'Non-matching sync versions, local is version ' + appInfo.syncVersion }];
|
||||||
}
|
}
|
||||||
|
|
||||||
const documentSecret = await options.getOption('documentSecret');
|
const documentSecret = await options.getOption('documentSecret');
|
||||||
|
|||||||
@@ -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
|
|
||||||
};
|
|
||||||
@@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
const sql = require('../../services/sql');
|
const sql = require('../../services/sql');
|
||||||
const optionService = require('../../services/options');
|
const optionService = require('../../services/options');
|
||||||
|
const log = require('../../services/log');
|
||||||
|
|
||||||
// options allowed to be updated directly in options dialog
|
// options allowed to be updated directly in options dialog
|
||||||
const ALLOWED_OPTIONS = ['protectedSessionTimeout', 'noteRevisionSnapshotTimeInterval'];
|
const ALLOWED_OPTIONS = ['protectedSessionTimeout', 'noteRevisionSnapshotTimeInterval', 'zoomFactor', 'theme'];
|
||||||
|
|
||||||
async function getOptions() {
|
async function getOptions() {
|
||||||
const options = await sql.getMap("SELECT name, value FROM options WHERE name IN ("
|
const options = await sql.getMap("SELECT name, value FROM options WHERE name IN ("
|
||||||
@@ -20,6 +21,8 @@ async function updateOption(req) {
|
|||||||
return [400, "not allowed option to set"];
|
return [400, "not allowed option to set"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info(`Updating option ${name} to ${value}`);
|
||||||
|
|
||||||
await optionService.setOption(name, value);
|
await optionService.setOption(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const repository = require('../../services/repository');
|
const repository = require('../../services/repository');
|
||||||
const dateUtils = require('../../services/date_utils');
|
|
||||||
const optionService = require('../../services/options');
|
const optionService = require('../../services/options');
|
||||||
const RecentNote = require('../../entities/recent_note');
|
const RecentNote = require('../../entities/recent_note');
|
||||||
|
const noteCacheService = require('../../services/note_cache');
|
||||||
|
|
||||||
async function getRecentNotes() {
|
async function getRecentNotes() {
|
||||||
return await repository.getEntities(`
|
const recentNotes = await repository.getEntities(`
|
||||||
SELECT
|
SELECT
|
||||||
recent_notes.*
|
recent_notes.*
|
||||||
FROM
|
FROM
|
||||||
@@ -18,6 +18,12 @@ async function getRecentNotes() {
|
|||||||
ORDER BY
|
ORDER BY
|
||||||
dateCreated DESC
|
dateCreated DESC
|
||||||
LIMIT 200`);
|
LIMIT 200`);
|
||||||
|
|
||||||
|
for (const rn of recentNotes) {
|
||||||
|
rn.title = noteCacheService.getNoteTitleForPath(rn.notePath.split('/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return recentNotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addRecentNote(req) {
|
async function addRecentNote(req) {
|
||||||
|
|||||||
@@ -2,15 +2,69 @@
|
|||||||
|
|
||||||
const sql = require('../../services/sql');
|
const sql = require('../../services/sql');
|
||||||
const noteService = require('../../services/notes');
|
const noteService = require('../../services/notes');
|
||||||
|
const noteCacheService = require('../../services/note_cache');
|
||||||
const parseFilters = require('../../services/parse_filters');
|
const parseFilters = require('../../services/parse_filters');
|
||||||
const buildSearchQuery = require('../../services/build_search_query');
|
const buildSearchQuery = require('../../services/build_search_query');
|
||||||
|
|
||||||
async function searchNotes(req) {
|
async function searchNotes(req) {
|
||||||
const {labelFilters, searchText} = parseFilters(req.params.searchString);
|
const {labelFilters, searchText} = parseFilters(req.params.searchString);
|
||||||
|
|
||||||
const {query, params} = buildSearchQuery(labelFilters, searchText);
|
let labelFiltersNoteIds = null;
|
||||||
|
|
||||||
const noteIds = await sql.getColumn(query, params);
|
if (labelFilters.length > 0) {
|
||||||
|
const {query, params} = buildSearchQuery(labelFilters, searchText);
|
||||||
|
|
||||||
|
labelFiltersNoteIds = await sql.getColumn(query, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
let searchTextResults = null;
|
||||||
|
|
||||||
|
if (searchText.trim().length > 0) {
|
||||||
|
searchTextResults = noteCacheService.findNotes(searchText);
|
||||||
|
|
||||||
|
let fullTextNoteIds = await getFullTextResults(searchText);
|
||||||
|
|
||||||
|
for (const noteId of fullTextNoteIds) {
|
||||||
|
if (!searchTextResults.some(item => item.noteId === noteId)) {
|
||||||
|
const result = noteCacheService.getNotePath(noteId);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
searchTextResults.push(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let results;
|
||||||
|
|
||||||
|
if (labelFiltersNoteIds && searchTextResults) {
|
||||||
|
results = labelFiltersNoteIds.filter(item => searchTextResults.includes(item.noteId));
|
||||||
|
}
|
||||||
|
else if (labelFiltersNoteIds) {
|
||||||
|
results = labelFiltersNoteIds.map(noteCacheService.getNotePath).filter(res => !!res);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
results = searchTextResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getFullTextResults(searchText) {
|
||||||
|
const tokens = searchText.toLowerCase().split(" ");
|
||||||
|
const tokenSql = ["1=1"];
|
||||||
|
|
||||||
|
for (const token of tokens) {
|
||||||
|
// FIXME: escape token!
|
||||||
|
tokenSql.push(`(title LIKE '%${token}%' OR content LIKE '%${token}%')`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const noteIds = await sql.getColumn(`
|
||||||
|
SELECT DISTINCT noteId
|
||||||
|
FROM notes
|
||||||
|
WHERE isDeleted = 0
|
||||||
|
AND isProtected = 0
|
||||||
|
AND ${tokenSql.join(' AND ')}`);
|
||||||
|
|
||||||
return noteIds;
|
return noteIds;
|
||||||
}
|
}
|
||||||
@@ -20,7 +74,7 @@ async function saveSearchToNote(req) {
|
|||||||
searchString: req.params.searchString
|
searchString: req.params.searchString
|
||||||
};
|
};
|
||||||
|
|
||||||
const {note} = await noteService.createNote('root', 'Search note', noteContent, {
|
const {note} = await noteService.createNote('root', req.params.searchString, noteContent, {
|
||||||
json: true,
|
json: true,
|
||||||
type: 'search',
|
type: 'search',
|
||||||
mime: "application/json"
|
mime: "application/json"
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ const sourceIdService = require('../services/source_id');
|
|||||||
const sql = require('../services/sql');
|
const sql = require('../services/sql');
|
||||||
const labelService = require('../services/labels');
|
const labelService = require('../services/labels');
|
||||||
const config = require('../services/config');
|
const config = require('../services/config');
|
||||||
|
const optionService = require('../services/options');
|
||||||
|
|
||||||
async function index(req, res) {
|
async function index(req, res) {
|
||||||
res.render('index', {
|
res.render('index', {
|
||||||
|
theme: await optionService.getOption('theme'),
|
||||||
sourceId: await sourceIdService.generateSourceId(),
|
sourceId: await sourceIdService.generateSourceId(),
|
||||||
maxSyncIdAtLoad: await sql.getValue("SELECT MAX(id) FROM sync"),
|
maxSyncIdAtLoad: await sql.getValue("SELECT MAX(id) FROM sync"),
|
||||||
instanceName: config.General ? config.General.instanceName : null,
|
instanceName: config.General ? config.General.instanceName : null,
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
function migrationPage(req, res) {
|
|
||||||
res.render('migration', {});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
migrationPage
|
|
||||||
};
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
const indexRoute = require('./index');
|
const indexRoute = require('./index');
|
||||||
const loginRoute = require('./login');
|
const loginRoute = require('./login');
|
||||||
const migrationRoute = require('./migration');
|
|
||||||
const setupRoute = require('./setup');
|
const setupRoute = require('./setup');
|
||||||
const multer = require('multer')();
|
const multer = require('multer')();
|
||||||
|
|
||||||
@@ -14,7 +13,6 @@ const noteRevisionsApiRoute = require('./api/note_revisions');
|
|||||||
const recentChangesApiRoute = require('./api/recent_changes');
|
const recentChangesApiRoute = require('./api/recent_changes');
|
||||||
const optionsApiRoute = require('./api/options');
|
const optionsApiRoute = require('./api/options');
|
||||||
const passwordApiRoute = require('./api/password');
|
const passwordApiRoute = require('./api/password');
|
||||||
const migrationApiRoute = require('./api/migration');
|
|
||||||
const syncApiRoute = require('./api/sync');
|
const syncApiRoute = require('./api/sync');
|
||||||
const loginApiRoute = require('./api/login');
|
const loginApiRoute = require('./api/login');
|
||||||
const eventLogRoute = require('./api/event_log');
|
const eventLogRoute = require('./api/event_log');
|
||||||
@@ -96,7 +94,6 @@ function register(app) {
|
|||||||
route(GET, '/login', [], loginRoute.loginPage);
|
route(GET, '/login', [], loginRoute.loginPage);
|
||||||
route(POST, '/login', [], loginRoute.login);
|
route(POST, '/login', [], loginRoute.login);
|
||||||
route(POST, '/logout', [auth.checkAuth], loginRoute.logout);
|
route(POST, '/logout', [auth.checkAuth], loginRoute.logout);
|
||||||
route(GET, '/migration', [auth.checkAuthForMigrationPage], migrationRoute.migrationPage);
|
|
||||||
route(GET, '/setup', [auth.checkAppNotInitialized], setupRoute.setupPage);
|
route(GET, '/setup', [auth.checkAppNotInitialized], setupRoute.setupPage);
|
||||||
|
|
||||||
apiRoute(GET, '/api/tree', treeApiRoute.getTree);
|
apiRoute(GET, '/api/tree', treeApiRoute.getTree);
|
||||||
@@ -180,9 +177,6 @@ function register(app) {
|
|||||||
apiRoute(GET, '/api/search/:searchString', searchRoute.searchNotes);
|
apiRoute(GET, '/api/search/:searchString', searchRoute.searchNotes);
|
||||||
apiRoute(POST, '/api/search/:searchString', searchRoute.saveSearchToNote);
|
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);
|
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)
|
// 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);
|
apiRoute(POST, '/api/login/protected', loginApiRoute.loginToProtectedSession);
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
const build = require('./build');
|
const build = require('./build');
|
||||||
const packageJson = require('../../package');
|
const packageJson = require('../../package');
|
||||||
|
|
||||||
const APP_DB_VERSION = 96;
|
const APP_DB_VERSION = 100;
|
||||||
|
const SYNC_VERSION = 1;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
appVersion: packageJson.version,
|
appVersion: packageJson.version,
|
||||||
dbVersion: APP_DB_VERSION,
|
dbVersion: APP_DB_VERSION,
|
||||||
|
syncVersion: SYNC_VERSION,
|
||||||
buildDate: build.buildDate,
|
buildDate: build.buildDate,
|
||||||
buildRevision: build.buildRevision
|
buildRevision: build.buildRevision
|
||||||
};
|
};
|
||||||
@@ -12,18 +12,6 @@ async function checkAuth(req, res, next) {
|
|||||||
else if (!req.session.loggedIn && !utils.isElectron()) {
|
else if (!req.session.loggedIn && !utils.isElectron()) {
|
||||||
res.redirect("login");
|
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 {
|
else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
@@ -35,27 +23,12 @@ async function checkApiAuthOrElectron(req, res, next) {
|
|||||||
if (!req.session.loggedIn && !utils.isElectron()) {
|
if (!req.session.loggedIn && !utils.isElectron()) {
|
||||||
res.status(401).send("Not authorized");
|
res.status(401).send("Not authorized");
|
||||||
}
|
}
|
||||||
else if (await sqlInit.isDbUpToDate()) {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
res.status(409).send("Mismatched app versions"); // need better response than that
|
next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkApiAuth(req, res, 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) {
|
if (!req.session.loggedIn) {
|
||||||
res.status(401).send("Not authorized");
|
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) {
|
if (await sql.getValue("SELECT COUNT(*) FROM api_tokens WHERE isDeleted = 0 AND token = ?", [token]) === 0) {
|
||||||
res.status(401).send("Not authorized");
|
res.status(401).send("Not authorized");
|
||||||
}
|
}
|
||||||
else if (await sqlInit.isDbUpToDate()) {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
res.status(409).send("Mismatched app versions"); // need better response than that
|
next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
checkAuth,
|
checkAuth,
|
||||||
checkAuthForMigrationPage,
|
|
||||||
checkApiAuth,
|
checkApiAuth,
|
||||||
checkApiAuthForMigrationPage,
|
|
||||||
checkAppNotInitialized,
|
checkAppNotInitialized,
|
||||||
checkApiAuthOrElectron,
|
checkApiAuthOrElectron,
|
||||||
checkSenderToken
|
checkSenderToken
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
module.exports = { buildDate:"2018-06-02T09:39:37-04:00", buildRevision: "af529f82e5080f01b26ac7db104a8041f137dc48" };
|
module.exports = { buildDate:"2018-06-16T13:34:39-04:00", buildRevision: "3a95c9e1bc4d608a2135c9412732c2c7fbb2834c" };
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
module.exports = function(labelFilters, searchText) {
|
module.exports = function(labelFilters) {
|
||||||
const joins = [];
|
const joins = [];
|
||||||
const joinParams = [];
|
const joinParams = [];
|
||||||
let where = '1';
|
let where = '1';
|
||||||
@@ -44,16 +44,6 @@ module.exports = function(labelFilters, searchText) {
|
|||||||
let searchCondition = '';
|
let searchCondition = '';
|
||||||
const searchParams = [];
|
const searchParams = [];
|
||||||
|
|
||||||
if (searchText.trim() !== '') {
|
|
||||||
// searching in protected notes is pointless because of encryption
|
|
||||||
searchCondition = ' AND (notes.isProtected = 0 AND (notes.title LIKE ? OR notes.content LIKE ?))';
|
|
||||||
|
|
||||||
searchText = '%' + searchText.trim() + '%';
|
|
||||||
|
|
||||||
searchParams.push(searchText);
|
|
||||||
searchParams.push(searchText); // two occurences in searchCondition
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = `SELECT DISTINCT notes.noteId FROM notes
|
const query = `SELECT DISTINCT notes.noteId FROM notes
|
||||||
${joins.join('\r\n')}
|
${joins.join('\r\n')}
|
||||||
WHERE
|
WHERE
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const Label = require('../entities/label');
|
|||||||
const BUILTIN_LABELS = [
|
const BUILTIN_LABELS = [
|
||||||
'disableVersioning',
|
'disableVersioning',
|
||||||
'calendarRoot',
|
'calendarRoot',
|
||||||
'hideInAutocomplete',
|
'archived',
|
||||||
'excludeFromExport',
|
'excludeFromExport',
|
||||||
'run',
|
'run',
|
||||||
'manualTransactionHandling',
|
'manualTransactionHandling',
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ const utils = require('./utils');
|
|||||||
let noteTitles;
|
let noteTitles;
|
||||||
let protectedNoteTitles;
|
let protectedNoteTitles;
|
||||||
let noteIds;
|
let noteIds;
|
||||||
|
let childParentToBranchId = {};
|
||||||
const childToParent = {};
|
const childToParent = {};
|
||||||
const hideInAutocomplete = {};
|
const archived = {};
|
||||||
|
|
||||||
// key is 'childNoteId-parentNoteId' as a replacement for branchId which we don't use here
|
// key is 'childNoteId-parentNoteId' as a replacement for branchId which we don't use here
|
||||||
let prefixes = {};
|
let prefixes = {};
|
||||||
@@ -20,21 +21,22 @@ async function load() {
|
|||||||
|
|
||||||
prefixes = await sql.getMap(`SELECT noteId || '-' || parentNoteId, prefix FROM branches WHERE prefix IS NOT NULL AND prefix != ''`);
|
prefixes = await sql.getMap(`SELECT noteId || '-' || parentNoteId, prefix FROM branches WHERE prefix IS NOT NULL AND prefix != ''`);
|
||||||
|
|
||||||
const relations = await sql.getRows(`SELECT noteId, parentNoteId FROM branches WHERE isDeleted = 0`);
|
const relations = await sql.getRows(`SELECT branchId, noteId, parentNoteId FROM branches WHERE isDeleted = 0`);
|
||||||
|
|
||||||
for (const rel of relations) {
|
for (const rel of relations) {
|
||||||
childToParent[rel.noteId] = childToParent[rel.noteId] || [];
|
childToParent[rel.noteId] = childToParent[rel.noteId] || [];
|
||||||
childToParent[rel.noteId].push(rel.parentNoteId);
|
childToParent[rel.noteId].push(rel.parentNoteId);
|
||||||
|
childParentToBranchId[`${rel.noteId}-${rel.parentNoteId}`] = rel.branchId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hiddenLabels = await sql.getColumn(`SELECT noteId FROM labels WHERE isDeleted = 0 AND name = 'hideInAutocomplete'`);
|
const hiddenLabels = await sql.getColumn(`SELECT noteId FROM labels WHERE isDeleted = 0 AND name = 'archived'`);
|
||||||
|
|
||||||
for (const noteId of hiddenLabels) {
|
for (const noteId of hiddenLabels) {
|
||||||
hideInAutocomplete[noteId] = true;
|
archived[noteId] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getResults(query) {
|
function findNotes(query) {
|
||||||
if (!noteTitles || query.length <= 2) {
|
if (!noteTitles || query.length <= 2) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -49,7 +51,7 @@ function getResults(query) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const noteId of noteIds) {
|
for (const noteId of noteIds) {
|
||||||
if (hideInAutocomplete[noteId]) {
|
if (archived[noteId]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +61,7 @@ function getResults(query) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const parentNoteId of parents) {
|
for (const parentNoteId of parents) {
|
||||||
if (hideInAutocomplete[parentNoteId]) {
|
if (archived[parentNoteId]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,8 +93,12 @@ function search(noteId, tokens, path, results) {
|
|||||||
|
|
||||||
if (retPath) {
|
if (retPath) {
|
||||||
const noteTitle = getNoteTitleForPath(retPath);
|
const noteTitle = getNoteTitleForPath(retPath);
|
||||||
|
const thisNoteId = retPath[retPath.length - 1];
|
||||||
|
const thisParentNoteId = retPath[retPath.length - 2];
|
||||||
|
|
||||||
results.push({
|
results.push({
|
||||||
|
noteId: thisNoteId,
|
||||||
|
branchId: childParentToBranchId[`${thisNoteId}-${thisParentNoteId}`],
|
||||||
title: noteTitle,
|
title: noteTitle,
|
||||||
path: retPath.join('/')
|
path: retPath.join('/')
|
||||||
});
|
});
|
||||||
@@ -111,7 +117,7 @@ function search(noteId, tokens, path, results) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentNoteId === 'root' || hideInAutocomplete[parentNoteId]) {
|
if (parentNoteId === 'root' || archived[parentNoteId]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +161,15 @@ function getNoteTitle(noteId, parentNoteId) {
|
|||||||
function getNoteTitleForPath(path) {
|
function getNoteTitleForPath(path) {
|
||||||
const titles = [];
|
const titles = [];
|
||||||
|
|
||||||
|
if (path[0] === 'root') {
|
||||||
|
if (path.length === 1) {
|
||||||
|
return getNoteTitle('root');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
path = path.slice(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let parentNoteId = 'root';
|
let parentNoteId = 'root';
|
||||||
|
|
||||||
for (const noteId of path) {
|
for (const noteId of path) {
|
||||||
@@ -180,6 +195,10 @@ function getSomePath(noteId, path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const parentNoteId of parents) {
|
for (const parentNoteId of parents) {
|
||||||
|
if (archived[parentNoteId]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const retPath = getSomePath(parentNoteId, path.concat([noteId]));
|
const retPath = getSomePath(parentNoteId, path.concat([noteId]));
|
||||||
|
|
||||||
if (retPath) {
|
if (retPath) {
|
||||||
@@ -190,6 +209,22 @@ function getSomePath(noteId, path) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getNotePath(noteId) {
|
||||||
|
const retPath = getSomePath(noteId, []);
|
||||||
|
|
||||||
|
if (retPath) {
|
||||||
|
const noteTitle = getNoteTitleForPath(retPath);
|
||||||
|
const parentNoteId = childToParent[noteId][0];
|
||||||
|
|
||||||
|
return {
|
||||||
|
noteId: noteId,
|
||||||
|
branchId: childParentToBranchId[`${noteId}-${parentNoteId}`],
|
||||||
|
title: noteTitle,
|
||||||
|
path: retPath.join('/')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId}) => {
|
eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId}) => {
|
||||||
if (entityName === 'notes') {
|
if (entityName === 'notes') {
|
||||||
const note = await repository.getNote(entityId);
|
const note = await repository.getNote(entityId);
|
||||||
@@ -211,6 +246,7 @@ eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId
|
|||||||
|
|
||||||
if (branch.isDeleted) {
|
if (branch.isDeleted) {
|
||||||
delete prefixes[branch.noteId + '-' + branch.parentNoteId];
|
delete prefixes[branch.noteId + '-' + branch.parentNoteId];
|
||||||
|
delete childParentToBranchId[branch.noteId + '-' + branch.parentNoteId];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (branch.prefix) {
|
if (branch.prefix) {
|
||||||
@@ -219,21 +255,22 @@ eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entityId
|
|||||||
|
|
||||||
childToParent[branch.noteId] = childToParent[branch.noteId] || [];
|
childToParent[branch.noteId] = childToParent[branch.noteId] || [];
|
||||||
childToParent[branch.noteId].push(branch.parentNoteId);
|
childToParent[branch.noteId].push(branch.parentNoteId);
|
||||||
|
childParentToBranchId[branch.noteId + '-' + branch.parentNoteId] = branch.branchId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (entityName === 'labels') {
|
else if (entityName === 'labels') {
|
||||||
const label = await repository.getLabel(entityId);
|
const label = await repository.getLabel(entityId);
|
||||||
|
|
||||||
if (label.name === 'hideInAutocomplete') {
|
if (label.name === 'archived') {
|
||||||
// we're not using label object directly, since there might be other non-deleted hideInAutocomplete label
|
// we're not using label object directly, since there might be other non-deleted archived label
|
||||||
const hideLabel = await repository.getEntity(`SELECT * FROM labels WHERE isDeleted = 0
|
const hideLabel = await repository.getEntity(`SELECT * FROM labels WHERE isDeleted = 0
|
||||||
AND name = 'hideInAutocomplete' AND noteId = ?`, [label.noteId]);
|
AND name = 'archived' AND noteId = ?`, [label.noteId]);
|
||||||
|
|
||||||
if (hideLabel) {
|
if (hideLabel) {
|
||||||
hideInAutocomplete[label.noteId] = true;
|
archived[label.noteId] = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
delete hideInAutocomplete[label.noteId];
|
delete archived[label.noteId];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,5 +287,7 @@ eventService.subscribe(eventService.ENTER_PROTECTED_SESSION, async () => {
|
|||||||
sqlInit.dbReady.then(() => utils.stopWatch("Autocomplete load", load));
|
sqlInit.dbReady.then(() => utils.stopWatch("Autocomplete load", load));
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getResults
|
findNotes,
|
||||||
|
getNotePath,
|
||||||
|
getNoteTitleForPath
|
||||||
};
|
};
|
||||||
@@ -2,7 +2,6 @@ const repository = require('./repository');
|
|||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const dateUtils = require('./date_utils');
|
const dateUtils = require('./date_utils');
|
||||||
const appInfo = require('./app_info');
|
const appInfo = require('./app_info');
|
||||||
const Option = require('../entities/option');
|
|
||||||
|
|
||||||
async function getOption(name) {
|
async function getOption(name) {
|
||||||
const option = await repository.getOption(name);
|
const option = await repository.getOption(name);
|
||||||
@@ -27,6 +26,9 @@ async function setOption(name, value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function createOption(name, value, isSynced) {
|
async function createOption(name, value, isSynced) {
|
||||||
|
// to avoid circular dependency, need to find better solution
|
||||||
|
const Option = require('../entities/option');
|
||||||
|
|
||||||
await new Option({
|
await new Option({
|
||||||
name: name,
|
name: name,
|
||||||
value: value,
|
value: value,
|
||||||
@@ -53,6 +55,9 @@ async function initOptions(startNotePath) {
|
|||||||
|
|
||||||
await createOption('lastSyncedPull', appInfo.dbVersion, false);
|
await createOption('lastSyncedPull', appInfo.dbVersion, false);
|
||||||
await createOption('lastSyncedPush', 0, false);
|
await createOption('lastSyncedPush', 0, false);
|
||||||
|
|
||||||
|
await createOption('zoomFactor', 1.0, false);
|
||||||
|
await createOption('theme', 'white', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -42,7 +42,10 @@ const dbReady = new Promise((resolve, reject) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!await isDbUpToDate()) {
|
if (!await isDbUpToDate()) {
|
||||||
return;
|
// avoiding circular dependency
|
||||||
|
const migrationService = require('./migration');
|
||||||
|
|
||||||
|
await migrationService.migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(db);
|
resolve(db);
|
||||||
|
|||||||
@@ -22,13 +22,6 @@ let syncServerCertificate = null;
|
|||||||
async function sync() {
|
async function sync() {
|
||||||
try {
|
try {
|
||||||
await syncMutexService.doExclusively(async () => {
|
await syncMutexService.doExclusively(async () => {
|
||||||
if (!await sqlInit.isDbUpToDate()) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
message: "DB not up to date"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const syncContext = await login();
|
const syncContext = await login();
|
||||||
|
|
||||||
await pushSync(syncContext);
|
await pushSync(syncContext);
|
||||||
@@ -76,7 +69,7 @@ async function login() {
|
|||||||
|
|
||||||
const resp = await syncRequest(syncContext, 'POST', '/api/login/sync', {
|
const resp = await syncRequest(syncContext, 'POST', '/api/login/sync', {
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
dbVersion: appInfo.dbVersion,
|
syncVersion: appInfo.syncVersion,
|
||||||
hash: hash
|
hash: hash
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -212,7 +205,7 @@ const primaryKeys = {
|
|||||||
"note_images": "noteImageId",
|
"note_images": "noteImageId",
|
||||||
"labels": "labelId",
|
"labels": "labelId",
|
||||||
"api_tokens": "apiTokenId",
|
"api_tokens": "apiTokenId",
|
||||||
"options": "optionId"
|
"options": "name"
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getEntityRow(entityName, entityId) {
|
async function getEntityRow(entityName, entityId) {
|
||||||
|
|||||||
@@ -79,6 +79,32 @@ function stripTags(text) {
|
|||||||
return text.replace(/<(?:.|\n)*?>/gm, '');
|
return text.replace(/<(?:.|\n)*?>/gm, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function intersection(a, b) {
|
||||||
|
return a.filter(value => b.indexOf(value) !== -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function union(a, b) {
|
||||||
|
const obj = {};
|
||||||
|
|
||||||
|
for (let i = a.length-1; i >= 0; i--) {
|
||||||
|
obj[a[i]] = a[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = b.length-1; i >= 0; i--) {
|
||||||
|
obj[b[i]] = b[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = [];
|
||||||
|
|
||||||
|
for (const k in obj) {
|
||||||
|
if (obj.hasOwnProperty(k)) { // <-- optional
|
||||||
|
res.push(obj[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
randomSecureToken,
|
randomSecureToken,
|
||||||
randomString,
|
randomString,
|
||||||
@@ -93,5 +119,7 @@ module.exports = {
|
|||||||
stopWatch,
|
stopWatch,
|
||||||
unescapeHtml,
|
unescapeHtml,
|
||||||
toObject,
|
toObject,
|
||||||
stripTags
|
stripTags,
|
||||||
|
intersection,
|
||||||
|
union
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" class="theme-<%= theme %>">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Trilium Notes</title>
|
<title>Trilium Notes</title>
|
||||||
@@ -7,24 +7,18 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="container" style="display:none;">
|
<div id="container" style="display:none;">
|
||||||
<div id="header" class="hide-toggle">
|
<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;">
|
<div id="history-navigation" style="display: none;">
|
||||||
<a id="history-back-button" title="Go to previous note." class="icon-action"
|
<a id="history-back-button" title="Go to previous note." class="icon-action"
|
||||||
style="background: url('/images/icons/back.png')"></a>
|
style="background: url('/images/icons/back-24.png')"></a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a id="history-forward-button" title="Go to next note." class="icon-action"
|
<a id="history-forward-button" title="Go to next note." class="icon-action"
|
||||||
style="background: url('/images/icons/forward.png')"></a>
|
style="background: url('/images/icons/forward-24.png')"></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="flex-grow: 100; display: flex;">
|
<div style="flex-grow: 100; display: flex;">
|
||||||
<button class="btn btn-xs" id="jump-to-note-button" title="CTRL+J">Jump to note</button>
|
<button class="btn btn-xs" id="jump-to-note-dialog-button" title="CTRL+J">Jump to note</button>
|
||||||
<button class="btn btn-xs" id="recent-notes-button" title="CTRL+E">Recent notes</button>
|
<button class="btn btn-xs" id="recent-notes-button" title="CTRL+E">Recent notes</button>
|
||||||
<button class="btn btn-xs" id="recent-changes-button">Recent changes</button>
|
<button class="btn btn-xs" id="recent-changes-button">Recent changes</button>
|
||||||
<div>
|
<div>
|
||||||
@@ -57,18 +51,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="grid-area: left-pane; display: flex; flex-direction: column;" class="hide-toggle">
|
<div style="grid-area: left-pane; display: flex; flex-direction: column;" class="hide-toggle">
|
||||||
<div style="display: flex; justify-content: space-around; padding: 10px 0 10px 0; margin: 0 20px 0 20px; border: 1px solid #ccc;">
|
<div style="display: flex; justify-content: space-around; padding: 10px 0 10px 0; margin: 0 10px 0 16px; border: 1px solid #ccc;">
|
||||||
<a id="create-top-level-note-button" title="Create new top level note" class="icon-action"
|
<a id="create-top-level-note-button" title="Create new top level note" class="icon-action"
|
||||||
style="background: url('/images/icons/file-plus.png')"></a>
|
style="background: url('/images/icons/file-plus-24.png')"></a>
|
||||||
|
|
||||||
<a id="collapse-tree-button" title="Collapse note tree" class="icon-action"
|
<a id="collapse-tree-button" title="Collapse note tree" class="icon-action"
|
||||||
style="background: url('/images/icons/list.png')"></a>
|
style="background: url('/images/icons/list-24.png')"></a>
|
||||||
|
|
||||||
<a id="scroll-to-current-note-button" title="Scroll to current note. Shortcut CTRL+." class="icon-action"
|
<a id="scroll-to-current-note-button" title="Scroll to current note. Shortcut CTRL+." class="icon-action"
|
||||||
style="background: url('/images/icons/crosshair.png')"></a>
|
style="background: url('/images/icons/crosshair-24.png')"></a>
|
||||||
|
|
||||||
<a id="toggle-search-button" title="Search in notes" class="icon-action"
|
<a id="toggle-search-button" title="Search in notes" class="icon-action"
|
||||||
style="background: url('/images/icons/search.png')"></a>
|
style="background: url('/images/icons/search-24.png')"></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input type="file" id="import-upload" style="display: none" />
|
<input type="file" id="import-upload" style="display: none" />
|
||||||
@@ -76,22 +70,37 @@
|
|||||||
<div id="search-box" style="display: none; padding: 10px; margin-top: 10px;">
|
<div id="search-box" style="display: none; padding: 10px; margin-top: 10px;">
|
||||||
<div style="display: flex; align-items: center;">
|
<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">
|
<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-primary btn-sm" title="Search">Search</button>
|
<button id="do-search-button" class="btn btn-sm icon-button" title="Search (enter)"
|
||||||
</div>
|
style="background-image: url('/images/icons/search-20.png');"></button>
|
||||||
|
|
||||||
<div style="display: flex; align-items: center; justify-content: space-evenly; margin-top: 10px;">
|
|
||||||
<button id="reset-search-button" class="btn btn-sm" title="Reset search">Reset search</button>
|
|
||||||
|
|
||||||
<button id="save-search-button" class="btn btn-sm" title="Save search">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>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="search-results">
|
||||||
|
<strong>Search results:</strong>
|
||||||
|
|
||||||
|
<ul id="search-results-inner">
|
||||||
|
<li>aaa</li>
|
||||||
|
<li>bbb</li>
|
||||||
|
<li>ccc</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="tree"></div>
|
<div id="tree"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="grid-area: title;">
|
<div style="grid-area: title;">
|
||||||
<div class="hide-toggle" style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center;">
|
||||||
<div class="dropdown">
|
<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">
|
<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>
|
<span id="note-path-count">1 path</span>
|
||||||
|
|
||||||
@@ -103,60 +112,69 @@
|
|||||||
|
|
||||||
<input autocomplete="off" value="" id="note-title" style="margin-left: 15px; font-size: x-large; border: 0; flex-grow: 100;" tabindex="1">
|
<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>
|
||||||
|
|
||||||
<a title="Protect the note so that password will be required to view the note"
|
<button class="btn btn-sm icon-button"
|
||||||
class="icon-action"
|
style="display: none; margin-right: 10px; background-image: url('/images/icons/edit-20.png');"
|
||||||
id="protect-button"
|
title="Toggle edit"
|
||||||
style="display: none; background: url('images/icons/lock.png')"></a>
|
id="toggle-edit-button"></button>
|
||||||
|
|
||||||
<a title="Unprotect note so that password will not be required to access this note in the future"
|
<button class="btn btn-sm icon-button"
|
||||||
class="icon-action"
|
style="display: none; margin-right: 10px; background-image: url('/images/icons/play-20.png');"
|
||||||
id="unprotect-button"
|
title="Render (Ctrl+Enter)"
|
||||||
style="display: none; background: url('images/icons/unlock.png')"></a>
|
id="render-button"></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>
|
||||||
|
|
||||||
<button class="btn btn-sm"
|
<div>
|
||||||
style="display: none; margin-right: 10px"
|
<button type="button"
|
||||||
id="toggle-edit-button">Toggle edit</button>
|
class="btn btn-sm icon-button"
|
||||||
|
id="protect-button"
|
||||||
|
title="Protected note can be viewed and edited only after entering password"
|
||||||
|
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="background-image: url('/images/icons/shield-off-20.png');">
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-sm"
|
|
||||||
style="display: none; margin-right: 10px"
|
|
||||||
id="render-button">Render <kbd>Ctrl+Enter</kbd></button>
|
|
||||||
|
|
||||||
<button class="btn btn-sm"
|
<div class="dropdown" id="note-type" data-bind="visible: type() != 'search'">
|
||||||
style="display: none; margin-right: 10px"
|
<button data-bind="disable: isDisabled()" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm">
|
||||||
id="execute-script-button">Execute <kbd>Ctrl+Enter</kbd></button>
|
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">✓</span> <strong>Text</strong></li>
|
||||||
|
<li role="separator" class="divider"></li>
|
||||||
|
<li data-bind="click: selectRender, css: { selected: type() == 'render' && mime() == '' }"><span class="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">✓</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">✓</span> <span data-bind="text: title"></span></li>
|
||||||
|
<!-- /ko -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="dropdown" id="note-type" data-bind="visible: type() != 'search'">
|
<div class="dropdown" id="note-actions">
|
||||||
<button data-bind="disable: isDisabled()" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm">
|
<button type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm">
|
||||||
Type: <span data-bind="text: typeString()"></span>
|
Note actions
|
||||||
<span class="caret"></span>
|
<span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul id="note-type-dropdown" class="dropdown-menu dropdown-menu-right">
|
<ul class="dropdown-menu dropdown-menu-right">
|
||||||
<li data-bind="click: selectText, css: { selected: type() == 'text' }"><span class="check">✓</span> <strong>Text</strong></li>
|
<li><a id="show-note-revisions-button">Note revisions</a></li>
|
||||||
<li role="separator" class="divider"></li>
|
<li><a class="show-labels-button"><kbd>Alt+L</kbd> Labels</a></li>
|
||||||
<li data-bind="click: selectRender, css: { selected: type() == 'render' && mime() == '' }"><span class="check">✓</span> <strong>Render HTML note</strong></li>
|
<li><a id="show-source-button"><kbd>Ctrl+U</kbd> HTML source</a></li>
|
||||||
<li role="separator" class="divider"></li>
|
<li><a id="upload-file-button">Upload file</a></li>
|
||||||
<li data-bind="click: selectCode, css: { selected: type() == 'code' && mime() == '' }"><span class="check">✓</span> <strong>Code</strong></li>
|
</ul>
|
||||||
<!-- ko foreach: codeMimeTypes -->
|
</div>
|
||||||
<li data-bind="click: $parent.selectCodeMime, css: { selected: $parent.type() == 'code' && $parent.mime() == mime }"><span class="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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -248,7 +266,7 @@
|
|||||||
<input id="recent-notes-search-input" class="form-control"/>
|
<input id="recent-notes-search-input" class="form-control"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="add-link-dialog" title="Add link" style="display: none;">
|
<div id="add-link-dialog" title="Add note link" style="display: none;">
|
||||||
<form id="add-link-form">
|
<form id="add-link-form">
|
||||||
<div id="add-link-type-div" class="radio">
|
<div id="add-link-type-div" class="radio">
|
||||||
<label title="Add HTML link to the selected note at cursor in current note">
|
<label title="Add HTML link to the selected note at cursor in current note">
|
||||||
@@ -266,7 +284,7 @@
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="note-autocomplete">Note</label>
|
<label for="note-autocomplete">Note</label>
|
||||||
<input id="note-autocomplete" style="width: 100%;">
|
<input id="note-autocomplete" placeholder="search for note by its name" style="width: 100%;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group" id="add-link-title-form-group">
|
<div class="form-group" id="add-link-title-form-group">
|
||||||
@@ -279,7 +297,7 @@
|
|||||||
<input id="clone-prefix" style="width: 100%;">
|
<input id="clone-prefix" style="width: 100%;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-sm">Add link</button>
|
<button class="btn btn-sm">Add note link</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -287,10 +305,14 @@
|
|||||||
<form id="jump-to-note-form">
|
<form id="jump-to-note-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="jump-to-note-autocomplete">Note</label>
|
<label for="jump-to-note-autocomplete">Note</label>
|
||||||
<input id="jump-to-note-autocomplete" style="width: 100%;">
|
<input id="jump-to-note-autocomplete" placeholder="search for note by its name" style="width: 100%;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button name="action" value="jump" class="btn btn-sm">Jump <kbd>enter</kbd></button>
|
<div style="display: flex; justify-content: space-between;">
|
||||||
|
<button id="jump-to-note-button" class="btn btn-sm btn-primary">Jump <kbd>enter</kbd></button>
|
||||||
|
|
||||||
|
<button id="show-in-full-text-button" class="btn btn-sm">Search in full text <kbd>ctrl+enter</kbd></button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -308,12 +330,35 @@
|
|||||||
<div id="options-dialog" title="Options" style="display: none;">
|
<div id="options-dialog" title="Options" style="display: none;">
|
||||||
<div id="options-tabs">
|
<div id="options-tabs">
|
||||||
<ul>
|
<ul>
|
||||||
|
<li><a href="#appearance">Apperance</a></li>
|
||||||
<li><a href="#change-password">Change password</a></li>
|
<li><a href="#change-password">Change password</a></li>
|
||||||
<li><a href="#protected-session-timeout">Protected session</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="#note-revision-snapshot-time-interval">Note revisions</a></li>
|
||||||
<li><a href="#advanced">Advanced</a></li>
|
<li><a href="#advanced">Advanced</a></li>
|
||||||
<li><a href="#about">About Trilium</a></li>
|
<li><a href="#about">About Trilium</a></li>
|
||||||
</ul>
|
</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">
|
<div id="change-password">
|
||||||
<form id="change-password-form">
|
<form id="change-password-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -402,12 +447,14 @@
|
|||||||
<th>App version:</th>
|
<th>App version:</th>
|
||||||
<td id="app-version"></td>
|
<td id="app-version"></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>DB version:</th>
|
<th>DB version:</th>
|
||||||
<td id="db-version"></td>
|
<td id="db-version"></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Sync version:</th>
|
||||||
|
<td id="sync-version"></td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Build date:</th>
|
<th>Build date:</th>
|
||||||
<td id="build-date"></td>
|
<td id="build-date"></td>
|
||||||
|
|||||||
@@ -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>
|
|
||||||